@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.
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/src/primitives/AESGCM.js +113 -137
- package/dist/cjs/src/primitives/AESGCM.js.map +1 -1
- package/dist/cjs/src/primitives/BigNumber.js +1019 -3947
- package/dist/cjs/src/primitives/BigNumber.js.map +1 -1
- package/dist/cjs/src/primitives/K256.js +53 -37
- package/dist/cjs/src/primitives/K256.js.map +1 -1
- package/dist/cjs/src/primitives/Mersenne.js +16 -21
- package/dist/cjs/src/primitives/Mersenne.js.map +1 -1
- package/dist/cjs/src/primitives/MontgomoryMethod.js.map +1 -1
- package/dist/cjs/src/primitives/utils.js +27 -17
- package/dist/cjs/src/primitives/utils.js.map +1 -1
- package/dist/cjs/src/script/Spend.js +618 -858
- package/dist/cjs/src/script/Spend.js.map +1 -1
- package/dist/cjs/src/transaction/Beef.js +17 -0
- package/dist/cjs/src/transaction/Beef.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/primitives/AESGCM.js +112 -137
- package/dist/esm/src/primitives/AESGCM.js.map +1 -1
- package/dist/esm/src/primitives/BigNumber.js +1011 -3969
- package/dist/esm/src/primitives/BigNumber.js.map +1 -1
- package/dist/esm/src/primitives/K256.js +53 -37
- package/dist/esm/src/primitives/K256.js.map +1 -1
- package/dist/esm/src/primitives/Mersenne.js +16 -21
- package/dist/esm/src/primitives/Mersenne.js.map +1 -1
- package/dist/esm/src/primitives/MontgomoryMethod.js.map +1 -1
- package/dist/esm/src/primitives/utils.js +29 -17
- package/dist/esm/src/primitives/utils.js.map +1 -1
- package/dist/esm/src/script/Spend.js +618 -857
- package/dist/esm/src/script/Spend.js.map +1 -1
- package/dist/esm/src/transaction/Beef.js +17 -0
- package/dist/esm/src/transaction/Beef.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/primitives/AESGCM.d.ts.map +1 -1
- package/dist/types/src/primitives/BigNumber.d.ts +238 -1705
- package/dist/types/src/primitives/BigNumber.d.ts.map +1 -1
- package/dist/types/src/primitives/K256.d.ts.map +1 -1
- package/dist/types/src/primitives/Mersenne.d.ts +2 -2
- package/dist/types/src/primitives/Mersenne.d.ts.map +1 -1
- package/dist/types/src/primitives/utils.d.ts +2 -0
- package/dist/types/src/primitives/utils.d.ts.map +1 -1
- package/dist/types/src/script/Spend.d.ts +11 -1
- package/dist/types/src/script/Spend.d.ts.map +1 -1
- package/dist/types/src/transaction/Beef.d.ts +21 -1
- package/dist/types/src/transaction/Beef.d.ts.map +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/dist/umd/bundle.js +1 -1
- package/docs/performance.md +70 -0
- package/docs/primitives.md +262 -3049
- package/docs/transaction.md +34 -0
- package/docs/wallet.md +1 -1
- package/package.json +1 -1
- package/src/auth/__tests/Peer.test.ts +38 -23
- package/src/auth/certificates/__tests/MasterCertificate.test.ts +27 -20
- package/src/auth/certificates/__tests/VerifiableCertificate.test.ts +24 -24
- package/src/primitives/AESGCM.ts +118 -164
- package/src/primitives/BigNumber.ts +867 -4180
- package/src/primitives/K256.ts +57 -37
- package/src/primitives/Mersenne.ts +16 -20
- package/src/primitives/MontgomoryMethod.ts +2 -2
- package/src/primitives/__tests/ReductionContext.test.ts +6 -1
- package/src/primitives/utils.ts +28 -17
- package/src/script/Spend.ts +634 -1309
- package/src/transaction/Beef.ts +18 -1
- package/src/transaction/__tests/Transaction.test.ts +14 -16
- 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 :
|
|
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
|
-
|
|
126
|
-
|
|
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
|
-
|
|
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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
-
|
|
153
|
-
|
|
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
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
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
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
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
|
-
|
|
343
|
-
|
|
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
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
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(`
|
|
365
|
+
this.scriptEvaluationError(`Missing opcode in ${this.context} at pc=${this.programCounter}.`); // Error thrown
|
|
372
366
|
}
|
|
373
|
-
if (Array.isArray(operation.data) &&
|
|
374
|
-
|
|
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
|
-
|
|
378
|
-
|
|
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
|
-
|
|
382
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
534
|
-
|
|
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
|
-
|
|
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
|
-
|
|
597
|
-
|
|
598
|
-
this.
|
|
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
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
this.
|
|
611
|
-
this.
|
|
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
|
-
|
|
622
|
-
|
|
623
|
-
this.
|
|
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
|
-
|
|
633
|
-
this.
|
|
634
|
-
|
|
635
|
-
this.
|
|
636
|
-
|
|
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
|
-
|
|
639
|
-
|
|
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
|
-
|
|
643
|
-
this.
|
|
644
|
-
|
|
645
|
-
this.
|
|
646
|
-
this.
|
|
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
|
-
|
|
653
|
-
|
|
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
|
-
|
|
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.
|
|
686
|
-
this.
|
|
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
|
-
|
|
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 -
|
|
714
|
-
this.stackMem -=
|
|
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
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
this.
|
|
727
|
-
this.
|
|
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.
|
|
735
|
-
|
|
736
|
-
this.
|
|
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
|
-
|
|
744
|
-
|
|
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
|
|
759
|
-
|
|
760
|
-
buf1 = this.
|
|
761
|
-
|
|
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
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
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
|
-
|
|
790
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
806
|
-
|
|
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.
|
|
846
|
-
|
|
847
|
-
|
|
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 (
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
787
|
+
bn = new BigNumber_js_1.default(bn.cmpn(0) !== 0 ? 1 : 0);
|
|
904
788
|
break;
|
|
905
789
|
}
|
|
906
|
-
|
|
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
|
-
|
|
934
|
-
|
|
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
|
-
|
|
830
|
+
resultBnArithmetic = bn1.add(bn2);
|
|
939
831
|
break;
|
|
940
832
|
case OP_js_1.default.OP_SUB:
|
|
941
|
-
|
|
833
|
+
resultBnArithmetic = bn1.sub(bn2);
|
|
942
834
|
break;
|
|
943
835
|
case OP_js_1.default.OP_MUL:
|
|
944
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
876
|
+
resultBnArithmetic = bn1.cmp(bn2) < 0 ? bn1 : bn2;
|
|
987
877
|
break;
|
|
988
878
|
case OP_js_1.default.OP_MAX:
|
|
989
|
-
|
|
879
|
+
resultBnArithmetic = bn1.cmp(bn2) > 0 ? bn1 : bn2;
|
|
990
880
|
break;
|
|
991
881
|
}
|
|
992
|
-
|
|
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.
|
|
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
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
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
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
else if (currentOpcode === OP_js_1.default.
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
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.
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
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
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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]}
|
|
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.
|
|
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]}
|
|
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
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
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.
|
|
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
|
-
//
|
|
1184
|
-
|
|
1185
|
-
|
|
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
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
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
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
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
|
-
//
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
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
|
-
|
|
1236
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1260
|
-
|
|
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.
|
|
1264
|
-
|
|
1265
|
-
if (
|
|
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
|
-
|
|
1279
|
-
|
|
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
|
-
|
|
1283
|
-
|
|
1284
|
-
n
|
|
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
|
-
|
|
1289
|
-
|
|
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
|
-
|
|
1300
|
-
|
|
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
|
-
|
|
1304
|
-
|
|
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.
|
|
1084
|
+
this.pushStack(rawnum);
|
|
1323
1085
|
break;
|
|
1324
1086
|
}
|
|
1325
|
-
|
|
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
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
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
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1345
|
-
|
|
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
|
-
|
|
1349
|
-
|
|
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(
|
|
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(
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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;
|