@bsv/sdk 1.4.19 → 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/src/wallet/substrates/index.js +3 -1
- package/dist/cjs/src/wallet/substrates/index.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/src/wallet/substrates/index.js +1 -0
- package/dist/esm/src/wallet/substrates/index.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/src/wallet/substrates/index.d.ts +1 -0
- package/dist/types/src/wallet/substrates/index.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/src/wallet/substrates/index.ts +1 -0
|
@@ -31,6 +31,7 @@ const requireCleanStack = true;
|
|
|
31
31
|
* @property {number} inputIndex - The index of this input in the current transaction.
|
|
32
32
|
* @property {UnlockingScript} unlockingScript - The unlocking script that unlocks the UTXO for spending.
|
|
33
33
|
* @property {number} inputSequence - The sequence number of this input.
|
|
34
|
+
* @property {number} lockTime - The lock time of the transaction.
|
|
34
35
|
*/
|
|
35
36
|
export default class Spend {
|
|
36
37
|
sourceTXID;
|
|
@@ -50,6 +51,9 @@ export default class Spend {
|
|
|
50
51
|
stack;
|
|
51
52
|
altStack;
|
|
52
53
|
ifStack;
|
|
54
|
+
memoryLimit;
|
|
55
|
+
stackMem;
|
|
56
|
+
altStackMem;
|
|
53
57
|
/**
|
|
54
58
|
* @constructor
|
|
55
59
|
* Constructs the Spend object with necessary transaction details.
|
|
@@ -79,6 +83,7 @@ export default class Spend {
|
|
|
79
83
|
* inputIndex: 0, // inputIndex
|
|
80
84
|
* unlockingScript: UnlockingScript.fromASM("3045... 02ab..."),
|
|
81
85
|
* inputSequence: 0xffffffff // inputSequence
|
|
86
|
+
* memoryLimit: 100000 // memoryLimit
|
|
82
87
|
* });
|
|
83
88
|
*/
|
|
84
89
|
constructor(params) {
|
|
@@ -93,6 +98,9 @@ export default class Spend {
|
|
|
93
98
|
this.unlockingScript = params.unlockingScript;
|
|
94
99
|
this.inputSequence = params.inputSequence;
|
|
95
100
|
this.lockTime = params.lockTime;
|
|
101
|
+
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.
|
|
102
|
+
this.stackMem = 0;
|
|
103
|
+
this.altStackMem = 0;
|
|
96
104
|
this.reset();
|
|
97
105
|
}
|
|
98
106
|
reset() {
|
|
@@ -102,8 +110,24 @@ export default class Spend {
|
|
|
102
110
|
this.stack = [];
|
|
103
111
|
this.altStack = [];
|
|
104
112
|
this.ifStack = [];
|
|
113
|
+
this.stackMem = 0;
|
|
114
|
+
this.altStackMem = 0;
|
|
105
115
|
}
|
|
106
116
|
step() {
|
|
117
|
+
let poppedValue;
|
|
118
|
+
// If the stack (or alt stack) is over the memory limit, evaluation has failed.
|
|
119
|
+
if (this.stackMem > this.memoryLimit) {
|
|
120
|
+
this.scriptEvaluationError('Stack memory usage has exceeded ' + String(this.memoryLimit) + ' bytes');
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
if (this.altStackMem > this.memoryLimit) {
|
|
124
|
+
this.scriptEvaluationError('Alt stack memory usage has exceeded ' + String(this.memoryLimit) + ' bytes');
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
// Clear popped values after use to free memory
|
|
128
|
+
const clearPoppedValue = () => {
|
|
129
|
+
poppedValue = undefined;
|
|
130
|
+
};
|
|
107
131
|
// If the context is UnlockingScript and we have reached the end,
|
|
108
132
|
// set the context to LockingScript and zero the program counter
|
|
109
133
|
if (this.context === 'UnlockingScript' &&
|
|
@@ -233,6 +257,10 @@ export default class Spend {
|
|
|
233
257
|
// Non-canonical signature: R value type mismatch
|
|
234
258
|
return false;
|
|
235
259
|
}
|
|
260
|
+
if (buf[4 - 1] !== nLEnR) {
|
|
261
|
+
// Non-canonical signature: R length mismatch
|
|
262
|
+
return false;
|
|
263
|
+
}
|
|
236
264
|
if (nLEnR === 0) {
|
|
237
265
|
// Non-canonical signature: R length is zero
|
|
238
266
|
return false;
|
|
@@ -250,6 +278,10 @@ export default class Spend {
|
|
|
250
278
|
// Non-canonical signature: S value type mismatch
|
|
251
279
|
return false;
|
|
252
280
|
}
|
|
281
|
+
if (buf[6 + nLEnR - 1] !== nLEnS) {
|
|
282
|
+
// Non-canonical signature: S length mismatch
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
253
285
|
if (nLEnS === 0) {
|
|
254
286
|
// Non-canonical signature: S length is zero
|
|
255
287
|
return false;
|
|
@@ -343,9 +375,11 @@ export default class Spend {
|
|
|
343
375
|
}
|
|
344
376
|
if (!Array.isArray(operation.data)) {
|
|
345
377
|
this.stack.push([]);
|
|
378
|
+
this.stackMem += 0;
|
|
346
379
|
}
|
|
347
380
|
else {
|
|
348
381
|
this.stack.push(operation.data);
|
|
382
|
+
this.stackMem += operation.data.length;
|
|
349
383
|
}
|
|
350
384
|
}
|
|
351
385
|
else if (isScriptExecuting ||
|
|
@@ -371,6 +405,7 @@ export default class Spend {
|
|
|
371
405
|
n = currentOpcode - (OP.OP_1 - 1);
|
|
372
406
|
buf = new BigNumber(n).toScriptNum();
|
|
373
407
|
this.stack.push(buf);
|
|
408
|
+
this.stackMem += buf.length;
|
|
374
409
|
break;
|
|
375
410
|
case OP.OP_NOP:
|
|
376
411
|
case OP.OP_NOP2:
|
|
@@ -460,7 +495,11 @@ export default class Spend {
|
|
|
460
495
|
if (currentOpcode === OP.OP_NOTIF) {
|
|
461
496
|
fValue = !fValue;
|
|
462
497
|
}
|
|
463
|
-
this.stack.pop();
|
|
498
|
+
poppedValue = this.stack.pop();
|
|
499
|
+
if (poppedValue != null) {
|
|
500
|
+
this.stackMem -= poppedValue.length;
|
|
501
|
+
}
|
|
502
|
+
clearPoppedValue();
|
|
464
503
|
}
|
|
465
504
|
this.ifStack.push(fValue);
|
|
466
505
|
break;
|
|
@@ -483,10 +522,12 @@ export default class Spend {
|
|
|
483
522
|
}
|
|
484
523
|
buf = this.stacktop(-1);
|
|
485
524
|
fValue = this.castToBool(buf);
|
|
486
|
-
|
|
487
|
-
|
|
525
|
+
poppedValue = this.stack.pop();
|
|
526
|
+
if (poppedValue != null) {
|
|
527
|
+
this.stackMem -= poppedValue.length;
|
|
488
528
|
}
|
|
489
|
-
|
|
529
|
+
clearPoppedValue();
|
|
530
|
+
if (!fValue) {
|
|
490
531
|
this.scriptEvaluationError('OP_VERIFY requires the top stack value to be truthy.');
|
|
491
532
|
}
|
|
492
533
|
break;
|
|
@@ -503,20 +544,40 @@ export default class Spend {
|
|
|
503
544
|
if (this.stack.length < 1) {
|
|
504
545
|
this.scriptEvaluationError('OP_TOALTSTACK requires at oeast one item to be on the stack.');
|
|
505
546
|
}
|
|
506
|
-
this.
|
|
547
|
+
poppedValue = this.stack.pop();
|
|
548
|
+
if (poppedValue != null) {
|
|
549
|
+
this.altStack.push(poppedValue);
|
|
550
|
+
this.altStackMem += poppedValue.length;
|
|
551
|
+
this.stackMem -= poppedValue.length;
|
|
552
|
+
}
|
|
553
|
+
clearPoppedValue();
|
|
507
554
|
break;
|
|
508
555
|
case OP.OP_FROMALTSTACK:
|
|
509
556
|
if (this.altStack.length < 1) {
|
|
510
557
|
this.scriptEvaluationError('OP_FROMALTSTACK requires at least one item to be on the stack.');
|
|
511
558
|
}
|
|
512
|
-
this.
|
|
559
|
+
poppedValue = this.altStack.pop();
|
|
560
|
+
if (poppedValue != null) {
|
|
561
|
+
this.stack.push(poppedValue);
|
|
562
|
+
this.stackMem += poppedValue.length;
|
|
563
|
+
this.altStackMem -= poppedValue.length;
|
|
564
|
+
}
|
|
565
|
+
clearPoppedValue();
|
|
513
566
|
break;
|
|
514
567
|
case OP.OP_2DROP:
|
|
515
568
|
if (this.stack.length < 2) {
|
|
516
569
|
this.scriptEvaluationError('OP_2DROP requires at least two items to be on the stack.');
|
|
517
570
|
}
|
|
518
|
-
this.stack.pop();
|
|
519
|
-
|
|
571
|
+
poppedValue = this.stack.pop();
|
|
572
|
+
if (poppedValue != null) {
|
|
573
|
+
this.stackMem -= poppedValue.length;
|
|
574
|
+
}
|
|
575
|
+
clearPoppedValue();
|
|
576
|
+
poppedValue = this.stack.pop();
|
|
577
|
+
if (poppedValue != null) {
|
|
578
|
+
this.stackMem -= poppedValue.length;
|
|
579
|
+
}
|
|
580
|
+
clearPoppedValue();
|
|
520
581
|
break;
|
|
521
582
|
case OP.OP_2DUP:
|
|
522
583
|
if (this.stack.length < 2) {
|
|
@@ -525,7 +586,9 @@ export default class Spend {
|
|
|
525
586
|
buf1 = this.stacktop(-2);
|
|
526
587
|
buf2 = this.stacktop(-1);
|
|
527
588
|
this.stack.push([...buf1]);
|
|
589
|
+
this.stackMem += buf1.length;
|
|
528
590
|
this.stack.push([...buf2]);
|
|
591
|
+
this.stackMem += buf2.length;
|
|
529
592
|
break;
|
|
530
593
|
case OP.OP_3DUP:
|
|
531
594
|
if (this.stack.length < 3) {
|
|
@@ -535,8 +598,11 @@ export default class Spend {
|
|
|
535
598
|
buf2 = this.stacktop(-2);
|
|
536
599
|
buf3 = this.stacktop(-1);
|
|
537
600
|
this.stack.push([...buf1]);
|
|
601
|
+
this.stackMem += buf1.length;
|
|
538
602
|
this.stack.push([...buf2]);
|
|
603
|
+
this.stackMem += buf2.length;
|
|
539
604
|
this.stack.push([...buf3]);
|
|
605
|
+
this.stackMem += buf3.length;
|
|
540
606
|
break;
|
|
541
607
|
case OP.OP_2OVER:
|
|
542
608
|
if (this.stack.length < 4) {
|
|
@@ -545,7 +611,9 @@ export default class Spend {
|
|
|
545
611
|
buf1 = this.stacktop(-4);
|
|
546
612
|
buf2 = this.stacktop(-3);
|
|
547
613
|
this.stack.push([...buf1]);
|
|
614
|
+
this.stackMem += buf1.length;
|
|
548
615
|
this.stack.push([...buf2]);
|
|
616
|
+
this.stackMem += buf2.length;
|
|
549
617
|
break;
|
|
550
618
|
case OP.OP_2ROT:
|
|
551
619
|
if (this.stack.length < 6) {
|
|
@@ -553,7 +621,9 @@ export default class Spend {
|
|
|
553
621
|
}
|
|
554
622
|
spliced = this.stack.splice(this.stack.length - 6, 2);
|
|
555
623
|
this.stack.push(spliced[0]);
|
|
624
|
+
this.stackMem += spliced[0].length;
|
|
556
625
|
this.stack.push(spliced[1]);
|
|
626
|
+
this.stackMem += spliced[1].length;
|
|
557
627
|
break;
|
|
558
628
|
case OP.OP_2SWAP:
|
|
559
629
|
if (this.stack.length < 4) {
|
|
@@ -561,7 +631,9 @@ export default class Spend {
|
|
|
561
631
|
}
|
|
562
632
|
spliced = this.stack.splice(this.stack.length - 4, 2);
|
|
563
633
|
this.stack.push(spliced[0]);
|
|
634
|
+
this.stackMem += spliced[0].length;
|
|
564
635
|
this.stack.push(spliced[1]);
|
|
636
|
+
this.stackMem += spliced[1].length;
|
|
565
637
|
break;
|
|
566
638
|
case OP.OP_IFDUP:
|
|
567
639
|
if (this.stack.length < 1) {
|
|
@@ -571,35 +643,44 @@ export default class Spend {
|
|
|
571
643
|
fValue = this.castToBool(buf);
|
|
572
644
|
if (fValue) {
|
|
573
645
|
this.stack.push([...buf]);
|
|
646
|
+
this.stackMem += buf.length;
|
|
574
647
|
}
|
|
575
648
|
break;
|
|
576
649
|
case OP.OP_DEPTH:
|
|
577
650
|
buf = new BigNumber(this.stack.length).toScriptNum();
|
|
578
651
|
this.stack.push(buf);
|
|
652
|
+
this.stackMem += buf.length;
|
|
579
653
|
break;
|
|
580
654
|
case OP.OP_DROP:
|
|
581
655
|
if (this.stack.length < 1) {
|
|
582
656
|
this.scriptEvaluationError('OP_DROP requires at least one item to be on the stack.');
|
|
583
657
|
}
|
|
584
|
-
this.stack.pop();
|
|
658
|
+
poppedValue = this.stack.pop();
|
|
659
|
+
if (poppedValue != null) {
|
|
660
|
+
this.stackMem -= poppedValue.length;
|
|
661
|
+
}
|
|
662
|
+
clearPoppedValue();
|
|
585
663
|
break;
|
|
586
664
|
case OP.OP_DUP:
|
|
587
665
|
if (this.stack.length < 1) {
|
|
588
666
|
this.scriptEvaluationError('OP_DUP requires at least one item to be on the stack.');
|
|
589
667
|
}
|
|
590
668
|
this.stack.push([...this.stacktop(-1)]);
|
|
669
|
+
this.stackMem += this.stacktop(-1).length;
|
|
591
670
|
break;
|
|
592
671
|
case OP.OP_NIP:
|
|
593
672
|
if (this.stack.length < 2) {
|
|
594
673
|
this.scriptEvaluationError('OP_NIP requires at least two items to be on the stack.');
|
|
595
674
|
}
|
|
596
675
|
this.stack.splice(this.stack.length - 2, 1);
|
|
676
|
+
this.stackMem -= this.stacktop(-1).length;
|
|
597
677
|
break;
|
|
598
678
|
case OP.OP_OVER:
|
|
599
679
|
if (this.stack.length < 2) {
|
|
600
680
|
this.scriptEvaluationError('OP_OVER requires at least two items to be on the stack.');
|
|
601
681
|
}
|
|
602
682
|
this.stack.push([...this.stacktop(-2)]);
|
|
683
|
+
this.stackMem += this.stacktop(-2).length;
|
|
603
684
|
break;
|
|
604
685
|
case OP.OP_PICK:
|
|
605
686
|
case OP.OP_ROLL:
|
|
@@ -609,15 +690,21 @@ export default class Spend {
|
|
|
609
690
|
buf = this.stacktop(-1);
|
|
610
691
|
bn = BigNumber.fromScriptNum(buf, requireMinimalPush);
|
|
611
692
|
n = bn.toNumber();
|
|
612
|
-
this.stack.pop();
|
|
693
|
+
poppedValue = this.stack.pop();
|
|
694
|
+
if (poppedValue != null) {
|
|
695
|
+
this.stackMem -= poppedValue.length;
|
|
696
|
+
}
|
|
697
|
+
clearPoppedValue();
|
|
613
698
|
if (n < 0 || n >= this.stack.length) {
|
|
614
699
|
this.scriptEvaluationError(`${OP[currentOpcode]} requires the top stack element to be 0 or a positive number less than the current size of the stack.`);
|
|
615
700
|
}
|
|
616
701
|
buf = this.stacktop(-n - 1);
|
|
617
702
|
if (currentOpcode === OP.OP_ROLL) {
|
|
618
703
|
this.stack.splice(this.stack.length - n - 1, 1);
|
|
704
|
+
this.stackMem -= buf.length;
|
|
619
705
|
}
|
|
620
706
|
this.stack.push([...buf]);
|
|
707
|
+
this.stackMem += buf.length;
|
|
621
708
|
break;
|
|
622
709
|
case OP.OP_ROT:
|
|
623
710
|
if (this.stack.length < 3) {
|
|
@@ -644,6 +731,7 @@ export default class Spend {
|
|
|
644
731
|
this.scriptEvaluationError('OP_TUCK requires at least two items to be on the stack.');
|
|
645
732
|
}
|
|
646
733
|
this.stack.splice(this.stack.length - 2, 0, [...this.stacktop(-1)]);
|
|
734
|
+
this.stackMem += this.stacktop(-1).length;
|
|
647
735
|
break;
|
|
648
736
|
case OP.OP_SIZE:
|
|
649
737
|
if (this.stack.length < 1) {
|
|
@@ -651,6 +739,7 @@ export default class Spend {
|
|
|
651
739
|
}
|
|
652
740
|
bn = new BigNumber(this.stacktop(-1).length);
|
|
653
741
|
this.stack.push(bn.toScriptNum());
|
|
742
|
+
this.stackMem += bn.toScriptNum().length;
|
|
654
743
|
break;
|
|
655
744
|
case OP.OP_AND:
|
|
656
745
|
case OP.OP_OR:
|
|
@@ -681,7 +770,11 @@ export default class Spend {
|
|
|
681
770
|
break;
|
|
682
771
|
}
|
|
683
772
|
// And pop vch2.
|
|
684
|
-
this.stack.pop();
|
|
773
|
+
poppedValue = this.stack.pop();
|
|
774
|
+
if (poppedValue != null) {
|
|
775
|
+
this.stackMem -= poppedValue.length;
|
|
776
|
+
}
|
|
777
|
+
clearPoppedValue();
|
|
685
778
|
break;
|
|
686
779
|
case OP.OP_INVERT:
|
|
687
780
|
if (this.stack.length < 1) {
|
|
@@ -699,7 +792,11 @@ export default class Spend {
|
|
|
699
792
|
}
|
|
700
793
|
buf1 = this.stacktop(-2);
|
|
701
794
|
if (buf1.length === 0) {
|
|
702
|
-
this.stack.pop();
|
|
795
|
+
poppedValue = this.stack.pop();
|
|
796
|
+
if (poppedValue != null) {
|
|
797
|
+
this.stackMem -= poppedValue.length;
|
|
798
|
+
}
|
|
799
|
+
clearPoppedValue();
|
|
703
800
|
}
|
|
704
801
|
else {
|
|
705
802
|
bn1 = new BigNumber(buf1);
|
|
@@ -708,8 +805,16 @@ export default class Spend {
|
|
|
708
805
|
if (n < 0) {
|
|
709
806
|
this.scriptEvaluationError(`${OP[currentOpcode]} requires the top item on the stack not to be negative.`);
|
|
710
807
|
}
|
|
711
|
-
this.stack.pop();
|
|
712
|
-
|
|
808
|
+
poppedValue = this.stack.pop();
|
|
809
|
+
if (poppedValue != null) {
|
|
810
|
+
this.stackMem -= poppedValue.length;
|
|
811
|
+
}
|
|
812
|
+
clearPoppedValue();
|
|
813
|
+
poppedValue = this.stack.pop();
|
|
814
|
+
if (poppedValue != null) {
|
|
815
|
+
this.stackMem -= poppedValue.length;
|
|
816
|
+
}
|
|
817
|
+
clearPoppedValue();
|
|
713
818
|
let shifted;
|
|
714
819
|
if (currentOpcode === OP.OP_LSHIFT) {
|
|
715
820
|
shifted = bn1.ushln(n);
|
|
@@ -719,6 +824,7 @@ export default class Spend {
|
|
|
719
824
|
}
|
|
720
825
|
const bufShifted = padDataToSize(shifted.toArray().slice(buf1.length * -1), buf1.length);
|
|
721
826
|
this.stack.push(bufShifted);
|
|
827
|
+
this.stackMem += bufShifted.length;
|
|
722
828
|
}
|
|
723
829
|
break;
|
|
724
830
|
case OP.OP_EQUAL:
|
|
@@ -729,12 +835,25 @@ export default class Spend {
|
|
|
729
835
|
buf1 = this.stacktop(-2);
|
|
730
836
|
buf2 = this.stacktop(-1);
|
|
731
837
|
fEqual = toHex(buf1) === toHex(buf2);
|
|
732
|
-
this.stack.pop();
|
|
733
|
-
|
|
838
|
+
poppedValue = this.stack.pop();
|
|
839
|
+
if (poppedValue != null) {
|
|
840
|
+
this.stackMem -= poppedValue.length;
|
|
841
|
+
}
|
|
842
|
+
clearPoppedValue();
|
|
843
|
+
poppedValue = this.stack.pop();
|
|
844
|
+
if (poppedValue != null) {
|
|
845
|
+
this.stackMem -= poppedValue.length;
|
|
846
|
+
}
|
|
847
|
+
clearPoppedValue();
|
|
734
848
|
this.stack.push(fEqual ? [1] : []);
|
|
849
|
+
this.stackMem += (fEqual ? 1 : 0);
|
|
735
850
|
if (currentOpcode === OP.OP_EQUALVERIFY) {
|
|
736
|
-
if (
|
|
737
|
-
this.stack.pop();
|
|
851
|
+
if (this.castToBool(this.stacktop(-1))) {
|
|
852
|
+
poppedValue = this.stack.pop();
|
|
853
|
+
if (poppedValue != null) {
|
|
854
|
+
this.stackMem -= poppedValue.length;
|
|
855
|
+
}
|
|
856
|
+
clearPoppedValue();
|
|
738
857
|
}
|
|
739
858
|
else {
|
|
740
859
|
this.scriptEvaluationError('OP_EQUALVERIFY requires the top two stack items to be equal.');
|
|
@@ -774,8 +893,13 @@ export default class Spend {
|
|
|
774
893
|
bn = new BigNumber(bn.cmpn(0) !== 0 ? 1 : 0 + 0);
|
|
775
894
|
break;
|
|
776
895
|
}
|
|
777
|
-
this.stack.pop();
|
|
896
|
+
poppedValue = this.stack.pop();
|
|
897
|
+
if (poppedValue != null) {
|
|
898
|
+
this.stackMem -= poppedValue.length;
|
|
899
|
+
}
|
|
900
|
+
clearPoppedValue();
|
|
778
901
|
this.stack.push(bn.toScriptNum());
|
|
902
|
+
this.stackMem += bn.toScriptNum().length;
|
|
779
903
|
break;
|
|
780
904
|
case OP.OP_ADD:
|
|
781
905
|
case OP.OP_SUB:
|
|
@@ -855,12 +979,25 @@ export default class Spend {
|
|
|
855
979
|
bn = bn1.cmp(bn2) > 0 ? bn1 : bn2;
|
|
856
980
|
break;
|
|
857
981
|
}
|
|
858
|
-
this.stack.pop();
|
|
859
|
-
|
|
982
|
+
poppedValue = this.stack.pop();
|
|
983
|
+
if (poppedValue != null) {
|
|
984
|
+
this.stackMem -= poppedValue.length;
|
|
985
|
+
}
|
|
986
|
+
clearPoppedValue();
|
|
987
|
+
poppedValue = this.stack.pop();
|
|
988
|
+
if (poppedValue != null) {
|
|
989
|
+
this.stackMem -= poppedValue.length;
|
|
990
|
+
}
|
|
991
|
+
clearPoppedValue();
|
|
860
992
|
this.stack.push(bn.toScriptNum());
|
|
993
|
+
this.stackMem += bn.toScriptNum().length;
|
|
861
994
|
if (currentOpcode === OP.OP_NUMEQUALVERIFY) {
|
|
862
995
|
if (this.castToBool(this.stacktop(-1))) {
|
|
863
|
-
this.stack.pop();
|
|
996
|
+
poppedValue = this.stack.pop();
|
|
997
|
+
if (poppedValue != null) {
|
|
998
|
+
this.stackMem -= poppedValue.length;
|
|
999
|
+
}
|
|
1000
|
+
clearPoppedValue();
|
|
864
1001
|
}
|
|
865
1002
|
else {
|
|
866
1003
|
this.scriptEvaluationError('OP_NUMEQUALVERIFY requires the top stack item to be truthy.');
|
|
@@ -875,10 +1012,23 @@ export default class Spend {
|
|
|
875
1012
|
bn2 = BigNumber.fromScriptNum(this.stacktop(-2), requireMinimalPush);
|
|
876
1013
|
bn3 = BigNumber.fromScriptNum(this.stacktop(-1), requireMinimalPush);
|
|
877
1014
|
fValue = bn2.cmp(bn1) <= 0 && bn1.cmp(bn3) < 0;
|
|
878
|
-
this.stack.pop();
|
|
879
|
-
|
|
880
|
-
|
|
1015
|
+
poppedValue = this.stack.pop();
|
|
1016
|
+
if (poppedValue != null) {
|
|
1017
|
+
this.stackMem -= poppedValue.length;
|
|
1018
|
+
}
|
|
1019
|
+
clearPoppedValue();
|
|
1020
|
+
poppedValue = this.stack.pop();
|
|
1021
|
+
if (poppedValue != null) {
|
|
1022
|
+
this.stackMem -= poppedValue.length;
|
|
1023
|
+
}
|
|
1024
|
+
clearPoppedValue();
|
|
1025
|
+
poppedValue = this.stack.pop();
|
|
1026
|
+
if (poppedValue != null) {
|
|
1027
|
+
this.stackMem -= poppedValue.length;
|
|
1028
|
+
}
|
|
1029
|
+
clearPoppedValue();
|
|
881
1030
|
this.stack.push(fValue ? [1] : []);
|
|
1031
|
+
this.stackMem += (fValue ? 1 : 0);
|
|
882
1032
|
break;
|
|
883
1033
|
case OP.OP_RIPEMD160:
|
|
884
1034
|
case OP.OP_SHA1:
|
|
@@ -888,7 +1038,7 @@ export default class Spend {
|
|
|
888
1038
|
if (this.stack.length < 1) {
|
|
889
1039
|
this.scriptEvaluationError(`${OP[currentOpcode]} requires at least one item to be on the stack.`);
|
|
890
1040
|
}
|
|
891
|
-
let bufHash = []; //
|
|
1041
|
+
let bufHash = []; // Initialize bufHash to an empty array
|
|
892
1042
|
buf = this.stacktop(-1);
|
|
893
1043
|
if (currentOpcode === OP.OP_RIPEMD160) {
|
|
894
1044
|
bufHash = Hash.ripemd160(buf);
|
|
@@ -905,8 +1055,13 @@ export default class Spend {
|
|
|
905
1055
|
else if (currentOpcode === OP.OP_HASH256) {
|
|
906
1056
|
bufHash = Hash.hash256(buf);
|
|
907
1057
|
}
|
|
908
|
-
this.stack.pop();
|
|
1058
|
+
poppedValue = this.stack.pop();
|
|
1059
|
+
if (poppedValue != null) {
|
|
1060
|
+
this.stackMem -= poppedValue.length;
|
|
1061
|
+
}
|
|
1062
|
+
clearPoppedValue();
|
|
909
1063
|
this.stack.push(bufHash);
|
|
1064
|
+
this.stackMem += bufHash.length;
|
|
910
1065
|
break;
|
|
911
1066
|
}
|
|
912
1067
|
case OP.OP_CODESEPARATOR:
|
|
@@ -945,13 +1100,26 @@ export default class Spend {
|
|
|
945
1100
|
if (!fSuccess && bufSig.length > 0) {
|
|
946
1101
|
this.scriptEvaluationError(`${OP[currentOpcode]} failed to verify the signature, and requires an empty signature when verification fails.`);
|
|
947
1102
|
}
|
|
948
|
-
this.stack.pop();
|
|
949
|
-
|
|
1103
|
+
poppedValue = this.stack.pop();
|
|
1104
|
+
if (poppedValue != null) {
|
|
1105
|
+
this.stackMem -= poppedValue.length;
|
|
1106
|
+
}
|
|
1107
|
+
clearPoppedValue();
|
|
1108
|
+
poppedValue = this.stack.pop();
|
|
1109
|
+
if (poppedValue != null) {
|
|
1110
|
+
this.stackMem -= poppedValue.length;
|
|
1111
|
+
}
|
|
1112
|
+
clearPoppedValue();
|
|
950
1113
|
// stack.push_back(fSuccess ? vchTrue : vchFalse);
|
|
951
1114
|
this.stack.push(fSuccess ? [1] : []);
|
|
1115
|
+
this.stackMem += (fSuccess ? 1 : 0);
|
|
952
1116
|
if (currentOpcode === OP.OP_CHECKSIGVERIFY) {
|
|
953
1117
|
if (fSuccess) {
|
|
954
|
-
this.stack.pop();
|
|
1118
|
+
poppedValue = this.stack.pop();
|
|
1119
|
+
if (poppedValue != null) {
|
|
1120
|
+
this.stackMem -= poppedValue.length;
|
|
1121
|
+
}
|
|
1122
|
+
clearPoppedValue();
|
|
955
1123
|
}
|
|
956
1124
|
else {
|
|
957
1125
|
this.scriptEvaluationError('OP_CHECKSIGVERIFY requires that a valid signature is provided.');
|
|
@@ -1039,7 +1207,11 @@ export default class Spend {
|
|
|
1039
1207
|
if (ikey2 > 0) {
|
|
1040
1208
|
ikey2--;
|
|
1041
1209
|
}
|
|
1042
|
-
this.stack.pop();
|
|
1210
|
+
poppedValue = this.stack.pop();
|
|
1211
|
+
if (poppedValue != null) {
|
|
1212
|
+
this.stackMem -= poppedValue.length;
|
|
1213
|
+
}
|
|
1214
|
+
clearPoppedValue();
|
|
1043
1215
|
}
|
|
1044
1216
|
// A bug causes CHECKMULTISIG to consume one extra argument
|
|
1045
1217
|
// whose contents were not checked in any way.
|
|
@@ -1054,11 +1226,20 @@ export default class Spend {
|
|
|
1054
1226
|
// NOTE: Is this necessary? We don't care about malleability.
|
|
1055
1227
|
this.scriptEvaluationError(`${OP[currentOpcode]} requires the extra stack item to be empty.`);
|
|
1056
1228
|
}
|
|
1057
|
-
this.stack.pop();
|
|
1229
|
+
poppedValue = this.stack.pop();
|
|
1230
|
+
if (poppedValue != null) {
|
|
1231
|
+
this.stackMem -= poppedValue.length;
|
|
1232
|
+
}
|
|
1233
|
+
clearPoppedValue();
|
|
1058
1234
|
this.stack.push(fSuccess ? [1] : []);
|
|
1235
|
+
this.stackMem += (fSuccess ? 1 : 0);
|
|
1059
1236
|
if (currentOpcode === OP.OP_CHECKMULTISIGVERIFY) {
|
|
1060
1237
|
if (fSuccess) {
|
|
1061
|
-
this.stack.pop();
|
|
1238
|
+
poppedValue = this.stack.pop();
|
|
1239
|
+
if (poppedValue != null) {
|
|
1240
|
+
this.stackMem -= poppedValue.length;
|
|
1241
|
+
}
|
|
1242
|
+
clearPoppedValue();
|
|
1062
1243
|
}
|
|
1063
1244
|
else {
|
|
1064
1245
|
this.scriptEvaluationError('OP_CHECKMULTISIGVERIFY requires that a sufficient number of valid signatures are provided.');
|
|
@@ -1075,7 +1256,14 @@ export default class Spend {
|
|
|
1075
1256
|
this.scriptEvaluationError(`It's not currently possible to push data larger than ${maxScriptElementSize} bytes.`);
|
|
1076
1257
|
}
|
|
1077
1258
|
this.stack[this.stack.length - 2] = [...buf1, ...buf2];
|
|
1078
|
-
this.
|
|
1259
|
+
this.stackMem -= buf1.length;
|
|
1260
|
+
this.stackMem -= buf2.length;
|
|
1261
|
+
this.stackMem += buf1.length + buf2.length;
|
|
1262
|
+
poppedValue = this.stack.pop();
|
|
1263
|
+
if (poppedValue != null) {
|
|
1264
|
+
this.stackMem -= poppedValue.length;
|
|
1265
|
+
}
|
|
1266
|
+
clearPoppedValue();
|
|
1079
1267
|
break;
|
|
1080
1268
|
case OP.OP_SPLIT:
|
|
1081
1269
|
if (this.stack.length < 2) {
|
|
@@ -1093,7 +1281,10 @@ export default class Spend {
|
|
|
1093
1281
|
buf2 = [...buf1];
|
|
1094
1282
|
// Replace existing stack values by the new values.
|
|
1095
1283
|
this.stack[this.stack.length - 2] = buf2.slice(0, n);
|
|
1284
|
+
this.stackMem -= buf1.length;
|
|
1285
|
+
this.stackMem += n;
|
|
1096
1286
|
this.stack[this.stack.length - 1] = buf2.slice(n);
|
|
1287
|
+
this.stackMem += buf1.length - n;
|
|
1097
1288
|
break;
|
|
1098
1289
|
case OP.OP_NUM2BIN:
|
|
1099
1290
|
if (this.stack.length < 2) {
|
|
@@ -1103,7 +1294,11 @@ export default class Spend {
|
|
|
1103
1294
|
if (size > maxScriptElementSize) {
|
|
1104
1295
|
this.scriptEvaluationError(`It's not currently possible to push data larger than ${maxScriptElementSize} bytes.`);
|
|
1105
1296
|
}
|
|
1106
|
-
this.stack.pop();
|
|
1297
|
+
poppedValue = this.stack.pop();
|
|
1298
|
+
if (poppedValue != null) {
|
|
1299
|
+
this.stackMem -= poppedValue.length;
|
|
1300
|
+
}
|
|
1301
|
+
clearPoppedValue();
|
|
1107
1302
|
rawnum = this.stacktop(-1);
|
|
1108
1303
|
// Try to see if we can fit that number in the number of
|
|
1109
1304
|
// byte requested.
|
|
@@ -1133,6 +1328,8 @@ export default class Spend {
|
|
|
1133
1328
|
}
|
|
1134
1329
|
num[n] = signbit;
|
|
1135
1330
|
this.stack[this.stack.length - 1] = num;
|
|
1331
|
+
this.stackMem -= rawnum.length;
|
|
1332
|
+
this.stackMem += size;
|
|
1136
1333
|
break;
|
|
1137
1334
|
case OP.OP_BIN2NUM:
|
|
1138
1335
|
if (this.stack.length < 1) {
|
|
@@ -1141,6 +1338,8 @@ export default class Spend {
|
|
|
1141
1338
|
buf1 = this.stacktop(-1);
|
|
1142
1339
|
buf2 = minimallyEncode(buf1);
|
|
1143
1340
|
this.stack[this.stack.length - 1] = buf2;
|
|
1341
|
+
this.stackMem -= buf1.length;
|
|
1342
|
+
this.stackMem += buf2.length;
|
|
1144
1343
|
// The resulting number must be a valid number.
|
|
1145
1344
|
if (!isMinimallyEncoded(buf2)) {
|
|
1146
1345
|
this.scriptEvaluationError('OP_BIN2NUM requires that the resulting number is valid.');
|
|
@@ -1152,6 +1351,7 @@ export default class Spend {
|
|
|
1152
1351
|
}
|
|
1153
1352
|
// Finally, increment the program counter
|
|
1154
1353
|
this.programCounter++;
|
|
1354
|
+
return true;
|
|
1155
1355
|
}
|
|
1156
1356
|
/**
|
|
1157
1357
|
* @method validate
|
|
@@ -1168,8 +1368,7 @@ export default class Spend {
|
|
|
1168
1368
|
if (requirePushOnlyUnlockingScripts && !this.unlockingScript.isPushOnly()) {
|
|
1169
1369
|
this.scriptEvaluationError('Unlocking scripts can only contain push operations, and no other opcodes.');
|
|
1170
1370
|
}
|
|
1171
|
-
while (
|
|
1172
|
-
this.step();
|
|
1371
|
+
while (this.step()) {
|
|
1173
1372
|
if (this.context === 'LockingScript' &&
|
|
1174
1373
|
this.programCounter >= this.lockingScript.chunks.length) {
|
|
1175
1374
|
break;
|