@smartledger/bsv 9.0.0 → 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.
- package/CHANGELOG.md +137 -0
- package/README.md +152 -1041
- package/STABILITY.md +134 -0
- package/bsv-gdaf.min.js +53 -48
- package/bsv-ltp.min.js +23 -18
- package/bsv-smartcontract.min.js +18 -18
- package/bsv.bundle.js +53 -48
- package/bsv.min.js +53 -48
- package/docs/MODULE_REFERENCE_COMPLETE.md +27 -27
- 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/docs/proposals/10.0.0-package-split.md +131 -0
- package/index.js +5 -0
- package/lib/block/merkleblock.js +19 -5
- package/lib/hdprivatekey.js +15 -0
- package/lib/script/interpreter.js +109 -1
- package/lib/smart_contract/builder.js +14 -0
- package/lib/util/deprecate.js +159 -0
- package/package.json +19 -82
- package/version.js +1 -1
- package/.mocharc.json +0 -5
- package/test/address.js +0 -629
- package/test/block/block.js +0 -239
- package/test/block/blockheader.js +0 -270
- package/test/block/merkleblock.js +0 -207
- package/test/build/bundle_crypto_shim.js +0 -104
- package/test/build/bundle_externals.js +0 -125
- package/test/build/bundle_smoke.js +0 -55
- package/test/build/esbuild_main.js +0 -65
- package/test/build/esm_wrapper.js +0 -97
- package/test/build/exports_resolution.js +0 -84
- package/test/build/version_sync.js +0 -14
- package/test/cli/smoke.js +0 -261
- package/test/consensus/base58-vectors.js +0 -120
- package/test/consensus/sv-script-vectors.js +0 -81
- package/test/consensus/sv-sighash-vectors.js +0 -51
- package/test/consensus/sv-tx-vectors.js +0 -48
- package/test/credentials-test.js +0 -332
- package/test/crypto/backend_selection.js +0 -126
- package/test/crypto/bn.js +0 -168
- package/test/crypto/ecdsa.js +0 -426
- package/test/crypto/elliptic-fixed.js +0 -61
- package/test/crypto/hash.browser.js +0 -121
- package/test/crypto/hash.js +0 -122
- package/test/crypto/point.js +0 -193
- package/test/crypto/random.js +0 -105
- package/test/crypto/security.js +0 -176
- package/test/crypto/shamir.js +0 -177
- package/test/crypto/shamir_rngcap.js +0 -29
- package/test/crypto/signature.js +0 -399
- package/test/data/bip69.json +0 -215
- package/test/data/bitcoin-sv/README.md +0 -170
- package/test/data/bitcoin-sv/base58_encode_decode.json +0 -14
- package/test/data/bitcoin-sv/base58_keys_invalid.json +0 -152
- package/test/data/bitcoin-sv/base58_keys_valid.json +0 -452
- package/test/data/bitcoin-sv/script_tests.json +0 -2591
- package/test/data/bitcoin-sv/sighash.json +0 -1003
- package/test/data/bitcoin-sv/tx_invalid.json +0 -285
- package/test/data/bitcoin-sv/tx_valid.json +0 -367
- package/test/data/bitcoind/base58_keys_invalid.json +0 -152
- package/test/data/bitcoind/base58_keys_valid.json +0 -452
- package/test/data/bitcoind/blocks.json +0 -27
- package/test/data/bitcoind/script_tests.json +0 -2244
- package/test/data/bitcoind/sig_canonical.json +0 -7
- package/test/data/bitcoind/sig_noncanonical.json +0 -22
- package/test/data/bitcoind/tx_invalid.json +0 -177
- package/test/data/bitcoind/tx_valid.json +0 -224
- package/test/data/blk86756-testnet.dat +0 -0
- package/test/data/blk86756-testnet.js +0 -12
- package/test/data/blk86756-testnet.json +0 -684
- package/test/data/brc220-batch-vector.json +0 -129
- package/test/data/ecdsa.json +0 -230
- package/test/data/merkleblocks.js +0 -486
- package/test/data/messages.json +0 -22
- package/test/data/sighash.json +0 -1004
- package/test/data/tx_creation.json +0 -85
- package/test/didweb/relationships.js +0 -144
- package/test/ecies/bitcore-ecies.js +0 -178
- package/test/ecies/electrum-ecies.js +0 -206
- package/test/encoding/base58.js +0 -131
- package/test/encoding/base58check.js +0 -145
- package/test/encoding/bufferreader.js +0 -328
- package/test/encoding/bufferwriter.js +0 -160
- package/test/encoding/varint.js +0 -104
- package/test/gdaf/anchor_no_key_leak.js +0 -146
- package/test/gdaf/anchor_spv.js +0 -163
- package/test/gdaf/canonicalization.js +0 -106
- package/test/gdaf/canonicalize.js +0 -140
- package/test/gdaf/zk_prover.js +0 -204
- package/test/hdkeys.js +0 -365
- package/test/hdprivatekey.js +0 -339
- package/test/hdpublickey.js +0 -293
- package/test/index.js +0 -16
- package/test/ltp/ids.js +0 -74
- package/test/ltp/right.js +0 -198
- package/test/ltp/verify_failclosed.js +0 -134
- package/test/message/message.js +0 -190
- package/test/mnemonic/data/fixtures.json +0 -300
- package/test/mnemonic/mnemonic.js +0 -277
- package/test/mnemonic/mocha.opts +0 -1
- package/test/mnemonic/pbkdf2.test.js +0 -43
- package/test/networks.js +0 -207
- package/test/notaryhash/batch_leaf.js +0 -140
- package/test/notaryhash/batch_vector.js +0 -282
- package/test/notaryhash/certificate.js +0 -249
- package/test/notaryhash/encoding.js +0 -186
- package/test/notaryhash/interop.js +0 -112
- package/test/notaryhash/merkle.js +0 -181
- package/test/notaryhash/script.js +0 -270
- package/test/notaryhash/verify.js +0 -342
- package/test/opcode.js +0 -186
- package/test/ordinals/bsv20.js +0 -337
- package/test/ordinals/inscription.js +0 -329
- package/test/ordinals/ordlock.js +0 -567
- package/test/privatekey.js +0 -540
- package/test/publickey.js +0 -411
- package/test/regressions.js +0 -215
- package/test/script/chronicle.js +0 -543
- package/test/script/defaults.js +0 -160
- package/test/script/genesis_limits.js +0 -203
- package/test/script/interpreter.js +0 -776
- package/test/script/script.js +0 -1259
- package/test/script/string_ops.js +0 -88
- package/test/security/fail_closed_contracts.js +0 -104
- package/test/security/threat_model_coverage.js +0 -31
- package/test/smart_contract/covenants.js +0 -207
- package/test/smart_contract/dsl_debugger.js +0 -92
- package/test/smart_contract/extract_field.js +0 -61
- package/test/smart_contract/nonenforcing_guard.js +0 -44
- package/test/smart_contract/ordinal_transfer.js +0 -71
- package/test/smart_contract/preimage.js +0 -98
- package/test/smart_contract/sighash_marketplace.js +0 -98
- package/test/smart_contract/token_generalized.js +0 -175
- package/test/spv/headerchain.js +0 -86
- package/test/spv/merkleproof.js +0 -133
- package/test/statuslist/failclosed.js +0 -78
- package/test/transaction/deserialize.js +0 -33
- package/test/transaction/input/input.js +0 -92
- package/test/transaction/input/multisig.js +0 -174
- package/test/transaction/input/multisigscripthash.js +0 -111
- package/test/transaction/input/publickey.js +0 -68
- package/test/transaction/input/publickeyhash.js +0 -59
- package/test/transaction/output.js +0 -185
- package/test/transaction/sighash.js +0 -91
- package/test/transaction/signature.js +0 -127
- package/test/transaction/transaction.js +0 -1299
- package/test/transaction/unspentoutput.js +0 -97
- package/test/types/dts_drift.js +0 -124
- package/test/types/surface_honesty.js +0 -102
- package/test/util/id.js +0 -72
- package/test/util/js.js +0 -76
- package/test/util/preconditions.js +0 -79
- package/test/vcjwt/interop.js +0 -126
package/test/networks.js
DELETED
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
var expect = require('chai').expect
|
|
4
|
-
var should = require('chai').should()
|
|
5
|
-
var bsv = require('..')
|
|
6
|
-
var networks = bsv.Networks
|
|
7
|
-
|
|
8
|
-
describe('Networks', function () {
|
|
9
|
-
var customnet
|
|
10
|
-
|
|
11
|
-
it('should contain all Networks', function () {
|
|
12
|
-
should.exist(networks.livenet)
|
|
13
|
-
should.exist(networks.testnet)
|
|
14
|
-
should.exist(networks.stn)
|
|
15
|
-
should.exist(networks.defaultNetwork)
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
it('should be able to define a custom Network', function () {
|
|
19
|
-
var custom = {
|
|
20
|
-
name: 'customnet',
|
|
21
|
-
alias: 'mynet',
|
|
22
|
-
pubkeyhash: 0x10,
|
|
23
|
-
privatekey: 0x90,
|
|
24
|
-
scripthash: 0x08,
|
|
25
|
-
xpubkey: 0x0278b20e,
|
|
26
|
-
xprivkey: 0x0278ade4,
|
|
27
|
-
networkMagic: 0xe7beb4d4,
|
|
28
|
-
port: 20001,
|
|
29
|
-
dnsSeeds: [
|
|
30
|
-
'localhost',
|
|
31
|
-
'mynet.localhost'
|
|
32
|
-
]
|
|
33
|
-
}
|
|
34
|
-
networks.add(custom)
|
|
35
|
-
customnet = networks.get('customnet')
|
|
36
|
-
for (var key in custom) {
|
|
37
|
-
if (key !== 'networkMagic') {
|
|
38
|
-
customnet[key].should.equal(custom[key])
|
|
39
|
-
} else {
|
|
40
|
-
var expected = Buffer.from('e7beb4d4', 'hex')
|
|
41
|
-
customnet[key].should.deep.equal(expected)
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
it('should have network magic for testnet', function () {
|
|
47
|
-
var testnet = networks.get('testnet')
|
|
48
|
-
Buffer.isBuffer(testnet.networkMagic).should.equal(true)
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
it('should have network magic for stn', function () {
|
|
52
|
-
var stn = networks.get('stn')
|
|
53
|
-
Buffer.isBuffer(stn.networkMagic).should.equal(true)
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
it('can remove a custom network', function () {
|
|
57
|
-
networks.remove(customnet)
|
|
58
|
-
var net = networks.get('customnet')
|
|
59
|
-
should.equal(net, undefined)
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
it('should not set a network map for an undefined value', function () {
|
|
63
|
-
var custom = {
|
|
64
|
-
name: 'somenet',
|
|
65
|
-
pubkeyhash: 0x13,
|
|
66
|
-
privatekey: 0x93,
|
|
67
|
-
scripthash: 0x11,
|
|
68
|
-
xpubkey: 0x0278b20f,
|
|
69
|
-
xprivkey: 0x0278ade5,
|
|
70
|
-
networkMagic: 0xe7beb4d5,
|
|
71
|
-
port: 20008,
|
|
72
|
-
dnsSeeds: [
|
|
73
|
-
'somenet.localhost'
|
|
74
|
-
]
|
|
75
|
-
}
|
|
76
|
-
networks.add(custom)
|
|
77
|
-
var network = networks.get(undefined)
|
|
78
|
-
should.not.exist(network)
|
|
79
|
-
var somenet = networks.get('somenet')
|
|
80
|
-
should.exist(somenet)
|
|
81
|
-
somenet.name.should.equal('somenet')
|
|
82
|
-
networks.remove(somenet)
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
var constants = ['name', 'alias', 'pubkeyhash', 'scripthash', 'xpubkey', 'xprivkey']
|
|
86
|
-
|
|
87
|
-
constants.forEach(function (key) {
|
|
88
|
-
it('should have constant ' + key + ' for livenet, testnet and stn', function () {
|
|
89
|
-
networks.testnet.hasOwnProperty(key).should.equal(true)
|
|
90
|
-
networks.livenet.hasOwnProperty(key).should.equal(true)
|
|
91
|
-
networks.stn.hasOwnProperty(key).should.equal(true)
|
|
92
|
-
})
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
it('tests only for the specified key', function () {
|
|
96
|
-
expect(networks.get(0x6f, 'pubkeyhash')).to.equal(networks.testnet)
|
|
97
|
-
expect(networks.get(0x6f, 'privatekey')).to.equal(undefined)
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
it('can test for multiple keys', function () {
|
|
101
|
-
expect(networks.get(0x6f, ['pubkeyhash', 'scripthash'])).to.equal(networks.testnet)
|
|
102
|
-
expect(networks.get(0xc4, ['pubkeyhash', 'scripthash'])).to.equal(networks.testnet)
|
|
103
|
-
expect(networks.get(0x6f, ['privatekey', 'port'])).to.equal(undefined)
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
it('should have regtest network', function () {
|
|
107
|
-
expect(networks.get('regtest').name).to.equal('regtest')
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
it('should have testnet network', function () {
|
|
111
|
-
expect(networks.get('testnet').name).to.equal('testnet')
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
it('should have stn network', function () {
|
|
115
|
-
expect(networks.get('stn').name).to.equal('stn')
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
it('should have livenet network', function () {
|
|
119
|
-
expect(networks.get('livenet').name).to.equal('livenet')
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
it('should have bchtest cashAddrPrefix', function () {
|
|
123
|
-
expect(networks.get('testnet').cashAddrPrefix).to.equal('bchtest')
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
it('should have bchreg cashAddrPrefix', function () {
|
|
127
|
-
expect(networks.get('regtest').cashAddrPrefix).to.equal('bchreg')
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
it('should have bchreg cashAddrPrefix after enableRegtest is called', function () {
|
|
131
|
-
var network = networks.get('testnet')
|
|
132
|
-
networks.enableRegtest()
|
|
133
|
-
expect(network.cashAddrPrefix).to.equal('bchreg')
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
it('should have bchtest cashAddrPrefix after disableRegtest is called', function () {
|
|
137
|
-
var network = networks.get('testnet')
|
|
138
|
-
networks.disableRegtest()
|
|
139
|
-
expect(network.cashAddrPrefix).to.equal('bchtest')
|
|
140
|
-
})
|
|
141
|
-
it('should have bsvstn cashAddrPrefix after enableStn is called', function () {
|
|
142
|
-
var network = networks.get('testnet')
|
|
143
|
-
networks.enableStn()
|
|
144
|
-
expect(network.cashAddrPrefix).to.equal('bsvstn')
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
it('should have bchtest cashAddrPrefix after disableStn is called', function () {
|
|
148
|
-
var network = networks.get('testnet')
|
|
149
|
-
networks.disableStn()
|
|
150
|
-
expect(network.cashAddrPrefix).to.equal('bchtest')
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
it('converts to string using the "name" property', function () {
|
|
154
|
-
networks.livenet.toString().should.equal('livenet')
|
|
155
|
-
})
|
|
156
|
-
|
|
157
|
-
it('network object should be immutable', function () {
|
|
158
|
-
expect(networks.testnet.name).to.equal('testnet')
|
|
159
|
-
var fn = function () { networks.testnet.name = 'livenet' }
|
|
160
|
-
expect(fn).to.throw(TypeError)
|
|
161
|
-
})
|
|
162
|
-
// The `cashAddrPrefixArray` getter has three branches; only the STN one reached for
|
|
163
|
-
// the helper on the STN data object instead of the module-level function, so reading
|
|
164
|
-
// it with STN enabled threw `STN.cashAddrPrefixToArray is not a function`. The tests
|
|
165
|
-
// above cover `cashAddrPrefix` (the string) but never the array getter, which is why
|
|
166
|
-
// a live TypeError survived a 4,597-test suite.
|
|
167
|
-
describe('cashAddrPrefixArray across the regtest/stn toggles', function () {
|
|
168
|
-
// enableStn/enableRegtest mutate the shared testnet object, so without a reset the
|
|
169
|
-
// flags leak into every later test in the run.
|
|
170
|
-
afterEach(function () {
|
|
171
|
-
networks.disableStn()
|
|
172
|
-
networks.disableRegtest()
|
|
173
|
-
})
|
|
174
|
-
|
|
175
|
-
function assertPrefixArray (arr) {
|
|
176
|
-
Array.isArray(arr).should.equal(true)
|
|
177
|
-
arr.length.should.be.above(0)
|
|
178
|
-
arr.forEach(function (n) { n.should.be.a('number') })
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
it('works with stn enabled', function () {
|
|
182
|
-
networks.enableStn()
|
|
183
|
-
assertPrefixArray(networks.testnet.cashAddrPrefixArray)
|
|
184
|
-
})
|
|
185
|
-
|
|
186
|
-
it('works with regtest enabled', function () {
|
|
187
|
-
networks.enableRegtest()
|
|
188
|
-
assertPrefixArray(networks.testnet.cashAddrPrefixArray)
|
|
189
|
-
})
|
|
190
|
-
|
|
191
|
-
it('works with neither enabled', function () {
|
|
192
|
-
assertPrefixArray(networks.testnet.cashAddrPrefixArray)
|
|
193
|
-
})
|
|
194
|
-
|
|
195
|
-
it('returns a different prefix array per mode', function () {
|
|
196
|
-
var plain = networks.testnet.cashAddrPrefixArray.join(',')
|
|
197
|
-
networks.enableStn()
|
|
198
|
-
var stn = networks.testnet.cashAddrPrefixArray.join(',')
|
|
199
|
-
networks.disableStn()
|
|
200
|
-
networks.enableRegtest()
|
|
201
|
-
var regtest = networks.testnet.cashAddrPrefixArray.join(',')
|
|
202
|
-
stn.should.not.equal(plain)
|
|
203
|
-
regtest.should.not.equal(plain)
|
|
204
|
-
stn.should.not.equal(regtest)
|
|
205
|
-
})
|
|
206
|
-
})
|
|
207
|
-
})
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
/* global describe, it */
|
|
4
|
-
|
|
5
|
-
// BRC-220 batch mode: what is in a Merkle leaf.
|
|
6
|
-
//
|
|
7
|
-
// The spec writes `leaf = SHA256(0x00 ‖ d)` and never binds `d`. That is RFC 6962's own
|
|
8
|
-
// generic notation for the construction, quoted to identify WHICH tree — domain
|
|
9
|
-
// separation, the power-of-two split, no duplicated last leaf — not to define the leaf's
|
|
10
|
-
// contents. `canonicalBytes` appears once in the whole document, in the proofHash
|
|
11
|
-
// definition, and nowhere in the batch text.
|
|
12
|
-
//
|
|
13
|
-
// This library reads `d` as proofHash. The alternative reading, d = canonicalBytes, is
|
|
14
|
-
// equally sound cryptographically and produces a DIFFERENT root — so the two do not
|
|
15
|
-
// interoperate, and the failure is silent: the fold just does not reach the root.
|
|
16
|
-
//
|
|
17
|
-
// These tests pin the choice, and pin the divergence too. A comment saying "we use
|
|
18
|
-
// proofHash" would not fail if someone changed it; this does.
|
|
19
|
-
//
|
|
20
|
-
// Reasoning and proposed spec text: docs/BRC220_BATCH_LEAF_AMENDMENT.md
|
|
21
|
-
|
|
22
|
-
require('chai').should()
|
|
23
|
-
var NotaryHash = require('../../lib/notaryhash')
|
|
24
|
-
var Merkle = require('../../lib/notaryhash/merkle')
|
|
25
|
-
var Hash = require('../../lib/crypto/hash')
|
|
26
|
-
var NotaryScript = require('../../lib/notaryhash/script')
|
|
27
|
-
|
|
28
|
-
// Four fixed proofHash values, standing in for a four-proof batch.
|
|
29
|
-
var PROOF_HASHES = [
|
|
30
|
-
'00'.repeat(31) + '01',
|
|
31
|
-
'00'.repeat(31) + '02',
|
|
32
|
-
'ab'.repeat(32),
|
|
33
|
-
'ff'.repeat(32)
|
|
34
|
-
].map(function (h) { return Buffer.from(h, 'hex') })
|
|
35
|
-
|
|
36
|
-
function batchCertificate (index, leaves) {
|
|
37
|
-
return {
|
|
38
|
-
mode: NotaryScript.MODE.BATCH,
|
|
39
|
-
proofHash: leaves[index].toString('hex'),
|
|
40
|
-
merkle: {
|
|
41
|
-
root: Merkle.root(leaves).toString('hex'),
|
|
42
|
-
leafIndex: index,
|
|
43
|
-
leafCount: leaves.length,
|
|
44
|
-
path: Merkle.path(leaves, index).map(function (n) { return n.toString('hex') })
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
describe('BRC-220 batch leaf', function () {
|
|
50
|
-
describe('the leaf datum is proofHash', function () {
|
|
51
|
-
it('a leaf is SHA-256(0x00 || proofHash)', function () {
|
|
52
|
-
var proofHash = PROOF_HASHES[0]
|
|
53
|
-
Merkle.hashLeaf(proofHash).toString('hex').should.equal(
|
|
54
|
-
Hash.sha256(Buffer.concat([Buffer.from([0x00]), proofHash])).toString('hex')
|
|
55
|
-
)
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
// Stated the long way round, because this is the identity the spec leaves open.
|
|
59
|
-
it('is therefore SHA-256(0x00 || SHA-256(canonicalBytes))', function () {
|
|
60
|
-
var canonicalBytes = Buffer.from('any canonical proof bytes at all')
|
|
61
|
-
var proofHash = Hash.sha256(canonicalBytes)
|
|
62
|
-
Merkle.hashLeaf(proofHash).toString('hex').should.equal(
|
|
63
|
-
Hash.sha256(Buffer.concat([
|
|
64
|
-
Buffer.from([0x00]), Hash.sha256(canonicalBytes)
|
|
65
|
-
])).toString('hex')
|
|
66
|
-
)
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
it('verifies inclusion for every leaf of a four-proof batch', function () {
|
|
70
|
-
for (var i = 0; i < PROOF_HASHES.length; i++) {
|
|
71
|
-
var report = NotaryHash.verifyBatchInclusion(batchCertificate(i, PROOF_HASHES))
|
|
72
|
-
report.valid.should.equal(true, 'leaf ' + i + ' should verify')
|
|
73
|
-
report.errors.should.deep.equal([])
|
|
74
|
-
}
|
|
75
|
-
})
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
describe('the two readings of `d` are not interchangeable', function () {
|
|
79
|
-
var canonicalBytesSet = [
|
|
80
|
-
Buffer.from('proof one canonical bytes'),
|
|
81
|
-
Buffer.from('proof two canonical bytes'),
|
|
82
|
-
Buffer.from('proof three canonical bytes'),
|
|
83
|
-
Buffer.from('proof four canonical bytes')
|
|
84
|
-
]
|
|
85
|
-
var proofHashes = canonicalBytesSet.map(function (b) { return Hash.sha256(b) })
|
|
86
|
-
|
|
87
|
-
it('produces a different root under each reading', function () {
|
|
88
|
-
Merkle.root(proofHashes).toString('hex')
|
|
89
|
-
.should.not.equal(Merkle.root(canonicalBytesSet).toString('hex'))
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
// The interoperability failure, made concrete. This is what a batch built by an
|
|
93
|
-
// implementation using the other reading would look like to this verifier.
|
|
94
|
-
it('REJECTS a certificate whose tree was built over canonicalBytes', function () {
|
|
95
|
-
var certificate = {
|
|
96
|
-
mode: NotaryScript.MODE.BATCH,
|
|
97
|
-
proofHash: proofHashes[1].toString('hex'),
|
|
98
|
-
merkle: {
|
|
99
|
-
// Root and path from the canonicalBytes tree — the other reading.
|
|
100
|
-
root: Merkle.root(canonicalBytesSet).toString('hex'),
|
|
101
|
-
leafIndex: 1,
|
|
102
|
-
leafCount: 4,
|
|
103
|
-
path: Merkle.path(canonicalBytesSet, 1).map(function (n) { return n.toString('hex') })
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
var report = NotaryHash.verifyBatchInclusion(certificate)
|
|
107
|
-
report.valid.should.equal(false)
|
|
108
|
-
report.errors.should.deep.equal(['merkle inclusion proof does not fold to the batch root'])
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
// Both are sound; neither is being called wrong. The point is only that a choice had
|
|
112
|
-
// to be made and that it is checkable.
|
|
113
|
-
it('both readings are domain-separated and second-preimage resistant', function () {
|
|
114
|
-
var leafBytes = Buffer.alloc(64, 7)
|
|
115
|
-
Merkle.hashLeaf(leafBytes).toString('hex')
|
|
116
|
-
.should.not.equal(Merkle.hashNode(Buffer.alloc(32, 7), Buffer.alloc(32, 7)).toString('hex'))
|
|
117
|
-
})
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
describe('the verdict is a strict report, not a truthy object', function () {
|
|
121
|
-
it('reports valid:false rather than throwing on a malformed merkle object', function () {
|
|
122
|
-
var report = NotaryHash.verifyBatchInclusion({
|
|
123
|
-
mode: NotaryScript.MODE.BATCH,
|
|
124
|
-
proofHash: PROOF_HASHES[0].toString('hex'),
|
|
125
|
-
merkle: { root: 'not-hex', leafIndex: 0, leafCount: 1, path: [] }
|
|
126
|
-
})
|
|
127
|
-
report.valid.should.equal(false)
|
|
128
|
-
report.errors.length.should.be.above(0)
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
it('rejects a non-batch certificate by mode', function () {
|
|
132
|
-
var report = NotaryHash.verifyBatchInclusion({
|
|
133
|
-
mode: NotaryScript.MODE.FULL,
|
|
134
|
-
proofHash: PROOF_HASHES[0].toString('hex')
|
|
135
|
-
})
|
|
136
|
-
report.valid.should.equal(false)
|
|
137
|
-
report.errors.should.deep.equal(['certificate is not in batch mode'])
|
|
138
|
-
})
|
|
139
|
-
})
|
|
140
|
-
})
|
|
@@ -1,282 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
/* global describe, it */
|
|
4
|
-
|
|
5
|
-
// The BRC-220 batch-mode golden vector.
|
|
6
|
-
//
|
|
7
|
-
// BRC-220 writes the batch leaf as `SHA256(0x00 ‖ d)` and never says what `d` is. Prose
|
|
8
|
-
// cannot reliably close that gap — two implementers read the same sentence and chose
|
|
9
|
-
// differently — so this file holds a concrete vector that any implementation can check
|
|
10
|
-
// itself against.
|
|
11
|
-
//
|
|
12
|
-
// What this vector is: a CONFORMANCE TARGET produced by this library. It is not external
|
|
13
|
-
// validation, and nothing here proves our reading is the one the spec intended. What makes
|
|
14
|
-
// it worth having is that the tree underneath it IS externally anchored — the RFC 6962
|
|
15
|
-
// construction is checked against the published Certificate Transparency roots in
|
|
16
|
-
// merkle.js. Given a tree that provably matches RFC 6962, this vector pins the one thing
|
|
17
|
-
// the spec leaves open.
|
|
18
|
-
//
|
|
19
|
-
// These tests rebuild every value from the recorded inputs rather than reading the
|
|
20
|
-
// recorded outputs back. A test that asserts `vector.root === vector.root` would pass
|
|
21
|
-
// forever while proving nothing, which is the failure mode this repository has fixed most
|
|
22
|
-
// often.
|
|
23
|
-
//
|
|
24
|
-
// See docs/BRC220_BATCH_LEAF_AMENDMENT.md and tools/gen-brc220-batch-vector.js.
|
|
25
|
-
|
|
26
|
-
require('chai').should()
|
|
27
|
-
var vector = require('../data/brc220-batch-vector.json')
|
|
28
|
-
var Hash = require('../../lib/crypto/hash')
|
|
29
|
-
var Encoding = require('../../lib/notaryhash/encoding')
|
|
30
|
-
var Merkle = require('../../lib/notaryhash/merkle')
|
|
31
|
-
var Suites = require('../../lib/notaryhash/suites')
|
|
32
|
-
var NotaryHash = require('../../lib/notaryhash')
|
|
33
|
-
var NotaryScript = require('../../lib/notaryhash/script')
|
|
34
|
-
var BN = require('../../lib/crypto/bn')
|
|
35
|
-
var PrivateKey = require('../../lib/privatekey')
|
|
36
|
-
// Deliberately a SECOND implementation: see the independent-verification test below.
|
|
37
|
-
var { secp256k1 } = require('@noble/curves/secp256k1.js')
|
|
38
|
-
|
|
39
|
-
function hex (s) { return Buffer.from(s, 'hex') }
|
|
40
|
-
|
|
41
|
-
describe('BRC-220 batch golden vector', function () {
|
|
42
|
-
describe('the vector describes the shape it claims', function () {
|
|
43
|
-
it('is five leaves, which is deliberate', function () {
|
|
44
|
-
// n=5 splits 4/1 under RFC 6962 (largest power of two BELOW n), where a midpoint
|
|
45
|
-
// split would give 2/3 and a different root. Odd n also exercises the rule that the
|
|
46
|
-
// last leaf is never duplicated. A power-of-two vector would catch neither.
|
|
47
|
-
vector.leafCount.should.equal(5)
|
|
48
|
-
vector.proofs.length.should.equal(5)
|
|
49
|
-
Merkle.largestPowerOfTwoBelow(5).should.equal(4)
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
it('names its tree and leaf datum explicitly', function () {
|
|
53
|
-
vector.tree.should.equal('RFC 6962')
|
|
54
|
-
vector.leafDatum.should.equal('proofHash')
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
// The 4/1 split shows up in the path lengths: the lone right-hand leaf needs one
|
|
58
|
-
// sibling, the four on the left need three. If a future change altered the split rule,
|
|
59
|
-
// this would move before any root did.
|
|
60
|
-
it('has path lengths matching a 4/1 split', function () {
|
|
61
|
-
vector.proofs.map(function (p) { return p.path.length })
|
|
62
|
-
.should.deep.equal([3, 3, 3, 3, 1])
|
|
63
|
-
})
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
describe('every input is derived, so the vector is reproducible', function () {
|
|
67
|
-
it('rebuilds each payloadHash from its recorded preimage', function () {
|
|
68
|
-
vector.proofs.forEach(function (p) {
|
|
69
|
-
Hash.sha256(Buffer.from(p.payloadPreimage, 'utf8')).toString('hex')
|
|
70
|
-
.should.equal(p.payloadHash, 'leaf ' + p.leafIndex)
|
|
71
|
-
})
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
it('records the preimage labels the generator actually uses', function () {
|
|
75
|
-
vector.proofs.forEach(function (p, i) {
|
|
76
|
-
p.keyPreimage.should.equal('BRC-220/batch-vector/key/' + i)
|
|
77
|
-
p.payloadPreimage.should.equal('BRC-220/batch-vector/payload/' + i)
|
|
78
|
-
})
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
it('uses raw encoding: 64-byte signatures and 33-byte compressed keys', function () {
|
|
82
|
-
vector.proofs.forEach(function (p) {
|
|
83
|
-
p.encoding.should.equal('raw')
|
|
84
|
-
hex(p.signature).length.should.equal(64, 'leaf ' + p.leafIndex + ' signature')
|
|
85
|
-
hex(p.publicKey).length.should.equal(33, 'leaf ' + p.leafIndex + ' publicKey')
|
|
86
|
-
})
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
it('rebuilds createdAtUnix from the recorded ISO timestamp', function () {
|
|
90
|
-
vector.proofs.forEach(function (p) {
|
|
91
|
-
Encoding.toUnixSeconds(p.createdAt).should.equal(p.createdAtUnix)
|
|
92
|
-
})
|
|
93
|
-
})
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
describe('every proof stands on its own', function () {
|
|
97
|
-
// Each signature must verify independently. Without this the vector could pin a tree
|
|
98
|
-
// built over five values that are not valid proofs at all.
|
|
99
|
-
it('verifies all five signatures under the named suite', function () {
|
|
100
|
-
vector.proofs.forEach(function (p) {
|
|
101
|
-
Suites.verify(p.algorithm, hex(p.payloadHash), hex(p.signature), hex(p.publicKey))
|
|
102
|
-
.should.equal(true, 'leaf ' + p.leafIndex + ' signature must verify')
|
|
103
|
-
})
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
// THE test this file most needs, and the one it originally lacked.
|
|
107
|
-
//
|
|
108
|
-
// The assertion above uses our own suite, and the generator uses our own signer, so
|
|
109
|
-
// the two agree by construction. The first version of this vector was generated with
|
|
110
|
-
// `endian: 'little'` — matching a verifier bug — and every signature in it was
|
|
111
|
-
// unverifiable by any other implementation. Both tests above still passed, because
|
|
112
|
-
// both sides of the comparison were wrong in the same direction.
|
|
113
|
-
//
|
|
114
|
-
// @noble/curves shares no signing or verification code with lib/crypto/ecdsa.js. If
|
|
115
|
-
// this vector is ever regenerated by a signer that disagrees with the rest of the
|
|
116
|
-
// world, this is the assertion that says so.
|
|
117
|
-
//
|
|
118
|
-
// { prehash: false } is REQUIRED: noble v2 hashes its input by default, so omitting
|
|
119
|
-
// it would compare our signature over `payloadHash` against noble's over
|
|
120
|
-
// `sha256(payloadHash)` — a mismatch that looks exactly like the bug this guards.
|
|
121
|
-
it('verifies all five signatures under an INDEPENDENT implementation', function () {
|
|
122
|
-
vector.proofs.forEach(function (p) {
|
|
123
|
-
secp256k1.verify(hex(p.signature), hex(p.payloadHash), hex(p.publicKey), { prehash: false })
|
|
124
|
-
.should.equal(true,
|
|
125
|
-
'leaf ' + p.leafIndex + ' signature must verify under @noble/curves, not just ours')
|
|
126
|
-
})
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
it('derives each public key from the recorded key preimage', function () {
|
|
130
|
-
// Ties the published public keys back to their labelled preimages, so a third party
|
|
131
|
-
// can reproduce the keys and not merely check the signatures against them.
|
|
132
|
-
vector.proofs.forEach(function (p) {
|
|
133
|
-
var priv = PrivateKey.fromObject({
|
|
134
|
-
bn: BN.fromBuffer(Hash.sha256(Buffer.from(p.keyPreimage, 'utf8'))),
|
|
135
|
-
compressed: true,
|
|
136
|
-
network: 'livenet'
|
|
137
|
-
})
|
|
138
|
-
priv.publicKey.toBuffer().toString('hex')
|
|
139
|
-
.should.equal(p.publicKey, 'leaf ' + p.leafIndex + ' public key must derive from its preimage')
|
|
140
|
-
})
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
it('rebuilds each canonicalBytes from the recorded fields', function () {
|
|
144
|
-
vector.proofs.forEach(function (p) {
|
|
145
|
-
Encoding.canonicalBytes({
|
|
146
|
-
algorithm: p.algorithm,
|
|
147
|
-
hashAlgorithm: p.hashAlgorithm,
|
|
148
|
-
payloadHash: hex(p.payloadHash),
|
|
149
|
-
publicKey: hex(p.publicKey),
|
|
150
|
-
signature: hex(p.signature),
|
|
151
|
-
createdAtUnix: p.createdAtUnix
|
|
152
|
-
}).toString('hex').should.equal(p.canonicalBytes, 'leaf ' + p.leafIndex)
|
|
153
|
-
})
|
|
154
|
-
})
|
|
155
|
-
|
|
156
|
-
it('rebuilds each proofHash as SHA-256 of those canonical bytes', function () {
|
|
157
|
-
vector.proofs.forEach(function (p) {
|
|
158
|
-
Hash.sha256(hex(p.canonicalBytes)).toString('hex')
|
|
159
|
-
.should.equal(p.proofHash, 'leaf ' + p.leafIndex)
|
|
160
|
-
})
|
|
161
|
-
})
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
describe('the leaf definition the spec leaves open', function () {
|
|
165
|
-
it('rebuilds each leaf as SHA-256(0x00 || proofHash)', function () {
|
|
166
|
-
vector.proofs.forEach(function (p) {
|
|
167
|
-
Merkle.hashLeaf(hex(p.proofHash)).toString('hex')
|
|
168
|
-
.should.equal(p.leafHash, 'leaf ' + p.leafIndex)
|
|
169
|
-
})
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
it('rebuilds the root from the five proofHash values', function () {
|
|
173
|
-
Merkle.root(vector.proofs.map(function (p) { return hex(p.proofHash) }))
|
|
174
|
-
.toString('hex').should.equal(vector.root)
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
it('rebuilds every inclusion path and folds each back to the root', function () {
|
|
178
|
-
var leaves = vector.proofs.map(function (p) { return hex(p.proofHash) })
|
|
179
|
-
vector.proofs.forEach(function (p, i) {
|
|
180
|
-
Merkle.path(leaves, i).map(function (n) { return n.toString('hex') })
|
|
181
|
-
.should.deep.equal(p.path, 'path for leaf ' + i)
|
|
182
|
-
Merkle.verifyInclusion(leaves[i], i, vector.leafCount,
|
|
183
|
-
p.path.map(hex), hex(vector.root)).should.equal(true, 'fold for leaf ' + i)
|
|
184
|
-
})
|
|
185
|
-
})
|
|
186
|
-
|
|
187
|
-
it('encodes leafCount as u32be for the on-chain record', function () {
|
|
188
|
-
var buf = Buffer.alloc(4)
|
|
189
|
-
buf.writeUInt32BE(vector.leafCount, 0)
|
|
190
|
-
buf.toString('hex').should.equal(vector.onChainRecordTail.leafCountU32be)
|
|
191
|
-
vector.onChainRecordTail.merkleRoot.should.equal(vector.root)
|
|
192
|
-
})
|
|
193
|
-
})
|
|
194
|
-
|
|
195
|
-
describe('the rejected reading is recorded and really does diverge', function () {
|
|
196
|
-
it('reproduces the canonicalBytes-leaf root the vector records', function () {
|
|
197
|
-
Merkle.root(vector.proofs.map(function (p) { return hex(p.canonicalBytes) }))
|
|
198
|
-
.toString('hex').should.equal(vector.rejectedReading.root)
|
|
199
|
-
})
|
|
200
|
-
|
|
201
|
-
// The whole reason the vector exists. Equal in strength, different root.
|
|
202
|
-
it('is a different root from the proofHash reading', function () {
|
|
203
|
-
vector.root.should.not.equal(vector.rejectedReading.root)
|
|
204
|
-
})
|
|
205
|
-
|
|
206
|
-
it('records each leaf hash under both readings, and they differ', function () {
|
|
207
|
-
vector.proofs.forEach(function (p) {
|
|
208
|
-
Merkle.hashLeaf(hex(p.canonicalBytes)).toString('hex')
|
|
209
|
-
.should.equal(p.leafHashUnderRejectedReading, 'leaf ' + p.leafIndex)
|
|
210
|
-
p.leafHash.should.not.equal(p.leafHashUnderRejectedReading)
|
|
211
|
-
})
|
|
212
|
-
})
|
|
213
|
-
})
|
|
214
|
-
|
|
215
|
-
// The amendment publishes these values as the text to put in the spec. A document that
|
|
216
|
-
// has drifted from the artifact it describes is worse than no document, and nothing
|
|
217
|
-
// else would notice: the doc is prose, the vector is JSON, and they are edited
|
|
218
|
-
// separately.
|
|
219
|
-
describe('the amendment document publishes the same values', function () {
|
|
220
|
-
var fs = require('fs')
|
|
221
|
-
var path = require('path')
|
|
222
|
-
var doc = fs.readFileSync(
|
|
223
|
-
path.join(__dirname, '..', '..', 'docs', 'BRC220_BATCH_LEAF_AMENDMENT.md'), 'utf8')
|
|
224
|
-
|
|
225
|
-
it('publishes the root', function () {
|
|
226
|
-
doc.indexOf(vector.root).should.be.above(-1, 'root missing from the amendment')
|
|
227
|
-
})
|
|
228
|
-
|
|
229
|
-
it('publishes the rejected reading root, so the divergence is diagnosable', function () {
|
|
230
|
-
doc.indexOf(vector.rejectedReading.root)
|
|
231
|
-
.should.be.above(-1, 'rejected reading root missing from the amendment')
|
|
232
|
-
})
|
|
233
|
-
|
|
234
|
-
it('publishes every proofHash', function () {
|
|
235
|
-
vector.proofs.forEach(function (p) {
|
|
236
|
-
doc.indexOf(p.proofHash).should.be.above(-1,
|
|
237
|
-
'proofHash for leaf ' + p.leafIndex + ' missing from the amendment')
|
|
238
|
-
})
|
|
239
|
-
})
|
|
240
|
-
|
|
241
|
-
it('publishes the leaf-4 audit path and the u32be leaf count', function () {
|
|
242
|
-
doc.indexOf(vector.proofs[4].path[0]).should.be.above(-1, 'leaf 4 path missing')
|
|
243
|
-
doc.indexOf(vector.onChainRecordTail.leafCountU32be).should.be.above(-1,
|
|
244
|
-
'u32be leaf count missing')
|
|
245
|
-
})
|
|
246
|
-
})
|
|
247
|
-
|
|
248
|
-
describe('the library verifies certificates built from this vector', function () {
|
|
249
|
-
it('accepts a batch certificate for every leaf', function () {
|
|
250
|
-
vector.proofs.forEach(function (p, i) {
|
|
251
|
-
var report = NotaryHash.verifyBatchInclusion({
|
|
252
|
-
mode: NotaryScript.MODE.BATCH,
|
|
253
|
-
proofHash: p.proofHash,
|
|
254
|
-
merkle: {
|
|
255
|
-
root: vector.root,
|
|
256
|
-
leafIndex: i,
|
|
257
|
-
leafCount: vector.leafCount,
|
|
258
|
-
path: p.path
|
|
259
|
-
}
|
|
260
|
-
})
|
|
261
|
-
report.valid.should.equal(true, 'leaf ' + i)
|
|
262
|
-
report.errors.should.deep.equal([])
|
|
263
|
-
})
|
|
264
|
-
})
|
|
265
|
-
|
|
266
|
-
// An implementation that chose the other reading produces exactly this, and this is
|
|
267
|
-
// what it looks like when it reaches our verifier.
|
|
268
|
-
it('rejects a certificate carrying the rejected reading root', function () {
|
|
269
|
-
var report = NotaryHash.verifyBatchInclusion({
|
|
270
|
-
mode: NotaryScript.MODE.BATCH,
|
|
271
|
-
proofHash: vector.proofs[0].proofHash,
|
|
272
|
-
merkle: {
|
|
273
|
-
root: vector.rejectedReading.root,
|
|
274
|
-
leafIndex: 0,
|
|
275
|
-
leafCount: vector.leafCount,
|
|
276
|
-
path: vector.proofs[0].path
|
|
277
|
-
}
|
|
278
|
-
})
|
|
279
|
-
report.valid.should.equal(false)
|
|
280
|
-
})
|
|
281
|
-
})
|
|
282
|
-
})
|