diff --git a/src/addresstype.cpp b/src/addresstype.cpp index 67e643943d4da..66d6c47ff9a6d 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/consensus/params.h b/src/consensus/params.h index dd29b9408e232..b833bc1376fcb 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -33,6 +33,7 @@ enum DeploymentPos : uint16_t { DEPLOYMENT_TESTDUMMY, DEPLOYMENT_TAPROOT, // Deployment of Schnorr/Taproot (BIPs 340-342) // NOTE: Also add new deployments to VersionBitsDeploymentInfo in deploymentinfo.cpp + DEPLOYMENT_CTV, // Deployment of CHECKTEMPLATEVERIFY (BIP 119) (regtest only) MAX_VERSION_BITS_DEPLOYMENTS }; constexpr bool ValidDeployment(DeploymentPos dep) { return dep < MAX_VERSION_BITS_DEPLOYMENTS; } diff --git a/src/deploymentinfo.cpp b/src/deploymentinfo.cpp index 185a7dcb54ce7..95c583696ec7a 100644 --- a/src/deploymentinfo.cpp +++ b/src/deploymentinfo.cpp @@ -17,6 +17,10 @@ const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_B /*.name =*/ "taproot", /*.gbt_force =*/ true, }, + { + /*.name =*/ "ctv", + /*.gbt_force =*/ true, + }, }; std::string DeploymentName(Consensus::BuriedDeployment dep) diff --git a/src/kernel/chainparams.cpp b/src/kernel/chainparams.cpp index 47fe82fa91faa..22431d2151014 100644 --- a/src/kernel/chainparams.cpp +++ b/src/kernel/chainparams.cpp @@ -591,6 +591,11 @@ class CRegTestParams : public CChainParams consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay + consensus.vDeployments[Consensus::DEPLOYMENT_CTV].bit = 1; + consensus.vDeployments[Consensus::DEPLOYMENT_CTV].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_CTV].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + consensus.vDeployments[Consensus::DEPLOYMENT_CTV].min_activation_height = 0; // No activation delay + consensus.nMinimumChainWork = uint256{}; consensus.defaultAssumeValid = uint256{}; diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index f1a4bc5bc13c3..367c773ff0dbd 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -342,6 +342,11 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs, if (subscript.GetSigOpCount(true) > MAX_P2SH_SIGOPS) { MaybeReject("scriptcheck-sigops"); } + } 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 a9c557279e06a..fe3e96f149f4b 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -170,7 +170,9 @@ 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}; /** 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/blockchain.cpp b/src/rpc/blockchain.cpp index 242ad6ce7e472..ca41929df3814 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1849,6 +1849,10 @@ UniValue DeploymentInfo(const CBlockIndex* blockindex, const ChainstateManager& SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_SEGWIT); SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_TESTDUMMY); SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_TAPROOT); + + if (chainman.GetParams().GetChainType() == ChainType::REGTEST) { + SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_CTV); + } return softforks; } } // anon namespace diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index e464d9d021aa8..37149847b3908 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -573,6 +573,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 @@ -616,6 +618,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 360f6fd9a0775..8311a0896b41a 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 std::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,66 @@ uint256 GetSpentScriptsSHA256(const std::vector& outputs_spent) return ss.GetSHA256(); } +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 +1531,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 evaluation. + 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); @@ -1788,6 +1899,22 @@ bool GenericTransactionSignatureChecker::CheckSequence(const CScriptNum& nSeq return true; } +template +bool GenericTransactionSignatureChecker::CheckDefaultCheckTemplateVerifyHash(const std::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 5b20a78d98eb5..4d98b20cba614 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. - bool m_bip143_segwit_ready = false; + // 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; + //! Whether m_spent_outputs is initialized. bool m_spent_outputs_ready = false; @@ -187,6 +209,17 @@ struct PrecomputedTransactionData explicit PrecomputedTransactionData(const T& tx); }; +/** + * Compute the default template hash for OP_CHECKTEMPLATEVERIFY using some precomputed + * hash inputs. + * + * (This exported interface is only used in tests.) + */ +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 +298,11 @@ class BaseSignatureChecker return false; } + virtual bool CheckDefaultCheckTemplateVerifyHash(const std::span& hash) const + { + return false; + } + virtual ~BaseSignatureChecker() = default; }; @@ -301,8 +339,7 @@ class GenericTransactionSignatureChecker : public BaseSignatureChecker 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 m_require_sighash_all{false}; + bool CheckDefaultCheckTemplateVerifyHash(const std::span& hash) const override; }; using TransactionSignatureChecker = GenericTransactionSignatureChecker; diff --git a/src/script/script.cpp b/src/script/script.cpp index b5bbb0487ed4d..f772a23f73059 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -139,7 +139,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"; @@ -222,6 +222,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 f38d15811953c..de1bd2ac3ab7b 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, @@ -553,6 +554,8 @@ class CScript : public CScriptBase */ 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 fadc04262c314..f33994d0b3b43 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 44e68fe0fae30..74d7041533d24 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 60edff2b1bd11..d992242dd7907 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -414,6 +414,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 43af6c4bee88f..93bb8ff727215 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 c3e952097ca78..94f3222da0cab 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 9f5f45e9fd886..ca63b123cb239 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -36,6 +36,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 @@ -131,6 +132,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 0000000000000..04636dd6780c2 --- /dev/null +++ b/src/test/ctvhash_tests.cpp @@ -0,0 +1,203 @@ +// 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