From 70ec9f20341a5337372597ed481b048e40982b55 Mon Sep 17 00:00:00 2001 From: dergoegge Date: Tue, 10 Dec 2024 10:46:40 +0000 Subject: [PATCH 1/2] [interpreter] Move taproot commitment and witness program check to BaseSignatureChecker --- src/script/interpreter.cpp | 49 ++++++++++++++++++----------- src/script/interpreter.h | 29 +++++++++++++++++ src/script/sign.cpp | 13 ++++++++ src/test/fuzz/miniscript.cpp | 12 +++++++ src/test/fuzz/signature_checker.cpp | 12 +++++++ src/test/miniscript_tests.cpp | 13 ++++++++ 6 files changed, 110 insertions(+), 18 deletions(-) diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index a35306b6935..d77dc89b675 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1781,6 +1781,35 @@ bool GenericTransactionSignatureChecker::CheckSequence(const CScriptNum& nSeq return true; } +template +bool GenericTransactionSignatureChecker::CheckTaprootCommitment( + const std::vector& control, + const std::vector& program, + const uint256& tapleaf_hash) const +{ + 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)}; + //! 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); + // Verify that the output pubkey matches the tweaked internal pubkey, after correcting for parity. + return q.CheckTapTweak(p, merkle_root, control[0] & 1); +} + +template +bool GenericTransactionSignatureChecker::CheckWitnessScriptHash( + Span program, + const CScript& exec_script) const +{ + assert(program.size() >= uint256::size()); + uint256 hash_exec_script; + CSHA256().Write(exec_script.data(), exec_script.size()).Finalize(hash_exec_script.begin()); + return memcmp(hash_exec_script.begin(), program.data(), uint256::size()) == 0; +} + // explicit instantiation template class GenericTransactionSignatureChecker; template class GenericTransactionSignatureChecker; @@ -1856,20 +1885,6 @@ uint256 ComputeTaprootMerkleRoot(Span control, const uint25 return k; } -static bool VerifyTaprootCommitment(const std::vector& control, const std::vector& program, const uint256& tapleaf_hash) -{ - 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)}; - //! 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); - // Verify that the output pubkey matches the tweaked internal pubkey, after correcting for parity. - return q.CheckTapTweak(p, merkle_root, 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) { CScript exec_script; //!< Actually executed script (last stack item in P2WSH; implied P2PKH script in P2WPKH; leaf script in P2TR) @@ -1884,9 +1899,7 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, } const valtype& script_bytes = SpanPopBack(stack); exec_script = CScript(script_bytes.begin(), script_bytes.end()); - uint256 hash_exec_script; - CSHA256().Write(exec_script.data(), exec_script.size()).Finalize(hash_exec_script.begin()); - if (memcmp(hash_exec_script.begin(), program.data(), 32)) { + if (!checker.CheckWitnessScriptHash(program, exec_script)) { return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH); } return ExecuteWitnessScript(stack, exec_script, flags, SigVersion::WITNESS_V0, checker, execdata, serror); @@ -1927,7 +1940,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 (!checker.CheckTaprootCommitment(control, program, execdata.m_tapleaf_hash)) { 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 e2fb1998f0b..39c0ea67a60 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -265,6 +265,18 @@ public: return false; } + virtual bool CheckTaprootCommitment(const std::vector& control, + const std::vector& program, + const uint256& tapleaf_hash) const + { + return false; + } + virtual bool CheckWitnessScriptHash(Span program, + const CScript& exec_script) const + { + return false; + } + virtual ~BaseSignatureChecker() = default; }; @@ -301,6 +313,11 @@ 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 CheckTaprootCommitment(const std::vector& control, + const std::vector& program, + const uint256& tapleaf_hash) const override; + bool CheckWitnessScriptHash(Span program, + const CScript& exec_script) const override; }; using TransactionSignatureChecker = GenericTransactionSignatureChecker; @@ -332,6 +349,18 @@ public: { return m_checker.CheckSequence(nSequence); } + + bool CheckTaprootCommitment(const std::vector& control, + const std::vector& program, + const uint256& tapleaf_hash) const override + { + return m_checker.CheckTaprootCommitment(control, program, tapleaf_hash); + } + bool CheckWitnessScriptHash(Span program, + const CScript& exec_script) const override + { + return m_checker.CheckWitnessScriptHash(program, exec_script); + } }; /** Compute the BIP341 tapleaf hash from leaf version & script. */ diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 42db2513597..bcc4af1dfef 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -704,6 +704,19 @@ public: bool CheckSchnorrSignature(Span sig, Span pubkey, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror) const override { return sig.size() != 0; } bool CheckLockTime(const CScriptNum& nLockTime) const override { return true; } bool CheckSequence(const CScriptNum& nSequence) const override { return true; } + + bool CheckTaprootCommitment(const std::vector& control, + const std::vector& program, + const uint256& tapleaf_hash) const override + { + return true; + } + + bool CheckWitnessScriptHash(Span program, + const CScript& exec_script) const override + { + return true; + } }; } diff --git a/src/test/fuzz/miniscript.cpp b/src/test/fuzz/miniscript.cpp index 60d096bb5a9..51eb3361b29 100644 --- a/src/test/fuzz/miniscript.cpp +++ b/src/test/fuzz/miniscript.cpp @@ -299,6 +299,18 @@ const struct CheckerContext: BaseSignatureChecker { } bool CheckLockTime(const CScriptNum& nLockTime) const override { return nLockTime.GetInt64() & 1; } bool CheckSequence(const CScriptNum& nSequence) const override { return nSequence.GetInt64() & 1; } + + bool CheckTaprootCommitment(const std::vector& control, + const std::vector& program, + const uint256& tapleaf_hash) const override + { + return true; + } + bool CheckWitnessScriptHash(Span program, + const CScript& exec_script) const override + { + return true; + } } CHECKER_CTX; //! Context to check for duplicates when instancing a Node. diff --git a/src/test/fuzz/signature_checker.cpp b/src/test/fuzz/signature_checker.cpp index 59f4792961e..f7d2d5837bc 100644 --- a/src/test/fuzz/signature_checker.cpp +++ b/src/test/fuzz/signature_checker.cpp @@ -44,6 +44,18 @@ public: return m_fuzzed_data_provider.ConsumeBool(); } + bool CheckTaprootCommitment(const std::vector& control, + const std::vector& program, + const uint256& tapleaf_hash) const override + { + return m_fuzzed_data_provider.ConsumeBool(); + } + bool CheckWitnessScriptHash(Span program, + const CScript& exec_script) const override + { + return m_fuzzed_data_provider.ConsumeBool(); + } + virtual ~FuzzedSignatureChecker() = default; }; } // namespace diff --git a/src/test/miniscript_tests.cpp b/src/test/miniscript_tests.cpp index 077ef498b2d..f0fe70d5f54 100644 --- a/src/test/miniscript_tests.cpp +++ b/src/test/miniscript_tests.cpp @@ -289,6 +289,19 @@ public: // Delegate to Satisfier. return ctx.CheckOlder(sequence.GetInt64()); } + + bool CheckTaprootCommitment(const std::vector& control, + const std::vector& program, + const uint256& tapleaf_hash) const override + { + return true; + } + + bool CheckWitnessScriptHash(Span program, + const CScript& exec_script) const override + { + return true; + } }; using Fragment = miniscript::Fragment; From f9bc6ba48dd83f6d10ef23f1271388ca04031853 Mon Sep 17 00:00:00 2001 From: dergoegge Date: Tue, 10 Dec 2024 10:47:35 +0000 Subject: [PATCH 2/2] [fuzz] Introduce script_flags_mocked to cover segwit v{0,1} script --- src/test/fuzz/script_flags.cpp | 91 +++++++++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 2 deletions(-) diff --git a/src/test/fuzz/script_flags.cpp b/src/test/fuzz/script_flags.cpp index accb32f1cc4..dad037969ac 100644 --- a/src/test/fuzz/script_flags.cpp +++ b/src/test/fuzz/script_flags.cpp @@ -3,6 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include +#include #include #include