diff --git a/src/addresstype.cpp b/src/addresstype.cpp index 67e643943d4..66d6c47ff9a 100644 --- a/src/addresstype.cpp +++ b/src/addresstype.cpp @@ -98,6 +98,7 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) case TxoutType::MULTISIG: case TxoutType::NULL_DATA: case TxoutType::NONSTANDARD: + case TxoutType::TX_BARE_DEFAULT_CHECKTEMPLATEVERIFY: addressRet = CNoDestination(scriptPubKey); return false; } // no default case, so the compiler can warn about missing cases diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index ed336928235..1f4a325d6cb 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -214,6 +214,11 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) if (subscript.GetSigOpCount(true) > MAX_P2SH_SIGOPS) { return false; } + } else if (whichType == TxoutType::TX_BARE_DEFAULT_CHECKTEMPLATEVERIFY) { + // after activation, only allow bare with no scriptsig. + // pre-activation disallowing enforced via discouraged logic in the + // interpreter. + if (tx.vin[i].scriptSig.size() != 0) return false; } } diff --git a/src/policy/policy.h b/src/policy/policy.h index 2151ec13dd0..800e74c4e7e 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -124,7 +124,11 @@ static constexpr unsigned int STANDARD_SCRIPT_VERIFY_FLAGS{MANDATORY_SCRIPT_VERI SCRIPT_VERIFY_CONST_SCRIPTCODE | SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION | SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS | - SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE}; + SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE | + SCRIPT_VERIFY_DISCOURAGE_CHECKTEMPLATEVERIFY | + SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_CHECKTEMPLATEVERIFY | + SCRIPT_VERIFY_CHECKTEMPLATEVERIFY}; + /** For convenience, standard but not mandatory verify flags. */ static constexpr unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS{STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS}; diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 421656152cb..309ce717fa7 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -544,6 +544,8 @@ static RPCHelpMan decodescript() case TxoutType::WITNESS_UNKNOWN: case TxoutType::WITNESS_V1_TAPROOT: case TxoutType::ANCHOR: + // don't wrap CTV because P2SH CTV is a hash cycle + case TxoutType::TX_BARE_DEFAULT_CHECKTEMPLATEVERIFY: // Should not be wrapped return false; } // no default case, so the compiler can warn about missing cases @@ -587,6 +589,8 @@ static RPCHelpMan decodescript() case TxoutType::WITNESS_V0_SCRIPTHASH: case TxoutType::WITNESS_V1_TAPROOT: case TxoutType::ANCHOR: + // don't wrap CTV because P2SH CTV is a hash cycle + case TxoutType::TX_BARE_DEFAULT_CHECKTEMPLATEVERIFY: // Should not be wrapped return false; } // no default case, so the compiler can warn about missing cases diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index a35306b6935..b032f4451f6 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -591,7 +591,42 @@ bool EvalScript(std::vector >& stack, const CScript& break; } - case OP_NOP1: case OP_NOP4: case OP_NOP5: + case OP_CHECKTEMPLATEVERIFY: + { + if (flags & SCRIPT_VERIFY_DISCOURAGE_CHECKTEMPLATEVERIFY) { + return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS); + } + + // if flags not enabled; treat as a NOP4 + if (!(flags & SCRIPT_VERIFY_CHECKTEMPLATEVERIFY)) { + break; + } + + if (stack.size() < 1) { + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + } + + // If the argument was not 32 bytes, treat as OP_NOP4: + switch (stack.back().size()) { + case 32: + { + const Span hash{stack.back()}; + if (!checker.CheckDefaultCheckTemplateVerifyHash(hash)) { + return set_error(serror, SCRIPT_ERR_TEMPLATE_MISMATCH); + } + break; + } + default: + // future upgrade can add semantics for this opcode with different length args + // so discourage use when applicable + if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_CHECKTEMPLATEVERIFY) { + return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS); + } + } + } + break; + + case OP_NOP1: 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) @@ -1377,6 +1412,18 @@ uint256 GetSpentAmountsSHA256(const std::vector& outputs_spent) HashWriter ss{}; for (const auto& txout : outputs_spent) { ss << txout.nValue; + + } + return ss.GetSHA256(); +} + +/** Compute the (single) SHA256 of the concatenation of all scriptSigs in a tx. */ +template +uint256 GetScriptSigsSHA256(const T& txTo) +{ + HashWriter ss{}; + for (const auto& in : txTo.vin) { + ss << in.scriptSig; } return ss.GetSHA256(); } @@ -1391,9 +1438,64 @@ uint256 GetSpentScriptsSHA256(const std::vector& outputs_spent) return ss.GetSHA256(); } +/* Not Exported, just convenience */ +template +uint256 GetDefaultCheckTemplateVerifyHashWithScript( + const TxType& tx, const uint256& outputs_hash, const uint256& sequences_hash, + const uint256& scriptSig_hash, const uint32_t input_index) { + auto h = HashWriter{} + << tx.version + << tx.nLockTime + << scriptSig_hash + << uint32_t(tx.vin.size()) + << sequences_hash + << uint32_t(tx.vout.size()) + << outputs_hash + << input_index; + return h.GetSHA256(); +} + +template +uint256 GetDefaultCheckTemplateVerifyHashEmptyScript( + const TxType& tx, const uint256& outputs_hash, const uint256& sequences_hash, const uint32_t input_index) { + auto h = HashWriter{} + << tx.version + << tx.nLockTime + << uint32_t(tx.vin.size()) + << sequences_hash + << uint32_t(tx.vout.size()) + << outputs_hash + << input_index; + return h.GetSHA256(); +} } // namespace +template +uint256 GetDefaultCheckTemplateVerifyHash(const TxType& tx, uint32_t input_index) { + return GetDefaultCheckTemplateVerifyHash(tx, GetOutputsSHA256(tx), GetSequencesSHA256(tx), input_index); +} + +template +static bool NoScriptSigs(const TxType& tx) +{ + return std::all_of(tx.vin.begin(), tx.vin.end(), [](const CTxIn& c) { return c.scriptSig.empty(); }); +} + +template +uint256 GetDefaultCheckTemplateVerifyHash( + const TxType& tx, const uint256& outputs_hash, const uint256& sequences_hash, const uint32_t input_index) { + return NoScriptSigs(tx) ? GetDefaultCheckTemplateVerifyHashEmptyScript(tx, outputs_hash, sequences_hash, input_index) : + GetDefaultCheckTemplateVerifyHashWithScript(tx, outputs_hash, sequences_hash, GetScriptSigsSHA256(tx), input_index); +} + +template +uint256 GetDefaultCheckTemplateVerifyHash(const CTransaction& tx, const uint256& outputs_hash, const uint256& sequences_hash, + const uint32_t input_index); +template +uint256 GetDefaultCheckTemplateVerifyHash(const CMutableTransaction& tx, const uint256& outputs_hash, const uint256& sequences_hash, + const uint32_t input_index); + template void PrecomputedTransactionData::Init(const T& txTo, std::vector&& spent_outputs, bool force) { @@ -1427,12 +1529,19 @@ void PrecomputedTransactionData::Init(const T& txTo, std::vector&& spent if (uses_bip341_taproot && uses_bip143_segwit) break; // No need to scan further if we already need all. } - if (uses_bip143_segwit || uses_bip341_taproot) { - // Computations shared between both sighash schemes. - m_prevouts_single_hash = GetPrevoutsSHA256(txTo); - m_sequences_single_hash = GetSequencesSHA256(txTo); - m_outputs_single_hash = GetOutputsSHA256(txTo); - } + // Each of these computations is always required for CHECKTEMPLATEVERIFY, and sometimes + // required for any segwit/taproot. + m_prevouts_single_hash = GetPrevoutsSHA256(txTo); + m_sequences_single_hash = GetSequencesSHA256(txTo); + m_outputs_single_hash = GetOutputsSHA256(txTo); + + // Only required for CHECKTEMPLATEVERIFY. + // + // The empty hash is used to signal whether or not we should skip scriptSigs + // when re-computing for different indexes. + m_scriptSigs_single_hash = NoScriptSigs(txTo) ? uint256{} : GetScriptSigsSHA256(txTo); + m_bip119_ctv_ready = true; + if (uses_bip143_segwit) { hashPrevouts = SHA256Uint256(m_prevouts_single_hash); hashSequence = SHA256Uint256(m_sequences_single_hash); @@ -1781,6 +1890,22 @@ bool GenericTransactionSignatureChecker::CheckSequence(const CScriptNum& nSeq return true; } +template +bool GenericTransactionSignatureChecker::CheckDefaultCheckTemplateVerifyHash(const Span& hash) const +{ + // Should already be checked before calling... + assert(hash.size() == 32); + if (txdata && txdata->m_bip119_ctv_ready) { + assert(txTo != nullptr); + uint256 hash_tmpl = txdata->m_scriptSigs_single_hash.IsNull() ? + GetDefaultCheckTemplateVerifyHashEmptyScript(*txTo, txdata->m_outputs_single_hash, txdata->m_sequences_single_hash, nIn) : + GetDefaultCheckTemplateVerifyHashWithScript(*txTo, txdata->m_outputs_single_hash, txdata->m_sequences_single_hash, + txdata->m_scriptSigs_single_hash, nIn); + return std::equal(hash_tmpl.begin(), hash_tmpl.end(), hash.data()); + } else { + return HandleMissingData(m_mdb); + } +} // explicit instantiation template class GenericTransactionSignatureChecker; template class GenericTransactionSignatureChecker; diff --git a/src/script/interpreter.h b/src/script/interpreter.h index e2fb1998f0b..51e672275e0 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -143,6 +143,15 @@ enum : uint32_t { // Making unknown public key versions (in BIP 342 scripts) non-standard SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE = (1U << 20), + // CHECKTEMPLATEVERIFY validation (BIP-119) + SCRIPT_VERIFY_CHECKTEMPLATEVERIFY = (1U << 21), + + // discourage upgradable OP_CHECKTEMPLATEVERIFY hashes + SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_CHECKTEMPLATEVERIFY = (1U << 22), + + // discourage OP_CHECKTEMPLATEVERIFY + SCRIPT_VERIFY_DISCOURAGE_CHECKTEMPLATEVERIFY = (1U << 23), + // Constants to point to the highest flag in use. Add new flags above this line. // SCRIPT_VERIFY_END_MARKER @@ -152,6 +161,9 @@ bool CheckSignatureEncoding(const std::vector &vchSig, unsigned i struct PrecomputedTransactionData { + // Order of fields is packed below (uint256 is 32 bytes, vector is 24 bytes + // (3 ptrs), ready flags (1 byte each). + // BIP341 precomputed data. // These are single-SHA256, see https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_note-16. uint256 m_prevouts_single_hash; @@ -159,15 +171,25 @@ struct PrecomputedTransactionData uint256 m_outputs_single_hash; uint256 m_spent_amounts_single_hash; uint256 m_spent_scripts_single_hash; - //! Whether the 5 fields above are initialized. - bool m_bip341_taproot_ready = false; + + // BIP119 precomputed data (single SHA256). + uint256 m_scriptSigs_single_hash; // BIP143 precomputed data (double-SHA256). uint256 hashPrevouts, hashSequence, hashOutputs; - //! Whether the 3 fields above are initialized. + + // BIP341 cached outputs. + std::vector m_spent_outputs; + + //! Whether the bip341 fields above are initialized. + bool m_bip341_taproot_ready = false; + + //! Whether the bip119 fields above are initialized. + bool m_bip119_ctv_ready = false; + + //! Whether the bip143 fields above are initialized. bool m_bip143_segwit_ready = false; - std::vector m_spent_outputs; //! Whether m_spent_outputs is initialized. bool m_spent_outputs_ready = false; @@ -187,6 +209,11 @@ struct PrecomputedTransactionData explicit PrecomputedTransactionData(const T& tx); }; +/* Standard Template Hash Declarations */ +template +uint256 GetDefaultCheckTemplateVerifyHash(const TxType& tx, const uint256& outputs_hash, const uint256& sequences_hash, + const uint32_t input_index); + enum class SigVersion { BASE = 0, //!< Bare scripts and BIP16 P2SH-wrapped redeemscripts @@ -265,6 +292,11 @@ public: return false; } + virtual bool CheckDefaultCheckTemplateVerifyHash(const Span& hash) const + { + return false; + } + virtual ~BaseSignatureChecker() = default; }; @@ -301,6 +333,7 @@ public: bool CheckSchnorrSignature(Span sig, Span pubkey, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override; bool CheckLockTime(const CScriptNum& nLockTime) const override; bool CheckSequence(const CScriptNum& nSequence) const override; + bool CheckDefaultCheckTemplateVerifyHash(const Span& hash) const override; }; using TransactionSignatureChecker = GenericTransactionSignatureChecker; diff --git a/src/script/script.cpp b/src/script/script.cpp index d650db9a0d6..fc77d70a9e9 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -138,7 +138,7 @@ std::string GetOpName(opcodetype opcode) case OP_NOP1 : return "OP_NOP1"; case OP_CHECKLOCKTIMEVERIFY : return "OP_CHECKLOCKTIMEVERIFY"; case OP_CHECKSEQUENCEVERIFY : return "OP_CHECKSEQUENCEVERIFY"; - case OP_NOP4 : return "OP_NOP4"; + case OP_CHECKTEMPLATEVERIFY : return "OP_CHECKTEMPLATEVERIFY"; case OP_NOP5 : return "OP_NOP5"; case OP_NOP6 : return "OP_NOP6"; case OP_NOP7 : return "OP_NOP7"; @@ -221,6 +221,14 @@ bool CScript::IsPayToAnchor(int version, const std::vector& progr program[1] == 0x73; } +bool CScript::IsPayToBareDefaultCheckTemplateVerifyHash() const +{ + // Extra-fast test for pay-to-bare-default-check-template-verify-hash CScripts: + return (this->size() == 34 && + (*this)[0] == 0x20 && + (*this)[33] == OP_CHECKTEMPLATEVERIFY); +} + bool CScript::IsPayToScriptHash() const { // Extra-fast test for pay-to-script-hash CScripts: diff --git a/src/script/script.h b/src/script/script.h index f4579849803..f42c31caa04 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -198,7 +198,8 @@ enum opcodetype OP_NOP2 = OP_CHECKLOCKTIMEVERIFY, OP_CHECKSEQUENCEVERIFY = 0xb2, OP_NOP3 = OP_CHECKSEQUENCEVERIFY, - OP_NOP4 = 0xb3, + OP_CHECKTEMPLATEVERIFY = 0xb3, + OP_NOP4 = OP_CHECKTEMPLATEVERIFY, OP_NOP5 = 0xb4, OP_NOP6 = 0xb5, OP_NOP7 = 0xb6, @@ -552,6 +553,8 @@ public: */ static bool IsPayToAnchor(int version, const std::vector& program); + bool IsPayToBareDefaultCheckTemplateVerifyHash() const; + bool IsPayToScriptHash() const; bool IsPayToWitnessScriptHash() const; bool IsWitnessProgram(int& version, std::vector& program) const; diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp index fadc04262c3..f33994d0b3b 100644 --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -25,6 +25,8 @@ std::string ScriptErrorString(const ScriptError serror) return "Script failed an OP_CHECKSIGVERIFY operation"; case SCRIPT_ERR_NUMEQUALVERIFY: return "Script failed an OP_NUMEQUALVERIFY operation"; + case SCRIPT_ERR_TEMPLATE_MISMATCH: + return "Script failed an OP_CHECKTEMPLATEVERIFY operation"; case SCRIPT_ERR_SCRIPT_SIZE: return "Script is too big"; case SCRIPT_ERR_PUSH_SIZE: diff --git a/src/script/script_error.h b/src/script/script_error.h index 44e68fe0fae..74d7041533d 100644 --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -29,6 +29,7 @@ typedef enum ScriptError_t SCRIPT_ERR_CHECKMULTISIGVERIFY, SCRIPT_ERR_CHECKSIGVERIFY, SCRIPT_ERR_NUMEQUALVERIFY, + SCRIPT_ERR_TEMPLATE_MISMATCH, /* Logical/Format/Canonical errors */ SCRIPT_ERR_BAD_OPCODE, diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 42db2513597..74d9663bc41 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -412,6 +412,7 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator case TxoutType::NONSTANDARD: case TxoutType::NULL_DATA: case TxoutType::WITNESS_UNKNOWN: + case TxoutType::TX_BARE_DEFAULT_CHECKTEMPLATEVERIFY: return false; case TxoutType::PUBKEY: if (!CreateSig(creator, sigdata, provider, sig, CPubKey(vSolutions[0]), scriptPubKey, sigversion)) return false; diff --git a/src/script/solver.cpp b/src/script/solver.cpp index bd3c5cdf724..04622069ddf 100644 --- a/src/script/solver.cpp +++ b/src/script/solver.cpp @@ -29,6 +29,7 @@ std::string GetTxnOutputType(TxoutType t) case TxoutType::WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash"; case TxoutType::WITNESS_V1_TAPROOT: return "witness_v1_taproot"; case TxoutType::WITNESS_UNKNOWN: return "witness_unknown"; + case TxoutType::TX_BARE_DEFAULT_CHECKTEMPLATEVERIFY: return "bare_default_ctv_hash"; } // no default case, so the compiler can warn about missing cases assert(false); } @@ -151,6 +152,10 @@ TxoutType Solver(const CScript& scriptPubKey, std::vector witnessprogram; if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) { diff --git a/src/script/solver.h b/src/script/solver.h index 5b945477c90..0b64551bf72 100644 --- a/src/script/solver.h +++ b/src/script/solver.h @@ -31,6 +31,7 @@ enum class TxoutType { WITNESS_V0_SCRIPTHASH, WITNESS_V0_KEYHASH, WITNESS_V1_TAPROOT, + TX_BARE_DEFAULT_CHECKTEMPLATEVERIFY, WITNESS_UNKNOWN, //!< Only for Witness versions not already defined above }; diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 336377331d9..8418686d27b 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -34,6 +34,7 @@ add_executable(test_bitcoin compilerbug_tests.cpp compress_tests.cpp crypto_tests.cpp + ctvhash_tests.cpp cuckoocache_tests.cpp dbwrapper_tests.cpp denialofservice_tests.cpp @@ -130,6 +131,7 @@ target_json_data_sources(test_bitcoin data/base58_encode_decode.json data/bip341_wallet_vectors.json data/blockfilters.json + data/ctvhash.json data/key_io_invalid.json data/key_io_valid.json data/script_tests.json diff --git a/src/test/ctvhash_tests.cpp b/src/test/ctvhash_tests.cpp new file mode 100644 index 00000000000..f3f3d49a1f5 --- /dev/null +++ b/src/test/ctvhash_tests.cpp @@ -0,0 +1,198 @@ +// Copyright (c) 2013-2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include +#include +#include