diff --git a/src/consensus/params.h b/src/consensus/params.h index dd29b9408e2..2b0957629ac 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -32,6 +32,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_CHECKCONTRACTVERIFY, // 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 185a7dcb54c..482309a4fcd 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 =*/ "checkcontractverify", + /*.gbt_force =*/ true, + }, }; std::string DeploymentName(Consensus::BuriedDeployment dep) diff --git a/src/kernel/chainparams.cpp b/src/kernel/chainparams.cpp index 4a747e7317c..264ca9ce50c 100644 --- a/src/kernel/chainparams.cpp +++ b/src/kernel/chainparams.cpp @@ -117,6 +117,12 @@ public: consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1628640000; // August 11th, 2021 consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 709632; // Approximately November 12th, 2021 + // Deployment of OP_CHECKCONTRACTVERIFY + consensus.vDeployments[Consensus::DEPLOYMENT_CHECKCONTRACTVERIFY].bit = 3; + consensus.vDeployments[Consensus::DEPLOYMENT_CHECKCONTRACTVERIFY].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_CHECKCONTRACTVERIFY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + consensus.vDeployments[Consensus::DEPLOYMENT_CHECKCONTRACTVERIFY].min_activation_height = 0; + consensus.nMinimumChainWork = uint256{"0000000000000000000000000000000000000000b1f3b93b65b16d035a82be84"}; consensus.defaultAssumeValid = uint256{"00000000000000000001b658dd1120e82e66d2790811f89ede9742ada3ed6d77"}; // 886157 @@ -229,6 +235,12 @@ public: consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1628640000; // August 11th, 2021 consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay + // Deployment of OP_CHECKCONTRACTVERIFY + consensus.vDeployments[Consensus::DEPLOYMENT_CHECKCONTRACTVERIFY].bit = 3; + consensus.vDeployments[Consensus::DEPLOYMENT_CHECKCONTRACTVERIFY].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_CHECKCONTRACTVERIFY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + consensus.vDeployments[Consensus::DEPLOYMENT_CHECKCONTRACTVERIFY].min_activation_height = 0; + consensus.nMinimumChainWork = uint256{"0000000000000000000000000000000000000000000015f5e0c9f13455b0eb17"}; consensus.defaultAssumeValid = uint256{"00000000000003fc7967410ba2d0a8a8d50daedc318d43e8baf1a9782c236a57"}; // 3974606 @@ -322,6 +334,12 @@ public: consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay + // Deployment of OP_CHECKCONTRACTVERIFY + consensus.vDeployments[Consensus::DEPLOYMENT_CHECKCONTRACTVERIFY].bit = 3; + consensus.vDeployments[Consensus::DEPLOYMENT_CHECKCONTRACTVERIFY].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_CHECKCONTRACTVERIFY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + consensus.vDeployments[Consensus::DEPLOYMENT_CHECKCONTRACTVERIFY].min_activation_height = 0; + consensus.nMinimumChainWork = uint256{"0000000000000000000000000000000000000000000001d6dce8651b6094e4c1"}; consensus.defaultAssumeValid = uint256{"0000000000003ed4f08dbdf6f7d6b271a6bcffce25675cb40aa9fa43179a89f3"}; // 72600 @@ -454,6 +472,12 @@ public: consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay + // Deployment of OP_CHECKCONTRACTVERIFY + consensus.vDeployments[Consensus::DEPLOYMENT_CHECKCONTRACTVERIFY].bit = 3; + consensus.vDeployments[Consensus::DEPLOYMENT_CHECKCONTRACTVERIFY].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_CHECKCONTRACTVERIFY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + consensus.vDeployments[Consensus::DEPLOYMENT_CHECKCONTRACTVERIFY].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; @@ -529,6 +553,12 @@ public: consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay + // Deployment of OP_CHECKCONTRACTVERIFY + consensus.vDeployments[Consensus::DEPLOYMENT_CHECKCONTRACTVERIFY].bit = 3; + consensus.vDeployments[Consensus::DEPLOYMENT_CHECKCONTRACTVERIFY].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_CHECKCONTRACTVERIFY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + consensus.vDeployments[Consensus::DEPLOYMENT_CHECKCONTRACTVERIFY].min_activation_height = 0; + consensus.nMinimumChainWork = uint256{}; consensus.defaultAssumeValid = uint256{}; diff --git a/src/pubkey.cpp b/src/pubkey.cpp index a4ca9a170a9..f33a5c2d4a2 100644 --- a/src/pubkey.cpp +++ b/src/pubkey.cpp @@ -273,6 +273,40 @@ std::optional> XOnlyPubKey::CreateTapTweak(const ui return ret; } +bool XOnlyPubKey::CheckDoubleTweak(const XOnlyPubKey& naked_key, const std::vector& data, const uint256* merkle_root) const +{ + int parity; + secp256k1_xonly_pubkey internal_xonly; + if (data.empty()) { + // No data tweak; the internal key is the naked key + if (!secp256k1_xonly_pubkey_parse(secp256k1_context_static, &internal_xonly, naked_key.data())) return false; + } else { + // Compute the sha256 of naked_key || data + uint256 data_tweak = (HashWriter{} << naked_key << MakeUCharSpan(data)).GetSHA256(); + secp256k1_xonly_pubkey naked_key_parsed; + if (!secp256k1_xonly_pubkey_parse(secp256k1_context_static, &naked_key_parsed, naked_key.data())) return false; + secp256k1_pubkey internal; + if (!secp256k1_xonly_pubkey_tweak_add(secp256k1_context_static, &internal, &naked_key_parsed, data_tweak.data())) return false; + if (!secp256k1_xonly_pubkey_from_pubkey(secp256k1_context_static, &internal_xonly, &parity, &internal)) return false; + } + secp256k1_xonly_pubkey expected_xonly; + if (!secp256k1_xonly_pubkey_parse(secp256k1_context_static, &expected_xonly, m_keydata.data())) return false; + if (merkle_root != nullptr) { + // Compute the taptweak based on merkle_root + unsigned char pubkey_bytes[32]; + secp256k1_xonly_pubkey_serialize(secp256k1_context_static, pubkey_bytes, &internal_xonly); + XOnlyPubKey internal_key = XOnlyPubKey(pubkey_bytes); + uint256 tweak = internal_key.ComputeTapTweakHash(merkle_root); + secp256k1_pubkey result; + if (!secp256k1_xonly_pubkey_tweak_add(secp256k1_context_static, &result, &internal_xonly, tweak.begin())) return false; + secp256k1_xonly_pubkey result_xonly; + if (!secp256k1_xonly_pubkey_from_pubkey(secp256k1_context_static, &result_xonly, &parity, &result)) return false; + return secp256k1_xonly_pubkey_cmp(secp256k1_context_static, &result_xonly, &expected_xonly) == 0; + } else { + // If merkle_root is nullptr, compare internal_xonly with expected_xonly + return secp256k1_xonly_pubkey_cmp(secp256k1_context_static, &internal_xonly, &expected_xonly) == 0; + } +} bool CPubKey::Verify(const uint256 &hash, const std::vector& vchSig) const { if (!IsValid()) diff --git a/src/pubkey.h b/src/pubkey.h index cbc827dc606..384b611a073 100644 --- a/src/pubkey.h +++ b/src/pubkey.h @@ -282,6 +282,12 @@ public: /** Construct a Taproot tweaked output point with this point as internal key. */ std::optional> CreateTapTweak(const uint256* merkle_root) const; + /** Verify that this key is obtained from the x-only pubkey `naked` after applying in sequence: + * - the tweak with `data` (this tweak is skipped if `data` is empty); + * - the taptweak with `merkle_root` (unless it's null). + */ + bool CheckDoubleTweak(const XOnlyPubKey& naked_key, const std::vector& data, const uint256* merkle_root) const; + /** Returns a list of CKeyIDs for the CPubKeys that could have been used to create this XOnlyPubKey. * This is needed for key lookups since keys are indexed by CKeyID. */ diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 61ea7f4503c..341973d2e82 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -14,6 +14,15 @@ typedef std::vector valtype; +//! Flag to mark an OP_CHECKCONTRACVERIFY as referring to an input. +const int CCV_MODE_CHECK_INPUT = -1; +//! Flag to specify that an OP_CHECKCONTRACVERIFY which refers to an output, with the default amount logic. +const int CCV_MODE_CHECK_OUTPUT = 0; +//! Flag to specify that an OP_CHECKCONTRACVERIFY which refers to an output does not check the output amount. +const int CCV_MODE_CHECK_OUTPUT_IGNORE_AMOUNT = 1; +//! Flag to specify that an OP_CHECKCONTRACVERIFY referring to an output deducts the amount of its output from the current input amount for future calls. +const int CCV_MODE_CHECK_OUTPUT_DEDUCT_AMOUNT = 2; + namespace { inline bool set_success(ScriptError* ret) @@ -403,7 +412,7 @@ static bool EvalChecksig(const valtype& sig, const valtype& pubkey, CScript::con assert(false); } -bool EvalScript(std::vector >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror) +bool EvalScript(std::vector >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror, TransactionExecutionData* tx_exec_data) { static const CScriptNum bnZero(0); static const CScriptNum bnOne(1); @@ -1101,6 +1110,51 @@ bool EvalScript(std::vector >& stack, const CScript& } break; + case OP_CHECKCONTRACTVERIFY: + { + // OP_CHECKCONTRACTVERIFY is only available in Tapscript + if (sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0) return set_error(serror, SCRIPT_ERR_BAD_OPCODE); + + // we expect at least the flag to be on the stack + if (stack.empty()) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + + // initially, read only a single parameter at the top of stack + int flags = CScriptNum(stacktop(-1), fRequireMinimal).getint(); + if (flags < -1 || flags > CCV_MODE_CHECK_OUTPUT_DEDUCT_AMOUNT) { + // undefined values of the flags; keep OP_SUCCESS behavior + // in order to enable future upgrades via soft-fork + stack = { {1} }; + return set_success(serror); + } + + // all currently defined versions require exactly 5 stack elements + + // (data index pk taptree flags -- ) + if (stack.size() < 5) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + + valtype& data = stacktop(-5); + int index = CScriptNum(stacktop(-4), fRequireMinimal).getint(); + valtype& pk = stacktop(-3); + valtype& taptree = stacktop(-2); + + if (!pk.empty() && pk != std::vector{0x81} && pk.size() != 32) { + return set_error(serror, SCRIPT_ERR_CHECKCONTRACTVERIFY_WRONG_ARGS); + } + + if (!checker.CheckContract(flags, index, pk, data, taptree, execdata, serror, tx_exec_data)) { + return false; // serror is set + } + + popstack(stack); + popstack(stack); + popstack(stack); + popstack(stack); + popstack(stack); + } + break; + case OP_CHECKMULTISIG: case OP_CHECKMULTISIGVERIFY: { @@ -1233,10 +1287,10 @@ bool EvalScript(std::vector >& stack, const CScript& return set_success(serror); } -bool EvalScript(std::vector >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror) +bool EvalScript(std::vector >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, TransactionExecutionData* tx_exec_data) { ScriptExecutionData execdata; - return EvalScript(stack, script, flags, checker, sigversion, execdata, serror); + return EvalScript(stack, script, flags, checker, sigversion, execdata, serror, tx_exec_data); } namespace { @@ -1781,14 +1835,126 @@ bool GenericTransactionSignatureChecker::CheckSequence(const CScriptNum& nSeq return true; } +template +bool GenericTransactionSignatureChecker::CheckContract(int mode, int index, const std::vector& pubkey, const std::vector& data, const std::vector& taptree, ScriptExecutionData& ScriptExecutionData, ScriptError* serror, TransactionExecutionData* tx_exec_data) const +{ + assert(ScriptExecutionData.m_internal_key.has_value()); + assert(ScriptExecutionData.m_taproot_merkle_root.has_value()); + assert(tx_exec_data != nullptr); + + if (!(txdata->m_bip341_taproot_ready && txdata->m_spent_outputs_ready)) { + return HandleMissingData(m_mdb); + } + + bool use_current_taptree = taptree.size() == 1 && taptree.data()[0] == 0x81; + bool use_current_pubkey = pubkey.size() == 1 && pubkey.data()[0] == 0x81; + + uint256 merkle_tree; + const uint256 *merkle_tree_ptr = nullptr; + if (taptree.empty()) { + // no taptweak, leave nullptr + } else if (use_current_taptree) { + merkle_tree_ptr = &ScriptExecutionData.m_taproot_merkle_root.value(); + } else if (taptree.size() == 32) { + merkle_tree = uint256(taptree); + merkle_tree_ptr = &merkle_tree; + } else { + return set_error(serror, SCRIPT_ERR_CHECKCONTRACTVERIFY_WRONG_ARGS); + } + + XOnlyPubKey initialXOnlyKey; + if (use_current_pubkey) { + initialXOnlyKey = ScriptExecutionData.m_internal_key.value(); + } else if (pubkey.empty()) { + initialXOnlyKey = XOnlyPubKey::NUMS_H; + } else { + initialXOnlyKey = XOnlyPubKey{std::span{pubkey.data(), pubkey.data() + 32}}; + } + + if (index == -1) { + index = nIn; + } + + auto indexLimit = (mode == CCV_MODE_CHECK_INPUT ? txTo->vin.size() : txTo->vout.size()); + if (index < 0 || index >= static_cast(indexLimit)) { + return set_error(serror, SCRIPT_ERR_CHECKCONTRACTVERIFY_OUT_OF_BOUNDS); + } + + CScript scriptPubKey = (mode == CCV_MODE_CHECK_INPUT) ? txdata->m_spent_outputs[index].scriptPubKey : txTo->vout.at(index).scriptPubKey; + + if (scriptPubKey.size() != 1 + 1 + 32 || scriptPubKey[0] != OP_1 || scriptPubKey[1] != 32) { + return set_error(serror, SCRIPT_ERR_CHECKCONTRACTVERIFY_WRONG_ARGS); + } + + const XOnlyPubKey finalXOnlyKey{std::span{scriptPubKey.data() + 2, scriptPubKey.data() + 34}}; + + if (!finalXOnlyKey.CheckDoubleTweak(initialXOnlyKey, data, merkle_tree_ptr)) { + return set_error(serror, SCRIPT_ERR_CHECKCONTRACTVERIFY_MISMATCH); + } + + + if (!ScriptExecutionData.m_ccv_amount_init) { + ScriptExecutionData.m_ccv_amount = amount; + ScriptExecutionData.m_ccv_amount_init = true; + } + + { + LOCK(tx_exec_data->m_mutex); + if (tx_exec_data->m_error.has_value()) { + // an error related to tx_exec_data already occurred while validating the transaction + return set_error(serror, tx_exec_data->m_error.value()); + } + + switch (mode) { + case CCV_MODE_CHECK_OUTPUT: + if (tx_exec_data->m_ccv_output_checked_deduct[index]) { + tx_exec_data->m_error = SCRIPT_ERR_CHECKCONTRACTVERIFY_WRONG_AMOUNT; + return set_error(serror, SCRIPT_ERR_CHECKCONTRACTVERIFY_WRONG_AMOUNT); + } + tx_exec_data->m_ccv_output_checked_default[index] = true; + + tx_exec_data->m_ccv_output_min_amount[index] += ScriptExecutionData.m_ccv_amount; + ScriptExecutionData.m_ccv_amount = 0; + if (txTo->vout[index].nValue < tx_exec_data->m_ccv_output_min_amount[index]) { + tx_exec_data->m_error = SCRIPT_ERR_CHECKCONTRACTVERIFY_WRONG_AMOUNT; + return set_error(serror, SCRIPT_ERR_CHECKCONTRACTVERIFY_WRONG_AMOUNT); + } + break; + case CCV_MODE_CHECK_OUTPUT_IGNORE_AMOUNT: + // amount checking is disabled + break; + case CCV_MODE_CHECK_OUTPUT_DEDUCT_AMOUNT: + if (tx_exec_data->m_ccv_output_checked_default[index] || tx_exec_data->m_ccv_output_checked_deduct[index]) { + tx_exec_data->m_error = SCRIPT_ERR_CHECKCONTRACTVERIFY_WRONG_AMOUNT; + return set_error(serror, SCRIPT_ERR_CHECKCONTRACTVERIFY_WRONG_AMOUNT); + } + tx_exec_data->m_ccv_output_checked_deduct[index] = true; + // subtract amount from input + if (txTo->vout[index].nValue > ScriptExecutionData.m_ccv_amount) { + tx_exec_data->m_error = SCRIPT_ERR_CHECKCONTRACTVERIFY_WRONG_AMOUNT; + return set_error(serror, SCRIPT_ERR_CHECKCONTRACTVERIFY_WRONG_AMOUNT); + } + ScriptExecutionData.m_ccv_amount -= txTo->vout[index].nValue; + + break; + default: + break; + } + } + + return true; +} + // explicit instantiation template class GenericTransactionSignatureChecker; template class GenericTransactionSignatureChecker; -static bool ExecuteWitnessScript(const std::span& stack_span, const CScript& exec_script, unsigned int flags, SigVersion sigversion, const BaseSignatureChecker& checker, ScriptExecutionData& execdata, ScriptError* serror) +static bool ExecuteWitnessScript(const std::span& stack_span, const CScript& exec_script, unsigned int flags, SigVersion sigversion, const BaseSignatureChecker& checker, ScriptExecutionData& execdata, TransactionExecutionData* tx_exec_data, ScriptError* serror) { std::vector stack{stack_span.begin(), stack_span.end()}; + const bool is_checkcontractverify_active = (flags & SCRIPT_VERIFY_CHECKCONTRACTVERIFY); + if (sigversion == SigVersion::TAPSCRIPT) { // OP_SUCCESSx processing overrides everything, including stack element size limits CScript::const_iterator pc = exec_script.begin(); @@ -1799,7 +1965,9 @@ static bool ExecuteWitnessScript(const std::span& stack_span, con return set_error(serror, SCRIPT_ERR_BAD_OPCODE); } // New opcodes will be listed here. May use a different sigversion to modify existing opcodes. - if (IsOpSuccess(opcode)) { + if (is_checkcontractverify_active && opcode == OP_CHECKCONTRACTVERIFY) { + continue; + } else if (IsOpSuccess(opcode)) { if (flags & SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS) { return set_error(serror, SCRIPT_ERR_DISCOURAGE_OP_SUCCESS); } @@ -1817,7 +1985,7 @@ static bool ExecuteWitnessScript(const std::span& stack_span, con } // Run the script interpreter. - if (!EvalScript(stack, exec_script, flags, checker, sigversion, execdata, serror)) return false; + if (!EvalScript(stack, exec_script, flags, checker, sigversion, execdata, serror, tx_exec_data)) return false; // Scripts inside witness implicitly require cleanstack behaviour if (stack.size() != 1) return set_error(serror, SCRIPT_ERR_CLEANSTACK); @@ -1856,21 +2024,22 @@ uint256 ComputeTaprootMerkleRoot(std::span control, const u return k; } -static bool VerifyTaprootCommitment(const std::vector& control, const std::vector& program, const uint256& tapleaf_hash) +static bool VerifyTaprootCommitment(const std::vector& control, const std::vector& program, const uint256& tapleaf_hash, std::optional& internal_key, std::optional& merkle_root) { assert(control.size() >= TAPROOT_CONTROL_BASE_SIZE); assert(program.size() >= uint256::size()); //! The internal pubkey (x-only, so no Y coordinate parity). const XOnlyPubKey p{std::span{control}.subspan(1, TAPROOT_CONTROL_BASE_SIZE - 1)}; + internal_key = p; //! The output pubkey (taken from the scriptPubKey). const XOnlyPubKey q{program}; // Compute the Merkle root from the leaf and the provided path. - const uint256 merkle_root = ComputeTaprootMerkleRoot(control, tapleaf_hash); + merkle_root = ComputeTaprootMerkleRoot(control, tapleaf_hash); // Verify that the output pubkey matches the tweaked internal pubkey, after correcting for parity. - return q.CheckTapTweak(p, merkle_root, control[0] & 1); + return q.CheckTapTweak(p, merkle_root.value(), control[0] & 1); } -static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror, bool is_p2sh) +static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror, bool is_p2sh, TransactionExecutionData* tx_exec_data = nullptr) { CScript exec_script; //!< Actually executed script (last stack item in P2WSH; implied P2PKH script in P2WPKH; leaf script in P2TR) std::span stack{witness.stack}; @@ -1889,14 +2058,14 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, if (memcmp(hash_exec_script.begin(), program.data(), 32)) { return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH); } - return ExecuteWitnessScript(stack, exec_script, flags, SigVersion::WITNESS_V0, checker, execdata, serror); + return ExecuteWitnessScript(stack, exec_script, flags, SigVersion::WITNESS_V0, checker, execdata, tx_exec_data, serror); } else if (program.size() == WITNESS_V0_KEYHASH_SIZE) { // BIP141 P2WPKH: 20-byte witness v0 program (which encodes Hash160(pubkey)) if (stack.size() != 2) { return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH); // 2 items in witness } exec_script << OP_DUP << OP_HASH160 << program << OP_EQUALVERIFY << OP_CHECKSIG; - return ExecuteWitnessScript(stack, exec_script, flags, SigVersion::WITNESS_V0, checker, execdata, serror); + return ExecuteWitnessScript(stack, exec_script, flags, SigVersion::WITNESS_V0, checker, execdata, tx_exec_data, serror); } else { return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH); } @@ -1927,7 +2096,7 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, return set_error(serror, SCRIPT_ERR_TAPROOT_WRONG_CONTROL_SIZE); } execdata.m_tapleaf_hash = ComputeTapleafHash(control[0] & TAPROOT_LEAF_MASK, script); - if (!VerifyTaprootCommitment(control, program, execdata.m_tapleaf_hash)) { + if (!VerifyTaprootCommitment(control, program, execdata.m_tapleaf_hash, execdata.m_internal_key, execdata.m_taproot_merkle_root)) { return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH); } execdata.m_tapleaf_hash_init = true; @@ -1936,7 +2105,7 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, exec_script = CScript(script.begin(), script.end()); execdata.m_validation_weight_left = ::GetSerializeSize(witness.stack) + VALIDATION_WEIGHT_OFFSET; execdata.m_validation_weight_left_init = true; - return ExecuteWitnessScript(stack, exec_script, flags, SigVersion::TAPSCRIPT, checker, execdata, serror); + return ExecuteWitnessScript(stack, exec_script, flags, SigVersion::TAPSCRIPT, checker, execdata, tx_exec_data, serror); } if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION) { return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION); @@ -1955,7 +2124,7 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, // There is intentionally no return statement here, to be able to use "control reaches end of non-void function" warnings to detect gaps in the logic above. } -bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror) +bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror, TransactionExecutionData* tx_exec_data) { static const CScriptWitness emptyWitness; if (witness == nullptr) { @@ -1995,7 +2164,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C // The scriptSig must be _exactly_ CScript(), otherwise we reintroduce malleability. return set_error(serror, SCRIPT_ERR_WITNESS_MALLEATED); } - if (!VerifyWitnessProgram(*witness, witnessversion, witnessprogram, flags, checker, serror, /*is_p2sh=*/false)) { + if (!VerifyWitnessProgram(*witness, witnessversion, witnessprogram, flags, checker, serror, /*is_p2sh=*/false, tx_exec_data)) { return false; } // Bypass the cleanstack check at the end. The actual stack is obviously not clean @@ -2040,7 +2209,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C // reintroduce malleability. return set_error(serror, SCRIPT_ERR_WITNESS_MALLEATED_P2SH); } - if (!VerifyWitnessProgram(*witness, witnessversion, witnessprogram, flags, checker, serror, /*is_p2sh=*/true)) { + if (!VerifyWitnessProgram(*witness, witnessversion, witnessprogram, flags, checker, serror, /*is_p2sh=*/true, tx_exec_data)) { return false; } // Bypass the cleanstack check at the end. The actual stack is obviously not clean diff --git a/src/script/interpreter.h b/src/script/interpreter.h index e8c5b09045f..19b32c060cd 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -9,13 +9,16 @@ #include #include #include +#include #include