aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPieter Wuille <pieter@wuille.net>2021-02-01 16:21:59 -0800
committerPieter Wuille <pieter@wuille.net>2021-05-24 12:14:16 -0700
commit2fbfb1becb3c0c109cd7c30b245b51da22039932 (patch)
tree46b4a54e0e774bb0f811bbc1268d080757932bdc
parenta4bf84039c00b196b87f969acf6369d72c56ab46 (diff)
downloadbitcoin-2fbfb1becb3c0c109cd7c30b245b51da22039932.tar.xz
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).
-rw-r--r--src/pubkey.cpp22
-rw-r--r--src/pubkey.h15
-rw-r--r--src/script/interpreter.cpp5
3 files changed, 33 insertions, 9 deletions
diff --git a/src/pubkey.cpp b/src/pubkey.cpp
index 334acb454e..f78779c182 100644
--- a/src/pubkey.cpp
+++ b/src/pubkey.cpp
@@ -188,11 +188,25 @@ bool XOnlyPubKey::VerifySchnorr(const uint256& msg, Span<const unsigned char> si
return secp256k1_schnorrsig_verify(secp256k1_context_verify, sigbytes.data(), msg.begin(), &pubkey);
}
-bool XOnlyPubKey::CheckPayToContract(const XOnlyPubKey& base, const uint256& hash, bool parity) const
+static const CHashWriter HASHER_TAPTWEAK = TaggedHash("TapTweak");
+
+uint256 XOnlyPubKey::ComputeTapTweakHash(const uint256* merkle_root) const
+{
+ if (merkle_root == nullptr) {
+ // We have no scripts. The actual tweak does not matter, but follow BIP341 here to
+ // allow for reproducible tweaking.
+ return (CHashWriter(HASHER_TAPTWEAK) << m_keydata).GetSHA256();
+ } else {
+ return (CHashWriter(HASHER_TAPTWEAK) << m_keydata << *merkle_root).GetSHA256();
+ }
+}
+
+bool XOnlyPubKey::CheckTapTweak(const XOnlyPubKey& internal, const uint256& merkle_root, bool parity) const
{
- secp256k1_xonly_pubkey base_point;
- if (!secp256k1_xonly_pubkey_parse(secp256k1_context_verify, &base_point, base.data())) return false;
- return secp256k1_xonly_pubkey_tweak_add_check(secp256k1_context_verify, m_keydata.begin(), parity, &base_point, hash.begin());
+ secp256k1_xonly_pubkey internal_key;
+ if (!secp256k1_xonly_pubkey_parse(secp256k1_context_verify, &internal_key, internal.data())) return false;
+ uint256 tweak = internal.ComputeTapTweakHash(&merkle_root);
+ return secp256k1_xonly_pubkey_tweak_add_check(secp256k1_context_verify, m_keydata.begin(), parity, &internal_key, tweak.begin());
}
bool CPubKey::Verify(const uint256 &hash, const std::vector<unsigned char>& vchSig) const {
diff --git a/src/pubkey.h b/src/pubkey.h
index 7d09faa9c1..e5d9d08b52 100644
--- a/src/pubkey.h
+++ b/src/pubkey.h
@@ -236,7 +236,20 @@ public:
* sigbytes must be exactly 64 bytes.
*/
bool VerifySchnorr(const uint256& msg, Span<const unsigned char> sigbytes) const;
- 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(); }
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index dc0f165be0..5f04d486b1 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -1486,7 +1486,6 @@ template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTr
static const CHashWriter HASHER_TAPSIGHASH = TaggedHash("TapSighash");
static const CHashWriter HASHER_TAPLEAF = TaggedHash("TapLeaf");
static const CHashWriter HASHER_TAPBRANCH = TaggedHash("TapBranch");
-static const CHashWriter HASHER_TAPTWEAK = TaggedHash("TapTweak");
static bool HandleMissingData(MissingDataBehavior mdb)
{
@@ -1869,10 +1868,8 @@ static bool VerifyTaprootCommitment(const std::vector<unsigned char>& control, c
}
k = ss_branch.GetSHA256();
}
- // Compute the tweak from the Merkle root and the internal pubkey.
- k = (CHashWriter(HASHER_TAPTWEAK) << MakeSpan(p) << k).GetSHA256();
// Verify that the output pubkey matches the tweaked internal pubkey, after correcting for parity.
- return q.CheckPayToContract(p, k, control[0] & 1);
+ return q.CheckTapTweak(p, k, control[0] & 1);
}
static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector<unsigned char>& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror, bool is_p2sh)