@bsv/sdk 1.4.20 → 1.4.22
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/script/Spend.js +239 -42
- package/dist/cjs/src/script/Spend.js.map +1 -1
- package/dist/cjs/src/transaction/Transaction.js +5 -2
- package/dist/cjs/src/transaction/Transaction.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/script/Spend.js +236 -37
- package/dist/esm/src/script/Spend.js.map +1 -1
- package/dist/esm/src/transaction/Transaction.js +5 -2
- package/dist/esm/src/transaction/Transaction.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/script/Spend.d.ts +7 -1
- package/dist/types/src/script/Spend.d.ts.map +1 -1
- package/dist/types/src/transaction/Transaction.d.ts +3 -1
- package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/dist/umd/bundle.js +1 -1
- package/docs/transaction.md +5 -1
- package/package.json +1 -1
- package/src/script/Spend.ts +253 -42
- package/src/transaction/Transaction.ts +6 -2
- package/src/transaction/__tests/Transaction.test.ts +125 -1
package/docs/transaction.md
CHANGED
|
@@ -1974,7 +1974,7 @@ Argument Details
|
|
|
1974
1974
|
Verifies the legitimacy of the Bitcoin transaction according to the rules of SPV by ensuring all the input transactions link back to valid block headers, the chain of spends for all inputs are valid, and the sum of inputs is not less than the sum of outputs.
|
|
1975
1975
|
|
|
1976
1976
|
```ts
|
|
1977
|
-
async verify(chainTracker: ChainTracker | "scripts only" = defaultChainTracker(), feeModel?: FeeModel): Promise<boolean>
|
|
1977
|
+
async verify(chainTracker: ChainTracker | "scripts only" = defaultChainTracker(), feeModel?: FeeModel, memoryLimit?: number): Promise<boolean>
|
|
1978
1978
|
```
|
|
1979
1979
|
See also: [ChainTracker](./transaction.md#interface-chaintracker), [FeeModel](./transaction.md#interface-feemodel), [defaultChainTracker](./transaction.md#function-defaultchaintracker)
|
|
1980
1980
|
|
|
@@ -1986,6 +1986,10 @@ Argument Details
|
|
|
1986
1986
|
|
|
1987
1987
|
+ **chainTracker**
|
|
1988
1988
|
+ An instance of ChainTracker, a Bitcoin block header tracker. If the value is set to 'scripts only', headers will not be verified. If not provided then the default chain tracker will be used.
|
|
1989
|
+
+ **feeModel**
|
|
1990
|
+
+ An instance of FeeModel, a fee model to use for fee calculation. If not provided then the default fee model will be used.
|
|
1991
|
+
+ **memoryLimit**
|
|
1992
|
+
+ The maximum memory in bytes usage allowed for script evaluation. If not provided then the default memory limit will be used.
|
|
1989
1993
|
|
|
1990
1994
|
Example
|
|
1991
1995
|
|
package/package.json
CHANGED
package/src/script/Spend.ts
CHANGED
|
@@ -38,6 +38,7 @@ const requireCleanStack = true
|
|
|
38
38
|
* @property {number} inputIndex - The index of this input in the current transaction.
|
|
39
39
|
* @property {UnlockingScript} unlockingScript - The unlocking script that unlocks the UTXO for spending.
|
|
40
40
|
* @property {number} inputSequence - The sequence number of this input.
|
|
41
|
+
* @property {number} lockTime - The lock time of the transaction.
|
|
41
42
|
*/
|
|
42
43
|
export default class Spend {
|
|
43
44
|
sourceTXID: string
|
|
@@ -57,6 +58,9 @@ export default class Spend {
|
|
|
57
58
|
stack: number[][]
|
|
58
59
|
altStack: number[][]
|
|
59
60
|
ifStack: boolean[]
|
|
61
|
+
memoryLimit: number
|
|
62
|
+
stackMem: number
|
|
63
|
+
altStackMem: number
|
|
60
64
|
|
|
61
65
|
/**
|
|
62
66
|
* @constructor
|
|
@@ -87,6 +91,7 @@ export default class Spend {
|
|
|
87
91
|
* inputIndex: 0, // inputIndex
|
|
88
92
|
* unlockingScript: UnlockingScript.fromASM("3045... 02ab..."),
|
|
89
93
|
* inputSequence: 0xffffffff // inputSequence
|
|
94
|
+
* memoryLimit: 100000 // memoryLimit
|
|
90
95
|
* });
|
|
91
96
|
*/
|
|
92
97
|
constructor (params: {
|
|
@@ -101,6 +106,7 @@ export default class Spend {
|
|
|
101
106
|
inputSequence: number
|
|
102
107
|
inputIndex: number
|
|
103
108
|
lockTime: number
|
|
109
|
+
memoryLimit?: number
|
|
104
110
|
}) {
|
|
105
111
|
this.sourceTXID = params.sourceTXID
|
|
106
112
|
this.sourceOutputIndex = params.sourceOutputIndex
|
|
@@ -113,6 +119,9 @@ export default class Spend {
|
|
|
113
119
|
this.unlockingScript = params.unlockingScript
|
|
114
120
|
this.inputSequence = params.inputSequence
|
|
115
121
|
this.lockTime = params.lockTime
|
|
122
|
+
this.memoryLimit = params.memoryLimit ?? 100000 // 100 MB is going to be processed by most miners by policy, but the default should protect apps against memory attacks.
|
|
123
|
+
this.stackMem = 0
|
|
124
|
+
this.altStackMem = 0
|
|
116
125
|
this.reset()
|
|
117
126
|
}
|
|
118
127
|
|
|
@@ -123,9 +132,27 @@ export default class Spend {
|
|
|
123
132
|
this.stack = []
|
|
124
133
|
this.altStack = []
|
|
125
134
|
this.ifStack = []
|
|
135
|
+
this.stackMem = 0
|
|
136
|
+
this.altStackMem = 0
|
|
126
137
|
}
|
|
127
138
|
|
|
128
|
-
step ():
|
|
139
|
+
step (): boolean {
|
|
140
|
+
let poppedValue: number[] | undefined
|
|
141
|
+
// If the stack (or alt stack) is over the memory limit, evaluation has failed.
|
|
142
|
+
if (this.stackMem > this.memoryLimit) {
|
|
143
|
+
this.scriptEvaluationError('Stack memory usage has exceeded ' + String(this.memoryLimit) + ' bytes')
|
|
144
|
+
return false
|
|
145
|
+
}
|
|
146
|
+
if (this.altStackMem > this.memoryLimit) {
|
|
147
|
+
this.scriptEvaluationError('Alt stack memory usage has exceeded ' + String(this.memoryLimit) + ' bytes')
|
|
148
|
+
return false
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Clear popped values after use to free memory
|
|
152
|
+
const clearPoppedValue = (): void => {
|
|
153
|
+
poppedValue = undefined
|
|
154
|
+
}
|
|
155
|
+
|
|
129
156
|
// If the context is UnlockingScript and we have reached the end,
|
|
130
157
|
// set the context to LockingScript and zero the program counter
|
|
131
158
|
if (
|
|
@@ -264,6 +291,10 @@ export default class Spend {
|
|
|
264
291
|
// Non-canonical signature: R value type mismatch
|
|
265
292
|
return false
|
|
266
293
|
}
|
|
294
|
+
if (buf[4 - 1] !== nLEnR) {
|
|
295
|
+
// Non-canonical signature: R length mismatch
|
|
296
|
+
return false
|
|
297
|
+
}
|
|
267
298
|
if (nLEnR === 0) {
|
|
268
299
|
// Non-canonical signature: R length is zero
|
|
269
300
|
return false
|
|
@@ -282,6 +313,10 @@ export default class Spend {
|
|
|
282
313
|
// Non-canonical signature: S value type mismatch
|
|
283
314
|
return false
|
|
284
315
|
}
|
|
316
|
+
if (buf[6 + nLEnR - 1] !== nLEnS) {
|
|
317
|
+
// Non-canonical signature: S length mismatch
|
|
318
|
+
return false
|
|
319
|
+
}
|
|
285
320
|
if (nLEnS === 0) {
|
|
286
321
|
// Non-canonical signature: S length is zero
|
|
287
322
|
return false
|
|
@@ -305,14 +340,20 @@ export default class Spend {
|
|
|
305
340
|
}
|
|
306
341
|
|
|
307
342
|
if (!isChecksigFormat(buf)) {
|
|
308
|
-
this.scriptEvaluationError(
|
|
343
|
+
this.scriptEvaluationError(
|
|
344
|
+
'The signature format is invalid.'
|
|
345
|
+
)
|
|
309
346
|
}
|
|
310
347
|
const sig = TransactionSignature.fromChecksigFormat(buf)
|
|
311
348
|
if (requireLowSSignatures && !sig.hasLowS()) {
|
|
312
|
-
this.scriptEvaluationError(
|
|
349
|
+
this.scriptEvaluationError(
|
|
350
|
+
'The signature must have a low S value.'
|
|
351
|
+
)
|
|
313
352
|
}
|
|
314
353
|
if ((sig.scope & TransactionSignature.SIGHASH_FORKID) === 0) {
|
|
315
|
-
this.scriptEvaluationError(
|
|
354
|
+
this.scriptEvaluationError(
|
|
355
|
+
'The signature must use SIGHASH_FORKID.'
|
|
356
|
+
)
|
|
316
357
|
return false
|
|
317
358
|
}
|
|
318
359
|
|
|
@@ -338,7 +379,9 @@ export default class Spend {
|
|
|
338
379
|
)
|
|
339
380
|
}
|
|
340
381
|
} else {
|
|
341
|
-
this.scriptEvaluationError(
|
|
382
|
+
this.scriptEvaluationError(
|
|
383
|
+
'The public key is in an unknown format.'
|
|
384
|
+
)
|
|
342
385
|
}
|
|
343
386
|
return true
|
|
344
387
|
}
|
|
@@ -428,8 +471,10 @@ export default class Spend {
|
|
|
428
471
|
|
|
429
472
|
if (!Array.isArray(operation.data)) {
|
|
430
473
|
this.stack.push([])
|
|
474
|
+
this.stackMem += 0
|
|
431
475
|
} else {
|
|
432
476
|
this.stack.push(operation.data)
|
|
477
|
+
this.stackMem += operation.data.length
|
|
433
478
|
}
|
|
434
479
|
} else if (
|
|
435
480
|
isScriptExecuting ||
|
|
@@ -456,6 +501,7 @@ export default class Spend {
|
|
|
456
501
|
n = currentOpcode - (OP.OP_1 - 1)
|
|
457
502
|
buf = new BigNumber(n).toScriptNum()
|
|
458
503
|
this.stack.push(buf)
|
|
504
|
+
this.stackMem += buf.length
|
|
459
505
|
break
|
|
460
506
|
|
|
461
507
|
case OP.OP_NOP:
|
|
@@ -550,7 +596,11 @@ export default class Spend {
|
|
|
550
596
|
if (currentOpcode === OP.OP_NOTIF) {
|
|
551
597
|
fValue = !fValue
|
|
552
598
|
}
|
|
553
|
-
this.stack.pop()
|
|
599
|
+
poppedValue = this.stack.pop()
|
|
600
|
+
if (poppedValue != null) {
|
|
601
|
+
this.stackMem -= poppedValue.length
|
|
602
|
+
}
|
|
603
|
+
clearPoppedValue()
|
|
554
604
|
}
|
|
555
605
|
this.ifStack.push(fValue)
|
|
556
606
|
break
|
|
@@ -578,9 +628,12 @@ export default class Spend {
|
|
|
578
628
|
}
|
|
579
629
|
buf = this.stacktop(-1)
|
|
580
630
|
fValue = this.castToBool(buf)
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
631
|
+
poppedValue = this.stack.pop()
|
|
632
|
+
if (poppedValue != null) {
|
|
633
|
+
this.stackMem -= poppedValue.length
|
|
634
|
+
}
|
|
635
|
+
clearPoppedValue()
|
|
636
|
+
if (!fValue) {
|
|
584
637
|
this.scriptEvaluationError(
|
|
585
638
|
'OP_VERIFY requires the top stack value to be truthy.'
|
|
586
639
|
)
|
|
@@ -601,7 +654,13 @@ export default class Spend {
|
|
|
601
654
|
this.scriptEvaluationError(
|
|
602
655
|
'OP_TOALTSTACK requires at oeast one item to be on the stack.')
|
|
603
656
|
}
|
|
604
|
-
this.
|
|
657
|
+
poppedValue = this.stack.pop()
|
|
658
|
+
if (poppedValue != null) {
|
|
659
|
+
this.altStack.push(poppedValue)
|
|
660
|
+
this.altStackMem += poppedValue.length
|
|
661
|
+
this.stackMem -= poppedValue.length
|
|
662
|
+
}
|
|
663
|
+
clearPoppedValue()
|
|
605
664
|
break
|
|
606
665
|
|
|
607
666
|
case OP.OP_FROMALTSTACK:
|
|
@@ -610,7 +669,13 @@ export default class Spend {
|
|
|
610
669
|
'OP_FROMALTSTACK requires at least one item to be on the stack.'
|
|
611
670
|
)
|
|
612
671
|
}
|
|
613
|
-
this.
|
|
672
|
+
poppedValue = this.altStack.pop()
|
|
673
|
+
if (poppedValue != null) {
|
|
674
|
+
this.stack.push(poppedValue)
|
|
675
|
+
this.stackMem += poppedValue.length
|
|
676
|
+
this.altStackMem -= poppedValue.length
|
|
677
|
+
}
|
|
678
|
+
clearPoppedValue()
|
|
614
679
|
break
|
|
615
680
|
|
|
616
681
|
case OP.OP_2DROP:
|
|
@@ -619,8 +684,16 @@ export default class Spend {
|
|
|
619
684
|
'OP_2DROP requires at least two items to be on the stack.'
|
|
620
685
|
)
|
|
621
686
|
}
|
|
622
|
-
this.stack.pop()
|
|
623
|
-
|
|
687
|
+
poppedValue = this.stack.pop()
|
|
688
|
+
if (poppedValue != null) {
|
|
689
|
+
this.stackMem -= poppedValue.length
|
|
690
|
+
}
|
|
691
|
+
clearPoppedValue()
|
|
692
|
+
poppedValue = this.stack.pop()
|
|
693
|
+
if (poppedValue != null) {
|
|
694
|
+
this.stackMem -= poppedValue.length
|
|
695
|
+
}
|
|
696
|
+
clearPoppedValue()
|
|
624
697
|
break
|
|
625
698
|
|
|
626
699
|
case OP.OP_2DUP:
|
|
@@ -632,7 +705,9 @@ export default class Spend {
|
|
|
632
705
|
buf1 = this.stacktop(-2)
|
|
633
706
|
buf2 = this.stacktop(-1)
|
|
634
707
|
this.stack.push([...buf1])
|
|
708
|
+
this.stackMem += buf1.length
|
|
635
709
|
this.stack.push([...buf2])
|
|
710
|
+
this.stackMem += buf2.length
|
|
636
711
|
break
|
|
637
712
|
|
|
638
713
|
case OP.OP_3DUP:
|
|
@@ -645,8 +720,11 @@ export default class Spend {
|
|
|
645
720
|
buf2 = this.stacktop(-2)
|
|
646
721
|
buf3 = this.stacktop(-1)
|
|
647
722
|
this.stack.push([...buf1])
|
|
723
|
+
this.stackMem += buf1.length
|
|
648
724
|
this.stack.push([...buf2])
|
|
725
|
+
this.stackMem += buf2.length
|
|
649
726
|
this.stack.push([...buf3])
|
|
727
|
+
this.stackMem += buf3.length
|
|
650
728
|
break
|
|
651
729
|
|
|
652
730
|
case OP.OP_2OVER:
|
|
@@ -658,7 +736,9 @@ export default class Spend {
|
|
|
658
736
|
buf1 = this.stacktop(-4)
|
|
659
737
|
buf2 = this.stacktop(-3)
|
|
660
738
|
this.stack.push([...buf1])
|
|
739
|
+
this.stackMem += buf1.length
|
|
661
740
|
this.stack.push([...buf2])
|
|
741
|
+
this.stackMem += buf2.length
|
|
662
742
|
break
|
|
663
743
|
|
|
664
744
|
case OP.OP_2ROT:
|
|
@@ -669,7 +749,9 @@ export default class Spend {
|
|
|
669
749
|
}
|
|
670
750
|
spliced = this.stack.splice(this.stack.length - 6, 2)
|
|
671
751
|
this.stack.push(spliced[0])
|
|
752
|
+
this.stackMem += spliced[0].length
|
|
672
753
|
this.stack.push(spliced[1])
|
|
754
|
+
this.stackMem += spliced[1].length
|
|
673
755
|
break
|
|
674
756
|
|
|
675
757
|
case OP.OP_2SWAP:
|
|
@@ -680,7 +762,9 @@ export default class Spend {
|
|
|
680
762
|
}
|
|
681
763
|
spliced = this.stack.splice(this.stack.length - 4, 2)
|
|
682
764
|
this.stack.push(spliced[0])
|
|
765
|
+
this.stackMem += spliced[0].length
|
|
683
766
|
this.stack.push(spliced[1])
|
|
767
|
+
this.stackMem += spliced[1].length
|
|
684
768
|
break
|
|
685
769
|
|
|
686
770
|
case OP.OP_IFDUP:
|
|
@@ -693,12 +777,14 @@ export default class Spend {
|
|
|
693
777
|
fValue = this.castToBool(buf)
|
|
694
778
|
if (fValue) {
|
|
695
779
|
this.stack.push([...buf])
|
|
780
|
+
this.stackMem += buf.length
|
|
696
781
|
}
|
|
697
782
|
break
|
|
698
783
|
|
|
699
784
|
case OP.OP_DEPTH:
|
|
700
785
|
buf = new BigNumber(this.stack.length).toScriptNum()
|
|
701
786
|
this.stack.push(buf)
|
|
787
|
+
this.stackMem += buf.length
|
|
702
788
|
break
|
|
703
789
|
|
|
704
790
|
case OP.OP_DROP:
|
|
@@ -707,7 +793,11 @@ export default class Spend {
|
|
|
707
793
|
'OP_DROP requires at least one item to be on the stack.'
|
|
708
794
|
)
|
|
709
795
|
}
|
|
710
|
-
this.stack.pop()
|
|
796
|
+
poppedValue = this.stack.pop()
|
|
797
|
+
if (poppedValue != null) {
|
|
798
|
+
this.stackMem -= poppedValue.length
|
|
799
|
+
}
|
|
800
|
+
clearPoppedValue()
|
|
711
801
|
break
|
|
712
802
|
|
|
713
803
|
case OP.OP_DUP:
|
|
@@ -717,6 +807,7 @@ export default class Spend {
|
|
|
717
807
|
)
|
|
718
808
|
}
|
|
719
809
|
this.stack.push([...this.stacktop(-1)])
|
|
810
|
+
this.stackMem += this.stacktop(-1).length
|
|
720
811
|
break
|
|
721
812
|
|
|
722
813
|
case OP.OP_NIP:
|
|
@@ -726,6 +817,7 @@ export default class Spend {
|
|
|
726
817
|
)
|
|
727
818
|
}
|
|
728
819
|
this.stack.splice(this.stack.length - 2, 1)
|
|
820
|
+
this.stackMem -= this.stacktop(-1).length
|
|
729
821
|
break
|
|
730
822
|
|
|
731
823
|
case OP.OP_OVER:
|
|
@@ -735,6 +827,7 @@ export default class Spend {
|
|
|
735
827
|
)
|
|
736
828
|
}
|
|
737
829
|
this.stack.push([...this.stacktop(-2)])
|
|
830
|
+
this.stackMem += this.stacktop(-2).length
|
|
738
831
|
break
|
|
739
832
|
|
|
740
833
|
case OP.OP_PICK:
|
|
@@ -747,7 +840,11 @@ export default class Spend {
|
|
|
747
840
|
buf = this.stacktop(-1)
|
|
748
841
|
bn = BigNumber.fromScriptNum(buf, requireMinimalPush)
|
|
749
842
|
n = bn.toNumber()
|
|
750
|
-
this.stack.pop()
|
|
843
|
+
poppedValue = this.stack.pop()
|
|
844
|
+
if (poppedValue != null) {
|
|
845
|
+
this.stackMem -= poppedValue.length
|
|
846
|
+
}
|
|
847
|
+
clearPoppedValue()
|
|
751
848
|
if (n < 0 || n >= this.stack.length) {
|
|
752
849
|
this.scriptEvaluationError(
|
|
753
850
|
`${OP[currentOpcode] as string} requires the top stack element to be 0 or a positive number less than the current size of the stack.`
|
|
@@ -756,8 +853,10 @@ export default class Spend {
|
|
|
756
853
|
buf = this.stacktop(-n - 1)
|
|
757
854
|
if (currentOpcode === OP.OP_ROLL) {
|
|
758
855
|
this.stack.splice(this.stack.length - n - 1, 1)
|
|
856
|
+
this.stackMem -= buf.length
|
|
759
857
|
}
|
|
760
858
|
this.stack.push([...buf])
|
|
859
|
+
this.stackMem += buf.length
|
|
761
860
|
break
|
|
762
861
|
|
|
763
862
|
case OP.OP_ROT:
|
|
@@ -793,6 +892,7 @@ export default class Spend {
|
|
|
793
892
|
)
|
|
794
893
|
}
|
|
795
894
|
this.stack.splice(this.stack.length - 2, 0, [...this.stacktop(-1)])
|
|
895
|
+
this.stackMem += this.stacktop(-1).length
|
|
796
896
|
break
|
|
797
897
|
|
|
798
898
|
case OP.OP_SIZE:
|
|
@@ -803,6 +903,7 @@ export default class Spend {
|
|
|
803
903
|
}
|
|
804
904
|
bn = new BigNumber(this.stacktop(-1).length)
|
|
805
905
|
this.stack.push(bn.toScriptNum())
|
|
906
|
+
this.stackMem += bn.toScriptNum().length
|
|
806
907
|
break
|
|
807
908
|
|
|
808
909
|
case OP.OP_AND:
|
|
@@ -841,7 +942,11 @@ export default class Spend {
|
|
|
841
942
|
}
|
|
842
943
|
|
|
843
944
|
// And pop vch2.
|
|
844
|
-
this.stack.pop()
|
|
945
|
+
poppedValue = this.stack.pop()
|
|
946
|
+
if (poppedValue != null) {
|
|
947
|
+
this.stackMem -= poppedValue.length
|
|
948
|
+
}
|
|
949
|
+
clearPoppedValue()
|
|
845
950
|
break
|
|
846
951
|
|
|
847
952
|
case OP.OP_INVERT:
|
|
@@ -865,7 +970,11 @@ export default class Spend {
|
|
|
865
970
|
}
|
|
866
971
|
buf1 = this.stacktop(-2)
|
|
867
972
|
if (buf1.length === 0) {
|
|
868
|
-
this.stack.pop()
|
|
973
|
+
poppedValue = this.stack.pop()
|
|
974
|
+
if (poppedValue != null) {
|
|
975
|
+
this.stackMem -= poppedValue.length
|
|
976
|
+
}
|
|
977
|
+
clearPoppedValue()
|
|
869
978
|
} else {
|
|
870
979
|
bn1 = new BigNumber(buf1)
|
|
871
980
|
bn2 = BigNumber.fromScriptNum(
|
|
@@ -878,8 +987,16 @@ export default class Spend {
|
|
|
878
987
|
`${OP[currentOpcode] as string} requires the top item on the stack not to be negative.`
|
|
879
988
|
)
|
|
880
989
|
}
|
|
881
|
-
this.stack.pop()
|
|
882
|
-
|
|
990
|
+
poppedValue = this.stack.pop()
|
|
991
|
+
if (poppedValue != null) {
|
|
992
|
+
this.stackMem -= poppedValue.length
|
|
993
|
+
}
|
|
994
|
+
clearPoppedValue()
|
|
995
|
+
poppedValue = this.stack.pop()
|
|
996
|
+
if (poppedValue != null) {
|
|
997
|
+
this.stackMem -= poppedValue.length
|
|
998
|
+
}
|
|
999
|
+
clearPoppedValue()
|
|
883
1000
|
let shifted
|
|
884
1001
|
if (currentOpcode === OP.OP_LSHIFT) {
|
|
885
1002
|
shifted = bn1.ushln(n)
|
|
@@ -892,6 +1009,7 @@ export default class Spend {
|
|
|
892
1009
|
buf1.length
|
|
893
1010
|
)
|
|
894
1011
|
this.stack.push(bufShifted)
|
|
1012
|
+
this.stackMem += bufShifted.length
|
|
895
1013
|
}
|
|
896
1014
|
break
|
|
897
1015
|
|
|
@@ -905,12 +1023,25 @@ export default class Spend {
|
|
|
905
1023
|
buf1 = this.stacktop(-2)
|
|
906
1024
|
buf2 = this.stacktop(-1)
|
|
907
1025
|
fEqual = toHex(buf1) === toHex(buf2)
|
|
908
|
-
this.stack.pop()
|
|
909
|
-
|
|
1026
|
+
poppedValue = this.stack.pop()
|
|
1027
|
+
if (poppedValue != null) {
|
|
1028
|
+
this.stackMem -= poppedValue.length
|
|
1029
|
+
}
|
|
1030
|
+
clearPoppedValue()
|
|
1031
|
+
poppedValue = this.stack.pop()
|
|
1032
|
+
if (poppedValue != null) {
|
|
1033
|
+
this.stackMem -= poppedValue.length
|
|
1034
|
+
}
|
|
1035
|
+
clearPoppedValue()
|
|
910
1036
|
this.stack.push(fEqual ? [1] : [])
|
|
1037
|
+
this.stackMem += (fEqual ? 1 : 0)
|
|
911
1038
|
if (currentOpcode === OP.OP_EQUALVERIFY) {
|
|
912
|
-
if (
|
|
913
|
-
this.stack.pop()
|
|
1039
|
+
if (this.castToBool(this.stacktop(-1))) {
|
|
1040
|
+
poppedValue = this.stack.pop()
|
|
1041
|
+
if (poppedValue != null) {
|
|
1042
|
+
this.stackMem -= poppedValue.length
|
|
1043
|
+
}
|
|
1044
|
+
clearPoppedValue()
|
|
914
1045
|
} else {
|
|
915
1046
|
this.scriptEvaluationError(
|
|
916
1047
|
'OP_EQUALVERIFY requires the top two stack items to be equal.'
|
|
@@ -954,8 +1085,13 @@ export default class Spend {
|
|
|
954
1085
|
bn = new BigNumber(bn.cmpn(0) !== 0 ? 1 : 0 + 0)
|
|
955
1086
|
break
|
|
956
1087
|
}
|
|
957
|
-
this.stack.pop()
|
|
1088
|
+
poppedValue = this.stack.pop()
|
|
1089
|
+
if (poppedValue != null) {
|
|
1090
|
+
this.stackMem -= poppedValue.length
|
|
1091
|
+
}
|
|
1092
|
+
clearPoppedValue()
|
|
958
1093
|
this.stack.push(bn.toScriptNum())
|
|
1094
|
+
this.stackMem += bn.toScriptNum().length
|
|
959
1095
|
break
|
|
960
1096
|
|
|
961
1097
|
case OP.OP_ADD:
|
|
@@ -1043,13 +1179,26 @@ export default class Spend {
|
|
|
1043
1179
|
bn = bn1.cmp(bn2) > 0 ? bn1 : bn2
|
|
1044
1180
|
break
|
|
1045
1181
|
}
|
|
1046
|
-
this.stack.pop()
|
|
1047
|
-
|
|
1182
|
+
poppedValue = this.stack.pop()
|
|
1183
|
+
if (poppedValue != null) {
|
|
1184
|
+
this.stackMem -= poppedValue.length
|
|
1185
|
+
}
|
|
1186
|
+
clearPoppedValue()
|
|
1187
|
+
poppedValue = this.stack.pop()
|
|
1188
|
+
if (poppedValue != null) {
|
|
1189
|
+
this.stackMem -= poppedValue.length
|
|
1190
|
+
}
|
|
1191
|
+
clearPoppedValue()
|
|
1048
1192
|
this.stack.push(bn.toScriptNum())
|
|
1193
|
+
this.stackMem += bn.toScriptNum().length
|
|
1049
1194
|
|
|
1050
1195
|
if (currentOpcode === OP.OP_NUMEQUALVERIFY) {
|
|
1051
1196
|
if (this.castToBool(this.stacktop(-1))) {
|
|
1052
|
-
this.stack.pop()
|
|
1197
|
+
poppedValue = this.stack.pop()
|
|
1198
|
+
if (poppedValue != null) {
|
|
1199
|
+
this.stackMem -= poppedValue.length
|
|
1200
|
+
}
|
|
1201
|
+
clearPoppedValue()
|
|
1053
1202
|
} else {
|
|
1054
1203
|
this.scriptEvaluationError(
|
|
1055
1204
|
'OP_NUMEQUALVERIFY requires the top stack item to be truthy.'
|
|
@@ -1068,10 +1217,23 @@ export default class Spend {
|
|
|
1068
1217
|
bn2 = BigNumber.fromScriptNum(this.stacktop(-2), requireMinimalPush)
|
|
1069
1218
|
bn3 = BigNumber.fromScriptNum(this.stacktop(-1), requireMinimalPush)
|
|
1070
1219
|
fValue = bn2.cmp(bn1) <= 0 && bn1.cmp(bn3) < 0
|
|
1071
|
-
this.stack.pop()
|
|
1072
|
-
|
|
1073
|
-
|
|
1220
|
+
poppedValue = this.stack.pop()
|
|
1221
|
+
if (poppedValue != null) {
|
|
1222
|
+
this.stackMem -= poppedValue.length
|
|
1223
|
+
}
|
|
1224
|
+
clearPoppedValue()
|
|
1225
|
+
poppedValue = this.stack.pop()
|
|
1226
|
+
if (poppedValue != null) {
|
|
1227
|
+
this.stackMem -= poppedValue.length
|
|
1228
|
+
}
|
|
1229
|
+
clearPoppedValue()
|
|
1230
|
+
poppedValue = this.stack.pop()
|
|
1231
|
+
if (poppedValue != null) {
|
|
1232
|
+
this.stackMem -= poppedValue.length
|
|
1233
|
+
}
|
|
1234
|
+
clearPoppedValue()
|
|
1074
1235
|
this.stack.push(fValue ? [1] : [])
|
|
1236
|
+
this.stackMem += (fValue ? 1 : 0)
|
|
1075
1237
|
break
|
|
1076
1238
|
|
|
1077
1239
|
case OP.OP_RIPEMD160:
|
|
@@ -1085,7 +1247,7 @@ export default class Spend {
|
|
|
1085
1247
|
)
|
|
1086
1248
|
}
|
|
1087
1249
|
|
|
1088
|
-
let bufHash: number[] = [] //
|
|
1250
|
+
let bufHash: number[] = [] // Initialize bufHash to an empty array
|
|
1089
1251
|
buf = this.stacktop(-1)
|
|
1090
1252
|
if (currentOpcode === OP.OP_RIPEMD160) {
|
|
1091
1253
|
bufHash = Hash.ripemd160(buf)
|
|
@@ -1099,8 +1261,13 @@ export default class Spend {
|
|
|
1099
1261
|
bufHash = Hash.hash256(buf)
|
|
1100
1262
|
}
|
|
1101
1263
|
|
|
1102
|
-
this.stack.pop()
|
|
1264
|
+
poppedValue = this.stack.pop()
|
|
1265
|
+
if (poppedValue != null) {
|
|
1266
|
+
this.stackMem -= poppedValue.length
|
|
1267
|
+
}
|
|
1268
|
+
clearPoppedValue()
|
|
1103
1269
|
this.stack.push(bufHash)
|
|
1270
|
+
this.stackMem += bufHash.length
|
|
1104
1271
|
break
|
|
1105
1272
|
}
|
|
1106
1273
|
|
|
@@ -1158,14 +1325,27 @@ export default class Spend {
|
|
|
1158
1325
|
)
|
|
1159
1326
|
}
|
|
1160
1327
|
|
|
1161
|
-
this.stack.pop()
|
|
1162
|
-
|
|
1328
|
+
poppedValue = this.stack.pop()
|
|
1329
|
+
if (poppedValue != null) {
|
|
1330
|
+
this.stackMem -= poppedValue.length
|
|
1331
|
+
}
|
|
1332
|
+
clearPoppedValue()
|
|
1333
|
+
poppedValue = this.stack.pop()
|
|
1334
|
+
if (poppedValue != null) {
|
|
1335
|
+
this.stackMem -= poppedValue.length
|
|
1336
|
+
}
|
|
1337
|
+
clearPoppedValue()
|
|
1163
1338
|
|
|
1164
1339
|
// stack.push_back(fSuccess ? vchTrue : vchFalse);
|
|
1165
1340
|
this.stack.push(fSuccess ? [1] : [])
|
|
1341
|
+
this.stackMem += (fSuccess ? 1 : 0)
|
|
1166
1342
|
if (currentOpcode === OP.OP_CHECKSIGVERIFY) {
|
|
1167
1343
|
if (fSuccess) {
|
|
1168
|
-
this.stack.pop()
|
|
1344
|
+
poppedValue = this.stack.pop()
|
|
1345
|
+
if (poppedValue != null) {
|
|
1346
|
+
this.stackMem -= poppedValue.length
|
|
1347
|
+
}
|
|
1348
|
+
clearPoppedValue()
|
|
1169
1349
|
} else {
|
|
1170
1350
|
this.scriptEvaluationError(
|
|
1171
1351
|
'OP_CHECKSIGVERIFY requires that a valid signature is provided.'
|
|
@@ -1293,7 +1473,11 @@ export default class Spend {
|
|
|
1293
1473
|
ikey2--
|
|
1294
1474
|
}
|
|
1295
1475
|
|
|
1296
|
-
this.stack.pop()
|
|
1476
|
+
poppedValue = this.stack.pop()
|
|
1477
|
+
if (poppedValue != null) {
|
|
1478
|
+
this.stackMem -= poppedValue.length
|
|
1479
|
+
}
|
|
1480
|
+
clearPoppedValue()
|
|
1297
1481
|
}
|
|
1298
1482
|
|
|
1299
1483
|
// A bug causes CHECKMULTISIG to consume one extra argument
|
|
@@ -1313,13 +1497,22 @@ export default class Spend {
|
|
|
1313
1497
|
`${OP[currentOpcode] as string} requires the extra stack item to be empty.`
|
|
1314
1498
|
)
|
|
1315
1499
|
}
|
|
1316
|
-
this.stack.pop()
|
|
1500
|
+
poppedValue = this.stack.pop()
|
|
1501
|
+
if (poppedValue != null) {
|
|
1502
|
+
this.stackMem -= poppedValue.length
|
|
1503
|
+
}
|
|
1504
|
+
clearPoppedValue()
|
|
1317
1505
|
|
|
1318
1506
|
this.stack.push(fSuccess ? [1] : [])
|
|
1507
|
+
this.stackMem += (fSuccess ? 1 : 0)
|
|
1319
1508
|
|
|
1320
1509
|
if (currentOpcode === OP.OP_CHECKMULTISIGVERIFY) {
|
|
1321
1510
|
if (fSuccess) {
|
|
1322
|
-
this.stack.pop()
|
|
1511
|
+
poppedValue = this.stack.pop()
|
|
1512
|
+
if (poppedValue != null) {
|
|
1513
|
+
this.stackMem -= poppedValue.length
|
|
1514
|
+
}
|
|
1515
|
+
clearPoppedValue()
|
|
1323
1516
|
} else {
|
|
1324
1517
|
this.scriptEvaluationError(
|
|
1325
1518
|
'OP_CHECKMULTISIGVERIFY requires that a sufficient number of valid signatures are provided.'
|
|
@@ -1343,7 +1536,14 @@ export default class Spend {
|
|
|
1343
1536
|
)
|
|
1344
1537
|
}
|
|
1345
1538
|
this.stack[this.stack.length - 2] = [...buf1, ...buf2]
|
|
1346
|
-
this.
|
|
1539
|
+
this.stackMem -= buf1.length
|
|
1540
|
+
this.stackMem -= buf2.length
|
|
1541
|
+
this.stackMem += buf1.length + buf2.length
|
|
1542
|
+
poppedValue = this.stack.pop()
|
|
1543
|
+
if (poppedValue != null) {
|
|
1544
|
+
this.stackMem -= poppedValue.length
|
|
1545
|
+
}
|
|
1546
|
+
clearPoppedValue()
|
|
1347
1547
|
break
|
|
1348
1548
|
|
|
1349
1549
|
case OP.OP_SPLIT:
|
|
@@ -1372,7 +1572,10 @@ export default class Spend {
|
|
|
1372
1572
|
|
|
1373
1573
|
// Replace existing stack values by the new values.
|
|
1374
1574
|
this.stack[this.stack.length - 2] = buf2.slice(0, n)
|
|
1575
|
+
this.stackMem -= buf1.length
|
|
1576
|
+
this.stackMem += n
|
|
1375
1577
|
this.stack[this.stack.length - 1] = buf2.slice(n)
|
|
1578
|
+
this.stackMem += buf1.length - n
|
|
1376
1579
|
break
|
|
1377
1580
|
|
|
1378
1581
|
case OP.OP_NUM2BIN:
|
|
@@ -1392,7 +1595,11 @@ export default class Spend {
|
|
|
1392
1595
|
)
|
|
1393
1596
|
}
|
|
1394
1597
|
|
|
1395
|
-
this.stack.pop()
|
|
1598
|
+
poppedValue = this.stack.pop()
|
|
1599
|
+
if (poppedValue != null) {
|
|
1600
|
+
this.stackMem -= poppedValue.length
|
|
1601
|
+
}
|
|
1602
|
+
clearPoppedValue()
|
|
1396
1603
|
rawnum = this.stacktop(-1)
|
|
1397
1604
|
|
|
1398
1605
|
// Try to see if we can fit that number in the number of
|
|
@@ -1431,6 +1638,8 @@ export default class Spend {
|
|
|
1431
1638
|
num[n] = signbit
|
|
1432
1639
|
|
|
1433
1640
|
this.stack[this.stack.length - 1] = num
|
|
1641
|
+
this.stackMem -= rawnum.length
|
|
1642
|
+
this.stackMem += size
|
|
1434
1643
|
break
|
|
1435
1644
|
|
|
1436
1645
|
case OP.OP_BIN2NUM:
|
|
@@ -1444,6 +1653,8 @@ export default class Spend {
|
|
|
1444
1653
|
buf2 = minimallyEncode(buf1)
|
|
1445
1654
|
|
|
1446
1655
|
this.stack[this.stack.length - 1] = buf2
|
|
1656
|
+
this.stackMem -= buf1.length
|
|
1657
|
+
this.stackMem += buf2.length
|
|
1447
1658
|
|
|
1448
1659
|
// The resulting number must be a valid number.
|
|
1449
1660
|
if (!isMinimallyEncoded(buf2)) {
|
|
@@ -1460,6 +1671,7 @@ export default class Spend {
|
|
|
1460
1671
|
|
|
1461
1672
|
// Finally, increment the program counter
|
|
1462
1673
|
this.programCounter++
|
|
1674
|
+
return true
|
|
1463
1675
|
}
|
|
1464
1676
|
|
|
1465
1677
|
/**
|
|
@@ -1479,8 +1691,7 @@ export default class Spend {
|
|
|
1479
1691
|
'Unlocking scripts can only contain push operations, and no other opcodes.'
|
|
1480
1692
|
)
|
|
1481
1693
|
}
|
|
1482
|
-
while (
|
|
1483
|
-
this.step()
|
|
1694
|
+
while (this.step()) {
|
|
1484
1695
|
if (
|
|
1485
1696
|
this.context === 'LockingScript' &&
|
|
1486
1697
|
this.programCounter >= this.lockingScript.chunks.length
|