@smartledger/bsv 8.3.0 → 9.0.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.
Files changed (47) hide show
  1. package/CHANGELOG.md +133 -0
  2. package/README.md +374 -222
  3. package/bsv-gdaf.min.js +49 -49
  4. package/bsv-script-helper.min.js +1 -1
  5. package/bsv-smartcontract.min.js +21 -21
  6. package/bsv.bundle.js +49 -49
  7. package/bsv.d.ts +0 -4
  8. package/bsv.min.js +49 -49
  9. package/docs/BRC220_BATCH_LEAF_AMENDMENT.md +194 -0
  10. package/docs/BRC220_PLAN.md +9 -0
  11. package/docs/MODULE_REFERENCE_COMPLETE.md +27 -27
  12. package/docs/README.md +2 -1
  13. package/docs/THREAT_MODEL.md +10 -2
  14. package/docs/advanced/UTXO_MANAGER_GUIDE.md +1 -1
  15. package/docs/getting-started/INSTALLATION.md +23 -23
  16. package/docs/getting-started/QUICK_START.md +7 -7
  17. package/docs/migration/FROM_BSV_1_5_6.md +5 -5
  18. package/index.js +10 -26
  19. package/lib/covenant/helpers.js +34 -35
  20. package/lib/covenant/pushtx.js +2 -2
  21. package/lib/custom-script-helper.js +0 -16
  22. package/lib/notaryhash/index.js +20 -0
  23. package/lib/notaryhash/merkle.js +8 -0
  24. package/lib/notaryhash/suites.js +17 -1
  25. package/lib/ordinals/ordlock.js +1 -1
  26. package/lib/smart_contract/debugger.js +2 -2
  27. package/lib/smart_contract/dsl.js +2 -2
  28. package/lib/smart_contract/index.js +0 -3
  29. package/lib/smart_contract/locks.js +1 -41
  30. package/lib/smart_contract/pels.js +1 -1
  31. package/lib/smart_contract/token.js +1 -1
  32. package/package.json +3 -2
  33. package/test/build/esm_wrapper.js +32 -8
  34. package/test/data/brc220-batch-vector.json +129 -0
  35. package/test/notaryhash/batch_leaf.js +140 -0
  36. package/test/notaryhash/batch_vector.js +282 -0
  37. package/test/notaryhash/interop.js +112 -0
  38. package/test/notaryhash/verify.js +5 -2
  39. package/test/ordinals/inscription.js +24 -14
  40. package/test/ordinals/ordlock.js +0 -46
  41. package/test/smart_contract/covenants.js +0 -38
  42. package/test/smart_contract/dsl_debugger.js +0 -9
  43. package/test/smart_contract/ordinal_transfer.js +0 -10
  44. package/test/smart_contract/sighash_marketplace.js +0 -10
  45. package/test/smart_contract/token_generalized.js +0 -10
  46. package/tools/gen-brc220-batch-vector.js +205 -0
  47. package/version.js +1 -1
@@ -24,16 +24,6 @@ var PRICE = 100000
24
24
 
25
25
  describe('Ordinals OrdLock marketplace', function () {
26
26
  this.timeout(20000)
27
- var I = bsv.Script.Interpreter
28
- var saved
29
-
30
- before(function () {
31
- saved = I.getLimits()
32
- SC.enableGenesis()
33
- })
34
- after(function () {
35
- I.setLimits(saved)
36
- })
37
27
 
38
28
  var seller = PrivateKey.fromRandom()
39
29
  var buyer = PrivateKey.fromRandom()
@@ -168,15 +158,6 @@ describe('Ordinals OrdLock marketplace', function () {
168
158
 
169
159
  describe('Ordinals OrdLock — multi-output payments (royalties + marketplace fee)', function () {
170
160
  this.timeout(20000)
171
- var I = bsv.Script.Interpreter
172
- var saved
173
- before(function () {
174
- saved = I.getLimits()
175
- SC.enableGenesis()
176
- })
177
- after(function () {
178
- I.setLimits(saved)
179
- })
180
161
 
181
162
  var seller = PrivateKey.fromRandom()
182
163
  var royalty = PrivateKey.fromRandom()
@@ -283,15 +264,6 @@ describe('Ordinals OrdLock — parseOrdLock (self-describing listings)', functio
283
264
 
284
265
  describe('Ordinals OrdLock — buildPurchaseTx (end-to-end)', function () {
285
266
  this.timeout(20000)
286
- var I = bsv.Script.Interpreter
287
- var saved
288
- before(function () {
289
- saved = I.getLimits()
290
- SC.enableGenesis()
291
- })
292
- after(function () {
293
- I.setLimits(saved)
294
- })
295
267
 
296
268
  var seller = PrivateKey.fromRandom()
297
269
  var royalty = PrivateKey.fromRandom()
@@ -377,15 +349,6 @@ describe('Ordinals OrdLock — buildPurchaseTx (end-to-end)', function () {
377
349
 
378
350
  describe('Ordinals OrdLock — full lifecycle (list -> buy / cancel)', function () {
379
351
  this.timeout(20000)
380
- var I = bsv.Script.Interpreter
381
- var saved
382
- before(function () {
383
- saved = I.getLimits()
384
- SC.enableGenesis()
385
- })
386
- after(function () {
387
- I.setLimits(saved)
388
- })
389
352
 
390
353
  var owner = PrivateKey.fromRandom() // current ordinal owner / seller
391
354
  var royalty = PrivateKey.fromRandom()
@@ -475,15 +438,6 @@ describe('Ordinals OrdLock — full lifecycle (list -> buy / cancel)', function
475
438
 
476
439
  describe('Ordinals OrdLock — code-review regressions', function () {
477
440
  this.timeout(20000)
478
- var I = bsv.Script.Interpreter
479
- var saved
480
- before(function () {
481
- saved = I.getLimits()
482
- SC.enableGenesis()
483
- })
484
- after(function () {
485
- I.setLimits(saved)
486
- })
487
441
 
488
442
  var owner = PrivateKey.fromRandom()
489
443
  var fundKey = PrivateKey.fromRandom()
@@ -20,16 +20,6 @@ var SATS = 100000
20
20
 
21
21
  describe('SmartContract covenants (v4.2.0)', function () {
22
22
  this.timeout(20000)
23
- var I = bsv.Script.Interpreter
24
- var saved
25
-
26
- before(function () {
27
- saved = I.getLimits()
28
- SC.enableGenesis() // OP_PUSH_TX covenants need post-Genesis limits
29
- })
30
- after(function () {
31
- I.setLimits(saved)
32
- })
33
23
 
34
24
  var alice = PrivateKey.fromRandom()
35
25
  var bob = PrivateKey.fromRandom()
@@ -59,40 +49,12 @@ describe('SmartContract covenants (v4.2.0)', function () {
59
49
  verify(spend.inputs[0].script, c.lock, { tx: spend, satoshis: SATS }).ok.should.equal(true)
60
50
  })
61
51
 
62
- it('CLTV accepts a matured spend and rejects an immature one', function () {
63
- var lt = 800000
64
- var c = Locks.timeLockCLTV(alice, lt)
65
- var spend = spendOf(c.lock, SATS, [p2pkhOutput(alice, SATS - 500)])
66
- spend.nLockTime = lt; spend.inputs[0].sequenceNumber = 0xfffffffe
67
- spend.inputs[0].setScript(c.unlock(spend, SATS))
68
- verify(spend.inputs[0].script, c.lock, { tx: spend, satoshis: SATS }).ok.should.equal(true)
69
-
70
- spend = spendOf(c.lock, SATS, [p2pkhOutput(alice, SATS - 500)])
71
- spend.nLockTime = lt - 1; spend.inputs[0].sequenceNumber = 0xfffffffe
72
- spend.inputs[0].setScript(c.unlock(spend, SATS))
73
- verify(spend.inputs[0].script, c.lock, { tx: spend, satoshis: SATS }).ok.should.equal(false)
74
- })
75
-
76
52
  it('2-of-3 multisig verifies with two valid signatures', function () {
77
53
  var c = Locks.multisig(2, [alice, bob, carol])
78
54
  var spend = spendOf(c.lock, SATS, [p2pkhOutput(bob, SATS - 500)])
79
55
  spend.inputs[0].setScript(c.unlock(spend, SATS, [alice, bob]))
80
56
  verify(spend.inputs[0].script, c.lock, { tx: spend, satoshis: SATS }).ok.should.equal(true)
81
57
  })
82
-
83
- it('HTLC supports claim and rejects an early refund', function () {
84
- var secret = Buffer.from('lightning-preimage-32bytes------')
85
- var timeout = 750000
86
- var c = Locks.htlc({ secret: secret, receiver: bob, sender: alice, timeout: timeout })
87
- var spend = spendOf(c.lock, SATS, [p2pkhOutput(bob, SATS - 500)])
88
- spend.inputs[0].setScript(c.unlockClaim(spend, SATS))
89
- verify(spend.inputs[0].script, c.lock, { tx: spend, satoshis: SATS }).ok.should.equal(true)
90
-
91
- spend = spendOf(c.lock, SATS, [p2pkhOutput(alice, SATS - 500)])
92
- spend.nLockTime = timeout - 1; spend.inputs[0].sequenceNumber = 0xfffffffe
93
- spend.inputs[0].setScript(c.unlockRefund(spend, SATS))
94
- verify(spend.inputs[0].script, c.lock, { tx: spend, satoshis: SATS }).ok.should.equal(false)
95
- })
96
58
  })
97
59
 
98
60
  describe('OP_PUSH_TX', function () {
@@ -14,15 +14,6 @@ var SATS = 100000
14
14
 
15
15
  describe('SmartContract covenant DSL + debugger (v4.5.0)', function () {
16
16
  this.timeout(20000)
17
- var I = bsv.Script.Interpreter
18
- var saved
19
- before(function () {
20
- saved = I.getLimits()
21
- SC.enableGenesis()
22
- })
23
- after(function () {
24
- I.setLimits(saved)
25
- })
26
17
 
27
18
  var alice = PrivateKey.fromRandom()
28
19
  var attacker = PrivateKey.fromRandom()
@@ -23,19 +23,9 @@ var verify = help.verify
23
23
 
24
24
  describe('Ordinal-safe transfer (1-sat, SIGHASH_ALL)', function () {
25
25
  this.timeout(20000)
26
- var I = bsv.Script.Interpreter
27
- var saved
28
26
  var alice = PrivateKey.fromRandom()
29
27
  var bob = PrivateKey.fromRandom()
30
28
 
31
- before(function () {
32
- saved = I.getLimits()
33
- SC.enableGenesis()
34
- })
35
- after(function () {
36
- I.setLimits(saved)
37
- })
38
-
39
29
  it('transferOrdinal recreates the 1-sat token among funding outputs and verifies', function () {
40
30
  var lock = Token.ordinalLock(Token.ownerId(alice))
41
31
  var bobHash = Token.ownerId(bob)
@@ -27,18 +27,8 @@ var SATS = 100000
27
27
 
28
28
  describe('Configurable SIGHASH — SINGLE|ANYONECANPAY marketplace covenant', function () {
29
29
  this.timeout(20000)
30
- var I = bsv.Script.Interpreter
31
- var saved
32
30
  var flag = P.SIGHASH_SINGLE_ANYONECANPAY_FORKID // 0xc3
33
31
 
34
- before(function () {
35
- saved = I.getLimits()
36
- SC.enableGenesis()
37
- })
38
- after(function () {
39
- I.setLimits(saved)
40
- })
41
-
42
32
  // Lock: authenticate the pushed preimage under `flag`, assert the flag, succeed.
43
33
  function makeLock () {
44
34
  var lock = new Script()
@@ -20,16 +20,6 @@ var FEE = 500
20
20
 
21
21
  describe('SmartContract generalized token (pluggable auth + multi-output)', function () {
22
22
  this.timeout(20000)
23
- var I = bsv.Script.Interpreter
24
- var saved
25
-
26
- before(function () {
27
- saved = I.getLimits()
28
- SC.enableGenesis()
29
- })
30
- after(function () {
31
- I.setLimits(saved)
32
- })
33
23
 
34
24
  function spendOf (lock, sats, outputs) {
35
25
  return fundAndSpend(lock, sats, { outputs: outputs }).spend
@@ -0,0 +1,205 @@
1
+ 'use strict'
2
+
3
+ /**
4
+ * Generate the BRC-220 batch-mode golden vector.
5
+ *
6
+ * node tools/gen-brc220-batch-vector.js > test/data/brc220-batch-vector.json
7
+ *
8
+ * WHY THIS FILE EXISTS
9
+ *
10
+ * BRC-220 §On-chain record specifies the batch tree as RFC 6962 and writes
11
+ * `leaf = SHA256(0x00 ‖ d)`, but never binds `d` to a value. Two readings are available —
12
+ * `d = proofHash` and `d = canonicalBytes` — both second-preimage resistant, producing
13
+ * DIFFERENT roots. Implementations that choose differently do not interoperate, and the
14
+ * failure is silent: the fold simply does not reach the root.
15
+ *
16
+ * Prose cannot reliably close that. One golden vector can. See
17
+ * docs/BRC220_BATCH_LEAF_AMENDMENT.md.
18
+ *
19
+ * WHAT THIS VECTOR IS, AND IS NOT
20
+ *
21
+ * It is a CONFORMANCE TARGET, generated by this implementation. It is NOT external
22
+ * validation, and nothing here should be read as proving our tree is the "correct" one.
23
+ * What makes it useful is that the tree underneath it IS externally anchored: the RFC 6962
24
+ * construction is checked against the published Certificate Transparency roots in
25
+ * test/notaryhash/merkle.js. Given a tree that provably matches RFC 6962, this vector
26
+ * fixes the one thing the spec leaves open — what goes in the leaf.
27
+ *
28
+ * EVERY VALUE IS DERIVED, NOT CHOSEN
29
+ *
30
+ * Keys and payloads come from labelled SHA-256 preimages, and timestamps are fixed
31
+ * constants, so the file regenerates byte-identically and a third party can reproduce
32
+ * every intermediate with any library. The vector records those intermediates —
33
+ * canonicalBytes, proofHash, leaf hash — precisely so a reader can check each step
34
+ * independently instead of trusting the final root.
35
+ *
36
+ * Do not regenerate this to make a failing test pass. A diff is either a real change in
37
+ * the encoding or a bug.
38
+ */
39
+
40
+ var Hash = require('../lib/crypto/hash')
41
+ var BN = require('../lib/crypto/bn')
42
+ var PrivateKey = require('../lib/privatekey')
43
+ var ECDSA = require('../lib/crypto/ecdsa')
44
+ var Point = require('../lib/crypto/point')
45
+ var Encoding = require('../lib/notaryhash/encoding')
46
+ var Merkle = require('../lib/notaryhash/merkle')
47
+
48
+ // Five proofs. Five rather than four on purpose: RFC 6962 splits at the largest power of
49
+ // two BELOW n, so n=5 splits 4/1 where a midpoint split would give 2/3 and a different
50
+ // root. It also makes the leaf count odd, exercising the rule that the last leaf is never
51
+ // duplicated. Those are the two places an implementation of this tree goes wrong, so the
52
+ // vector is worth more at n=5 than at any power of two.
53
+ var LEAF_COUNT = 5
54
+
55
+ // Fixed, so the file is reproducible. ISO-8601 Z, one day apart.
56
+ var CREATED_AT = [
57
+ '2026-01-01T00:00:00.000Z',
58
+ '2026-01-02T00:00:00.000Z',
59
+ '2026-01-03T00:00:00.000Z',
60
+ '2026-01-04T00:00:00.000Z',
61
+ '2026-01-05T00:00:00.000Z'
62
+ ]
63
+
64
+ var ALGORITHM = 'ECDSA-secp256k1'
65
+ var HASH_ALGORITHM = 'SHA-256'
66
+
67
+ // Labelled preimages, so anyone can recompute the inputs themselves.
68
+ function derive (label, i) {
69
+ return Hash.sha256(Buffer.from('BRC-220/batch-vector/' + label + '/' + i, 'utf8'))
70
+ }
71
+
72
+ // Raw 64-byte r‖s, low-S, as `encoding: "raw"` requires. Not DER.
73
+ function signRaw (payloadHash, privkey) {
74
+ // set() rather than assigning fields directly: it derives `pubkey` from `privkey`,
75
+ // which sign() requires. RFC 6979 makes this deterministic, so the vector reproduces.
76
+ //
77
+ // NO `endian` option. BRC-220 signs the 32-byte payloadHash directly, so the digest IS
78
+ // the scalar, big-endian. The first version of this file passed `endian: 'little'`,
79
+ // matching the verifier bug fixed in 8.3.1, and produced a "golden" vector whose five
80
+ // signatures no conformant implementation could verify — 0/5 under @noble/curves. A
81
+ // reference vector that is wrong is worse than no vector, because it is published as
82
+ // the thing to match.
83
+ //
84
+ // It survived review because the generator and the checker used the same signer, so
85
+ // both were wrong in the same direction. test/notaryhash/batch_vector.js now verifies
86
+ // every signature under @noble/curves as well, which is the assertion that fails if
87
+ // this line is ever changed back.
88
+ var ecdsa = ECDSA().set({ hashbuf: payloadHash, privkey: privkey })
89
+ ecdsa.sign()
90
+ var sig = ecdsa.sig
91
+
92
+ var halfOrder = Point.getN().div(new BN(2))
93
+ if (sig.s.gt(halfOrder)) {
94
+ throw new Error('signature is not low-S; the signer must produce low-S, not normalise')
95
+ }
96
+
97
+ var r = sig.r.toBuffer({ size: 32 })
98
+ var s = sig.s.toBuffer({ size: 32 })
99
+ return Buffer.concat([r, s])
100
+ }
101
+
102
+ function u32beHex (n) {
103
+ var b = Buffer.alloc(4)
104
+ b.writeUInt32BE(n, 0)
105
+ return b.toString('hex')
106
+ }
107
+
108
+ var proofs = []
109
+ for (var i = 0; i < LEAF_COUNT; i++) {
110
+ // fromObject rather than new PrivateKey(bn): the bare-BN form leaves `compressed`
111
+ // unset, and public-key derivation then fails.
112
+ var privkey = PrivateKey.fromObject({
113
+ bn: BN.fromBuffer(derive('key', i)),
114
+ compressed: true,
115
+ network: 'livenet'
116
+ })
117
+ var payloadHash = derive('payload', i)
118
+ var publicKey = privkey.publicKey.toBuffer() // 33-byte compressed SEC1
119
+ var signature = signRaw(payloadHash, privkey)
120
+ var createdAtUnix = Encoding.toUnixSeconds(CREATED_AT[i])
121
+
122
+ var fields = {
123
+ algorithm: ALGORITHM,
124
+ hashAlgorithm: HASH_ALGORITHM,
125
+ payloadHash: payloadHash,
126
+ publicKey: publicKey,
127
+ signature: signature,
128
+ createdAtUnix: createdAtUnix
129
+ }
130
+
131
+ var canonicalBytes = Encoding.canonicalBytes(fields)
132
+ var proofHash = Encoding.proofHash(fields)
133
+
134
+ proofs.push({
135
+ leafIndex: i,
136
+ keyPreimage: 'BRC-220/batch-vector/key/' + i,
137
+ payloadPreimage: 'BRC-220/batch-vector/payload/' + i,
138
+ algorithm: ALGORITHM,
139
+ hashAlgorithm: HASH_ALGORITHM,
140
+ encoding: 'raw',
141
+ payloadHash: payloadHash.toString('hex'),
142
+ publicKey: publicKey.toString('hex'),
143
+ signature: signature.toString('hex'),
144
+ createdAt: CREATED_AT[i],
145
+ createdAtUnix: createdAtUnix,
146
+ canonicalBytes: canonicalBytes.toString('hex'),
147
+ proofHash: proofHash.toString('hex'),
148
+ // The value the spec leaves open, spelled out at both readings so the divergence is
149
+ // visible in the published artifact rather than only in prose.
150
+ leafHash: Merkle.hashLeaf(proofHash).toString('hex'),
151
+ leafHashUnderRejectedReading: Merkle.hashLeaf(canonicalBytes).toString('hex')
152
+ })
153
+ }
154
+
155
+ var leaves = proofs.map(function (p) { return Buffer.from(p.proofHash, 'hex') })
156
+ var root = Merkle.root(leaves)
157
+
158
+ proofs.forEach(function (p, idx) {
159
+ p.path = Merkle.path(leaves, idx).map(function (n) { return n.toString('hex') })
160
+ })
161
+
162
+ var vector = {
163
+ _comment: [
164
+ 'BRC-220 batch-mode golden vector. Generated by tools/gen-brc220-batch-vector.js;',
165
+ 'do not hand-edit. Every value is derived from the labelled preimages recorded on each',
166
+ 'proof, so the file is reproducible from scratch in any language.',
167
+ '',
168
+ 'THE POINT OF THIS FILE: BRC-220 writes the batch leaf as SHA256(0x00 || d) and never',
169
+ 'says what d is. This vector fixes d = proofHash. The `rejectedReading` block records',
170
+ 'what the same five proofs produce under d = canonicalBytes — equally sound',
171
+ 'cryptographically, a different root, and therefore not interoperable. An',
172
+ 'implementation matching `rejectedReading.root` has made the other choice, which is',
173
+ 'exactly the confusion this vector exists to make visible.',
174
+ '',
175
+ 'This is a conformance target produced by @smartledger/bsv, not external validation.',
176
+ 'The tree beneath it is externally anchored: the RFC 6962 construction is verified',
177
+ 'against the published Certificate Transparency roots in test/notaryhash/merkle.js.',
178
+ '',
179
+ 'n = 5 deliberately: RFC 6962 splits at the largest power of two BELOW n, so this',
180
+ 'splits 4/1 (a midpoint split would give 2/3 and a different root), and the odd count',
181
+ 'exercises the rule that the last leaf is never duplicated.'
182
+ ].join(' ').replace(/\s+/g, ' ').trim(),
183
+ spec: 'BRC-220',
184
+ tree: 'RFC 6962',
185
+ leafDatum: 'proofHash',
186
+ leafRule: 'leaf = SHA-256(0x00 || proofHash); node = SHA-256(0x01 || left || right)',
187
+ leafCount: LEAF_COUNT,
188
+ root: root.toString('hex'),
189
+ onChainRecordTail: {
190
+ _comment: 'The batch record is "NOTARYHASH" | u8(1) | u8(2) | merkleRoot(32) | u32be(leafCount).',
191
+ merkleRoot: root.toString('hex'),
192
+ leafCountU32be: u32beHex(LEAF_COUNT)
193
+ },
194
+ rejectedReading: {
195
+ _comment: 'What d = canonicalBytes yields over the SAME five proofs. Recorded so the ' +
196
+ 'divergence is checkable, not to endorse it.',
197
+ leafDatum: 'canonicalBytes',
198
+ root: Merkle.root(proofs.map(function (p) {
199
+ return Buffer.from(p.canonicalBytes, 'hex')
200
+ })).toString('hex')
201
+ },
202
+ proofs: proofs
203
+ }
204
+
205
+ process.stdout.write(JSON.stringify(vector, null, 2) + '\n')
package/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  'use strict'
2
2
  // GENERATED by scripts/sync-version.js on `npm version` — do not edit.
3
- module.exports = '8.3.0'
3
+ module.exports = '9.0.0'