diff --git a/src/consensus/params.h b/src/consensus/params.h index 6344349b8661e..ca02e0c7f0f85 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -33,6 +33,7 @@ constexpr bool ValidDeployment(BuriedDeployment dep) { return dep <= DEPLOYMENT_ enum DeploymentPos : uint16_t { DEPLOYMENT_TESTDUMMY, DEPLOYMENT_TAPROOT, // Deployment of Schnorr/Taproot (BIPs 340-342) + DEPLOYMENT_TEMPLATEHASH, // NOTE: Also add new deployments to VersionBitsDeploymentInfo in deploymentinfo.cpp MAX_VERSION_BITS_DEPLOYMENTS }; diff --git a/src/deploymentinfo.cpp b/src/deploymentinfo.cpp index 5c4795505be06..a15068ec9385a 100644 --- a/src/deploymentinfo.cpp +++ b/src/deploymentinfo.cpp @@ -17,6 +17,10 @@ const std::array Versi .name = "taproot", .gbt_optional_rule = true, }, + VBDeploymentInfo{ + .name = "templatehash", + .gbt_optional_rule = true, + } }; std::string DeploymentName(Consensus::BuriedDeployment dep) diff --git a/src/kernel/chainparams.cpp b/src/kernel/chainparams.cpp index f81758879b284..a00b2e2df4f89 100644 --- a/src/kernel/chainparams.cpp +++ b/src/kernel/chainparams.cpp @@ -118,6 +118,12 @@ class CMainParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].threshold = 1815; // 90% consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].period = 2016; + // Deployment of OP_TEMPLATEHASH + consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].bit = 3; + consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].min_activation_height = 0; + consensus.nMinimumChainWork = uint256{"0000000000000000000000000000000000000000b1f3b93b65b16d035a82be84"}; consensus.defaultAssumeValid = uint256{"00000000000000000001b658dd1120e82e66d2790811f89ede9742ada3ed6d77"}; // 886157 @@ -232,6 +238,12 @@ class CTestNetParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].threshold = 1512; // 75% consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].period = 2016; + // Deployment of OP_TEMPLATEHASH + consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].bit = 3; + consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].min_activation_height = 0; + consensus.nMinimumChainWork = uint256{"0000000000000000000000000000000000000000000015f5e0c9f13455b0eb17"}; consensus.defaultAssumeValid = uint256{"00000000000003fc7967410ba2d0a8a8d50daedc318d43e8baf1a9782c236a57"}; // 3974606 @@ -328,6 +340,12 @@ class CTestNet4Params : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].threshold = 1512; // 75% consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].period = 2016; + // Deployment of OP_TEMPLATEHASH + consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].bit = 3; + consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].min_activation_height = 0; + consensus.nMinimumChainWork = uint256{"0000000000000000000000000000000000000000000001d6dce8651b6094e4c1"}; consensus.defaultAssumeValid = uint256{"0000000000003ed4f08dbdf6f7d6b271a6bcffce25675cb40aa9fa43179a89f3"}; // 72600 @@ -462,6 +480,12 @@ class SigNetParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].threshold = 1815; // 90% consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].period = 2016; + // Deployment of OP_TEMPLATEHASH + consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].bit = 3; + consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].min_activation_height = 0; + // message start is defined as the first 4 bytes of the sha256d of the block script HashWriter h{}; h << consensus.signet_challenge; @@ -539,6 +563,14 @@ class CRegTestParams : public CChainParams consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].threshold = 108; // 75% consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].period = 144; + // Deployment of OP_TEMPLATEHASH + consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].bit = 3; + consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].min_activation_height = 0; // No activation delay + consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].threshold = 108; // 75% + consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].period = 144; + consensus.nMinimumChainWork = uint256{}; consensus.defaultAssumeValid = uint256{}; diff --git a/src/policy/policy.h b/src/policy/policy.h index e62efa02e3edf..2ada66002c368 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -123,7 +123,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_TEMPLATEHASH | + SCRIPT_VERIFY_TEMPLATEHASH}; /** 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 a237a24c84e53..0850378a7a60a 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1423,6 +1423,7 @@ 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); + SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_TEMPLATEHASH); return softforks; } } // anon namespace diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 61ea7f4503c2e..b361566891fcc 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1213,6 +1213,20 @@ bool EvalScript(std::vector >& stack, const CScript& } break; + case OP_TEMPLATEHASH: + { + // OP_TEMPLATEHASH is only available in Tapscript. Note this is the exact same error + // returned in the default case, which would be the one hit by OP_SUCCESS187 before + // the introduction of OP_TEMPLATEHASH. + if (sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0) { + return set_error(serror, SCRIPT_ERR_BAD_OPCODE); + } + + const uint256 template_hash{checker.GetTemplateHash(execdata)}; + stack.push_back({template_hash.begin(), template_hash.end()}); + } + break; + default: return set_error(serror, SCRIPT_ERR_BAD_OPCODE); } @@ -1461,6 +1475,7 @@ template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTr const HashWriter HASHER_TAPSIGHASH{TaggedHash("TapSighash")}; const HashWriter HASHER_TAPLEAF{TaggedHash("TapLeaf")}; const HashWriter HASHER_TAPBRANCH{TaggedHash("TapBranch")}; +const HashWriter HASHER_TEMPLATEHASH{TaggedHash("TemplateHash")}; static bool HandleMissingData(MissingDataBehavior mdb) { @@ -1781,6 +1796,27 @@ bool GenericTransactionSignatureChecker::CheckSequence(const CScriptNum& nSeq return true; } +template +uint256 GenericTransactionSignatureChecker::GetTemplateHash(ScriptExecutionData& execdata) const +{ + assert(txdata && txdata->m_bip341_taproot_ready); + assert(execdata.m_annex_init); + + HashWriter ss{HASHER_TEMPLATEHASH}; + ss << txTo->version; + ss << txTo->nLockTime; + ss << txdata->m_sequences_single_hash; + ss << txdata->m_outputs_single_hash; + const uint8_t annex_present = (execdata.m_annex_present ? 1 : 0); + ss << annex_present; + ss << nIn; + if (execdata.m_annex_present) { + ss << execdata.m_annex_hash; + } + + return ss.GetSHA256(); +} + // explicit instantiation template class GenericTransactionSignatureChecker; template class GenericTransactionSignatureChecker; @@ -1800,6 +1836,16 @@ static bool ExecuteWitnessScript(const std::span& stack_span, con } // New opcodes will be listed here. May use a different sigversion to modify existing opcodes. if (IsOpSuccess(opcode)) { + // Do not return early success on OP_TEMPLATEHASH (OP_SUCCESS187) once it is active. It is + // non-standard until then. + if (opcode == OP_TEMPLATEHASH) { + if (flags & SCRIPT_VERIFY_DISCOURAGE_TEMPLATEHASH) { + return set_error(serror, SCRIPT_ERR_DISCOURAGE_TEMPLATEHASH); + } + if (flags & SCRIPT_VERIFY_TEMPLATEHASH) { + continue; + } + } if (flags & SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS) { return set_error(serror, SCRIPT_ERR_DISCOURAGE_OP_SUCCESS); } diff --git a/src/script/interpreter.h b/src/script/interpreter.h index e8c5b09045fd1..49302b458e87f 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -143,6 +143,12 @@ enum : uint32_t { // Making unknown public key versions (in BIP 342 scripts) non-standard SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE = (1U << 20), + // OP_TEMPLATEHASH validation (BIP xx) + SCRIPT_VERIFY_TEMPLATEHASH = (1U << 21), + + // Make OP_TEMPLATEHASH spend non-standard before activation. + SCRIPT_VERIFY_DISCOURAGE_TEMPLATEHASH = (1U << 22), + // Constants to point to the highest flag in use. Add new flags above this line. // SCRIPT_VERIFY_END_MARKER @@ -265,6 +271,11 @@ class BaseSignatureChecker return false; } + virtual uint256 GetTemplateHash(ScriptExecutionData& execdata) const + { + return {}; + } + virtual ~BaseSignatureChecker() = default; }; @@ -301,6 +312,7 @@ class GenericTransactionSignatureChecker : public BaseSignatureChecker bool CheckSchnorrSignature(std::span sig, std::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; + uint256 GetTemplateHash(ScriptExecutionData& execdata) const override; }; using TransactionSignatureChecker = GenericTransactionSignatureChecker; @@ -332,6 +344,11 @@ class DeferringSignatureChecker : public BaseSignatureChecker { return m_checker.CheckSequence(nSequence); } + + uint256 GetTemplateHash(ScriptExecutionData& execdata) const override + { + return m_checker.GetTemplateHash(execdata); + } }; /** Compute the BIP341 tapleaf hash from leaf version & script. */ diff --git a/src/script/script.cpp b/src/script/script.cpp index 829e351be697a..f2aaf68e618e1 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -149,6 +149,9 @@ std::string GetOpName(opcodetype opcode) // Opcode added by BIP 342 (Tapscript) case OP_CHECKSIGADD : return "OP_CHECKSIGADD"; + // Opcode added by BIP xx (Tapscript-only, formerly OP_SUCCESS187) + case OP_TEMPLATEHASH : return "OP_TEMPLATEHASH"; + case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE"; default: diff --git a/src/script/script.h b/src/script/script.h index 6104630a1eeba..8299083576486 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -209,6 +209,9 @@ enum opcodetype // Opcode added by BIP 342 (Tapscript) OP_CHECKSIGADD = 0xba, + // Opcode added by BIP xx (Tapscript-only, formerly OP_SUCCESS206) + OP_TEMPLATEHASH = 0xce, + OP_INVALIDOPCODE = 0xff, }; diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp index fadc04262c314..efb680b135900 100644 --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -79,6 +79,8 @@ std::string ScriptErrorString(const ScriptError serror) return "OP_SUCCESSx reserved for soft-fork upgrades"; case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_PUBKEYTYPE: return "Public key version reserved for soft-fork upgrades"; + case SCRIPT_ERR_DISCOURAGE_TEMPLATEHASH: + return "Templatehash is not active"; case SCRIPT_ERR_PUBKEYTYPE: return "Public key is neither compressed or uncompressed"; case SCRIPT_ERR_CLEANSTACK: diff --git a/src/script/script_error.h b/src/script/script_error.h index 44e68fe0fae30..23bc3003f5a14 100644 --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -59,6 +59,7 @@ typedef enum ScriptError_t SCRIPT_ERR_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION, SCRIPT_ERR_DISCOURAGE_OP_SUCCESS, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_PUBKEYTYPE, + SCRIPT_ERR_DISCOURAGE_TEMPLATEHASH, /* segregated witness */ SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH, diff --git a/src/test/fuzz/CMakeLists.txt b/src/test/fuzz/CMakeLists.txt index 20a5109a3cd44..eabbaade970e3 100644 --- a/src/test/fuzz/CMakeLists.txt +++ b/src/test/fuzz/CMakeLists.txt @@ -117,6 +117,7 @@ add_executable(fuzz string.cpp strprintf.cpp system.cpp + templatehash.cpp timeoffsets.cpp torcontrol.cpp transaction.cpp diff --git a/src/test/fuzz/script_assets_test_minimizer.cpp b/src/test/fuzz/script_assets_test_minimizer.cpp index 5c5517343c510..40eab4dd64a7d 100644 --- a/src/test/fuzz/script_assets_test_minimizer.cpp +++ b/src/test/fuzz/script_assets_test_minimizer.cpp @@ -98,13 +98,14 @@ const std::map FLAG_NAMES = { {std::string("CHECKSEQUENCEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKSEQUENCEVERIFY}, {std::string("WITNESS"), (unsigned int)SCRIPT_VERIFY_WITNESS}, {std::string("TAPROOT"), (unsigned int)SCRIPT_VERIFY_TAPROOT}, + {std::string("TEMPLATE"), (unsigned int)SCRIPT_VERIFY_TEMPLATEHASH}, }; std::vector AllFlags() { std::vector ret; - for (unsigned int i = 0; i < 128; ++i) { + for (unsigned int i = 0; i < 256; ++i) { unsigned int flag = 0; if (i & 1) flag |= SCRIPT_VERIFY_P2SH; if (i & 2) flag |= SCRIPT_VERIFY_DERSIG; @@ -113,6 +114,7 @@ std::vector AllFlags() if (i & 16) flag |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY; if (i & 32) flag |= SCRIPT_VERIFY_WITNESS; if (i & 64) flag |= SCRIPT_VERIFY_TAPROOT; + if (i & 128) flag |= SCRIPT_VERIFY_TEMPLATEHASH; // SCRIPT_VERIFY_WITNESS requires SCRIPT_VERIFY_P2SH if (flag & SCRIPT_VERIFY_WITNESS && !(flag & SCRIPT_VERIFY_P2SH)) continue; diff --git a/src/test/fuzz/templatehash.cpp b/src/test/fuzz/templatehash.cpp new file mode 100644 index 0000000000000..d591a4624660b --- /dev/null +++ b/src/test/fuzz/templatehash.cpp @@ -0,0 +1,189 @@ +// Copyright (c) 2009-present 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