@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.
- package/CHANGELOG.md +133 -0
- package/README.md +374 -222
- package/bsv-gdaf.min.js +49 -49
- package/bsv-script-helper.min.js +1 -1
- package/bsv-smartcontract.min.js +21 -21
- package/bsv.bundle.js +49 -49
- package/bsv.d.ts +0 -4
- package/bsv.min.js +49 -49
- package/docs/BRC220_BATCH_LEAF_AMENDMENT.md +194 -0
- package/docs/BRC220_PLAN.md +9 -0
- package/docs/MODULE_REFERENCE_COMPLETE.md +27 -27
- package/docs/README.md +2 -1
- package/docs/THREAT_MODEL.md +10 -2
- package/docs/advanced/UTXO_MANAGER_GUIDE.md +1 -1
- package/docs/getting-started/INSTALLATION.md +23 -23
- package/docs/getting-started/QUICK_START.md +7 -7
- package/docs/migration/FROM_BSV_1_5_6.md +5 -5
- package/index.js +10 -26
- package/lib/covenant/helpers.js +34 -35
- package/lib/covenant/pushtx.js +2 -2
- package/lib/custom-script-helper.js +0 -16
- package/lib/notaryhash/index.js +20 -0
- package/lib/notaryhash/merkle.js +8 -0
- package/lib/notaryhash/suites.js +17 -1
- package/lib/ordinals/ordlock.js +1 -1
- package/lib/smart_contract/debugger.js +2 -2
- package/lib/smart_contract/dsl.js +2 -2
- package/lib/smart_contract/index.js +0 -3
- package/lib/smart_contract/locks.js +1 -41
- package/lib/smart_contract/pels.js +1 -1
- package/lib/smart_contract/token.js +1 -1
- package/package.json +3 -2
- package/test/build/esm_wrapper.js +32 -8
- package/test/data/brc220-batch-vector.json +129 -0
- package/test/notaryhash/batch_leaf.js +140 -0
- package/test/notaryhash/batch_vector.js +282 -0
- package/test/notaryhash/interop.js +112 -0
- package/test/notaryhash/verify.js +5 -2
- package/test/ordinals/inscription.js +24 -14
- package/test/ordinals/ordlock.js +0 -46
- package/test/smart_contract/covenants.js +0 -38
- package/test/smart_contract/dsl_debugger.js +0 -9
- package/test/smart_contract/ordinal_transfer.js +0 -10
- package/test/smart_contract/sighash_marketplace.js +0 -10
- package/test/smart_contract/token_generalized.js +0 -10
- package/tools/gen-brc220-batch-vector.js +205 -0
- package/version.js +1 -1
package/test/ordinals/ordlock.js
CHANGED
|
@@ -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