@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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsv/sdk",
3
- "version": "1.4.20",
3
+ "version": "1.4.22",
4
4
  "type": "commonjs",
5
5
  "description": "BSV Blockchain Software Development Kit",
6
6
  "files": [
@@ -59,6 +59,7 @@ const requireCleanStack = true;
59
59
  * @property {number} inputIndex - The index of this input in the current transaction.
60
60
  * @property {UnlockingScript} unlockingScript - The unlocking script that unlocks the UTXO for spending.
61
61
  * @property {number} inputSequence - The sequence number of this input.
62
+ * @property {number} lockTime - The lock time of the transaction.
62
63
  */
63
64
  class Spend {
64
65
  /**
@@ -90,9 +91,11 @@ class Spend {
90
91
  * inputIndex: 0, // inputIndex
91
92
  * unlockingScript: UnlockingScript.fromASM("3045... 02ab..."),
92
93
  * inputSequence: 0xffffffff // inputSequence
94
+ * memoryLimit: 100000 // memoryLimit
93
95
  * });
94
96
  */
95
97
  constructor(params) {
98
+ var _a;
96
99
  this.sourceTXID = params.sourceTXID;
97
100
  this.sourceOutputIndex = params.sourceOutputIndex;
98
101
  this.sourceSatoshis = params.sourceSatoshis;
@@ -104,6 +107,9 @@ class Spend {
104
107
  this.unlockingScript = params.unlockingScript;
105
108
  this.inputSequence = params.inputSequence;
106
109
  this.lockTime = params.lockTime;
110
+ this.memoryLimit = (_a = params.memoryLimit) !== null && _a !== void 0 ? _a : 100000; // 100 MB is going to be processed by most miners by policy, but the default should protect apps against memory attacks.
111
+ this.stackMem = 0;
112
+ this.altStackMem = 0;
107
113
  this.reset();
108
114
  }
109
115
  reset() {
@@ -113,9 +119,25 @@ class Spend {
113
119
  this.stack = [];
114
120
  this.altStack = [];
115
121
  this.ifStack = [];
122
+ this.stackMem = 0;
123
+ this.altStackMem = 0;
116
124
  }
117
125
  step() {
118
- var _a, _b, _c, _d, _e, _f;
126
+ var _a, _b, _c, _d;
127
+ let poppedValue;
128
+ // If the stack (or alt stack) is over the memory limit, evaluation has failed.
129
+ if (this.stackMem > this.memoryLimit) {
130
+ this.scriptEvaluationError('Stack memory usage has exceeded ' + String(this.memoryLimit) + ' bytes');
131
+ return false;
132
+ }
133
+ if (this.altStackMem > this.memoryLimit) {
134
+ this.scriptEvaluationError('Alt stack memory usage has exceeded ' + String(this.memoryLimit) + ' bytes');
135
+ return false;
136
+ }
137
+ // Clear popped values after use to free memory
138
+ const clearPoppedValue = () => {
139
+ poppedValue = undefined;
140
+ };
119
141
  // If the context is UnlockingScript and we have reached the end,
120
142
  // set the context to LockingScript and zero the program counter
121
143
  if (this.context === 'UnlockingScript' &&
@@ -245,6 +267,10 @@ class Spend {
245
267
  // Non-canonical signature: R value type mismatch
246
268
  return false;
247
269
  }
270
+ if (buf[4 - 1] !== nLEnR) {
271
+ // Non-canonical signature: R length mismatch
272
+ return false;
273
+ }
248
274
  if (nLEnR === 0) {
249
275
  // Non-canonical signature: R length is zero
250
276
  return false;
@@ -262,6 +288,10 @@ class Spend {
262
288
  // Non-canonical signature: S value type mismatch
263
289
  return false;
264
290
  }
291
+ if (buf[6 + nLEnR - 1] !== nLEnS) {
292
+ // Non-canonical signature: S length mismatch
293
+ return false;
294
+ }
265
295
  if (nLEnS === 0) {
266
296
  // Non-canonical signature: S length is zero
267
297
  return false;
@@ -355,9 +385,11 @@ class Spend {
355
385
  }
356
386
  if (!Array.isArray(operation.data)) {
357
387
  this.stack.push([]);
388
+ this.stackMem += 0;
358
389
  }
359
390
  else {
360
391
  this.stack.push(operation.data);
392
+ this.stackMem += operation.data.length;
361
393
  }
362
394
  }
363
395
  else if (isScriptExecuting ||
@@ -383,6 +415,7 @@ class Spend {
383
415
  n = currentOpcode - (OP_js_1.default.OP_1 - 1);
384
416
  buf = new BigNumber_js_1.default(n).toScriptNum();
385
417
  this.stack.push(buf);
418
+ this.stackMem += buf.length;
386
419
  break;
387
420
  case OP_js_1.default.OP_NOP:
388
421
  case OP_js_1.default.OP_NOP2:
@@ -472,7 +505,11 @@ class Spend {
472
505
  if (currentOpcode === OP_js_1.default.OP_NOTIF) {
473
506
  fValue = !fValue;
474
507
  }
475
- this.stack.pop();
508
+ poppedValue = this.stack.pop();
509
+ if (poppedValue != null) {
510
+ this.stackMem -= poppedValue.length;
511
+ }
512
+ clearPoppedValue();
476
513
  }
477
514
  this.ifStack.push(fValue);
478
515
  break;
@@ -495,10 +532,12 @@ class Spend {
495
532
  }
496
533
  buf = this.stacktop(-1);
497
534
  fValue = this.castToBool(buf);
498
- if (fValue) {
499
- this.stack.pop();
535
+ poppedValue = this.stack.pop();
536
+ if (poppedValue != null) {
537
+ this.stackMem -= poppedValue.length;
500
538
  }
501
- else {
539
+ clearPoppedValue();
540
+ if (!fValue) {
502
541
  this.scriptEvaluationError('OP_VERIFY requires the top stack value to be truthy.');
503
542
  }
504
543
  break;
@@ -515,20 +554,40 @@ class Spend {
515
554
  if (this.stack.length < 1) {
516
555
  this.scriptEvaluationError('OP_TOALTSTACK requires at oeast one item to be on the stack.');
517
556
  }
518
- this.altStack.push((_a = this.stack.pop()) !== null && _a !== void 0 ? _a : []);
557
+ poppedValue = this.stack.pop();
558
+ if (poppedValue != null) {
559
+ this.altStack.push(poppedValue);
560
+ this.altStackMem += poppedValue.length;
561
+ this.stackMem -= poppedValue.length;
562
+ }
563
+ clearPoppedValue();
519
564
  break;
520
565
  case OP_js_1.default.OP_FROMALTSTACK:
521
566
  if (this.altStack.length < 1) {
522
567
  this.scriptEvaluationError('OP_FROMALTSTACK requires at least one item to be on the stack.');
523
568
  }
524
- this.stack.push((_b = this.altStack.pop()) !== null && _b !== void 0 ? _b : []);
569
+ poppedValue = this.altStack.pop();
570
+ if (poppedValue != null) {
571
+ this.stack.push(poppedValue);
572
+ this.stackMem += poppedValue.length;
573
+ this.altStackMem -= poppedValue.length;
574
+ }
575
+ clearPoppedValue();
525
576
  break;
526
577
  case OP_js_1.default.OP_2DROP:
527
578
  if (this.stack.length < 2) {
528
579
  this.scriptEvaluationError('OP_2DROP requires at least two items to be on the stack.');
529
580
  }
530
- this.stack.pop();
531
- this.stack.pop();
581
+ poppedValue = this.stack.pop();
582
+ if (poppedValue != null) {
583
+ this.stackMem -= poppedValue.length;
584
+ }
585
+ clearPoppedValue();
586
+ poppedValue = this.stack.pop();
587
+ if (poppedValue != null) {
588
+ this.stackMem -= poppedValue.length;
589
+ }
590
+ clearPoppedValue();
532
591
  break;
533
592
  case OP_js_1.default.OP_2DUP:
534
593
  if (this.stack.length < 2) {
@@ -537,7 +596,9 @@ class Spend {
537
596
  buf1 = this.stacktop(-2);
538
597
  buf2 = this.stacktop(-1);
539
598
  this.stack.push([...buf1]);
599
+ this.stackMem += buf1.length;
540
600
  this.stack.push([...buf2]);
601
+ this.stackMem += buf2.length;
541
602
  break;
542
603
  case OP_js_1.default.OP_3DUP:
543
604
  if (this.stack.length < 3) {
@@ -547,8 +608,11 @@ class Spend {
547
608
  buf2 = this.stacktop(-2);
548
609
  buf3 = this.stacktop(-1);
549
610
  this.stack.push([...buf1]);
611
+ this.stackMem += buf1.length;
550
612
  this.stack.push([...buf2]);
613
+ this.stackMem += buf2.length;
551
614
  this.stack.push([...buf3]);
615
+ this.stackMem += buf3.length;
552
616
  break;
553
617
  case OP_js_1.default.OP_2OVER:
554
618
  if (this.stack.length < 4) {
@@ -557,7 +621,9 @@ class Spend {
557
621
  buf1 = this.stacktop(-4);
558
622
  buf2 = this.stacktop(-3);
559
623
  this.stack.push([...buf1]);
624
+ this.stackMem += buf1.length;
560
625
  this.stack.push([...buf2]);
626
+ this.stackMem += buf2.length;
561
627
  break;
562
628
  case OP_js_1.default.OP_2ROT:
563
629
  if (this.stack.length < 6) {
@@ -565,7 +631,9 @@ class Spend {
565
631
  }
566
632
  spliced = this.stack.splice(this.stack.length - 6, 2);
567
633
  this.stack.push(spliced[0]);
634
+ this.stackMem += spliced[0].length;
568
635
  this.stack.push(spliced[1]);
636
+ this.stackMem += spliced[1].length;
569
637
  break;
570
638
  case OP_js_1.default.OP_2SWAP:
571
639
  if (this.stack.length < 4) {
@@ -573,7 +641,9 @@ class Spend {
573
641
  }
574
642
  spliced = this.stack.splice(this.stack.length - 4, 2);
575
643
  this.stack.push(spliced[0]);
644
+ this.stackMem += spliced[0].length;
576
645
  this.stack.push(spliced[1]);
646
+ this.stackMem += spliced[1].length;
577
647
  break;
578
648
  case OP_js_1.default.OP_IFDUP:
579
649
  if (this.stack.length < 1) {
@@ -583,35 +653,44 @@ class Spend {
583
653
  fValue = this.castToBool(buf);
584
654
  if (fValue) {
585
655
  this.stack.push([...buf]);
656
+ this.stackMem += buf.length;
586
657
  }
587
658
  break;
588
659
  case OP_js_1.default.OP_DEPTH:
589
660
  buf = new BigNumber_js_1.default(this.stack.length).toScriptNum();
590
661
  this.stack.push(buf);
662
+ this.stackMem += buf.length;
591
663
  break;
592
664
  case OP_js_1.default.OP_DROP:
593
665
  if (this.stack.length < 1) {
594
666
  this.scriptEvaluationError('OP_DROP requires at least one item to be on the stack.');
595
667
  }
596
- this.stack.pop();
668
+ poppedValue = this.stack.pop();
669
+ if (poppedValue != null) {
670
+ this.stackMem -= poppedValue.length;
671
+ }
672
+ clearPoppedValue();
597
673
  break;
598
674
  case OP_js_1.default.OP_DUP:
599
675
  if (this.stack.length < 1) {
600
676
  this.scriptEvaluationError('OP_DUP requires at least one item to be on the stack.');
601
677
  }
602
678
  this.stack.push([...this.stacktop(-1)]);
679
+ this.stackMem += this.stacktop(-1).length;
603
680
  break;
604
681
  case OP_js_1.default.OP_NIP:
605
682
  if (this.stack.length < 2) {
606
683
  this.scriptEvaluationError('OP_NIP requires at least two items to be on the stack.');
607
684
  }
608
685
  this.stack.splice(this.stack.length - 2, 1);
686
+ this.stackMem -= this.stacktop(-1).length;
609
687
  break;
610
688
  case OP_js_1.default.OP_OVER:
611
689
  if (this.stack.length < 2) {
612
690
  this.scriptEvaluationError('OP_OVER requires at least two items to be on the stack.');
613
691
  }
614
692
  this.stack.push([...this.stacktop(-2)]);
693
+ this.stackMem += this.stacktop(-2).length;
615
694
  break;
616
695
  case OP_js_1.default.OP_PICK:
617
696
  case OP_js_1.default.OP_ROLL:
@@ -621,15 +700,21 @@ class Spend {
621
700
  buf = this.stacktop(-1);
622
701
  bn = BigNumber_js_1.default.fromScriptNum(buf, requireMinimalPush);
623
702
  n = bn.toNumber();
624
- this.stack.pop();
703
+ poppedValue = this.stack.pop();
704
+ if (poppedValue != null) {
705
+ this.stackMem -= poppedValue.length;
706
+ }
707
+ clearPoppedValue();
625
708
  if (n < 0 || n >= this.stack.length) {
626
709
  this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} requires the top stack element to be 0 or a positive number less than the current size of the stack.`);
627
710
  }
628
711
  buf = this.stacktop(-n - 1);
629
712
  if (currentOpcode === OP_js_1.default.OP_ROLL) {
630
713
  this.stack.splice(this.stack.length - n - 1, 1);
714
+ this.stackMem -= buf.length;
631
715
  }
632
716
  this.stack.push([...buf]);
717
+ this.stackMem += buf.length;
633
718
  break;
634
719
  case OP_js_1.default.OP_ROT:
635
720
  if (this.stack.length < 3) {
@@ -656,6 +741,7 @@ class Spend {
656
741
  this.scriptEvaluationError('OP_TUCK requires at least two items to be on the stack.');
657
742
  }
658
743
  this.stack.splice(this.stack.length - 2, 0, [...this.stacktop(-1)]);
744
+ this.stackMem += this.stacktop(-1).length;
659
745
  break;
660
746
  case OP_js_1.default.OP_SIZE:
661
747
  if (this.stack.length < 1) {
@@ -663,6 +749,7 @@ class Spend {
663
749
  }
664
750
  bn = new BigNumber_js_1.default(this.stacktop(-1).length);
665
751
  this.stack.push(bn.toScriptNum());
752
+ this.stackMem += bn.toScriptNum().length;
666
753
  break;
667
754
  case OP_js_1.default.OP_AND:
668
755
  case OP_js_1.default.OP_OR:
@@ -693,7 +780,11 @@ class Spend {
693
780
  break;
694
781
  }
695
782
  // And pop vch2.
696
- this.stack.pop();
783
+ poppedValue = this.stack.pop();
784
+ if (poppedValue != null) {
785
+ this.stackMem -= poppedValue.length;
786
+ }
787
+ clearPoppedValue();
697
788
  break;
698
789
  case OP_js_1.default.OP_INVERT:
699
790
  if (this.stack.length < 1) {
@@ -711,7 +802,11 @@ class Spend {
711
802
  }
712
803
  buf1 = this.stacktop(-2);
713
804
  if (buf1.length === 0) {
714
- this.stack.pop();
805
+ poppedValue = this.stack.pop();
806
+ if (poppedValue != null) {
807
+ this.stackMem -= poppedValue.length;
808
+ }
809
+ clearPoppedValue();
715
810
  }
716
811
  else {
717
812
  bn1 = new BigNumber_js_1.default(buf1);
@@ -720,8 +815,16 @@ class Spend {
720
815
  if (n < 0) {
721
816
  this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} requires the top item on the stack not to be negative.`);
722
817
  }
723
- this.stack.pop();
724
- this.stack.pop();
818
+ poppedValue = this.stack.pop();
819
+ if (poppedValue != null) {
820
+ this.stackMem -= poppedValue.length;
821
+ }
822
+ clearPoppedValue();
823
+ poppedValue = this.stack.pop();
824
+ if (poppedValue != null) {
825
+ this.stackMem -= poppedValue.length;
826
+ }
827
+ clearPoppedValue();
725
828
  let shifted;
726
829
  if (currentOpcode === OP_js_1.default.OP_LSHIFT) {
727
830
  shifted = bn1.ushln(n);
@@ -731,6 +834,7 @@ class Spend {
731
834
  }
732
835
  const bufShifted = padDataToSize(shifted.toArray().slice(buf1.length * -1), buf1.length);
733
836
  this.stack.push(bufShifted);
837
+ this.stackMem += bufShifted.length;
734
838
  }
735
839
  break;
736
840
  case OP_js_1.default.OP_EQUAL:
@@ -741,12 +845,25 @@ class Spend {
741
845
  buf1 = this.stacktop(-2);
742
846
  buf2 = this.stacktop(-1);
743
847
  fEqual = (0, utils_js_1.toHex)(buf1) === (0, utils_js_1.toHex)(buf2);
744
- this.stack.pop();
745
- this.stack.pop();
848
+ poppedValue = this.stack.pop();
849
+ if (poppedValue != null) {
850
+ this.stackMem -= poppedValue.length;
851
+ }
852
+ clearPoppedValue();
853
+ poppedValue = this.stack.pop();
854
+ if (poppedValue != null) {
855
+ this.stackMem -= poppedValue.length;
856
+ }
857
+ clearPoppedValue();
746
858
  this.stack.push(fEqual ? [1] : []);
859
+ this.stackMem += (fEqual ? 1 : 0);
747
860
  if (currentOpcode === OP_js_1.default.OP_EQUALVERIFY) {
748
- if (fEqual) {
749
- this.stack.pop();
861
+ if (this.castToBool(this.stacktop(-1))) {
862
+ poppedValue = this.stack.pop();
863
+ if (poppedValue != null) {
864
+ this.stackMem -= poppedValue.length;
865
+ }
866
+ clearPoppedValue();
750
867
  }
751
868
  else {
752
869
  this.scriptEvaluationError('OP_EQUALVERIFY requires the top two stack items to be equal.');
@@ -786,8 +903,13 @@ class Spend {
786
903
  bn = new BigNumber_js_1.default(bn.cmpn(0) !== 0 ? 1 : 0 + 0);
787
904
  break;
788
905
  }
789
- this.stack.pop();
906
+ poppedValue = this.stack.pop();
907
+ if (poppedValue != null) {
908
+ this.stackMem -= poppedValue.length;
909
+ }
910
+ clearPoppedValue();
790
911
  this.stack.push(bn.toScriptNum());
912
+ this.stackMem += bn.toScriptNum().length;
791
913
  break;
792
914
  case OP_js_1.default.OP_ADD:
793
915
  case OP_js_1.default.OP_SUB:
@@ -867,12 +989,25 @@ class Spend {
867
989
  bn = bn1.cmp(bn2) > 0 ? bn1 : bn2;
868
990
  break;
869
991
  }
870
- this.stack.pop();
871
- this.stack.pop();
992
+ poppedValue = this.stack.pop();
993
+ if (poppedValue != null) {
994
+ this.stackMem -= poppedValue.length;
995
+ }
996
+ clearPoppedValue();
997
+ poppedValue = this.stack.pop();
998
+ if (poppedValue != null) {
999
+ this.stackMem -= poppedValue.length;
1000
+ }
1001
+ clearPoppedValue();
872
1002
  this.stack.push(bn.toScriptNum());
1003
+ this.stackMem += bn.toScriptNum().length;
873
1004
  if (currentOpcode === OP_js_1.default.OP_NUMEQUALVERIFY) {
874
1005
  if (this.castToBool(this.stacktop(-1))) {
875
- this.stack.pop();
1006
+ poppedValue = this.stack.pop();
1007
+ if (poppedValue != null) {
1008
+ this.stackMem -= poppedValue.length;
1009
+ }
1010
+ clearPoppedValue();
876
1011
  }
877
1012
  else {
878
1013
  this.scriptEvaluationError('OP_NUMEQUALVERIFY requires the top stack item to be truthy.');
@@ -887,10 +1022,23 @@ class Spend {
887
1022
  bn2 = BigNumber_js_1.default.fromScriptNum(this.stacktop(-2), requireMinimalPush);
888
1023
  bn3 = BigNumber_js_1.default.fromScriptNum(this.stacktop(-1), requireMinimalPush);
889
1024
  fValue = bn2.cmp(bn1) <= 0 && bn1.cmp(bn3) < 0;
890
- this.stack.pop();
891
- this.stack.pop();
892
- this.stack.pop();
1025
+ poppedValue = this.stack.pop();
1026
+ if (poppedValue != null) {
1027
+ this.stackMem -= poppedValue.length;
1028
+ }
1029
+ clearPoppedValue();
1030
+ poppedValue = this.stack.pop();
1031
+ if (poppedValue != null) {
1032
+ this.stackMem -= poppedValue.length;
1033
+ }
1034
+ clearPoppedValue();
1035
+ poppedValue = this.stack.pop();
1036
+ if (poppedValue != null) {
1037
+ this.stackMem -= poppedValue.length;
1038
+ }
1039
+ clearPoppedValue();
893
1040
  this.stack.push(fValue ? [1] : []);
1041
+ this.stackMem += (fValue ? 1 : 0);
894
1042
  break;
895
1043
  case OP_js_1.default.OP_RIPEMD160:
896
1044
  case OP_js_1.default.OP_SHA1:
@@ -900,7 +1048,7 @@ class Spend {
900
1048
  if (this.stack.length < 1) {
901
1049
  this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} requires at least one item to be on the stack.`);
902
1050
  }
903
- let bufHash = []; // Initialize bufHash to an empty array
1051
+ let bufHash = []; // Initialize bufHash to an empty array
904
1052
  buf = this.stacktop(-1);
905
1053
  if (currentOpcode === OP_js_1.default.OP_RIPEMD160) {
906
1054
  bufHash = Hash.ripemd160(buf);
@@ -917,8 +1065,13 @@ class Spend {
917
1065
  else if (currentOpcode === OP_js_1.default.OP_HASH256) {
918
1066
  bufHash = Hash.hash256(buf);
919
1067
  }
920
- this.stack.pop();
1068
+ poppedValue = this.stack.pop();
1069
+ if (poppedValue != null) {
1070
+ this.stackMem -= poppedValue.length;
1071
+ }
1072
+ clearPoppedValue();
921
1073
  this.stack.push(bufHash);
1074
+ this.stackMem += bufHash.length;
922
1075
  break;
923
1076
  }
924
1077
  case OP_js_1.default.OP_CODESEPARATOR:
@@ -938,10 +1091,10 @@ class Spend {
938
1091
  // Subset of script starting at the most recent codeseparator
939
1092
  // CScript scriptCode(pbegincodehash, pend);
940
1093
  if (this.context === 'UnlockingScript') {
941
- subscript = new Script_js_1.default(this.unlockingScript.chunks.slice((_c = this.lastCodeSeparator) !== null && _c !== void 0 ? _c : 0));
1094
+ subscript = new Script_js_1.default(this.unlockingScript.chunks.slice((_a = this.lastCodeSeparator) !== null && _a !== void 0 ? _a : 0));
942
1095
  }
943
1096
  else {
944
- subscript = new Script_js_1.default(this.lockingScript.chunks.slice((_d = this.lastCodeSeparator) !== null && _d !== void 0 ? _d : 0));
1097
+ subscript = new Script_js_1.default(this.lockingScript.chunks.slice((_b = this.lastCodeSeparator) !== null && _b !== void 0 ? _b : 0));
945
1098
  }
946
1099
  // Drop the signature, since there's no way for a signature to sign itself
947
1100
  subscript.findAndDelete(new Script_js_1.default().writeBin(bufSig));
@@ -957,13 +1110,26 @@ class Spend {
957
1110
  if (!fSuccess && bufSig.length > 0) {
958
1111
  this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} failed to verify the signature, and requires an empty signature when verification fails.`);
959
1112
  }
960
- this.stack.pop();
961
- this.stack.pop();
1113
+ poppedValue = this.stack.pop();
1114
+ if (poppedValue != null) {
1115
+ this.stackMem -= poppedValue.length;
1116
+ }
1117
+ clearPoppedValue();
1118
+ poppedValue = this.stack.pop();
1119
+ if (poppedValue != null) {
1120
+ this.stackMem -= poppedValue.length;
1121
+ }
1122
+ clearPoppedValue();
962
1123
  // stack.push_back(fSuccess ? vchTrue : vchFalse);
963
1124
  this.stack.push(fSuccess ? [1] : []);
1125
+ this.stackMem += (fSuccess ? 1 : 0);
964
1126
  if (currentOpcode === OP_js_1.default.OP_CHECKSIGVERIFY) {
965
1127
  if (fSuccess) {
966
- this.stack.pop();
1128
+ poppedValue = this.stack.pop();
1129
+ if (poppedValue != null) {
1130
+ this.stackMem -= poppedValue.length;
1131
+ }
1132
+ clearPoppedValue();
967
1133
  }
968
1134
  else {
969
1135
  this.scriptEvaluationError('OP_CHECKSIGVERIFY requires that a valid signature is provided.');
@@ -1002,10 +1168,10 @@ class Spend {
1002
1168
  }
1003
1169
  // Subset of script starting at the most recent codeseparator
1004
1170
  if (this.context === 'UnlockingScript') {
1005
- subscript = new Script_js_1.default(this.unlockingScript.chunks.slice((_e = this.lastCodeSeparator) !== null && _e !== void 0 ? _e : 0));
1171
+ subscript = new Script_js_1.default(this.unlockingScript.chunks.slice((_c = this.lastCodeSeparator) !== null && _c !== void 0 ? _c : 0));
1006
1172
  }
1007
1173
  else {
1008
- subscript = new Script_js_1.default(this.lockingScript.chunks.slice((_f = this.lastCodeSeparator) !== null && _f !== void 0 ? _f : 0));
1174
+ subscript = new Script_js_1.default(this.lockingScript.chunks.slice((_d = this.lastCodeSeparator) !== null && _d !== void 0 ? _d : 0));
1009
1175
  }
1010
1176
  // Drop the signatures, since there's no way for a signature to sign itself
1011
1177
  for (let k = 0; k < nSigsCount; k++) {
@@ -1051,7 +1217,11 @@ class Spend {
1051
1217
  if (ikey2 > 0) {
1052
1218
  ikey2--;
1053
1219
  }
1054
- this.stack.pop();
1220
+ poppedValue = this.stack.pop();
1221
+ if (poppedValue != null) {
1222
+ this.stackMem -= poppedValue.length;
1223
+ }
1224
+ clearPoppedValue();
1055
1225
  }
1056
1226
  // A bug causes CHECKMULTISIG to consume one extra argument
1057
1227
  // whose contents were not checked in any way.
@@ -1066,11 +1236,20 @@ class Spend {
1066
1236
  // NOTE: Is this necessary? We don't care about malleability.
1067
1237
  this.scriptEvaluationError(`${OP_js_1.default[currentOpcode]} requires the extra stack item to be empty.`);
1068
1238
  }
1069
- this.stack.pop();
1239
+ poppedValue = this.stack.pop();
1240
+ if (poppedValue != null) {
1241
+ this.stackMem -= poppedValue.length;
1242
+ }
1243
+ clearPoppedValue();
1070
1244
  this.stack.push(fSuccess ? [1] : []);
1245
+ this.stackMem += (fSuccess ? 1 : 0);
1071
1246
  if (currentOpcode === OP_js_1.default.OP_CHECKMULTISIGVERIFY) {
1072
1247
  if (fSuccess) {
1073
- this.stack.pop();
1248
+ poppedValue = this.stack.pop();
1249
+ if (poppedValue != null) {
1250
+ this.stackMem -= poppedValue.length;
1251
+ }
1252
+ clearPoppedValue();
1074
1253
  }
1075
1254
  else {
1076
1255
  this.scriptEvaluationError('OP_CHECKMULTISIGVERIFY requires that a sufficient number of valid signatures are provided.');
@@ -1087,7 +1266,14 @@ class Spend {
1087
1266
  this.scriptEvaluationError(`It's not currently possible to push data larger than ${maxScriptElementSize} bytes.`);
1088
1267
  }
1089
1268
  this.stack[this.stack.length - 2] = [...buf1, ...buf2];
1090
- this.stack.pop();
1269
+ this.stackMem -= buf1.length;
1270
+ this.stackMem -= buf2.length;
1271
+ this.stackMem += buf1.length + buf2.length;
1272
+ poppedValue = this.stack.pop();
1273
+ if (poppedValue != null) {
1274
+ this.stackMem -= poppedValue.length;
1275
+ }
1276
+ clearPoppedValue();
1091
1277
  break;
1092
1278
  case OP_js_1.default.OP_SPLIT:
1093
1279
  if (this.stack.length < 2) {
@@ -1105,7 +1291,10 @@ class Spend {
1105
1291
  buf2 = [...buf1];
1106
1292
  // Replace existing stack values by the new values.
1107
1293
  this.stack[this.stack.length - 2] = buf2.slice(0, n);
1294
+ this.stackMem -= buf1.length;
1295
+ this.stackMem += n;
1108
1296
  this.stack[this.stack.length - 1] = buf2.slice(n);
1297
+ this.stackMem += buf1.length - n;
1109
1298
  break;
1110
1299
  case OP_js_1.default.OP_NUM2BIN:
1111
1300
  if (this.stack.length < 2) {
@@ -1115,7 +1304,11 @@ class Spend {
1115
1304
  if (size > maxScriptElementSize) {
1116
1305
  this.scriptEvaluationError(`It's not currently possible to push data larger than ${maxScriptElementSize} bytes.`);
1117
1306
  }
1118
- this.stack.pop();
1307
+ poppedValue = this.stack.pop();
1308
+ if (poppedValue != null) {
1309
+ this.stackMem -= poppedValue.length;
1310
+ }
1311
+ clearPoppedValue();
1119
1312
  rawnum = this.stacktop(-1);
1120
1313
  // Try to see if we can fit that number in the number of
1121
1314
  // byte requested.
@@ -1145,6 +1338,8 @@ class Spend {
1145
1338
  }
1146
1339
  num[n] = signbit;
1147
1340
  this.stack[this.stack.length - 1] = num;
1341
+ this.stackMem -= rawnum.length;
1342
+ this.stackMem += size;
1148
1343
  break;
1149
1344
  case OP_js_1.default.OP_BIN2NUM:
1150
1345
  if (this.stack.length < 1) {
@@ -1153,6 +1348,8 @@ class Spend {
1153
1348
  buf1 = this.stacktop(-1);
1154
1349
  buf2 = (0, utils_js_1.minimallyEncode)(buf1);
1155
1350
  this.stack[this.stack.length - 1] = buf2;
1351
+ this.stackMem -= buf1.length;
1352
+ this.stackMem += buf2.length;
1156
1353
  // The resulting number must be a valid number.
1157
1354
  if (!isMinimallyEncoded(buf2)) {
1158
1355
  this.scriptEvaluationError('OP_BIN2NUM requires that the resulting number is valid.');
@@ -1164,6 +1361,7 @@ class Spend {
1164
1361
  }
1165
1362
  // Finally, increment the program counter
1166
1363
  this.programCounter++;
1364
+ return true;
1167
1365
  }
1168
1366
  /**
1169
1367
  * @method validate
@@ -1180,8 +1378,7 @@ class Spend {
1180
1378
  if (requirePushOnlyUnlockingScripts && !this.unlockingScript.isPushOnly()) {
1181
1379
  this.scriptEvaluationError('Unlocking scripts can only contain push operations, and no other opcodes.');
1182
1380
  }
1183
- while (true) {
1184
- this.step();
1381
+ while (this.step()) {
1185
1382
  if (this.context === 'LockingScript' &&
1186
1383
  this.programCounter >= this.lockingScript.chunks.length) {
1187
1384
  break;