diff --git a/src/policy/policy.h b/src/policy/policy.h index 6a7980c312c..938f823fbc4 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -113,7 +113,8 @@ 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_INTERNALKEY}; /** 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/script/interpreter.cpp b/src/script/interpreter.cpp index c969ce45f12..a693d43374c 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1213,6 +1213,15 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& } break; + case OP_INTERNALKEY: { + // OP_INTERNALKEY is only available in Tapscript + if (sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0) return set_error(serror, SCRIPT_ERR_BAD_OPCODE); + // Always present in Tapscript + assert(execdata.m_internal_key); + stack.emplace_back(execdata.m_internal_key->begin(), execdata.m_internal_key->end()); + break; + } + default: return set_error(serror, SCRIPT_ERR_BAD_OPCODE); } @@ -1798,6 +1807,12 @@ static bool ExecuteWitnessScript(const Span<const valtype>& stack_span, const CS // Note how this condition would not be reached if an unknown OP_SUCCESSx was found return set_error(serror, SCRIPT_ERR_BAD_OPCODE); } + if (opcode == OP_INTERNALKEY) { + if (flags & SCRIPT_VERIFY_DISCOURAGE_INTERNALKEY) + return set_error(serror, SCRIPT_ERR_DISCOURAGE_OP_SUCCESS); + if (flags & SCRIPT_VERIFY_INTERNALKEY) continue; + return set_success(serror); + } // New opcodes will be listed here. May use a different sigversion to modify existing opcodes. if (IsOpSuccess(opcode)) { if (flags & SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS) { @@ -1856,12 +1871,13 @@ uint256 ComputeTaprootMerkleRoot(Span<const unsigned char> control, const uint25 return k; } -static bool VerifyTaprootCommitment(const std::vector<unsigned char>& control, const std::vector<unsigned char>& program, const uint256& tapleaf_hash) +static bool VerifyTaprootCommitment(const std::vector<unsigned char>& control, const std::vector<unsigned char>& program, const uint256& tapleaf_hash, std::optional<XOnlyPubKey>& internal_key) { 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{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. @@ -1927,7 +1943,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)) { return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH); } execdata.m_tapleaf_hash_init = true; diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 836c2e7982a..c354bbf968f 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -11,6 +11,7 @@ #include <primitives/transaction.h> #include <script/script_error.h> // IWYU pragma: export #include <span.h> +#include <pubkey.h> #include <uint256.h> #include <cstddef> @@ -143,6 +144,12 @@ enum : uint32_t { // Making unknown public key versions (in BIP 342 scripts) non-standard SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE = (1U << 20), + // Executing OP_INTERNALKEY + SCRIPT_VERIFY_INTERNALKEY = (1U << 21), + + // Making OP_INTERNALKEY non-standard + SCRIPT_VERIFY_DISCOURAGE_INTERNALKEY = (1U << 22), + // Constants to point to the highest flag in use. Add new flags above this line. // SCRIPT_VERIFY_END_MARKER @@ -221,6 +228,9 @@ struct ScriptExecutionData //! The hash of the corresponding output std::optional<uint256> m_output_hash; + + //! The taproot internal key. */ + std::optional<XOnlyPubKey> m_internal_key = std::nullopt; }; /** Signature hash sizes */ diff --git a/src/script/script.cpp b/src/script/script.cpp index 80e8d26bcfb..a1363dee5ae 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -149,6 +149,8 @@ std::string GetOpName(opcodetype opcode) // Opcode added by BIP 342 (Tapscript) case OP_CHECKSIGADD : return "OP_CHECKSIGADD"; + case OP_INTERNALKEY : return "OP_INTERNALKEY"; + case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE"; default: diff --git a/src/script/script.h b/src/script/script.h index 66d63fae89e..d2c04ace085 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -207,6 +207,7 @@ enum opcodetype // Opcode added by BIP 342 (Tapscript) OP_CHECKSIGADD = 0xba, + OP_INTERNALKEY = 0xcb, OP_INVALIDOPCODE = 0xff, }; diff --git a/src/test/data/tx_valid.json b/src/test/data/tx_valid.json index 70df0d0f697..a6e941c0743 100644 --- a/src/test/data/tx_valid.json +++ b/src/test/data/tx_valid.json @@ -520,5 +520,13 @@ [[["1111111111111111111111111111111111111111111111111111111111111111", 0, "0x00 0x14 0x751e76e8199196d454941c45d1b3a323f1433bd6", 5000000]], "0100000000010111111111111111111111111111111111111111111111111111111111111111110000000000ffffffff0130244c0000000000fd02014cdc1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111175210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac02483045022100c1a4a6581996a7fdfea77d58d537955a5655c1d619b6f3ab6874f28bb2e19708022056402db6fede03caae045a3be616a1a2d0919a475ed4be828dc9ff21f24063aa01210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179800000000", "NONE"], + ["Test OP_INTERNALKEY"], + [[["e2f2baee9c59389b34e39742ce05debf64aaa7a00fbdab88614f4d3c133186d5", + 0, + "1 0x20 0xa9e62de0f9782710f702214fc81c0f0f90fb3537987b3685caad6d52db305447", + 155000]], +"02000000000101d58631133c4d4f6188abbd0fa0a7aa64bfde05ce4297e3349b38599ceebaf2e20000000000ffffffff01f0490200000000002251202ca3bc76489a54904ad2507005789afc1e6b362b451be89f69de39ddf9ba8abf0223cb2050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac08721c050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac000000000", + "DISCOURAGE_INTERNALKEY"], + ["Make diffs cleaner by leaving a comment here without comma at the end"] ] diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index e6cf64611ef..04be9fbd543 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -65,6 +65,8 @@ static std::map<std::string, unsigned int> mapFlagNames = { {std::string("DISCOURAGE_UPGRADABLE_PUBKEYTYPE"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE}, {std::string("DISCOURAGE_OP_SUCCESS"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS}, {std::string("DISCOURAGE_UPGRADABLE_TAPROOT_VERSION"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION}, + {std::string("INTERNALKEY"), (unsigned int)SCRIPT_VERIFY_INTERNALKEY}, + {std::string("DISCOURAGE_INTERNALKEY"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_INTERNALKEY}, }; unsigned int ParseScriptFlags(std::string strFlags) diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py index 3275517888d..80aa66cfa31 100644 --- a/test/functional/test_framework/script.py +++ b/test/functional/test_framework/script.py @@ -924,4 +924,4 @@ def taproot_construct(pubkey, scripts=None, treat_internal_as_infinity=False): return TaprootInfo(CScript([OP_1, tweaked]), pubkey, negated + 0, tweak, leaves, h, tweaked) def is_op_success(o): - return o == 0x50 or o == 0x62 or o == 0x89 or o == 0x8a or o == 0x8d or o == 0x8e or (o >= 0x7e and o <= 0x81) or (o >= 0x83 and o <= 0x86) or (o >= 0x95 and o <= 0x99) or (o >= 0xbb and o <= 0xfe) + return o == 0x50 or o == 0x62 or o == 0x89 or o == 0x8a or o == 0x8d or o == 0x8e or (o >= 0x7e and o <= 0x81) or (o >= 0x83 and o <= 0x86) or (o >= 0x95 and o <= 0x99) or (o >= 0xbb and o <= 0xca) or (o >= 0xcc and o <= 0xfe)