@bsv/sdk 1.5.2 → 1.6.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 (66) hide show
  1. package/dist/cjs/package.json +1 -1
  2. package/dist/cjs/src/primitives/AESGCM.js +113 -137
  3. package/dist/cjs/src/primitives/AESGCM.js.map +1 -1
  4. package/dist/cjs/src/primitives/BigNumber.js +1019 -3947
  5. package/dist/cjs/src/primitives/BigNumber.js.map +1 -1
  6. package/dist/cjs/src/primitives/K256.js +53 -37
  7. package/dist/cjs/src/primitives/K256.js.map +1 -1
  8. package/dist/cjs/src/primitives/Mersenne.js +16 -21
  9. package/dist/cjs/src/primitives/Mersenne.js.map +1 -1
  10. package/dist/cjs/src/primitives/MontgomoryMethod.js.map +1 -1
  11. package/dist/cjs/src/primitives/utils.js +27 -17
  12. package/dist/cjs/src/primitives/utils.js.map +1 -1
  13. package/dist/cjs/src/script/Spend.js +618 -858
  14. package/dist/cjs/src/script/Spend.js.map +1 -1
  15. package/dist/cjs/src/transaction/Beef.js +17 -0
  16. package/dist/cjs/src/transaction/Beef.js.map +1 -1
  17. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  18. package/dist/esm/src/primitives/AESGCM.js +112 -137
  19. package/dist/esm/src/primitives/AESGCM.js.map +1 -1
  20. package/dist/esm/src/primitives/BigNumber.js +1011 -3969
  21. package/dist/esm/src/primitives/BigNumber.js.map +1 -1
  22. package/dist/esm/src/primitives/K256.js +53 -37
  23. package/dist/esm/src/primitives/K256.js.map +1 -1
  24. package/dist/esm/src/primitives/Mersenne.js +16 -21
  25. package/dist/esm/src/primitives/Mersenne.js.map +1 -1
  26. package/dist/esm/src/primitives/MontgomoryMethod.js.map +1 -1
  27. package/dist/esm/src/primitives/utils.js +29 -17
  28. package/dist/esm/src/primitives/utils.js.map +1 -1
  29. package/dist/esm/src/script/Spend.js +618 -857
  30. package/dist/esm/src/script/Spend.js.map +1 -1
  31. package/dist/esm/src/transaction/Beef.js +17 -0
  32. package/dist/esm/src/transaction/Beef.js.map +1 -1
  33. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  34. package/dist/types/src/primitives/AESGCM.d.ts.map +1 -1
  35. package/dist/types/src/primitives/BigNumber.d.ts +238 -1705
  36. package/dist/types/src/primitives/BigNumber.d.ts.map +1 -1
  37. package/dist/types/src/primitives/K256.d.ts.map +1 -1
  38. package/dist/types/src/primitives/Mersenne.d.ts +2 -2
  39. package/dist/types/src/primitives/Mersenne.d.ts.map +1 -1
  40. package/dist/types/src/primitives/utils.d.ts +2 -0
  41. package/dist/types/src/primitives/utils.d.ts.map +1 -1
  42. package/dist/types/src/script/Spend.d.ts +11 -1
  43. package/dist/types/src/script/Spend.d.ts.map +1 -1
  44. package/dist/types/src/transaction/Beef.d.ts +21 -1
  45. package/dist/types/src/transaction/Beef.d.ts.map +1 -1
  46. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  47. package/dist/umd/bundle.js +1 -1
  48. package/docs/performance.md +70 -0
  49. package/docs/primitives.md +262 -3049
  50. package/docs/transaction.md +34 -0
  51. package/docs/wallet.md +1 -1
  52. package/package.json +1 -1
  53. package/src/auth/__tests/Peer.test.ts +38 -23
  54. package/src/auth/certificates/__tests/MasterCertificate.test.ts +27 -20
  55. package/src/auth/certificates/__tests/VerifiableCertificate.test.ts +24 -24
  56. package/src/primitives/AESGCM.ts +118 -164
  57. package/src/primitives/BigNumber.ts +867 -4180
  58. package/src/primitives/K256.ts +57 -37
  59. package/src/primitives/Mersenne.ts +16 -20
  60. package/src/primitives/MontgomoryMethod.ts +2 -2
  61. package/src/primitives/__tests/ReductionContext.test.ts +6 -1
  62. package/src/primitives/utils.ts +28 -17
  63. package/src/script/Spend.ts +634 -1309
  64. package/src/transaction/Beef.ts +18 -1
  65. package/src/transaction/__tests/Transaction.test.ts +14 -16
  66. package/src/transaction/__tests/Transaction.benchmarks.test.ts +0 -237
@@ -35,13 +35,104 @@ const TransactionSignature_js_1 = __importDefault(require("../primitives/Transac
35
35
  const PublicKey_js_1 = __importDefault(require("../primitives/PublicKey.js"));
36
36
  const ECDSA_js_1 = require("../primitives/ECDSA.js");
37
37
  // These constants control the current behavior of the interpreter.
38
- // In the future, all of them will go away.
39
38
  const maxScriptElementSize = 1024 * 1024 * 1024;
40
39
  const maxMultisigKeyCount = Math.pow(2, 31) - 1;
41
40
  const requireMinimalPush = true;
42
41
  const requirePushOnlyUnlockingScripts = true;
43
42
  const requireLowSSignatures = true;
44
43
  const requireCleanStack = true;
44
+ // --- Optimization: Pre-computed script numbers ---
45
+ const SCRIPTNUM_NEG_1 = Object.freeze(new BigNumber_js_1.default(-1).toScriptNum());
46
+ const SCRIPTNUMS_0_TO_16 = Object.freeze(Array.from({ length: 17 }, (_, i) => Object.freeze(new BigNumber_js_1.default(i).toScriptNum())));
47
+ // --- Helper functions ---
48
+ function compareNumberArrays(a, b) {
49
+ if (a.length !== b.length)
50
+ return false;
51
+ for (let i = 0; i < a.length; i++) {
52
+ if (a[i] !== b[i])
53
+ return false;
54
+ }
55
+ return true;
56
+ }
57
+ function isMinimallyEncodedHelper(buf, maxNumSize = Number.MAX_SAFE_INTEGER) {
58
+ if (buf.length > maxNumSize) {
59
+ return false;
60
+ }
61
+ if (buf.length > 0) {
62
+ if ((buf[buf.length - 1] & 0x7f) === 0) {
63
+ if (buf.length <= 1 || (buf[buf.length - 2] & 0x80) === 0) {
64
+ return false;
65
+ }
66
+ }
67
+ }
68
+ return true;
69
+ }
70
+ function isChecksigFormatHelper(buf) {
71
+ // This is a simplified check. The full DER check is more complex and typically
72
+ // done by TransactionSignature.fromChecksigFormat which can throw.
73
+ // This helper is mostly for early bailout or non-throwing checks if needed.
74
+ if (buf.length < 9 || buf.length > 73)
75
+ return false;
76
+ if (buf[0] !== 0x30)
77
+ return false; // DER SEQUENCE
78
+ if (buf[1] !== buf.length - 3)
79
+ return false; // Total length (excluding type and length byte for sequence, and hash type)
80
+ const rMarker = buf[2];
81
+ const rLen = buf[3];
82
+ if (rMarker !== 0x02)
83
+ return false; // DER INTEGER
84
+ if (rLen === 0)
85
+ return false; // R length is zero
86
+ if (5 + rLen >= buf.length)
87
+ return false; // S length misplaced or R too long
88
+ const sMarkerOffset = 4 + rLen;
89
+ const sMarker = buf[sMarkerOffset];
90
+ const sLen = buf[sMarkerOffset + 1];
91
+ if (sMarker !== 0x02)
92
+ return false; // DER INTEGER
93
+ if (sLen === 0)
94
+ return false; // S length is zero
95
+ // Check R value negative or excessively padded
96
+ if ((buf[4] & 0x80) !== 0)
97
+ return false; // R value negative
98
+ if (rLen > 1 && buf[4] === 0x00 && (buf[5] & 0x80) === 0)
99
+ return false; // R value excessively padded
100
+ // Check S value negative or excessively padded
101
+ const sValueOffset = sMarkerOffset + 2;
102
+ if ((buf[sValueOffset] & 0x80) !== 0)
103
+ return false; // S value negative
104
+ if (sLen > 1 && buf[sValueOffset] === 0x00 && (buf[sValueOffset + 1] & 0x80) === 0)
105
+ return false; // S value excessively padded
106
+ if (rLen + sLen + 7 !== buf.length)
107
+ return false; // Final length check including hash type
108
+ return true;
109
+ }
110
+ function isOpcodeDisabledHelper(op) {
111
+ return (op === OP_js_1.default.OP_2MUL ||
112
+ op === OP_js_1.default.OP_2DIV ||
113
+ op === OP_js_1.default.OP_VERIF ||
114
+ op === OP_js_1.default.OP_VERNOTIF ||
115
+ op === OP_js_1.default.OP_VER);
116
+ }
117
+ function isChunkMinimalPushHelper(chunk) {
118
+ const data = chunk.data;
119
+ const op = chunk.op;
120
+ if (!Array.isArray(data))
121
+ return true;
122
+ if (data.length === 0)
123
+ return op === OP_js_1.default.OP_0;
124
+ if (data.length === 1 && data[0] >= 1 && data[0] <= 16)
125
+ return op === OP_js_1.default.OP_1 + (data[0] - 1);
126
+ if (data.length === 1 && data[0] === 0x81)
127
+ return op === OP_js_1.default.OP_1NEGATE;
128
+ if (data.length <= 75)
129
+ return op === data.length;
130
+ if (data.length <= 255)
131
+ return op === OP_js_1.default.OP_PUSHDATA1;
132
+ if (data.length <= 65535)
133
+ return op === OP_js_1.default.OP_PUSHDATA2;
134
+ return true;
135
+ }
45
136
  /**
46
137
  * The Spend class represents a spend action within a Bitcoin SV transaction.
47
138
  * It encapsulates all the necessary data required for spending a UTXO (Unspent Transaction Output)
@@ -107,7 +198,10 @@ class Spend {
107
198
  this.unlockingScript = params.unlockingScript;
108
199
  this.inputSequence = params.inputSequence;
109
200
  this.lockTime = params.lockTime;
110
- this.memoryLimit = (_a = params.memoryLimit) !== null && _a !== void 0 ? _a : 100000; // 100 MB is going to be processed by most miners by policy, but the default should protect apps against memory attacks.
201
+ this.memoryLimit = (_a = params.memoryLimit) !== null && _a !== void 0 ? _a : 32000000;
202
+ this.stack = [];
203
+ this.altStack = [];
204
+ this.ifStack = [];
111
205
  this.stackMem = 0;
112
206
  this.altStackMem = 0;
113
207
  this.reset();
@@ -122,280 +216,182 @@ class Spend {
122
216
  this.stackMem = 0;
123
217
  this.altStackMem = 0;
124
218
  }
125
- step() {
126
- var _a, _b, _c, _d;
127
- let poppedValue;
128
- // If the stack (or alt stack) is over the memory limit, evaluation has failed.
129
- if (this.stackMem > this.memoryLimit) {
219
+ ensureStackMem(additional) {
220
+ if (this.stackMem + additional > this.memoryLimit) {
130
221
  this.scriptEvaluationError('Stack memory usage has exceeded ' + String(this.memoryLimit) + ' bytes');
131
- return false;
132
222
  }
133
- if (this.altStackMem > this.memoryLimit) {
223
+ }
224
+ ensureAltStackMem(additional) {
225
+ if (this.altStackMem + additional > this.memoryLimit) {
134
226
  this.scriptEvaluationError('Alt stack memory usage has exceeded ' + String(this.memoryLimit) + ' bytes');
135
- return false;
136
227
  }
137
- // Clear popped values after use to free memory
138
- const clearPoppedValue = () => {
139
- poppedValue = undefined;
140
- };
141
- // If the context is UnlockingScript and we have reached the end,
142
- // set the context to LockingScript and zero the program counter
143
- if (this.context === 'UnlockingScript' &&
144
- this.programCounter >= this.unlockingScript.chunks.length) {
145
- this.context = 'LockingScript';
146
- this.programCounter = 0;
228
+ }
229
+ pushStack(item) {
230
+ this.ensureStackMem(item.length);
231
+ this.stack.push(item);
232
+ this.stackMem += item.length;
233
+ }
234
+ pushStackCopy(item) {
235
+ this.ensureStackMem(item.length);
236
+ const copy = item.slice();
237
+ this.stack.push(copy);
238
+ this.stackMem += copy.length;
239
+ }
240
+ popStack() {
241
+ if (this.stack.length === 0) {
242
+ this.scriptEvaluationError('Attempted to pop from an empty stack.');
147
243
  }
148
- let operation;
149
- if (this.context === 'UnlockingScript') {
150
- operation = this.unlockingScript.chunks[this.programCounter];
244
+ const item = this.stack.pop();
245
+ this.stackMem -= item.length;
246
+ return item;
247
+ }
248
+ stackTop(index = -1) {
249
+ // index = -1 for top, -2 for second top, etc.
250
+ // stack.length + index provides 0-based index from start
251
+ if (this.stack.length === 0 || this.stack.length < Math.abs(index) || (index >= 0 && index >= this.stack.length)) {
252
+ this.scriptEvaluationError(`Stack underflow accessing element at index ${index}. Stack length is ${this.stack.length}.`);
151
253
  }
152
- else {
153
- operation = this.lockingScript.chunks[this.programCounter];
254
+ return this.stack[this.stack.length + index];
255
+ }
256
+ pushAltStack(item) {
257
+ this.ensureAltStackMem(item.length);
258
+ this.altStack.push(item);
259
+ this.altStackMem += item.length;
260
+ }
261
+ popAltStack() {
262
+ if (this.altStack.length === 0) {
263
+ this.scriptEvaluationError('Attempted to pop from an empty alt stack.');
154
264
  }
155
- const isOpcodeDisabled = (op) => {
156
- return (op === OP_js_1.default.OP_2MUL ||
157
- op === OP_js_1.default.OP_2DIV ||
158
- op === OP_js_1.default.OP_VERIF ||
159
- op === OP_js_1.default.OP_VERNOTIF ||
160
- op === OP_js_1.default.OP_VER);
161
- };
162
- const isChunkMinimal = (chunk) => {
163
- const data = chunk.data;
164
- const op = chunk.op;
165
- if (!Array.isArray(data)) {
166
- return true;
167
- }
168
- if (data.length === 0) {
169
- // Could have used OP_0.
170
- return op === OP_js_1.default.OP_0;
171
- }
172
- else if (data.length === 1 && data[0] >= 1 && data[0] <= 16) {
173
- // Could have used OP_1 .. OP_16.
174
- return op === OP_js_1.default.OP_1 + (data[0] - 1);
175
- }
176
- else if (data.length === 1 && data[0] === 0x81) {
177
- // Could have used OP_1NEGATE.
178
- return op === OP_js_1.default.OP_1NEGATE;
179
- }
180
- else if (data.length <= 75) {
181
- // Could have used a direct push (opCode indicating number of bytes pushed + those bytes).
182
- return op === data.length;
183
- }
184
- else if (data.length <= 255) {
185
- // Could have used OP_PUSHDATA.
186
- return op === OP_js_1.default.OP_PUSHDATA1;
187
- }
188
- else if (data.length <= 65535) {
189
- // Could have used OP_PUSHDATA2.
190
- return op === OP_js_1.default.OP_PUSHDATA2;
191
- }
192
- return true;
193
- };
194
- // Following example from sCrypt now using Number.MAX_SAFE_INTEGER (bsv/lib/transaction/input/input).
195
- const isMinimallyEncoded = (buf, maxNumSize = Number.MAX_SAFE_INTEGER) => {
196
- if (buf.length > maxNumSize) {
197
- return false;
198
- }
199
- if (buf.length > 0) {
200
- // Check that the number is encoded with the minimum possible number
201
- // of bytes.
202
- //
203
- // If the most-significant-byte - excluding the sign bit - is zero
204
- // then we're not minimal. Note how this test also rejects the
205
- // negative-zero encoding, 0x80.
206
- if ((buf[buf.length - 1] & 0x7f) === 0) {
207
- // One exception: if there's more than one byte and the most
208
- // significant bit of the second-most-significant-byte is set it
209
- // would conflict with the sign bit. An example of this case is
210
- // +-255, which encode to 0xff00 and 0xff80 respectively.
211
- // (big-endian).
212
- if (buf.length <= 1 || (buf[buf.length - 2] & 0x80) === 0) {
213
- return false;
214
- }
215
- }
216
- }
217
- return true;
218
- };
219
- const padDataToSize = (buf, len) => {
220
- const b = buf;
221
- while (b.length < len) {
222
- b.unshift(0);
223
- }
224
- return b;
225
- };
226
- /**
227
- * This function is translated from bitcoind's IsDERSignature and is used in
228
- * the script interpreter. This "DER" format actually includes an extra byte,
229
- * the nHashType, at the end. It is really the tx format, not DER format.
230
- *
231
- * A canonical signature exists of: [30] [total len] [02] [len R] [R] [02] [len S] [S] [hashtype]
232
- * Where R and S are not negative (their first byte has its highest bit not set), and not
233
- * excessively padded (do not start with a 0 byte, unless an otherwise negative number follows,
234
- * in which case a single 0 byte is necessary and even required).
235
- *
236
- * See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623
237
- */
238
- const isChecksigFormat = (buf) => {
239
- if (buf.length < 9) {
240
- // Non-canonical signature: too short
241
- return false;
242
- }
243
- if (buf.length > 73) {
244
- // Non-canonical signature: too long
245
- return false;
246
- }
247
- if (buf[0] !== 0x30) {
248
- // Non-canonical signature: wrong type
249
- return false;
250
- }
251
- if (buf[1] !== buf.length - 3) {
252
- // Non-canonical signature: wrong length marker
253
- return false;
254
- }
255
- const nLEnR = buf[3];
256
- if (5 + nLEnR >= buf.length) {
257
- // Non-canonical signature: S length misplaced
258
- return false;
259
- }
260
- const nLEnS = buf[5 + nLEnR];
261
- if (nLEnR + nLEnS + 7 !== buf.length) {
262
- // Non-canonical signature: R+S length mismatch
263
- return false;
264
- }
265
- const R = buf.slice(4);
266
- if (buf[4 - 2] !== 0x02) {
267
- // Non-canonical signature: R value type mismatch
268
- return false;
269
- }
270
- if (buf[4 - 1] !== nLEnR) {
271
- // Non-canonical signature: R length mismatch
272
- return false;
273
- }
274
- if (nLEnR === 0) {
275
- // Non-canonical signature: R length is zero
276
- return false;
277
- }
278
- if ((R[0] & 0x80) !== 0) {
279
- // Non-canonical signature: R value negative
280
- return false;
281
- }
282
- if (nLEnR > 1 && R[0] === 0x00 && (R[1] & 0x80) === 0) {
283
- // Non-canonical signature: R value excessively padded
284
- return false;
285
- }
286
- const S = buf.slice(6 + nLEnR);
287
- if (buf[6 + nLEnR - 2] !== 0x02) {
288
- // Non-canonical signature: S value type mismatch
289
- return false;
290
- }
291
- if (buf[6 + nLEnR - 1] !== nLEnS) {
292
- // Non-canonical signature: S length mismatch
293
- return false;
294
- }
295
- if (nLEnS === 0) {
296
- // Non-canonical signature: S length is zero
297
- return false;
298
- }
299
- if ((S[0] & 0x80) !== 0) {
300
- // Non-canonical signature: S value negative
301
- return false;
302
- }
303
- if (nLEnS > 1 && S[0] === 0x00 && (S[1] & 0x80) === 0) {
304
- // Non-canonical signature: S value excessively padded
305
- return false;
306
- }
265
+ const item = this.altStack.pop();
266
+ this.altStackMem -= item.length;
267
+ return item;
268
+ }
269
+ checkSignatureEncoding(buf) {
270
+ if (buf.length === 0)
307
271
  return true;
308
- };
309
- const checkSignatureEncoding = (buf) => {
310
- // Empty signature. Not strictly DER encoded, but allowed to provide a
311
- // compact way to provide an invalid signature for use with CHECK(MULTI)SIG
312
- if (buf.length === 0) {
313
- return true;
314
- }
315
- if (!isChecksigFormat(buf)) {
316
- this.scriptEvaluationError('The signature format is invalid.');
317
- }
318
- const sig = TransactionSignature_js_1.default.fromChecksigFormat(buf);
272
+ if (!isChecksigFormatHelper(buf)) {
273
+ this.scriptEvaluationError('The signature format is invalid.'); // Generic message like original
274
+ return false;
275
+ }
276
+ try {
277
+ const sig = TransactionSignature_js_1.default.fromChecksigFormat(buf); // This can throw for stricter DER rules
319
278
  if (requireLowSSignatures && !sig.hasLowS()) {
320
279
  this.scriptEvaluationError('The signature must have a low S value.');
280
+ return false;
321
281
  }
322
282
  if ((sig.scope & TransactionSignature_js_1.default.SIGHASH_FORKID) === 0) {
323
283
  this.scriptEvaluationError('The signature must use SIGHASH_FORKID.');
324
284
  return false;
325
285
  }
326
- return true;
327
- };
328
- const checkPublicKeyEncoding = (buf) => {
329
- if (buf.length < 33) {
330
- this.scriptEvaluationError('The public key is too short, it must be at least 33 bytes.');
331
- }
332
- if (buf[0] === 0x04) {
333
- if (buf.length !== 65) {
334
- this.scriptEvaluationError('The non-compressed public key must be 65 bytes.');
335
- }
336
- }
337
- else if (buf[0] === 0x02 || buf[0] === 0x03) {
338
- if (buf.length !== 33) {
339
- this.scriptEvaluationError('The compressed public key must be 33 bytes.');
340
- }
286
+ }
287
+ catch (e) {
288
+ this.scriptEvaluationError('The signature format is invalid.');
289
+ return false;
290
+ }
291
+ return true;
292
+ }
293
+ checkPublicKeyEncoding(buf) {
294
+ if (buf.length === 0) {
295
+ this.scriptEvaluationError('Public key is empty.');
296
+ return false;
297
+ }
298
+ if (buf.length < 33) {
299
+ this.scriptEvaluationError('The public key is too short, it must be at least 33 bytes.');
300
+ return false;
301
+ }
302
+ if (buf[0] === 0x04) {
303
+ if (buf.length !== 65) {
304
+ this.scriptEvaluationError('The non-compressed public key must be 65 bytes.');
305
+ return false;
341
306
  }
342
- else {
343
- this.scriptEvaluationError('The public key is in an unknown format.');
307
+ }
308
+ else if (buf[0] === 0x02 || buf[0] === 0x03) {
309
+ if (buf.length !== 33) {
310
+ this.scriptEvaluationError('The compressed public key must be 33 bytes.');
311
+ return false;
344
312
  }
345
- return true;
346
- };
347
- const verifySignature = (sig, pubkey, subscript) => {
348
- const preimage = TransactionSignature_js_1.default.format({
349
- sourceTXID: this.sourceTXID,
350
- sourceOutputIndex: this.sourceOutputIndex,
351
- sourceSatoshis: this.sourceSatoshis,
352
- transactionVersion: this.transactionVersion,
353
- otherInputs: this.otherInputs,
354
- outputs: this.outputs,
355
- inputIndex: this.inputIndex,
356
- subscript,
357
- inputSequence: this.inputSequence,
358
- lockTime: this.lockTime,
359
- scope: sig.scope
360
- });
361
- const hash = new BigNumber_js_1.default(Hash.hash256(preimage));
362
- return (0, ECDSA_js_1.verify)(hash, sig, pubkey);
363
- };
364
- const isScriptExecuting = !this.ifStack.includes(false);
365
- let buf, buf1, buf2, buf3, spliced, n, size, rawnum, num, signbit, x1, x2, x3, bn, bn1, bn2, bn3, bufSig, bufPubkey, subscript;
366
- let sig, pubkey, i, fOk, nKeysCount, ikey, ikey2, nSigsCount, isig;
367
- let fValue, fEqual, fSuccess;
368
- // Read instruction
313
+ }
314
+ else {
315
+ this.scriptEvaluationError('The public key is in an unknown format.');
316
+ return false;
317
+ }
318
+ try {
319
+ PublicKey_js_1.default.fromDER(buf); // This can throw for stricter DER rules
320
+ }
321
+ catch (e) {
322
+ this.scriptEvaluationError('The public key is in an unknown format.');
323
+ return false;
324
+ }
325
+ return true;
326
+ }
327
+ verifySignature(sig, pubkey, subscript) {
328
+ const preimage = TransactionSignature_js_1.default.format({
329
+ sourceTXID: this.sourceTXID,
330
+ sourceOutputIndex: this.sourceOutputIndex,
331
+ sourceSatoshis: this.sourceSatoshis,
332
+ transactionVersion: this.transactionVersion,
333
+ otherInputs: this.otherInputs,
334
+ outputs: this.outputs,
335
+ inputIndex: this.inputIndex,
336
+ subscript,
337
+ inputSequence: this.inputSequence,
338
+ lockTime: this.lockTime,
339
+ scope: sig.scope
340
+ });
341
+ const hash = new BigNumber_js_1.default(Hash.hash256(preimage));
342
+ return (0, ECDSA_js_1.verify)(hash, sig, pubkey);
343
+ }
344
+ step() {
345
+ if (this.stackMem > this.memoryLimit) {
346
+ this.scriptEvaluationError('Stack memory usage has exceeded ' + String(this.memoryLimit) + ' bytes');
347
+ return false; // Error thrown
348
+ }
349
+ if (this.altStackMem > this.memoryLimit) {
350
+ this.scriptEvaluationError('Alt stack memory usage has exceeded ' + String(this.memoryLimit) + ' bytes');
351
+ return false; // Error thrown
352
+ }
353
+ if (this.context === 'UnlockingScript' &&
354
+ this.programCounter >= this.unlockingScript.chunks.length) {
355
+ this.context = 'LockingScript';
356
+ this.programCounter = 0;
357
+ }
358
+ const currentScript = this.context === 'UnlockingScript' ? this.unlockingScript : this.lockingScript;
359
+ if (this.programCounter >= currentScript.chunks.length) {
360
+ return false;
361
+ }
362
+ const operation = currentScript.chunks[this.programCounter];
369
363
  const currentOpcode = operation.op;
370
364
  if (typeof currentOpcode === 'undefined') {
371
- this.scriptEvaluationError(`An opcode is missing in this chunk of the ${this.context}!`);
365
+ this.scriptEvaluationError(`Missing opcode in ${this.context} at pc=${this.programCounter}.`); // Error thrown
372
366
  }
373
- if (Array.isArray(operation.data) &&
374
- operation.data.length > maxScriptElementSize) {
375
- this.scriptEvaluationError(`It's not currently possible to push data larger than ${maxScriptElementSize} bytes.`);
367
+ if (Array.isArray(operation.data) && operation.data.length > maxScriptElementSize) {
368
+ this.scriptEvaluationError(`Data push > ${maxScriptElementSize} bytes (pc=${this.programCounter}).`); // Error thrown
376
369
  }
377
- if (isScriptExecuting && isOpcodeDisabled(currentOpcode)) {
378
- this.scriptEvaluationError('This opcode is currently disabled.');
370
+ const isScriptExecuting = !this.ifStack.includes(false);
371
+ if (isScriptExecuting && isOpcodeDisabledHelper(currentOpcode)) {
372
+ this.scriptEvaluationError(`This opcode is currently disabled. (Opcode: ${OP_js_1.default[currentOpcode]}, PC: ${this.programCounter})`); // Error thrown
379
373
  }
380
- if (isScriptExecuting &&
381
- currentOpcode >= 0 &&
382
- currentOpcode <= OP_js_1.default.OP_PUSHDATA4) {
383
- if (requireMinimalPush && !isChunkMinimal(operation)) {
384
- this.scriptEvaluationError('This data is not minimally-encoded.');
385
- }
386
- if (!Array.isArray(operation.data)) {
387
- this.stack.push([]);
388
- this.stackMem += 0;
389
- }
390
- else {
391
- this.stack.push(operation.data);
392
- this.stackMem += operation.data.length;
374
+ if (isScriptExecuting && currentOpcode >= 0 && currentOpcode <= OP_js_1.default.OP_PUSHDATA4) {
375
+ if (requireMinimalPush && !isChunkMinimalPushHelper(operation)) {
376
+ this.scriptEvaluationError(`This data is not minimally-encoded. (PC: ${this.programCounter})`); // Error thrown
393
377
  }
378
+ this.pushStack(Array.isArray(operation.data) ? operation.data : []);
394
379
  }
395
- else if (isScriptExecuting ||
396
- (OP_js_1.default.OP_IF <= currentOpcode && currentOpcode <= OP_js_1.default.OP_ENDIF)) {
380
+ else if (isScriptExecuting || (currentOpcode >= OP_js_1.default.OP_IF && currentOpcode <= OP_js_1.default.OP_ENDIF)) {
381
+ let buf, buf1, buf2, buf3;
382
+ let x1, x2, x3;
383
+ let bn, bn1, bn2, bn3;
384
+ let n, size, fValue, fSuccess, subscript;
385
+ let bufSig, bufPubkey;
386
+ let sig, pubkey;
387
+ let i, ikey, isig, nKeysCount, nSigsCount, fOk;
397
388
  switch (currentOpcode) {
398
389
  case OP_js_1.default.OP_1NEGATE:
390
+ this.pushStackCopy(SCRIPTNUM_NEG_1);
391
+ break;
392
+ case OP_js_1.default.OP_0:
393
+ this.pushStackCopy(SCRIPTNUMS_0_TO_16[0]);
394
+ break;
399
395
  case OP_js_1.default.OP_1:
400
396
  case OP_js_1.default.OP_2:
401
397
  case OP_js_1.default.OP_3:
@@ -413,13 +409,11 @@ class Spend {
413
409
  case OP_js_1.default.OP_15:
414
410
  case OP_js_1.default.OP_16:
415
411
  n = currentOpcode - (OP_js_1.default.OP_1 - 1);
416
- buf = new BigNumber_js_1.default(n).toScriptNum();
417
- this.stack.push(buf);
418
- this.stackMem += buf.length;
412
+ this.pushStackCopy(SCRIPTNUMS_0_TO_16[n]);
419
413
  break;
420
414
  case OP_js_1.default.OP_NOP:
421
- case OP_js_1.default.OP_NOP2:
422
- case OP_js_1.default.OP_NOP3:
415
+ case OP_js_1.default.OP_NOP2: // Formerly CHECKLOCKTIMEVERIFY
416
+ case OP_js_1.default.OP_NOP3: // Formerly CHECKSEQUENCEVERIFY
423
417
  case OP_js_1.default.OP_NOP1:
424
418
  case OP_js_1.default.OP_NOP4:
425
419
  case OP_js_1.default.OP_NOP5:
@@ -428,6 +422,9 @@ class Spend {
428
422
  case OP_js_1.default.OP_NOP8:
429
423
  case OP_js_1.default.OP_NOP9:
430
424
  case OP_js_1.default.OP_NOP10:
425
+ /* falls through */
426
+ // eslint-disable-next-line no-fallthrough
427
+ // eslint-disable-next-line no-fallthrough
431
428
  case OP_js_1.default.OP_NOP11:
432
429
  case OP_js_1.default.OP_NOP12:
433
430
  case OP_js_1.default.OP_NOP13:
@@ -497,377 +494,267 @@ class Spend {
497
494
  case OP_js_1.default.OP_NOTIF:
498
495
  fValue = false;
499
496
  if (isScriptExecuting) {
500
- if (this.stack.length < 1) {
497
+ if (this.stack.length < 1)
501
498
  this.scriptEvaluationError('OP_IF and OP_NOTIF require at least one item on the stack when they are used!');
502
- }
503
- buf = this.stacktop(-1);
499
+ buf = this.popStack();
504
500
  fValue = this.castToBool(buf);
505
- if (currentOpcode === OP_js_1.default.OP_NOTIF) {
501
+ if (currentOpcode === OP_js_1.default.OP_NOTIF)
506
502
  fValue = !fValue;
507
- }
508
- poppedValue = this.stack.pop();
509
- if (poppedValue != null) {
510
- this.stackMem -= poppedValue.length;
511
- }
512
- clearPoppedValue();
513
503
  }
514
504
  this.ifStack.push(fValue);
515
505
  break;
516
506
  case OP_js_1.default.OP_ELSE:
517
- if (this.ifStack.length === 0) {
507
+ if (this.ifStack.length === 0)
518
508
  this.scriptEvaluationError('OP_ELSE requires a preceeding OP_IF.');
519
- }
520
- this.ifStack[this.ifStack.length - 1] =
521
- !this.ifStack[this.ifStack.length - 1];
509
+ this.ifStack[this.ifStack.length - 1] = !this.ifStack[this.ifStack.length - 1];
522
510
  break;
523
511
  case OP_js_1.default.OP_ENDIF:
524
- if (this.ifStack.length === 0) {
512
+ if (this.ifStack.length === 0)
525
513
  this.scriptEvaluationError('OP_ENDIF requires a preceeding OP_IF.');
526
- }
527
514
  this.ifStack.pop();
528
515
  break;
529
516
  case OP_js_1.default.OP_VERIFY:
530
- if (this.stack.length < 1) {
517
+ if (this.stack.length < 1)
531
518
  this.scriptEvaluationError('OP_VERIFY requires at least one item to be on the stack.');
532
- }
533
- buf = this.stacktop(-1);
534
- fValue = this.castToBool(buf);
535
- poppedValue = this.stack.pop();
536
- if (poppedValue != null) {
537
- this.stackMem -= poppedValue.length;
538
- }
539
- clearPoppedValue();
540
- if (!fValue) {
519
+ buf1 = this.stackTop();
520
+ fValue = this.castToBool(buf1);
521
+ if (!fValue)
541
522
  this.scriptEvaluationError('OP_VERIFY requires the top stack value to be truthy.');
542
- }
523
+ this.popStack();
543
524
  break;
544
525
  case OP_js_1.default.OP_RETURN:
545
- if (this.context === 'UnlockingScript') {
526
+ if (this.context === 'UnlockingScript')
546
527
  this.programCounter = this.unlockingScript.chunks.length;
547
- }
548
- else {
528
+ else
549
529
  this.programCounter = this.lockingScript.chunks.length;
550
- }
551
530
  this.ifStack = [];
531
+ this.programCounter--; // To counteract the final increment and ensure loop termination
552
532
  break;
553
533
  case OP_js_1.default.OP_TOALTSTACK:
554
- if (this.stack.length < 1) {
534
+ if (this.stack.length < 1)
555
535
  this.scriptEvaluationError('OP_TOALTSTACK requires at oeast one item to be on the stack.');
556
- }
557
- poppedValue = this.stack.pop();
558
- if (poppedValue != null) {
559
- this.altStack.push(poppedValue);
560
- this.altStackMem += poppedValue.length;
561
- this.stackMem -= poppedValue.length;
562
- }
563
- clearPoppedValue();
536
+ this.pushAltStack(this.popStack());
564
537
  break;
565
538
  case OP_js_1.default.OP_FROMALTSTACK:
566
- if (this.altStack.length < 1) {
567
- this.scriptEvaluationError('OP_FROMALTSTACK requires at least one item to be on the stack.');
568
- }
569
- poppedValue = this.altStack.pop();
570
- if (poppedValue != null) {
571
- this.stack.push(poppedValue);
572
- this.stackMem += poppedValue.length;
573
- this.altStackMem -= poppedValue.length;
574
- }
575
- clearPoppedValue();
539
+ if (this.altStack.length < 1)
540
+ this.scriptEvaluationError('OP_FROMALTSTACK requires at least one item to be on the stack.'); // "stack" here means altstack
541
+ this.pushStack(this.popAltStack());
576
542
  break;
577
543
  case OP_js_1.default.OP_2DROP:
578
- if (this.stack.length < 2) {
544
+ if (this.stack.length < 2)
579
545
  this.scriptEvaluationError('OP_2DROP requires at least two items to be on the stack.');
580
- }
581
- poppedValue = this.stack.pop();
582
- if (poppedValue != null) {
583
- this.stackMem -= poppedValue.length;
584
- }
585
- clearPoppedValue();
586
- poppedValue = this.stack.pop();
587
- if (poppedValue != null) {
588
- this.stackMem -= poppedValue.length;
589
- }
590
- clearPoppedValue();
546
+ this.popStack();
547
+ this.popStack();
591
548
  break;
592
549
  case OP_js_1.default.OP_2DUP:
593
- if (this.stack.length < 2) {
550
+ if (this.stack.length < 2)
594
551
  this.scriptEvaluationError('OP_2DUP requires at least two items to be on the stack.');
595
- }
596
- buf1 = this.stacktop(-2);
597
- buf2 = this.stacktop(-1);
598
- this.stack.push([...buf1]);
599
- this.stackMem += buf1.length;
600
- this.stack.push([...buf2]);
601
- this.stackMem += buf2.length;
552
+ buf1 = this.stackTop(-2);
553
+ buf2 = this.stackTop(-1);
554
+ this.pushStackCopy(buf1);
555
+ this.pushStackCopy(buf2);
602
556
  break;
603
557
  case OP_js_1.default.OP_3DUP:
604
- if (this.stack.length < 3) {
558
+ if (this.stack.length < 3)
605
559
  this.scriptEvaluationError('OP_3DUP requires at least three items to be on the stack.');
606
- }
607
- buf1 = this.stacktop(-3);
608
- buf2 = this.stacktop(-2);
609
- buf3 = this.stacktop(-1);
610
- this.stack.push([...buf1]);
611
- this.stackMem += buf1.length;
612
- this.stack.push([...buf2]);
613
- this.stackMem += buf2.length;
614
- this.stack.push([...buf3]);
615
- this.stackMem += buf3.length;
560
+ buf1 = this.stackTop(-3);
561
+ buf2 = this.stackTop(-2);
562
+ buf3 = this.stackTop(-1);
563
+ this.pushStackCopy(buf1);
564
+ this.pushStackCopy(buf2);
565
+ this.pushStackCopy(buf3);
616
566
  break;
617
567
  case OP_js_1.default.OP_2OVER:
618
- if (this.stack.length < 4) {
568
+ if (this.stack.length < 4)
619
569
  this.scriptEvaluationError('OP_2OVER requires at least four items to be on the stack.');
620
- }
621
- buf1 = this.stacktop(-4);
622
- buf2 = this.stacktop(-3);
623
- this.stack.push([...buf1]);
624
- this.stackMem += buf1.length;
625
- this.stack.push([...buf2]);
626
- this.stackMem += buf2.length;
570
+ buf1 = this.stackTop(-4);
571
+ buf2 = this.stackTop(-3);
572
+ this.pushStackCopy(buf1);
573
+ this.pushStackCopy(buf2);
627
574
  break;
628
- case OP_js_1.default.OP_2ROT:
629
- if (this.stack.length < 6) {
575
+ case OP_js_1.default.OP_2ROT: {
576
+ if (this.stack.length < 6)
630
577
  this.scriptEvaluationError('OP_2ROT requires at least six items to be on the stack.');
631
- }
632
- spliced = this.stack.splice(this.stack.length - 6, 2);
633
- this.stack.push(spliced[0]);
634
- this.stackMem += spliced[0].length;
635
- this.stack.push(spliced[1]);
636
- this.stackMem += spliced[1].length;
578
+ const rot6 = this.popStack();
579
+ const rot5 = this.popStack();
580
+ const rot4 = this.popStack();
581
+ const rot3 = this.popStack();
582
+ const rot2 = this.popStack();
583
+ const rot1 = this.popStack();
584
+ this.pushStack(rot3);
585
+ this.pushStack(rot4);
586
+ this.pushStack(rot5);
587
+ this.pushStack(rot6);
588
+ this.pushStack(rot1);
589
+ this.pushStack(rot2);
637
590
  break;
638
- case OP_js_1.default.OP_2SWAP:
639
- if (this.stack.length < 4) {
591
+ }
592
+ case OP_js_1.default.OP_2SWAP: {
593
+ if (this.stack.length < 4)
640
594
  this.scriptEvaluationError('OP_2SWAP requires at least four items to be on the stack.');
641
- }
642
- spliced = this.stack.splice(this.stack.length - 4, 2);
643
- this.stack.push(spliced[0]);
644
- this.stackMem += spliced[0].length;
645
- this.stack.push(spliced[1]);
646
- this.stackMem += spliced[1].length;
595
+ const swap4 = this.popStack();
596
+ const swap3 = this.popStack();
597
+ const swap2 = this.popStack();
598
+ const swap1 = this.popStack();
599
+ this.pushStack(swap3);
600
+ this.pushStack(swap4);
601
+ this.pushStack(swap1);
602
+ this.pushStack(swap2);
647
603
  break;
604
+ }
648
605
  case OP_js_1.default.OP_IFDUP:
649
- if (this.stack.length < 1) {
606
+ if (this.stack.length < 1)
650
607
  this.scriptEvaluationError('OP_IFDUP requires at least one item to be on the stack.');
651
- }
652
- buf = this.stacktop(-1);
653
- fValue = this.castToBool(buf);
654
- if (fValue) {
655
- this.stack.push([...buf]);
656
- this.stackMem += buf.length;
608
+ buf1 = this.stackTop();
609
+ if (this.castToBool(buf1)) {
610
+ this.pushStackCopy(buf1);
657
611
  }
658
612
  break;
659
613
  case OP_js_1.default.OP_DEPTH:
660
- buf = new BigNumber_js_1.default(this.stack.length).toScriptNum();
661
- this.stack.push(buf);
662
- this.stackMem += buf.length;
614
+ this.pushStack(new BigNumber_js_1.default(this.stack.length).toScriptNum());
663
615
  break;
664
616
  case OP_js_1.default.OP_DROP:
665
- if (this.stack.length < 1) {
617
+ if (this.stack.length < 1)
666
618
  this.scriptEvaluationError('OP_DROP requires at least one item to be on the stack.');
667
- }
668
- poppedValue = this.stack.pop();
669
- if (poppedValue != null) {
670
- this.stackMem -= poppedValue.length;
671
- }
672
- clearPoppedValue();
619
+ this.popStack();
673
620
  break;
674
621
  case OP_js_1.default.OP_DUP:
675
- if (this.stack.length < 1) {
622
+ if (this.stack.length < 1)
676
623
  this.scriptEvaluationError('OP_DUP requires at least one item to be on the stack.');
677
- }
678
- this.stack.push([...this.stacktop(-1)]);
679
- this.stackMem += this.stacktop(-1).length;
624
+ this.pushStackCopy(this.stackTop());
680
625
  break;
681
626
  case OP_js_1.default.OP_NIP:
682
- if (this.stack.length < 2) {
627
+ if (this.stack.length < 2)
683
628
  this.scriptEvaluationError('OP_NIP requires at least two items to be on the stack.');
684
- }
685
- this.stack.splice(this.stack.length - 2, 1);
686
- this.stackMem -= this.stacktop(-1).length;
629
+ buf2 = this.popStack();
630
+ this.popStack();
631
+ this.pushStack(buf2);
687
632
  break;
688
633
  case OP_js_1.default.OP_OVER:
689
- if (this.stack.length < 2) {
634
+ if (this.stack.length < 2)
690
635
  this.scriptEvaluationError('OP_OVER requires at least two items to be on the stack.');
691
- }
692
- this.stack.push([...this.stacktop(-2)]);
693
- this.stackMem += this.stacktop(-2).length;
636
+ this.pushStackCopy(this.stackTop(-2));
694
637
  break;
695
638
  case OP_js_1.default.OP_PICK:
696
- case OP_js_1.default.OP_ROLL:
697
- if (this.stack.length < 2) {
639
+ case OP_js_1.default.OP_ROLL: {
640
+ if (this.stack.length < 2)
698
641
  this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} requires at least two items to be on the stack.`);
699
- }
700
- buf = this.stacktop(-1);
701
- bn = BigNumber_js_1.default.fromScriptNum(buf, requireMinimalPush);
642
+ bn = BigNumber_js_1.default.fromScriptNum(this.popStack(), requireMinimalPush);
702
643
  n = bn.toNumber();
703
- poppedValue = this.stack.pop();
704
- if (poppedValue != null) {
705
- this.stackMem -= poppedValue.length;
706
- }
707
- clearPoppedValue();
708
644
  if (n < 0 || n >= this.stack.length) {
709
645
  this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} requires the top stack element to be 0 or a positive number less than the current size of the stack.`);
710
646
  }
711
- buf = this.stacktop(-n - 1);
647
+ const itemToMoveOrCopy = this.stack[this.stack.length - 1 - n];
712
648
  if (currentOpcode === OP_js_1.default.OP_ROLL) {
713
- this.stack.splice(this.stack.length - n - 1, 1);
714
- this.stackMem -= buf.length;
649
+ this.stack.splice(this.stack.length - 1 - n, 1);
650
+ this.stackMem -= itemToMoveOrCopy.length;
651
+ this.pushStack(itemToMoveOrCopy);
652
+ }
653
+ else { // OP_PICK
654
+ this.pushStackCopy(itemToMoveOrCopy);
715
655
  }
716
- this.stack.push([...buf]);
717
- this.stackMem += buf.length;
718
656
  break;
657
+ }
719
658
  case OP_js_1.default.OP_ROT:
720
- if (this.stack.length < 3) {
659
+ if (this.stack.length < 3)
721
660
  this.scriptEvaluationError('OP_ROT requires at least three items to be on the stack.');
722
- }
723
- x1 = this.stacktop(-3);
724
- x2 = this.stacktop(-2);
725
- x3 = this.stacktop(-1);
726
- this.stack[this.stack.length - 3] = x2;
727
- this.stack[this.stack.length - 2] = x3;
728
- this.stack[this.stack.length - 1] = x1;
661
+ x3 = this.popStack();
662
+ x2 = this.popStack();
663
+ x1 = this.popStack();
664
+ this.pushStack(x2);
665
+ this.pushStack(x3);
666
+ this.pushStack(x1);
729
667
  break;
730
668
  case OP_js_1.default.OP_SWAP:
731
- if (this.stack.length < 2) {
669
+ if (this.stack.length < 2)
732
670
  this.scriptEvaluationError('OP_SWAP requires at least two items to be on the stack.');
733
- }
734
- x1 = this.stacktop(-2);
735
- x2 = this.stacktop(-1);
736
- this.stack[this.stack.length - 2] = x2;
737
- this.stack[this.stack.length - 1] = x1;
671
+ x2 = this.popStack();
672
+ x1 = this.popStack();
673
+ this.pushStack(x2);
674
+ this.pushStack(x1);
738
675
  break;
739
676
  case OP_js_1.default.OP_TUCK:
740
- if (this.stack.length < 2) {
677
+ if (this.stack.length < 2)
741
678
  this.scriptEvaluationError('OP_TUCK requires at least two items to be on the stack.');
742
- }
743
- this.stack.splice(this.stack.length - 2, 0, [...this.stacktop(-1)]);
744
- this.stackMem += this.stacktop(-1).length;
679
+ buf1 = this.stackTop(-1); // Top element (x2)
680
+ // stack is [... rest, x1, x2]
681
+ // We want [... rest, x2_copy, x1, x2]
682
+ this.ensureStackMem(buf1.length);
683
+ this.stack.splice(this.stack.length - 2, 0, buf1.slice()); // Insert copy of x2 before x1
684
+ this.stackMem += buf1.length; // Account for the new copy
745
685
  break;
746
686
  case OP_js_1.default.OP_SIZE:
747
- if (this.stack.length < 1) {
687
+ if (this.stack.length < 1)
748
688
  this.scriptEvaluationError('OP_SIZE requires at least one item to be on the stack.');
749
- }
750
- bn = new BigNumber_js_1.default(this.stacktop(-1).length);
751
- this.stack.push(bn.toScriptNum());
752
- this.stackMem += bn.toScriptNum().length;
689
+ this.pushStack(new BigNumber_js_1.default(this.stackTop().length).toScriptNum());
753
690
  break;
754
691
  case OP_js_1.default.OP_AND:
755
692
  case OP_js_1.default.OP_OR:
756
- case OP_js_1.default.OP_XOR:
757
- if (this.stack.length < 2) {
758
- this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} requires at least one item to be on the stack.`);
759
- }
760
- buf1 = this.stacktop(-2);
761
- buf2 = this.stacktop(-1);
762
- if (buf1.length !== buf2.length) {
693
+ case OP_js_1.default.OP_XOR: {
694
+ if (this.stack.length < 2)
695
+ this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} requires at least two items on the stack.`);
696
+ buf2 = this.popStack();
697
+ buf1 = this.popStack();
698
+ if (buf1.length !== buf2.length)
763
699
  this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} requires the top two stack items to be the same size.`);
764
- }
765
- switch (currentOpcode) {
766
- case OP_js_1.default.OP_AND:
767
- for (let i = 0; i < buf1.length; i++) {
768
- buf1[i] &= buf2[i];
769
- }
770
- break;
771
- case OP_js_1.default.OP_OR:
772
- for (let i = 0; i < buf1.length; i++) {
773
- buf1[i] |= buf2[i];
774
- }
775
- break;
776
- case OP_js_1.default.OP_XOR:
777
- for (let i = 0; i < buf1.length; i++) {
778
- buf1[i] ^= buf2[i];
779
- }
780
- break;
781
- }
782
- // And pop vch2.
783
- poppedValue = this.stack.pop();
784
- if (poppedValue != null) {
785
- this.stackMem -= poppedValue.length;
786
- }
787
- clearPoppedValue();
700
+ const resultBufBitwiseOp = new Array(buf1.length);
701
+ for (let k = 0; k < buf1.length; k++) {
702
+ if (currentOpcode === OP_js_1.default.OP_AND)
703
+ resultBufBitwiseOp[k] = buf1[k] & buf2[k];
704
+ else if (currentOpcode === OP_js_1.default.OP_OR)
705
+ resultBufBitwiseOp[k] = buf1[k] | buf2[k];
706
+ else
707
+ resultBufBitwiseOp[k] = buf1[k] ^ buf2[k];
708
+ }
709
+ this.pushStack(resultBufBitwiseOp);
788
710
  break;
789
- case OP_js_1.default.OP_INVERT:
790
- if (this.stack.length < 1) {
711
+ }
712
+ case OP_js_1.default.OP_INVERT: {
713
+ if (this.stack.length < 1)
791
714
  this.scriptEvaluationError('OP_INVERT requires at least one item to be on the stack.');
715
+ buf = this.popStack();
716
+ const invertedBufOp = new Array(buf.length);
717
+ for (let k = 0; k < buf.length; k++) {
718
+ invertedBufOp[k] = (~buf[k]) & 0xff;
792
719
  }
793
- buf = this.stacktop(-1);
794
- for (let i = 0; i < buf.length; i++) {
795
- buf[i] = ~buf[i];
796
- }
720
+ this.pushStack(invertedBufOp);
797
721
  break;
722
+ }
798
723
  case OP_js_1.default.OP_LSHIFT:
799
- case OP_js_1.default.OP_RSHIFT:
800
- if (this.stack.length < 2) {
724
+ case OP_js_1.default.OP_RSHIFT: {
725
+ if (this.stack.length < 2)
801
726
  this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} requires at least two items to be on the stack.`);
802
- }
803
- buf1 = this.stacktop(-2);
727
+ bn2 = BigNumber_js_1.default.fromScriptNum(this.popStack(), requireMinimalPush); // n (shift amount)
728
+ buf1 = this.popStack(); // value to shift
729
+ n = bn2.toNumber();
730
+ if (n < 0)
731
+ this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} requires the top item on the stack not to be negative.`);
804
732
  if (buf1.length === 0) {
805
- poppedValue = this.stack.pop();
806
- if (poppedValue != null) {
807
- this.stackMem -= poppedValue.length;
808
- }
809
- clearPoppedValue();
810
- }
811
- else {
812
- bn1 = new BigNumber_js_1.default(buf1);
813
- bn2 = BigNumber_js_1.default.fromScriptNum(this.stacktop(-1), requireMinimalPush);
814
- n = bn2.toNumber();
815
- if (n < 0) {
816
- this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} requires the top item on the stack not to be negative.`);
817
- }
818
- poppedValue = this.stack.pop();
819
- if (poppedValue != null) {
820
- this.stackMem -= poppedValue.length;
821
- }
822
- clearPoppedValue();
823
- poppedValue = this.stack.pop();
824
- if (poppedValue != null) {
825
- this.stackMem -= poppedValue.length;
826
- }
827
- clearPoppedValue();
828
- let shifted;
829
- if (currentOpcode === OP_js_1.default.OP_LSHIFT) {
830
- shifted = bn1.ushln(n);
831
- }
832
- if (currentOpcode === OP_js_1.default.OP_RSHIFT) {
833
- shifted = bn1.ushrn(n);
834
- }
835
- const bufShifted = padDataToSize(shifted.toArray().slice(buf1.length * -1), buf1.length);
836
- this.stack.push(bufShifted);
837
- this.stackMem += bufShifted.length;
733
+ this.pushStack([]);
734
+ break;
838
735
  }
736
+ bn1 = new BigNumber_js_1.default(buf1);
737
+ let shiftedBn;
738
+ if (currentOpcode === OP_js_1.default.OP_LSHIFT)
739
+ shiftedBn = bn1.ushln(n);
740
+ else
741
+ shiftedBn = bn1.ushrn(n);
742
+ const shiftedArr = shiftedBn.toArray('le', buf1.length);
743
+ this.pushStack(shiftedArr);
839
744
  break;
745
+ }
840
746
  case OP_js_1.default.OP_EQUAL:
841
747
  case OP_js_1.default.OP_EQUALVERIFY:
842
- if (this.stack.length < 2) {
748
+ if (this.stack.length < 2)
843
749
  this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} requires at least two items to be on the stack.`);
844
- }
845
- buf1 = this.stacktop(-2);
846
- buf2 = this.stacktop(-1);
847
- fEqual = (0, utils_js_1.toHex)(buf1) === (0, utils_js_1.toHex)(buf2);
848
- poppedValue = this.stack.pop();
849
- if (poppedValue != null) {
850
- this.stackMem -= poppedValue.length;
851
- }
852
- clearPoppedValue();
853
- poppedValue = this.stack.pop();
854
- if (poppedValue != null) {
855
- this.stackMem -= poppedValue.length;
856
- }
857
- clearPoppedValue();
858
- this.stack.push(fEqual ? [1] : []);
859
- this.stackMem += (fEqual ? 1 : 0);
750
+ buf2 = this.popStack();
751
+ buf1 = this.popStack();
752
+ fValue = compareNumberArrays(buf1, buf2);
753
+ this.pushStack(fValue ? [1] : []);
860
754
  if (currentOpcode === OP_js_1.default.OP_EQUALVERIFY) {
861
- if (this.castToBool(this.stacktop(-1))) {
862
- poppedValue = this.stack.pop();
863
- if (poppedValue != null) {
864
- this.stackMem -= poppedValue.length;
865
- }
866
- clearPoppedValue();
867
- }
868
- else {
755
+ if (!fValue)
869
756
  this.scriptEvaluationError('OP_EQUALVERIFY requires the top two stack items to be equal.');
870
- }
757
+ this.popStack();
871
758
  }
872
759
  break;
873
760
  case OP_js_1.default.OP_1ADD:
@@ -876,46 +763,37 @@ class Spend {
876
763
  case OP_js_1.default.OP_ABS:
877
764
  case OP_js_1.default.OP_NOT:
878
765
  case OP_js_1.default.OP_0NOTEQUAL:
879
- if (this.stack.length < 1) {
880
- this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} requires at least one items to be on the stack.`);
881
- }
882
- buf = this.stacktop(-1);
883
- bn = BigNumber_js_1.default.fromScriptNum(buf, requireMinimalPush);
766
+ if (this.stack.length < 1)
767
+ this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} requires at least one item to be on the stack.`);
768
+ bn = BigNumber_js_1.default.fromScriptNum(this.popStack(), requireMinimalPush);
884
769
  switch (currentOpcode) {
885
770
  case OP_js_1.default.OP_1ADD:
886
- bn = bn.addn(1);
771
+ bn = bn.add(new BigNumber_js_1.default(1));
887
772
  break;
888
773
  case OP_js_1.default.OP_1SUB:
889
- bn = bn.subn(1);
774
+ bn = bn.sub(new BigNumber_js_1.default(1));
890
775
  break;
891
776
  case OP_js_1.default.OP_NEGATE:
892
777
  bn = bn.neg();
893
778
  break;
894
779
  case OP_js_1.default.OP_ABS:
895
- if (bn.cmpn(0) < 0) {
780
+ if (bn.isNeg())
896
781
  bn = bn.neg();
897
- }
898
782
  break;
899
783
  case OP_js_1.default.OP_NOT:
900
- bn = new BigNumber_js_1.default(bn.cmpn(0) === 0 ? 1 : 0 + 0);
784
+ bn = new BigNumber_js_1.default(bn.cmpn(0) === 0 ? 1 : 0);
901
785
  break;
902
786
  case OP_js_1.default.OP_0NOTEQUAL:
903
- bn = new BigNumber_js_1.default(bn.cmpn(0) !== 0 ? 1 : 0 + 0);
787
+ bn = new BigNumber_js_1.default(bn.cmpn(0) !== 0 ? 1 : 0);
904
788
  break;
905
789
  }
906
- poppedValue = this.stack.pop();
907
- if (poppedValue != null) {
908
- this.stackMem -= poppedValue.length;
909
- }
910
- clearPoppedValue();
911
- this.stack.push(bn.toScriptNum());
912
- this.stackMem += bn.toScriptNum().length;
790
+ this.pushStack(bn.toScriptNum());
913
791
  break;
914
792
  case OP_js_1.default.OP_ADD:
915
793
  case OP_js_1.default.OP_SUB:
916
794
  case OP_js_1.default.OP_MUL:
917
- case OP_js_1.default.OP_MOD:
918
795
  case OP_js_1.default.OP_DIV:
796
+ case OP_js_1.default.OP_MOD:
919
797
  case OP_js_1.default.OP_BOOLAND:
920
798
  case OP_js_1.default.OP_BOOLOR:
921
799
  case OP_js_1.default.OP_NUMEQUAL:
@@ -926,276 +804,208 @@ class Spend {
926
804
  case OP_js_1.default.OP_LESSTHANOREQUAL:
927
805
  case OP_js_1.default.OP_GREATERTHANOREQUAL:
928
806
  case OP_js_1.default.OP_MIN:
929
- case OP_js_1.default.OP_MAX:
930
- if (this.stack.length < 2) {
807
+ case OP_js_1.default.OP_MAX: {
808
+ if (this.stack.length < 2)
931
809
  this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} requires at least two items to be on the stack.`);
810
+ buf2 = this.popStack();
811
+ buf1 = this.popStack();
812
+ bn2 = BigNumber_js_1.default.fromScriptNum(buf2, requireMinimalPush);
813
+ bn1 = BigNumber_js_1.default.fromScriptNum(buf1, requireMinimalPush);
814
+ let predictedLen = 0;
815
+ switch (currentOpcode) {
816
+ case OP_js_1.default.OP_MUL:
817
+ predictedLen = bn1.byteLength() + bn2.byteLength();
818
+ break;
819
+ case OP_js_1.default.OP_ADD:
820
+ case OP_js_1.default.OP_SUB:
821
+ predictedLen = Math.max(bn1.byteLength(), bn2.byteLength()) + 1;
822
+ break;
823
+ default:
824
+ predictedLen = Math.max(bn1.byteLength(), bn2.byteLength());
932
825
  }
933
- bn1 = BigNumber_js_1.default.fromScriptNum(this.stacktop(-2), requireMinimalPush);
934
- bn2 = BigNumber_js_1.default.fromScriptNum(this.stacktop(-1), requireMinimalPush);
935
- bn = new BigNumber_js_1.default(0);
826
+ this.ensureStackMem(predictedLen);
827
+ let resultBnArithmetic = new BigNumber_js_1.default(0);
936
828
  switch (currentOpcode) {
937
829
  case OP_js_1.default.OP_ADD:
938
- bn = bn1.add(bn2);
830
+ resultBnArithmetic = bn1.add(bn2);
939
831
  break;
940
832
  case OP_js_1.default.OP_SUB:
941
- bn = bn1.sub(bn2);
833
+ resultBnArithmetic = bn1.sub(bn2);
942
834
  break;
943
835
  case OP_js_1.default.OP_MUL:
944
- bn = bn1.mul(bn2);
836
+ resultBnArithmetic = bn1.mul(bn2);
945
837
  break;
946
838
  case OP_js_1.default.OP_DIV:
947
- if (bn2.cmpn(0) === 0) {
839
+ if (bn2.cmpn(0) === 0)
948
840
  this.scriptEvaluationError('OP_DIV cannot divide by zero!');
949
- }
950
- bn = bn1.div(bn2);
841
+ resultBnArithmetic = bn1.div(bn2);
951
842
  break;
952
843
  case OP_js_1.default.OP_MOD:
953
- if (bn2.cmpn(0) === 0) {
844
+ if (bn2.cmpn(0) === 0)
954
845
  this.scriptEvaluationError('OP_MOD cannot divide by zero!');
955
- }
956
- bn = bn1.mod(bn2);
846
+ resultBnArithmetic = bn1.mod(bn2);
957
847
  break;
958
848
  case OP_js_1.default.OP_BOOLAND:
959
- bn = new BigNumber_js_1.default(bn1.cmpn(0) !== 0 && bn2.cmpn(0) !== 0 ? 1 : 0 + 0);
849
+ resultBnArithmetic = new BigNumber_js_1.default((bn1.cmpn(0) !== 0 && bn2.cmpn(0) !== 0) ? 1 : 0);
960
850
  break;
961
851
  case OP_js_1.default.OP_BOOLOR:
962
- bn = new BigNumber_js_1.default(bn1.cmpn(0) !== 0 || bn2.cmpn(0) !== 0 ? 1 : 0 + 0);
852
+ resultBnArithmetic = new BigNumber_js_1.default((bn1.cmpn(0) !== 0 || bn2.cmpn(0) !== 0) ? 1 : 0);
963
853
  break;
964
854
  case OP_js_1.default.OP_NUMEQUAL:
965
- bn = new BigNumber_js_1.default(bn1.cmp(bn2) === 0 ? 1 : 0 + 0);
855
+ resultBnArithmetic = new BigNumber_js_1.default(bn1.cmp(bn2) === 0 ? 1 : 0);
966
856
  break;
967
857
  case OP_js_1.default.OP_NUMEQUALVERIFY:
968
- bn = new BigNumber_js_1.default(bn1.cmp(bn2) === 0 ? 1 : 0 + 0);
858
+ resultBnArithmetic = new BigNumber_js_1.default(bn1.cmp(bn2) === 0 ? 1 : 0);
969
859
  break;
970
860
  case OP_js_1.default.OP_NUMNOTEQUAL:
971
- bn = new BigNumber_js_1.default(bn1.cmp(bn2) !== 0 ? 1 : 0 + 0);
861
+ resultBnArithmetic = new BigNumber_js_1.default(bn1.cmp(bn2) !== 0 ? 1 : 0);
972
862
  break;
973
863
  case OP_js_1.default.OP_LESSTHAN:
974
- bn = new BigNumber_js_1.default(bn1.cmp(bn2) < 0 ? 1 : 0 + 0);
864
+ resultBnArithmetic = new BigNumber_js_1.default(bn1.cmp(bn2) < 0 ? 1 : 0);
975
865
  break;
976
866
  case OP_js_1.default.OP_GREATERTHAN:
977
- bn = new BigNumber_js_1.default(bn1.cmp(bn2) > 0 ? 1 : 0 + 0);
867
+ resultBnArithmetic = new BigNumber_js_1.default(bn1.cmp(bn2) > 0 ? 1 : 0);
978
868
  break;
979
869
  case OP_js_1.default.OP_LESSTHANOREQUAL:
980
- bn = new BigNumber_js_1.default(bn1.cmp(bn2) <= 0 ? 1 : 0 + 0);
870
+ resultBnArithmetic = new BigNumber_js_1.default(bn1.cmp(bn2) <= 0 ? 1 : 0);
981
871
  break;
982
872
  case OP_js_1.default.OP_GREATERTHANOREQUAL:
983
- bn = new BigNumber_js_1.default(bn1.cmp(bn2) >= 0 ? 1 : 0 + 0);
873
+ resultBnArithmetic = new BigNumber_js_1.default(bn1.cmp(bn2) >= 0 ? 1 : 0);
984
874
  break;
985
875
  case OP_js_1.default.OP_MIN:
986
- bn = bn1.cmp(bn2) < 0 ? bn1 : bn2;
876
+ resultBnArithmetic = bn1.cmp(bn2) < 0 ? bn1 : bn2;
987
877
  break;
988
878
  case OP_js_1.default.OP_MAX:
989
- bn = bn1.cmp(bn2) > 0 ? bn1 : bn2;
879
+ resultBnArithmetic = bn1.cmp(bn2) > 0 ? bn1 : bn2;
990
880
  break;
991
881
  }
992
- poppedValue = this.stack.pop();
993
- if (poppedValue != null) {
994
- this.stackMem -= poppedValue.length;
995
- }
996
- clearPoppedValue();
997
- poppedValue = this.stack.pop();
998
- if (poppedValue != null) {
999
- this.stackMem -= poppedValue.length;
1000
- }
1001
- clearPoppedValue();
1002
- this.stack.push(bn.toScriptNum());
1003
- this.stackMem += bn.toScriptNum().length;
882
+ this.pushStack(resultBnArithmetic.toScriptNum());
1004
883
  if (currentOpcode === OP_js_1.default.OP_NUMEQUALVERIFY) {
1005
- if (this.castToBool(this.stacktop(-1))) {
1006
- poppedValue = this.stack.pop();
1007
- if (poppedValue != null) {
1008
- this.stackMem -= poppedValue.length;
1009
- }
1010
- clearPoppedValue();
1011
- }
1012
- else {
884
+ if (!this.castToBool(this.stackTop()))
1013
885
  this.scriptEvaluationError('OP_NUMEQUALVERIFY requires the top stack item to be truthy.');
1014
- }
886
+ this.popStack();
1015
887
  }
1016
888
  break;
889
+ }
1017
890
  case OP_js_1.default.OP_WITHIN:
1018
- if (this.stack.length < 3) {
891
+ if (this.stack.length < 3)
1019
892
  this.scriptEvaluationError('OP_WITHIN requires at least three items to be on the stack.');
1020
- }
1021
- bn1 = BigNumber_js_1.default.fromScriptNum(this.stacktop(-3), requireMinimalPush);
1022
- bn2 = BigNumber_js_1.default.fromScriptNum(this.stacktop(-2), requireMinimalPush);
1023
- bn3 = BigNumber_js_1.default.fromScriptNum(this.stacktop(-1), requireMinimalPush);
1024
- fValue = bn2.cmp(bn1) <= 0 && bn1.cmp(bn3) < 0;
1025
- poppedValue = this.stack.pop();
1026
- if (poppedValue != null) {
1027
- this.stackMem -= poppedValue.length;
1028
- }
1029
- clearPoppedValue();
1030
- poppedValue = this.stack.pop();
1031
- if (poppedValue != null) {
1032
- this.stackMem -= poppedValue.length;
1033
- }
1034
- clearPoppedValue();
1035
- poppedValue = this.stack.pop();
1036
- if (poppedValue != null) {
1037
- this.stackMem -= poppedValue.length;
1038
- }
1039
- clearPoppedValue();
1040
- this.stack.push(fValue ? [1] : []);
1041
- this.stackMem += (fValue ? 1 : 0);
893
+ bn3 = BigNumber_js_1.default.fromScriptNum(this.popStack(), requireMinimalPush); // max
894
+ bn2 = BigNumber_js_1.default.fromScriptNum(this.popStack(), requireMinimalPush); // min
895
+ bn1 = BigNumber_js_1.default.fromScriptNum(this.popStack(), requireMinimalPush); // x
896
+ fValue = bn1.cmp(bn2) >= 0 && bn1.cmp(bn3) < 0;
897
+ this.pushStack(fValue ? [1] : []);
1042
898
  break;
1043
899
  case OP_js_1.default.OP_RIPEMD160:
1044
900
  case OP_js_1.default.OP_SHA1:
1045
901
  case OP_js_1.default.OP_SHA256:
1046
902
  case OP_js_1.default.OP_HASH160:
1047
903
  case OP_js_1.default.OP_HASH256: {
1048
- if (this.stack.length < 1) {
904
+ if (this.stack.length < 1)
1049
905
  this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} requires at least one item to be on the stack.`);
1050
- }
1051
- let bufHash = []; // Initialize bufHash to an empty array
1052
- buf = this.stacktop(-1);
1053
- if (currentOpcode === OP_js_1.default.OP_RIPEMD160) {
1054
- bufHash = Hash.ripemd160(buf);
1055
- }
1056
- else if (currentOpcode === OP_js_1.default.OP_SHA1) {
1057
- bufHash = Hash.sha1(buf);
1058
- }
1059
- else if (currentOpcode === OP_js_1.default.OP_SHA256) {
1060
- bufHash = Hash.sha256(buf);
1061
- }
1062
- else if (currentOpcode === OP_js_1.default.OP_HASH160) {
1063
- bufHash = Hash.hash160(buf);
1064
- }
1065
- else if (currentOpcode === OP_js_1.default.OP_HASH256) {
1066
- bufHash = Hash.hash256(buf);
1067
- }
1068
- poppedValue = this.stack.pop();
1069
- if (poppedValue != null) {
1070
- this.stackMem -= poppedValue.length;
1071
- }
1072
- clearPoppedValue();
1073
- this.stack.push(bufHash);
1074
- this.stackMem += bufHash.length;
906
+ buf = this.popStack();
907
+ let hashResult = []; // Initialize to empty, to satisfy TS compiler
908
+ if (currentOpcode === OP_js_1.default.OP_RIPEMD160)
909
+ hashResult = Hash.ripemd160(buf);
910
+ else if (currentOpcode === OP_js_1.default.OP_SHA1)
911
+ hashResult = Hash.sha1(buf);
912
+ else if (currentOpcode === OP_js_1.default.OP_SHA256)
913
+ hashResult = Hash.sha256(buf);
914
+ else if (currentOpcode === OP_js_1.default.OP_HASH160)
915
+ hashResult = Hash.hash160(buf);
916
+ else if (currentOpcode === OP_js_1.default.OP_HASH256)
917
+ hashResult = Hash.hash256(buf);
918
+ this.pushStack(hashResult);
1075
919
  break;
1076
920
  }
1077
921
  case OP_js_1.default.OP_CODESEPARATOR:
1078
922
  this.lastCodeSeparator = this.programCounter;
1079
923
  break;
1080
924
  case OP_js_1.default.OP_CHECKSIG:
1081
- case OP_js_1.default.OP_CHECKSIGVERIFY:
1082
- if (this.stack.length < 2) {
925
+ case OP_js_1.default.OP_CHECKSIGVERIFY: {
926
+ if (this.stack.length < 2)
1083
927
  this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} requires at least two items to be on the stack.`);
1084
- }
1085
- bufSig = this.stacktop(-2);
1086
- bufPubkey = this.stacktop(-1);
1087
- if (!checkSignatureEncoding(bufSig) ||
1088
- !checkPublicKeyEncoding(bufPubkey)) {
1089
- this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} requires correct encoding for the public key and signature.`);
1090
- }
1091
- // Subset of script starting at the most recent codeseparator
1092
- // CScript scriptCode(pbegincodehash, pend);
1093
- if (this.context === 'UnlockingScript') {
1094
- subscript = new Script_js_1.default(this.unlockingScript.chunks.slice((_a = this.lastCodeSeparator) !== null && _a !== void 0 ? _a : 0));
1095
- }
1096
- else {
1097
- subscript = new Script_js_1.default(this.lockingScript.chunks.slice((_b = this.lastCodeSeparator) !== null && _b !== void 0 ? _b : 0));
1098
- }
1099
- // Drop the signature, since there's no way for a signature to sign itself
928
+ bufPubkey = this.popStack();
929
+ bufSig = this.popStack();
930
+ if (!this.checkSignatureEncoding(bufSig) || !this.checkPublicKeyEncoding(bufPubkey)) {
931
+ // Error already thrown by helpers
932
+ this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} requires correct encoding for the public key and signature.`); // Fallback, should be unreachable
933
+ }
934
+ const scriptForChecksig = this.context === 'UnlockingScript' ? this.unlockingScript : this.lockingScript;
935
+ const scriptCodeChunks = scriptForChecksig.chunks.slice(this.lastCodeSeparator === null ? 0 : this.lastCodeSeparator + 1);
936
+ subscript = new Script_js_1.default(scriptCodeChunks);
1100
937
  subscript.findAndDelete(new Script_js_1.default().writeBin(bufSig));
1101
- try {
1102
- sig = TransactionSignature_js_1.default.fromChecksigFormat(bufSig);
1103
- pubkey = PublicKey_js_1.default.fromDER(bufPubkey);
1104
- fSuccess = verifySignature(sig, pubkey, subscript);
1105
- }
1106
- catch (e) {
1107
- // invalid sig or pubkey
1108
- fSuccess = false;
1109
- }
1110
- if (!fSuccess && bufSig.length > 0) {
1111
- this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} failed to verify the signature, and requires an empty signature when verification fails.`);
1112
- }
1113
- poppedValue = this.stack.pop();
1114
- if (poppedValue != null) {
1115
- this.stackMem -= poppedValue.length;
1116
- }
1117
- clearPoppedValue();
1118
- poppedValue = this.stack.pop();
1119
- if (poppedValue != null) {
1120
- this.stackMem -= poppedValue.length;
938
+ fSuccess = false;
939
+ if (bufSig.length > 0) {
940
+ try {
941
+ sig = TransactionSignature_js_1.default.fromChecksigFormat(bufSig);
942
+ pubkey = PublicKey_js_1.default.fromDER(bufPubkey);
943
+ fSuccess = this.verifySignature(sig, pubkey, subscript);
944
+ }
945
+ catch (e) {
946
+ fSuccess = false;
947
+ }
1121
948
  }
1122
- clearPoppedValue();
1123
- // stack.push_back(fSuccess ? vchTrue : vchFalse);
1124
- this.stack.push(fSuccess ? [1] : []);
1125
- this.stackMem += (fSuccess ? 1 : 0);
949
+ this.pushStack(fSuccess ? [1] : []);
1126
950
  if (currentOpcode === OP_js_1.default.OP_CHECKSIGVERIFY) {
1127
- if (fSuccess) {
1128
- poppedValue = this.stack.pop();
1129
- if (poppedValue != null) {
1130
- this.stackMem -= poppedValue.length;
1131
- }
1132
- clearPoppedValue();
1133
- }
1134
- else {
951
+ if (!fSuccess)
1135
952
  this.scriptEvaluationError('OP_CHECKSIGVERIFY requires that a valid signature is provided.');
1136
- }
953
+ this.popStack();
1137
954
  }
1138
955
  break;
956
+ }
1139
957
  case OP_js_1.default.OP_CHECKMULTISIG:
1140
- case OP_js_1.default.OP_CHECKMULTISIGVERIFY:
958
+ case OP_js_1.default.OP_CHECKMULTISIGVERIFY: {
1141
959
  i = 1;
1142
960
  if (this.stack.length < i) {
1143
- this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} requires at least 1 item to be on the stack.`);
961
+ this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} requires at least 1 item for nKeys.`);
1144
962
  }
1145
- nKeysCount = BigNumber_js_1.default.fromScriptNum(this.stacktop(-i), requireMinimalPush).toNumber();
1146
- // TODO: Keys and opcount are parameterized in client. No magic numbers!
963
+ nKeysCount = BigNumber_js_1.default.fromScriptNum(this.stackTop(-i), requireMinimalPush).toNumber();
1147
964
  if (nKeysCount < 0 || nKeysCount > maxMultisigKeyCount) {
1148
965
  this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} requires a key count between 0 and ${maxMultisigKeyCount}.`);
1149
966
  }
1150
967
  ikey = ++i;
1151
968
  i += nKeysCount;
1152
- // ikey2 is the position of last non-signature item in
1153
- // the stack. Top stack item = 1. With
1154
- // SCRIPT_VERIFY_NULLFAIL, this is used for cleanup if
1155
- // operation fails.
1156
- ikey2 = nKeysCount + 2;
1157
969
  if (this.stack.length < i) {
1158
- this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} requires the number of stack items not to be less than the number of keys used.`);
970
+ this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} stack too small for nKeys and keys. Need ${i}, have ${this.stack.length}.`);
1159
971
  }
1160
- nSigsCount = BigNumber_js_1.default.fromScriptNum(this.stacktop(-i), requireMinimalPush).toNumber();
972
+ nSigsCount = BigNumber_js_1.default.fromScriptNum(this.stackTop(-i), requireMinimalPush).toNumber();
1161
973
  if (nSigsCount < 0 || nSigsCount > nKeysCount) {
1162
974
  this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} requires the number of signatures to be no greater than the number of keys.`);
1163
975
  }
1164
976
  isig = ++i;
1165
977
  i += nSigsCount;
1166
978
  if (this.stack.length < i) {
1167
- this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} requires the number of stack items not to be less than the number of signatures provided.`);
979
+ this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} stack too small for N, keys, M, sigs, and dummy. Need ${i}, have ${this.stack.length}.`);
1168
980
  }
1169
- // Subset of script starting at the most recent codeseparator
1170
- if (this.context === 'UnlockingScript') {
1171
- subscript = new Script_js_1.default(this.unlockingScript.chunks.slice((_c = this.lastCodeSeparator) !== null && _c !== void 0 ? _c : 0));
1172
- }
1173
- else {
1174
- subscript = new Script_js_1.default(this.lockingScript.chunks.slice((_d = this.lastCodeSeparator) !== null && _d !== void 0 ? _d : 0));
1175
- }
1176
- // Drop the signatures, since there's no way for a signature to sign itself
981
+ const baseScriptCMS = this.context === 'UnlockingScript' ? this.unlockingScript : this.lockingScript;
982
+ const subscriptChunksCMS = baseScriptCMS.chunks.slice(this.lastCodeSeparator === null ? 0 : this.lastCodeSeparator + 1);
983
+ subscript = new Script_js_1.default(subscriptChunksCMS);
1177
984
  for (let k = 0; k < nSigsCount; k++) {
1178
- bufSig = this.stacktop(-isig - k);
985
+ bufSig = this.stackTop(-isig - k); // Sigs are closer to top than keys
1179
986
  subscript.findAndDelete(new Script_js_1.default().writeBin(bufSig));
1180
987
  }
1181
988
  fSuccess = true;
1182
989
  while (fSuccess && nSigsCount > 0) {
1183
- // valtype& vchSig = this.stacktop(-isig);
1184
- bufSig = this.stacktop(-isig);
1185
- // valtype& vchPubKey = this.stacktop(-ikey);
1186
- bufPubkey = this.stacktop(-ikey);
1187
- if (!checkSignatureEncoding(bufSig) ||
1188
- !checkPublicKeyEncoding(bufPubkey)) {
1189
- this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} requires correct encoding for the public key and signature.`);
990
+ if (nKeysCount === 0) { // No more keys to check against but still sigs left
991
+ fSuccess = false;
992
+ break;
1190
993
  }
1191
- try {
1192
- sig = TransactionSignature_js_1.default.fromChecksigFormat(bufSig);
1193
- pubkey = PublicKey_js_1.default.fromString((0, utils_js_1.toHex)(bufPubkey));
1194
- fOk = verifySignature(sig, pubkey, subscript);
994
+ bufSig = this.stackTop(-isig);
995
+ bufPubkey = this.stackTop(-ikey);
996
+ if (!this.checkSignatureEncoding(bufSig) || !this.checkPublicKeyEncoding(bufPubkey)) {
997
+ this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} requires correct encoding for the public key and signature.`);
1195
998
  }
1196
- catch (e) {
1197
- // invalid sig or pubkey
1198
- fOk = false;
999
+ fOk = false;
1000
+ if (bufSig.length > 0) {
1001
+ try {
1002
+ sig = TransactionSignature_js_1.default.fromChecksigFormat(bufSig);
1003
+ pubkey = PublicKey_js_1.default.fromDER(bufPubkey);
1004
+ fOk = this.verifySignature(sig, pubkey, subscript);
1005
+ }
1006
+ catch (e) {
1007
+ fOk = false;
1008
+ }
1199
1009
  }
1200
1010
  if (fOk) {
1201
1011
  isig++;
@@ -1203,163 +1013,109 @@ class Spend {
1203
1013
  }
1204
1014
  ikey++;
1205
1015
  nKeysCount--;
1206
- // If there are more signatures left than keys left,
1207
- // then too many signatures have failed
1208
1016
  if (nSigsCount > nKeysCount) {
1209
1017
  fSuccess = false;
1210
1018
  }
1211
1019
  }
1212
- // Clean up stack of actual arguments
1213
- while (i-- > 1) {
1214
- if (!fSuccess && ikey2 === 0 && this.stacktop(-1).length > 0) {
1215
- this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} failed to verify a signature, and requires an empty signature when verification fails.`);
1216
- }
1217
- if (ikey2 > 0) {
1218
- ikey2--;
1219
- }
1220
- poppedValue = this.stack.pop();
1221
- if (poppedValue != null) {
1222
- this.stackMem -= poppedValue.length;
1223
- }
1224
- clearPoppedValue();
1225
- }
1226
- // A bug causes CHECKMULTISIG to consume one extra argument
1227
- // whose contents were not checked in any way.
1228
- //
1229
- // Unfortunately this is a potential source of mutability,
1230
- // so optionally verify it is exactly equal to zero prior
1231
- // to removing it from the stack.
1020
+ // Correct total items consumed by op (N_val, keys, M_val, sigs, dummy)
1021
+ const itemsConsumedByOp = 1 + // N_val
1022
+ BigNumber_js_1.default.fromScriptNum(this.stackTop(-1), false).toNumber() + // keys
1023
+ 1 + // M_val
1024
+ BigNumber_js_1.default.fromScriptNum(this.stackTop(-(1 + BigNumber_js_1.default.fromScriptNum(this.stackTop(-1), false).toNumber() + 1)), false).toNumber() + // sigs
1025
+ 1; // dummy
1026
+ let popCount = itemsConsumedByOp - 1; // Pop all except dummy
1027
+ while (popCount > 0) {
1028
+ this.popStack();
1029
+ popCount--;
1030
+ }
1031
+ // Check and pop dummy
1232
1032
  if (this.stack.length < 1) {
1233
- this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} requires an extra item to be on the stack.`);
1033
+ this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} requires an extra item (dummy) to be on the stack.`);
1234
1034
  }
1235
- if (this.stacktop(-1).length > 0) {
1236
- // NOTE: Is this necessary? We don't care about malleability.
1237
- this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} requires the extra stack item to be empty.`);
1035
+ const dummyBuf = this.popStack();
1036
+ if (dummyBuf.length > 0) { // SCRIPT_VERIFY_NULLDUMMY
1037
+ this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} requires the extra stack item (dummy) to be empty.`);
1238
1038
  }
1239
- poppedValue = this.stack.pop();
1240
- if (poppedValue != null) {
1241
- this.stackMem -= poppedValue.length;
1242
- }
1243
- clearPoppedValue();
1244
- this.stack.push(fSuccess ? [1] : []);
1245
- this.stackMem += (fSuccess ? 1 : 0);
1039
+ this.pushStack(fSuccess ? [1] : []);
1246
1040
  if (currentOpcode === OP_js_1.default.OP_CHECKMULTISIGVERIFY) {
1247
- if (fSuccess) {
1248
- poppedValue = this.stack.pop();
1249
- if (poppedValue != null) {
1250
- this.stackMem -= poppedValue.length;
1251
- }
1252
- clearPoppedValue();
1253
- }
1254
- else {
1041
+ if (!fSuccess)
1255
1042
  this.scriptEvaluationError('OP_CHECKMULTISIGVERIFY requires that a sufficient number of valid signatures are provided.');
1256
- }
1043
+ this.popStack();
1257
1044
  }
1258
1045
  break;
1259
- case OP_js_1.default.OP_CAT:
1260
- if (this.stack.length < 2) {
1046
+ }
1047
+ case OP_js_1.default.OP_CAT: {
1048
+ if (this.stack.length < 2)
1261
1049
  this.scriptEvaluationError('OP_CAT requires at least two items to be on the stack.');
1262
- }
1263
- buf1 = this.stacktop(-2);
1264
- buf2 = this.stacktop(-1);
1265
- if (buf1.length + buf2.length > maxScriptElementSize) {
1050
+ buf2 = this.popStack();
1051
+ buf1 = this.popStack();
1052
+ const catResult = (buf1).concat(buf2);
1053
+ if (catResult.length > maxScriptElementSize)
1266
1054
  this.scriptEvaluationError(`It's not currently possible to push data larger than ${maxScriptElementSize} bytes.`);
1267
- }
1268
- this.stack[this.stack.length - 2] = [...buf1, ...buf2];
1269
- this.stackMem -= buf1.length;
1270
- this.stackMem -= buf2.length;
1271
- this.stackMem += buf1.length + buf2.length;
1272
- poppedValue = this.stack.pop();
1273
- if (poppedValue != null) {
1274
- this.stackMem -= poppedValue.length;
1275
- }
1276
- clearPoppedValue();
1055
+ this.pushStack(catResult);
1277
1056
  break;
1278
- case OP_js_1.default.OP_SPLIT:
1279
- if (this.stack.length < 2) {
1057
+ }
1058
+ case OP_js_1.default.OP_SPLIT: {
1059
+ if (this.stack.length < 2)
1280
1060
  this.scriptEvaluationError('OP_SPLIT requires at least two items to be on the stack.');
1281
- }
1282
- buf1 = this.stacktop(-2);
1283
- // Make sure the split point is apropriate.
1284
- n = BigNumber_js_1.default.fromScriptNum(this.stacktop(-1), requireMinimalPush).toNumber();
1285
- if (n < 0 || n > buf1.length) {
1061
+ const posBuf = this.popStack();
1062
+ const dataToSplit = this.popStack();
1063
+ n = BigNumber_js_1.default.fromScriptNum(posBuf, requireMinimalPush).toNumber();
1064
+ if (n < 0 || n > dataToSplit.length) {
1286
1065
  this.scriptEvaluationError('OP_SPLIT requires the first stack item to be a non-negative number less than or equal to the size of the second-from-top stack item.');
1287
1066
  }
1288
- // Prepare the results in their own buffer as `data`
1289
- // will be invalidated.
1290
- // Copy buffer data, to slice it before
1291
- buf2 = [...buf1];
1292
- // Replace existing stack values by the new values.
1293
- this.stack[this.stack.length - 2] = buf2.slice(0, n);
1294
- this.stackMem -= buf1.length;
1295
- this.stackMem += n;
1296
- this.stack[this.stack.length - 1] = buf2.slice(n);
1297
- this.stackMem += buf1.length - n;
1067
+ this.pushStack(dataToSplit.slice(0, n));
1068
+ this.pushStack(dataToSplit.slice(n));
1298
1069
  break;
1299
- case OP_js_1.default.OP_NUM2BIN:
1300
- if (this.stack.length < 2) {
1070
+ }
1071
+ case OP_js_1.default.OP_NUM2BIN: {
1072
+ if (this.stack.length < 2)
1301
1073
  this.scriptEvaluationError('OP_NUM2BIN requires at least two items to be on the stack.');
1074
+ size = BigNumber_js_1.default.fromScriptNum(this.popStack(), requireMinimalPush).toNumber();
1075
+ if (size > maxScriptElementSize || size < 0) { // size can be 0
1076
+ this.scriptEvaluationError(`It's not currently possible to push data larger than ${maxScriptElementSize} bytes or negative size.`);
1302
1077
  }
1303
- size = BigNumber_js_1.default.fromScriptNum(this.stacktop(-1), requireMinimalPush).toNumber();
1304
- if (size > maxScriptElementSize) {
1305
- this.scriptEvaluationError(`It's not currently possible to push data larger than ${maxScriptElementSize} bytes.`);
1306
- }
1307
- poppedValue = this.stack.pop();
1308
- if (poppedValue != null) {
1309
- this.stackMem -= poppedValue.length;
1310
- }
1311
- clearPoppedValue();
1312
- rawnum = this.stacktop(-1);
1313
- // Try to see if we can fit that number in the number of
1314
- // byte requested.
1315
- rawnum = (0, utils_js_1.minimallyEncode)(rawnum);
1078
+ let rawnum = this.popStack(); // This is the number to convert
1079
+ rawnum = (0, utils_js_1.minimallyEncode)(rawnum); // Get its minimal scriptnum form
1316
1080
  if (rawnum.length > size) {
1317
1081
  this.scriptEvaluationError('OP_NUM2BIN requires that the size expressed in the top stack item is large enough to hold the value expressed in the second-from-top stack item.');
1318
1082
  }
1319
- // We already have an element of the right size, we
1320
- // don't need to do anything.
1321
1083
  if (rawnum.length === size) {
1322
- this.stack[this.stack.length - 1] = rawnum;
1084
+ this.pushStack(rawnum);
1323
1085
  break;
1324
1086
  }
1325
- signbit = 0x00;
1087
+ const resultN2B = new Array(size).fill(0x00);
1088
+ let signbit = 0x00;
1326
1089
  if (rawnum.length > 0) {
1327
- signbit = rawnum[rawnum.length - 1] & 0x80;
1328
- rawnum[rawnum.length - 1] &= 0x7f;
1090
+ signbit = rawnum[rawnum.length - 1] & 0x80; // Store sign bit
1091
+ rawnum[rawnum.length - 1] &= 0x7f; // Remove sign bit for padding
1329
1092
  }
1330
- num = new Array(size);
1331
- num.fill(0);
1332
- for (n = 0; n < size; n++) {
1333
- num[n] = rawnum[n];
1093
+ // Copy rawnum (now positive magnitude) into the result
1094
+ for (let k = 0; k < rawnum.length; k++) {
1095
+ resultN2B[k] = rawnum[k];
1334
1096
  }
1335
- n = rawnum.length - 1;
1336
- while (n++ < size - 2) {
1337
- num[n] = 0x00;
1097
+ // If the original number was negative, the sign bit must be set on the new MSB
1098
+ if (signbit !== 0) {
1099
+ resultN2B[size - 1] |= 0x80;
1338
1100
  }
1339
- num[n] = signbit;
1340
- this.stack[this.stack.length - 1] = num;
1341
- this.stackMem -= rawnum.length;
1342
- this.stackMem += size;
1101
+ this.pushStack(resultN2B);
1343
1102
  break;
1344
- case OP_js_1.default.OP_BIN2NUM:
1345
- if (this.stack.length < 1) {
1103
+ }
1104
+ case OP_js_1.default.OP_BIN2NUM: {
1105
+ if (this.stack.length < 1)
1346
1106
  this.scriptEvaluationError('OP_BIN2NUM requires at least one item to be on the stack.');
1347
- }
1348
- buf1 = this.stacktop(-1);
1349
- buf2 = (0, utils_js_1.minimallyEncode)(buf1);
1350
- this.stack[this.stack.length - 1] = buf2;
1351
- this.stackMem -= buf1.length;
1352
- this.stackMem += buf2.length;
1353
- // The resulting number must be a valid number.
1354
- if (!isMinimallyEncoded(buf2)) {
1107
+ buf1 = this.popStack();
1108
+ const b2nResult = (0, utils_js_1.minimallyEncode)(buf1);
1109
+ if (!isMinimallyEncodedHelper(b2nResult)) {
1355
1110
  this.scriptEvaluationError('OP_BIN2NUM requires that the resulting number is valid.');
1356
1111
  }
1112
+ this.pushStack(b2nResult);
1357
1113
  break;
1114
+ }
1358
1115
  default:
1359
- this.scriptEvaluationError('Invalid opcode!');
1116
+ this.scriptEvaluationError(`Invalid opcode ${currentOpcode} (pc=${this.programCounter}).`);
1360
1117
  }
1361
1118
  }
1362
- // Finally, increment the program counter
1363
1119
  this.programCounter++;
1364
1120
  return true;
1365
1121
  }
@@ -1385,35 +1141,39 @@ class Spend {
1385
1141
  }
1386
1142
  }
1387
1143
  if (this.ifStack.length > 0) {
1388
- this.scriptEvaluationError('Every OP_IF must be terminated prior to the end of the script.');
1144
+ this.scriptEvaluationError('Every OP_IF, OP_NOTIF, or OP_ELSE must be terminated with OP_ENDIF prior to the end of the script.');
1389
1145
  }
1390
1146
  if (requireCleanStack) {
1391
1147
  if (this.stack.length !== 1) {
1392
- this.scriptEvaluationError('The clean stack rule requires exactly one item to be on the stack after script execution.');
1148
+ this.scriptEvaluationError(`The clean stack rule requires exactly one item to be on the stack after script execution, found ${this.stack.length}.`);
1393
1149
  }
1394
1150
  }
1395
- if (!this.castToBool(this.stacktop(-1))) {
1151
+ if (this.stack.length === 0) {
1152
+ this.scriptEvaluationError('The top stack element must be truthy after script evaluation (stack is empty).');
1153
+ }
1154
+ else if (!this.castToBool(this.stackTop())) {
1396
1155
  this.scriptEvaluationError('The top stack element must be truthy after script evaluation.');
1397
1156
  }
1398
1157
  return true;
1399
1158
  }
1400
- stacktop(i) {
1401
- return this.stack[this.stack.length + i];
1402
- }
1403
1159
  castToBool(val) {
1160
+ if (val.length === 0)
1161
+ return false;
1404
1162
  for (let i = 0; i < val.length; i++) {
1405
1163
  if (val[i] !== 0) {
1406
- // can be negative zero
1407
- if (i === val.length - 1 && val[i] === 0x80) {
1408
- return false;
1409
- }
1410
- return true;
1164
+ return !(i === val.length - 1 && val[i] === 0x80);
1411
1165
  }
1412
1166
  }
1413
1167
  return false;
1414
1168
  }
1415
1169
  scriptEvaluationError(str) {
1416
- throw new Error(`Script evaluation error: ${str}\n\nSource TXID: ${this.sourceTXID}\nSource output index: ${this.sourceOutputIndex}\nContext: ${this.context}\nProgram counter: ${this.programCounter}\nStack size: ${this.stack.length}\nAlt stack size: ${this.altStack.length}`);
1170
+ const pcInfo = `Context: ${this.context}, PC: ${this.programCounter}`;
1171
+ const stackHex = this.stack.map(s => (s != null && typeof s.length !== 'undefined') ? (0, utils_js_1.toHex)(s) : (s === null || s === undefined ? 'null/undef' : 'INVALID_STACK_ITEM')).join(', ');
1172
+ const altStackHex = this.altStack.map(s => (s != null && typeof s.length !== 'undefined') ? (0, utils_js_1.toHex)(s) : (s === null || s === undefined ? 'null/undef' : 'INVALID_STACK_ITEM')).join(', ');
1173
+ const stackInfo = `Stack: [${stackHex}] (len: ${this.stack.length}, mem: ${this.stackMem})`;
1174
+ const altStackInfo = `AltStack: [${altStackHex}] (len: ${this.altStack.length}, mem: ${this.altStackMem})`;
1175
+ const ifStackInfo = `IfStack: [${this.ifStack.join(', ')}]`;
1176
+ throw new Error(`Script evaluation error: ${str}\nTXID: ${this.sourceTXID}, OutputIdx: ${this.sourceOutputIndex}\n${pcInfo}\n${stackInfo}\n${altStackInfo}\n${ifStackInfo}`);
1417
1177
  }
1418
1178
  }
1419
1179
  exports.default = Spend;