From 68961ca60e1a82eba11810d73d89249960784d16 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 | 22 ++++++++++++++++++++++ src/pubkey.h | 15 +++++++++++++++ src/script/interpreter.cpp | 5 ++--- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/pubkey.cpp b/src/pubkey.cpp index a909e86830..a79913e648 100644 --- a/src/pubkey.cpp +++ b/src/pubkey.cpp @@ -181,6 +181,7 @@ bool XOnlyPubKey::VerifySchnorr(const Span msg, SpanIsCompressed()); diff --git a/src/pubkey.h b/src/pubkey.h index 0a6ee943e9..b26ef349e5 100644 --- a/src/pubkey.h +++ b/src/pubkey.h @@ -231,8 +231,23 @@ public: * sigbytes must be exactly 64 bytes. */ bool VerifySchnorr(const 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 62e2d9df86..9c77382a2b 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -569,7 +569,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) @@ -3074,8 +3073,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)