diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 225e53b4fb..eec4d891fe 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -379,7 +379,47 @@ bool EvalScript(vector >& stack, const CScript& script, un break; } - case OP_NOP1: case OP_NOP3: case OP_NOP4: case OP_NOP5: + case OP_CHECKSEQUENCEVERIFY: + { + if (!(flags & SCRIPT_VERIFY_CHECKSEQUENCEVERIFY)) { + // not enabled; treat as a NOP3 + if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) { + return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS); + } + break; + } + + if (stack.size() < 1) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + + // Note that unlike CHECKLOCKTIMEVERIFY we do not need to + // accept 5-byte bignums since any value greater than or + // equal to SEQUENCE_THRESHOLD (= 1 << 31) will be rejected + // anyway. This limitation just happens to coincide with + // CScriptNum's default 4-byte limit with an explicit sign + // bit. + // + // This means there is a maximum relative lock time of 52 + // years, even though the nSequence field in transactions + // themselves is uint32_t and could allow a relative lock + // time of up to 120 years. + const CScriptNum nInvSequence(stacktop(-1), fRequireMinimal); + + // In the rare event that the argument may be < 0 due to + // some arithmetic being done first, you can always use + // 0 MAX CHECKSEQUENCEVERIFY. + if (nInvSequence < 0) + return set_error(serror, SCRIPT_ERR_NEGATIVE_LOCKTIME); + + // Actually compare the specified inverse sequence number + // with the input. + if (!checker.CheckLockTime(nInvSequence, true)) + return set_error(serror, SCRIPT_ERR_UNSATISFIED_LOCKTIME); + + break; + } + + case OP_NOP1: case OP_NOP4: case OP_NOP5: case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10: { if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) @@ -1125,8 +1165,21 @@ bool TransactionSignatureChecker::CheckSig(const vector& vchSigIn return true; } -bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) const +bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime, bool fSequence) const { + // Relative lock times are supported by comparing the passed + // in lock time to the sequence number of the input. All other + // logic is the same, all that differs is what we are comparing + // the lock time to. + int64_t txToLockTime; + if (fSequence) { + txToLockTime = (int64_t)~txTo->vin[nIn].nSequence; + if (txToLockTime >= SEQUENCE_THRESHOLD) + return false; + } else { + txToLockTime = (int64_t)txTo->nLockTime; + } + // There are two types of nLockTime: lock-by-blockheight // and lock-by-blocktime, distinguished by whether // nLockTime < LOCKTIME_THRESHOLD. @@ -1135,14 +1188,14 @@ bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) con // unless the type of nLockTime being tested is the same as // the nLockTime in the transaction. if (!( - (txTo->nLockTime < LOCKTIME_THRESHOLD && nLockTime < LOCKTIME_THRESHOLD) || - (txTo->nLockTime >= LOCKTIME_THRESHOLD && nLockTime >= LOCKTIME_THRESHOLD) + (txToLockTime < LOCKTIME_THRESHOLD && nLockTime < LOCKTIME_THRESHOLD) || + (txToLockTime >= LOCKTIME_THRESHOLD && nLockTime >= LOCKTIME_THRESHOLD) )) return false; // Now that we know we're comparing apples-to-apples, the // comparison is a simple numeric one. - if (nLockTime > (int64_t)txTo->nLockTime) + if (nLockTime > txToLockTime) return false; // Finally the nLockTime feature can be disabled and thus @@ -1155,7 +1208,7 @@ bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) con // prevent this condition. Alternatively we could test all // inputs, but testing just this input minimizes the data // required to prove correct CHECKLOCKTIMEVERIFY execution. - if (!~txTo->vin[nIn].nSequence) + if (!fSequence && !~txTo->vin[nIn].nSequence) return false; return true; diff --git a/src/script/interpreter.h b/src/script/interpreter.h index dfae8714ce..4467a702e0 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -71,7 +71,10 @@ enum SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS = (1U << 7), // Verify CHECKLOCKTIMEVERIFY (BIP65) - SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9) + SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9), + + // support CHECKSEQUENCEVERIFY opcode + SCRIPT_VERIFY_CHECKSEQUENCEVERIFY = (1U << 10) }; uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); @@ -84,7 +87,7 @@ public: return false; } - virtual bool CheckLockTime(const CScriptNum& nLockTime) const + virtual bool CheckLockTime(const CScriptNum& nLockTime, bool fSequence = false) const { return false; } @@ -104,7 +107,7 @@ protected: public: TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn) : txTo(txToIn), nIn(nInIn) {} bool CheckSig(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode) const; - bool CheckLockTime(const CScriptNum& nLockTime) const; + bool CheckLockTime(const CScriptNum& nLockTime, bool fSequence = false) const; }; class MutableTransactionSignatureChecker : public TransactionSignatureChecker diff --git a/src/script/script.h b/src/script/script.h index ffbabf0f4f..8eb7eb1766 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -159,6 +159,7 @@ enum opcodetype OP_NOP2 = 0xb1, OP_CHECKLOCKTIMEVERIFY = OP_NOP2, OP_NOP3 = 0xb2, + OP_CHECKSEQUENCEVERIFY = OP_NOP3, OP_NOP4 = 0xb3, OP_NOP5 = 0xb4, OP_NOP6 = 0xb5, diff --git a/src/test/data/tx_invalid.json b/src/test/data/tx_invalid.json index e4d113372d..c35c82b3bb 100644 --- a/src/test/data/tx_invalid.json +++ b/src/test/data/tx_invalid.json @@ -168,5 +168,72 @@ [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xc5b93064159b3b2d6ab506a41b1f50463771b988 EQUAL"]], "0100000001000100000000000000000000000000000000000000000000000000000000000000000000030251b1000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], +["CHECKSEQUENCEVERIFY tests"], + +["By-height locks, with argument just beyond ~txin.nSequence"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1 NOP3 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2 NOP3 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000feffffff0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 NOP3 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000019b32e20100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["By-time locks, with argument just beyond ~txin.nSequence (but within numerical boundries)"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000001 NOP3 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ff9a32e20100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 NOP3 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000010000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["Argument missing"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "NOP3 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["Argument negative with by-blockheight ~txin.nSequence=0"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "-1 NOP3 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["Argument negative with by-blocktime ~txin.nSequence=500,000,000"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "-1 NOP3 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ff9a32e20100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["Another input being unlocked isn't sufficient; the CHECKSEQUENCEVERIFY-using input must be unlocked"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP3 1"] , + ["0000000000000000000000000000000000000000000000000000000000000200", 1, "0 NOP3 1"]], +"010000000200010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00020000000000000000000000000000000000000000000000000000000000000100000000000000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["Argument/tx height/time mismatch, both versions"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP3 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ff9a32e20100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 NOP3 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ff9a32e20100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 NOP3 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 NOP3 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000009b32e20100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["Argument 2^32 with nLockTime=2^32-1"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967296 NOP3 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["Same, but with nLockTime=2^31-1"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 NOP3 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000800100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["An ADD producing a 5-byte result that is out of bounds"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483647 1ADD NOP3 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000800100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["5 byte non-minimally-encoded arguments are invalid even if their contents are valid"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x05 0x0000000000 NOP3 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000080100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["Failure due to failing CHECKSEQUENCEVERIFY in scriptSig"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b2ffffffff0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["Failure due to failing CHECKSEQUENCEVERIFY in redeemScript"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7c17aff532f22beb54069942f9bf567a66133eaf EQUAL"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000030251b2ffffffff0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + ["Make diffs cleaner by leaving a comment here without comma at the end"] ] diff --git a/src/test/data/tx_valid.json b/src/test/data/tx_valid.json index ecbce93601..b2adc8c306 100644 --- a/src/test/data/tx_valid.json +++ b/src/test/data/tx_valid.json @@ -220,5 +220,45 @@ [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xc5b93064159b3b2d6ab506a41b1f50463771b988 EQUAL"]], "0100000001000100000000000000000000000000000000000000000000000000000000000000000000030251b1000000000100000000000000000001000000", "P2SH,CHECKLOCKTIMEVERIFY"], +["CHECKSEQUENCEVERIFY tests"], + +["By-height locks, with argument == 0 and == ~txin.nSequence"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP3 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 NOP3 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000009b32e20100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP3 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000009b32e20100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["By-time locks, with argument just beyond ~txin.nSequence (but within numerical boundries)"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 NOP3 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ff9a32e20100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483647 NOP3 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000800100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 NOP3 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000800100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["Any non-maxint nSequence is fine"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1 NOP3 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000feffffff0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP3 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000feffffff0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["Input locked"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP3 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["The argument can be calculated rather than created directly by a PUSHDATA"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 1ADD NOP3 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ff9a32e20100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["Valid CHECKSEQUENCEVERIFY in scriptSig"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b2feffffff0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["Valid CHECKSEQUENCEVERIFY in redeemScript"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7c17aff532f22beb54069942f9bf567a66133eaf EQUAL"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000030251b2feffffff0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + ["Make diffs cleaner by leaving a comment here without comma at the end"] ] diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index abd33017ed..0e16662084 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -39,7 +39,8 @@ static std::map mapFlagNames = boost::assign::map_list_of (string("MINIMALDATA"), (unsigned int)SCRIPT_VERIFY_MINIMALDATA) (string("NULLDUMMY"), (unsigned int)SCRIPT_VERIFY_NULLDUMMY) (string("DISCOURAGE_UPGRADABLE_NOPS"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) - (string("CHECKLOCKTIMEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY); + (string("CHECKLOCKTIMEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY) + (string("CHECKSEQUENCEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKSEQUENCEVERIFY); unsigned int ParseScriptFlags(string strFlags) {