@bsv/sdk 1.0.0 → 1.0.1

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 (123) hide show
  1. package/README.md +25 -3
  2. package/package.json +9 -5
  3. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
  4. package/.github/ISSUE_TEMPLATE/discussion.md +0 -24
  5. package/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +0 -23
  6. package/CHANGELOG.md +0 -72
  7. package/CONTRIBUTING.md +0 -85
  8. package/ROADMAP.md +0 -3
  9. package/docs/getting-started/COMMONJS.md +0 -94
  10. package/docs/getting-started/REACT-TS.md +0 -131
  11. package/docs/getting-started/TS-NODE.md +0 -106
  12. package/docs/getting-started/VUE.md +0 -103
  13. package/jest.config.js +0 -6
  14. package/mod.ts +0 -8
  15. package/src/compat/BSM.ts +0 -51
  16. package/src/compat/ECIES.ts +0 -557
  17. package/src/compat/HD.ts +0 -348
  18. package/src/compat/Mnemonic.ts +0 -295
  19. package/src/compat/__tests/BSM.test.ts +0 -38
  20. package/src/compat/__tests/ECIES.test.ts +0 -90
  21. package/src/compat/__tests/HD.test.ts +0 -405
  22. package/src/compat/__tests/Mnemonic.test.ts +0 -177
  23. package/src/compat/__tests/Mnemonic.vectors.ts +0 -172
  24. package/src/compat/bip-39-wordlist-en.ts +0 -2053
  25. package/src/compat/index.ts +0 -4
  26. package/src/messages/EncryptedMessage.ts +0 -70
  27. package/src/messages/SignedMessage.ts +0 -87
  28. package/src/messages/__tests/EncryptedMessage.test.ts +0 -36
  29. package/src/messages/__tests/SignedMessage.test.ts +0 -53
  30. package/src/messages/index.ts +0 -2
  31. package/src/primitives/AESGCM.ts +0 -479
  32. package/src/primitives/BasePoint.ts +0 -21
  33. package/src/primitives/BigNumber.ts +0 -4619
  34. package/src/primitives/Curve.ts +0 -1163
  35. package/src/primitives/DRBG.ts +0 -102
  36. package/src/primitives/ECDSA.ts +0 -164
  37. package/src/primitives/Hash.ts +0 -1420
  38. package/src/primitives/JacobianPoint.ts +0 -410
  39. package/src/primitives/K256.ts +0 -116
  40. package/src/primitives/Mersenne.ts +0 -123
  41. package/src/primitives/MontgomoryMethod.ts +0 -160
  42. package/src/primitives/Point.ts +0 -852
  43. package/src/primitives/PrivateKey.ts +0 -195
  44. package/src/primitives/PublicKey.ts +0 -154
  45. package/src/primitives/Random.ts +0 -55
  46. package/src/primitives/ReductionContext.ts +0 -528
  47. package/src/primitives/Signature.ts +0 -235
  48. package/src/primitives/SymmetricKey.ts +0 -75
  49. package/src/primitives/TransactionSignature.ts +0 -189
  50. package/src/primitives/__tests/AESGCM.test.ts +0 -338
  51. package/src/primitives/__tests/BRC42.private.vectors.ts +0 -33
  52. package/src/primitives/__tests/BRC42.public.vectors.ts +0 -33
  53. package/src/primitives/__tests/BigNumber.arithmatic.test.ts +0 -572
  54. package/src/primitives/__tests/BigNumber.binary.test.ts +0 -203
  55. package/src/primitives/__tests/BigNumber.constructor.test.ts +0 -176
  56. package/src/primitives/__tests/BigNumber.dhGroup.test.ts +0 -18
  57. package/src/primitives/__tests/BigNumber.fixtures.ts +0 -264
  58. package/src/primitives/__tests/BigNumber.serializers.test.ts +0 -157
  59. package/src/primitives/__tests/BigNumber.utils.test.ts +0 -347
  60. package/src/primitives/__tests/Curve.unit.test.ts +0 -192
  61. package/src/primitives/__tests/DRBG.test.ts +0 -18
  62. package/src/primitives/__tests/DRBG.vectors.ts +0 -167
  63. package/src/primitives/__tests/ECDH.test.ts +0 -31
  64. package/src/primitives/__tests/ECDSA.test.ts +0 -58
  65. package/src/primitives/__tests/HMAC.test.ts +0 -59
  66. package/src/primitives/__tests/Hash.test.ts +0 -121
  67. package/src/primitives/__tests/PBKDF2.vectors.ts +0 -119
  68. package/src/primitives/__tests/PrivateKey.test.ts +0 -17
  69. package/src/primitives/__tests/PublicKey.test.ts +0 -66
  70. package/src/primitives/__tests/Random.test.ts +0 -14
  71. package/src/primitives/__tests/Reader.test.ts +0 -296
  72. package/src/primitives/__tests/ReductionContext.test.ts +0 -279
  73. package/src/primitives/__tests/SymmetricKey.test.ts +0 -58
  74. package/src/primitives/__tests/SymmetricKey.vectors.ts +0 -40
  75. package/src/primitives/__tests/Writer.test.ts +0 -198
  76. package/src/primitives/__tests/sighash.vectors.ts +0 -3503
  77. package/src/primitives/__tests/utils.test.ts +0 -108
  78. package/src/primitives/index.ts +0 -8
  79. package/src/primitives/utils.ts +0 -665
  80. package/src/script/LockingScript.ts +0 -30
  81. package/src/script/OP.ts +0 -219
  82. package/src/script/Script.ts +0 -426
  83. package/src/script/ScriptChunk.ts +0 -7
  84. package/src/script/ScriptTemplate.ts +0 -36
  85. package/src/script/Spend.ts +0 -1379
  86. package/src/script/UnlockingScript.ts +0 -30
  87. package/src/script/__tests/Script.test.ts +0 -369
  88. package/src/script/__tests/Spend.test.ts +0 -248
  89. package/src/script/__tests/script.invalid.vectors.ts +0 -925
  90. package/src/script/__tests/script.valid.vectors.ts +0 -1120
  91. package/src/script/__tests/scriptFromVector.ts +0 -42
  92. package/src/script/__tests/spend.valid.vectors.ts +0 -2288
  93. package/src/script/index.ts +0 -7
  94. package/src/script/templates/P2PKH.ts +0 -109
  95. package/src/script/templates/RPuzzle.ts +0 -140
  96. package/src/script/templates/index.ts +0 -2
  97. package/src/transaction/Broadcaster.ts +0 -42
  98. package/src/transaction/ChainTracker.ts +0 -22
  99. package/src/transaction/FeeModel.ts +0 -13
  100. package/src/transaction/MerklePath.ts +0 -259
  101. package/src/transaction/Transaction.ts +0 -602
  102. package/src/transaction/TransactionInput.ts +0 -63
  103. package/src/transaction/TransactionOutput.ts +0 -37
  104. package/src/transaction/__tests/MerklePath.test.ts +0 -181
  105. package/src/transaction/__tests/Transaction.test.ts +0 -413
  106. package/src/transaction/__tests/bigtx.vectors.ts +0 -4
  107. package/src/transaction/__tests/bump.invalid.vectors.ts +0 -8
  108. package/src/transaction/__tests/bump.valid.vectors.ts +0 -4
  109. package/src/transaction/__tests/tx.invalid.vectors.ts +0 -281
  110. package/src/transaction/__tests/tx.valid.vectors.ts +0 -364
  111. package/src/transaction/broadcasters/ARC.ts +0 -106
  112. package/src/transaction/broadcasters/__tests/ARC.test.ts +0 -115
  113. package/src/transaction/broadcasters/index.ts +0 -1
  114. package/src/transaction/fee-models/SatoshisPerKilobyte.ts +0 -71
  115. package/src/transaction/fee-models/index.ts +0 -1
  116. package/src/transaction/index.ts +0 -6
  117. package/ts2md.json +0 -5
  118. package/tsconfig.base.json +0 -26
  119. package/tsconfig.cjs.json +0 -11
  120. package/tsconfig.eslint.json +0 -12
  121. package/tsconfig.esm.json +0 -9
  122. package/tsconfig.json +0 -17
  123. package/tsconfig.types.json +0 -11
@@ -1,1379 +0,0 @@
1
- import LockingScript from './LockingScript.js'
2
- import UnlockingScript from './UnlockingScript.js'
3
- import Script from './Script.js'
4
- import BigNumber from '../primitives/BigNumber.js'
5
- import OP from './OP.js'
6
- import ScriptChunk from './ScriptChunk.js'
7
- import { toHex } from '../primitives/utils.js'
8
- import * as Hash from '../primitives/Hash.js'
9
- import TransactionSignature from '../primitives/TransactionSignature.js'
10
- import PublicKey from '../primitives/PublicKey.js'
11
- import { verify } from '../primitives/ECDSA.js'
12
- import TransactionInput from '../transaction/TransactionInput.js'
13
- import TransactionOutput from '../transaction/TransactionOutput.js'
14
-
15
- // These constants control the current behavior of the interpreter.
16
- // In the future, all of them will go away.
17
- const maxScriptElementSize = 1024 * 1024 * 1024
18
- const maxMultisigKeyCount = Math.pow(2, 31) - 1
19
- const requireMinimalPush = true
20
- const requirePushOnlyUnlockingScripts = true
21
- const requireLowSSignatures = true
22
- const requireCleanStack = true
23
-
24
- /**
25
- * The Spend class represents a spend action within a Bitcoin SV transaction.
26
- * It encapsulates all the necessary data required for spending a UTXO (Unspent Transaction Output)
27
- * and includes details about the source transaction, output, and the spending transaction itself.
28
- *
29
- * @property {string} sourceTXID - The transaction ID of the source UTXO.
30
- * @property {number} sourceOutputIndex - The index of the output in the source transaction.
31
- * @property {BigNumber} sourceSatoshis - The amount of satoshis in the source UTXO.
32
- * @property {LockingScript} lockingScript - The locking script associated with the UTXO.
33
- * @property {number} transactionVersion - The version of the current transaction.
34
- * @property {Array<{ sourceTXID: string, sourceOutputIndex: number, sequence: number }>} otherInputs -
35
- * An array of other inputs in the transaction, each with a txid, outputIndex, and sequence number.
36
- * @property {Array<{ satoshis: BigNumber, lockingScript: LockingScript }>} outputs -
37
- * An array of outputs of the current transaction, including the satoshi value and locking script for each.
38
- * @property {number} inputIndex - The index of this input in the current transaction.
39
- * @property {UnlockingScript} unlockingScript - The unlocking script that unlocks the UTXO for spending.
40
- * @property {number} inputSequence - The sequence number of this input.
41
- */
42
- export default class Spend {
43
- sourceTXID: string
44
- sourceOutputIndex: number
45
- sourceSatoshis: number
46
- lockingScript: LockingScript
47
- transactionVersion: number
48
- otherInputs: TransactionInput[]
49
- outputs: TransactionOutput[]
50
- inputIndex: number
51
- unlockingScript: UnlockingScript
52
- inputSequence: number
53
- lockTime: number
54
- context: 'UnlockingScript' | 'LockingScript'
55
- programCounter: number
56
- lastCodeSeparator: number | null
57
- stack: number[][]
58
- altStack: number[][]
59
- ifStack: boolean[]
60
-
61
- /**
62
- * @constructor
63
- * Constructs the Spend object with necessary transaction details.
64
- * @param {string} params.sourceTXID - The transaction ID of the source UTXO.
65
- * @param {number} params.sourceOutputIndex - The index of the output in the source transaction.
66
- * @param {BigNumber} params.sourceSatoshis - The amount of satoshis in the source UTXO.
67
- * @param {LockingScript} params.lockingScript - The locking script associated with the UTXO.
68
- * @param {number} params.transactionVersion - The version of the current transaction.
69
- * @param {Array<{ sourceTXID: string, sourceOutputIndex: number, sequence: number }>} params.otherInputs -
70
- * An array of other inputs in the transaction.
71
- * @param {Array<{ satoshis: BigNumber, lockingScript: LockingScript }>} params.outputs -
72
- * The outputs of the current transaction.
73
- * @param {number} params.inputIndex - The index of this input in the current transaction.
74
- * @param {UnlockingScript} params.unlockingScript - The unlocking script for this spend.
75
- * @param {number} params.inputSequence - The sequence number of this input.
76
- * @param {number} params.lockTime - The lock time of the transaction.
77
- *
78
- * @example
79
- * const spend = new Spend({
80
- * sourceTXID: "abcd1234", // sourceTXID
81
- * sourceOutputIndex: 0, // sourceOutputIndex
82
- * sourceSatoshis: new BigNumber(1000), // sourceSatoshis
83
- * lockingScript: LockingScript.fromASM("OP_DUP OP_HASH160 abcd1234... OP_EQUALVERIFY OP_CHECKSIG"),
84
- * transactionVersion: 1, // transactionVersion
85
- * otherInputs: [{ sourceTXID: "abcd1234", sourceOutputIndex: 1, sequence: 0xffffffff }], // otherInputs
86
- * outputs: [{ satoshis: new BigNumber(500), lockingScript: LockingScript.fromASM("OP_DUP...") }], // outputs
87
- * inputIndex: 0, // inputIndex
88
- * unlockingScript: UnlockingScript.fromASM("3045... 02ab..."),
89
- * inputSequence: 0xffffffff // inputSequence
90
- * });
91
- */
92
- constructor(params: {
93
- sourceTXID: string
94
- sourceOutputIndex: number
95
- sourceSatoshis: number
96
- lockingScript: LockingScript
97
- transactionVersion: number
98
- otherInputs: TransactionInput[]
99
- outputs: TransactionOutput[]
100
- unlockingScript: UnlockingScript
101
- inputSequence: number
102
- inputIndex: number
103
- lockTime: number
104
- }) {
105
- this.sourceTXID = params.sourceTXID
106
- this.sourceOutputIndex = params.sourceOutputIndex
107
- this.sourceSatoshis = params.sourceSatoshis
108
- this.lockingScript = params.lockingScript
109
- this.transactionVersion = params.transactionVersion
110
- this.otherInputs = params.otherInputs
111
- this.outputs = params.outputs
112
- this.inputIndex = params.inputIndex
113
- this.unlockingScript = params.unlockingScript
114
- this.inputSequence = params.inputSequence
115
- this.lockTime = params.lockTime
116
- this.reset()
117
- }
118
-
119
- reset(): void {
120
- this.context = 'UnlockingScript'
121
- this.programCounter = 0
122
- this.lastCodeSeparator = null
123
- this.stack = []
124
- this.altStack = []
125
- this.ifStack = []
126
- }
127
-
128
- step(): void {
129
- // If the context is UnlockingScript and we have reached the end,
130
- // set the context to LockingScript and zero the program counter
131
- if (
132
- this.context === 'UnlockingScript' &&
133
- this.programCounter >= this.unlockingScript.chunks.length
134
- ) {
135
- this.context = 'LockingScript'
136
- this.programCounter = 0
137
- }
138
-
139
- let operation: ScriptChunk
140
- if (this.context === 'UnlockingScript') {
141
- operation = this.unlockingScript.chunks[this.programCounter]
142
- } else {
143
- operation = this.lockingScript.chunks[this.programCounter]
144
- }
145
-
146
- const isOpcodeDisabled = (op: number): boolean => {
147
- return op === OP.OP_2MUL ||
148
- op === OP.OP_2DIV ||
149
- op === OP.OP_VERIF ||
150
- op === OP.OP_VERNOTIF ||
151
- op === OP.OP_VER
152
- }
153
-
154
- const isChunkMinimal = (chunk: ScriptChunk): boolean => {
155
- const data = chunk.data
156
- const op = chunk.op
157
- if (!Array.isArray(data)) {
158
- return true
159
- }
160
- if (data.length === 0) {
161
- // Could have used OP_0.
162
- return op === OP.OP_0
163
- } else if (data.length === 1 && data[0] >= 1 && data[0] <= 16) {
164
- // Could have used OP_1 .. OP_16.
165
- return op === OP.OP_1 + (data[0] - 1)
166
- } else if (data.length === 1 && data[0] === 0x81) {
167
- // Could have used OP_1NEGATE.
168
- return op === OP.OP_1NEGATE
169
- } else if (data.length <= 75) {
170
- // Could have used a direct push (opCode indicating number of bytes pushed + those bytes).
171
- return op === data.length
172
- } else if (data.length <= 255) {
173
- // Could have used OP_PUSHDATA.
174
- return op === OP.OP_PUSHDATA1
175
- } else if (data.length <= 65535) {
176
- // Could have used OP_PUSHDATA2.
177
- return op === OP.OP_PUSHDATA2
178
- }
179
- return true
180
- }
181
-
182
- // NOTE: Is 4 still the default maximum number size?
183
- // In Genesis, was it lifted to 750000?
184
- const isMinimallyEncoded = (buf: number[], maxNumSize: number = 4): boolean => {
185
- if (buf.length > maxNumSize) {
186
- return false
187
- }
188
-
189
- if (buf.length > 0) {
190
- // Check that the number is encoded with the minimum possible number
191
- // of bytes.
192
- //
193
- // If the most-significant-byte - excluding the sign bit - is zero
194
- // then we're not minimal. Note how this test also rejects the
195
- // negative-zero encoding, 0x80.
196
- if ((buf[buf.length - 1] & 0x7f) === 0) {
197
- // One exception: if there's more than one byte and the most
198
- // significant bit of the second-most-significant-byte is set it
199
- // would conflict with the sign bit. An example of this case is
200
- // +-255, which encode to 0xff00 and 0xff80 respectively.
201
- // (big-endian).
202
- if (buf.length <= 1 || (buf[buf.length - 2] & 0x80) === 0) {
203
- return false
204
- }
205
- }
206
- }
207
- return true
208
- }
209
-
210
- const minimallyEncode = (buf: number[]): number[] => {
211
- if (buf.length === 0) {
212
- return buf
213
- }
214
-
215
- // If the last byte is not 0x00 or 0x80, we are minimally encoded.
216
- const last = buf[buf.length - 1]
217
- if ((last & 0x7f) !== 0) {
218
- return buf
219
- }
220
-
221
- // If the script is one byte long, then we have a zero, which encodes as an
222
- // empty array.
223
- if (buf.length === 1) {
224
- return []
225
- }
226
-
227
- // If the next byte has it sign bit set, then we are minimaly encoded.
228
- if ((buf[buf.length - 2] & 0x80) !== 0) {
229
- return buf
230
- }
231
-
232
- // We are not minimally encoded, we need to figure out how much to trim.
233
- for (let i = buf.length - 1; i > 0; i--) {
234
- // We found a non zero byte, time to encode.
235
- if (buf[i - 1] !== 0) {
236
- if ((buf[i - 1] & 0x80) !== 0) {
237
- // We found a byte with it sign bit set so we need one more
238
- // byte.
239
- buf[i++] = last
240
- } else {
241
- // the sign bit is clear, we can use it.
242
- buf[i - 1] |= last
243
- }
244
-
245
- return buf.slice(0, i)
246
- }
247
- }
248
-
249
- // If we found the whole thing is zeros, then we have a zero.
250
- return []
251
- }
252
-
253
- const padDataToSize = (buf: number[], len: number): number[] => {
254
- let b = buf
255
- while (b.length < len) {
256
- b = [0x00, ...b]
257
- }
258
- return b
259
- }
260
-
261
- /**
262
- * This function is translated from bitcoind's IsDERSignature and is used in
263
- * the script interpreter. This "DER" format actually includes an extra byte,
264
- * the nHashType, at the end. It is really the tx format, not DER format.
265
- *
266
- * A canonical signature exists of: [30] [total len] [02] [len R] [R] [02] [len S] [S] [hashtype]
267
- * Where R and S are not negative (their first byte has its highest bit not set), and not
268
- * excessively padded (do not start with a 0 byte, unless an otherwise negative number follows,
269
- * in which case a single 0 byte is necessary and even required).
270
- *
271
- * See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623
272
- */
273
- const isChecksigFormat = (buf: number[]): boolean => {
274
- if (buf.length < 9) {
275
- // Non-canonical signature: too short
276
- return false
277
- }
278
- if (buf.length > 73) {
279
- // Non-canonical signature: too long
280
- return false
281
- }
282
- if (buf[0] !== 0x30) {
283
- // Non-canonical signature: wrong type
284
- return false
285
- }
286
- if (buf[1] !== buf.length - 3) {
287
- // Non-canonical signature: wrong length marker
288
- return false
289
- }
290
- const nLEnR = buf[3]
291
- if (5 + nLEnR >= buf.length) {
292
- // Non-canonical signature: S length misplaced
293
- return false
294
- }
295
- const nLEnS = buf[5 + nLEnR]
296
- if (nLEnR + nLEnS + 7 !== buf.length) {
297
- // Non-canonical signature: R+S length mismatch
298
- return false
299
- }
300
-
301
- const R = buf.slice(4)
302
- if (buf[4 - 2] !== 0x02) {
303
- // Non-canonical signature: R value type mismatch
304
- return false
305
- }
306
- if (nLEnR === 0) {
307
- // Non-canonical signature: R length is zero
308
- return false
309
- }
310
- if ((R[0] & 0x80) !== 0) {
311
- // Non-canonical signature: R value negative
312
- return false
313
- }
314
- if (nLEnR > 1 && R[0] === 0x00 && (R[1] & 0x80) === 0) {
315
- // Non-canonical signature: R value excessively padded
316
- return false
317
- }
318
-
319
- const S = buf.slice(6 + nLEnR)
320
- if (buf[6 + nLEnR - 2] !== 0x02) {
321
- // Non-canonical signature: S value type mismatch
322
- return false
323
- }
324
- if (nLEnS === 0) {
325
- // Non-canonical signature: S length is zero
326
- return false
327
- }
328
- if ((S[0] & 0x80) !== 0) {
329
- // Non-canonical signature: S value negative
330
- return false
331
- }
332
- if (nLEnS > 1 && S[0] === 0x00 && (S[1] & 0x80) === 0) {
333
- // Non-canonical signature: S value excessively padded
334
- return false
335
- }
336
- return true
337
- }
338
-
339
- const checkSignatureEncoding = (buf: number[]): boolean => {
340
- // Empty signature. Not strictly DER encoded, but allowed to provide a
341
- // compact way to provide an invalid signature for use with CHECK(MULTI)SIG
342
- if (buf.length === 0) {
343
- return true
344
- }
345
-
346
- if (!isChecksigFormat(buf)) {
347
- this.scriptEvaluationError('The signature format is invalid.')
348
- }
349
- const sig = TransactionSignature.fromChecksigFormat(buf)
350
- if (requireLowSSignatures && !sig.hasLowS()) {
351
- this.scriptEvaluationError('The signature must have a low S value.')
352
- }
353
- if ((sig.scope & TransactionSignature.SIGHASH_FORKID) === 0) {
354
- this.scriptEvaluationError('The signature must use SIGHASH_FORKID.')
355
- return false
356
- }
357
-
358
- return true
359
- }
360
-
361
- const checkPublicKeyEncoding = (buf: number[]): boolean => {
362
- if (buf.length < 33) {
363
- this.scriptEvaluationError('The public key is too short, it must be at least 33 bytes.')
364
- }
365
- if (buf[0] === 0x04) {
366
- if (buf.length !== 65) {
367
- this.scriptEvaluationError('The non-compressed public key must be 65 bytes.')
368
- }
369
- } else if ((buf[0] === 0x02 || buf[0] === 0x03)) {
370
- if (buf.length !== 33) {
371
- this.scriptEvaluationError('The compressed public key must be 33 bytes.')
372
- }
373
- } else {
374
- this.scriptEvaluationError('The public key is in an unknown format.')
375
- }
376
- return true
377
- }
378
-
379
- const verifySignature = (
380
- sig: TransactionSignature,
381
- pubkey: PublicKey,
382
- subscript: Script
383
- ): boolean => {
384
- const preimage = TransactionSignature.format({
385
- sourceTXID: this.sourceTXID,
386
- sourceOutputIndex: this.sourceOutputIndex,
387
- sourceSatoshis: this.sourceSatoshis,
388
- transactionVersion: this.transactionVersion,
389
- otherInputs: this.otherInputs,
390
- outputs: this.outputs,
391
- inputIndex: this.inputIndex,
392
- subscript,
393
- inputSequence: this.inputSequence,
394
- lockTime: this.lockTime,
395
- scope: sig.scope
396
- })
397
- const hash = new BigNumber(Hash.hash256(preimage))
398
- return verify(hash, sig, pubkey)
399
- }
400
-
401
- const isScriptExecuting = !this.ifStack.includes(false)
402
- let buf: number[], buf1: number[], buf2: number[], buf3: number[], spliced: number[][], n: number, size: number, rawnum: number[], num: number[], signbit: number, x1: number[], x2: number[], x3: number[], bn: BigNumber, bn1: BigNumber, bn2: BigNumber, bn3: BigNumber, bufSig: number[], bufPubkey: number[], subscript, bufHash: number[]
403
- let sig, pubkey, i: number, fOk: boolean, nKeysCount: number, ikey: number, ikey2: number, nSigsCount: number, isig: number
404
- let fValue: boolean, fEqual: boolean, fSuccess: boolean
405
-
406
- // Read instruction
407
- const currentOpcode = operation.op
408
- if (typeof currentOpcode === 'undefined') {
409
- this.scriptEvaluationError(`An opcode is missing in this chunk of the ${this.context}!`)
410
- }
411
- if (
412
- Array.isArray(operation.data) &&
413
- operation.data.length > maxScriptElementSize
414
- ) {
415
- this.scriptEvaluationError(`It's not currently possible to push data larger than ${maxScriptElementSize} bytes.`)
416
- }
417
-
418
- if (isScriptExecuting && isOpcodeDisabled(currentOpcode)) {
419
- this.scriptEvaluationError('This opcode is currently disabled.')
420
- }
421
-
422
- if (
423
- isScriptExecuting && currentOpcode >= 0 &&
424
- currentOpcode <= OP.OP_PUSHDATA4
425
- ) {
426
- if (requireMinimalPush && !isChunkMinimal(operation)) {
427
- this.scriptEvaluationError('This data is not minimally-encoded.')
428
- }
429
-
430
- if (!Array.isArray(operation.data)) {
431
- this.stack.push([])
432
- } else {
433
- this.stack.push(operation.data)
434
- }
435
- } else if (isScriptExecuting || (OP.OP_IF <= currentOpcode && currentOpcode <= OP.OP_ENDIF)) {
436
- switch (currentOpcode) {
437
- case OP.OP_1NEGATE:
438
- case OP.OP_1:
439
- case OP.OP_2:
440
- case OP.OP_3:
441
- case OP.OP_4:
442
- case OP.OP_5:
443
- case OP.OP_6:
444
- case OP.OP_7:
445
- case OP.OP_8:
446
- case OP.OP_9:
447
- case OP.OP_10:
448
- case OP.OP_11:
449
- case OP.OP_12:
450
- case OP.OP_13:
451
- case OP.OP_14:
452
- case OP.OP_15:
453
- case OP.OP_16:
454
- n = currentOpcode - (OP.OP_1 - 1)
455
- buf = new BigNumber(n).toScriptNum()
456
- this.stack.push(buf)
457
- break
458
-
459
- case OP.OP_NOP:
460
- case OP.OP_NOP2:
461
- case OP.OP_NOP3:
462
- case OP.OP_NOP1:
463
- case OP.OP_NOP4:
464
- case OP.OP_NOP5:
465
- case OP.OP_NOP6:
466
- case OP.OP_NOP7:
467
- case OP.OP_NOP8:
468
- case OP.OP_NOP9:
469
- case OP.OP_NOP10:
470
- case OP.OP_NOP11:
471
- case OP.OP_NOP12:
472
- case OP.OP_NOP13:
473
- case OP.OP_NOP14:
474
- case OP.OP_NOP15:
475
- case OP.OP_NOP16:
476
- case OP.OP_NOP17:
477
- case OP.OP_NOP18:
478
- case OP.OP_NOP19:
479
- case OP.OP_NOP20:
480
- case OP.OP_NOP21:
481
- case OP.OP_NOP22:
482
- case OP.OP_NOP23:
483
- case OP.OP_NOP24:
484
- case OP.OP_NOP25:
485
- case OP.OP_NOP26:
486
- case OP.OP_NOP27:
487
- case OP.OP_NOP28:
488
- case OP.OP_NOP29:
489
- case OP.OP_NOP30:
490
- case OP.OP_NOP31:
491
- case OP.OP_NOP32:
492
- case OP.OP_NOP33:
493
- case OP.OP_NOP34:
494
- case OP.OP_NOP35:
495
- case OP.OP_NOP36:
496
- case OP.OP_NOP37:
497
- case OP.OP_NOP38:
498
- case OP.OP_NOP39:
499
- case OP.OP_NOP40:
500
- case OP.OP_NOP41:
501
- case OP.OP_NOP42:
502
- case OP.OP_NOP43:
503
- case OP.OP_NOP44:
504
- case OP.OP_NOP45:
505
- case OP.OP_NOP46:
506
- case OP.OP_NOP47:
507
- case OP.OP_NOP48:
508
- case OP.OP_NOP49:
509
- case OP.OP_NOP50:
510
- case OP.OP_NOP51:
511
- case OP.OP_NOP52:
512
- case OP.OP_NOP53:
513
- case OP.OP_NOP54:
514
- case OP.OP_NOP55:
515
- case OP.OP_NOP56:
516
- case OP.OP_NOP57:
517
- case OP.OP_NOP58:
518
- case OP.OP_NOP59:
519
- case OP.OP_NOP60:
520
- case OP.OP_NOP61:
521
- case OP.OP_NOP62:
522
- case OP.OP_NOP63:
523
- case OP.OP_NOP64:
524
- case OP.OP_NOP65:
525
- case OP.OP_NOP66:
526
- case OP.OP_NOP67:
527
- case OP.OP_NOP68:
528
- case OP.OP_NOP69:
529
- case OP.OP_NOP70:
530
- case OP.OP_NOP71:
531
- case OP.OP_NOP72:
532
- case OP.OP_NOP73:
533
- case OP.OP_NOP77:
534
- break
535
-
536
- case OP.OP_IF:
537
- case OP.OP_NOTIF:
538
- fValue = false
539
- if (isScriptExecuting) {
540
- if (this.stack.length < 1) {
541
- this.scriptEvaluationError('OP_IF and OP_NOTIF require at least one item on the stack when they are used!')
542
- }
543
- buf = this.stacktop(-1)
544
-
545
- fValue = this.castToBool(buf)
546
- if (currentOpcode === OP.OP_NOTIF) {
547
- fValue = !fValue
548
- }
549
- this.stack.pop()
550
- }
551
- this.ifStack.push(fValue)
552
- break
553
-
554
- case OP.OP_ELSE:
555
- if (this.ifStack.length === 0) {
556
- this.scriptEvaluationError('OP_ELSE requires a preceeding OP_IF.')
557
- }
558
- this.ifStack[this.ifStack.length - 1] = !this.ifStack[this.ifStack.length - 1]
559
- break
560
-
561
- case OP.OP_ENDIF:
562
- if (this.ifStack.length === 0) {
563
- this.scriptEvaluationError('OP_ENDIF requires a preceeding OP_IF.')
564
- }
565
- this.ifStack.pop()
566
- break
567
-
568
- case OP.OP_VERIFY:
569
- if (this.stack.length < 1) {
570
- this.scriptEvaluationError('OP_VERIFY requires at least one item to be on the stack.')
571
- }
572
- buf = this.stacktop(-1)
573
- fValue = this.castToBool(buf)
574
- if (fValue) {
575
- this.stack.pop()
576
- } else {
577
- this.scriptEvaluationError('OP_VERIFY requires the top stack value to be truthy.')
578
- }
579
- break
580
-
581
- case OP.OP_RETURN:
582
- if (this.context === 'UnlockingScript') {
583
- this.programCounter = this.unlockingScript.chunks.length
584
- } else {
585
- this.programCounter = this.lockingScript.chunks.length
586
- }
587
- break
588
-
589
- case OP.OP_TOALTSTACK:
590
- if (this.stack.length < 1) {
591
- this.scriptEvaluationError('OP_TOALTSTACK requires at oeast one item to be on the stack.')
592
- }
593
- this.altStack.push(this.stack.pop())
594
- break
595
-
596
- case OP.OP_FROMALTSTACK:
597
- if (this.altStack.length < 1) {
598
- this.scriptEvaluationError('OP_FROMALTSTACK requires at least one item to be on the stack.')
599
- }
600
- this.stack.push(this.altStack.pop())
601
- break
602
-
603
- case OP.OP_2DROP:
604
- if (this.stack.length < 2) {
605
- this.scriptEvaluationError('OP_2DROP requires at least two items to be on the stack.')
606
- }
607
- this.stack.pop()
608
- this.stack.pop()
609
- break
610
-
611
- case OP.OP_2DUP:
612
- if (this.stack.length < 2) {
613
- this.scriptEvaluationError('OP_2DUP requires at least two items to be on the stack.')
614
- }
615
- buf1 = this.stacktop(-2)
616
- buf2 = this.stacktop(-1)
617
- this.stack.push([...buf1])
618
- this.stack.push([...buf2])
619
- break
620
-
621
- case OP.OP_3DUP:
622
- if (this.stack.length < 3) {
623
- this.scriptEvaluationError('OP_3DUP requires at least three items to be on the stack.')
624
- }
625
- buf1 = this.stacktop(-3)
626
- buf2 = this.stacktop(-2)
627
- buf3 = this.stacktop(-1)
628
- this.stack.push([...buf1])
629
- this.stack.push([...buf2])
630
- this.stack.push([...buf3])
631
- break
632
-
633
- case OP.OP_2OVER:
634
- if (this.stack.length < 4) {
635
- this.scriptEvaluationError('OP_2OVER requires at least four items to be on the stack.')
636
- }
637
- buf1 = this.stacktop(-4)
638
- buf2 = this.stacktop(-3)
639
- this.stack.push([...buf1])
640
- this.stack.push([...buf2])
641
- break
642
-
643
- case OP.OP_2ROT:
644
- if (this.stack.length < 6) {
645
- this.scriptEvaluationError('OP_2ROT requires at least six items to be on the stack.')
646
- }
647
- spliced = this.stack.splice(this.stack.length - 6, 2)
648
- this.stack.push(spliced[0])
649
- this.stack.push(spliced[1])
650
- break
651
-
652
- case OP.OP_2SWAP:
653
- if (this.stack.length < 4) {
654
- this.scriptEvaluationError('OP_2SWAP requires at least four items to be on the stack.')
655
- }
656
- spliced = this.stack.splice(this.stack.length - 4, 2)
657
- this.stack.push(spliced[0])
658
- this.stack.push(spliced[1])
659
- break
660
-
661
- case OP.OP_IFDUP:
662
- if (this.stack.length < 1) {
663
- this.scriptEvaluationError('OP_IFDUP requires at least one item to be on the stack.')
664
- }
665
- buf = this.stacktop(-1)
666
- fValue = this.castToBool(buf)
667
- if (fValue) {
668
- this.stack.push([...buf])
669
- }
670
- break
671
-
672
- case OP.OP_DEPTH:
673
- buf = new BigNumber(this.stack.length).toScriptNum()
674
- this.stack.push(buf)
675
- break
676
-
677
- case OP.OP_DROP:
678
- if (this.stack.length < 1) {
679
- this.scriptEvaluationError('OP_DROP requires at least one item to be on the stack.')
680
- }
681
- this.stack.pop()
682
- break
683
-
684
- case OP.OP_DUP:
685
- if (this.stack.length < 1) {
686
- this.scriptEvaluationError('OP_DUP requires at least one item to be on the stack.')
687
- }
688
- this.stack.push([...this.stacktop(-1)])
689
- break
690
-
691
- case OP.OP_NIP:
692
- if (this.stack.length < 2) {
693
- this.scriptEvaluationError('OP_NIP requires at least two items to be on the stack.')
694
- }
695
- this.stack.splice(this.stack.length - 2, 1)
696
- break
697
-
698
- case OP.OP_OVER:
699
- if (this.stack.length < 2) {
700
- this.scriptEvaluationError('OP_OVER requires at least two items to be on the stack.')
701
- }
702
- this.stack.push([...this.stacktop(-2)])
703
- break
704
-
705
- case OP.OP_PICK:
706
- case OP.OP_ROLL:
707
- if (this.stack.length < 2) {
708
- this.scriptEvaluationError(`${OP[currentOpcode] as string} requires at least two items to be on the stack.`)
709
- }
710
- buf = this.stacktop(-1)
711
- bn = BigNumber.fromScriptNum(buf, requireMinimalPush)
712
- n = bn.toNumber()
713
- this.stack.pop()
714
- if (n < 0 || n >= this.stack.length) {
715
- this.scriptEvaluationError(`${OP[currentOpcode] as string} requires the top stack element to be 0 or a positive number less than the current size of the stack.`)
716
- }
717
- buf = this.stacktop(-n - 1)
718
- if (currentOpcode === OP.OP_ROLL) {
719
- this.stack.splice(this.stack.length - n - 1, 1)
720
- }
721
- this.stack.push([...buf])
722
- break
723
-
724
- case OP.OP_ROT:
725
- if (this.stack.length < 3) {
726
- this.scriptEvaluationError('OP_ROT requires at least three items to be on the stack.')
727
- }
728
- x1 = this.stacktop(-3)
729
- x2 = this.stacktop(-2)
730
- x3 = this.stacktop(-1)
731
- this.stack[this.stack.length - 3] = x2
732
- this.stack[this.stack.length - 2] = x3
733
- this.stack[this.stack.length - 1] = x1
734
- break
735
-
736
- case OP.OP_SWAP:
737
- if (this.stack.length < 2) {
738
- this.scriptEvaluationError('OP_SWAP requires at least two items to be on the stack.')
739
- }
740
- x1 = this.stacktop(-2)
741
- x2 = this.stacktop(-1)
742
- this.stack[this.stack.length - 2] = x2
743
- this.stack[this.stack.length - 1] = x1
744
- break
745
-
746
- case OP.OP_TUCK:
747
- if (this.stack.length < 2) {
748
- this.scriptEvaluationError('OP_TUCK requires at least two items to be on the stack.')
749
- }
750
- this.stack.splice(this.stack.length - 2, 0, [...this.stacktop(-1)])
751
- break
752
-
753
- case OP.OP_SIZE:
754
- if (this.stack.length < 1) {
755
- this.scriptEvaluationError('OP_SIZE requires at least one item to be on the stack.')
756
- }
757
- bn = new BigNumber(this.stacktop(-1).length)
758
- this.stack.push(bn.toScriptNum())
759
- break
760
-
761
- case OP.OP_AND:
762
- case OP.OP_OR:
763
- case OP.OP_XOR:
764
- if (this.stack.length < 2) {
765
- this.scriptEvaluationError(`${OP[currentOpcode] as string} requires at least one item to be on the stack.`)
766
- }
767
- buf1 = this.stacktop(-2)
768
- buf2 = this.stacktop(-1)
769
-
770
- if (buf1.length !== buf2.length) {
771
- this.scriptEvaluationError(`${OP[currentOpcode] as string} requires the top two stack items to be the same size.`)
772
- }
773
-
774
- switch (currentOpcode) {
775
- case OP.OP_AND:
776
- for (let i = 0; i < buf1.length; i++) {
777
- buf1[i] &= buf2[i]
778
- }
779
- break
780
- case OP.OP_OR:
781
- for (let i = 0; i < buf1.length; i++) {
782
- buf1[i] |= buf2[i]
783
- }
784
- break
785
- case OP.OP_XOR:
786
- for (let i = 0; i < buf1.length; i++) {
787
- buf1[i] ^= buf2[i]
788
- }
789
- break
790
- }
791
-
792
- // And pop vch2.
793
- this.stack.pop()
794
- break
795
-
796
- case OP.OP_INVERT:
797
- if (this.stack.length < 1) {
798
- this.scriptEvaluationError('OP_INVERT requires at least one item to be on the stack.')
799
- }
800
- buf = this.stacktop(-1)
801
- for (let i = 0; i < buf.length; i++) {
802
- buf[i] = ~buf[i]
803
- }
804
- break
805
-
806
- case OP.OP_LSHIFT:
807
- case OP.OP_RSHIFT:
808
- if (this.stack.length < 2) {
809
- this.scriptEvaluationError(`${OP[currentOpcode] as string} requires at least two items to be on the stack.`)
810
- }
811
- buf1 = this.stacktop(-2)
812
- if (buf1.length === 0) {
813
- this.stack.pop()
814
- } else {
815
- bn1 = new BigNumber(buf1)
816
- bn2 = BigNumber.fromScriptNum(this.stacktop(-1), requireMinimalPush)
817
- n = bn2.toNumber()
818
- if (n < 0) {
819
- this.scriptEvaluationError(`${OP[currentOpcode] as string} requires the top item on the stack not to be negative.`)
820
- }
821
- this.stack.pop()
822
- this.stack.pop()
823
- let shifted
824
- if (currentOpcode === OP.OP_LSHIFT) {
825
- shifted = bn1.ushln(n)
826
- }
827
- if (currentOpcode === OP.OP_RSHIFT) {
828
- shifted = bn1.ushrn(n)
829
- }
830
- const bufShifted = padDataToSize(
831
- [...shifted.toArray().slice(buf1.length * -1)],
832
- buf1.length
833
- )
834
- this.stack.push(bufShifted)
835
- }
836
- break
837
-
838
- case OP.OP_EQUAL:
839
- case OP.OP_EQUALVERIFY:
840
- if (this.stack.length < 2) {
841
- this.scriptEvaluationError(`${OP[currentOpcode] as string} requires at least two items to be on the stack.`)
842
- }
843
- buf1 = this.stacktop(-2)
844
- buf2 = this.stacktop(-1)
845
- fEqual = toHex(buf1) === toHex(buf2)
846
- this.stack.pop()
847
- this.stack.pop()
848
- this.stack.push(fEqual ? [1] : [])
849
- if (currentOpcode === OP.OP_EQUALVERIFY) {
850
- if (fEqual) {
851
- this.stack.pop()
852
- } else {
853
- this.scriptEvaluationError('OP_EQUALVERIFY requires the top two stack items to be equal.')
854
- }
855
- }
856
- break
857
-
858
- case OP.OP_1ADD:
859
- case OP.OP_1SUB:
860
- case OP.OP_NEGATE:
861
- case OP.OP_ABS:
862
- case OP.OP_NOT:
863
- case OP.OP_0NOTEQUAL:
864
- if (this.stack.length < 1) {
865
- this.scriptEvaluationError(`${OP[currentOpcode] as string} requires at least one items to be on the stack.`)
866
- }
867
- buf = this.stacktop(-1)
868
- bn = BigNumber.fromScriptNum(buf, requireMinimalPush)
869
- switch (currentOpcode) {
870
- case OP.OP_1ADD:
871
- bn = bn.addn(1)
872
- break
873
- case OP.OP_1SUB:
874
- bn = bn.subn(1)
875
- break
876
- case OP.OP_NEGATE:
877
- bn = bn.neg()
878
- break
879
- case OP.OP_ABS:
880
- if (bn.cmpn(0) < 0) {
881
- bn = bn.neg()
882
- }
883
- break
884
- case OP.OP_NOT:
885
- bn = new BigNumber((bn.cmpn(0) === 0) ? 1 : 0 + 0)
886
- break
887
- case OP.OP_0NOTEQUAL:
888
- bn = new BigNumber((bn.cmpn(0) !== 0) ? 1 : 0 + 0)
889
- break
890
- }
891
- this.stack.pop()
892
- this.stack.push(bn.toScriptNum())
893
- break
894
-
895
- case OP.OP_ADD:
896
- case OP.OP_SUB:
897
- case OP.OP_MUL:
898
- case OP.OP_MOD:
899
- case OP.OP_DIV:
900
- case OP.OP_BOOLAND:
901
- case OP.OP_BOOLOR:
902
- case OP.OP_NUMEQUAL:
903
- case OP.OP_NUMEQUALVERIFY:
904
- case OP.OP_NUMNOTEQUAL:
905
- case OP.OP_LESSTHAN:
906
- case OP.OP_GREATERTHAN:
907
- case OP.OP_LESSTHANOREQUAL:
908
- case OP.OP_GREATERTHANOREQUAL:
909
- case OP.OP_MIN:
910
- case OP.OP_MAX:
911
- if (this.stack.length < 2) {
912
- this.scriptEvaluationError(`${OP[currentOpcode] as string} requires at least two items to be on the stack.`)
913
- }
914
- bn1 = BigNumber.fromScriptNum(this.stacktop(-2), requireMinimalPush)
915
- bn2 = BigNumber.fromScriptNum(this.stacktop(-1), requireMinimalPush)
916
- bn = new BigNumber(0)
917
-
918
- switch (currentOpcode) {
919
- case OP.OP_ADD:
920
- bn = bn1.add(bn2)
921
- break
922
- case OP.OP_SUB:
923
- bn = bn1.sub(bn2)
924
- break
925
- case OP.OP_MUL:
926
- bn = bn1.mul(bn2)
927
- break
928
- case OP.OP_DIV:
929
- if (bn2.cmpn(0) === 0) {
930
- this.scriptEvaluationError('OP_DIV cannot divide by zero!')
931
- }
932
- bn = bn1.div(bn2)
933
- break
934
- case OP.OP_MOD:
935
- if (bn2.cmpn(0) === 0) {
936
- this.scriptEvaluationError('OP_MOD cannot divide by zero!')
937
- }
938
- bn = bn1.mod(bn2)
939
- break
940
- case OP.OP_BOOLAND:
941
- bn = new BigNumber(
942
- ((bn1.cmpn(0) !== 0) && (bn2.cmpn(0) !== 0)) ? 1 : 0 + 0
943
- )
944
- break
945
- case OP.OP_BOOLOR:
946
- bn = new BigNumber(
947
- ((bn1.cmpn(0) !== 0) || (bn2.cmpn(0) !== 0)) ? 1 : 0 + 0
948
- )
949
- break
950
- case OP.OP_NUMEQUAL:
951
- bn = new BigNumber((bn1.cmp(bn2) === 0) ? 1 : 0 + 0)
952
- break
953
- case OP.OP_NUMEQUALVERIFY:
954
- bn = new BigNumber((bn1.cmp(bn2) === 0) ? 1 : 0 + 0)
955
- break
956
- case OP.OP_NUMNOTEQUAL:
957
- bn = new BigNumber((bn1.cmp(bn2) !== 0) ? 1 : 0 + 0)
958
- break
959
- case OP.OP_LESSTHAN:
960
- bn = new BigNumber((bn1.cmp(bn2) < 0) ? 1 : 0 + 0)
961
- break
962
- case OP.OP_GREATERTHAN:
963
- bn = new BigNumber((bn1.cmp(bn2) > 0) ? 1 : 0 + 0)
964
- break
965
- case OP.OP_LESSTHANOREQUAL:
966
- bn = new BigNumber((bn1.cmp(bn2) <= 0) ? 1 : 0 + 0)
967
- break
968
- case OP.OP_GREATERTHANOREQUAL:
969
- bn = new BigNumber((bn1.cmp(bn2) >= 0) ? 1 : 0 + 0)
970
- break
971
- case OP.OP_MIN:
972
- bn = (bn1.cmp(bn2) < 0 ? bn1 : bn2)
973
- break
974
- case OP.OP_MAX:
975
- bn = (bn1.cmp(bn2) > 0 ? bn1 : bn2)
976
- break
977
- }
978
- this.stack.pop()
979
- this.stack.pop()
980
- this.stack.push(bn.toScriptNum())
981
-
982
- if (currentOpcode === OP.OP_NUMEQUALVERIFY) {
983
- if (this.castToBool(this.stacktop(-1))) {
984
- this.stack.pop()
985
- } else {
986
- this.scriptEvaluationError('OP_NUMEQUALVERIFY requires the top stack item to be truthy.')
987
- }
988
- }
989
- break
990
-
991
- case OP.OP_WITHIN:
992
- if (this.stack.length < 3) {
993
- this.scriptEvaluationError('OP_WITHIN requires at least three items to be on the stack.')
994
- }
995
- bn1 = BigNumber.fromScriptNum(this.stacktop(-3), requireMinimalPush)
996
- bn2 = BigNumber.fromScriptNum(this.stacktop(-2), requireMinimalPush)
997
- bn3 = BigNumber.fromScriptNum(this.stacktop(-1), requireMinimalPush)
998
- fValue = (bn2.cmp(bn1) <= 0) && (bn1.cmp(bn3) < 0)
999
- this.stack.pop()
1000
- this.stack.pop()
1001
- this.stack.pop()
1002
- this.stack.push(fValue ? [1] : [])
1003
- break
1004
-
1005
- case OP.OP_RIPEMD160:
1006
- case OP.OP_SHA1:
1007
- case OP.OP_SHA256:
1008
- case OP.OP_HASH160:
1009
- case OP.OP_HASH256:
1010
- if (this.stack.length < 1) {
1011
- this.scriptEvaluationError(`${OP[currentOpcode] as string} requires at least one item to be on the stack.`)
1012
- }
1013
- buf = this.stacktop(-1)
1014
- if (currentOpcode === OP.OP_RIPEMD160) {
1015
- bufHash = Hash.ripemd160(buf) as number[]
1016
- } else if (currentOpcode === OP.OP_SHA1) {
1017
- bufHash = Hash.sha1(buf) as number[]
1018
- } else if (currentOpcode === OP.OP_SHA256) {
1019
- bufHash = Hash.sha256(buf) as number[]
1020
- } else if (currentOpcode === OP.OP_HASH160) {
1021
- bufHash = Hash.hash160(buf) as number[]
1022
- } else if (currentOpcode === OP.OP_HASH256) {
1023
- bufHash = Hash.hash256(buf) as number[]
1024
- }
1025
- this.stack.pop()
1026
- this.stack.push(bufHash)
1027
- break
1028
-
1029
- case OP.OP_CODESEPARATOR:
1030
- this.lastCodeSeparator = this.programCounter
1031
- break
1032
-
1033
- case OP.OP_CHECKSIG:
1034
- case OP.OP_CHECKSIGVERIFY:
1035
- if (this.stack.length < 2) {
1036
- this.scriptEvaluationError(`${OP[currentOpcode] as string} requires at least two items to be on the stack.`)
1037
- }
1038
-
1039
- bufSig = this.stacktop(-2)
1040
- bufPubkey = this.stacktop(-1)
1041
-
1042
- if (
1043
- !checkSignatureEncoding(bufSig) ||
1044
- !checkPublicKeyEncoding(bufPubkey)
1045
- ) {
1046
- this.scriptEvaluationError(`${OP[currentOpcode] as string} requires correct encoding for the public key and signature.`)
1047
- }
1048
-
1049
- // Subset of script starting at the most recent codeseparator
1050
- // CScript scriptCode(pbegincodehash, pend);
1051
- if (this.context === 'UnlockingScript') {
1052
- subscript = new Script(this.unlockingScript.chunks.slice(this.lastCodeSeparator))
1053
- } else {
1054
- subscript = new Script(this.lockingScript.chunks.slice(this.lastCodeSeparator))
1055
- }
1056
-
1057
- // Drop the signature, since there's no way for a signature to sign itself
1058
- subscript.findAndDelete(new Script().writeBin(bufSig))
1059
-
1060
- try {
1061
- sig = TransactionSignature.fromChecksigFormat(bufSig)
1062
- pubkey = PublicKey.fromString(toHex(bufPubkey))
1063
-
1064
- fSuccess = verifySignature(sig, pubkey, subscript)
1065
- } catch (e) {
1066
- // invalid sig or pubkey
1067
- fSuccess = false
1068
- }
1069
-
1070
- if (!fSuccess && bufSig.length > 0) {
1071
- this.scriptEvaluationError(`${OP[currentOpcode] as string} failed to verify the signature, and requires an empty signature when verification fails.`)
1072
- }
1073
-
1074
- this.stack.pop()
1075
- this.stack.pop()
1076
-
1077
- // stack.push_back(fSuccess ? vchTrue : vchFalse);
1078
- this.stack.push(fSuccess ? [1] : [])
1079
- if (currentOpcode === OP.OP_CHECKSIGVERIFY) {
1080
- if (fSuccess) {
1081
- this.stack.pop()
1082
- } else {
1083
- this.scriptEvaluationError('OP_CHECKSIGVERIFY requires that a valid signature is provided.')
1084
- }
1085
- }
1086
- break
1087
-
1088
- case OP.OP_CHECKMULTISIG:
1089
- case OP.OP_CHECKMULTISIGVERIFY:
1090
-
1091
- i = 1
1092
- if (this.stack.length < i) {
1093
- this.scriptEvaluationError(`${OP[currentOpcode] as string} requires at least 1 item to be on the stack.`)
1094
- }
1095
-
1096
- nKeysCount = BigNumber.fromScriptNum(this.stacktop(-i), requireMinimalPush).toNumber()
1097
- // TODO: Keys and opcount are parameterized in client. No magic numbers!
1098
- if (nKeysCount < 0 || nKeysCount > maxMultisigKeyCount) {
1099
- this.scriptEvaluationError(`${OP[currentOpcode] as string} requires a key count between 0 and ${maxMultisigKeyCount}.`)
1100
- }
1101
- ikey = ++i
1102
- i += nKeysCount
1103
-
1104
- // ikey2 is the position of last non-signature item in
1105
- // the stack. Top stack item = 1. With
1106
- // SCRIPT_VERIFY_NULLFAIL, this is used for cleanup if
1107
- // operation fails.
1108
- ikey2 = nKeysCount + 2
1109
-
1110
- if (this.stack.length < i) {
1111
- this.scriptEvaluationError(`${OP[currentOpcode] as string} requires the number of stack items not to be less than the number of keys used.`)
1112
- }
1113
-
1114
- nSigsCount = BigNumber.fromScriptNum(this.stacktop(-i), requireMinimalPush).toNumber()
1115
- if (nSigsCount < 0 || nSigsCount > nKeysCount) {
1116
- this.scriptEvaluationError(`${OP[currentOpcode] as string} requires the number of signatures to be no greater than the number of keys.`)
1117
- }
1118
- isig = ++i
1119
- i += nSigsCount
1120
- if (this.stack.length < i) {
1121
- this.scriptEvaluationError(`${OP[currentOpcode] as string} requires the number of stack items not to be less than the number of signatures provided.`)
1122
- }
1123
-
1124
- // Subset of script starting at the most recent codeseparator
1125
- if (this.context === 'UnlockingScript') {
1126
- subscript = new Script(this.unlockingScript.chunks.slice(this.lastCodeSeparator))
1127
- } else {
1128
- subscript = new Script(this.lockingScript.chunks.slice(this.lastCodeSeparator))
1129
- }
1130
-
1131
- // Drop the signatures, since there's no way for a signature to sign itself
1132
- for (let k = 0; k < nSigsCount; k++) {
1133
- bufSig = this.stacktop(-isig - k)
1134
- subscript.findAndDelete(new Script().writeBin(bufSig))
1135
- }
1136
-
1137
- fSuccess = true
1138
- while (fSuccess && nSigsCount > 0) {
1139
- // valtype& vchSig = this.stacktop(-isig);
1140
- bufSig = this.stacktop(-isig)
1141
- // valtype& vchPubKey = this.stacktop(-ikey);
1142
- bufPubkey = this.stacktop(-ikey)
1143
-
1144
- if (
1145
- !checkSignatureEncoding(bufSig) ||
1146
- !checkPublicKeyEncoding(bufPubkey)
1147
- ) {
1148
- this.scriptEvaluationError(`${OP[currentOpcode] as string} requires correct encoding for the public key and signature.`)
1149
- }
1150
-
1151
- try {
1152
- sig = TransactionSignature.fromChecksigFormat(bufSig)
1153
- pubkey = PublicKey.fromString(toHex(bufPubkey))
1154
- fOk = verifySignature(sig, pubkey, subscript)
1155
- } catch (e) {
1156
- // invalid sig or pubkey
1157
- fOk = false
1158
- }
1159
-
1160
- if (fOk) {
1161
- isig++
1162
- nSigsCount--
1163
- }
1164
- ikey++
1165
- nKeysCount--
1166
-
1167
- // If there are more signatures left than keys left,
1168
- // then too many signatures have failed
1169
- if (nSigsCount > nKeysCount) {
1170
- fSuccess = false
1171
- }
1172
- }
1173
-
1174
- // Clean up stack of actual arguments
1175
- while (i-- > 1) {
1176
- if (
1177
- !fSuccess && !ikey2 && (this.stacktop(-1).length > 0)
1178
- ) {
1179
- this.scriptEvaluationError(`${OP[currentOpcode] as string} failed to verify a signature, and requires an empty signature when verification fails.`)
1180
- }
1181
-
1182
- if (ikey2 > 0) {
1183
- ikey2--
1184
- }
1185
-
1186
- this.stack.pop()
1187
- }
1188
-
1189
- // A bug causes CHECKMULTISIG to consume one extra argument
1190
- // whose contents were not checked in any way.
1191
- //
1192
- // Unfortunately this is a potential source of mutability,
1193
- // so optionally verify it is exactly equal to zero prior
1194
- // to removing it from the stack.
1195
- if (this.stack.length < 1) {
1196
- this.scriptEvaluationError(`${OP[currentOpcode] as string} requires an extra item to be on the stack.`)
1197
- }
1198
- if (this.stacktop(-1).length > 0) { // NOTE: Is this necessary? We don't care about malleability.
1199
- this.scriptEvaluationError(`${OP[currentOpcode] as string} requires the extra stack item to be empty.`)
1200
- }
1201
- this.stack.pop()
1202
-
1203
- this.stack.push(fSuccess ? [1] : [])
1204
-
1205
- if (currentOpcode === OP.OP_CHECKMULTISIGVERIFY) {
1206
- if (fSuccess) {
1207
- this.stack.pop()
1208
- } else {
1209
- this.scriptEvaluationError('OP_CHECKMULTISIGVERIFY requires that a sufficient number of valid signatures are provided.')
1210
- }
1211
- }
1212
- break
1213
-
1214
- case OP.OP_CAT:
1215
- if (this.stack.length < 2) {
1216
- this.scriptEvaluationError('OP_CAT requires at least two items to be on the stack.')
1217
- }
1218
-
1219
- buf1 = this.stacktop(-2)
1220
- buf2 = this.stacktop(-1)
1221
- if (buf1.length + buf2.length > maxScriptElementSize) {
1222
- this.scriptEvaluationError(`It's not currently possible to push data larger than ${maxScriptElementSize} bytes.`)
1223
- }
1224
- this.stack[this.stack.length - 2] = [...buf1, ...buf2]
1225
- this.stack.pop()
1226
- break
1227
-
1228
- case OP.OP_SPLIT:
1229
- if (this.stack.length < 2) {
1230
- this.scriptEvaluationError('OP_SPLIT requires at least two items to be on the stack.')
1231
- }
1232
- buf1 = this.stacktop(-2)
1233
-
1234
- // Make sure the split point is apropriate.
1235
- n = BigNumber.fromScriptNum(this.stacktop(-1), requireMinimalPush).toNumber()
1236
- if (n < 0 || n > buf1.length) {
1237
- 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.')
1238
- }
1239
-
1240
- // Prepare the results in their own buffer as `data`
1241
- // will be invalidated.
1242
- // Copy buffer data, to slice it before
1243
- buf2 = [...buf1]
1244
-
1245
- // Replace existing stack values by the new values.
1246
- this.stack[this.stack.length - 2] = buf2.slice(0, n)
1247
- this.stack[this.stack.length - 1] = buf2.slice(n)
1248
- break
1249
-
1250
- case OP.OP_NUM2BIN:
1251
- if (this.stack.length < 2) {
1252
- this.scriptEvaluationError('OP_NUM2BIN requires at least two items to be on the stack.')
1253
- }
1254
-
1255
- size = BigNumber.fromScriptNum(this.stacktop(-1), requireMinimalPush).toNumber()
1256
- if (size > maxScriptElementSize) {
1257
- this.scriptEvaluationError(`It's not currently possible to push data larger than ${maxScriptElementSize} bytes.`)
1258
- }
1259
-
1260
- this.stack.pop()
1261
- rawnum = this.stacktop(-1)
1262
-
1263
- // Try to see if we can fit that number in the number of
1264
- // byte requested.
1265
- rawnum = minimallyEncode(rawnum)
1266
-
1267
- if (rawnum.length > size) {
1268
- 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.')
1269
- }
1270
-
1271
- // We already have an element of the right size, we
1272
- // don't need to do anything.
1273
- if (rawnum.length === size) {
1274
- this.stack[this.stack.length - 1] = rawnum
1275
- break
1276
- }
1277
-
1278
- signbit = 0x00
1279
- if (rawnum.length > 0) {
1280
- signbit = rawnum[rawnum.length - 1] & 0x80
1281
- rawnum[rawnum.length - 1] &= 0x7f
1282
- }
1283
-
1284
- num = new Array(size)
1285
- num.fill(0)
1286
- for (n = 0; n < size; n++) {
1287
- num[n] = rawnum[n]
1288
- }
1289
- n = rawnum.length - 1
1290
- while (n++ < size - 2) {
1291
- num[n] = 0x00
1292
- }
1293
-
1294
- num[n] = signbit
1295
-
1296
- this.stack[this.stack.length - 1] = num
1297
- break
1298
-
1299
- case OP.OP_BIN2NUM:
1300
- if (this.stack.length < 1) {
1301
- this.scriptEvaluationError('OP_BIN2NUM requires at least one item to be on the stack.')
1302
- }
1303
-
1304
- buf1 = this.stacktop(-1)
1305
- buf2 = minimallyEncode(buf1)
1306
-
1307
- this.stack[this.stack.length - 1] = buf2
1308
-
1309
- // The resulting number must be a valid number.
1310
- if (!isMinimallyEncoded(buf2)) {
1311
- this.scriptEvaluationError('OP_BIN2NUM requires that the resulting number is valid.')
1312
- }
1313
- break
1314
-
1315
- default:
1316
- this.scriptEvaluationError('Invalid opcode!')
1317
- }
1318
- }
1319
-
1320
- // Finally, increment the program counter
1321
- this.programCounter++
1322
- }
1323
-
1324
- /**
1325
- * @method validate
1326
- * Validates the spend action by interpreting the locking and unlocking scripts.
1327
- * @returns {boolean} Returns true if the scripts are valid and the spend is legitimate, otherwise false.
1328
- * @example
1329
- * if (spend.validate()) {
1330
- * console.log("Spend is valid!");
1331
- * } else {
1332
- * console.log("Invalid spend!");
1333
- * }
1334
- */
1335
- validate(): boolean {
1336
- if (requirePushOnlyUnlockingScripts && !this.unlockingScript.isPushOnly()) {
1337
- this.scriptEvaluationError('Unlocking scripts can only contain push operations, and no other opcodes.')
1338
- }
1339
- while (true) {
1340
- this.step()
1341
- if (this.context === 'LockingScript' && this.programCounter >= this.lockingScript.chunks.length) {
1342
- break
1343
- }
1344
- }
1345
- if (this.ifStack.length > 0) {
1346
- this.scriptEvaluationError('Every OP_IF must be terminated prior to the end of the script.')
1347
- }
1348
- if (requireCleanStack) {
1349
- if (this.stack.length !== 1) {
1350
- this.scriptEvaluationError('The clean stack rule requires exactly one item to be on the stack after script execution.')
1351
- }
1352
- }
1353
- if (!this.castToBool(this.stacktop(-1))) {
1354
- this.scriptEvaluationError('The top stack element must be truthy after script evaluation.')
1355
- }
1356
- return true
1357
- }
1358
-
1359
- private stacktop(i: number): number[] {
1360
- return this.stack[this.stack.length + i]
1361
- }
1362
-
1363
- private castToBool(val: number[]): boolean {
1364
- for (let i = 0; i < val.length; i++) {
1365
- if (val[i] !== 0) {
1366
- // can be negative zero
1367
- if (i === val.length - 1 && val[i] === 0x80) {
1368
- return false
1369
- }
1370
- return true
1371
- }
1372
- }
1373
- return false
1374
- }
1375
-
1376
- private scriptEvaluationError(str: string): void {
1377
- 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}`)
1378
- }
1379
- }