@smartledger/bsv 5.4.2 → 5.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +46 -0
- package/README.md +28 -8
- package/bsv-covenant.min.js +8 -8
- package/bsv-gdaf.min.js +11 -11
- package/bsv-ltp.min.js +11 -11
- package/bsv-smartcontract.min.js +8 -8
- package/bsv.bundle.js +11 -11
- package/bsv.min.js +11 -11
- package/lib/smart_contract/authorizers.js +159 -0
- package/lib/smart_contract/dsl.js +1 -1
- package/lib/smart_contract/index.js +5 -3
- package/lib/smart_contract/pels.js +1 -0
- package/lib/smart_contract/pushtx.js +51 -7
- package/lib/smart_contract/token.js +179 -35
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,52 @@ All notable changes to SmartLedger-BSV will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [5.5.0] - 2026-06-25
|
|
9
|
+
|
|
10
|
+
Minor release. Closes a front-running theft flaw in the ownership-token covenant
|
|
11
|
+
and generalizes the covenant core into a pluggable, multi-output state machine.
|
|
12
|
+
|
|
13
|
+
### Security
|
|
14
|
+
|
|
15
|
+
- **`SmartContract.Token` ownership is now ECDSA-authorized.** The previous design
|
|
16
|
+
gated a transfer on revealing `ownerSecret` with `SHA256(ownerSecret) ==
|
|
17
|
+
ownerHash` — a hash-lock. The secret was exposed in the mempool on the first
|
|
18
|
+
spend and the spender freely chose the next owner, so a watcher could lift the
|
|
19
|
+
revealed secret and broadcast a competing spend redirecting the token to
|
|
20
|
+
themselves. Ownership is now a key: state is `HASH160(ownerPubKey)` and a
|
|
21
|
+
transfer requires the current owner's signature over the spend
|
|
22
|
+
(`OP_CHECKSIGVERIFY`), which commits via `SIGHASH_ALL` `hashOutputs` to the
|
|
23
|
+
exact recreated output — so altering the destination invalidates the proof.
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- **Pluggable ownership — `SmartContract.Authorizers`.** Token ownership is always
|
|
28
|
+
a 20-byte commitment, so the transfer plumbing is identical across schemes:
|
|
29
|
+
- `singleKey()` — signature over the spend (the model above).
|
|
30
|
+
- `multisig(m, n)` — m-of-n group ownership; the key set is committed by hashing
|
|
31
|
+
the canonical redeem script, revealed at spend, bound to the commitment, and
|
|
32
|
+
checked with `OP_CHECKMULTISIG`.
|
|
33
|
+
- `predicate({ commit, emit, unlockArgs })` — escape hatch for custom schemes.
|
|
34
|
+
- **Multi-output (N-output) tokens** — `Token.ownershipTokenMulti` /
|
|
35
|
+
`unlockTransferMulti` let the recreated token output sit among other outputs
|
|
36
|
+
(payments, change, data); the spender reveals the surrounding output bytes and
|
|
37
|
+
the covenant binds them into the committed `hashOutputs`.
|
|
38
|
+
- **`PushTx.grind` configurable nonce** — `{ field: 'nLockTime' | 'sequence' }`,
|
|
39
|
+
so a covenant pinning `nLockTime` for a CLTV timelock can grind the input
|
|
40
|
+
sequence instead. `PushTx.assertSighashAll()` guard added to token/PELS scripts.
|
|
41
|
+
|
|
42
|
+
### Changed (breaking, `SmartContract.Token` only)
|
|
43
|
+
|
|
44
|
+
- `Token.ownershipToken(fee, owner)` — `owner` is now `HASH160(pubkey)` (use
|
|
45
|
+
`Token.ownerId(key)`), not `SHA256(secret)`.
|
|
46
|
+
- `Token.unlockTransfer(signer, newOwnerHash, spend, satoshis, lockingScript[, opts])`
|
|
47
|
+
— now takes the owner key and the spend (it grinds and signs), not a precomputed
|
|
48
|
+
preimage. The rest of the package's API is unchanged and fully compatible.
|
|
49
|
+
|
|
50
|
+
### Notes
|
|
51
|
+
|
|
52
|
+
- Full suite **4267 passing**; `lib/` and `test/` lint clean.
|
|
53
|
+
|
|
8
54
|
## [5.4.2] - 2026-06-25
|
|
9
55
|
|
|
10
56
|
Patch release. Dependency security maintenance — no API or behavior changes.
|
package/README.md
CHANGED
|
@@ -37,9 +37,9 @@ SC.enableGenesis() // post-Genesis limits (OP_PUSH_TX need
|
|
|
37
37
|
// Self-replicating covenant — every spend recreates the same script (value − fee).
|
|
38
38
|
const lock = SC.perpetualCovenant(500)
|
|
39
39
|
|
|
40
|
-
// Stateful ownership token (NFT) — transfer requires the owner's
|
|
41
|
-
// rewrites state, perpetuates the token code.
|
|
42
|
-
const token = SC.ownershipToken(500, ownerHash)
|
|
40
|
+
// Stateful ownership token (NFT) — transfer requires the owner's ECDSA signature
|
|
41
|
+
// over the spend, rewrites state, perpetuates the token code.
|
|
42
|
+
const token = SC.ownershipToken(500, ownerHash) // ownerHash = SC.Token.ownerId(ownerKey)
|
|
43
43
|
|
|
44
44
|
// Value covenant — forces spend outputs to match a specific hashOutputs.
|
|
45
45
|
const vlock = SC.valueCovenant(SC.PushTx.hashOutputs(requiredOutputs))
|
|
@@ -54,7 +54,8 @@ const ok = SC.verifyScript(unlockScript, lockingScript, tx, inputIndex, satoshis
|
|
|
54
54
|
|---|---|
|
|
55
55
|
| `PushTx` | nChain WP1605 OP_PUSH_TX — `authenticator()`, `valueCovenant()`, `hashOutputs()`, `extractHashOutputs()`, `grind()` |
|
|
56
56
|
| `PELS` | Perpetually Enforcing Locking Scripts — `perpetualCovenant(fee)` |
|
|
57
|
-
| `Token` | Stateful ownership token (NFT) — `ownershipToken(fee,
|
|
57
|
+
| `Token` | Stateful ownership token (NFT) — `ownershipToken(fee, owner[, auth])`, `ownershipTokenMulti(owner[, auth])`, `ownerId(key)`, `unlockTransfer(...)`, `unlockTransferMulti(...)` |
|
|
58
|
+
| `Authorizers` | Pluggable token ownership — `singleKey()`, `multisig(m, n)`, `predicate({...})` |
|
|
58
59
|
| `Locks` | Hash-lock, P2PKH, CLTV time-lock, m-of-n multisig, HTLC |
|
|
59
60
|
| `CovenantHelpers` | Consensus-flag `verify()` harness, raw BIP-143 preimage, signing, fund/spend scaffolding |
|
|
60
61
|
|
|
@@ -729,11 +730,30 @@ const pels = SC.perpetualCovenant(500) // fee in satoshis deducted each hop
|
|
|
729
730
|
|
|
730
731
|
### Ownership Tokens (NFT) — v4.2.0 API
|
|
731
732
|
```javascript
|
|
732
|
-
// Stateful ownership token. Owner is carried as on-chain state
|
|
733
|
-
// requires the current owner's
|
|
734
|
-
// the
|
|
735
|
-
|
|
733
|
+
// Stateful ownership token. Owner is carried as on-chain state (HASH160 of the
|
|
734
|
+
// owner's public key); transfer requires the current owner's ECDSA SIGNATURE over
|
|
735
|
+
// the spend (OP_CHECKSIG) and rewrites state, perpetuating the token code across
|
|
736
|
+
// the chain of spends. The signature commits to the chosen next owner, so a
|
|
737
|
+
// mempool watcher cannot redirect a pending transfer (no hash-lock front-running).
|
|
738
|
+
const ownerHash = SC.Token.ownerId(currentOwnerKey) // = HASH160(pubkey)
|
|
736
739
|
const token = SC.ownershipToken(500, ownerHash)
|
|
740
|
+
// To spend it forward to `nextOwnerHash`, the current owner signs:
|
|
741
|
+
// tokenInput.setScript(SC.Token.unlockTransfer(currentOwnerKey, nextOwnerHash, spendTx, sats, token))
|
|
742
|
+
|
|
743
|
+
// Pluggable ownership — the SAME covenant, owned by an m-of-n group. Ownership is
|
|
744
|
+
// committed as a 20-byte hash either way, so the transfer plumbing is identical.
|
|
745
|
+
const ms = SC.Authorizers.multisig(2, 3)
|
|
746
|
+
const groupToken = SC.Token.ownershipToken(500, ms.commit([pkA, pkB, pkC]), ms)
|
|
747
|
+
// transfer requires any 2 of the 3 keys:
|
|
748
|
+
// SC.Token.unlockTransfer({ keys: [pkA, pkB, pkC], signWith: [skA, skC] },
|
|
749
|
+
// nextOwnerHash, spendTx, sats, groupToken, { auth: ms })
|
|
750
|
+
// Custom schemes: SC.Authorizers.predicate({ commit, emit, unlockArgs }).
|
|
751
|
+
|
|
752
|
+
// N-output: recreate the token alongside other outputs (payments, change, data).
|
|
753
|
+
// The spender reveals the surrounding output bytes; the covenant binds them all.
|
|
754
|
+
const multi = SC.Token.ownershipTokenMulti(ownerHash)
|
|
755
|
+
// SC.Token.unlockTransferMulti(currentOwnerKey, nextOwnerHash, spendTx, sats, multi,
|
|
756
|
+
// { before: serializedOutputsBeforeToken, after: serializedOutputsAfterToken, tokenValue: le8(value) })
|
|
737
757
|
```
|
|
738
758
|
|
|
739
759
|
### End-to-end verification
|