@smartledger/bsv 7.8.1 → 7.10.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 (76) hide show
  1. package/CHANGELOG.md +117 -6
  2. package/README.md +38 -38
  3. package/bsv-covenant.min.js +1 -1
  4. package/bsv-ecies.min.js +1 -1
  5. package/bsv-gdaf.min.js +54 -54
  6. package/bsv-ltp.min.js +58 -85
  7. package/bsv-message.min.js +2 -2
  8. package/bsv-mnemonic.min.js +12 -14
  9. package/bsv-script-helper.min.js +1 -1
  10. package/bsv-shamir.min.js +14 -16
  11. package/bsv-smartcontract.min.js +23 -25
  12. package/bsv.bundle.js +54 -54
  13. package/bsv.min.js +54 -54
  14. package/build/esbuild.js +84 -4
  15. package/docs/MODULE_REFERENCE_COMPLETE.md +27 -27
  16. package/docs/advanced/UTXO_MANAGER_GUIDE.md +1 -1
  17. package/docs/getting-started/INSTALLATION.md +23 -23
  18. package/docs/getting-started/QUICK_START.md +7 -7
  19. package/docs/migration/FROM_BSV_1_5_6.md +5 -5
  20. package/index.js +9 -1
  21. package/lib/covenant/helpers.js +146 -0
  22. package/lib/covenant/index.js +21 -0
  23. package/lib/covenant/pushtx.js +243 -0
  24. package/lib/covenant-interface.js +408 -397
  25. package/lib/custom-script-helper.js +44 -38
  26. package/lib/ecies/bitcore-ecies.js +3 -4
  27. package/lib/ecies/electrum-ecies.js +4 -6
  28. package/lib/ecies/errors.js +1 -1
  29. package/lib/gdaf/attestation-signer.js +7 -7
  30. package/lib/gdaf/attestation-verifier.js +5 -6
  31. package/lib/gdaf/did-resolver.js +10 -9
  32. package/lib/gdaf/smartledger-anchor.js +13 -12
  33. package/lib/gdaf/zk-prover.js +8 -8
  34. package/lib/ltp/anchor.js +2 -3
  35. package/lib/ltp/claim.js +2 -3
  36. package/lib/ltp/index.js +2 -3
  37. package/lib/ltp/obligation.js +9 -9
  38. package/lib/ltp/proof.js +7 -7
  39. package/lib/ltp/registry.js +4 -5
  40. package/lib/ltp/right.js +11 -11
  41. package/lib/message/index.js +5 -4
  42. package/lib/message/message.js +11 -11
  43. package/lib/mnemonic/errors.js +1 -1
  44. package/lib/mnemonic/index.js +5 -4
  45. package/lib/mnemonic/mnemonic.js +22 -10
  46. package/lib/ordinals/bsv20.js +4 -4
  47. package/lib/ordinals/inscription.js +8 -12
  48. package/lib/ordinals/ordlock.js +26 -21
  49. package/lib/script/interpreter.js +96 -31
  50. package/lib/smart_contract/authorizers.js +8 -6
  51. package/lib/smart_contract/builder.js +32 -26
  52. package/lib/smart_contract/covenant.js +32 -25
  53. package/lib/smart_contract/covenant_helpers.js +8 -128
  54. package/lib/smart_contract/debugger.js +9 -6
  55. package/lib/smart_contract/dsl.js +5 -6
  56. package/lib/smart_contract/index.js +11 -8
  57. package/lib/smart_contract/locks.js +3 -4
  58. package/lib/smart_contract/opcode_list.js +2 -3
  59. package/lib/smart_contract/pels.js +4 -5
  60. package/lib/smart_contract/preimage.js +11 -7
  61. package/lib/smart_contract/pushtx.js +8 -263
  62. package/lib/smart_contract/script_interpreter.js +112 -105
  63. package/lib/smart_contract/script_tester.js +24 -18
  64. package/lib/smart_contract/script_utils.js +15 -14
  65. package/lib/smart_contract/sighash.js +23 -15
  66. package/lib/smart_contract/stack_examiner.js +70 -63
  67. package/lib/smart_contract/token.js +6 -6
  68. package/lib/smart_contract/utxo_generator.js +21 -15
  69. package/lib/smartutxo.js +27 -21
  70. package/lib/spv/headerchain.js +3 -3
  71. package/lib/spv/merkleproof.js +3 -3
  72. package/package.json +4 -2
  73. package/test/script/chronicle.js +53 -10
  74. package/test/script/interpreter.js +9 -6
  75. package/test/script/string_ops.js +26 -10
  76. package/version.js +1 -1
@@ -15,4 +15,4 @@ var spec = {
15
15
  }]
16
16
  }
17
17
 
18
- module.exports = require('../../').errors.extend(spec)
18
+ module.exports = require('../errors').extend(spec)
@@ -1,4 +1,5 @@
1
- var bsv = require('../../')
2
- bsv.Mnemonic = require('./mnemonic')
3
-
4
- module.exports = bsv.Mnemonic
1
+ // Previously this module required the package root and registered itself onto
2
+ // it (`bsv.Mnemonic = require('./mnemonic')`), which made lib/ depend on
3
+ // index.js. The registration was redundant — index.js assigns bsv.Mnemonic
4
+ // itself — so this is now a plain re-export.
5
+ module.exports = require('./mnemonic')
@@ -1,17 +1,20 @@
1
1
  'use strict'
2
2
 
3
- var bsv = require('../../')
4
- var BN = bsv.crypto.BN
3
+ var HDPrivateKey = require('../hdprivatekey')
4
+ var BN = require('../crypto/bn')
5
5
  var unorm = require('unorm')
6
- var _ = bsv.deps._
6
+ var _ = require('../util/_')
7
7
 
8
8
  var pbkdf2 = require('./pbkdf2')
9
9
  var errors = require('./errors')
10
+ // Named bsvErrors, not errors: `errors` above is this module's OWN error set.
11
+ // The library-wide errors module is a different object and must not shadow it.
12
+ var bsvErrors = require('../errors')
10
13
 
11
- var Hash = bsv.crypto.Hash
12
- var Random = bsv.crypto.Random
14
+ var Hash = require('../crypto/hash')
15
+ var Random = require('../crypto/random')
13
16
 
14
- var $ = bsv.util.preconditions
17
+ var $ = require('../util/preconditions')
15
18
 
16
19
  /**
17
20
  * This is an immutable class that represents a BIP39 Mnemonic code.
@@ -53,7 +56,7 @@ var Mnemonic = function (data, wordlist) {
53
56
  } else if (_.isNumber(data)) {
54
57
  ent = data
55
58
  } else if (data) {
56
- throw new bsv.errors.InvalidArgument('data', 'Must be a Buffer, a string or an integer')
59
+ throw new bsvErrors.InvalidArgument('data', 'Must be a Buffer, a string or an integer')
57
60
  }
58
61
  ent = ent || 128
59
62
 
@@ -73,7 +76,7 @@ var Mnemonic = function (data, wordlist) {
73
76
  throw new errors.InvalidMnemonic(phrase)
74
77
  }
75
78
  if (ent % 32 !== 0 || ent < 128) {
76
- throw new bsv.errors.InvalidArgument('ENT', 'Values must be ENT > 128 and ENT % 32 == 0')
79
+ throw new bsvErrors.InvalidArgument('ENT', 'Values must be ENT > 128 and ENT % 32 == 0')
77
80
  }
78
81
 
79
82
  phrase = phrase || Mnemonic._mnemonic(ent, wordlist)
@@ -241,7 +244,7 @@ Mnemonic.fromSeed = function (seed, wordlist) {
241
244
  */
242
245
  Mnemonic.prototype.toHDPrivateKey = function (passphrase, network) {
243
246
  var seed = this.toSeed(passphrase)
244
- return bsv.HDPrivateKey.fromSeed(seed, network)
247
+ return HDPrivateKey.fromSeed(seed, network)
245
248
  }
246
249
 
247
250
  /**
@@ -329,6 +332,15 @@ Mnemonic._entropyChecksum = function (entropy) {
329
332
  return checksum
330
333
  }
331
334
 
332
- Mnemonic.bsv = bsv
335
+ // Back-reference to the whole library, preserved as public API. Defined as a
336
+ // lazy getter rather than `Mnemonic.bsv = require('../../')`: this module is
337
+ // loaded while index.js is still initializing, so an eager require would
338
+ // capture a half-built namespace. Resolving on first access defers it past
339
+ // load and removes the module-init cycle.
340
+ Object.defineProperty(Mnemonic, 'bsv', {
341
+ configurable: true,
342
+ enumerable: true,
343
+ get: function () { return require('../../') }
344
+ })
333
345
 
334
346
  module.exports = Mnemonic
@@ -17,6 +17,8 @@
17
17
  * are never coerced to JS numbers. `dec` (decimals, 0–18) scales display only.
18
18
  */
19
19
  var inscription = require('./inscription')
20
+ var Script = require('../script')
21
+ var Output = require('../transaction/output')
20
22
 
21
23
  var CONTENT_TYPE = 'application/bsv-20'
22
24
  var MAX_DEC = 18
@@ -261,8 +263,7 @@ function buildDeployMint (params) {
261
263
  }
262
264
 
263
265
  function outputFor (script, satoshis) {
264
- var bsv = require('../..')
265
- return new bsv.Transaction.Output({ script: script, satoshis: satoshis != null ? satoshis : 1 })
266
+ return new Output({ script: script, satoshis: satoshis != null ? satoshis : 1 })
266
267
  }
267
268
 
268
269
  /** 1-sat Output helpers mirroring the builders. */
@@ -276,8 +277,7 @@ function createAuthOutput (params) { return outputFor(buildAuth(params), params
276
277
 
277
278
  /** Extract the JSON body from a locking script, a JSON string, a Buffer, or an object. */
278
279
  function bodyOf (input) {
279
- var bsv = require('../..')
280
- if (input && typeof input === 'object' && !Buffer.isBuffer(input) && !(input instanceof bsv.Script)) {
280
+ if (input && typeof input === 'object' && !Buffer.isBuffer(input) && !(input instanceof Script)) {
281
281
  return input // already a parsed object
282
282
  }
283
283
  if (typeof input === 'string' && input.trim()[0] === '{') return input // raw JSON string
@@ -16,13 +16,13 @@
16
16
  *
17
17
  * This matches the widely-used 1Sat Ordinals format (js-1sat-ord).
18
18
  */
19
- var bsv = require('../..')
19
+ var Script = require('../script')
20
+ var Opcode = require('../opcode')
21
+ var Address = require('../address')
22
+ var Output = require('../transaction/output')
20
23
 
21
24
  var ORD = Buffer.from('ord', 'utf8')
22
25
 
23
- function scriptClass () { return bsv.Script }
24
- function op () { return bsv.Opcode }
25
-
26
26
  /** Describe a rejected value for an error message, without dumping its contents. */
27
27
  function typeName (v) {
28
28
  if (v === null) return 'null'
@@ -50,7 +50,6 @@ var CONTENT_ALIASES = ['data', 'body', 'payload', 'text', 'message']
50
50
 
51
51
  /** Resolve a base locking script from a Script, an Address, or an address string. */
52
52
  function resolveLock (params) {
53
- var Script = scriptClass()
54
53
  var hasLock = params.lock != null
55
54
  var hasAddress = params.address != null
56
55
 
@@ -81,9 +80,9 @@ function resolveLock (params) {
81
80
  }
82
81
 
83
82
  if (hasAddress) {
84
- var addr = (params.address instanceof bsv.Address)
83
+ var addr = (params.address instanceof Address)
85
84
  ? params.address
86
- : bsv.Address.fromString(String(params.address))
85
+ : Address.fromString(String(params.address))
87
86
  return Script.buildPublicKeyHashOut(addr)
88
87
  }
89
88
 
@@ -111,7 +110,6 @@ function resolveLock (params) {
111
110
  */
112
111
  function buildInscription (params) {
113
112
  params = params || {}
114
- var Opcode = op()
115
113
  var lock = resolveLock(params)
116
114
 
117
115
  if (params.content == null) {
@@ -137,7 +135,7 @@ function buildInscription (params) {
137
135
  }
138
136
 
139
137
  // Clone the base lock so we never mutate the caller's Script.
140
- var s = scriptClass().fromBuffer(lock.toBuffer())
138
+ var s = Script.fromBuffer(lock.toBuffer())
141
139
  s.add(Opcode.OP_FALSE)
142
140
  s.add(Opcode.OP_IF)
143
141
  s.add(ORD)
@@ -168,8 +166,6 @@ function chunkIsOp (chunk, opcodenum) {
168
166
  * null if the script carries no inscription envelope.
169
167
  */
170
168
  function parseInscription (script) {
171
- var Script = scriptClass()
172
- var Opcode = op()
173
169
  var s = (script instanceof Script) ? script
174
170
  : Buffer.isBuffer(script) ? Script.fromBuffer(script)
175
171
  : Script.fromHex(script)
@@ -235,7 +231,7 @@ function createInscriptionOutput (params) {
235
231
  if (typeof satoshis !== 'number' || !Number.isInteger(satoshis) || satoshis < 1) {
236
232
  throw new Error('satoshis must be a positive integer (1 for a standard 1Sat ordinal)')
237
233
  }
238
- return new bsv.Transaction.Output({
234
+ return new Output({
239
235
  script: buildInscription(params),
240
236
  satoshis: satoshis
241
237
  })
@@ -37,14 +37,19 @@
37
37
  *
38
38
  * Requires post-Genesis limits: call SmartContract.enableGenesis() before verifying.
39
39
  */
40
- var bsv = require('../..')
41
- var P = require('../smart_contract/pushtx')
42
- var H = require('../smart_contract/covenant_helpers')
40
+ var P = require('../covenant/pushtx')
41
+ var H = require('../covenant/helpers')
43
42
  var inscription = require('./inscription')
44
43
 
45
- var Script = bsv.Script
46
- var Opcode = bsv.Opcode
47
- var BufferReader = bsv.encoding.BufferReader
44
+ var Script = require('../script')
45
+ var Opcode = require('../opcode')
46
+ var Address = require('../address')
47
+ var PublicKey = require('../publickey')
48
+ var Hash = require('../crypto/hash')
49
+ var Transaction = require('../transaction')
50
+ var Input = require('../transaction/input')
51
+ var Output = require('../transaction/output')
52
+ var BufferReader = require('../encoding/bufferreader')
48
53
 
49
54
  // OrdLock commits to the FULL output set (so payments can be pinned) but leaves the
50
55
  // input set open for the buyer's funding — SIGHASH_ALL|ANYONECANPAY|FORKID (0xc1).
@@ -73,9 +78,9 @@ function coerceScript (v) {
73
78
 
74
79
  /** Coerce an Address, PublicKey, PrivateKey, or address string to an Address. */
75
80
  function toAddress (v) {
76
- if (v instanceof bsv.Address) return v
81
+ if (v instanceof Address) return v
77
82
  if (v && typeof v.toAddress === 'function') return v.toAddress() // PublicKey / PrivateKey
78
- return bsv.Address.fromString(String(v))
83
+ return Address.fromString(String(v))
79
84
  }
80
85
 
81
86
  /** Resolve a 20-byte HASH160 owner commitment from an Address/PublicKey/key or 20-byte Buffer. */
@@ -84,7 +89,7 @@ function resolvePubKeyHash (owner) {
84
89
  if (owner.length !== 20) throw new Error('seller pubkeyhash buffer must be 20 bytes (HASH160)')
85
90
  return owner
86
91
  }
87
- if (owner instanceof bsv.PublicKey) return bsv.crypto.Hash.sha256ripemd160(owner.toBuffer())
92
+ if (owner instanceof PublicKey) return Hash.sha256ripemd160(owner.toBuffer())
88
93
  return toAddress(owner).hashBuffer
89
94
  }
90
95
 
@@ -95,15 +100,15 @@ function resolvePubKeyHash (owner) {
95
100
  */
96
101
  function payOutputFor (payTo, price) {
97
102
  assertSats(price, 'payment price')
98
- return new bsv.Transaction.Output({
103
+ return new Output({
99
104
  script: Script.buildPublicKeyHashOut(toAddress(payTo)), satoshis: price
100
105
  })
101
106
  }
102
107
 
103
108
  /** Coerce one payment spec to a Transaction.Output. */
104
109
  function outputFromSpec (spec) {
105
- if (spec instanceof bsv.Transaction.Output) return spec
106
- if (Buffer.isBuffer(spec)) return bsv.Transaction.Output.fromBufferReader(new BufferReader(spec))
110
+ if (spec instanceof Output) return spec
111
+ if (Buffer.isBuffer(spec)) return Output.fromBufferReader(new BufferReader(spec))
107
112
  var to = spec.payTo != null ? spec.payTo : spec.address
108
113
  var amt = spec.satoshis != null ? spec.satoshis : spec.price
109
114
  if (to == null || amt == null) throw new Error('payment spec needs {address|payTo, satoshis|price}')
@@ -125,7 +130,7 @@ function resolvePayOutputs (params) {
125
130
  if (params.price != null) {
126
131
  var payTo = params.payTo != null ? params.payTo : params.seller
127
132
  // A pre-built Output as payTo is used verbatim (its own value); otherwise build P2PKH(price).
128
- var first = (payTo instanceof bsv.Transaction.Output) ? payTo : payOutputFor(payTo, params.price)
133
+ var first = (payTo instanceof Output) ? payTo : payOutputFor(payTo, params.price)
129
134
  var arr = [first]
130
135
  if (params.royalties) params.royalties.forEach(function (r) { arr.push(outputFromSpec(r)) })
131
136
  return arr
@@ -199,7 +204,7 @@ function chunkIsOp (chunk, opcodenum) {
199
204
  function splitPayBlob (blob) {
200
205
  var br = new BufferReader(blob)
201
206
  var outs = []
202
- while (!br.eof()) outs.push(bsv.Transaction.Output.fromBufferReader(br))
207
+ while (!br.eof()) outs.push(Output.fromBufferReader(br))
203
208
  return outs
204
209
  }
205
210
 
@@ -296,7 +301,7 @@ function parseOrdLock (script, opts) {
296
301
  if (rebuilt.toHex() !== s.toHex()) return null
297
302
 
298
303
  return {
299
- seller: { pubKeyHash: sellerPKH, address: bsv.Address.fromPublicKeyHash(sellerPKH, network).toString() },
304
+ seller: { pubKeyHash: sellerPKH, address: Address.fromPublicKeyHash(sellerPKH, network).toString() },
300
305
  payOutputs: payOutputs,
301
306
  payBlob: payBlob,
302
307
  totalPrice: totalPrice,
@@ -317,7 +322,7 @@ function isOrdLock (script, opts) { return parseOrdLock(script, opts) !== null }
317
322
  function listInscriptionOutput (params) {
318
323
  params = params || {}
319
324
  var satoshis = params.satoshis != null ? params.satoshis : 1
320
- return new bsv.Transaction.Output({ script: buildOrdLock(params), satoshis: satoshis })
325
+ return new Output({ script: buildOrdLock(params), satoshis: satoshis })
321
326
  }
322
327
 
323
328
  /**
@@ -426,7 +431,7 @@ function cancel (params) {
426
431
 
427
432
  /** Add an input spending `outpoint` under `script` worth `satoshis`, script-sig empty. */
428
433
  function addSpendInput (tx, outpoint, script, satoshis) {
429
- tx.addInput(new bsv.Transaction.Input({
434
+ tx.addInput(new Input({
430
435
  prevTxId: outpoint.txid || outpoint.prevTxId,
431
436
  outputIndex: outpoint.outputIndex != null ? outpoint.outputIndex : outpoint.vout,
432
437
  script: Script.empty()
@@ -479,11 +484,11 @@ function buildListingTx (params) {
479
484
  throw new Error('buildListingTx: insufficient funding — need ' + fee + ' sat fee, funded ' + fundingTotal)
480
485
  }
481
486
 
482
- var tx = new bsv.Transaction()
487
+ var tx = new Transaction()
483
488
  addSpendInput(tx, ordinal, ordinal.script, ordSats) // input 0 = the ordinal (P2PKH)
484
489
  funding.forEach(function (f) { addSpendInput(tx, f, f.script, f.satoshis) })
485
490
 
486
- tx.addOutput(new bsv.Transaction.Output({ script: lock, satoshis: ordSats })) // output 0 = listing
491
+ tx.addOutput(new Output({ script: lock, satoshis: ordSats })) // output 0 = listing
487
492
  if (change > 0) {
488
493
  tx.addOutput(payOutputFor(params.changeAddress || ordinal.privateKey.toAddress(), change))
489
494
  }
@@ -533,7 +538,7 @@ function buildPurchaseTx (params) {
533
538
  var payOuts = params.payOutputs
534
539
  ? params.payOutputs.map(outputFromSpec)
535
540
  : parsed.payOutputs.map(function (p) {
536
- return new bsv.Transaction.Output({ script: p.script, satoshis: p.satoshis })
541
+ return new Output({ script: p.script, satoshis: p.satoshis })
537
542
  })
538
543
  var paidTotal = payOuts.reduce(function (a, o) { return a + o.satoshis }, 0)
539
544
 
@@ -545,7 +550,7 @@ function buildPurchaseTx (params) {
545
550
  ' sat (payments ' + paidTotal + ' + fee ' + fee + '), funded ' + fundingTotal)
546
551
  }
547
552
 
548
- var tx = new bsv.Transaction()
553
+ var tx = new Transaction()
549
554
  addSpendInput(tx, listing, lockingScript, ordSats) // input 0 = the listing (covenant)
550
555
  params.funding.forEach(function (f) { addSpendInput(tx, f, f.script, f.satoshis) })
551
556
 
@@ -728,6 +728,13 @@ function padBufferToSize (buf, len) {
728
728
  * Based on the inner loop of bitcoind's EvalScript function
729
729
  * bitcoind commit: b5d1b1092998bc95313856d535c632ea5a8f9104
730
730
  */
731
+ /** The transaction version as the four little-endian bytes Chronicle compares. */
732
+ function versionLE (version) {
733
+ var buf = Buffer.alloc(4)
734
+ buf.writeInt32LE(version | 0, 0)
735
+ return buf
736
+ }
737
+
731
738
  Interpreter.prototype.step = function () {
732
739
  var self = this
733
740
 
@@ -1044,7 +1051,19 @@ Interpreter.prototype.step = function () {
1044
1051
  break
1045
1052
 
1046
1053
  case Opcode.OP_VER:
1047
- // Chronicle: pushes the executing transaction's version.
1054
+ // Chronicle: pushes the transaction version as FOUR LITTLE-ENDIAN
1055
+ // BYTES, not as a script number. The node is:
1056
+ //
1057
+ // const auto tx_version{checker.Version()};
1058
+ // std::vector<uint8_t> val(sizeof(tx_version));
1059
+ // to_le(tx_version, val.data());
1060
+ // stack.push_back(std::move(val));
1061
+ //
1062
+ // A script number pushes `01` for version 1 where the node pushes
1063
+ // `01000000`, so anything consuming the value diverges.
1064
+ //
1065
+ // Unlike the string opcodes, BAD_OPCODE pre-Chronicle IS correct here —
1066
+ // the node returns it unconditionally.
1048
1067
  if ((this.flags & Interpreter.SCRIPT_ENABLE_CHRONICLE) === 0) {
1049
1068
  this.errstr = 'SCRIPT_ERR_BAD_OPCODE'
1050
1069
  return false
@@ -1053,18 +1072,26 @@ Interpreter.prototype.step = function () {
1053
1072
  this.errstr = 'SCRIPT_ERR_UNKNOWN_ERROR'
1054
1073
  return false
1055
1074
  }
1056
- this.stack.push(new BN(this.tx.version).toScriptNumBuffer())
1075
+ this.stack.push(versionLE(this.tx.version))
1057
1076
  break
1058
1077
 
1059
1078
  case Opcode.OP_VERIF:
1060
1079
  case Opcode.OP_VERNOTIF:
1061
- // Chronicle: an IF whose condition is "top of stack equals the executing
1062
- // transaction's version", closed by OP_ENDIF.
1080
+ // Chronicle: an IF whose condition is "top of stack equals the
1081
+ // transaction version", closed by OP_ENDIF.
1063
1082
  //
1064
- // Before Chronicle these are invalid EVEN IN AN UNEXECUTED BRANCH — a rule
1065
- // Bitcoin applies to no other opcode — so the flag check deliberately sits
1066
- // OUTSIDE the fExec guard below, which preserves that.
1083
+ // if(!utxo_after_chronicle) {
1084
+ // if(utxo_after_genesis && !fExec) break;
1085
+ // else return SCRIPT_ERR_BAD_OPCODE;
1086
+ // }
1087
+ //
1088
+ // So pre-Chronicle they are an error only in an EXECUTED branch. BSV is
1089
+ // post-Genesis, which is the whole condition. Returning BAD_OPCODE
1090
+ // unconditionally rejected scripts the network accepts.
1067
1091
  if ((this.flags & Interpreter.SCRIPT_ENABLE_CHRONICLE) === 0) {
1092
+ if (!fExec) {
1093
+ break
1094
+ }
1068
1095
  this.errstr = 'SCRIPT_ERR_BAD_OPCODE'
1069
1096
  return false
1070
1097
  }
@@ -1078,12 +1105,16 @@ Interpreter.prototype.step = function () {
1078
1105
  this.errstr = 'SCRIPT_ERR_UNKNOWN_ERROR'
1079
1106
  return false
1080
1107
  }
1081
- // Popping mirrors OP_IF. The spec describes the comparison but not the
1082
- // stack effect; the OP_VERIF ... OP_ENDIF form it documents is an IF, and
1083
- // an IF that left its condition behind would unbalance every script using
1084
- // it. Pinned by its own test so a correction shows up as a diff.
1085
- bn = BN.fromScriptNumBuffer(stacktop(-1), fRequireMinimal)
1086
- fValue = bn.cmp(new BN(this.tx.version)) === 0
1108
+ // BYTE-WISE against the 4-byte little-endian version, not a numeric
1109
+ // compare. An operand of any other length simply yields false — it is
1110
+ // NOT an error — so `OP_2 OP_VERIF` against version 2 is false, where
1111
+ // a numeric comparison made it true. The idiomatic form is
1112
+ // `OP_VER OP_VERIF`.
1113
+ //
1114
+ // MINIMALIF deliberately does not apply: the node guards it with
1115
+ // `(opcode == OP_IF || opcode == OP_NOTIF) && VerifyMinimalIf(flags)`.
1116
+ buf = stacktop(-1)
1117
+ fValue = buf.length === 4 && buf.equals(versionLE(this.tx.version))
1087
1118
  if (opcodenum === Opcode.OP_VERNOTIF) {
1088
1119
  fValue = !fValue
1089
1120
  }
@@ -1953,21 +1984,39 @@ Interpreter.prototype.step = function () {
1953
1984
 
1954
1985
  case Opcode.OP_LEFT:
1955
1986
  case Opcode.OP_RIGHT:
1956
- // (in size -- out) OP_LEFT keeps the first `size` bytes; OP_RIGHT the last.
1957
- // Re-enabled string opcodes (BSV Chronicle). Original Satoshi semantics.
1987
+ // (in size -- out) Chronicle, ported from SV Node.
1988
+ // Pre-Chronicle these bytes are UPGRADABLE NOPS on the network — they were
1989
+ // OP_NOP4/OP_NOP5/OP_NOP6 — so the node breaks rather than erroring:
1990
+ //
1991
+ // if(!utxo_after_chronicle) {
1992
+ // if(flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
1993
+ // return SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS;
1994
+ // else break;
1995
+ // }
1996
+ //
1997
+ // Running them unconditionally consumed stack where the network does
1998
+ // nothing, which is a wrong answer rather than an error.
1999
+ if ((this.flags & Interpreter.SCRIPT_ENABLE_CHRONICLE) === 0) {
2000
+ if (this.flags & Interpreter.SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) {
2001
+ this.errstr = 'SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS'
2002
+ return false
2003
+ }
2004
+ break
2005
+ }
2006
+
1958
2007
  if (this.stack.length < 2) {
1959
2008
  this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'
1960
2009
  return false
1961
2010
  }
1962
2011
  buf1 = stacktop(-2)
1963
- n = BN.fromScriptNumBuffer(stacktop(-1), fRequireMinimal).toNumber()
1964
- if (n < 0) {
2012
+ n = BN.fromScriptNumBuffer(stacktop(-1), fRequireMinimal, Interpreter.MAXIMUM_ELEMENT_SIZE).toNumber()
2013
+ // ERRORS on out of range; it does not clamp. The node is
2014
+ // `if(len < 0 || len > size) return SCRIPT_ERR_INVALID_NUMBER_RANGE`.
2015
+ // Clamping made scripts the node rejects succeed here.
2016
+ if (n < 0 || n > buf1.length) {
1965
2017
  this.errstr = 'SCRIPT_ERR_INVALID_NUMBER_RANGE'
1966
2018
  return false
1967
2019
  }
1968
- if (n > buf1.length) {
1969
- n = buf1.length
1970
- }
1971
2020
  this.stack.pop()
1972
2021
  if (opcodenum === Opcode.OP_LEFT) {
1973
2022
  this.stack[this.stack.length - 1] = Buffer.from(buf1).slice(0, n)
@@ -1977,25 +2026,41 @@ Interpreter.prototype.step = function () {
1977
2026
  break
1978
2027
 
1979
2028
  case Opcode.OP_SUBSTR:
1980
- // (in begin size -- out) out = in[begin : begin + size]
1981
- // Re-enabled string opcode (BSV Chronicle). Original Satoshi semantics.
2029
+ // (in begin size -- out) Chronicle, ported from SV Node.
2030
+ // Pre-Chronicle these bytes are UPGRADABLE NOPS on the network — they were
2031
+ // OP_NOP4/OP_NOP5/OP_NOP6 — so the node breaks rather than erroring:
2032
+ //
2033
+ // if(!utxo_after_chronicle) {
2034
+ // if(flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
2035
+ // return SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS;
2036
+ // else break;
2037
+ // }
2038
+ //
2039
+ // Running them unconditionally consumed stack where the network does
2040
+ // nothing, which is a wrong answer rather than an error.
2041
+ if ((this.flags & Interpreter.SCRIPT_ENABLE_CHRONICLE) === 0) {
2042
+ if (this.flags & Interpreter.SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) {
2043
+ this.errstr = 'SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS'
2044
+ return false
2045
+ }
2046
+ break
2047
+ }
2048
+
1982
2049
  if (this.stack.length < 3) {
1983
2050
  this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'
1984
2051
  return false
1985
2052
  }
1986
2053
  buf1 = stacktop(-3)
1987
- var subSize = BN.fromScriptNumBuffer(stacktop(-1), fRequireMinimal).toNumber()
1988
- var subBegin = BN.fromScriptNumBuffer(stacktop(-2), fRequireMinimal).toNumber()
1989
- if (subBegin < 0 || subSize < 0) {
2054
+ var subSize = BN.fromScriptNumBuffer(stacktop(-1), fRequireMinimal, Interpreter.MAXIMUM_ELEMENT_SIZE).toNumber()
2055
+ var subBegin = BN.fromScriptNumBuffer(stacktop(-2), fRequireMinimal, Interpreter.MAXIMUM_ELEMENT_SIZE).toNumber()
2056
+ // ERRORS on out of range, matching
2057
+ // offset < 0 || offset >= size || len < 0 || len > size - offset
2058
+ // Note `offset >= size` is strict: a begin index AT the end is an
2059
+ // error, not an empty result.
2060
+ if (subBegin < 0 || subBegin >= buf1.length || subSize < 0 || subSize > buf1.length - subBegin) {
1990
2061
  this.errstr = 'SCRIPT_ERR_INVALID_NUMBER_RANGE'
1991
2062
  return false
1992
2063
  }
1993
- if (subBegin > buf1.length) {
1994
- subBegin = buf1.length
1995
- }
1996
- if (subBegin + subSize > buf1.length) {
1997
- subSize = buf1.length - subBegin
1998
- }
1999
2064
  this.stack.pop()
2000
2065
  this.stack.pop()
2001
2066
  this.stack[this.stack.length - 1] = Buffer.from(buf1).slice(subBegin, subBegin + subSize)
@@ -28,16 +28,18 @@
28
28
  * always just a 20-byte value the covenant splices into the recreated scriptCode.
29
29
  */
30
30
 
31
- var bsv = require('../..')
31
+ var PrivateKey = require('../privatekey')
32
+ var PublicKey = require('../publickey')
33
+ var Script = require('../script')
32
34
  var Hash = require('../crypto/hash')
33
- var H = require('./covenant_helpers')
35
+ var H = require('../covenant/helpers')
34
36
 
35
- var Opcode = bsv.Opcode
37
+ var Opcode = require('../opcode')
36
38
  var n = H.scriptNum
37
39
 
38
40
  function pubBuffer (key) {
39
- if (key instanceof bsv.PrivateKey) return key.toPublicKey().toBuffer()
40
- if (key instanceof bsv.PublicKey) return key.toBuffer()
41
+ if (key instanceof PrivateKey) return key.toPublicKey().toBuffer()
42
+ if (key instanceof PublicKey) return key.toBuffer()
41
43
  return key // already a Buffer
42
44
  }
43
45
 
@@ -71,7 +73,7 @@ function singleKey () {
71
73
  function redeemScript (m, pubkeys) {
72
74
  var nn = pubkeys.length
73
75
  if (m < 1 || m > nn || nn < 1 || nn > 16) throw new Error('multisig requires 1 <= m <= n <= 16')
74
- var s = new bsv.Script().add(Opcode['OP_' + m])
76
+ var s = new Script().add(Opcode['OP_' + m])
75
77
  pubkeys.forEach(function (p) { s.add(Buffer.from(p)) }) // each emitted as 0x21||pk (33-byte push)
76
78
  s.add(Opcode['OP_' + nn]).add(Opcode.OP_CHECKMULTISIG)
77
79
  return s.toBuffer()