33
33
34
34
import javax .annotation .Nullable ;
35
35
import java .io .IOException ;
36
+ import java .math .BigDecimal ;
36
37
import java .math .BigInteger ;
37
38
import java .nio .ByteBuffer ;
38
39
import java .nio .ByteOrder ;
@@ -50,16 +51,35 @@ public class Interpreter {
50
51
51
52
private static final Logger log = LoggerFactory .getLogger (Script .class );
52
53
53
- // public static final long MAX_SCRIPT_ELEMENT_SIZE = 520; // bytes
54
+ // Maximum script number length after Genesis
55
+ //consensus.h in node client
56
+ /** 1KB */
57
+ public static final int ONE_KILOBYTE = 1000 ;
58
+
59
+ // public static final long MAX_SCRIPT_ELEMENT_SIZE = 520; // bytes
54
60
public static final long MAX_SCRIPT_ELEMENT_SIZE = 2147483647 ; // 2Gigabytes after Genesis - (2^31 -1)
55
- private static final int MAX_OPS_PER_SCRIPT = 201 ;
61
+ // private static final int MAX_OPS_PER_SCRIPT = 201;
62
+
63
+ // Maximum number of non-push operations per script after GENESIS
64
+ // Maximum number of non-push operations per script before GENESIS
65
+ private static final long MAX_OPS_PER_SCRIPT_BEFORE_GENESIS = 500 ;
66
+
67
+ // Maximum number of non-push operations per script after GENESIS
68
+ private static long UINT32_MAX = 4294967295L ;
69
+ private static final long MAX_OPS_PER_SCRIPT_AFTER_GENESIS = UINT32_MAX ;
70
+
56
71
private static final int MAX_STACK_SIZE = 1000 ;
57
72
private static final int DEFAULT_MAX_NUM_ELEMENT_SIZE = 4 ;
58
73
private static final int MAX_PUBKEYS_PER_MULTISIG = 20 ;
59
74
private static final int MAX_SCRIPT_SIZE = 10000 ;
60
75
public static final int SIG_SIZE = 75 ;
61
76
/** Max number of sigops allowed in a standard p2sh redeem script */
62
77
public static final int MAX_P2SH_SIGOPS = 15 ;
78
+
79
+ // Maximum script number length after Genesis
80
+ public static final int MAX_SCRIPT_NUM_LENGTH_AFTER_GENESIS = 750 * ONE_KILOBYTE ;
81
+
82
+ public static final int MAX_SCRIPT_NUM_LENGTH_BEFORE_GENESIS = 4 ;
63
83
public static final int DEFAULT_SCRIPT_NUM_LENGTH_POLICY_AFTER_GENESIS = 250 * 1024 ;
64
84
65
85
public static final int MAX_SCRIPT_ELEMENT_SIZE_BEFORE_GENESIS = 520 ;
@@ -207,6 +227,9 @@ public static void executeScript(@Nullable Transaction txContainingThis, long in
207
227
int opCount = 0 ;
208
228
int lastCodeSepLocation = 0 ;
209
229
final boolean enforceMinimal = verifyFlags .contains (VerifyFlag .MINIMALDATA );
230
+ final boolean utxoAfterGenesis = verifyFlags .contains (VerifyFlag .UTXO_AFTER_GENESIS );
231
+ final int maxScriptNumLength = getMaxScriptNumLength (utxoAfterGenesis );
232
+
210
233
211
234
LinkedList <byte []> altstack = new LinkedList <>();
212
235
LinkedList <Boolean > ifStack = new LinkedList <>();
@@ -228,7 +251,7 @@ public static void executeScript(@Nullable Transaction txContainingThis, long in
228
251
// Note how OP_RESERVED does not count towards the opcode limit.
229
252
if (opcode > OP_16 ) {
230
253
opCount ++;
231
- if (opCount > DEFAULT_SCRIPT_NUM_LENGTH_POLICY_AFTER_GENESIS )
254
+ if (! isValidMaxOpsPerScript ( opCount , utxoAfterGenesis ) )
232
255
throw new ScriptException (ScriptError .SCRIPT_ERR_OP_COUNT , "More script operations than is allowed" );
233
256
}
234
257
@@ -445,7 +468,7 @@ public static void executeScript(@Nullable Transaction txContainingThis, long in
445
468
case OP_ROLL :
446
469
if (stack .size () < 1 )
447
470
throw new ScriptException (SCRIPT_ERR_INVALID_STACK_OPERATION , "Attempted OP_PICK/OP_ROLL on an empty stack" );
448
- long val = castToBigInteger (stack .pollLast (), verifyFlags .contains (VerifyFlag .MINIMALDATA )).longValue ();
471
+ long val = castToBigInteger (stack .pollLast (),maxScriptNumLength , verifyFlags .contains (VerifyFlag .MINIMALDATA )).longValue ();
449
472
if (val < 0 || val >= stack .size ())
450
473
throw new ScriptException (SCRIPT_ERR_INVALID_STACK_OPERATION , "OP_PICK/OP_ROLL attempted to get data deeper than stack size" );
451
474
Iterator <byte []> itPICK = stack .descendingIterator ();
@@ -500,7 +523,7 @@ public static void executeScript(@Nullable Transaction txContainingThis, long in
500
523
if (stack .size () < 2 )
501
524
throw new ScriptException (SCRIPT_ERR_INVALID_STACK_OPERATION , "Invalid stack operation." );
502
525
503
- int numSize = castToBigInteger (stack .pollLast (), enforceMinimal ).intValue ();
526
+ int numSize = castToBigInteger (stack .pollLast (), maxScriptNumLength , enforceMinimal ).intValue ();
504
527
505
528
if (!verifyFlags .contains (VerifyFlag .UTXO_AFTER_GENESIS ) && numSize > MAX_SCRIPT_ELEMENT_SIZE_BEFORE_GENESIS )
506
529
throw new ScriptException (ScriptError .SCRIPT_ERR_PUSH_SIZE , "Push value size limit exceeded." );
@@ -538,7 +561,7 @@ public static void executeScript(@Nullable Transaction txContainingThis, long in
538
561
if (stack .size () < 2 )
539
562
throw new ScriptException (SCRIPT_ERR_INVALID_STACK_OPERATION , "Invalid stack operation." );
540
563
541
- BigInteger biSplitPos = castToBigInteger (stack .pollLast (), enforceMinimal );
564
+ BigInteger biSplitPos = castToBigInteger (stack .pollLast (), maxScriptNumLength , enforceMinimal );
542
565
543
566
//sanity check in case we aren't enforcing minimal number encoding
544
567
//we will check that the biSplitPos value can be safely held in an int
@@ -571,7 +594,7 @@ public static void executeScript(@Nullable Transaction txContainingThis, long in
571
594
byte [] binBytes = stack .pollLast ();
572
595
byte [] numBytes = Utils .minimallyEncodeLE (binBytes );
573
596
574
- if (!Utils .checkMinimallyEncodedLE (numBytes , DEFAULT_MAX_NUM_ELEMENT_SIZE ))
597
+ if (!Utils .checkMinimallyEncodedLE (numBytes , maxScriptNumLength ))
575
598
throw new ScriptException (ScriptError .SCRIPT_ERR_INVALID_NUMBER_RANGE , "Given operand is not a number within the valid range [-2^31...2^31]" );
576
599
577
600
stack .addLast (numBytes );
@@ -705,7 +728,7 @@ public static void executeScript(@Nullable Transaction txContainingThis, long in
705
728
case OP_0NOTEQUAL :
706
729
if (stack .size () < 1 )
707
730
throw new ScriptException (SCRIPT_ERR_INVALID_STACK_OPERATION , "Attempted a numeric op on an empty stack" );
708
- BigInteger numericOPnum = castToBigInteger (stack .pollLast (), verifyFlags .contains (VerifyFlag .MINIMALDATA ));
731
+ BigInteger numericOPnum = castToBigInteger (stack .pollLast (), maxScriptNumLength , verifyFlags .contains (VerifyFlag .MINIMALDATA ));
709
732
710
733
switch (opcode ) {
711
734
case OP_1ADD :
@@ -756,8 +779,8 @@ public static void executeScript(@Nullable Transaction txContainingThis, long in
756
779
case OP_MAX :
757
780
if (stack .size () < 2 )
758
781
throw new ScriptException (SCRIPT_ERR_INVALID_STACK_OPERATION , "Attempted a numeric op on a stack with size < 2" );
759
- BigInteger numericOPnum2 = castToBigInteger (stack .pollLast (), verifyFlags .contains (VerifyFlag .MINIMALDATA ));
760
- BigInteger numericOPnum1 = castToBigInteger (stack .pollLast (), verifyFlags .contains (VerifyFlag .MINIMALDATA ));
782
+ BigInteger numericOPnum2 = castToBigInteger (stack .pollLast (),maxScriptNumLength , verifyFlags .contains (VerifyFlag .MINIMALDATA ));
783
+ BigInteger numericOPnum1 = castToBigInteger (stack .pollLast (),maxScriptNumLength , verifyFlags .contains (VerifyFlag .MINIMALDATA ));
761
784
762
785
BigInteger numericOPresult ;
763
786
switch (opcode ) {
@@ -784,23 +807,16 @@ public static void executeScript(@Nullable Transaction txContainingThis, long in
784
807
785
808
/**
786
809
* BigInteger doesn't behave the way we want for modulo operations. Firstly it's
787
- * always garunteed to return a +ve result. Secondly it will throw an exception
788
- * if the 2nd operand is negative. So we'll convert the values to longs and use native
789
- * modulo. When we expand the number limits to arbitrary length we will likely need
790
- * a new BigNum implementation to handle this correctly.
810
+ * always guaranteed to return a +ve result. Secondly it will throw an exception
811
+ * if the 2nd operand is negative.
812
+ * Instead we will use the BigDecimal to perform modular arithmetic, then convert
813
+ * back to BigInteger
791
814
*/
792
- long lOp1 = numericOPnum1 .longValue ();
793
- if (!BigInteger .valueOf (lOp1 ).equals (numericOPnum1 )) {
794
- //in case the value is larger than a long can handle we need to crash and burn.
795
- throw new RuntimeException ("Cannot handle large negative operand for modulo operation" );
796
- }
797
- long lOp2 = numericOPnum2 .longValue ();
798
- if (!BigInteger .valueOf (lOp2 ).equals (numericOPnum2 )) {
799
- //in case the value is larger than a long can handle we need to crash and burn.
800
- throw new RuntimeException ("Cannot handle large negative operand for modulo operation" );
801
- }
802
- long lOpResult = lOp1 % lOp2 ;
803
- numericOPresult = BigInteger .valueOf (lOpResult );
815
+
816
+ BigDecimal bd1 = new BigDecimal (numericOPnum1 );
817
+ BigDecimal bd2 = new BigDecimal (numericOPnum2 );
818
+
819
+ numericOPresult = bd1 .remainder (bd2 ).toBigInteger ();
804
820
805
821
break ;
806
822
@@ -873,18 +889,18 @@ public static void executeScript(@Nullable Transaction txContainingThis, long in
873
889
case OP_NUMEQUALVERIFY :
874
890
if (stack .size () < 2 )
875
891
throw new ScriptException (SCRIPT_ERR_INVALID_STACK_OPERATION , "Attempted OP_NUMEQUALVERIFY on a stack with size < 2" );
876
- BigInteger OPNUMEQUALVERIFYnum2 = castToBigInteger (stack .pollLast (), verifyFlags .contains (VerifyFlag .MINIMALDATA ));
877
- BigInteger OPNUMEQUALVERIFYnum1 = castToBigInteger (stack .pollLast (), verifyFlags .contains (VerifyFlag .MINIMALDATA ));
892
+ BigInteger OPNUMEQUALVERIFYnum2 = castToBigInteger (stack .pollLast (), maxScriptNumLength , verifyFlags .contains (VerifyFlag .MINIMALDATA ));
893
+ BigInteger OPNUMEQUALVERIFYnum1 = castToBigInteger (stack .pollLast (), maxScriptNumLength , verifyFlags .contains (VerifyFlag .MINIMALDATA ));
878
894
879
895
if (!OPNUMEQUALVERIFYnum1 .equals (OPNUMEQUALVERIFYnum2 ))
880
896
throw new ScriptException (ScriptError .SCRIPT_ERR_NUMEQUALVERIFY , "OP_NUMEQUALVERIFY failed" );
881
897
break ;
882
898
case OP_WITHIN :
883
899
if (stack .size () < 3 )
884
900
throw new ScriptException (SCRIPT_ERR_INVALID_STACK_OPERATION , "Attempted OP_WITHIN on a stack with size < 3" );
885
- BigInteger OPWITHINnum3 = castToBigInteger (stack .pollLast (), verifyFlags .contains (VerifyFlag .MINIMALDATA ));
886
- BigInteger OPWITHINnum2 = castToBigInteger (stack .pollLast (), verifyFlags .contains (VerifyFlag .MINIMALDATA ));
887
- BigInteger OPWITHINnum1 = castToBigInteger (stack .pollLast (), verifyFlags .contains (VerifyFlag .MINIMALDATA ));
901
+ BigInteger OPWITHINnum3 = castToBigInteger (stack .pollLast (),maxScriptNumLength , verifyFlags .contains (VerifyFlag .MINIMALDATA ));
902
+ BigInteger OPWITHINnum2 = castToBigInteger (stack .pollLast (),maxScriptNumLength , verifyFlags .contains (VerifyFlag .MINIMALDATA ));
903
+ BigInteger OPWITHINnum1 = castToBigInteger (stack .pollLast (),maxScriptNumLength , verifyFlags .contains (VerifyFlag .MINIMALDATA ));
888
904
if (OPWITHINnum2 .compareTo (OPWITHINnum1 ) <= 0 && OPWITHINnum1 .compareTo (OPWITHINnum3 ) < 0 )
889
905
stack .add (Utils .reverseBytes (Utils .encodeMPI (BigInteger .ONE , false )));
890
906
else
@@ -1438,17 +1454,20 @@ private static int executeMultiSig(Transaction txContainingThis, int index, Scri
1438
1454
|| verifyFlags .contains (VerifyFlag .DERSIG )
1439
1455
|| verifyFlags .contains (VerifyFlag .LOW_S );
1440
1456
final boolean enforceMinimal = verifyFlags .contains (VerifyFlag .MINIMALDATA );
1457
+ final boolean utxoAfterGenesis = verifyFlags .contains (VerifyFlag .UTXO_AFTER_GENESIS );
1441
1458
1442
1459
if (stack .size () < 1 )
1443
1460
throw new ScriptException (SCRIPT_ERR_INVALID_STACK_OPERATION , "Attempted OP_CHECKMULTISIG(VERIFY) on a stack with size < 2" );
1444
1461
1445
- int pubKeyCount = castToBigInteger (stack .pollLast (), enforceMinimal ).intValue ();
1462
+ int pubKeyCount = castToBigInteger (stack .pollLast (), getMaxScriptNumLength ( utxoAfterGenesis ), enforceMinimal ).intValue ();
1446
1463
if (pubKeyCount < 0 || (!verifyFlags .contains (VerifyFlag .UTXO_AFTER_GENESIS ) && pubKeyCount > 20 )
1447
1464
|| (verifyFlags .contains (VerifyFlag .UTXO_AFTER_GENESIS ) && pubKeyCount > Integer .MAX_VALUE ))
1448
1465
throw new ScriptException (ScriptError .SCRIPT_ERR_PUBKEY_COUNT , "OP_CHECKMULTISIG(VERIFY) with pubkey count out of range" );
1449
1466
opCount += pubKeyCount ;
1450
- if (opCount > DEFAULT_SCRIPT_NUM_LENGTH_POLICY_AFTER_GENESIS )
1467
+
1468
+ if (!isValidMaxOpsPerScript (opCount , utxoAfterGenesis ))
1451
1469
throw new ScriptException (ScriptError .SCRIPT_ERR_CHECKMULTISIGVERIFY , "Total op (count > 250 * 1024) during OP_CHECKMULTISIG(VERIFY)" );
1470
+
1452
1471
if (stack .size () < pubKeyCount + 1 )
1453
1472
throw new ScriptException (SCRIPT_ERR_INVALID_STACK_OPERATION , "Attempted OP_CHECKMULTISIG(VERIFY) on a stack with size < num_of_pubkeys + 2" );
1454
1473
@@ -1459,7 +1478,7 @@ private static int executeMultiSig(Transaction txContainingThis, int index, Scri
1459
1478
pubkeys .add (pubKey );
1460
1479
}
1461
1480
1462
- int sigCount = castToBigInteger (stack .pollLast (), enforceMinimal ).intValue ();
1481
+ int sigCount = castToBigInteger (stack .pollLast (), getMaxScriptNumLength ( utxoAfterGenesis ), enforceMinimal ).intValue ();
1463
1482
if (sigCount < 0 || sigCount > pubKeyCount )
1464
1483
throw new ScriptException (ScriptError .SCRIPT_ERR_SIG_COUNT , "OP_CHECKMULTISIG(VERIFY) with sig count out of range" );
1465
1484
if (stack .size () < sigCount + 1 )
@@ -1653,4 +1672,24 @@ private static void checkSequence(long nSequence, Transaction txContainingThis,
1653
1672
if (nSequenceMasked > txToSequenceMasked )
1654
1673
throw new ScriptException (ScriptError .SCRIPT_ERR_UNSATISFIED_LOCKTIME , "Relative locktime requirement not satisfied" );
1655
1674
}
1675
+
1676
+ private static int getMaxScriptNumLength (boolean isGenesisEnabled ) {
1677
+ if (!isGenesisEnabled ) {
1678
+ return MAX_SCRIPT_NUM_LENGTH_BEFORE_GENESIS ;
1679
+ }
1680
+
1681
+ return MAX_SCRIPT_NUM_LENGTH_AFTER_GENESIS ; // use new limit after genesis
1682
+ }
1683
+
1684
+ private static long getMaxOpsPerScript (boolean isGenesisEnabled ) {
1685
+ if (!isGenesisEnabled ) {
1686
+ return MAX_OPS_PER_SCRIPT_BEFORE_GENESIS ; // no changes before genesis
1687
+ }
1688
+
1689
+ return MAX_OPS_PER_SCRIPT_AFTER_GENESIS ; // use new limit after genesis
1690
+ }
1691
+
1692
+ private static boolean isValidMaxOpsPerScript (int nOpCount , boolean isGenesisEnabled ) {
1693
+ return (nOpCount <= getMaxOpsPerScript (isGenesisEnabled ));
1694
+ }
1656
1695
}
0 commit comments