@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
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
/* global describe, it, before, after */
|
|
4
|
-
|
|
5
|
-
// Ordinal-safe transfer (Phase 1). A 1-sat ordinal held in a covenant must be
|
|
6
|
-
// transferred WITHOUT burning the inscribed satoshi. The single-output
|
|
7
|
-
// ownershipToken sets output = input - fee, so it destroys a 1-sat UTXO; these
|
|
8
|
-
// tests prove (a) the single-output path refuses a 1-sat UTXO, and (b)
|
|
9
|
-
// transferOrdinal recreates the token at exactly 1 sat among funding outputs and
|
|
10
|
-
// the covenant verifies.
|
|
11
|
-
|
|
12
|
-
require('chai').should()
|
|
13
|
-
var bsv = require('../..')
|
|
14
|
-
var SC = bsv.SmartContract
|
|
15
|
-
var PrivateKey = bsv.PrivateKey
|
|
16
|
-
var Transaction = bsv.Transaction
|
|
17
|
-
|
|
18
|
-
var Token = SC.Token
|
|
19
|
-
var help = SC.CovenantHelpers
|
|
20
|
-
var fundAndSpend = help.fundAndSpend
|
|
21
|
-
var p2pkhOutput = help.p2pkhOutput
|
|
22
|
-
var verify = help.verify
|
|
23
|
-
|
|
24
|
-
describe('Ordinal-safe transfer (1-sat, SIGHASH_ALL)', function () {
|
|
25
|
-
this.timeout(20000)
|
|
26
|
-
var alice = PrivateKey.fromRandom()
|
|
27
|
-
var bob = PrivateKey.fromRandom()
|
|
28
|
-
|
|
29
|
-
it('transferOrdinal recreates the 1-sat token among funding outputs and verifies', function () {
|
|
30
|
-
var lock = Token.ordinalLock(Token.ownerId(alice))
|
|
31
|
-
var bobHash = Token.ownerId(bob)
|
|
32
|
-
var nextToken = Token.ordinalLock(bobHash)
|
|
33
|
-
|
|
34
|
-
// Ordinal output stays exactly 1 sat; a change output carries the funding.
|
|
35
|
-
var tokenOutput = new Transaction.Output({ script: nextToken, satoshis: 1 })
|
|
36
|
-
var change = p2pkhOutput(PrivateKey.fromRandom(), 5000)
|
|
37
|
-
|
|
38
|
-
// token output at index 0 (ordinal FIFO), change after.
|
|
39
|
-
var spend = fundAndSpend(lock, 1, { outputs: [tokenOutput, change] }).spend
|
|
40
|
-
spend.inputs[0].setScript(Token.transferOrdinal(alice, bobHash, spend, 1, lock, { tokenIndex: 0 }))
|
|
41
|
-
|
|
42
|
-
verify(spend.inputs[0].script, lock, { tx: spend, satoshis: 1 }).ok.should.equal(true)
|
|
43
|
-
// The recreated token output is still exactly 1 sat.
|
|
44
|
-
spend.outputs[0].satoshis.should.equal(1)
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
it('guardrail: single-output unlockTransfer refuses a 1-sat UTXO', function () {
|
|
48
|
-
var lock = Token.ordinalLock(Token.ownerId(alice))
|
|
49
|
-
var bobHash = Token.ownerId(bob)
|
|
50
|
-
var next = Token.ownershipToken(500, bobHash)
|
|
51
|
-
var spend = fundAndSpend(lock, 1, {
|
|
52
|
-
outputs: [new Transaction.Output({ script: next, satoshis: 1 })]
|
|
53
|
-
}).spend
|
|
54
|
-
;(function () {
|
|
55
|
-
Token.unlockTransfer(alice, bobHash, spend, 1, lock)
|
|
56
|
-
}).should.throw(/burns|ordinal/)
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
it('anti-burn: transferOrdinal rejects a token output not equal to the ordinal value', function () {
|
|
60
|
-
var lock = Token.ordinalLock(Token.ownerId(alice))
|
|
61
|
-
var bobHash = Token.ownerId(bob)
|
|
62
|
-
var nextToken = Token.ordinalLock(bobHash)
|
|
63
|
-
// Wrong: token output is 2 sat, not the ordinal's 1 sat.
|
|
64
|
-
var tokenOutput = new Transaction.Output({ script: nextToken, satoshis: 2 })
|
|
65
|
-
var change = p2pkhOutput(PrivateKey.fromRandom(), 5000)
|
|
66
|
-
var spend = fundAndSpend(lock, 1, { outputs: [tokenOutput, change] }).spend
|
|
67
|
-
;(function () {
|
|
68
|
-
Token.transferOrdinal(alice, bobHash, spend, 1, lock, { tokenIndex: 0 })
|
|
69
|
-
}).should.throw(/equal the ordinal|burn/)
|
|
70
|
-
})
|
|
71
|
-
})
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
/* global describe, it */
|
|
4
|
-
|
|
5
|
-
// Coverage for SmartContract.Preimage (previously untested public class).
|
|
6
|
-
// Focus: a too-short buffer must fail with an HONEST error (it used to trip
|
|
7
|
-
// decodeCompactSize into a misleading "8-byte CompactSize" throw plus console
|
|
8
|
-
// spam), while real BIP-143 preimages round-trip their fields.
|
|
9
|
-
|
|
10
|
-
require('chai').should()
|
|
11
|
-
var bsv = require('../..')
|
|
12
|
-
var Preimage = bsv.SmartContract.Preimage
|
|
13
|
-
|
|
14
|
-
describe('SmartContract.Preimage', function () {
|
|
15
|
-
describe('length guard (honest failure)', function () {
|
|
16
|
-
it('exposes the derived BIP-143 structural minimum (157)', function () {
|
|
17
|
-
Preimage.LEFT_FIXED_LENGTH.should.equal(104)
|
|
18
|
-
Preimage.RIGHT_FIXED_LENGTH.should.equal(52)
|
|
19
|
-
Preimage.MIN_PREIMAGE_LENGTH.should.equal(157)
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
it('throws a clear "too short" error at construction, not a CompactSize error', function () {
|
|
23
|
-
;[0, 50, 103, 156].forEach(function (n) {
|
|
24
|
-
(function () { return new Preimage(Buffer.alloc(n)) })
|
|
25
|
-
.should.throw(/Preimage too short/)
|
|
26
|
-
})
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
it('does NOT mention CompactSize when the real problem is length', function () {
|
|
30
|
-
var threw
|
|
31
|
-
try { threw = new Preimage(Buffer.alloc(50)) } catch (e) { threw = e.message }
|
|
32
|
-
threw.should.match(/Preimage too short/)
|
|
33
|
-
threw.should.not.match(/CompactSize/)
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
it('validate() reports the length gracefully for a deferred preimage (no throw)', function () {
|
|
37
|
-
var p = new Preimage(Buffer.alloc(50), { deferExtraction: true })
|
|
38
|
-
var res = p.validate()
|
|
39
|
-
res.valid.should.equal(false)
|
|
40
|
-
res.errors.join(' ').should.match(/Preimage too short: 50 bytes/)
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
it('accepts a buffer exactly at the minimum length', function () {
|
|
44
|
-
// 157 bytes: 104 left + 1-byte scriptLen varint (0x00 => empty script) + 52 right.
|
|
45
|
-
var buf = Buffer.alloc(Preimage.MIN_PREIMAGE_LENGTH)
|
|
46
|
-
;(function () { return new Preimage(buf) }).should.not.throw()
|
|
47
|
-
})
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
describe('decodeCompactSize (honest failure)', function () {
|
|
51
|
-
it('throws when reading past the end of the buffer', function () {
|
|
52
|
-
(function () { Preimage.decodeCompactSize(Buffer.alloc(4), 4) })
|
|
53
|
-
.should.throw(/past end of preimage buffer/)
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
it('rejects the 8-byte (0xff) length prefix as invalid for a script length', function () {
|
|
57
|
-
var buf = Buffer.concat([Buffer.from([0xff]), Buffer.alloc(8)])
|
|
58
|
-
;(function () { Preimage.decodeCompactSize(buf, 0) })
|
|
59
|
-
.should.throw(/8-byte length prefix/)
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
it('rejects a truncated 2-byte CompactSize', function () {
|
|
63
|
-
(function () { Preimage.decodeCompactSize(Buffer.from([0xfd, 0x01]), 0) })
|
|
64
|
-
.should.throw(/Truncated 2-byte CompactSize/)
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
it('decodes a plain 1-byte length', function () {
|
|
68
|
-
var r = Preimage.decodeCompactSize(Buffer.from([0x19]), 0)
|
|
69
|
-
r.value.should.equal(0x19)
|
|
70
|
-
r.bytes.should.equal(1)
|
|
71
|
-
r.nextOffset.should.equal(1)
|
|
72
|
-
})
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
describe('real preimage round-trip', function () {
|
|
76
|
-
it('constructs, extracts fields, and validates a genuine preimage', function () {
|
|
77
|
-
var ex = Preimage.createExample(0x41) // SIGHASH_ALL | FORKID
|
|
78
|
-
ex.preimage.length.should.be.above(Preimage.MIN_PREIMAGE_LENGTH)
|
|
79
|
-
|
|
80
|
-
// Fixed-width fields come back at their BIP-143 sizes.
|
|
81
|
-
ex.getField('version').length.should.equal(4)
|
|
82
|
-
ex.getField('hashPrevouts').length.should.equal(32)
|
|
83
|
-
ex.getField('hashOutputs').length.should.equal(32)
|
|
84
|
-
ex.getField('sighash').length.should.equal(4)
|
|
85
|
-
|
|
86
|
-
var v = ex.validate()
|
|
87
|
-
v.valid.should.equal(true)
|
|
88
|
-
v.errors.length.should.equal(0)
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
it('reports SIGHASH flag info for the extracted sighash', function () {
|
|
92
|
-
var ex = Preimage.createExample(0x41)
|
|
93
|
-
var info = ex.getSighashInfo()
|
|
94
|
-
info.flagName.should.match(/ALL/)
|
|
95
|
-
info.flagName.should.match(/FORKID/)
|
|
96
|
-
})
|
|
97
|
-
})
|
|
98
|
-
})
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
/* global describe, it, before, after */
|
|
4
|
-
|
|
5
|
-
// Configurable SIGHASH core (Phase 2). The OP_PUSH_TX core was hard-wired to
|
|
6
|
-
// SIGHASH_ALL|FORKID (0x41). It can now be built under any flag; these tests prove
|
|
7
|
-
// the marketplace / partially-signed pattern with SIGHASH_SINGLE|ANYONECANPAY|FORKID
|
|
8
|
-
// (0xc3): a seller commits only to its own input and its own output, and a buyer may
|
|
9
|
-
// add funding inputs and change outputs without invalidating the covenant — while
|
|
10
|
-
// the seller's OWN output stays bound.
|
|
11
|
-
|
|
12
|
-
require('chai').should()
|
|
13
|
-
var bsv = require('../..')
|
|
14
|
-
var SC = bsv.SmartContract
|
|
15
|
-
var PrivateKey = bsv.PrivateKey
|
|
16
|
-
var Script = bsv.Script
|
|
17
|
-
var Opcode = bsv.Opcode
|
|
18
|
-
var Transaction = bsv.Transaction
|
|
19
|
-
|
|
20
|
-
var P = SC.PushTx
|
|
21
|
-
var help = SC.CovenantHelpers
|
|
22
|
-
var fundAndSpend = help.fundAndSpend
|
|
23
|
-
var p2pkhOutput = help.p2pkhOutput
|
|
24
|
-
var verify = help.verify
|
|
25
|
-
|
|
26
|
-
var SATS = 100000
|
|
27
|
-
|
|
28
|
-
describe('Configurable SIGHASH — SINGLE|ANYONECANPAY marketplace covenant', function () {
|
|
29
|
-
this.timeout(20000)
|
|
30
|
-
var flag = P.SIGHASH_SINGLE_ANYONECANPAY_FORKID // 0xc3
|
|
31
|
-
|
|
32
|
-
// Lock: authenticate the pushed preimage under `flag`, assert the flag, succeed.
|
|
33
|
-
function makeLock () {
|
|
34
|
-
var lock = new Script()
|
|
35
|
-
lock.add(Opcode.OP_DUP)
|
|
36
|
-
P.pushTxCore(lock, { sighashType: flag })
|
|
37
|
-
lock.add(Opcode.OP_VERIFY)
|
|
38
|
-
P.assertSighashType(lock, flag)
|
|
39
|
-
lock.add(Opcode.OP_DROP)
|
|
40
|
-
lock.add(Opcode.OP_TRUE)
|
|
41
|
-
return lock
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function grindUnlock (spend, lock) {
|
|
45
|
-
var g = P.grind(spend, 0, lock, SATS, { sighashType: flag })
|
|
46
|
-
spend.inputs[0].setScript(new Script().add(g.preimage))
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
it('verifies a SINGLE|ANYONECANPAY covenant spend', function () {
|
|
50
|
-
var lock = makeLock()
|
|
51
|
-
var sellerOut = p2pkhOutput(PrivateKey.fromRandom(), SATS - 500) // committed output (index 0)
|
|
52
|
-
var spend = fundAndSpend(lock, SATS, { outputs: [sellerOut] }).spend
|
|
53
|
-
grindUnlock(spend, lock)
|
|
54
|
-
verify(spend.inputs[0].script, lock, { tx: spend, satoshis: SATS }).ok.should.equal(true)
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
it('stays valid when a buyer adds a funding input and a change output', function () {
|
|
58
|
-
var lock = makeLock()
|
|
59
|
-
var sellerOut = p2pkhOutput(PrivateKey.fromRandom(), SATS - 500)
|
|
60
|
-
var spend = fundAndSpend(lock, SATS, { outputs: [sellerOut] }).spend
|
|
61
|
-
grindUnlock(spend, lock) // seller signs input0 + output0 only
|
|
62
|
-
|
|
63
|
-
// Buyer adds a funding input and their own change output AFTER the seller signed.
|
|
64
|
-
spend.addInput(new Transaction.Input({
|
|
65
|
-
prevTxId: '11'.repeat(32), outputIndex: 0, script: Script.empty()
|
|
66
|
-
}), p2pkhOutput(PrivateKey.fromRandom(), 50000).script, 50000)
|
|
67
|
-
spend.addOutput(p2pkhOutput(PrivateKey.fromRandom(), 49000)) // buyer change at index 1
|
|
68
|
-
|
|
69
|
-
// The seller's covenant on input 0 is unaffected (ANYONECANPAY ignores the new
|
|
70
|
-
// input; SINGLE ignores the new output).
|
|
71
|
-
verify(spend.inputs[0].script, lock, { tx: spend, satoshis: SATS }).ok.should.equal(true)
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
it('still binds the seller\'s OWN output (SINGLE commits output at the input index)', function () {
|
|
75
|
-
var lock = makeLock()
|
|
76
|
-
var sellerOut = p2pkhOutput(PrivateKey.fromRandom(), SATS - 500)
|
|
77
|
-
var spend = fundAndSpend(lock, SATS, { outputs: [sellerOut] }).spend
|
|
78
|
-
grindUnlock(spend, lock)
|
|
79
|
-
|
|
80
|
-
// Tamper the committed output (index 0) after signing → must fail.
|
|
81
|
-
spend.outputs[0] = p2pkhOutput(PrivateKey.fromRandom(), 1)
|
|
82
|
-
verify(spend.inputs[0].script, lock, { tx: spend, satoshis: SATS }).ok.should.equal(false)
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
it('back-compat: default pushTxCore is still SIGHASH_ALL|FORKID', function () {
|
|
86
|
-
var lock = new Script()
|
|
87
|
-
lock.add(Opcode.OP_DUP)
|
|
88
|
-
P.pushTxCore(lock) // no opts → 0x41
|
|
89
|
-
lock.add(Opcode.OP_VERIFY)
|
|
90
|
-
P.assertSighashAll(lock)
|
|
91
|
-
lock.add(Opcode.OP_DROP).add(Opcode.OP_TRUE)
|
|
92
|
-
var out = p2pkhOutput(PrivateKey.fromRandom(), SATS - 500)
|
|
93
|
-
var spend = fundAndSpend(lock, SATS, { outputs: [out] }).spend
|
|
94
|
-
var g = P.grind(spend, 0, lock, SATS) // default sighash
|
|
95
|
-
spend.inputs[0].setScript(new Script().add(g.preimage))
|
|
96
|
-
verify(spend.inputs[0].script, lock, { tx: spend, satoshis: SATS }).ok.should.equal(true)
|
|
97
|
-
})
|
|
98
|
-
})
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
/* global describe, it, before, after */
|
|
4
|
-
require('chai').should()
|
|
5
|
-
var bsv = require('../..')
|
|
6
|
-
var SC = bsv.SmartContract
|
|
7
|
-
var PrivateKey = bsv.PrivateKey
|
|
8
|
-
var Script = bsv.Script
|
|
9
|
-
var Transaction = bsv.Transaction
|
|
10
|
-
|
|
11
|
-
var Token = SC.Token
|
|
12
|
-
var Auth = SC.Authorizers
|
|
13
|
-
var help = SC.CovenantHelpers
|
|
14
|
-
var fundAndSpend = help.fundAndSpend
|
|
15
|
-
var p2pkhOutput = help.p2pkhOutput
|
|
16
|
-
var verify = help.verify
|
|
17
|
-
|
|
18
|
-
var SATS = 100000
|
|
19
|
-
var FEE = 500
|
|
20
|
-
|
|
21
|
-
describe('SmartContract generalized token (pluggable auth + multi-output)', function () {
|
|
22
|
-
this.timeout(20000)
|
|
23
|
-
|
|
24
|
-
function spendOf (lock, sats, outputs) {
|
|
25
|
-
return fundAndSpend(lock, sats, { outputs: outputs }).spend
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
describe('single-key authorizer (default)', function () {
|
|
29
|
-
var alice = PrivateKey.fromRandom()
|
|
30
|
-
var bob = PrivateKey.fromRandom()
|
|
31
|
-
|
|
32
|
-
it('transfers and rejects a non-owner', function () {
|
|
33
|
-
var lock = Token.ownershipToken(FEE, Token.ownerId(alice))
|
|
34
|
-
var bobHash = Token.ownerId(bob)
|
|
35
|
-
var next = Token.ownershipToken(FEE, bobHash)
|
|
36
|
-
var spend = spendOf(lock, SATS, [new Transaction.Output({ script: next, satoshis: SATS - FEE })])
|
|
37
|
-
spend.inputs[0].setScript(Token.unlockTransfer(alice, bobHash, spend, SATS, lock))
|
|
38
|
-
verify(spend.inputs[0].script, lock, { tx: spend, satoshis: SATS }).ok.should.equal(true)
|
|
39
|
-
|
|
40
|
-
var bad = spendOf(lock, SATS, [new Transaction.Output({ script: next, satoshis: SATS - FEE })])
|
|
41
|
-
bad.inputs[0].setScript(Token.unlockTransfer(bob, bobHash, bad, SATS, lock)) // bob does not own alice's token
|
|
42
|
-
verify(bad.inputs[0].script, lock, { tx: bad, satoshis: SATS }).ok.should.equal(false)
|
|
43
|
-
})
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
describe('multisig authorizer (m-of-n)', function () {
|
|
47
|
-
var a = PrivateKey.fromRandom()
|
|
48
|
-
var b = PrivateKey.fromRandom()
|
|
49
|
-
var c = PrivateKey.fromRandom()
|
|
50
|
-
var keys = [a.toPublicKey(), b.toPublicKey(), c.toPublicKey()]
|
|
51
|
-
var auth23 = Auth.multisig(2, 3)
|
|
52
|
-
var commit = auth23.commit(keys)
|
|
53
|
-
var nextOwner = Token.ownerId(PrivateKey.fromRandom())
|
|
54
|
-
|
|
55
|
-
function transfer23 (signWith) {
|
|
56
|
-
var lock = Token.ownershipToken(FEE, commit, auth23)
|
|
57
|
-
var next = Token.ownershipToken(FEE, nextOwner, auth23) // covenant recreates the SAME (multisig) code
|
|
58
|
-
var spend = spendOf(lock, SATS, [new Transaction.Output({ script: next, satoshis: SATS - FEE })])
|
|
59
|
-
spend.inputs[0].setScript(
|
|
60
|
-
Token.unlockTransfer({ keys: keys, signWith: signWith }, nextOwner, spend, SATS, lock, { auth: auth23 }))
|
|
61
|
-
return { spend: spend, lock: lock }
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
it('accepts a valid 2-of-3 (keys a,c)', function () {
|
|
65
|
-
var r = transfer23([a, c])
|
|
66
|
-
verify(r.spend.inputs[0].script, r.lock, { tx: r.spend, satoshis: SATS }).ok.should.equal(true)
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
it('accepts a different valid pair (keys b,c)', function () {
|
|
70
|
-
var r = transfer23([b, c])
|
|
71
|
-
verify(r.spend.inputs[0].script, r.lock, { tx: r.spend, satoshis: SATS }).ok.should.equal(true)
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
it('rejects a single signature (below threshold)', function () {
|
|
75
|
-
var run = function () { transfer23([a]) }
|
|
76
|
-
run.should.throw(/at least 2/)
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
it('rejects a signer outside the committed set', function () {
|
|
80
|
-
var outsider = PrivateKey.fromRandom()
|
|
81
|
-
var run = function () { transfer23([a, outsider]) }
|
|
82
|
-
run.should.throw(/not in the committed key set/)
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
it('rejects a forged set (right threshold, wrong keys)', function () {
|
|
86
|
-
// Commit to {a,b,c} but try to spend revealing a different key set {a,b,x}.
|
|
87
|
-
var x = PrivateKey.fromRandom()
|
|
88
|
-
var wrongKeys = [a.toPublicKey(), b.toPublicKey(), x.toPublicKey()]
|
|
89
|
-
var lock = Token.ownershipToken(FEE, commit, auth23)
|
|
90
|
-
var next = Token.ownershipToken(FEE, nextOwner, auth23)
|
|
91
|
-
var spend = spendOf(lock, SATS, [new Transaction.Output({ script: next, satoshis: SATS - FEE })])
|
|
92
|
-
spend.inputs[0].setScript(
|
|
93
|
-
Token.unlockTransfer({ keys: wrongKeys, signWith: [a, b] }, nextOwner, spend, SATS, lock, { auth: auth23 }))
|
|
94
|
-
// HASH160(forged redeem) != committed redeem hash => fails.
|
|
95
|
-
verify(spend.inputs[0].script, lock, { tx: spend, satoshis: SATS }).ok.should.equal(false)
|
|
96
|
-
})
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
describe('predicate authorizer (custom escape hatch)', function () {
|
|
100
|
-
// Re-implement single-key through the generic predicate interface to prove
|
|
101
|
-
// the contract is sufficient for custom schemes.
|
|
102
|
-
var Opcode = bsv.Opcode
|
|
103
|
-
var Hash = bsv.crypto.Hash
|
|
104
|
-
var owner = PrivateKey.fromRandom()
|
|
105
|
-
var custom = Auth.predicate({
|
|
106
|
-
name: 'custom-single-key',
|
|
107
|
-
commit: function (key) { return Hash.sha256ripemd160(key.toPublicKey().toBuffer()) },
|
|
108
|
-
emit: function (s) {
|
|
109
|
-
return s.add(Opcode.OP_OVER).add(Opcode.OP_HASH160).add(Opcode.OP_EQUALVERIFY).add(Opcode.OP_CHECKSIGVERIFY)
|
|
110
|
-
},
|
|
111
|
-
unlockArgs: function (spend, inputIndex, lock, sats, key) {
|
|
112
|
-
return [help.signInput(spend, key, inputIndex, lock, sats), key.toPublicKey().toBuffer()]
|
|
113
|
-
}
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
it('authorizes a transfer via a custom predicate', function () {
|
|
117
|
-
var lock = Token.ownershipToken(FEE, custom.commit(owner), custom)
|
|
118
|
-
var nextOwner = Token.ownerId(PrivateKey.fromRandom())
|
|
119
|
-
var next = Token.ownershipToken(FEE, nextOwner)
|
|
120
|
-
var spend = spendOf(lock, SATS, [new Transaction.Output({ script: next, satoshis: SATS - FEE })])
|
|
121
|
-
spend.inputs[0].setScript(Token.unlockTransfer(owner, nextOwner, spend, SATS, lock, { auth: custom }))
|
|
122
|
-
verify(spend.inputs[0].script, lock, { tx: spend, satoshis: SATS }).ok.should.equal(true)
|
|
123
|
-
})
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
describe('multi-output token (N-output)', function () {
|
|
127
|
-
var alice = PrivateKey.fromRandom()
|
|
128
|
-
var bob = PrivateKey.fromRandom()
|
|
129
|
-
|
|
130
|
-
function serialize (output) { return output.toBufferWriter().toBuffer() }
|
|
131
|
-
function le8 (v) { return new bsv.crypto.BN(v).toBuffer({ endian: 'little', size: 8 }) }
|
|
132
|
-
|
|
133
|
-
it('recreates the token among other outputs and binds them all', function () {
|
|
134
|
-
var lock = Token.ownershipTokenMulti(Token.ownerId(alice))
|
|
135
|
-
var bobHash = Token.ownerId(bob)
|
|
136
|
-
var nextToken = Token.ownershipTokenMulti(bobHash)
|
|
137
|
-
|
|
138
|
-
var payment = p2pkhOutput(PrivateKey.fromRandom(), 4000)
|
|
139
|
-
var tokenValue = 1000
|
|
140
|
-
var tokenOutput = new Transaction.Output({ script: nextToken, satoshis: tokenValue })
|
|
141
|
-
var dataOutput = new Transaction.Output({
|
|
142
|
-
script: new Script().add(bsv.Opcode.OP_FALSE).add(bsv.Opcode.OP_RETURN).add(Buffer.from('hello')),
|
|
143
|
-
satoshis: 0
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
// tx outputs in order: [payment, token, data]
|
|
147
|
-
var spend = spendOf(lock, SATS, [payment, tokenOutput, dataOutput])
|
|
148
|
-
spend.inputs[0].setScript(Token.unlockTransferMulti(alice, bobHash, spend, SATS, lock, {
|
|
149
|
-
before: serialize(payment),
|
|
150
|
-
after: serialize(dataOutput),
|
|
151
|
-
tokenValue: le8(tokenValue)
|
|
152
|
-
}))
|
|
153
|
-
verify(spend.inputs[0].script, lock, { tx: spend, satoshis: SATS }).ok.should.equal(true)
|
|
154
|
-
})
|
|
155
|
-
|
|
156
|
-
it('rejects a tampered surrounding output', function () {
|
|
157
|
-
var lock = Token.ownershipTokenMulti(Token.ownerId(alice))
|
|
158
|
-
var bobHash = Token.ownerId(bob)
|
|
159
|
-
var nextToken = Token.ownershipTokenMulti(bobHash)
|
|
160
|
-
var payment = p2pkhOutput(PrivateKey.fromRandom(), 4000)
|
|
161
|
-
var tokenValue = 1000
|
|
162
|
-
var tokenOutput = new Transaction.Output({ script: nextToken, satoshis: tokenValue })
|
|
163
|
-
|
|
164
|
-
var spend = spendOf(lock, SATS, [payment, tokenOutput])
|
|
165
|
-
// Claim a DIFFERENT `before` than the real first output => HASH160 mismatch.
|
|
166
|
-
var lyingPayment = p2pkhOutput(PrivateKey.fromRandom(), 4000)
|
|
167
|
-
spend.inputs[0].setScript(Token.unlockTransferMulti(alice, bobHash, spend, SATS, lock, {
|
|
168
|
-
before: serialize(lyingPayment),
|
|
169
|
-
after: Buffer.alloc(0),
|
|
170
|
-
tokenValue: le8(tokenValue)
|
|
171
|
-
}))
|
|
172
|
-
verify(spend.inputs[0].script, lock, { tx: spend, satoshis: SATS }).ok.should.equal(false)
|
|
173
|
-
})
|
|
174
|
-
})
|
|
175
|
-
})
|
package/test/spv/headerchain.js
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
/* global describe, it */
|
|
4
|
-
|
|
5
|
-
// SPV header-chain verification: linkage + per-header proof-of-work + optional
|
|
6
|
-
// trusted-hash anchoring. Uses the regtest max target (0x207fffff) so a header's
|
|
7
|
-
// PoW passes after trivial nonce grinding (no real mining needed).
|
|
8
|
-
|
|
9
|
-
require('chai').should()
|
|
10
|
-
var bsv = require('../..')
|
|
11
|
-
var SPV = bsv.SPV
|
|
12
|
-
|
|
13
|
-
function rand32 () { return bsv.crypto.Random.getRandomBuffer(32) }
|
|
14
|
-
|
|
15
|
-
// "Mine" a header at regtest difficulty: grind nonce until PoW passes (~1-2 tries).
|
|
16
|
-
function minedHeader (prevHashInternal, merkleInternal) {
|
|
17
|
-
for (var nonce = 0; nonce < 100000; nonce++) {
|
|
18
|
-
var h = new bsv.BlockHeader({
|
|
19
|
-
version: 1,
|
|
20
|
-
prevHash: prevHashInternal,
|
|
21
|
-
merkleRoot: merkleInternal,
|
|
22
|
-
time: 1231006505 + nonce,
|
|
23
|
-
bits: 0x207fffff,
|
|
24
|
-
nonce: nonce
|
|
25
|
-
})
|
|
26
|
-
if (h.validProofOfWork()) return h
|
|
27
|
-
}
|
|
28
|
-
throw new Error('could not mine a regtest header')
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function chain (n) {
|
|
32
|
-
var hs = []
|
|
33
|
-
var prev = Buffer.alloc(32)
|
|
34
|
-
for (var i = 0; i < n; i++) {
|
|
35
|
-
var h = minedHeader(prev, rand32())
|
|
36
|
-
hs.push(h)
|
|
37
|
-
prev = h._getHash()
|
|
38
|
-
}
|
|
39
|
-
return hs
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
describe('SPV.verifyHeaderChain', function () {
|
|
43
|
-
this.timeout(10000)
|
|
44
|
-
|
|
45
|
-
it('accepts a linked, PoW-valid chain', function () {
|
|
46
|
-
var hs = chain(4)
|
|
47
|
-
var res = SPV.verifyHeaderChain(hs)
|
|
48
|
-
res.valid.should.equal(true)
|
|
49
|
-
res.count.should.equal(4)
|
|
50
|
-
res.anchorHash.should.equal(hs[0].id)
|
|
51
|
-
res.tipHash.should.equal(hs[3].id)
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
it('rejects a broken link', function () {
|
|
55
|
-
var hs = chain(3)
|
|
56
|
-
var res = SPV.verifyHeaderChain([hs[0], hs[2]]) // hs[2] links to hs[1], not hs[0]
|
|
57
|
-
res.valid.should.equal(false)
|
|
58
|
-
res.reason.should.match(/broken link/)
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
it('rejects a header that fails proof-of-work', function () {
|
|
62
|
-
// Mainnet-difficulty bits with nonce 0 — the hash will not meet the tiny target.
|
|
63
|
-
var bad = new bsv.BlockHeader({
|
|
64
|
-
version: 1, prevHash: Buffer.alloc(32), merkleRoot: rand32(),
|
|
65
|
-
time: 1231006505, bits: 0x1d00ffff, nonce: 0
|
|
66
|
-
})
|
|
67
|
-
SPV.verifyHeaderChain([bad]).valid.should.equal(false)
|
|
68
|
-
// ...but skipping PoW, a single header is trivially "valid".
|
|
69
|
-
SPV.verifyHeaderChain([bad], { requirePow: false }).valid.should.equal(true)
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
it('honours a trusted-hash anchor (tip or anchor)', function () {
|
|
73
|
-
var hs = chain(3)
|
|
74
|
-
SPV.verifyHeaderChain(hs, { trustedHash: hs[2].id }).valid.should.equal(true)
|
|
75
|
-
SPV.verifyHeaderChain(hs, { trustedHash: hs[0].id }).valid.should.equal(true)
|
|
76
|
-
var res = SPV.verifyHeaderChain(hs, { trustedHash: 'ff'.repeat(32) })
|
|
77
|
-
res.valid.should.equal(false)
|
|
78
|
-
res.reason.should.match(/trusted hash/)
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
it('accepts hex / buffer headers', function () {
|
|
82
|
-
var hs = chain(2)
|
|
83
|
-
var asHex = hs.map(function (h) { return h.toBuffer().toString('hex') })
|
|
84
|
-
SPV.verifyHeaderChain(asHex).valid.should.equal(true)
|
|
85
|
-
})
|
|
86
|
-
})
|
package/test/spv/merkleproof.js
DELETED
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
/* global describe, it */
|
|
4
|
-
|
|
5
|
-
// SPV Merkle inclusion proofs, cross-checked against the codebase's own Merkle
|
|
6
|
-
// tree algorithm (lib/block/block.js getMerkleTree). For random trees of many
|
|
7
|
-
// shapes (even, odd, duplicate-last), extract the branch for each leaf and assert
|
|
8
|
-
// the SPV verifier recomputes the exact same root — then assert tampering fails.
|
|
9
|
-
|
|
10
|
-
require('chai').should()
|
|
11
|
-
var bsv = require('../..')
|
|
12
|
-
var SPV = bsv.SPV
|
|
13
|
-
var Hash = bsv.crypto.Hash
|
|
14
|
-
|
|
15
|
-
function rev (b) { return Buffer.from(b).reverse() }
|
|
16
|
-
|
|
17
|
-
// Build the flat merkle tree exactly like Block.getMerkleTree (leaves = internal-LE).
|
|
18
|
-
function buildTree (leaves) {
|
|
19
|
-
var tree = leaves.slice()
|
|
20
|
-
var j = 0
|
|
21
|
-
for (var size = leaves.length; size > 1; size = Math.floor((size + 1) / 2)) {
|
|
22
|
-
for (var i = 0; i < size; i += 2) {
|
|
23
|
-
var i2 = Math.min(i + 1, size - 1)
|
|
24
|
-
tree.push(Hash.sha256sha256(Buffer.concat([tree[j + i], tree[j + i2]])))
|
|
25
|
-
}
|
|
26
|
-
j += size
|
|
27
|
-
}
|
|
28
|
-
return tree
|
|
29
|
-
}
|
|
30
|
-
function rootOf (leaves) { var t = buildTree(leaves); return t[t.length - 1] }
|
|
31
|
-
|
|
32
|
-
// Extract the sibling branch (display-order hex) for a leaf, mirroring the tree layout.
|
|
33
|
-
function branchFor (leaves, index) {
|
|
34
|
-
var tree = buildTree(leaves)
|
|
35
|
-
var branch = []
|
|
36
|
-
var j = 0
|
|
37
|
-
var idx = index
|
|
38
|
-
for (var size = leaves.length; size > 1; size = Math.floor((size + 1) / 2)) {
|
|
39
|
-
var sib = idx ^ 1
|
|
40
|
-
if (sib >= size) sib = idx // odd end -> duplicate self
|
|
41
|
-
branch.push(rev(tree[j + sib]).toString('hex'))
|
|
42
|
-
j += size
|
|
43
|
-
idx = Math.floor(idx / 2)
|
|
44
|
-
}
|
|
45
|
-
return branch
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function randLeaves (n) {
|
|
49
|
-
var a = []
|
|
50
|
-
for (var i = 0; i < n; i++) a.push(bsv.crypto.Random.getRandomBuffer(32))
|
|
51
|
-
return a
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
describe('SPV.verifyMerkleProof (cross-checked vs Block.getMerkleTree)', function () {
|
|
55
|
-
var sizes = [1, 2, 3, 4, 5, 7, 8, 9, 16, 21]
|
|
56
|
-
|
|
57
|
-
it('recomputes the root for every leaf across many tree shapes', function () {
|
|
58
|
-
sizes.forEach(function (n) {
|
|
59
|
-
var leaves = randLeaves(n)
|
|
60
|
-
var root = rev(rootOf(leaves)).toString('hex') // display order
|
|
61
|
-
for (var idx = 0; idx < n; idx++) {
|
|
62
|
-
var txid = rev(leaves[idx]).toString('hex')
|
|
63
|
-
var nodes = branchFor(leaves, idx)
|
|
64
|
-
SPV.merkleRootFromBranch(txid, idx, nodes).should.equal(root,
|
|
65
|
-
'size ' + n + ' index ' + idx)
|
|
66
|
-
SPV.verifyMerkleProof({ txid: txid, index: idx, nodes: nodes, merkleRoot: root })
|
|
67
|
-
.should.equal(true)
|
|
68
|
-
}
|
|
69
|
-
})
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
it('rejects a tampered branch node', function () {
|
|
73
|
-
var leaves = randLeaves(6)
|
|
74
|
-
var root = rev(rootOf(leaves)).toString('hex')
|
|
75
|
-
var nodes = branchFor(leaves, 2)
|
|
76
|
-
nodes[0] = rev(bsv.crypto.Random.getRandomBuffer(32)).toString('hex') // wrong sibling
|
|
77
|
-
SPV.verifyMerkleProof({ txid: rev(leaves[2]).toString('hex'), index: 2, nodes: nodes, merkleRoot: root })
|
|
78
|
-
.should.equal(false)
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
it('rejects the right proof against the wrong txid', function () {
|
|
82
|
-
var leaves = randLeaves(8)
|
|
83
|
-
var root = rev(rootOf(leaves)).toString('hex')
|
|
84
|
-
var nodes = branchFor(leaves, 3)
|
|
85
|
-
var wrongTxid = rev(bsv.crypto.Random.getRandomBuffer(32)).toString('hex')
|
|
86
|
-
SPV.verifyMerkleProof({ txid: wrongTxid, index: 3, nodes: nodes, merkleRoot: root })
|
|
87
|
-
.should.equal(false)
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
it("accepts '*' as the odd-node duplicate marker", function () {
|
|
91
|
-
// 3 leaves: index 2 is the odd one; its level-0 sibling is itself.
|
|
92
|
-
var leaves = randLeaves(3)
|
|
93
|
-
var root = rev(rootOf(leaves)).toString('hex')
|
|
94
|
-
var nodes = branchFor(leaves, 2)
|
|
95
|
-
nodes[0] = '*' // replace the explicit self-hash with the duplicate marker
|
|
96
|
-
SPV.verifyMerkleProof({ txid: rev(leaves[2]).toString('hex'), index: 2, nodes: nodes, merkleRoot: root })
|
|
97
|
-
.should.equal(true)
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
it('single-tx block: empty branch, root == txid', function () {
|
|
101
|
-
var leaves = randLeaves(1)
|
|
102
|
-
var txid = rev(leaves[0]).toString('hex')
|
|
103
|
-
SPV.merkleRootFromBranch(txid, 0, []).should.equal(txid)
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
it('verifyTxInclusion binds the branch to a header merkle root', function () {
|
|
107
|
-
var leaves = randLeaves(5)
|
|
108
|
-
var rootInternal = rootOf(leaves)
|
|
109
|
-
var header = new bsv.BlockHeader({
|
|
110
|
-
version: 1,
|
|
111
|
-
prevHash: Buffer.alloc(32),
|
|
112
|
-
merkleRoot: rootInternal, // internal-LE buffer
|
|
113
|
-
time: 1231006505,
|
|
114
|
-
bits: 0x1d00ffff,
|
|
115
|
-
nonce: 0
|
|
116
|
-
})
|
|
117
|
-
var res = SPV.verifyTxInclusion({
|
|
118
|
-
txid: rev(leaves[3]).toString('hex'),
|
|
119
|
-
index: 3,
|
|
120
|
-
nodes: branchFor(leaves, 3),
|
|
121
|
-
header: header,
|
|
122
|
-
requirePow: false // synthetic header has no real PoW
|
|
123
|
-
})
|
|
124
|
-
res.rootMatches.should.equal(true)
|
|
125
|
-
res.valid.should.equal(true)
|
|
126
|
-
|
|
127
|
-
// Wrong index -> root mismatch.
|
|
128
|
-
SPV.verifyTxInclusion({
|
|
129
|
-
txid: rev(leaves[3]).toString('hex'), index: 1,
|
|
130
|
-
nodes: branchFor(leaves, 3), header: header, requirePow: false
|
|
131
|
-
}).valid.should.equal(false)
|
|
132
|
-
})
|
|
133
|
-
})
|