From bc60b2b4b401f0adff5b8b9678903ff8feb5867b Mon Sep 17 00:00:00 2001 From: Peter Todd Date: Mon, 29 Sep 2014 03:44:25 -0400 Subject: Replace NOP2 with CHECKLOCKTIMEVERIFY (BIP65) CHECKLOCKTIMEVERIFY -> Fails if tx.nLockTime < nLockTime, allowing the funds in a txout to be locked until some block height or block time in the future is reached. Only the logic and unittests are implemented; this commit does not have any actual soft-fork logic in it. Thanks to Pieter Wuille for rebase. Credit goes to Gregory Maxwell for the suggestion of comparing the argument against the transaction nLockTime rather than the current time/blockheight directly. --- src/script/interpreter.cpp | 83 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 2 deletions(-) (limited to 'src/script/interpreter.cpp') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 84a7432fd..0b78fdf5a 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -335,9 +335,51 @@ bool EvalScript(vector >& stack, const CScript& script, un // Control // case OP_NOP: - break; + break; + + case OP_CHECKLOCKTIMEVERIFY: + { + if (!(flags & SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY)) { + // not enabled; treat as a NOP2 + 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 elsewhere numeric opcodes are limited to + // operands in the range -2**31+1 to 2**31-1, however it is + // legal for opcodes to produce results exceeding that + // range. This limitation is implemented by CScriptNum's + // default 4-byte limit. + // + // If we kept to that limit we'd have a year 2038 problem, + // even though the nLockTime field in transactions + // themselves is uint32 which only becomes meaningless + // after the year 2106. + // + // Thus as a special case we tell CScriptNum to accept up + // to 5-byte bignums, which are good until 2**39-1, well + // beyond the 2**32-1 limit of the nLockTime field itself. + const CScriptNum nLockTime(stacktop(-1), fRequireMinimal, 5); + + // In the rare event that the argument may be < 0 due to + // some arithmetic being done first, you can always use + // 0 MAX CHECKLOCKTIMEVERIFY. + if (nLockTime < 0) + return set_error(serror, SCRIPT_ERR_NEGATIVE_LOCKTIME); + + // Actually compare the specified lock time with the transaction. + if (!checker.CheckLockTime(nLockTime)) + return set_error(serror, SCRIPT_ERR_UNSATISFIED_LOCKTIME); + + break; + } - case OP_NOP1: case OP_NOP2: case OP_NOP3: case OP_NOP4: case OP_NOP5: + case OP_NOP1: case OP_NOP3: 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) @@ -1084,6 +1126,43 @@ bool TransactionSignatureChecker::CheckSig(const vector& vchSigIn return true; } +bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) const +{ + // There are two times of nLockTime: lock-by-blockheight + // and lock-by-blocktime, distinguished by whether + // nLockTime < LOCKTIME_THRESHOLD. + // + // We want to compare apples to apples, so fail the script + // 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) + )) + 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) + return false; + + // Finally the nLockTime feature can be disabled and thus + // CHECKLOCKTIMEVERIFY bypassed if every txin has been + // finalized by setting nSequence to maxint. The + // transaction would be allowed into the blockchain, making + // the opcode ineffective. + // + // Testing if this vin is not final is sufficient to + // 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].IsFinal()) + return false; + + return true; +} + + bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror) { set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR); -- cgit v1.2.3 From af3208bfa6967d6b35aecf0ba35d9d6bf0f8317e Mon Sep 17 00:00:00 2001 From: mruddy Date: Thu, 30 Jul 2015 19:56:00 -0400 Subject: Resolve issue 3166. These changes decode valid SIGHASH types on signatures in assembly (asm) representations of scriptSig scripts. This squashed commit incorporates substantial helpful feedback from jtimon, laanwj, and sipa. --- src/script/interpreter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/script/interpreter.cpp') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 0b78fdf5a..03af78bce 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -188,7 +188,7 @@ bool static IsDefinedHashtypeSignature(const valtype &vchSig) { return true; } -bool static CheckSignatureEncoding(const valtype &vchSig, unsigned int flags, ScriptError* serror) { +bool CheckSignatureEncoding(const vector &vchSig, unsigned int flags, ScriptError* serror) { // Empty signature. Not strictly DER encoded, but allowed to provide a // compact way to provide an invalid signature for use with CHECK(MULTI)SIG if (vchSig.size() == 0) { -- cgit v1.2.3 From e846b2a1e96f27257cbb79f7f61bfaf3d873bb97 Mon Sep 17 00:00:00 2001 From: Mark Friedenbach Date: Mon, 10 Aug 2015 18:08:30 -0700 Subject: Correct a possibly intentional pun that is nevertheless hard to read: "two times of nLockTime." What is meant is that there are two kinds, or categories of nLockTime. --- src/script/interpreter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/script/interpreter.cpp') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 0b78fdf5a..bd5e54b33 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1128,7 +1128,7 @@ bool TransactionSignatureChecker::CheckSig(const vector& vchSigIn bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) const { - // There are two times of nLockTime: lock-by-blockheight + // There are two kinds of nLockTime: lock-by-blockheight // and lock-by-blocktime, distinguished by whether // nLockTime < LOCKTIME_THRESHOLD. // -- cgit v1.2.3 From b48da5c1894a70f8fa2a50deb2e056c38ed27ecb Mon Sep 17 00:00:00 2001 From: David Hill Date: Tue, 13 Oct 2015 09:56:45 -0400 Subject: script: Remove magic numbers This adds two new constants, MAX_OPS_PER_SCRIPT and MAX_PUBKEYS_PER_MULTISIG. --- src/script/interpreter.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/script/interpreter.cpp') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index d3aec2602..6a20d497c 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -273,7 +273,7 @@ bool EvalScript(vector >& stack, const CScript& script, un return set_error(serror, SCRIPT_ERR_PUSH_SIZE); // Note how OP_RESERVED does not count towards the opcode limit. - if (opcode > OP_16 && ++nOpCount > 201) + if (opcode > OP_16 && ++nOpCount > MAX_OPS_PER_SCRIPT) return set_error(serror, SCRIPT_ERR_OP_COUNT); if (opcode == OP_CAT || @@ -869,10 +869,10 @@ bool EvalScript(vector >& stack, const CScript& script, un return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); int nKeysCount = CScriptNum(stacktop(-i), fRequireMinimal).getint(); - if (nKeysCount < 0 || nKeysCount > 20) + if (nKeysCount < 0 || nKeysCount > MAX_PUBKEYS_PER_MULTISIG) return set_error(serror, SCRIPT_ERR_PUBKEY_COUNT); nOpCount += nKeysCount; - if (nOpCount > 201) + if (nOpCount > MAX_OPS_PER_SCRIPT) return set_error(serror, SCRIPT_ERR_OP_COUNT); int ikey = ++i; i += nKeysCount; -- cgit v1.2.3 From 114b5812f6283f2325fc31e186b26c6d76f9551a Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 29 Oct 2015 07:11:24 +0100 Subject: Prevector type --- src/script/interpreter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/script/interpreter.cpp') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 6a20d497c..e0853fef4 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1032,7 +1032,7 @@ public: // Serialize the script if (nInput != nIn) // Blank out other inputs' signatures - ::Serialize(s, CScript(), nType, nVersion); + ::Serialize(s, CScriptBase(), nType, nVersion); else SerializeScriptCode(s, nType, nVersion); // Serialize the nSequence -- cgit v1.2.3 From 6e182686163ce3c15b878bd78c41d8d18db344f1 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 28 Jul 2015 20:11:20 +0200 Subject: Switch to libsecp256k1-based validation for ECDSA --- src/script/interpreter.cpp | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) (limited to 'src/script/interpreter.cpp') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 6a20d497c..8dcab832c 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -9,7 +9,6 @@ #include "crypto/ripemd160.h" #include "crypto/sha1.h" #include "crypto/sha256.h" -#include "eccryptoverify.h" #include "pubkey.h" #include "script/script.h" #include "uint256.h" @@ -165,16 +164,8 @@ bool static IsLowDERSignature(const valtype &vchSig, ScriptError* serror) { if (!IsValidSignatureEncoding(vchSig)) { return set_error(serror, SCRIPT_ERR_SIG_DER); } - unsigned int nLenR = vchSig[3]; - unsigned int nLenS = vchSig[5+nLenR]; - const unsigned char *S = &vchSig[6+nLenR]; - // If the S value is above the order of the curve divided by two, its - // complement modulo the order could have been used instead, which is - // one byte shorter when encoded correctly. - if (!eccrypto::CheckSignatureElement(S, nLenS, true)) - return set_error(serror, SCRIPT_ERR_SIG_HIGH_S); - - return true; + std::vector vchSigCopy(vchSig.begin(), vchSig.begin() + vchSig.size() - 1); + return CPubKey::CheckLowS(vchSigCopy); } bool static IsDefinedHashtypeSignature(const valtype &vchSig) { -- cgit v1.2.3 From fa24439ff3d8ab5b9efaf66ef4dae6713b88cb35 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Sun, 13 Dec 2015 17:58:29 +0100 Subject: Bump copyright headers to 2015 --- src/script/interpreter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/script/interpreter.cpp') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 57e0edc4b..a92822326 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2009-2015 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -- cgit v1.2.3 From 9d95187d5ddee56b6dfb55985008bdf70aed31f2 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 10 Feb 2016 14:19:20 +0100 Subject: Correctly report high-S violations --- src/script/interpreter.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/script/interpreter.cpp') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index a92822326..265131ae0 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -165,7 +165,10 @@ bool static IsLowDERSignature(const valtype &vchSig, ScriptError* serror) { return set_error(serror, SCRIPT_ERR_SIG_DER); } std::vector vchSigCopy(vchSig.begin(), vchSig.begin() + vchSig.size() - 1); - return CPubKey::CheckLowS(vchSigCopy); + if (!CPubKey::CheckLowS(vchSigCopy)) { + return set_error(serror, SCRIPT_ERR_SIG_HIGH_S); + } + return true; } bool static IsDefinedHashtypeSignature(const valtype &vchSig) { -- cgit v1.2.3 From c6c2f0fd782ccf607027414012f45c8f48561a30 Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Mon, 7 Dec 2015 15:44:16 -0500 Subject: Implement SequenceLocks functions SequenceLocks functions are used to evaluate sequence lock times or heights per BIP 68. The majority of this code is copied from maaku in #6312 Further credit: btcdrak, sipa, NicolasDorier --- src/script/interpreter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/script/interpreter.cpp') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 57e0edc4b..ac753a9d5 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1147,7 +1147,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].IsFinal()) + if (CTxIn::SEQUENCE_FINAL == txTo->vin[nIn].nSequence) return false; return true; -- cgit v1.2.3 From 53e53a33c939949665f60d5eeb82abbb21f97128 Mon Sep 17 00:00:00 2001 From: Mark Friedenbach Date: Fri, 25 Sep 2015 16:18:51 -0700 Subject: BIP112: Implement CHECKSEQUENCEVERIFY - Replace NOP3 with CHECKSEQUENCEVERIFY (BIP112) CHECKSEQUENCEVERIFY -> - Fails if txin.nSequence < nSequence, allowing funds of a txout to be locked for a number of blocks or a duration of time after its inclusion in a block. - Pull most of CheckLockTime() out into VerifyLockTime(), a local function that will be reused for CheckSequence() - Add bitwise AND operator to CScriptNum - Enable CHECKSEQUENCEVERIFY as a standard script verify flag - Transactions that fail CSV verification will be rejected from the mempool, making it easy to test the feature. However blocks containing "invalid" CSV-using transactions will still be accepted; this is *not* the soft-fork required to actually enable CSV for production use. --- src/script/interpreter.cpp | 89 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 82 insertions(+), 7 deletions(-) (limited to 'src/script/interpreter.cpp') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 901f901f0..4e87006f5 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -373,7 +373,44 @@ 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); + + // nSequence, like nLockTime, is a 32-bit unsigned integer + // field. See the comment in CHECKLOCKTIMEVERIFY regarding + // 5-byte numeric operands. + const CScriptNum nSequence(stacktop(-1), fRequireMinimal, 5); + + // 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 (nSequence < 0) + return set_error(serror, SCRIPT_ERR_NEGATIVE_LOCKTIME); + + // To provide for future soft-fork extensibility, if the + // operand has the disabled lock-time flag set, + // CHECKSEQUENCEVERIFY behaves as a NOP. + if ((nSequence & CTxIn::SEQUENCE_LOCKTIME_DISABLE_FLAG) != 0) + break; + + // Compare the specified sequence number with the input. + if (!checker.CheckSequence(nSequence)) + 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) @@ -1120,27 +1157,33 @@ bool TransactionSignatureChecker::CheckSig(const vector& vchSigIn return true; } -bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) const +static bool VerifyLockTime(int64_t txToLockTime, int64_t nThreshold, const CScriptNum& nLockTime) { // There are two kinds of nLockTime: lock-by-blockheight // and lock-by-blocktime, distinguished by whether - // nLockTime < LOCKTIME_THRESHOLD. + // nLockTime < nThreshold (either LOCKTIME_THRESHOLD or + // CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG). // // We want to compare apples to apples, so fail the script // 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 < nThreshold && nLockTime < nThreshold) || + (txToLockTime >= nThreshold && nLockTime >= nThreshold) )) 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 + return true; +} + +bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) const +{ + // The nLockTime feature can be disabled and thus // CHECKLOCKTIMEVERIFY bypassed if every txin has been // finalized by setting nSequence to maxint. The // transaction would be allowed into the blockchain, making @@ -1153,6 +1196,38 @@ bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) con if (CTxIn::SEQUENCE_FINAL == txTo->vin[nIn].nSequence) return false; + if (!::VerifyLockTime((int64_t)txTo->nLockTime, LOCKTIME_THRESHOLD, nLockTime)) + return false; + + return true; +} + +bool TransactionSignatureChecker::CheckSequence(const CScriptNum& nSequence) const +{ + // Relative lock times are supported by comparing the passed + // in operand to the sequence number of the input. + const int64_t txToSequence = (int64_t)txTo->vin[nIn].nSequence; + + // Fail if the transaction's version number is not set high + // enough to trigger BIP 68 rules. + if (static_cast(txTo->nVersion) < 2) + return false; + + // Sequence numbers with their most significant bit set are not + // consensus constrained. Testing that the transaction's sequence + // number do not have this bit set prevents using this property + // to get around a CHECKSEQUENCEVERIFY check. + if (txToSequence & CTxIn::SEQUENCE_LOCKTIME_DISABLE_FLAG) + return false; + + // Mask off any bits that do not have consensus-enforced meaning + // before doing the integer comparisons of ::VerifyLockTime. + const uint32_t nLockTimeMask = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG + | CTxIn::SEQUENCE_LOCKTIME_MASK; + + if (!::VerifyLockTime(txToSequence & nLockTimeMask, CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG, nSequence & nLockTimeMask)) + return false; + return true; } -- cgit v1.2.3 From c3c375226ebf98901849593b8ebfe8e8b69895c2 Mon Sep 17 00:00:00 2001 From: BtcDrak Date: Fri, 12 Feb 2016 20:02:46 +0000 Subject: Separate CheckLockTime() and CheckSequence() logic For the sake of a little repetition, make code more readable. --- src/script/interpreter.cpp | 46 ++++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 20 deletions(-) (limited to 'src/script/interpreter.cpp') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 4e87006f5..d4fe001d7 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1157,33 +1157,27 @@ bool TransactionSignatureChecker::CheckSig(const vector& vchSigIn return true; } -static bool VerifyLockTime(int64_t txToLockTime, int64_t nThreshold, const CScriptNum& nLockTime) +bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) const { // There are two kinds of nLockTime: lock-by-blockheight // and lock-by-blocktime, distinguished by whether - // nLockTime < nThreshold (either LOCKTIME_THRESHOLD or - // CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG). + // nLockTime < LOCKTIME_THRESHOLD. // // We want to compare apples to apples, so fail the script // unless the type of nLockTime being tested is the same as // the nLockTime in the transaction. if (!( - (txToLockTime < nThreshold && nLockTime < nThreshold) || - (txToLockTime >= nThreshold && nLockTime >= nThreshold) + (txTo->nLockTime < LOCKTIME_THRESHOLD && nLockTime < LOCKTIME_THRESHOLD) || + (txTo->nLockTime >= 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 > txToLockTime) + if (nLockTime > (int64_t)txTo->nLockTime) return false; - return true; -} - -bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) const -{ - // The nLockTime feature can be disabled and thus + // Finally the nLockTime feature can be disabled and thus // CHECKLOCKTIMEVERIFY bypassed if every txin has been // finalized by setting nSequence to maxint. The // transaction would be allowed into the blockchain, making @@ -1196,9 +1190,6 @@ bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) con if (CTxIn::SEQUENCE_FINAL == txTo->vin[nIn].nSequence) return false; - if (!::VerifyLockTime((int64_t)txTo->nLockTime, LOCKTIME_THRESHOLD, nLockTime)) - return false; - return true; } @@ -1221,17 +1212,32 @@ bool TransactionSignatureChecker::CheckSequence(const CScriptNum& nSequence) con return false; // Mask off any bits that do not have consensus-enforced meaning - // before doing the integer comparisons of ::VerifyLockTime. - const uint32_t nLockTimeMask = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG - | CTxIn::SEQUENCE_LOCKTIME_MASK; + // before doing the integer comparisons + const uint32_t nLockTimeMask = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | CTxIn::SEQUENCE_LOCKTIME_MASK; + const int64_t txToSequenceMasked = txToSequence & nLockTimeMask; + const CScriptNum nSequenceMasked = nSequence & nLockTimeMask; + + // There are two kinds of nSequence: lock-by-blockheight + // and lock-by-blocktime, distinguished by whether + // nSequenceMasked < CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG. + // + // We want to compare apples to apples, so fail the script + // unless the type of nSequenceMasked being tested is the same as + // the nSequenceMasked in the transaction. + if (!( + (txToSequenceMasked < CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG && nSequenceMasked < CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG) || + (txToSequenceMasked >= CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG && nSequenceMasked >= CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG) + )) + return false; - if (!::VerifyLockTime(txToSequence & nLockTimeMask, CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG, nSequence & nLockTimeMask)) + // Now that we know we're comparing apples-to-apples, the + // comparison is a simple numeric one. + if (nSequenceMasked > txToSequenceMasked) return false; return true; } - bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror) { set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR); -- cgit v1.2.3 From a38107643f3f01cd92a4e6da9e7a9d025770ff37 Mon Sep 17 00:00:00 2001 From: BtcDrak Date: Tue, 16 Feb 2016 09:39:44 +0000 Subject: Code style fix. This if statement is a little obtuse and using braces here improves readability. --- src/script/interpreter.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/script/interpreter.cpp') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index d4fe001d7..149a4f015 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1227,8 +1227,9 @@ bool TransactionSignatureChecker::CheckSequence(const CScriptNum& nSequence) con if (!( (txToSequenceMasked < CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG && nSequenceMasked < CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG) || (txToSequenceMasked >= CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG && nSequenceMasked >= CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG) - )) + )) { return false; + } // Now that we know we're comparing apples-to-apples, the // comparison is a simple numeric one. -- cgit v1.2.3 From fada0c422c081ba53a324aaf63c0a750cb56498e Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Sun, 3 Apr 2016 11:49:36 +0200 Subject: [doc] Fix doxygen comments for members --- src/script/interpreter.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/script/interpreter.cpp') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 149a4f015..9c47f7c6c 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1015,12 +1015,12 @@ namespace { */ class CTransactionSignatureSerializer { private: - const CTransaction &txTo; //! reference to the spending transaction (the one being serialized) - const CScript &scriptCode; //! output script being consumed - const unsigned int nIn; //! input index of txTo being signed - const bool fAnyoneCanPay; //! whether the hashtype has the SIGHASH_ANYONECANPAY flag set - const bool fHashSingle; //! whether the hashtype is SIGHASH_SINGLE - const bool fHashNone; //! whether the hashtype is SIGHASH_NONE + const CTransaction& txTo; //!< reference to the spending transaction (the one being serialized) + const CScript& scriptCode; //!< output script being consumed + const unsigned int nIn; //!< input index of txTo being signed + const bool fAnyoneCanPay; //!< whether the hashtype has the SIGHASH_ANYONECANPAY flag set + const bool fHashSingle; //!< whether the hashtype is SIGHASH_SINGLE + const bool fHashNone; //!< whether the hashtype is SIGHASH_NONE public: CTransactionSignatureSerializer(const CTransaction &txToIn, const CScript &scriptCodeIn, unsigned int nInIn, int nHashTypeIn) : -- cgit v1.2.3 From f8e6fb1800fbac87e76cdddc074d8f4af585f050 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 25 Apr 2016 12:31:45 +0200 Subject: Introduce constant for maximum CScript length --- src/script/interpreter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/script/interpreter.cpp') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 9c47f7c6c..fd4a5674c 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -247,7 +247,7 @@ bool EvalScript(vector >& stack, const CScript& script, un vector vfExec; vector altstack; set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR); - if (script.size() > 10000) + if (script.size() > MAX_SCRIPT_SIZE) return set_error(serror, SCRIPT_ERR_SCRIPT_SIZE); int nOpCount = 0; bool fRequireMinimal = (flags & SCRIPT_VERIFY_MINIMALDATA) != 0; -- cgit v1.2.3 From 449f9b8debcceb61a92043bc7031528a53627c47 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 8 Nov 2015 01:16:45 +0100 Subject: BIP141: Witness program --- src/script/interpreter.cpp | 111 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 109 insertions(+), 2 deletions(-) (limited to 'src/script/interpreter.cpp') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index fd4a5674c..be649fca2 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1239,8 +1239,67 @@ bool TransactionSignatureChecker::CheckSequence(const CScriptNum& nSequence) con return true; } -bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror) +static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror) { + vector > stack; + CScript scriptPubKey; + + if (witversion == 0) { + if (program.size() == 32) { + // Version 0 segregated witness program: SHA256(CScript) inside the program, CScript + inputs in witness + if (witness.stack.size() == 0) { + return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY); + } + scriptPubKey = CScript(witness.stack.back().begin(), witness.stack.back().end()); + stack = std::vector >(witness.stack.begin(), witness.stack.end() - 1); + uint256 hashScriptPubKey; + CSHA256().Write(&scriptPubKey[0], scriptPubKey.size()).Finalize(hashScriptPubKey.begin()); + if (memcmp(hashScriptPubKey.begin(), &program[0], 32)) { + return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH); + } + } else if (program.size() == 20) { + // Special case for pay-to-pubkeyhash; signature + pubkey in witness + if (witness.stack.size() != 2) { + return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH); // 2 items in witness + } + scriptPubKey << OP_DUP << OP_HASH160 << program << OP_EQUALVERIFY << OP_CHECKSIG; + stack = witness.stack; + } else { + return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH); + } + } else if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM) { + return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM); + } else { + // Higher version witness scripts return true for future softfork compatibility + return set_success(serror); + } + + // Disallow stack item size > MAX_SCRIPT_ELEMENT_SIZE in witness stack + for (unsigned int i = 0; i < stack.size(); i++) { + if (stack.at(i).size() > MAX_SCRIPT_ELEMENT_SIZE) + return set_error(serror, SCRIPT_ERR_PUSH_SIZE); + } + + if (!EvalScript(stack, scriptPubKey, flags, checker, serror)) { + return false; + } + + // Scripts inside witness implicitly require cleanstack behaviour + if (stack.size() != 1) + return set_error(serror, SCRIPT_ERR_EVAL_FALSE); + if (!CastToBool(stack.back())) + return set_error(serror, SCRIPT_ERR_EVAL_FALSE); + return true; +} + +bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror) +{ + static const CScriptWitness emptyWitness; + if (witness == NULL) { + witness = &emptyWitness; + } + bool hadWitness = false; + set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR); if ((flags & SCRIPT_VERIFY_SIGPUSHONLY) != 0 && !scriptSig.IsPushOnly()) { @@ -1261,6 +1320,25 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigne if (CastToBool(stack.back()) == false) return set_error(serror, SCRIPT_ERR_EVAL_FALSE); + // Bare witness programs + int witnessversion; + std::vector witnessprogram; + if (flags & SCRIPT_VERIFY_WITNESS) { + if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) { + hadWitness = true; + if (scriptSig.size() != 0) { + // The scriptSig must be _exactly_ CScript(), otherwise we reintroduce malleability. + return set_error(serror, SCRIPT_ERR_WITNESS_MALLEATED); + } + if (!VerifyWitnessProgram(*witness, witnessversion, witnessprogram, flags, checker, serror)) { + return false; + } + // Bypass the cleanstack check at the end. The actual stack is obviously not clean + // for witness programs. + stack.resize(1); + } + } + // Additional validation for spend-to-script-hash transactions: if ((flags & SCRIPT_VERIFY_P2SH) && scriptPubKey.IsPayToScriptHash()) { @@ -1287,19 +1365,48 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigne return set_error(serror, SCRIPT_ERR_EVAL_FALSE); if (!CastToBool(stack.back())) return set_error(serror, SCRIPT_ERR_EVAL_FALSE); + + // P2SH witness program + if (flags & SCRIPT_VERIFY_WITNESS) { + if (pubKey2.IsWitnessProgram(witnessversion, witnessprogram)) { + hadWitness = true; + if (scriptSig != CScript() << std::vector(pubKey2.begin(), pubKey2.end())) { + // The scriptSig must be _exactly_ a single push of the redeemScript. Otherwise we + // reintroduce malleability. + return set_error(serror, SCRIPT_ERR_WITNESS_MALLEATED_P2SH); + } + if (!VerifyWitnessProgram(*witness, witnessversion, witnessprogram, flags, checker, serror)) { + return false; + } + // Bypass the cleanstack check at the end. The actual stack is obviously not clean + // for witness programs. + stack.resize(1); + } + } } // The CLEANSTACK check is only performed after potential P2SH evaluation, // as the non-P2SH evaluation of a P2SH script will obviously not result in - // a clean stack (the P2SH inputs remain). + // a clean stack (the P2SH inputs remain). The same holds for witness evaluation. if ((flags & SCRIPT_VERIFY_CLEANSTACK) != 0) { // Disallow CLEANSTACK without P2SH, as otherwise a switch CLEANSTACK->P2SH+CLEANSTACK // would be possible, which is not a softfork (and P2SH should be one). assert((flags & SCRIPT_VERIFY_P2SH) != 0); + assert((flags & SCRIPT_VERIFY_WITNESS) != 0); if (stack.size() != 1) { return set_error(serror, SCRIPT_ERR_CLEANSTACK); } } + if (flags & SCRIPT_VERIFY_WITNESS) { + // We can't check for correct unexpected witness data if P2SH was off, so require + // that WITNESS implies P2SH. Otherwise, going from WITNESS->P2SH+WITNESS would be + // possible, which is not a softfork. + assert((flags & SCRIPT_VERIFY_P2SH) != 0); + if (!hadWitness && !witness->IsNull()) { + return set_error(serror, SCRIPT_ERR_WITNESS_UNEXPECTED); + } + } + return set_success(serror); } -- cgit v1.2.3 From 3dd410294d42f251e4808ef1dfcfcd64817edbac Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 27 Dec 2015 19:49:08 +0100 Subject: BIP143: Verification logic Includes simplifications by Eric Lombrozo. --- src/script/interpreter.cpp | 84 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 72 insertions(+), 12 deletions(-) (limited to 'src/script/interpreter.cpp') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index be649fca2..4deebd050 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -229,7 +229,7 @@ bool static CheckMinimalPush(const valtype& data, opcodetype opcode) { return true; } -bool EvalScript(vector >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror) +bool EvalScript(vector >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror) { static const CScriptNum bnZero(0); static const CScriptNum bnOne(1); @@ -869,13 +869,15 @@ bool EvalScript(vector >& stack, const CScript& script, un CScript scriptCode(pbegincodehash, pend); // Drop the signature, since there's no way for a signature to sign itself - scriptCode.FindAndDelete(CScript(vchSig)); + if (sigversion == SIGVERSION_BASE) { + scriptCode.FindAndDelete(CScript(vchSig)); + } if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, serror)) { //serror is set return false; } - bool fSuccess = checker.CheckSig(vchSig, vchPubKey, scriptCode); + bool fSuccess = checker.CheckSig(vchSig, vchPubKey, scriptCode, sigversion); popstack(stack); popstack(stack); @@ -925,7 +927,9 @@ bool EvalScript(vector >& stack, const CScript& script, un for (int k = 0; k < nSigsCount; k++) { valtype& vchSig = stacktop(-isig-k); - scriptCode.FindAndDelete(CScript(vchSig)); + if (sigversion == SIGVERSION_BASE) { + scriptCode.FindAndDelete(CScript(vchSig)); + } } bool fSuccess = true; @@ -943,7 +947,7 @@ bool EvalScript(vector >& stack, const CScript& script, un } // Check signature - bool fOk = checker.CheckSig(vchSig, vchPubKey, scriptCode); + bool fOk = checker.CheckSig(vchSig, vchPubKey, scriptCode, sigversion); if (fOk) { isig++; @@ -1106,8 +1110,64 @@ public: } // anon namespace -uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType) +uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion) { + if (sigversion == SIGVERSION_WITNESS_V0) { + uint256 hashPrevouts; + uint256 hashSequence; + uint256 hashOutputs; + + if (!(nHashType & SIGHASH_ANYONECANPAY)) { + CHashWriter ss(SER_GETHASH, 0); + for (unsigned int n = 0; n < txTo.vin.size(); n++) { + ss << txTo.vin[n].prevout; + } + hashPrevouts = ss.GetHash(); // TODO: cache this value for all signatures in a transaction + } + + if (!(nHashType & SIGHASH_ANYONECANPAY) && (nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) { + CHashWriter ss(SER_GETHASH, 0); + for (unsigned int n = 0; n < txTo.vin.size(); n++) { + ss << txTo.vin[n].nSequence; + } + hashSequence = ss.GetHash(); // TODO: cache this value for all signatures in a transaction + } + + if ((nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) { + CHashWriter ss(SER_GETHASH, 0); + for (unsigned int n = 0; n < txTo.vout.size(); n++) { + ss << txTo.vout[n]; + } + hashOutputs = ss.GetHash(); // TODO: cache this value for all signatures in a transaction + } else if ((nHashType & 0x1f) == SIGHASH_SINGLE && nIn < txTo.vout.size()) { + CHashWriter ss(SER_GETHASH, 0); + ss << txTo.vout[nIn]; + hashOutputs = ss.GetHash(); + } + + CHashWriter ss(SER_GETHASH, 0); + // Version + ss << txTo.nVersion; + // Input prevouts/nSequence (none/all, depending on flags) + ss << hashPrevouts; + ss << hashSequence; + // The input being signed (replacing the scriptSig with scriptCode + amount) + // The prevout may already be contained in hashPrevout, and the nSequence + // may already be contain in hashSequence. + ss << txTo.vin[nIn].prevout; + ss << static_cast(scriptCode); + ss << amount; + ss << txTo.vin[nIn].nSequence; + // Outputs (none/one/all, depending on flags) + ss << hashOutputs; + // Locktime + ss << txTo.nLockTime; + // Sighash type + ss << nHashType; + + return ss.GetHash(); + } + static const uint256 one(uint256S("0000000000000000000000000000000000000000000000000000000000000001")); if (nIn >= txTo.vin.size()) { // nIn out of range @@ -1136,7 +1196,7 @@ bool TransactionSignatureChecker::VerifySignature(const std::vector& vchSigIn, const vector& vchPubKey, const CScript& scriptCode) const +bool TransactionSignatureChecker::CheckSig(const vector& vchSigIn, const vector& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const { CPubKey pubkey(vchPubKey); if (!pubkey.IsValid()) @@ -1149,7 +1209,7 @@ bool TransactionSignatureChecker::CheckSig(const vector& vchSigIn int nHashType = vchSig.back(); vchSig.pop_back(); - uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType); + uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion); if (!VerifySignature(vchSig, pubkey, sighash)) return false; @@ -1280,7 +1340,7 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, return set_error(serror, SCRIPT_ERR_PUSH_SIZE); } - if (!EvalScript(stack, scriptPubKey, flags, checker, serror)) { + if (!EvalScript(stack, scriptPubKey, flags, checker, SIGVERSION_WITNESS_V0, serror)) { return false; } @@ -1307,12 +1367,12 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C } vector > stack, stackCopy; - if (!EvalScript(stack, scriptSig, flags, checker, serror)) + if (!EvalScript(stack, scriptSig, flags, checker, SIGVERSION_BASE, serror)) // serror is set return false; if (flags & SCRIPT_VERIFY_P2SH) stackCopy = stack; - if (!EvalScript(stack, scriptPubKey, flags, checker, serror)) + if (!EvalScript(stack, scriptPubKey, flags, checker, SIGVERSION_BASE, serror)) // serror is set return false; if (stack.empty()) @@ -1358,7 +1418,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end()); popstack(stack); - if (!EvalScript(stack, pubKey2, flags, checker, serror)) + if (!EvalScript(stack, pubKey2, flags, checker, SIGVERSION_BASE, serror)) // serror is set return false; if (stack.empty()) -- cgit v1.2.3 From 2b1f6f9ccf36f1e0a2c9d99154e1642f796d7c2b Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 3 Jan 2016 18:54:50 +0100 Subject: BIP141: Other consensus critical limits, and BIP145 Includes changes by Suhas Daftuar, Luke-jr, and mruddy. --- src/script/interpreter.cpp | 47 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) (limited to 'src/script/interpreter.cpp') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 4deebd050..bc027e9f0 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1470,3 +1470,50 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C return set_success(serror); } + +size_t static WitnessSigOps(int witversion, const std::vector& witprogram, const CScriptWitness& witness, int flags) +{ + if (witversion == 0) { + if (witprogram.size() == 20) + return 1; + + if (witprogram.size() == 32 && witness.stack.size() > 0) { + CScript subscript(witness.stack.back().begin(), witness.stack.back().end()); + return subscript.GetSigOpCount(true); + } + } + + // Future flags may be implemented here. + return 0; +} + +size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags) +{ + static const CScriptWitness witnessEmpty; + + if ((flags & SCRIPT_VERIFY_WITNESS) == 0) { + return 0; + } + assert((flags & SCRIPT_VERIFY_P2SH) != 0); + + int witnessversion; + std::vector witnessprogram; + if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) { + return WitnessSigOps(witnessversion, witnessprogram, witness ? *witness : witnessEmpty, flags); + } + + if (scriptPubKey.IsPayToScriptHash() && scriptSig.IsPushOnly()) { + CScript::const_iterator pc = scriptSig.begin(); + vector data; + while (pc < scriptSig.end()) { + opcodetype opcode; + scriptSig.GetOp(pc, opcode, data); + } + CScript subscript(data.begin(), data.end()); + if (subscript.IsWitnessProgram(witnessversion, witnessprogram)) { + return WitnessSigOps(witnessversion, witnessprogram, witness ? *witness : witnessEmpty, flags); + } + } + + return 0; +} -- cgit v1.2.3 From d2c5d044d00ec805957ab246a7863d83ca075805 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 16 Aug 2016 15:35:45 +0200 Subject: Precompute sighashes Original version by Nicolas Dorier. Precomputing version by Pieter Wuille. --- src/script/interpreter.cpp | 54 +++++++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 17 deletions(-) (limited to 'src/script/interpreter.cpp') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index bc027e9f0..459feaa0f 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1108,9 +1108,40 @@ public: } }; +uint256 GetPrevoutHash(const CTransaction& txTo) { + CHashWriter ss(SER_GETHASH, 0); + for (unsigned int n = 0; n < txTo.vin.size(); n++) { + ss << txTo.vin[n].prevout; + } + return ss.GetHash(); +} + +uint256 GetSequenceHash(const CTransaction& txTo) { + CHashWriter ss(SER_GETHASH, 0); + for (unsigned int n = 0; n < txTo.vin.size(); n++) { + ss << txTo.vin[n].nSequence; + } + return ss.GetHash(); +} + +uint256 GetOutputsHash(const CTransaction& txTo) { + CHashWriter ss(SER_GETHASH, 0); + for (unsigned int n = 0; n < txTo.vout.size(); n++) { + ss << txTo.vout[n]; + } + return ss.GetHash(); +} + } // anon namespace -uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion) +CachedHashes::CachedHashes(const CTransaction& txTo) +{ + hashPrevouts = GetPrevoutHash(txTo); + hashSequence = GetSequenceHash(txTo); + hashOutputs = GetOutputsHash(txTo); +} + +uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const CachedHashes* cache) { if (sigversion == SIGVERSION_WITNESS_V0) { uint256 hashPrevouts; @@ -1118,27 +1149,16 @@ uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsig uint256 hashOutputs; if (!(nHashType & SIGHASH_ANYONECANPAY)) { - CHashWriter ss(SER_GETHASH, 0); - for (unsigned int n = 0; n < txTo.vin.size(); n++) { - ss << txTo.vin[n].prevout; - } - hashPrevouts = ss.GetHash(); // TODO: cache this value for all signatures in a transaction + hashPrevouts = cache ? cache->hashPrevouts : GetPrevoutHash(txTo); } if (!(nHashType & SIGHASH_ANYONECANPAY) && (nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) { - CHashWriter ss(SER_GETHASH, 0); - for (unsigned int n = 0; n < txTo.vin.size(); n++) { - ss << txTo.vin[n].nSequence; - } - hashSequence = ss.GetHash(); // TODO: cache this value for all signatures in a transaction + hashSequence = cache ? cache->hashSequence : GetSequenceHash(txTo); } + if ((nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) { - CHashWriter ss(SER_GETHASH, 0); - for (unsigned int n = 0; n < txTo.vout.size(); n++) { - ss << txTo.vout[n]; - } - hashOutputs = ss.GetHash(); // TODO: cache this value for all signatures in a transaction + hashOutputs = cache ? cache->hashOutputs : GetOutputsHash(txTo); } else if ((nHashType & 0x1f) == SIGHASH_SINGLE && nIn < txTo.vout.size()) { CHashWriter ss(SER_GETHASH, 0); ss << txTo.vout[nIn]; @@ -1209,7 +1229,7 @@ bool TransactionSignatureChecker::CheckSig(const vector& vchSigIn int nHashType = vchSig.back(); vchSig.pop_back(); - uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion); + uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion, this->cachedHashes); if (!VerifySignature(vchSig, pubkey, sighash)) return false; -- cgit v1.2.3 From 35fe0393f216aa6020fc929272118eade5628636 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 26 Aug 2016 18:38:20 +0200 Subject: Rename to PrecomputedTransactionData --- src/script/interpreter.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/script/interpreter.cpp') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 459feaa0f..47ea261e3 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1134,14 +1134,14 @@ uint256 GetOutputsHash(const CTransaction& txTo) { } // anon namespace -CachedHashes::CachedHashes(const CTransaction& txTo) +PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo) { hashPrevouts = GetPrevoutHash(txTo); hashSequence = GetSequenceHash(txTo); hashOutputs = GetOutputsHash(txTo); } -uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const CachedHashes* cache) +uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache) { if (sigversion == SIGVERSION_WITNESS_V0) { uint256 hashPrevouts; @@ -1229,7 +1229,7 @@ bool TransactionSignatureChecker::CheckSig(const vector& vchSigIn int nHashType = vchSig.back(); vchSig.pop_back(); - uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion, this->cachedHashes); + uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion, this->txdata); if (!VerifySignature(vchSig, pubkey, sighash)) return false; -- cgit v1.2.3 From c72c5b1e3bd42e84465677e94aa83316ff3d9a14 Mon Sep 17 00:00:00 2001 From: Johnson Lau Date: Fri, 23 Sep 2016 13:06:45 +0800 Subject: Make non-minimal OP_IF/NOTIF argument non-standard for P2WSH --- src/script/interpreter.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/script/interpreter.cpp') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 47ea261e3..fd356fed0 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -428,6 +428,12 @@ bool EvalScript(vector >& stack, const CScript& script, un if (stack.size() < 1) return set_error(serror, SCRIPT_ERR_UNBALANCED_CONDITIONAL); valtype& vch = stacktop(-1); + if (sigversion == SIGVERSION_WITNESS_V0 && (flags & SCRIPT_VERIFY_MINIMALIF)) { + if (vch.size() > 1) + return set_error(serror, SCRIPT_ERR_MINIMALIF); + if (vch.size() == 1 && vch[0] != 1) + return set_error(serror, SCRIPT_ERR_MINIMALIF); + } fValue = CastToBool(vch); if (opcode == OP_NOTIF) fValue = !fValue; -- cgit v1.2.3 From e41bd449ab2b8d01260795383af2c40b659d8587 Mon Sep 17 00:00:00 2001 From: Johnson Lau Date: Thu, 22 Sep 2016 15:06:54 +0800 Subject: Add policy: null signature for failed CHECK(MULTI)SIG --- src/script/interpreter.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'src/script/interpreter.cpp') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index fd356fed0..41756ea71 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -885,6 +885,9 @@ bool EvalScript(vector >& stack, const CScript& script, un } bool fSuccess = checker.CheckSig(vchSig, vchPubKey, scriptCode, sigversion); + if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL) && vchSig.size()) + return set_error(serror, SCRIPT_ERR_SIG_NULLFAIL); + popstack(stack); popstack(stack); stack.push_back(fSuccess ? vchTrue : vchFalse); @@ -914,6 +917,9 @@ bool EvalScript(vector >& stack, const CScript& script, un if (nOpCount > MAX_OPS_PER_SCRIPT) return set_error(serror, SCRIPT_ERR_OP_COUNT); int ikey = ++i; + // ikey2 is the position of last non-signature item in the stack. Top stack item = 1. + // With SCRIPT_VERIFY_NULLFAIL, this is used for cleanup if operation fails. + int ikey2 = nKeysCount + 2; i += nKeysCount; if ((int)stack.size() < i) return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); @@ -970,8 +976,14 @@ bool EvalScript(vector >& stack, const CScript& script, un } // Clean up stack of actual arguments - while (i-- > 1) + while (i-- > 1) { + // If the operation failed, we require that all signatures must be empty vector + if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL) && !ikey2 && stacktop(-1).size()) + return set_error(serror, SCRIPT_ERR_SIG_NULLFAIL); + if (ikey2 > 0) + ikey2--; popstack(stack); + } // A bug causes CHECKMULTISIG to consume one extra argument // whose contents were not checked in any way. -- cgit v1.2.3 From acf853df910339412bafb1743f42af1774f5b910 Mon Sep 17 00:00:00 2001 From: Johnson Lau Date: Sat, 15 Oct 2016 23:45:07 +0800 Subject: Add script tests for FindAndDelete in pre-segwit and segwit scripts --- src/script/interpreter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/script/interpreter.cpp') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 41756ea71..e0f2f8919 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -874,7 +874,7 @@ bool EvalScript(vector >& stack, const CScript& script, un // Subset of script starting at the most recent codeseparator CScript scriptCode(pbegincodehash, pend); - // Drop the signature, since there's no way for a signature to sign itself + // Drop the signature in pre-segwit scripts but not segwit scripts if (sigversion == SIGVERSION_BASE) { scriptCode.FindAndDelete(CScript(vchSig)); } @@ -935,7 +935,7 @@ bool EvalScript(vector >& stack, const CScript& script, un // Subset of script starting at the most recent codeseparator CScript scriptCode(pbegincodehash, pend); - // Drop the signatures, since there's no way for a signature to sign itself + // Drop the signature in pre-segwit scripts but not segwit scripts for (int k = 0; k < nSigsCount; k++) { valtype& vchSig = stacktop(-isig-k); -- cgit v1.2.3 From 4c0c25a604cec39675187507df423cff5d707ab5 Mon Sep 17 00:00:00 2001 From: Johnson Lau Date: Sun, 2 Oct 2016 01:19:33 +0800 Subject: Require compressed keys in segwit as policy and disable signing with uncompressed keys for segwit scripts --- src/script/interpreter.cpp | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) (limited to 'src/script/interpreter.cpp') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 41756ea71..836cf9ee3 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -79,8 +79,20 @@ bool static IsCompressedOrUncompressedPubKey(const valtype &vchPubKey) { return false; } } else { - // Non-canonical public key: neither compressed nor uncompressed - return false; + // Non-canonical public key: neither compressed nor uncompressed + return false; + } + return true; +} + +bool static IsCompressedPubKey(const valtype &vchPubKey) { + if (vchPubKey.size() != 33) { + // Non-canonical public key: invalid length for compressed key + return false; + } + if (vchPubKey[0] != 0x02 && vchPubKey[0] != 0x03) { + // Non-canonical public key: invalid prefix for compressed key + return false; } return true; } @@ -199,10 +211,14 @@ bool CheckSignatureEncoding(const vector &vchSig, unsigned int fl return true; } -bool static CheckPubKeyEncoding(const valtype &vchSig, unsigned int flags, ScriptError* serror) { - if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && !IsCompressedOrUncompressedPubKey(vchSig)) { +bool static CheckPubKeyEncoding(const valtype &vchPubKey, unsigned int flags, const SigVersion &sigversion, ScriptError* serror) { + if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && !IsCompressedOrUncompressedPubKey(vchPubKey)) { return set_error(serror, SCRIPT_ERR_PUBKEYTYPE); } + // Only compressed keys are accepted in segwit + if ((flags & SCRIPT_VERIFY_WITNESS_PUBKEYTYPE) != 0 && sigversion == SIGVERSION_WITNESS_V0 && !IsCompressedPubKey(vchPubKey)) { + return set_error(serror, SCRIPT_ERR_WITNESS_PUBKEYTYPE); + } return true; } @@ -879,7 +895,7 @@ bool EvalScript(vector >& stack, const CScript& script, un scriptCode.FindAndDelete(CScript(vchSig)); } - if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, serror)) { + if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, sigversion, serror)) { //serror is set return false; } @@ -953,7 +969,7 @@ bool EvalScript(vector >& stack, const CScript& script, un // Note how this makes the exact order of pubkey/signature evaluation // distinguishable by CHECKMULTISIG NOT if the STRICTENC flag is set. // See the script_(in)valid tests for details. - if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, serror)) { + if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, sigversion, serror)) { // serror is set return false; } -- cgit v1.2.3 From 528472111b4965b1a99c4bcf08ac5ec93d87f10f Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 28 Oct 2016 16:29:17 -0700 Subject: Get rid of nType and nVersion Remove the nType and nVersion as parameters to all serialization methods and functions. There is only one place where it's read and has an impact (in CAddress), and even there it does not impact any of the recursively invoked serializers. Instead, the few places that need nType or nVersion are changed to read it directly from the stream object, through GetType() and GetVersion() methods which are added to all stream classes. --- src/script/interpreter.cpp | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'src/script/interpreter.cpp') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 0e17ddc13..a6403f936 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1069,7 +1069,7 @@ public: /** Serialize the passed scriptCode, skipping OP_CODESEPARATORs */ template - void SerializeScriptCode(S &s, int nType, int nVersion) const { + void SerializeScriptCode(S &s) const { CScript::const_iterator it = scriptCode.begin(); CScript::const_iterator itBegin = it; opcodetype opcode; @@ -1092,53 +1092,53 @@ public: /** Serialize an input of txTo */ template - void SerializeInput(S &s, unsigned int nInput, int nType, int nVersion) const { + void SerializeInput(S &s, unsigned int nInput) const { // In case of SIGHASH_ANYONECANPAY, only the input being signed is serialized if (fAnyoneCanPay) nInput = nIn; // Serialize the prevout - ::Serialize(s, txTo.vin[nInput].prevout, nType, nVersion); + ::Serialize(s, txTo.vin[nInput].prevout); // Serialize the script if (nInput != nIn) // Blank out other inputs' signatures - ::Serialize(s, CScriptBase(), nType, nVersion); + ::Serialize(s, CScriptBase()); else - SerializeScriptCode(s, nType, nVersion); + SerializeScriptCode(s); // Serialize the nSequence if (nInput != nIn && (fHashSingle || fHashNone)) // let the others update at will - ::Serialize(s, (int)0, nType, nVersion); + ::Serialize(s, (int)0); else - ::Serialize(s, txTo.vin[nInput].nSequence, nType, nVersion); + ::Serialize(s, txTo.vin[nInput].nSequence); } /** Serialize an output of txTo */ template - void SerializeOutput(S &s, unsigned int nOutput, int nType, int nVersion) const { + void SerializeOutput(S &s, unsigned int nOutput) const { if (fHashSingle && nOutput != nIn) // Do not lock-in the txout payee at other indices as txin - ::Serialize(s, CTxOut(), nType, nVersion); + ::Serialize(s, CTxOut()); else - ::Serialize(s, txTo.vout[nOutput], nType, nVersion); + ::Serialize(s, txTo.vout[nOutput]); } /** Serialize txTo */ template - void Serialize(S &s, int nType, int nVersion) const { + void Serialize(S &s) const { // Serialize nVersion - ::Serialize(s, txTo.nVersion, nType, nVersion); + ::Serialize(s, txTo.nVersion); // Serialize vin unsigned int nInputs = fAnyoneCanPay ? 1 : txTo.vin.size(); ::WriteCompactSize(s, nInputs); for (unsigned int nInput = 0; nInput < nInputs; nInput++) - SerializeInput(s, nInput, nType, nVersion); + SerializeInput(s, nInput); // Serialize vout unsigned int nOutputs = fHashNone ? 0 : (fHashSingle ? nIn+1 : txTo.vout.size()); ::WriteCompactSize(s, nOutputs); for (unsigned int nOutput = 0; nOutput < nOutputs; nOutput++) - SerializeOutput(s, nOutput, nType, nVersion); + SerializeOutput(s, nOutput); // Serialize nLockTime - ::Serialize(s, txTo.nLockTime, nType, nVersion); + ::Serialize(s, txTo.nLockTime); } }; -- cgit v1.2.3 From 8c1dbc5e9ddbafb77e60e8c4e6eb275a3a76ac12 Mon Sep 17 00:00:00 2001 From: Karl-Johan Alm Date: Fri, 9 Dec 2016 12:01:37 +0900 Subject: Refactor: Removed begin/end_ptr functions. --- src/script/interpreter.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/script/interpreter.cpp') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index a6403f936..1410d0b73 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -856,15 +856,15 @@ bool EvalScript(vector >& stack, const CScript& script, un valtype& vch = stacktop(-1); valtype vchHash((opcode == OP_RIPEMD160 || opcode == OP_SHA1 || opcode == OP_HASH160) ? 20 : 32); if (opcode == OP_RIPEMD160) - CRIPEMD160().Write(begin_ptr(vch), vch.size()).Finalize(begin_ptr(vchHash)); + CRIPEMD160().Write(vch.data(), vch.size()).Finalize(vchHash.data()); else if (opcode == OP_SHA1) - CSHA1().Write(begin_ptr(vch), vch.size()).Finalize(begin_ptr(vchHash)); + CSHA1().Write(vch.data(), vch.size()).Finalize(vchHash.data()); else if (opcode == OP_SHA256) - CSHA256().Write(begin_ptr(vch), vch.size()).Finalize(begin_ptr(vchHash)); + CSHA256().Write(vch.data(), vch.size()).Finalize(vchHash.data()); else if (opcode == OP_HASH160) - CHash160().Write(begin_ptr(vch), vch.size()).Finalize(begin_ptr(vchHash)); + CHash160().Write(vch.data(), vch.size()).Finalize(vchHash.data()); else if (opcode == OP_HASH256) - CHash256().Write(begin_ptr(vch), vch.size()).Finalize(begin_ptr(vchHash)); + CHash256().Write(vch.data(), vch.size()).Finalize(vchHash.data()); popstack(stack); stack.push_back(vchHash); } -- cgit v1.2.3 From 27765b6403cece54320374b37afb01a0cfe571c3 Mon Sep 17 00:00:00 2001 From: isle2983 Date: Sat, 31 Dec 2016 11:01:21 -0700 Subject: Increment MIT Licence copyright header year on files modified in 2016 Edited via: $ contrib/devtools/copyright_header.py update . --- src/script/interpreter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/script/interpreter.cpp') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 1410d0b73..f9b783588 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin Core developers +// Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -- cgit v1.2.3