@smartledger/bsv 8.3.0 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/CHANGELOG.md +133 -0
  2. package/README.md +374 -222
  3. package/bsv-gdaf.min.js +49 -49
  4. package/bsv-script-helper.min.js +1 -1
  5. package/bsv-smartcontract.min.js +21 -21
  6. package/bsv.bundle.js +49 -49
  7. package/bsv.d.ts +0 -4
  8. package/bsv.min.js +49 -49
  9. package/docs/BRC220_BATCH_LEAF_AMENDMENT.md +194 -0
  10. package/docs/BRC220_PLAN.md +9 -0
  11. package/docs/MODULE_REFERENCE_COMPLETE.md +27 -27
  12. package/docs/README.md +2 -1
  13. package/docs/THREAT_MODEL.md +10 -2
  14. package/docs/advanced/UTXO_MANAGER_GUIDE.md +1 -1
  15. package/docs/getting-started/INSTALLATION.md +23 -23
  16. package/docs/getting-started/QUICK_START.md +7 -7
  17. package/docs/migration/FROM_BSV_1_5_6.md +5 -5
  18. package/index.js +10 -26
  19. package/lib/covenant/helpers.js +34 -35
  20. package/lib/covenant/pushtx.js +2 -2
  21. package/lib/custom-script-helper.js +0 -16
  22. package/lib/notaryhash/index.js +20 -0
  23. package/lib/notaryhash/merkle.js +8 -0
  24. package/lib/notaryhash/suites.js +17 -1
  25. package/lib/ordinals/ordlock.js +1 -1
  26. package/lib/smart_contract/debugger.js +2 -2
  27. package/lib/smart_contract/dsl.js +2 -2
  28. package/lib/smart_contract/index.js +0 -3
  29. package/lib/smart_contract/locks.js +1 -41
  30. package/lib/smart_contract/pels.js +1 -1
  31. package/lib/smart_contract/token.js +1 -1
  32. package/package.json +3 -2
  33. package/test/build/esm_wrapper.js +32 -8
  34. package/test/data/brc220-batch-vector.json +129 -0
  35. package/test/notaryhash/batch_leaf.js +140 -0
  36. package/test/notaryhash/batch_vector.js +282 -0
  37. package/test/notaryhash/interop.js +112 -0
  38. package/test/notaryhash/verify.js +5 -2
  39. package/test/ordinals/inscription.js +24 -14
  40. package/test/ordinals/ordlock.js +0 -46
  41. package/test/smart_contract/covenants.js +0 -38
  42. package/test/smart_contract/dsl_debugger.js +0 -9
  43. package/test/smart_contract/ordinal_transfer.js +0 -10
  44. package/test/smart_contract/sighash_marketplace.js +0 -10
  45. package/test/smart_contract/token_generalized.js +0 -10
  46. package/tools/gen-brc220-batch-vector.js +205 -0
  47. package/version.js +1 -1
@@ -23,42 +23,42 @@ var Signature = require('../crypto/signature')
23
23
  // SIGHASH_ALL | SIGHASH_FORKID — the BSV default these covenants are built for.
24
24
  var SIGHASH = Signature.SIGHASH_ALL | Signature.SIGHASH_FORKID // 0x41
25
25
 
26
- // Post-Genesis consensus + STANDARD RELAY flags — what mainnet miners actually
27
- // enforce. Includes SCRIPT_VERIFY_MINIMALDATA (every push must be minimal) and
28
- // SCRIPT_VERIFY_LOW_S (canonical signatures). Verifying with these flags locally
29
- // mirrors mainnet relay/consensus policy, so a covenant that passes here is
30
- // expected to be accepted on broadcast — catch non-relayable scripts before then.
31
- function flags () {
32
- var I = Interpreter
33
- return I.SCRIPT_VERIFY_P2SH |
34
- I.SCRIPT_VERIFY_STRICTENC |
35
- I.SCRIPT_VERIFY_DERSIG |
36
- I.SCRIPT_VERIFY_LOW_S |
37
- I.SCRIPT_VERIFY_MINIMALDATA |
38
- I.SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY |
39
- I.SCRIPT_VERIFY_CHECKSEQUENCEVERIFY |
40
- I.SCRIPT_ENABLE_SIGHASH_FORKID |
41
- I.SCRIPT_ENABLE_MAGNETIC_OPCODES |
42
- I.SCRIPT_ENABLE_MONOLITH_OPCODES |
43
- // REQUIRED, not optional. pushTxCore emits OP_RIGHT/OP_LEFT — see
44
- // extractHashOutputs and assertSighashType — and those bytes (180/181) are
45
- // upgradable NOPs on the network until Chronicle activates. Without this
46
- // flag the interpreter treats them as no-ops, exactly as a pre-Chronicle
47
- // node does, and the covenant does not verify.
48
- //
49
- // Stated plainly: OP_PUSH_TX covenants built by this library DEPEND on
50
- // Chronicle and cannot be spent on a pre-Chronicle chain. That was
51
- // previously invisible because the interpreter ran the string opcodes
52
- // unconditionally — more permissively than the network.
53
- I.SCRIPT_ENABLE_CHRONICLE
54
- }
55
-
56
26
  /**
57
- * Opt into post-Genesis script limits (needed by OP_PUSH_TX covenants).
58
- * Thin wrapper over Interpreter.useGenesisLimits (added in 4.1.0).
27
+ * Current BSV mainnet consensus + standard relay flags — what miners actually
28
+ * enforce. A covenant that verifies here is expected to be accepted on broadcast.
29
+ *
30
+ * This DELEGATES to Interpreter.mainnetFlags() rather than assembling a list, and
31
+ * that is the whole point. Until 8.4.0 it was hand-assembled, and the hand-written
32
+ * list omitted the three UTXO-ERA flags — SCRIPT_GENESIS, SCRIPT_UTXO_AFTER_GENESIS
33
+ * and SCRIPT_UTXO_AFTER_CHRONICLE. It carried SCRIPT_ENABLE_CHRONICLE, which enables
34
+ * the string opcodes, so the omission was easy to miss: the opcodes ran, and only the
35
+ * *limits* were wrong.
36
+ *
37
+ * The era flags are what the interpreter derives its data limits from. Without them
38
+ * every covenant was verified under PRE-GENESIS rules — a 520-byte element cap that
39
+ * BSV removed in February 2020. An OP_PUSH_TX preimage is ~585 bytes, so this
40
+ * library's flagship feature could not verify against its own harness, and
41
+ * `enableGenesis()` existed to paper over it by raising process-wide statics. That
42
+ * was treating the symptom: raising the statics cannot enable post-Genesis
43
+ * arithmetic (only the era flags can) and it weakens pre-Genesis validation, turning
44
+ * 15 of the reference node's 22 SCRIPTNUM_OVERFLOW vectors into false accepts.
45
+ *
46
+ * Delegating also means the covenant path tracks consensus automatically instead of
47
+ * drifting the next time an era activates. `mainnetFlags()` is the same function the
48
+ * Interpreter uses for a no-flags verify(), so "verify locally" and "what the network
49
+ * does" cannot diverge again without both moving together.
50
+ *
51
+ * Two flags in the old list are deliberately gone: SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY
52
+ * and SCRIPT_VERIFY_CHECKSEQUENCEVERIFY. Genesis reverted OP_CLTV/OP_CSV to
53
+ * upgradable NOPs for outputs created after it (see interpreter.js, the
54
+ * `isAfterGenesis()` short-circuit), so once the era flags are present these two
55
+ * change nothing. Keeping them would have implied a time-lock guarantee mainnet does
56
+ * not provide, which is why the CLTV-based locks were removed in 9.0.0.
57
+ *
58
+ * NULLFAIL is gained, which is strictly stricter.
59
59
  */
60
- function enableGenesis (max) {
61
- return Interpreter.useGenesisLimits(max)
60
+ function flags () {
61
+ return Interpreter.mainnetFlags()
62
62
  }
63
63
 
64
64
  /**
@@ -135,7 +135,6 @@ function scriptNum (n) {
135
135
  module.exports = {
136
136
  SIGHASH: SIGHASH,
137
137
  flags: flags,
138
- enableGenesis: enableGenesis,
139
138
  verify: verify,
140
139
  rawPreimage: rawPreimage,
141
140
  sighashDigest: sighashDigest,
@@ -13,8 +13,8 @@
13
13
  * Optimal parameters: private key a = 1, ephemeral k = 1 => r = Gx,
14
14
  * s = (e + Gx) mod n, pubkey P = 02||Gx, e = HASH256(preimage).
15
15
  *
16
- * Requires post-Genesis limits — call SmartContract.enableGenesis() (a.k.a
17
- * Interpreter.useGenesisLimits()) before verifying these scripts.
16
+ * Verifies under mainnet consensus flags with no opt-in (8.4.0+). Earlier versions
17
+ * required SmartContract.enableGenesis() first; that call is now a deprecated no-op.
18
18
  */
19
19
 
20
20
  var Script = require('../script')
@@ -71,22 +71,6 @@ class CustomScriptHelper {
71
71
  return script;
72
72
  }
73
73
 
74
- /**
75
- * Create a time-locked script (CHECKLOCKTIMEVERIFY)
76
- * @param {number} lockTime - Block height or timestamp
77
- * @param {Script} baseScript - Base script to execute after time lock
78
- * @returns {Script} Time-locked script
79
- */
80
- static createTimelockScript(lockTime, baseScript) {
81
- const lockTimeBuffer = Buffer.from(lockTime.toString(16).padStart(8, '0'), 'hex').reverse();
82
-
83
- return new Script()
84
- .add(lockTimeBuffer)
85
- .add(Opcode.OP_CHECKLOCKTIMEVERIFY)
86
- .add(Opcode.OP_DROP)
87
- .add(baseScript.toBuffer());
88
- }
89
-
90
74
  /**
91
75
  * Create a conditional script (IF/ELSE/ENDIF)
92
76
  * @param {Script} ifScript - Script to execute if condition is true
@@ -241,6 +241,26 @@ NotaryHash.verifyBatchInclusion = function (certificate) {
241
241
  var m = certificate.merkle
242
242
  if (!m) return { valid: false, errors: ['batch certificate has no merkle proof'] }
243
243
 
244
+ // THE LEAF IS proofHash, NOT canonicalBytes.
245
+ //
246
+ // BRC-220 §On-chain record says the batch tree is RFC 6962 and writes
247
+ // `leaf = SHA256(0x00 ‖ d)` — but that is RFC 6962's own generic notation for the
248
+ // construction, and the spec never binds `d` to a value. `canonicalBytes` appears
249
+ // once in the whole document, in the proofHash definition, and nowhere in the batch
250
+ // text.
251
+ //
252
+ // So this is a choice the implementation had to make, and it is:
253
+ //
254
+ // leaf = SHA-256(0x00 ‖ proofHash) = SHA-256(0x00 ‖ SHA-256(canonicalBytes))
255
+ //
256
+ // The other reading — d = canonicalBytes — is equally sound cryptographically and
257
+ // produces a DIFFERENT root. A certificate built that way fails inclusion here with
258
+ // no error naming the cause: the fold simply does not reach the root. Anyone building
259
+ // batches with the exported NotaryHash.Merkle must use proofHash as the leaf datum.
260
+ //
261
+ // Reasoning and the proposed spec text: docs/BRC220_BATCH_LEAF_AMENDMENT.md.
262
+ // Enforced by test/notaryhash/batch_leaf.js, which checks that a canonicalBytes-leaf
263
+ // tree is rejected rather than trusting the comment.
244
264
  var leafData = Buffer.from(certificate.proofHash, 'hex')
245
265
  var path = (m.path || []).map(function (node) {
246
266
  return Buffer.isBuffer(node) ? node : Buffer.from(node, 'hex')
@@ -26,6 +26,14 @@ var $ = require('../util/preconditions')
26
26
  * root. Every non-power-of-two tree depends on getting this right.
27
27
  */
28
28
 
29
+ /**
30
+ * For BRC-220 batch mode the leaf datum `d` is a certificate's **proofHash**, so a leaf
31
+ * is `SHA-256(0x00 ‖ proofHash)`. The spec writes `leaf = SHA256(0x00 ‖ d)` using
32
+ * RFC 6962's generic notation and never binds `d`; passing `canonicalBytes` instead is
33
+ * equally sound and yields a different root, which NotaryHash.verifyBatchInclusion will
34
+ * reject. See docs/BRC220_BATCH_LEAF_AMENDMENT.md.
35
+ */
36
+
29
37
  var Merkle = {}
30
38
 
31
39
  /** Prefix bytes, per RFC 6962 §2.1. */
@@ -146,7 +146,23 @@ Suites.register('ECDSA-secp256k1', {
146
146
 
147
147
  var ecdsa = new ECDSA()
148
148
  ecdsa.hashbuf = payloadHash
149
- ecdsa.endian = 'little'
149
+ // NO endian override. `payloadHash` is the scalar, big-endian, exactly as the
150
+ // 32 bytes are given — which is what every other ECDSA implementation does and
151
+ // what BRC-220 means by "signs the payloadHash".
152
+ //
153
+ // 8.3.0 set `endian = 'little'` here, which made ECDSA reverse the digest before
154
+ // reducing it to the scalar. That is Bitcoin's message-signing convention, not
155
+ // this protocol's, and it is why lib/crypto/ecdsa.js offers the option at all.
156
+ //
157
+ // The effect was on the VERIFY side: a signature produced the way BRC-220
158
+ // specifies was rejected, and the only signatures accepted were ones made with
159
+ // bsv's own little-endian convention. Every test in this repo signed that way
160
+ // too, so the module and its tests were self-consistent and wrong together —
161
+ // the exact failure shape test/notaryhash/encoding.js warns about in its header.
162
+ //
163
+ // test/notaryhash/interop.js now pins this against @noble/curves, which shares
164
+ // no verification code with lib/crypto/ecdsa.js. A second implementation is the
165
+ // only thing that can catch a defect where the code and its tests agree.
150
166
  ecdsa.pubkey = pubkey
151
167
  ecdsa.sig = sig
152
168
  return ecdsa.verify() === true
@@ -35,7 +35,7 @@
35
35
  * OP_ENDIF
36
36
  * [OP_FALSE OP_IF "ord" … OP_ENDIF] // optional inert inscription envelope
37
37
  *
38
- * Requires post-Genesis limits: call SmartContract.enableGenesis() before verifying.
38
+ * Verifies under mainnet consensus flags with no opt-in (8.4.0+).
39
39
  */
40
40
  var P = require('../covenant/pushtx')
41
41
  var H = require('../covenant/helpers')
@@ -5,8 +5,8 @@
5
5
  * Step-traces a locking/unlocking pair through Script.Interpreter and records the
6
6
  * stack + alt-stack after every opcode, so you can watch an OP_PUSH_TX covenant
7
7
  * build its signature and enforce its constraints. Uses the interpreter's
8
- * stepListener hook. Call SmartContract.enableGenesis() first for OP_PUSH_TX
9
- * covenants (their preimage element / opcode count exceed pre-Genesis limits).
8
+ * stepListener hook. No opt-in is needed since 8.4.0: covenants verify under mainnet
9
+ * consensus flags, whose era bits lift the pre-Genesis element and opcode caps.
10
10
  */
11
11
 
12
12
  var Interpreter = require('../script/interpreter')
@@ -14,8 +14,8 @@
14
14
  * // c.unlock(spendTx, satoshis) -> grinds + returns the preimage unlock Script
15
15
  *
16
16
  * Each clause compiles to one preimage-field check on top of a single OP_PUSH_TX
17
- * authentication; clauses AND together. Requires post-Genesis limits at verify
18
- * time (SmartContract.enableGenesis()).
17
+ * authentication; clauses AND together. Verifies under mainnet consensus flags with
18
+ * no opt-in (8.4.0+).
19
19
  */
20
20
 
21
21
  var PushTx = require('../covenant/pushtx')
@@ -70,9 +70,6 @@ SmartContract.trace = function (unlockingScript, lockingScript, opts) {
70
70
 
71
71
  // --- Covenant convenience API (v4.2.0) ---
72
72
  // Opt into post-Genesis script limits (required by OP_PUSH_TX covenants).
73
- SmartContract.enableGenesis = function (max) {
74
- return SmartContract.CovenantHelpers.enableGenesis(max)
75
- }
76
73
  // Verify an unlocking/locking pair through the consensus interpreter.
77
74
  SmartContract.verifyScript = function (unlockingScript, lockingScript, opts) {
78
75
  return SmartContract.CovenantHelpers.verify(unlockingScript, lockingScript, opts)
@@ -10,7 +10,6 @@ var H = require('../covenant/helpers')
10
10
  var Script = require('../script')
11
11
  var Opcode = require('../opcode')
12
12
  var signInput = H.signInput
13
- var n = H.scriptNum
14
13
 
15
14
  /** Hash-lock: reveal a preimage whose SHA-256 matches a digest. */
16
15
  function hashLock (secret) {
@@ -36,20 +35,6 @@ function p2pkh (privateKey) {
36
35
  }
37
36
  }
38
37
 
39
- /** CLTV absolute time-lock: <locktime> CLTV DROP <pk> CHECKSIG. */
40
- function timeLockCLTV (privateKey, locktime) {
41
- var pub = privateKey.toPublicKey()
42
- var lock = new Script()
43
- .add(n(locktime)).add(Opcode.OP_CHECKLOCKTIMEVERIFY).add(Opcode.OP_DROP)
44
- .add(pub.toBuffer()).add(Opcode.OP_CHECKSIG)
45
- return {
46
- lock: lock,
47
- meta: { name: 'cltv-timelock', locktime: locktime },
48
- // Caller must set spend.nLockTime >= locktime and input sequence < 0xffffffff.
49
- unlock: function (spend, sats) { return new Script().add(signInput(spend, privateKey, 0, lock, sats)) }
50
- }
51
- }
52
-
53
38
  /** m-of-n multisig. */
54
39
  function multisig (m, privateKeys) {
55
40
  var pubs = privateKeys.map(function (k) { return k.toPublicKey() })
@@ -68,33 +53,8 @@ function multisig (m, privateKeys) {
68
53
  }
69
54
  }
70
55
 
71
- /** HTLC: hash-OR-timeout. */
72
- function htlc (opts) {
73
- var digest = Hash.sha256(opts.secret)
74
- var lock = new Script()
75
- .add(Opcode.OP_IF)
76
- .add(Opcode.OP_SHA256).add(digest).add(Opcode.OP_EQUALVERIFY)
77
- .add(opts.receiver.toPublicKey().toBuffer()).add(Opcode.OP_CHECKSIG)
78
- .add(Opcode.OP_ELSE)
79
- .add(n(opts.timeout)).add(Opcode.OP_CHECKLOCKTIMEVERIFY).add(Opcode.OP_DROP)
80
- .add(opts.sender.toPublicKey().toBuffer()).add(Opcode.OP_CHECKSIG)
81
- .add(Opcode.OP_ENDIF)
82
- return {
83
- lock: lock,
84
- meta: { name: 'htlc', digest: digest.toString('hex'), timeout: opts.timeout },
85
- unlockClaim: function (spend, sats) {
86
- return new Script().add(signInput(spend, opts.receiver, 0, lock, sats)).add(opts.secret).add(Opcode.OP_1)
87
- },
88
- unlockRefund: function (spend, sats) {
89
- return new Script().add(signInput(spend, opts.sender, 0, lock, sats)).add(Opcode.OP_0)
90
- }
91
- }
92
- }
93
-
94
56
  module.exports = {
95
57
  hashLock: hashLock,
96
58
  p2pkh: p2pkh,
97
- timeLockCLTV: timeLockCLTV,
98
- multisig: multisig,
99
- htlc: htlc
59
+ multisig: multisig
100
60
  }
@@ -14,7 +14,7 @@
14
14
  * the slice preimage[104 : len-52] is exactly the `scriptlen||script` half of a
15
15
  * TxOut, so the next output is `<newValue:8-LE> || preimage[104:len-52]`.
16
16
  *
17
- * Requires post-Genesis limits (SmartContract.enableGenesis()).
17
+ * Verifies under mainnet consensus flags with no opt-in (8.4.0+).
18
18
  */
19
19
 
20
20
  var P = require('../covenant/pushtx')
@@ -36,7 +36,7 @@
36
36
  * surrounding output bytes and the token's value; the covenant binds them
37
37
  * into the committed hashOutputs. Value conservation is left to the network.
38
38
  *
39
- * Requires post-Genesis limits (SmartContract.enableGenesis()).
39
+ * Verifies under mainnet consensus flags with no opt-in (8.4.0+).
40
40
  */
41
41
 
42
42
  var BN = require('../crypto/bn')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smartledger/bsv",
3
- "version": "8.3.0",
3
+ "version": "9.0.0",
4
4
  "description": "🚀 Complete Bitcoin SV development framework with legally-recognizable DID:web + W3C VC-JWT toolkit, Legal Token Protocol (LTP), Global Digital Attestation Framework (GDAF), StatusList2021 revocation, and 16 flexible loading options. Standards-based credentials with ES256/ES256K support, on-chain BSV anchoring, and comprehensive Bitcoin SV API. Perfect for legal tokens, verifiable credentials, DeFi, smart contracts, and secure Bitcoin applications.",
5
5
  "author": "SmartLedger Technology <hello@smartledger.technology> (https://smartledger.technology)",
6
6
  "homepage": "https://github.com/codenlighten/smartledger-bsv#readme",
@@ -65,6 +65,7 @@
65
65
  "vectors:sv-tx": "node tools/sv-tx-report.js",
66
66
  "vectors:sv-sighash": "node tools/sv-sighash-report.js",
67
67
  "conformance:generate": "node conformance/generate.js",
68
+ "check:readme": "node scripts/check-readme-accuracy.js",
68
69
  "lint:ratchet": "node scripts/lint-ratchet.js",
69
70
  "lint:ratchet:update": "node scripts/lint-ratchet.js --update",
70
71
  "test": "mocha",
@@ -97,7 +98,7 @@
97
98
  "test:browser": "echo 'Open tests/standalone-modules-test.html in browser for comprehensive testing'",
98
99
  "test:bundle": "echo 'Open tests/bundle-completeness-test.html in browser to verify bundle completeness'",
99
100
  "preimage:extract": "node examples/preimage/extract_preimage_bidirectional.js",
100
- "prepublishOnly": "npm run build-all",
101
+ "prepublishOnly": "npm run build-all && npm run check:readme",
101
102
  "sync-cdn": "node scripts/sync-cdn-urls.js",
102
103
  "version": "node scripts/sync-cdn-urls.js && node scripts/sync-version.js && node scripts/gen-esm-wrapper.js && git add README.md docs version.js index.mjs",
103
104
  "test:browser:ci": "node tests/browser-smoke-runner.js"
@@ -43,9 +43,30 @@ describe('ESM wrapper (index.mjs)', function () {
43
43
  var bsv = require('../..')
44
44
  var names = gen.exportableNames(bsv)
45
45
  names.length.should.be.above(100)
46
- // SmartUTXO is an accessor (deprecated getter) and must be excluded.
47
- names.should.not.include('SmartUTXO')
48
- Object.getOwnPropertyDescriptor(bsv, 'SmartUTXO').get.should.be.a('function')
46
+ })
47
+
48
+ // The library currently has NO accessor properties — bsv.SmartUTXO, the last one,
49
+ // was removed in 9.0.0. The exclusion rule still matters: re-exporting a getter as a
50
+ // named ESM binding evaluates it at import time, which for a deprecated accessor
51
+ // means a warning (or worse, a side effect) fires on a bare `import`. Testing that
52
+ // against a synthetic accessor keeps the guarantee under test instead of letting it
53
+ // rot untested until someone adds a getter back.
54
+ itFromCheckout('excludes accessor properties from the named bindings', function () {
55
+ var bsv = require('../..')
56
+ var probe = '__esmAccessorProbe__'
57
+ var touched = false
58
+ Object.defineProperty(bsv, probe, {
59
+ configurable: true,
60
+ enumerable: true,
61
+ get: function () { touched = true; return 1 }
62
+ })
63
+ try {
64
+ var names = gen.exportableNames(bsv)
65
+ names.should.not.include(probe)
66
+ touched.should.equal(false, 'exportableNames must not invoke the getter')
67
+ } finally {
68
+ delete bsv[probe]
69
+ }
49
70
  })
50
71
 
51
72
  it('resolves real ESM default + named imports natively', function () {
@@ -55,9 +76,9 @@ describe('ESM wrapper (index.mjs)', function () {
55
76
  'PrivateKey === bsv.PrivateKey && typeof crypto.ECDSA === "function") ? 0 : 3)')
56
77
  })
57
78
 
58
- it('does not emit the SmartUTXO deprecation warning at import (getter excluded)', function () {
59
- // Re-exporting the getter would trip its console.warn at import time; stderr
60
- // must be empty for a bare `import './index.mjs'`.
79
+ it('emits nothing on stderr at import', function () {
80
+ // Re-exporting a deprecated getter would trip its console.warn at import time;
81
+ // stderr must be empty for a bare `import './index.mjs'`.
61
82
  var res = spawnSync(process.execPath,
62
83
  ['--input-type=module', '-e', 'import ' + JSON.stringify('./index.mjs') + ';'],
63
84
  { cwd: ROOT, encoding: 'utf8' })
@@ -65,9 +86,12 @@ describe('ESM wrapper (index.mjs)', function () {
65
86
  res.stderr.should.equal('')
66
87
  })
67
88
 
68
- it('keeps deprecated getters reachable via the default export only', function () {
89
+ it('no longer exposes bsv.SmartUTXO, which was removed in 9.0.0', function () {
90
+ // Deliberately asserted rather than dropped: the namespace export is gone, and the
91
+ // module is reached by direct import — require('@smartledger/bsv/lib/smartutxo') —
92
+ // which is what its deprecation warning instructed from 4.0.1 onward.
69
93
  runEsm(
70
94
  'import bsv from ' + JSON.stringify('./index.mjs') + ';' +
71
- 'process.exit(("SmartUTXO" in bsv) ? 0 : 3)')
95
+ 'process.exit(("SmartUTXO" in bsv) ? 3 : 0)')
72
96
  })
73
97
  })
@@ -0,0 +1,129 @@
1
+ {
2
+ "_comment": "BRC-220 batch-mode golden vector. Generated by tools/gen-brc220-batch-vector.js; do not hand-edit. Every value is derived from the labelled preimages recorded on each proof, so the file is reproducible from scratch in any language. THE POINT OF THIS FILE: BRC-220 writes the batch leaf as SHA256(0x00 || d) and never says what d is. This vector fixes d = proofHash. The `rejectedReading` block records what the same five proofs produce under d = canonicalBytes — equally sound cryptographically, a different root, and therefore not interoperable. An implementation matching `rejectedReading.root` has made the other choice, which is exactly the confusion this vector exists to make visible. This is a conformance target produced by @smartledger/bsv, not external validation. The tree beneath it is externally anchored: the RFC 6962 construction is verified against the published Certificate Transparency roots in test/notaryhash/merkle.js. n = 5 deliberately: RFC 6962 splits at the largest power of two BELOW n, so this splits 4/1 (a midpoint split would give 2/3 and a different root), and the odd count exercises the rule that the last leaf is never duplicated.",
3
+ "spec": "BRC-220",
4
+ "tree": "RFC 6962",
5
+ "leafDatum": "proofHash",
6
+ "leafRule": "leaf = SHA-256(0x00 || proofHash); node = SHA-256(0x01 || left || right)",
7
+ "leafCount": 5,
8
+ "root": "abb53eb3b2e3530d51685c6813bb85c85892d2f214c2dc504029d071434ac074",
9
+ "onChainRecordTail": {
10
+ "_comment": "The batch record is \"NOTARYHASH\" | u8(1) | u8(2) | merkleRoot(32) | u32be(leafCount).",
11
+ "merkleRoot": "abb53eb3b2e3530d51685c6813bb85c85892d2f214c2dc504029d071434ac074",
12
+ "leafCountU32be": "00000005"
13
+ },
14
+ "rejectedReading": {
15
+ "_comment": "What d = canonicalBytes yields over the SAME five proofs. Recorded so the divergence is checkable, not to endorse it.",
16
+ "leafDatum": "canonicalBytes",
17
+ "root": "4a59ade74941e4d6ca4b7c9f610e0a01a0c3f24cfa94efe816e53ef85f99e324"
18
+ },
19
+ "proofs": [
20
+ {
21
+ "leafIndex": 0,
22
+ "keyPreimage": "BRC-220/batch-vector/key/0",
23
+ "payloadPreimage": "BRC-220/batch-vector/payload/0",
24
+ "algorithm": "ECDSA-secp256k1",
25
+ "hashAlgorithm": "SHA-256",
26
+ "encoding": "raw",
27
+ "payloadHash": "c61d9c7e35b46dc1ceea30094e1a2809d45d16014fa70ccfc75fc4d6f672643b",
28
+ "publicKey": "02ce29a831e3d359386c7f70ef7fd85a75d79168d546dd8a60a520df0e24c65ef3",
29
+ "signature": "1026f99510a52863e43e3e5a57395c9838a3e44a768932d35b460a287630c24e3bc3b81e1bb2cb7c834f5be97936a6a49ff9eb7adaae7670e32d6bc3a5c067cf",
30
+ "createdAt": "2026-01-01T00:00:00.000Z",
31
+ "createdAtUnix": 1767225600,
32
+ "canonicalBytes": "0000000e4e6f74617279486173682f312e30010000000f45434453412d736563703235366b31000000075348412d32353600000020c61d9c7e35b46dc1ceea30094e1a2809d45d16014fa70ccfc75fc4d6f672643b0000002102ce29a831e3d359386c7f70ef7fd85a75d79168d546dd8a60a520df0e24c65ef3000000401026f99510a52863e43e3e5a57395c9838a3e44a768932d35b460a287630c24e3bc3b81e1bb2cb7c834f5be97936a6a49ff9eb7adaae7670e32d6bc3a5c067cf000000006955b900",
33
+ "proofHash": "83f3f73ebc5146b96cce1ee0b6ed4fd251a933a5332fbe5e5ab80da7920fe2c9",
34
+ "leafHash": "27547ba238fba426b766979a4a46cd97578945cf450c113e45b487291cb56ba4",
35
+ "leafHashUnderRejectedReading": "ab0ee539d3e08e07a6aac157af2d3df5da5f637c5c360fa158a00d14c6644f05",
36
+ "path": [
37
+ "7cd7c9b4bc74dcc5d586f7b04f1f7bdf025a15f7e173dba021c8ed7fd84acdbf",
38
+ "15c4df48172c7607990059e0f646616f7e68a33f8f5e1a7b54a9fa5ee770bbb4",
39
+ "8591a9b65f86529312b0eae212a95c15fd407f36cb8f4872a01bc0a64f13df83"
40
+ ]
41
+ },
42
+ {
43
+ "leafIndex": 1,
44
+ "keyPreimage": "BRC-220/batch-vector/key/1",
45
+ "payloadPreimage": "BRC-220/batch-vector/payload/1",
46
+ "algorithm": "ECDSA-secp256k1",
47
+ "hashAlgorithm": "SHA-256",
48
+ "encoding": "raw",
49
+ "payloadHash": "7509fe7fc39be0a6a6fc7350287f96a03911c8a3af188d8fb4b42f92957562a1",
50
+ "publicKey": "02b3dfb7e2a167aa401547d93e1b684b6c9896b67d3aadb7d938cdb00040e64ce3",
51
+ "signature": "1014debea11afb698d3fe0778bfd6390efdb28f8fe160eb4f382213f35641afd4a308a159d342a7507873fff587eaad5ae73eed047b1dbb27ee2b6b0924f6d60",
52
+ "createdAt": "2026-01-02T00:00:00.000Z",
53
+ "createdAtUnix": 1767312000,
54
+ "canonicalBytes": "0000000e4e6f74617279486173682f312e30010000000f45434453412d736563703235366b31000000075348412d323536000000207509fe7fc39be0a6a6fc7350287f96a03911c8a3af188d8fb4b42f92957562a10000002102b3dfb7e2a167aa401547d93e1b684b6c9896b67d3aadb7d938cdb00040e64ce3000000401014debea11afb698d3fe0778bfd6390efdb28f8fe160eb4f382213f35641afd4a308a159d342a7507873fff587eaad5ae73eed047b1dbb27ee2b6b0924f6d600000000069570a80",
55
+ "proofHash": "29fca10808a3653efbe38abd5f0b926aa09f13bff7847475139a422869729e77",
56
+ "leafHash": "7cd7c9b4bc74dcc5d586f7b04f1f7bdf025a15f7e173dba021c8ed7fd84acdbf",
57
+ "leafHashUnderRejectedReading": "320aeefe6077a6163d6d72bda3b54443bb973220ef94a448e7e8c9be787f1242",
58
+ "path": [
59
+ "27547ba238fba426b766979a4a46cd97578945cf450c113e45b487291cb56ba4",
60
+ "15c4df48172c7607990059e0f646616f7e68a33f8f5e1a7b54a9fa5ee770bbb4",
61
+ "8591a9b65f86529312b0eae212a95c15fd407f36cb8f4872a01bc0a64f13df83"
62
+ ]
63
+ },
64
+ {
65
+ "leafIndex": 2,
66
+ "keyPreimage": "BRC-220/batch-vector/key/2",
67
+ "payloadPreimage": "BRC-220/batch-vector/payload/2",
68
+ "algorithm": "ECDSA-secp256k1",
69
+ "hashAlgorithm": "SHA-256",
70
+ "encoding": "raw",
71
+ "payloadHash": "b09ac5900c811654d3c31b43ad6c65512f9baa6585b121bdc88f43b40cbc55a0",
72
+ "publicKey": "02363ba0177d207ee13a23b63c9feb5c297e071c88c1d859312bf7cb46778b17f7",
73
+ "signature": "726113d4d8dafbe8f03b89377ffad4a4086ee30450c3e03ea62ae30d26c216091147dbfb36fa6c6f7e7e59eeae21c9b379e8fd012c7f8b87ac4ff416981dff11",
74
+ "createdAt": "2026-01-03T00:00:00.000Z",
75
+ "createdAtUnix": 1767398400,
76
+ "canonicalBytes": "0000000e4e6f74617279486173682f312e30010000000f45434453412d736563703235366b31000000075348412d32353600000020b09ac5900c811654d3c31b43ad6c65512f9baa6585b121bdc88f43b40cbc55a00000002102363ba0177d207ee13a23b63c9feb5c297e071c88c1d859312bf7cb46778b17f700000040726113d4d8dafbe8f03b89377ffad4a4086ee30450c3e03ea62ae30d26c216091147dbfb36fa6c6f7e7e59eeae21c9b379e8fd012c7f8b87ac4ff416981dff110000000069585c00",
77
+ "proofHash": "3c44828bea3ca01dfcd5305a839ce02cc1e855f5a7c7028bb4931d42dea3aa2b",
78
+ "leafHash": "9419bb538d2bb38e931ea45f2a6e83d38f99bf2679f6e31cd02145e68cda1d54",
79
+ "leafHashUnderRejectedReading": "b6beedf0fd93c2d32fe08db38242ec737e9f03605c457f0910ab42ff0f7c2eed",
80
+ "path": [
81
+ "341883196d41f5956f6b87238f71018be2bfc6ced6676ba64f5b1b0369b199a5",
82
+ "503a52c36f87e417987746db6964fbc21a177f034cc58639784e1e5b313cc7e4",
83
+ "8591a9b65f86529312b0eae212a95c15fd407f36cb8f4872a01bc0a64f13df83"
84
+ ]
85
+ },
86
+ {
87
+ "leafIndex": 3,
88
+ "keyPreimage": "BRC-220/batch-vector/key/3",
89
+ "payloadPreimage": "BRC-220/batch-vector/payload/3",
90
+ "algorithm": "ECDSA-secp256k1",
91
+ "hashAlgorithm": "SHA-256",
92
+ "encoding": "raw",
93
+ "payloadHash": "158d3edf327c1994d7154f9271dadc5d96418b54713aafbbf9ad6c534bf3dbe2",
94
+ "publicKey": "02927267de13d07285b06122293a72350376cc85060b00b8426c48e0cb2e39e2f9",
95
+ "signature": "8c1625d984577226d3f229d83aa1b3025821d1ef076b890f21667c62f49508640eaeb5ac1bfc2250a0b475653a598fa5f69f28b7e23aad18986a6f4a468d490b",
96
+ "createdAt": "2026-01-04T00:00:00.000Z",
97
+ "createdAtUnix": 1767484800,
98
+ "canonicalBytes": "0000000e4e6f74617279486173682f312e30010000000f45434453412d736563703235366b31000000075348412d32353600000020158d3edf327c1994d7154f9271dadc5d96418b54713aafbbf9ad6c534bf3dbe20000002102927267de13d07285b06122293a72350376cc85060b00b8426c48e0cb2e39e2f9000000408c1625d984577226d3f229d83aa1b3025821d1ef076b890f21667c62f49508640eaeb5ac1bfc2250a0b475653a598fa5f69f28b7e23aad18986a6f4a468d490b000000006959ad80",
99
+ "proofHash": "0692e22da9b68634e5384bb012324c140c676d2301e078a67f17e335661a9565",
100
+ "leafHash": "341883196d41f5956f6b87238f71018be2bfc6ced6676ba64f5b1b0369b199a5",
101
+ "leafHashUnderRejectedReading": "ca8d122b3f396533867bbe79cc0b878a3ece675ef636622cb321843cfc2b0a6c",
102
+ "path": [
103
+ "9419bb538d2bb38e931ea45f2a6e83d38f99bf2679f6e31cd02145e68cda1d54",
104
+ "503a52c36f87e417987746db6964fbc21a177f034cc58639784e1e5b313cc7e4",
105
+ "8591a9b65f86529312b0eae212a95c15fd407f36cb8f4872a01bc0a64f13df83"
106
+ ]
107
+ },
108
+ {
109
+ "leafIndex": 4,
110
+ "keyPreimage": "BRC-220/batch-vector/key/4",
111
+ "payloadPreimage": "BRC-220/batch-vector/payload/4",
112
+ "algorithm": "ECDSA-secp256k1",
113
+ "hashAlgorithm": "SHA-256",
114
+ "encoding": "raw",
115
+ "payloadHash": "1c9be18eba3f6d45e49ba3cc651978d803c4a031e61e9c4ea0c5c4954d0fc7b8",
116
+ "publicKey": "02497aeb1118e5fe44af0aade81945dc23b8c727433a9d76e2ec37e914ce0467cf",
117
+ "signature": "c903ba5d61bdbd983168febeabba3997c8811a24cf4624f6952887f6dd50c9141d07a20abcb7e6822a1012f15c8b5df63d52d28b13cddf9efce451d2a020a953",
118
+ "createdAt": "2026-01-05T00:00:00.000Z",
119
+ "createdAtUnix": 1767571200,
120
+ "canonicalBytes": "0000000e4e6f74617279486173682f312e30010000000f45434453412d736563703235366b31000000075348412d323536000000201c9be18eba3f6d45e49ba3cc651978d803c4a031e61e9c4ea0c5c4954d0fc7b80000002102497aeb1118e5fe44af0aade81945dc23b8c727433a9d76e2ec37e914ce0467cf00000040c903ba5d61bdbd983168febeabba3997c8811a24cf4624f6952887f6dd50c9141d07a20abcb7e6822a1012f15c8b5df63d52d28b13cddf9efce451d2a020a95300000000695aff00",
121
+ "proofHash": "1a9c6bc1a6cf8319344ca8c30ca9e9eef8ab3df732748f6be2c824bb84ec0ca9",
122
+ "leafHash": "8591a9b65f86529312b0eae212a95c15fd407f36cb8f4872a01bc0a64f13df83",
123
+ "leafHashUnderRejectedReading": "40cfb9005011331c8cbb4524c27b04b3e62cbe525e07ec1dc443de25079b16d6",
124
+ "path": [
125
+ "060ca4e4bbcb647ab0162bb027cd8f08c1577aaa7069166dd629a3df7e57befd"
126
+ ]
127
+ }
128
+ ]
129
+ }