From 86f35b24d1a55d015e0656865cf22f3f516007ae Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Thu, 19 Aug 2021 13:19:19 +0000 Subject: [PATCH] Make consensus checking of tweaks in pubkey.* Taproot-specific That results in a much safer interface (making the tweak commit to the key implicitly using a fixed tag means it can't be used for unrelated tweaking). https://github.com/bitcoin/bitcoin/pull/22051 (5/9) We actually preserve the "unrelated tweaking" method so we can use it in OP_TWEAKVERIFY --- src/pubkey.cpp | 25 ++++++++++++++++++++++++- src/pubkey.h | 15 +++++++++++++++ src/script/interpreter.cpp | 5 ++--- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/pubkey.cpp b/src/pubkey.cpp index cc6d56c84d1..ea5936d89ca 100644 --- a/src/pubkey.cpp +++ b/src/pubkey.cpp @@ -181,11 +181,34 @@ bool XOnlyPubKey::VerifySchnorr(const Span msg, Span msg, Span sigbytes) const; + // ELEMENTS: this is preserved from an old version of the Taproot code for use in OP_TWEAKVERIFY bool CheckPayToContract(const XOnlyPubKey& base, const uint256& hash, bool parity) const; + /** Compute the Taproot tweak as specified in BIP341, with *this as internal + * key: + * - if merkle_root == nullptr: H_TapTweak(xonly_pubkey) + * - otherwise: H_TapTweak(xonly_pubkey || *merkle_root) + * + * Note that the behavior of this function with merkle_root != nullptr is + * consensus critical. + */ + uint256 ComputeTapTweakHash(const uint256* merkle_root) const; + + /** Verify that this is a Taproot tweaked output point, against a specified internal key, + * Merkle root, and parity. */ + bool CheckTapTweak(const XOnlyPubKey& internal, const uint256& merkle_root, bool parity) const; + const unsigned char& operator[](int pos) const { return *(m_keydata.begin() + pos); } const unsigned char* data() const { return m_keydata.begin(); } static constexpr size_t size() { return decltype(m_keydata)::size(); } diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 2df9c4fdf58..f2a6a7949d5 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -575,7 +575,6 @@ static bool EvalChecksig(const valtype& sig, const valtype& pubkey, CScript::con static const CHashWriter HASHER_TAPLEAF_ELEMENTS = TaggedHash("TapLeaf/elements"); static const CHashWriter HASHER_TAPBRANCH_ELEMENTS = TaggedHash("TapBranch/elements"); -static const CHashWriter HASHER_TAPTWEAK_ELEMENTS = TaggedHash("TapTweak/elements"); static const CHashWriter HASHER_TAPSIGHASH_ELEMENTS = TaggedHash("TapSighash/elements"); bool EvalScript(std::vector >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror) @@ -3086,8 +3085,8 @@ static bool VerifyTaprootCommitment(const std::vector& control, c } k = ss_branch.GetSHA256(); } - k = (CHashWriter(HASHER_TAPTWEAK_ELEMENTS) << MakeSpan(p) << k).GetSHA256(); - return q.CheckPayToContract(p, k, control[0] & 1); + // Verify that the output pubkey matches the tweaked internal pubkey, after correcting for parity. + return q.CheckTapTweak(p, k, 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)