From b779f5a12a1fd7b2bfc8d194d635dec7d2514209 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Fri, 12 May 2017 02:38:01 +0000 Subject: [PATCH 1/5] script: Allow interpreter to fail with SCRIPT_ERR_NOT_FINAL to indicate a context-dependent/non-final status --- src/script/script_error.h | 2 ++ src/validation.cpp | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/script/script_error.h b/src/script/script_error.h index 3200e94707d..0d57bbeb22b 100644 --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -64,6 +64,8 @@ typedef enum ScriptError_t SCRIPT_ERR_WITNESS_UNEXPECTED, SCRIPT_ERR_WITNESS_PUBKEYTYPE, + SCRIPT_ERR_NOT_FINAL, + SCRIPT_ERR_ERROR_COUNT } ScriptError; diff --git a/src/validation.cpp b/src/validation.cpp index 75a35756d4c..9cdc7eaec9f 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1386,6 +1386,9 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi pvChecks->push_back(CScriptCheck()); check.swap(pvChecks->back()); } else if (!check()) { + if (check.GetScriptError() == SCRIPT_ERR_NOT_FINAL) { + return state.DoS(0, false, REJECT_NONSTANDARD, "non-final"); + } if (flags & STANDARD_NOT_MANDATORY_VERIFY_FLAGS) { // Check whether the failure was caused by a // non-mandatory script verification check, such as From 7aabfd8111ae5a26f6c19c7323b66e3141a36883 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Fri, 12 May 2017 02:54:33 +0000 Subject: [PATCH 2/5] policy: Split STANDARD_SCRIPT_VERIFY_FLAGS between contextual and non-contextual --- src/bitcoin-tx.cpp | 2 +- src/policy/policy.h | 7 +++++-- src/rpc/rawtransaction.cpp | 2 +- src/script/interpreter.h | 2 ++ src/script/sign.cpp | 2 +- src/test/transaction_tests.cpp | 32 ++++++++++++++++---------------- src/validation.cpp | 8 ++++---- 7 files changed, 30 insertions(+), 25 deletions(-) diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 45738b5df83..01052e76581 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -613,7 +613,7 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr) sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i, amount), sigdata, DataFromTransaction(txv, i)); UpdateTransaction(mergedTx, i, sigdata); - if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i, amount))) + if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_NONCONTEXTUAL_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i, amount))) fComplete = false; } diff --git a/src/policy/policy.h b/src/policy/policy.h index 2c2ea9d5b85..e42c115f330 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -51,7 +51,7 @@ static const unsigned int DUST_RELAY_TX_FEE = 1000; * with. However scripts violating these flags may still be present in valid * blocks and we must accept those blocks. */ -static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY_FLAGS | +static const unsigned int STANDARD_CONTEXTUAL_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_STRICTENC | SCRIPT_VERIFY_MINIMALDATA | @@ -67,8 +67,11 @@ static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE; +/** For convenience, standard but not contextual verify flags. */ +static const unsigned int STANDARD_NONCONTEXTUAL_SCRIPT_VERIFY_FLAGS = STANDARD_CONTEXTUAL_SCRIPT_VERIFY_FLAGS & ~CONTEXTUAL_SCRIPT_VERIFY_FLAGS; + /** For convenience, standard but not mandatory verify flags. */ -static const unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS; +static const unsigned int STANDARD_CONTEXTUAL_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_CONTEXTUAL_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS; /** Used as the flags parameter to sequence and nLocktime checks in non-consensus code. */ static const unsigned int STANDARD_LOCKTIME_VERIFY_FLAGS = LOCKTIME_VERIFY_SEQUENCE | diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 3947fb3f7da..a625af8093b 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -784,7 +784,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request) UpdateTransaction(mergedTx, i, sigdata); ScriptError serror = SCRIPT_ERR_OK; - if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) { + if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_NONCONTEXTUAL_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) { TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); } } diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 60f6f711e60..f38716584b9 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -108,6 +108,8 @@ enum SCRIPT_VERIFY_WITNESS_PUBKEYTYPE = (1U << 15), }; +static const unsigned int CONTEXTUAL_SCRIPT_VERIFY_FLAGS = 0; + bool CheckSignatureEncoding(const std::vector &vchSig, unsigned int flags, ScriptError* serror); struct PrecomputedTransactionData diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 56824185466..3c6eb37b912 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -184,7 +184,7 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu sigdata.scriptSig = PushAll(result); // Test solution - return solved && VerifyScript(sigdata.scriptSig, fromPubKey, &sigdata.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker()); + return solved && VerifyScript(sigdata.scriptSig, fromPubKey, &sigdata.scriptWitness, STANDARD_NONCONTEXTUAL_SCRIPT_VERIFY_FLAGS, creator.Checker()); } SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn) diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 3b5da4980bb..684785af420 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -546,11 +546,11 @@ BOOST_AUTO_TEST_CASE(test_witness) CheckWithFlag(output1, input1, 0, true); CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true); CheckWithFlag(output1, input1, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true); - CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); + CheckWithFlag(output1, input1, STANDARD_NONCONTEXTUAL_SCRIPT_VERIFY_FLAGS, true); CheckWithFlag(output1, input2, 0, false); CheckWithFlag(output1, input2, SCRIPT_VERIFY_P2SH, false); CheckWithFlag(output1, input2, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false); - CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false); + CheckWithFlag(output1, input2, STANDARD_NONCONTEXTUAL_SCRIPT_VERIFY_FLAGS, false); // P2SH pay-to-compressed-pubkey. CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(scriptPubkey1)), output1, input1); @@ -559,11 +559,11 @@ BOOST_AUTO_TEST_CASE(test_witness) CheckWithFlag(output1, input1, 0, true); CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true); CheckWithFlag(output1, input1, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true); - CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); + CheckWithFlag(output1, input1, STANDARD_NONCONTEXTUAL_SCRIPT_VERIFY_FLAGS, true); CheckWithFlag(output1, input2, 0, true); CheckWithFlag(output1, input2, SCRIPT_VERIFY_P2SH, false); CheckWithFlag(output1, input2, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false); - CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false); + CheckWithFlag(output1, input2, STANDARD_NONCONTEXTUAL_SCRIPT_VERIFY_FLAGS, false); // Witness pay-to-compressed-pubkey (v0). CreateCreditAndSpend(keystore, GetScriptForWitness(scriptPubkey1), output1, input1); @@ -571,11 +571,11 @@ BOOST_AUTO_TEST_CASE(test_witness) CheckWithFlag(output1, input1, 0, true); CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true); CheckWithFlag(output1, input1, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true); - CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); + CheckWithFlag(output1, input1, STANDARD_NONCONTEXTUAL_SCRIPT_VERIFY_FLAGS, true); CheckWithFlag(output1, input2, 0, true); CheckWithFlag(output1, input2, SCRIPT_VERIFY_P2SH, true); CheckWithFlag(output1, input2, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false); - CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false); + CheckWithFlag(output1, input2, STANDARD_NONCONTEXTUAL_SCRIPT_VERIFY_FLAGS, false); // P2SH witness pay-to-compressed-pubkey (v0). CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptPubkey1))), output1, input1); @@ -584,11 +584,11 @@ BOOST_AUTO_TEST_CASE(test_witness) CheckWithFlag(output1, input1, 0, true); CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true); CheckWithFlag(output1, input1, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true); - CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); + CheckWithFlag(output1, input1, STANDARD_NONCONTEXTUAL_SCRIPT_VERIFY_FLAGS, true); CheckWithFlag(output1, input2, 0, true); CheckWithFlag(output1, input2, SCRIPT_VERIFY_P2SH, true); CheckWithFlag(output1, input2, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false); - CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false); + CheckWithFlag(output1, input2, STANDARD_NONCONTEXTUAL_SCRIPT_VERIFY_FLAGS, false); // Normal pay-to-uncompressed-pubkey. CreateCreditAndSpend(keystore, scriptPubkey1L, output1, input1); @@ -596,11 +596,11 @@ BOOST_AUTO_TEST_CASE(test_witness) CheckWithFlag(output1, input1, 0, true); CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true); CheckWithFlag(output1, input1, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true); - CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); + CheckWithFlag(output1, input1, STANDARD_NONCONTEXTUAL_SCRIPT_VERIFY_FLAGS, true); CheckWithFlag(output1, input2, 0, false); CheckWithFlag(output1, input2, SCRIPT_VERIFY_P2SH, false); CheckWithFlag(output1, input2, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false); - CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false); + CheckWithFlag(output1, input2, STANDARD_NONCONTEXTUAL_SCRIPT_VERIFY_FLAGS, false); // P2SH pay-to-uncompressed-pubkey. CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(scriptPubkey1L)), output1, input1); @@ -609,11 +609,11 @@ BOOST_AUTO_TEST_CASE(test_witness) CheckWithFlag(output1, input1, 0, true); CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true); CheckWithFlag(output1, input1, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true); - CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); + CheckWithFlag(output1, input1, STANDARD_NONCONTEXTUAL_SCRIPT_VERIFY_FLAGS, true); CheckWithFlag(output1, input2, 0, true); CheckWithFlag(output1, input2, SCRIPT_VERIFY_P2SH, false); CheckWithFlag(output1, input2, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false); - CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false); + CheckWithFlag(output1, input2, STANDARD_NONCONTEXTUAL_SCRIPT_VERIFY_FLAGS, false); // Signing disabled for witness pay-to-uncompressed-pubkey (v1). CreateCreditAndSpend(keystore, GetScriptForWitness(scriptPubkey1L), output1, input1, false); @@ -630,7 +630,7 @@ BOOST_AUTO_TEST_CASE(test_witness) CheckWithFlag(output2, input2, 0, false); BOOST_CHECK(*output1 == *output2); UpdateTransaction(input1, 0, CombineSignatures(output1->vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1->vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0))); - CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); + CheckWithFlag(output1, input1, STANDARD_NONCONTEXTUAL_SCRIPT_VERIFY_FLAGS, true); // P2SH 2-of-2 multisig CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(scriptMulti)), output1, input1, false); @@ -642,7 +642,7 @@ BOOST_AUTO_TEST_CASE(test_witness) BOOST_CHECK(*output1 == *output2); UpdateTransaction(input1, 0, CombineSignatures(output1->vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1->vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0))); CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true); - CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); + CheckWithFlag(output1, input1, STANDARD_NONCONTEXTUAL_SCRIPT_VERIFY_FLAGS, true); // Witness 2-of-2 multisig CreateCreditAndSpend(keystore, GetScriptForWitness(scriptMulti), output1, input1, false); @@ -654,7 +654,7 @@ BOOST_AUTO_TEST_CASE(test_witness) BOOST_CHECK(*output1 == *output2); UpdateTransaction(input1, 0, CombineSignatures(output1->vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1->vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0))); CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true); - CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); + CheckWithFlag(output1, input1, STANDARD_NONCONTEXTUAL_SCRIPT_VERIFY_FLAGS, true); // P2SH witness 2-of-2 multisig CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptMulti))), output1, input1, false); @@ -666,7 +666,7 @@ BOOST_AUTO_TEST_CASE(test_witness) BOOST_CHECK(*output1 == *output2); UpdateTransaction(input1, 0, CombineSignatures(output1->vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1->vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0))); CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true); - CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); + CheckWithFlag(output1, input1, STANDARD_NONCONTEXTUAL_SCRIPT_VERIFY_FLAGS, true); } BOOST_AUTO_TEST_CASE(test_IsStandard) diff --git a/src/validation.cpp b/src/validation.cpp index 9cdc7eaec9f..01eb343b762 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -683,7 +683,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C if (tx.HasWitness() && fRequireStandard && !IsWitnessStandard(tx, view)) return state.DoS(0, false, REJECT_NONSTANDARD, "bad-witness-nonstandard", true); - int64_t nSigOpsCost = GetTransactionSigOpCost(tx, view, STANDARD_SCRIPT_VERIFY_FLAGS); + int64_t nSigOpsCost = GetTransactionSigOpCost(tx, view, STANDARD_NONCONTEXTUAL_SCRIPT_VERIFY_FLAGS); CAmount nValueOut = tx.GetValueOut(); CAmount nFees = nValueIn-nValueOut; @@ -885,7 +885,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C } } - unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS; + unsigned int scriptVerifyFlags = STANDARD_CONTEXTUAL_SCRIPT_VERIFY_FLAGS; if (!Params().RequireStandard()) { scriptVerifyFlags = GetArg("-promiscuousmempoolflags", scriptVerifyFlags); } @@ -1389,7 +1389,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi if (check.GetScriptError() == SCRIPT_ERR_NOT_FINAL) { return state.DoS(0, false, REJECT_NONSTANDARD, "non-final"); } - if (flags & STANDARD_NOT_MANDATORY_VERIFY_FLAGS) { + if (flags & STANDARD_CONTEXTUAL_NOT_MANDATORY_VERIFY_FLAGS) { // Check whether the failure was caused by a // non-mandatory script verification check, such as // non-standard DER encodings or non-null dummy @@ -1397,7 +1397,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi // avoid splitting the network between upgraded and // non-upgraded nodes. CScriptCheck check2(*coins, tx, i, - flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore, &txdata); + flags & ~STANDARD_CONTEXTUAL_NOT_MANDATORY_VERIFY_FLAGS, cacheStore, &txdata); if (check2()) return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError()))); } From 04b0a00c72ceff7c0437d2f3d636df4697be6004 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Fri, 12 May 2017 03:14:36 +0000 Subject: [PATCH 3/5] Pass CChain through to signature checker in contextual verifications --- src/rpc/rawtransaction.cpp | 4 ++-- src/script/bitcoinconsensus.cpp | 2 +- src/script/interpreter.h | 8 +++++--- src/script/sigcache.h | 2 +- src/script/sign.cpp | 2 +- src/test/script_P2SH_tests.cpp | 2 +- src/test/sigopcount_tests.cpp | 2 +- src/test/transaction_tests.cpp | 8 ++++---- src/validation.cpp | 27 ++++++++++++++------------- src/validation.h | 10 ++++++---- 10 files changed, 36 insertions(+), 31 deletions(-) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index a625af8093b..299185c658a 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -777,14 +777,14 @@ UniValue signrawtransaction(const JSONRPCRequest& request) // ... and merge in other signatures: BOOST_FOREACH(const CMutableTransaction& txv, txVariants) { if (txv.vin.size() > i) { - sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(txv, i)); + sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount, nullptr), sigdata, DataFromTransaction(txv, i)); } } UpdateTransaction(mergedTx, i, sigdata); ScriptError serror = SCRIPT_ERR_OK; - if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_NONCONTEXTUAL_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) { + if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_NONCONTEXTUAL_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount, nullptr), &serror)) { TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); } } diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp index c4ab441e2ca..a0bba9612bc 100644 --- a/src/script/bitcoinconsensus.cpp +++ b/src/script/bitcoinconsensus.cpp @@ -95,7 +95,7 @@ static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptP set_error(err, bitcoinconsensus_ERR_OK); PrecomputedTransactionData txdata(tx); - return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), &tx.vin[nIn].scriptWitness, flags, TransactionSignatureChecker(&tx, nIn, amount, txdata), NULL); + return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), &tx.vin[nIn].scriptWitness, flags, TransactionSignatureChecker(&tx, nIn, amount, nullptr, txdata), NULL); } catch (const std::exception&) { return set_error(err, bitcoinconsensus_ERR_TX_DESERIALIZE); // Error deserializing } diff --git a/src/script/interpreter.h b/src/script/interpreter.h index f38716584b9..6ec509fdedf 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -13,6 +13,7 @@ #include #include +class CChain; class CPubKey; class CScript; class CTransaction; @@ -154,14 +155,15 @@ class TransactionSignatureChecker : public BaseSignatureChecker const CTransaction* txTo; unsigned int nIn; const CAmount amount; + const CChain* chain; const PrecomputedTransactionData* txdata; protected: virtual bool VerifySignature(const std::vector& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const; public: - TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(NULL) {} - TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {} + TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const CChain* chainIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), chain(chainIn), txdata(NULL) {} + TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const CChain* chainIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), chain(chainIn), txdata(&txdataIn) {} bool CheckSig(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const; bool CheckLockTime(const CScriptNum& nLockTime) const; bool CheckSequence(const CScriptNum& nSequence) const; @@ -173,7 +175,7 @@ class MutableTransactionSignatureChecker : public TransactionSignatureChecker const CTransaction txTo; public: - MutableTransactionSignatureChecker(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn) : TransactionSignatureChecker(&txTo, nInIn, amountIn), txTo(*txToIn) {} + MutableTransactionSignatureChecker(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn) : TransactionSignatureChecker(&txTo, nInIn, amountIn, nullptr), txTo(*txToIn) {} }; bool EvalScript(std::vector >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* error = NULL); diff --git a/src/script/sigcache.h b/src/script/sigcache.h index 55cec4cc8d7..3a7eccc0882 100644 --- a/src/script/sigcache.h +++ b/src/script/sigcache.h @@ -46,7 +46,7 @@ class CachingTransactionSignatureChecker : public TransactionSignatureChecker bool store; public: - CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, bool storeIn, PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nInIn, amountIn, txdataIn), store(storeIn) {} + CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const CChain* chainIn, bool storeIn, PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nInIn, amountIn, chainIn, txdataIn), store(storeIn) {} bool VerifySignature(const std::vector& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const; }; diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 3c6eb37b912..e60c0b24dcf 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -16,7 +16,7 @@ typedef std::vector valtype; -TransactionSignatureCreator::TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : BaseSignatureCreator(keystoreIn), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {} +TransactionSignatureCreator::TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : BaseSignatureCreator(keystoreIn), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn, nullptr) {} bool TransactionSignatureCreator::CreateSig(std::vector& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion) const { diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index f8fd8cc30cf..c8ef6249b63 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -111,7 +111,7 @@ BOOST_AUTO_TEST_CASE(sign) { CScript sigSave = txTo[i].vin[0].scriptSig; txTo[i].vin[0].scriptSig = txTo[j].vin[0].scriptSig; - bool sigOK = CScriptCheck(CCoins(txFrom, 0), txTo[i], 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, false, &txdata)(); + bool sigOK = CScriptCheck(CCoins(txFrom, 0), txTo[i], 0, nullptr, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, false, &txdata)(); if (i == j) BOOST_CHECK_MESSAGE(sigOK, strprintf("VerifySignature %d %d", i, j)); else diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp index 13d8911f03d..108c01b5ec9 100644 --- a/src/test/sigopcount_tests.cpp +++ b/src/test/sigopcount_tests.cpp @@ -71,7 +71,7 @@ ScriptError VerifyWithFlag(const CTransaction& output, const CMutableTransaction { ScriptError error; CTransaction inputi(input); - bool ret = VerifyScript(inputi.vin[0].scriptSig, output.vout[0].scriptPubKey, &inputi.vin[0].scriptWitness, flags, TransactionSignatureChecker(&inputi, 0, output.vout[0].nValue), &error); + bool ret = VerifyScript(inputi.vin[0].scriptSig, output.vout[0].scriptPubKey, &inputi.vin[0].scriptWitness, flags, TransactionSignatureChecker(&inputi, 0, output.vout[0].nValue, nullptr), &error); BOOST_CHECK((ret == true) == (error == SCRIPT_ERR_OK)); return error; diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 684785af420..e172efeca39 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -170,7 +170,7 @@ BOOST_AUTO_TEST_CASE(tx_valid) unsigned int verify_flags = ParseScriptFlags(test[2].get_str()); const CScriptWitness *witness = &tx.vin[i].scriptWitness; BOOST_CHECK_MESSAGE(VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout], - witness, verify_flags, TransactionSignatureChecker(&tx, i, amount, txdata), &err), + witness, verify_flags, TransactionSignatureChecker(&tx, i, amount, nullptr, txdata), &err), strTest); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); } @@ -256,7 +256,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid) } const CScriptWitness *witness = &tx.vin[i].scriptWitness; fValid = VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout], - witness, verify_flags, TransactionSignatureChecker(&tx, i, amount, txdata), &err); + witness, verify_flags, TransactionSignatureChecker(&tx, i, amount, nullptr, txdata), &err); } BOOST_CHECK_MESSAGE(!fValid, strTest); BOOST_CHECK_MESSAGE(err != SCRIPT_ERR_OK, ScriptErrorString(err)); @@ -386,7 +386,7 @@ void CheckWithFlag(const CTransactionRef& output, const CMutableTransaction& inp { ScriptError error; CTransaction inputi(input); - bool ret = VerifyScript(inputi.vin[0].scriptSig, output->vout[0].scriptPubKey, &inputi.vin[0].scriptWitness, flags, TransactionSignatureChecker(&inputi, 0, output->vout[0].nValue), &error); + bool ret = VerifyScript(inputi.vin[0].scriptSig, output->vout[0].scriptPubKey, &inputi.vin[0].scriptWitness, flags, TransactionSignatureChecker(&inputi, 0, output->vout[0].nValue, nullptr), &error); assert(ret == success); } @@ -481,7 +481,7 @@ BOOST_AUTO_TEST_CASE(test_big_witness_transaction) { for(uint32_t i = 0; i < mtx.vin.size(); i++) { std::vector vChecks; - CScriptCheck check(coins, tx, i, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false, &txdata); + CScriptCheck check(coins, tx, i, nullptr, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false, &txdata); vChecks.push_back(CScriptCheck()); check.swap(vChecks.back()); control.Add(vChecks); diff --git a/src/validation.cpp b/src/validation.cpp index 01eb343b762..c606e82e283 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -893,13 +893,13 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. PrecomputedTransactionData txdata(tx); - if (!CheckInputs(tx, state, view, true, scriptVerifyFlags, true, txdata)) { + if (!CheckInputs(tx, state, view, true, chainActive, scriptVerifyFlags, true, txdata)) { // SCRIPT_VERIFY_CLEANSTACK requires SCRIPT_VERIFY_WITNESS, so we // need to turn both off, and compare against just turning off CLEANSTACK // to see if the failure is specifically due to witness validation. CValidationState stateDummy; // Want reported failures to be from first CheckInputs - if (!tx.HasWitness() && CheckInputs(tx, stateDummy, view, true, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, txdata) && - !CheckInputs(tx, stateDummy, view, true, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, txdata)) { + if (!tx.HasWitness() && CheckInputs(tx, stateDummy, view, true, chainActive, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, txdata) && + !CheckInputs(tx, stateDummy, view, true, chainActive, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, txdata)) { // Only the witness is missing, so the transaction itself may be fine. state.SetCorruptionPossible(); } @@ -915,7 +915,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C // There is a similar check in CreateNewBlock() to prevent creating // invalid blocks, however allowing such transactions into the mempool // can be exploited as a DoS attack. - if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata)) + if (!CheckInputs(tx, state, view, true, chainActive, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata)) { return error("%s: BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); @@ -1299,7 +1299,7 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight) bool CScriptCheck::operator()() { const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; const CScriptWitness *witness = &ptxTo->vin[nIn].scriptWitness; - return VerifyScript(scriptSig, scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, amount, cacheStore, *txdata), &error); + return VerifyScript(scriptSig, scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, amount, chain, cacheStore, *txdata), &error); } int GetSpendHeight(const CCoinsViewCache& inputs) @@ -1355,7 +1355,7 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins } }// namespace Consensus -bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, PrecomputedTransactionData& txdata, std::vector *pvChecks) +bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, const CChain& chain, unsigned int flags, bool cacheStore, PrecomputedTransactionData& txdata, std::vector *pvChecks) { if (!tx.IsCoinBase()) { @@ -1381,7 +1381,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi assert(coins); // Verify signature - CScriptCheck check(*coins, tx, i, flags, cacheStore, &txdata); + CScriptCheck check(*coins, tx, i, &chain, flags, cacheStore, &txdata); if (pvChecks) { pvChecks->push_back(CScriptCheck()); check.swap(pvChecks->back()); @@ -1396,7 +1396,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi // arguments; if so, don't trigger DoS protection to // avoid splitting the network between upgraded and // non-upgraded nodes. - CScriptCheck check2(*coins, tx, i, + CScriptCheck check2(*coins, tx, i, &chain, flags & ~STANDARD_CONTEXTUAL_NOT_MANDATORY_VERIFY_FLAGS, cacheStore, &txdata); if (check2()) return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError()))); @@ -1692,7 +1692,7 @@ static int64_t nTimeTotal = 0; * Validity checks that depend on the UTXO set are also done; ConnectBlock() * can fail if those validity checks fail (among other reasons). */ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, - CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck = false) + CCoinsViewCache& view, const CChain& chain, const CChainParams& chainparams, bool fJustCheck = false) { AssertLockHeld(cs_main); assert(pindex); @@ -1869,7 +1869,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd std::vector vChecks; bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */ - if (!CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, txdata[i], nScriptCheckThreads ? &vChecks : NULL)) + if (!CheckInputs(tx, state, view, fScriptChecks, chain, flags, fCacheResults, txdata[i], nScriptCheckThreads ? &vChecks : NULL)) return error("ConnectBlock(): CheckInputs on %s failed with %s", tx.GetHash().ToString(), FormatStateMessage(state)); control.Add(vChecks); @@ -2273,7 +2273,7 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, LogPrint(BCLog::BENCH, " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * 0.001, nTimeReadFromDisk * 0.000001); { CCoinsViewCache view(pcoinsTip); - bool rv = ConnectBlock(blockConnecting, state, pindexNew, view, chainparams); + bool rv = ConnectBlock(blockConnecting, state, pindexNew, view, chainActive, chainparams); GetMainSignals().BlockChecked(blockConnecting, state); if (!rv) { if (state.IsInvalid()) @@ -3280,7 +3280,7 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state)); if (!ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindexPrev)) return error("%s: Consensus::ContextualCheckBlock: %s", __func__, FormatStateMessage(state)); - if (!ConnectBlock(block, state, &indexDummy, viewNew, chainparams, true)) + if (!ConnectBlock(block, state, &indexDummy, viewNew, chainActive, chainparams, true)) return false; assert(state.IsValid()); @@ -3692,7 +3692,8 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, CBlock block; if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus())) return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); - if (!ConnectBlock(block, state, pindex, coins, chainparams)) + // FIXME: Need to shadow chainActive with the older height! + if (!ConnectBlock(block, state, pindex, coins, chainActive, chainparams)) return error("VerifyDB(): *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); } } diff --git a/src/validation.h b/src/validation.h index 8ddceb23065..c9a194c4797 100644 --- a/src/validation.h +++ b/src/validation.h @@ -373,7 +373,7 @@ int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& i * instead of being performed inline. */ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &view, bool fScriptChecks, - unsigned int flags, bool cacheStore, PrecomputedTransactionData& txdata, std::vector *pvChecks = NULL); + const CChain& chain, unsigned int flags, bool cacheStore, PrecomputedTransactionData& txdata, std::vector *pvChecks = NULL); /** Apply the effects of this transaction on the UTXO set represented by view */ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight); @@ -444,16 +444,17 @@ class CScriptCheck CAmount amount; const CTransaction *ptxTo; unsigned int nIn; + const CChain *chain; unsigned int nFlags; bool cacheStore; ScriptError error; PrecomputedTransactionData *txdata; public: - CScriptCheck(): amount(0), ptxTo(0), nIn(0), nFlags(0), cacheStore(false), error(SCRIPT_ERR_UNKNOWN_ERROR) {} - CScriptCheck(const CCoins& txFromIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn, PrecomputedTransactionData* txdataIn) : + CScriptCheck(): amount(0), ptxTo(0), nIn(0), chain(nullptr), nFlags(0), cacheStore(false), error(SCRIPT_ERR_UNKNOWN_ERROR) {} + CScriptCheck(const CCoins& txFromIn, const CTransaction& txToIn, unsigned int nInIn, const CChain* chainIn, unsigned int nFlagsIn, bool cacheIn, PrecomputedTransactionData* txdataIn) : scriptPubKey(txFromIn.vout[txToIn.vin[nInIn].prevout.n].scriptPubKey), amount(txFromIn.vout[txToIn.vin[nInIn].prevout.n].nValue), - ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), error(SCRIPT_ERR_UNKNOWN_ERROR), txdata(txdataIn) { } + ptxTo(&txToIn), nIn(nInIn), chain(chainIn), nFlags(nFlagsIn), cacheStore(cacheIn), error(SCRIPT_ERR_UNKNOWN_ERROR), txdata(txdataIn) { } bool operator()(); @@ -462,6 +463,7 @@ class CScriptCheck std::swap(ptxTo, check.ptxTo); std::swap(amount, check.amount); std::swap(nIn, check.nIn); + std::swap(chain, check.chain); std::swap(nFlags, check.nFlags); std::swap(cacheStore, check.cacheStore); std::swap(error, check.error); From c1bf3d75ede6d98678301f90534a889fd03228a6 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Fri, 12 May 2017 03:30:35 +0000 Subject: [PATCH 4/5] script: Implement OP_CHECKBLOCKATHEIGHT --- src/policy/policy.h | 3 +- src/script/bitcoinconsensus.h | 1 + src/script/interpreter.cpp | 54 +++++++++++++++++++++++++++++++- src/script/interpreter.h | 11 ++++++- src/script/script.cpp | 2 +- src/script/script.h | 3 +- src/test/data/script_tests.json | 12 +++---- test/functional/test_framework/script.py | 8 ++--- 8 files changed, 79 insertions(+), 15 deletions(-) diff --git a/src/policy/policy.h b/src/policy/policy.h index e42c115f330..6d52f875d49 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -65,7 +65,8 @@ static const unsigned int STANDARD_CONTEXTUAL_SCRIPT_VERIFY_FLAGS = MANDATORY_SC SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM | - SCRIPT_VERIFY_WITNESS_PUBKEYTYPE; + SCRIPT_VERIFY_WITNESS_PUBKEYTYPE | + SCRIPT_VERIFY_CHECKBLOCKATHEIGHT; /** For convenience, standard but not contextual verify flags. */ static const unsigned int STANDARD_NONCONTEXTUAL_SCRIPT_VERIFY_FLAGS = STANDARD_CONTEXTUAL_SCRIPT_VERIFY_FLAGS & ~CONTEXTUAL_SCRIPT_VERIFY_FLAGS; diff --git a/src/script/bitcoinconsensus.h b/src/script/bitcoinconsensus.h index 1bef4fe9e97..f37ca8965a4 100644 --- a/src/script/bitcoinconsensus.h +++ b/src/script/bitcoinconsensus.h @@ -55,6 +55,7 @@ enum bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9), // enable CHECKLOCKTIMEVERIFY (BIP65) bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKSEQUENCEVERIFY = (1U << 10), // enable CHECKSEQUENCEVERIFY (BIP112) bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS = (1U << 11), // enable WITNESS (BIP141) + // NOTE: Do NOT add CHECKBLOCKATHEIGHT here without updating verify_script to pass context! bitcoinconsensus_SCRIPT_FLAGS_VERIFY_ALL = bitcoinconsensus_SCRIPT_FLAGS_VERIFY_P2SH | bitcoinconsensus_SCRIPT_FLAGS_VERIFY_DERSIG | bitcoinconsensus_SCRIPT_FLAGS_VERIFY_NULLDUMMY | bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKLOCKTIMEVERIFY | bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKSEQUENCEVERIFY | bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index f4e5313a789..c29508296d3 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -5,6 +5,7 @@ #include "interpreter.h" +#include "chain.h" #include "primitives/transaction.h" #include "crypto/ripemd160.h" #include "crypto/sha1.h" @@ -424,7 +425,36 @@ bool EvalScript(std::vector >& stack, const CScript& break; } - case OP_NOP1: case OP_NOP4: case OP_NOP5: + case OP_CHECKBLOCKATHEIGHT: + { + if (!(flags & SCRIPT_VERIFY_CHECKBLOCKATHEIGHT)) { + // not enabled; treat as a NOP5 + if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) { + return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS); + } + break; + } + + if (stack.size() < 2) { + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + } + + // nHeight is a 32-bit signed integer field. + const int32_t nHeight = CScriptNum(stacktop(-1), true, 4).getint(); + + valtype vchHashCheck = stacktop(-2); + + // Compare the specified block hash with the input. + if (!checker.CheckBlockHash(nHeight, vchHashCheck)) { + // Not final rather than a hard reject to avoid caching across different blockchains + // Also because it will *eventually* become final when the height gets old enough + return set_error(serror, SCRIPT_ERR_NOT_FINAL); + } + + break; + } + + case OP_NOP1: case OP_NOP4: case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10: { if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) @@ -1351,6 +1381,28 @@ bool TransactionSignatureChecker::CheckSequence(const CScriptNum& nSequence) con return true; } +bool TransactionSignatureChecker::CheckBlockHash(const int32_t nHeight, const std::vector& vchCompareTo) const +{ + if (!chain) { + return false; + } + + // If the chain doesn't reach the desired height yet, the transaction is non-final + if (nHeight > chain->Height()) { + return false; + } + + // Sufficiently old blocks are always valid + if (nHeight <= chain->Height() - 52596) { + return true; + } + + CBlockIndex* pblockindex = (*chain)[nHeight]; + std::vector vchBlockHash(pblockindex->GetBlockHash().begin(), pblockindex->GetBlockHash().end()); + vchBlockHash.erase(vchBlockHash.begin(), vchBlockHash.end() - vchCompareTo.size()); + return (vchCompareTo == vchBlockHash); +} + static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror) { std::vector > stack; diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 6ec509fdedf..95b473947de 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -107,9 +107,12 @@ enum // Public keys in segregated witness scripts must be compressed // SCRIPT_VERIFY_WITNESS_PUBKEYTYPE = (1U << 15), + + // Enforce CHECKBLOCKATHEIGHT opcode + SCRIPT_VERIFY_CHECKBLOCKATHEIGHT = (1U << 16), }; -static const unsigned int CONTEXTUAL_SCRIPT_VERIFY_FLAGS = 0; +static const unsigned int CONTEXTUAL_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_CHECKBLOCKATHEIGHT; bool CheckSignatureEncoding(const std::vector &vchSig, unsigned int flags, ScriptError* serror); @@ -146,6 +149,11 @@ class BaseSignatureChecker return false; } + virtual bool CheckBlockHash(const int32_t nHeight, const std::vector& nBlockHash) const + { + return false; + } + virtual ~BaseSignatureChecker() {} }; @@ -167,6 +175,7 @@ class TransactionSignatureChecker : public BaseSignatureChecker bool CheckSig(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const; bool CheckLockTime(const CScriptNum& nLockTime) const; bool CheckSequence(const CScriptNum& nSequence) const; + bool CheckBlockHash(const int32_t nHeight, const std::vector& nBlockHash) const; }; class MutableTransactionSignatureChecker : public TransactionSignatureChecker diff --git a/src/script/script.cpp b/src/script/script.cpp index 70eb8a139b0..43db5f7d220 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -132,7 +132,7 @@ const char* GetOpName(opcodetype opcode) case OP_CHECKLOCKTIMEVERIFY : return "OP_CHECKLOCKTIMEVERIFY"; case OP_CHECKSEQUENCEVERIFY : return "OP_CHECKSEQUENCEVERIFY"; case OP_NOP4 : return "OP_NOP4"; - case OP_NOP5 : return "OP_NOP5"; + case OP_CHECKBLOCKATHEIGHT : return "OP_CHECKBLOCKATHEIGHT"; case OP_NOP6 : return "OP_NOP6"; case OP_NOP7 : return "OP_NOP7"; case OP_NOP8 : return "OP_NOP8"; diff --git a/src/script/script.h b/src/script/script.h index d7aaa04f802..7f81a5d51f9 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -170,7 +170,8 @@ enum opcodetype OP_CHECKSEQUENCEVERIFY = 0xb2, OP_NOP3 = OP_CHECKSEQUENCEVERIFY, OP_NOP4 = 0xb3, - OP_NOP5 = 0xb4, + OP_CHECKBLOCKATHEIGHT = 0xb4, + OP_NOP5 = OP_CHECKBLOCKATHEIGHT, OP_NOP6 = 0xb5, OP_NOP7 = 0xb6, OP_NOP8 = 0xb7, diff --git a/src/test/data/script_tests.json b/src/test/data/script_tests.json index 5c054ed3e8f..f0b0f6a77e2 100644 --- a/src/test/data/script_tests.json +++ b/src/test/data/script_tests.json @@ -232,8 +232,8 @@ ["'abcdefghijklmnopqrstuvwxyz'", "HASH256 0x4c 0x20 0xca139bc10c2f660da42666f72e89a225936fc60f193c161124a672050c434671 EQUAL", "P2SH,STRICTENC", "OK"], -["1","NOP1 CHECKLOCKTIMEVERIFY CHECKSEQUENCEVERIFY NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10 1 EQUAL", "P2SH,STRICTENC", "OK"], -["'NOP_1_to_10' NOP1 CHECKLOCKTIMEVERIFY CHECKSEQUENCEVERIFY NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10","'NOP_1_to_10' EQUAL", "P2SH,STRICTENC", "OK"], +["1","NOP1 CHECKLOCKTIMEVERIFY CHECKSEQUENCEVERIFY NOP4 CHECKBLOCKATHEIGHT NOP6 NOP7 NOP8 NOP9 NOP10 1 EQUAL", "P2SH,STRICTENC", "OK"], +["'NOP_1_to_10' NOP1 CHECKLOCKTIMEVERIFY CHECKSEQUENCEVERIFY NOP4 CHECKBLOCKATHEIGHT NOP6 NOP7 NOP8 NOP9 NOP10","'NOP_1_to_10' EQUAL", "P2SH,STRICTENC", "OK"], ["1", "NOP", "P2SH,STRICTENC,DISCOURAGE_UPGRADABLE_NOPS", "OK", "Discourage NOPx flag allows OP_NOP"], @@ -445,7 +445,7 @@ ["NOP", "CHECKLOCKTIMEVERIFY 1", "P2SH,STRICTENC", "OK"], ["NOP", "CHECKSEQUENCEVERIFY 1", "P2SH,STRICTENC", "OK"], ["NOP", "NOP4 1", "P2SH,STRICTENC", "OK"], -["NOP", "NOP5 1", "P2SH,STRICTENC", "OK"], +["NOP", "CHECKBLOCKATHEIGHT 1", "P2SH,STRICTENC", "OK"], ["NOP", "NOP6 1", "P2SH,STRICTENC", "OK"], ["NOP", "NOP7 1", "P2SH,STRICTENC", "OK"], ["NOP", "NOP8 1", "P2SH,STRICTENC", "OK"], @@ -857,15 +857,15 @@ ["2 2 LSHIFT", "8 EQUAL", "P2SH,STRICTENC", "DISABLED_OPCODE", "disabled"], ["2 1 RSHIFT", "1 EQUAL", "P2SH,STRICTENC", "DISABLED_OPCODE", "disabled"], -["1", "NOP1 CHECKLOCKTIMEVERIFY CHECKSEQUENCEVERIFY NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10 2 EQUAL", "P2SH,STRICTENC", "EVAL_FALSE"], -["'NOP_1_to_10' NOP1 CHECKLOCKTIMEVERIFY CHECKSEQUENCEVERIFY NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10","'NOP_1_to_11' EQUAL", "P2SH,STRICTENC", "EVAL_FALSE"], +["1", "NOP1 CHECKLOCKTIMEVERIFY CHECKSEQUENCEVERIFY NOP4 CHECKBLOCKATHEIGHT NOP6 NOP7 NOP8 NOP9 NOP10 2 EQUAL", "P2SH,STRICTENC", "EVAL_FALSE"], +["'NOP_1_to_10' NOP1 CHECKLOCKTIMEVERIFY CHECKSEQUENCEVERIFY NOP4 CHECKBLOCKATHEIGHT NOP6 NOP7 NOP8 NOP9 NOP10","'NOP_1_to_11' EQUAL", "P2SH,STRICTENC", "EVAL_FALSE"], ["Ensure 100% coverage of discouraged NOPS"], ["1", "NOP1", "P2SH,DISCOURAGE_UPGRADABLE_NOPS", "DISCOURAGE_UPGRADABLE_NOPS"], ["1", "CHECKLOCKTIMEVERIFY", "P2SH,DISCOURAGE_UPGRADABLE_NOPS", "DISCOURAGE_UPGRADABLE_NOPS"], ["1", "CHECKSEQUENCEVERIFY", "P2SH,DISCOURAGE_UPGRADABLE_NOPS", "DISCOURAGE_UPGRADABLE_NOPS"], ["1", "NOP4", "P2SH,DISCOURAGE_UPGRADABLE_NOPS", "DISCOURAGE_UPGRADABLE_NOPS"], -["1", "NOP5", "P2SH,DISCOURAGE_UPGRADABLE_NOPS", "DISCOURAGE_UPGRADABLE_NOPS"], +["1", "CHECKBLOCKATHEIGHT", "P2SH,DISCOURAGE_UPGRADABLE_NOPS", "DISCOURAGE_UPGRADABLE_NOPS"], ["1", "NOP6", "P2SH,DISCOURAGE_UPGRADABLE_NOPS", "DISCOURAGE_UPGRADABLE_NOPS"], ["1", "NOP7", "P2SH,DISCOURAGE_UPGRADABLE_NOPS", "DISCOURAGE_UPGRADABLE_NOPS"], ["1", "NOP8", "P2SH,DISCOURAGE_UPGRADABLE_NOPS", "DISCOURAGE_UPGRADABLE_NOPS"], diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py index 3d9572788eb..8a91693eb25 100644 --- a/test/functional/test_framework/script.py +++ b/test/functional/test_framework/script.py @@ -227,7 +227,7 @@ def __new__(cls, n): OP_CHECKLOCKTIMEVERIFY = CScriptOp(0xb1) OP_CHECKSEQUENCEVERIFY = CScriptOp(0xb2) OP_NOP4 = CScriptOp(0xb3) -OP_NOP5 = CScriptOp(0xb4) +OP_CHECKBLOCKATHEIGHT = CScriptOp(0xb4) OP_NOP6 = CScriptOp(0xb5) OP_NOP7 = CScriptOp(0xb6) OP_NOP8 = CScriptOp(0xb7) @@ -354,7 +354,7 @@ def __new__(cls, n): OP_CHECKLOCKTIMEVERIFY, OP_CHECKSEQUENCEVERIFY, OP_NOP4, - OP_NOP5, + OP_CHECKBLOCKATHEIGHT, OP_NOP6, OP_NOP7, OP_NOP8, @@ -473,7 +473,7 @@ def __new__(cls, n): OP_CHECKLOCKTIMEVERIFY : 'OP_CHECKLOCKTIMEVERIFY', OP_CHECKSEQUENCEVERIFY : 'OP_CHECKSEQUENCEVERIFY', OP_NOP4 : 'OP_NOP4', - OP_NOP5 : 'OP_NOP5', + OP_CHECKBLOCKATHEIGHT : 'OP_CHECKBLOCKATHEIGHT', OP_NOP6 : 'OP_NOP6', OP_NOP7 : 'OP_NOP7', OP_NOP8 : 'OP_NOP8', @@ -592,7 +592,7 @@ def __new__(cls, n): 'OP_CHECKLOCKTIMEVERIFY' : OP_CHECKLOCKTIMEVERIFY, 'OP_CHECKSEQUENCEVERIFY' : OP_CHECKSEQUENCEVERIFY, 'OP_NOP4' : OP_NOP4, - 'OP_NOP5' : OP_NOP5, + 'OP_CHECKBLOCKATHEIGHT' : OP_CHECKBLOCKATHEIGHT, 'OP_NOP6' : OP_NOP6, 'OP_NOP7' : OP_NOP7, 'OP_NOP8' : OP_NOP8, From 316d727dda71984403475b055ff9e60f0c30795b Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Fri, 12 May 2017 03:57:53 +0000 Subject: [PATCH 5/5] CHistoricalChain to fix context in CVerifyDB --- src/chain.cpp | 26 ++++++++++++++++++++++++++ src/chain.h | 43 +++++++++++++++++++++++++++++++++++-------- src/validation.cpp | 5 +++-- 3 files changed, 64 insertions(+), 10 deletions(-) diff --git a/src/chain.cpp b/src/chain.cpp index a5b369c4fc4..947149701d6 100644 --- a/src/chain.cpp +++ b/src/chain.cpp @@ -5,6 +5,10 @@ #include "chain.h" +#include "tinyformat.h" + +#include + /** * CChain implementation */ @@ -66,6 +70,28 @@ CBlockIndex* CChain::FindEarliestAtLeast(int64_t nTime) const return (lower == vChain.end() ? NULL : *lower); } +void CHistoricalChain::SetHeight(const int nHeight) +{ + if (nHeight > chain.Height()) { + throw std::runtime_error(strprintf("%s: Cannot set a height beyond parent!", __func__)); + } + my_height = nHeight; +} + +void CHistoricalChain::SetTip(CBlockIndex *pindex) +{ + throw std::runtime_error("Cannot SetTip of a CHistoricalChain!"); +} + +CBlockIndex* CHistoricalChain::FindEarliestAtLeast(int64_t nTime) const +{ + CBlockIndex * const pblockindex = chain.FindEarliestAtLeast(nTime); + if (pblockindex->nHeight > Height()) { + return NULL; + } + return pblockindex; +} + /** Turn the lowest '1' bit in the binary representation of a number into a '0'. */ int static inline InvertLowestOne(int n) { return n & (n - 1); } diff --git a/src/chain.h b/src/chain.h index de120d2d75d..6ae53df7e0c 100644 --- a/src/chain.h +++ b/src/chain.h @@ -436,25 +436,26 @@ class CChain { public: /** Returns the index entry for the genesis block of this chain, or NULL if none. */ CBlockIndex *Genesis() const { - return vChain.size() > 0 ? vChain[0] : NULL; + return (*this)[0]; } /** Returns the index entry for the tip of this chain, or NULL if none. */ CBlockIndex *Tip() const { - return vChain.size() > 0 ? vChain[vChain.size() - 1] : NULL; + const int nHeight = Height(); + return nHeight >= 0 ? (*this)[nHeight] : NULL; } /** Returns the index entry at a particular height in this chain, or NULL if no such height exists. */ - CBlockIndex *operator[](int nHeight) const { - if (nHeight < 0 || nHeight >= (int)vChain.size()) + virtual CBlockIndex *operator[](int nHeight) const { + if (nHeight < 0 || nHeight > Height()) { return NULL; + } return vChain[nHeight]; } /** Compare two chains efficiently. */ friend bool operator==(const CChain &a, const CChain &b) { - return a.vChain.size() == b.vChain.size() && - a.vChain[a.vChain.size() - 1] == b.vChain[b.vChain.size() - 1]; + return a.Tip() == b.Tip(); } /** Efficiently check whether a block is present in this chain. */ @@ -471,12 +472,12 @@ class CChain { } /** Return the maximal height in the chain. Is equal to chain.Tip() ? chain.Tip()->nHeight : -1. */ - int Height() const { + virtual int Height() const { return vChain.size() - 1; } /** Set/initialize a chain with a given tip. */ - void SetTip(CBlockIndex *pindex); + virtual void SetTip(CBlockIndex *pindex); /** Return a CBlockLocator that refers to a block in this chain (by default the tip). */ CBlockLocator GetLocator(const CBlockIndex *pindex = NULL) const; @@ -485,6 +486,32 @@ class CChain { const CBlockIndex *FindFork(const CBlockIndex *pindex) const; /** Find the earliest block with timestamp equal or greater than the given. */ + virtual CBlockIndex* FindEarliestAtLeast(int64_t nTime) const; +}; + +class CHistoricalChain : public CChain { +private: + const CChain& chain; + int my_height; + +public: + CHistoricalChain() = delete; + CHistoricalChain(const CChain& chainIn, const int heightIn) : chain(chainIn), my_height(heightIn) { } + + void SetHeight(int nHeight); + + CBlockIndex *operator[](int nHeight) const { + if (nHeight > Height()) { + return NULL; + } + return chain[nHeight]; + } + + int Height() const { + return my_height; + } + + void SetTip(CBlockIndex *pindex); CBlockIndex* FindEarliestAtLeast(int64_t nTime) const; }; diff --git a/src/validation.cpp b/src/validation.cpp index c606e82e283..5993309660e 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -3685,6 +3685,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, // check level 4: try reconnecting blocks if (nCheckLevel >= 4) { CBlockIndex *pindex = pindexState; + CHistoricalChain chainHistorical(chainActive, pindex->nHeight - 1); while (pindex != chainActive.Tip()) { boost::this_thread::interruption_point(); uiInterface.ShowProgress(_("Verifying blocks..."), std::max(1, std::min(99, 100 - (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * 50)))); @@ -3692,8 +3693,8 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, CBlock block; if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus())) return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); - // FIXME: Need to shadow chainActive with the older height! - if (!ConnectBlock(block, state, pindex, coins, chainActive, chainparams)) + chainHistorical.SetHeight(pindex->nHeight - 1); + if (!ConnectBlock(block, state, pindex, coins, chainHistorical, chainparams)) return error("VerifyDB(): *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); } }