@smartledger/bsv 8.3.1 → 9.1.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 (169) hide show
  1. package/CHANGELOG.md +228 -0
  2. package/README.md +152 -1031
  3. package/STABILITY.md +134 -0
  4. package/bsv-gdaf.min.js +58 -53
  5. package/bsv-ltp.min.js +23 -18
  6. package/bsv-script-helper.min.js +1 -1
  7. package/bsv-smartcontract.min.js +21 -21
  8. package/bsv.bundle.js +58 -53
  9. package/bsv.d.ts +0 -4
  10. package/bsv.min.js +58 -53
  11. package/docs/BRC220_BATCH_LEAF_AMENDMENT.md +82 -7
  12. package/docs/MODULE_REFERENCE_COMPLETE.md +27 -27
  13. package/docs/README.md +2 -1
  14. package/docs/THREAT_MODEL.md +10 -2
  15. package/docs/advanced/UTXO_MANAGER_GUIDE.md +1 -1
  16. package/docs/getting-started/INSTALLATION.md +23 -23
  17. package/docs/getting-started/QUICK_START.md +7 -7
  18. package/docs/migration/FROM_BSV_1_5_6.md +5 -5
  19. package/docs/proposals/10.0.0-package-split.md +131 -0
  20. package/index.js +15 -26
  21. package/lib/block/merkleblock.js +19 -5
  22. package/lib/covenant/helpers.js +34 -35
  23. package/lib/covenant/pushtx.js +2 -2
  24. package/lib/custom-script-helper.js +0 -16
  25. package/lib/hdprivatekey.js +15 -0
  26. package/lib/ordinals/ordlock.js +1 -1
  27. package/lib/script/interpreter.js +109 -1
  28. package/lib/smart_contract/builder.js +14 -0
  29. package/lib/smart_contract/debugger.js +2 -2
  30. package/lib/smart_contract/dsl.js +2 -2
  31. package/lib/smart_contract/index.js +0 -3
  32. package/lib/smart_contract/locks.js +1 -41
  33. package/lib/smart_contract/pels.js +1 -1
  34. package/lib/smart_contract/token.js +1 -1
  35. package/lib/util/deprecate.js +159 -0
  36. package/package.json +19 -82
  37. package/tools/gen-brc220-batch-vector.js +205 -0
  38. package/version.js +1 -1
  39. package/.mocharc.json +0 -5
  40. package/test/address.js +0 -629
  41. package/test/block/block.js +0 -239
  42. package/test/block/blockheader.js +0 -270
  43. package/test/block/merkleblock.js +0 -207
  44. package/test/build/bundle_crypto_shim.js +0 -104
  45. package/test/build/bundle_externals.js +0 -125
  46. package/test/build/bundle_smoke.js +0 -55
  47. package/test/build/esbuild_main.js +0 -65
  48. package/test/build/esm_wrapper.js +0 -73
  49. package/test/build/exports_resolution.js +0 -84
  50. package/test/build/version_sync.js +0 -14
  51. package/test/cli/smoke.js +0 -261
  52. package/test/consensus/base58-vectors.js +0 -120
  53. package/test/consensus/sv-script-vectors.js +0 -81
  54. package/test/consensus/sv-sighash-vectors.js +0 -51
  55. package/test/consensus/sv-tx-vectors.js +0 -48
  56. package/test/credentials-test.js +0 -332
  57. package/test/crypto/backend_selection.js +0 -126
  58. package/test/crypto/bn.js +0 -168
  59. package/test/crypto/ecdsa.js +0 -426
  60. package/test/crypto/elliptic-fixed.js +0 -61
  61. package/test/crypto/hash.browser.js +0 -121
  62. package/test/crypto/hash.js +0 -122
  63. package/test/crypto/point.js +0 -193
  64. package/test/crypto/random.js +0 -105
  65. package/test/crypto/security.js +0 -176
  66. package/test/crypto/shamir.js +0 -177
  67. package/test/crypto/shamir_rngcap.js +0 -29
  68. package/test/crypto/signature.js +0 -399
  69. package/test/data/bip69.json +0 -215
  70. package/test/data/bitcoin-sv/README.md +0 -170
  71. package/test/data/bitcoin-sv/base58_encode_decode.json +0 -14
  72. package/test/data/bitcoin-sv/base58_keys_invalid.json +0 -152
  73. package/test/data/bitcoin-sv/base58_keys_valid.json +0 -452
  74. package/test/data/bitcoin-sv/script_tests.json +0 -2591
  75. package/test/data/bitcoin-sv/sighash.json +0 -1003
  76. package/test/data/bitcoin-sv/tx_invalid.json +0 -285
  77. package/test/data/bitcoin-sv/tx_valid.json +0 -367
  78. package/test/data/bitcoind/base58_keys_invalid.json +0 -152
  79. package/test/data/bitcoind/base58_keys_valid.json +0 -452
  80. package/test/data/bitcoind/blocks.json +0 -27
  81. package/test/data/bitcoind/script_tests.json +0 -2244
  82. package/test/data/bitcoind/sig_canonical.json +0 -7
  83. package/test/data/bitcoind/sig_noncanonical.json +0 -22
  84. package/test/data/bitcoind/tx_invalid.json +0 -177
  85. package/test/data/bitcoind/tx_valid.json +0 -224
  86. package/test/data/blk86756-testnet.dat +0 -0
  87. package/test/data/blk86756-testnet.js +0 -12
  88. package/test/data/blk86756-testnet.json +0 -684
  89. package/test/data/ecdsa.json +0 -230
  90. package/test/data/merkleblocks.js +0 -486
  91. package/test/data/messages.json +0 -22
  92. package/test/data/sighash.json +0 -1004
  93. package/test/data/tx_creation.json +0 -85
  94. package/test/didweb/relationships.js +0 -144
  95. package/test/ecies/bitcore-ecies.js +0 -178
  96. package/test/ecies/electrum-ecies.js +0 -206
  97. package/test/encoding/base58.js +0 -131
  98. package/test/encoding/base58check.js +0 -145
  99. package/test/encoding/bufferreader.js +0 -328
  100. package/test/encoding/bufferwriter.js +0 -160
  101. package/test/encoding/varint.js +0 -104
  102. package/test/gdaf/anchor_no_key_leak.js +0 -146
  103. package/test/gdaf/anchor_spv.js +0 -163
  104. package/test/gdaf/canonicalization.js +0 -106
  105. package/test/gdaf/canonicalize.js +0 -140
  106. package/test/gdaf/zk_prover.js +0 -204
  107. package/test/hdkeys.js +0 -365
  108. package/test/hdprivatekey.js +0 -339
  109. package/test/hdpublickey.js +0 -293
  110. package/test/index.js +0 -16
  111. package/test/ltp/ids.js +0 -74
  112. package/test/ltp/right.js +0 -198
  113. package/test/ltp/verify_failclosed.js +0 -134
  114. package/test/message/message.js +0 -190
  115. package/test/mnemonic/data/fixtures.json +0 -300
  116. package/test/mnemonic/mnemonic.js +0 -277
  117. package/test/mnemonic/mocha.opts +0 -1
  118. package/test/mnemonic/pbkdf2.test.js +0 -43
  119. package/test/networks.js +0 -207
  120. package/test/notaryhash/batch_leaf.js +0 -140
  121. package/test/notaryhash/certificate.js +0 -249
  122. package/test/notaryhash/encoding.js +0 -186
  123. package/test/notaryhash/interop.js +0 -112
  124. package/test/notaryhash/merkle.js +0 -181
  125. package/test/notaryhash/script.js +0 -270
  126. package/test/notaryhash/verify.js +0 -342
  127. package/test/opcode.js +0 -186
  128. package/test/ordinals/bsv20.js +0 -337
  129. package/test/ordinals/inscription.js +0 -319
  130. package/test/ordinals/ordlock.js +0 -613
  131. package/test/privatekey.js +0 -540
  132. package/test/publickey.js +0 -411
  133. package/test/regressions.js +0 -215
  134. package/test/script/chronicle.js +0 -543
  135. package/test/script/defaults.js +0 -160
  136. package/test/script/genesis_limits.js +0 -203
  137. package/test/script/interpreter.js +0 -776
  138. package/test/script/script.js +0 -1259
  139. package/test/script/string_ops.js +0 -88
  140. package/test/security/fail_closed_contracts.js +0 -104
  141. package/test/security/threat_model_coverage.js +0 -31
  142. package/test/smart_contract/covenants.js +0 -245
  143. package/test/smart_contract/dsl_debugger.js +0 -101
  144. package/test/smart_contract/extract_field.js +0 -61
  145. package/test/smart_contract/nonenforcing_guard.js +0 -44
  146. package/test/smart_contract/ordinal_transfer.js +0 -81
  147. package/test/smart_contract/preimage.js +0 -98
  148. package/test/smart_contract/sighash_marketplace.js +0 -108
  149. package/test/smart_contract/token_generalized.js +0 -185
  150. package/test/spv/headerchain.js +0 -86
  151. package/test/spv/merkleproof.js +0 -133
  152. package/test/statuslist/failclosed.js +0 -78
  153. package/test/transaction/deserialize.js +0 -33
  154. package/test/transaction/input/input.js +0 -92
  155. package/test/transaction/input/multisig.js +0 -174
  156. package/test/transaction/input/multisigscripthash.js +0 -111
  157. package/test/transaction/input/publickey.js +0 -68
  158. package/test/transaction/input/publickeyhash.js +0 -59
  159. package/test/transaction/output.js +0 -185
  160. package/test/transaction/sighash.js +0 -91
  161. package/test/transaction/signature.js +0 -127
  162. package/test/transaction/transaction.js +0 -1299
  163. package/test/transaction/unspentoutput.js +0 -97
  164. package/test/types/dts_drift.js +0 -124
  165. package/test/types/surface_honesty.js +0 -102
  166. package/test/util/id.js +0 -72
  167. package/test/util/js.js +0 -76
  168. package/test/util/preconditions.js +0 -79
  169. package/test/vcjwt/interop.js +0 -126
@@ -1,186 +0,0 @@
1
- 'use strict'
2
-
3
- /* global describe, it */
4
-
5
- // BRC-220 canonical encoding. The property that matters is not that these bytes are
6
- // stable for us — it is that another implementation reading the spec produces the same
7
- // ones. So the tests assert the byte layout the spec dictates, field by field, rather
8
- // than only pinning our own output.
9
- //
10
- // The failure this module is most exposed to is a proofHash that is self-consistent and
11
- // wrong: it verifies against itself, every local test passes, and nobody finds out until
12
- // a third party tries to verify a certificate. Several tests below exist only to make
13
- // that shape impossible.
14
-
15
- require('chai').should()
16
- var Encoding = require('../../lib/notaryhash/encoding')
17
- var Hash = require('../../lib/crypto/hash')
18
-
19
- var FIELDS = {
20
- algorithm: 'ECDSA-secp256k1',
21
- hashAlgorithm: 'SHA-256',
22
- payloadHash: Buffer.alloc(32, 0x11),
23
- publicKey: Buffer.alloc(33, 0x02),
24
- signature: Buffer.alloc(71, 0x30),
25
- createdAtUnix: 1786838400
26
- }
27
-
28
- describe('BRC-220 encoding', function () {
29
- describe('lp()', function () {
30
- it('is u32be(len) followed by the bytes', function () {
31
- Encoding.lp(Buffer.from([0xaa, 0xbb])).toString('hex').should.equal('00000002aabb')
32
- })
33
-
34
- it('encodes an empty value as a bare zero length', function () {
35
- Encoding.lp(Buffer.alloc(0)).toString('hex').should.equal('00000000')
36
- })
37
-
38
- it('measures BYTES, not characters, for text', function () {
39
- // 'é' is one character and two UTF-8 bytes. Using .length here would prefix 1,
40
- // and every proof containing a non-ASCII algorithm name would be unverifiable.
41
- Encoding.lp('é').toString('hex').should.equal('00000002c3a9')
42
- Encoding.lp('abc').toString('hex').should.equal('00000003616263')
43
- })
44
-
45
- // Length-prefixing exists to make field boundaries unambiguous. If it did not, these
46
- // two field pairs would concatenate to identical bytes.
47
- it('keeps ambiguous field splits distinct', function () {
48
- var a = Buffer.concat([Encoding.lp('ab'), Encoding.lp('c')])
49
- var b = Buffer.concat([Encoding.lp('a'), Encoding.lp('bc')])
50
- a.toString('hex').should.not.equal(b.toString('hex'))
51
- })
52
- })
53
-
54
- describe('u64be()', function () {
55
- it('is 8 bytes, big-endian', function () {
56
- Encoding.u64be(1).toString('hex').should.equal('0000000000000001')
57
- Encoding.u64be(1786838400).toString('hex').should.equal('000000006a80fd80')
58
- })
59
-
60
- // Date.now() is milliseconds. A caller who forgets to divide gets a timestamp about
61
- // a thousand years in the future that still encodes cleanly, so the guard is on the
62
- // input rather than the output.
63
- it('rejects a fractional value, which is what a millisecond mistake looks like', function () {
64
- ;(function () { Encoding.u64be(1786838400.5) }).should.throw(/whole seconds/)
65
- })
66
-
67
- it('rejects non-finite and negative values', function () {
68
- ;(function () { Encoding.u64be(NaN) }).should.throw(/finite/)
69
- ;(function () { Encoding.u64be(-1) }).should.throw(/negative/)
70
- })
71
- })
72
-
73
- describe('toUnixSeconds()', function () {
74
- it('converts an ISO 8601 string to whole seconds', function () {
75
- Encoding.toUnixSeconds('2026-08-16T00:00:00.000Z').should.equal(1786838400)
76
- })
77
-
78
- // Certificates carry millisecond precision in createdAt; the canonical bytes do not.
79
- // Two implementations must agree on the fractional part, and truncation is the only
80
- // choice that never moves a timestamp forward.
81
- it('truncates rather than rounds', function () {
82
- Encoding.toUnixSeconds('2026-08-16T00:00:00.999Z').should.equal(1786838400)
83
- })
84
-
85
- it('rejects an unparseable date', function () {
86
- ;(function () { Encoding.toUnixSeconds('not-a-date') }).should.throw(/not a valid date/)
87
- })
88
- })
89
-
90
- describe('canonicalBytes()', function () {
91
- // The layout, asserted against a hand-built expectation rather than against the
92
- // function's own output — otherwise this only proves the function is deterministic.
93
- it('lays the fields out exactly as the spec specifies', function () {
94
- var expected = Buffer.concat([
95
- Encoding.lp('NotaryHash/1.0'),
96
- Buffer.from([1]),
97
- Encoding.lp('ECDSA-secp256k1'),
98
- Encoding.lp('SHA-256'),
99
- Encoding.lp(FIELDS.payloadHash),
100
- Encoding.lp(FIELDS.publicKey),
101
- Encoding.lp(FIELDS.signature),
102
- Encoding.u64be(FIELDS.createdAtUnix)
103
- ])
104
- Encoding.canonicalBytes(FIELDS).toString('hex').should.equal(expected.toString('hex'))
105
- })
106
-
107
- it('opens with the protocol prefix and the version byte', function () {
108
- var bytes = Encoding.canonicalBytes(FIELDS)
109
- bytes.slice(0, 4).readUInt32BE(0).should.equal(14) // len('NotaryHash/1.0')
110
- bytes.slice(4, 18).toString('utf8').should.equal('NotaryHash/1.0')
111
- bytes[18].should.equal(1) // u8(version)
112
- })
113
-
114
- it('ends with the timestamp', function () {
115
- var bytes = Encoding.canonicalBytes(FIELDS)
116
- bytes.slice(-8).toString('hex').should.equal(Encoding.u64be(FIELDS.createdAtUnix).toString('hex'))
117
- })
118
-
119
- // THE test for this module. GDAF once signed a serialization that silently omitted
120
- // credentialSubject, so a forger could rewrite the claims without breaking the proof.
121
- // Every field must move the hash, or it is not covered by it.
122
- it('covers every field — changing any one changes the hash', function () {
123
- var base = Encoding.proofHash(FIELDS).toString('hex')
124
- var mutations = {
125
- algorithm: 'ML-DSA-65',
126
- hashAlgorithm: 'SHA-512',
127
- payloadHash: Buffer.alloc(32, 0x12),
128
- publicKey: Buffer.alloc(33, 0x03),
129
- signature: Buffer.alloc(71, 0x31),
130
- createdAtUnix: FIELDS.createdAtUnix + 1
131
- }
132
- Object.keys(mutations).forEach(function (field) {
133
- var altered = Object.assign({}, FIELDS)
134
- altered[field] = mutations[field]
135
- Encoding.proofHash(altered).toString('hex')
136
- .should.not.equal(base, field + ' does not affect proofHash, so it is not covered by it')
137
- })
138
- })
139
-
140
- // Hex is what certificates carry, and hex is twice as long as the bytes it encodes.
141
- // Accepting it here would produce a proof that verifies locally and nowhere else.
142
- it('refuses a hex string where raw bytes are required', function () {
143
- var withHex = Object.assign({}, FIELDS, { payloadHash: FIELDS.payloadHash.toString('hex') })
144
- ;(function () { Encoding.canonicalBytes(withHex) }).should.throw(/must be a Buffer/)
145
- })
146
-
147
- it('names the offending field and points at the hex trap', function () {
148
- var withHex = Object.assign({}, FIELDS, { signature: FIELDS.signature.toString('hex') })
149
- ;(function () { Encoding.canonicalBytes(withHex) }).should.throw(/signature.*hex/)
150
- })
151
-
152
- it('requires the string fields to be present and non-empty', function () {
153
- ;(function () {
154
- Encoding.canonicalBytes(Object.assign({}, FIELDS, { algorithm: '' }))
155
- }).should.throw(/algorithm/)
156
- ;(function () {
157
- Encoding.canonicalBytes(Object.assign({}, FIELDS, { hashAlgorithm: undefined }))
158
- }).should.throw(/hashAlgorithm/)
159
- })
160
-
161
- it('handles a post-quantum-sized signature', function () {
162
- // SLH-DSA signatures run to tens of kilobytes; the u32be length prefix must carry
163
- // them without truncation.
164
- var big = Object.assign({}, FIELDS, { signature: Buffer.alloc(49856, 0x7f) })
165
- var bytes = Encoding.canonicalBytes(big)
166
- var sigLenOffset = bytes.length - 8 - 49856 - 4
167
- bytes.readUInt32BE(sigLenOffset).should.equal(49856)
168
- })
169
- })
170
-
171
- describe('proofHash()', function () {
172
- it('is SHA-256 of the canonical bytes', function () {
173
- Encoding.proofHash(FIELDS).toString('hex')
174
- .should.equal(Hash.sha256(Encoding.canonicalBytes(FIELDS)).toString('hex'))
175
- })
176
-
177
- it('is 32 bytes', function () {
178
- Encoding.proofHash(FIELDS).length.should.equal(32)
179
- })
180
-
181
- it('is deterministic', function () {
182
- Encoding.proofHash(FIELDS).toString('hex')
183
- .should.equal(Encoding.proofHash(Object.assign({}, FIELDS)).toString('hex'))
184
- })
185
- })
186
- })
@@ -1,112 +0,0 @@
1
- 'use strict'
2
-
3
- /* global describe, it */
4
-
5
- // BRC-220 signature interoperability.
6
- //
7
- // Every other test in this directory signs with `lib/crypto/ecdsa.js` and verifies
8
- // through `lib/notaryhash/suites.js`, which also uses `lib/crypto/ecdsa.js`. That
9
- // proves the module agrees with itself. It cannot prove the module agrees with the
10
- // spec, and in 8.3.0 it did not: the suite set `endian: 'little'` before verifying,
11
- // so it rejected any signature produced the way BRC-220 describes and accepted only
12
- // signatures made with bsv's own byte-reversed convention. Twenty-odd passing tests
13
- // said nothing about it, because they all signed the same wrong way.
14
- //
15
- // So this file verifies against `@noble/curves` instead — a separate implementation
16
- // that shares no verification code with ours. It is the only test here that can fail
17
- // when our code and our tests are wrong together.
18
- //
19
- // One trap worth naming, because it cost real time when this was diagnosed: noble v2
20
- // PREHASHES by default. `secp256k1.sign(digest, key)` signs `sha256(digest)`, and
21
- // `secp256k1.verify` prehashes to match, so noble looks self-consistent while
22
- // disagreeing with everyone. BRC-220 signs the payloadHash itself, so every call
23
- // below passes `{ prehash: false }`. Omit it and these tests compare the wrong things
24
- // and "pass" for the wrong reason.
25
-
26
- require('chai').should()
27
- var bsv = require('../..')
28
- var Suites = require('../../lib/notaryhash/suites')
29
- var Hash = require('../../lib/crypto/hash')
30
- var { secp256k1 } = require('@noble/curves/secp256k1.js')
31
-
32
- var PAYLOAD_HASH = Hash.sha256(Buffer.from('the document nobody sees'))
33
-
34
- // A fixed key, so a failure is reproducible rather than one-in-N flaky.
35
- var SECRET = Buffer.from('c0ffee00c0ffee00c0ffee00c0ffee00c0ffee00c0ffee00c0ffee00c0ffee01', 'hex')
36
- var PUBLIC_KEY = Buffer.from(secp256k1.getPublicKey(SECRET, true))
37
-
38
- function rawSig (sig) {
39
- return Buffer.concat([
40
- sig.r.toArrayLike(Buffer, 'be', 32),
41
- sig.s.toArrayLike(Buffer, 'be', 32)
42
- ])
43
- }
44
-
45
- describe('BRC-220 signature interop', function () {
46
- it('agrees with @noble/curves on the public key for a given secret', function () {
47
- // If this fails nothing below means anything — the two libraries would be
48
- // signing under different keys.
49
- var ours = bsv.PrivateKey.fromBuffer(SECRET).toPublicKey().toBuffer()
50
- ours.toString('hex').should.equal(PUBLIC_KEY.toString('hex'))
51
- })
52
-
53
- it('ACCEPTS a signature made the way the spec describes', function () {
54
- // This is the regression. Under 8.3.0 this assertion fails.
55
- var conformant = Buffer.from(secp256k1.sign(PAYLOAD_HASH, SECRET, { prehash: false }))
56
- Suites.verify('ECDSA-secp256k1', PAYLOAD_HASH, conformant, PUBLIC_KEY)
57
- .should.equal(true, 'a conformant BRC-220 signature must verify')
58
- })
59
-
60
- it('emits signatures an independent implementation accepts', function () {
61
- var key = bsv.PrivateKey.fromBuffer(SECRET)
62
- var ecdsa = bsv.crypto.ECDSA().set({ hashbuf: PAYLOAD_HASH, privkey: key })
63
- ecdsa.sign()
64
- secp256k1.verify(rawSig(ecdsa.sig), PAYLOAD_HASH, PUBLIC_KEY, { prehash: false })
65
- .should.equal(true, 'our signature must verify under @noble/curves')
66
- })
67
-
68
- it('REJECTS a signature over the byte-reversed digest', function () {
69
- // The 8.3.0 convention. It must now fail: accepting both would mean two valid
70
- // signatures exist for one signing act, and both are inside proofHash.
71
- var key = bsv.PrivateKey.fromBuffer(SECRET)
72
- var ecdsa = bsv.crypto.ECDSA().set({ hashbuf: PAYLOAD_HASH, endian: 'little', privkey: key })
73
- ecdsa.sign()
74
- Suites.verify('ECDSA-secp256k1', PAYLOAD_HASH, rawSig(ecdsa.sig), PUBLIC_KEY)
75
- .should.equal(false, 'the pre-8.3.1 little-endian convention must not verify')
76
- })
77
-
78
- it('round-trips both ways over many payloads, not just one', function () {
79
- // A single vector can pass by coincidence — the reversal of a digest whose bytes
80
- // happen to be near-palindromic still differs, but a lucky scalar could mask a
81
- // subtler bug. Twenty payloads with distinct digests makes that implausible.
82
- var key = bsv.PrivateKey.fromBuffer(SECRET)
83
- for (var i = 0; i < 20; i++) {
84
- var h = Hash.sha256(Buffer.from('payload ' + i))
85
-
86
- var theirs = Buffer.from(secp256k1.sign(h, SECRET, { prehash: false }))
87
- Suites.verify('ECDSA-secp256k1', h, theirs, PUBLIC_KEY)
88
- .should.equal(true, 'noble sig ' + i + ' rejected by our suite')
89
-
90
- var ecdsa = bsv.crypto.ECDSA().set({ hashbuf: h, privkey: key })
91
- ecdsa.sign()
92
- secp256k1.verify(rawSig(ecdsa.sig), h, PUBLIC_KEY, { prehash: false })
93
- .should.equal(true, 'our sig ' + i + ' rejected by noble')
94
- }
95
- })
96
-
97
- it('still rejects a signature over a DIFFERENT payload', function () {
98
- // Guards against the fix degenerating into "accept anything". A verifier that
99
- // returns true unconditionally would pass every test above.
100
- var other = Hash.sha256(Buffer.from('a different document'))
101
- var sig = Buffer.from(secp256k1.sign(other, SECRET, { prehash: false }))
102
- Suites.verify('ECDSA-secp256k1', PAYLOAD_HASH, sig, PUBLIC_KEY)
103
- .should.equal(false)
104
- })
105
-
106
- it('still rejects a signature from a DIFFERENT key', function () {
107
- var otherSecret = Buffer.from('a11ce00a11ce00a11ce00a11ce00a11ce00a11ce00a11ce00a11ce00a11ce001', 'hex')
108
- var sig = Buffer.from(secp256k1.sign(PAYLOAD_HASH, otherSecret, { prehash: false }))
109
- Suites.verify('ECDSA-secp256k1', PAYLOAD_HASH, sig, PUBLIC_KEY)
110
- .should.equal(false)
111
- })
112
- })
@@ -1,181 +0,0 @@
1
- 'use strict'
2
-
3
- /* global describe, it */
4
-
5
- // RFC 6962 Merkle trees, for BRC-220 batch mode.
6
- //
7
- // The first version of foldPath here descended from the root by splitting at the largest
8
- // power of two, mirroring how root() builds the tree. It round-tripped perfectly for
9
- // powers of two and failed for 21 of the 45 leaves in trees of size 1..9 — which is
10
- // exactly why the published vectors below matter more than any round-trip test. A tree
11
- // that agrees with itself is not evidence.
12
-
13
- require('chai').should()
14
- var Merkle = require('../../lib/notaryhash/merkle')
15
- var Hash = require('../../lib/crypto/hash')
16
-
17
- // The canonical RFC 6962 / Certificate Transparency inputs.
18
- var D = ['', '00', '10', '2021', '3031', '40414243', '5051525354555657',
19
- '606162636465666768696a6b6c6d6e6f'].map(function (h) { return Buffer.from(h, 'hex') })
20
-
21
- describe('RFC 6962 Merkle tree', function () {
22
- describe('published vectors', function () {
23
- // External validation. Everything else in this file could be internally consistent
24
- // and wrong; these numbers come from outside this codebase.
25
- var EXPECTED = {
26
- 0: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
27
- 1: '6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d',
28
- 2: 'fac54203e7cc696cf0dfcb42c92a1d9dbaf70ad9e621f4bd8d98662f00e3c125',
29
- 4: 'd37ee418976dd95753c1c73862b9398fa2a2cf9b4ff0fdfe8b30cd95209614b7',
30
- 7: 'ddb89be403809e325750d3d263cd78929c2942b7942a34b77e122c9594a74c8c',
31
- 8: '5dc9da79a70659a9ad559cb701ded9a2ab9d823aad2f4960cfe370eff4604328'
32
- }
33
-
34
- Object.keys(EXPECTED).forEach(function (n) {
35
- it('MTH of ' + n + ' leaves matches the published root', function () {
36
- Merkle.root(D.slice(0, Number(n))).toString('hex').should.equal(EXPECTED[n])
37
- })
38
- })
39
- })
40
-
41
- describe('construction', function () {
42
- it('hashes the empty tree as SHA-256 of the empty string, not zeroes', function () {
43
- Merkle.root([]).toString('hex').should.equal(Hash.sha256(Buffer.alloc(0)).toString('hex'))
44
- Merkle.root([]).toString('hex').should.not.equal('00'.repeat(32))
45
- })
46
-
47
- it('domain-separates leaves from internal nodes', function () {
48
- Merkle.hashLeaf(Buffer.from('ab', 'hex')).toString('hex')
49
- .should.equal(Hash.sha256(Buffer.from('00ab', 'hex')).toString('hex'))
50
- Merkle.hashNode(Buffer.alloc(32, 1), Buffer.alloc(32, 2)).toString('hex')
51
- .should.equal(Hash.sha256(Buffer.concat([
52
- Buffer.from([0x01]), Buffer.alloc(32, 1), Buffer.alloc(32, 2)
53
- ])).toString('hex'))
54
- })
55
-
56
- // Without the 0x00/0x01 prefixes an internal node could be reinterpreted as a leaf,
57
- // which is the second-preimage attack Bitcoin's tree is open to.
58
- it('gives a leaf and a node over the same bytes different hashes', function () {
59
- var payload = Buffer.alloc(64, 7)
60
- Merkle.hashLeaf(payload).toString('hex')
61
- .should.not.equal(Merkle.hashNode(Buffer.alloc(32, 7), Buffer.alloc(32, 7)).toString('hex'))
62
- })
63
-
64
- // The split point, not the midpoint. For n=5 the RFC splits 4/1.
65
- it('splits at the largest power of two below n', function () {
66
- var expected = { 2: 1, 3: 2, 4: 2, 5: 4, 6: 4, 7: 4, 8: 4, 9: 8, 16: 8, 17: 16 }
67
- Object.keys(expected).forEach(function (n) {
68
- Merkle.largestPowerOfTwoBelow(Number(n)).should.equal(expected[n])
69
- })
70
- })
71
- })
72
-
73
- // The property the plan named as the one that would ship silently: a Bitcoin-style
74
- // tree over the same leaves must produce a DIFFERENT root.
75
- describe('is not the Bitcoin tree', function () {
76
- function bitcoinStyleRoot (leaves) {
77
- var level = leaves.map(function (l) { return Hash.sha256sha256(l) })
78
- while (level.length > 1) {
79
- var next = []
80
- for (var i = 0; i < level.length; i += 2) {
81
- var left = level[i]
82
- var right = i + 1 < level.length ? level[i + 1] : left // duplicated
83
- next.push(Hash.sha256sha256(Buffer.concat([left, right])))
84
- }
85
- level = next
86
- }
87
- return level[0]
88
- }
89
-
90
- it('produces a different root over the same leaves', function () {
91
- var leaves = D.slice(0, 4)
92
- Merkle.root(leaves).toString('hex')
93
- .should.not.equal(bitcoinStyleRoot(leaves).toString('hex'))
94
- })
95
-
96
- it('differs for an odd leaf count, where Bitcoin duplicates and RFC 6962 does not', function () {
97
- var leaves = D.slice(0, 3)
98
- Merkle.root(leaves).toString('hex')
99
- .should.not.equal(bitcoinStyleRoot(leaves).toString('hex'))
100
- })
101
- })
102
-
103
- describe('inclusion proofs', function () {
104
- it('round-trips every leaf for every tree size 1..33', function () {
105
- var failures = []
106
- for (var n = 1; n <= 33; n++) {
107
- var leaves = []
108
- for (var i = 0; i < n; i++) leaves.push(Buffer.from([i & 0xff, n & 0xff]))
109
- var root = Merkle.root(leaves)
110
- for (var j = 0; j < n; j++) {
111
- if (!Merkle.verifyInclusion(leaves[j], j, n, Merkle.path(leaves, j), root)) {
112
- failures.push('n=' + n + ' i=' + j)
113
- }
114
- }
115
- }
116
- failures.should.deep.equal([])
117
- })
118
-
119
- it('rejects a proof for the wrong leaf', function () {
120
- var leaves = D.slice(0, 8)
121
- var root = Merkle.root(leaves)
122
- Merkle.verifyInclusion(D[1], 0, 8, Merkle.path(leaves, 0), root).should.equal(false)
123
- })
124
-
125
- it('rejects a proof at the wrong index', function () {
126
- var leaves = D.slice(0, 8)
127
- var root = Merkle.root(leaves)
128
- Merkle.verifyInclusion(leaves[3], 4, 8, Merkle.path(leaves, 3), root).should.equal(false)
129
- })
130
-
131
- // leafCount changes the tree shape — but only for SOME indices. On an 8-leaf tree
132
- // claimed as 7, indices 0..5 fold identically and only 6 and 7 differ, because the
133
- // left region of the tree is the same shape either way.
134
- //
135
- // That is a property worth knowing rather than a defect: it means an inclusion proof
136
- // alone cannot be trusted to detect a wrong leafCount, which is why
137
- // NotaryHash.recordMatchesCertificate compares the certificate's count against the
138
- // authoritative u32be in the on-chain record instead of assuming the fold implies it.
139
- it('rejects a wrong leafCount where the shape differs', function () {
140
- var leaves = D.slice(0, 8)
141
- var root = Merkle.root(leaves)
142
- Merkle.verifyInclusion(leaves[6], 6, 7, Merkle.path(leaves, 6), root).should.equal(false)
143
- Merkle.verifyInclusion(leaves[7], 7, 7, Merkle.path(leaves, 7), root).should.equal(false)
144
- })
145
-
146
- it('does NOT catch a wrong leafCount where the shape is the same', function () {
147
- var leaves = D.slice(0, 8)
148
- var root = Merkle.root(leaves)
149
- // Documented, not desired. The on-chain leafCount is what closes this.
150
- Merkle.verifyInclusion(leaves[3], 3, 7, Merkle.path(leaves, 3), root).should.equal(true)
151
- })
152
-
153
- it('rejects a truncated or padded path', function () {
154
- var leaves = D.slice(0, 8)
155
- var root = Merkle.root(leaves)
156
- var path = Merkle.path(leaves, 3)
157
- Merkle.verifyInclusion(leaves[3], 3, 8, path.slice(0, -1), root).should.equal(false)
158
- Merkle.verifyInclusion(leaves[3], 3, 8, path.concat([Buffer.alloc(32)]), root).should.equal(false)
159
- })
160
-
161
- it('rejects a tampered sibling', function () {
162
- var leaves = D.slice(0, 8)
163
- var root = Merkle.root(leaves)
164
- var path = Merkle.path(leaves, 3)
165
- path[0] = Buffer.alloc(32, 0xff)
166
- Merkle.verifyInclusion(leaves[3], 3, 8, path, root).should.equal(false)
167
- })
168
-
169
- it('returns a strict boolean and never throws on malformed input', function () {
170
- Merkle.verifyInclusion(D[0], 0, 1, [], Merkle.root([D[0]])).should.be.a('boolean')
171
- Merkle.verifyInclusion(null, 0, 1, [], Buffer.alloc(32)).should.equal(false)
172
- Merkle.verifyInclusion(D[0], 5, 1, [], Buffer.alloc(32)).should.equal(false)
173
- Merkle.verifyInclusion(D[0], 0, 1, [], 'not-a-buffer').should.equal(false)
174
- })
175
-
176
- it('gives an empty path for a single-leaf tree', function () {
177
- Merkle.path([D[0]], 0).should.deep.equal([])
178
- Merkle.verifyInclusion(D[0], 0, 1, [], Merkle.hashLeaf(D[0])).should.equal(true)
179
- })
180
- })
181
- })