Skip to content

Commit 2fbfb1b

Browse files
committed
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).
1 parent a4bf840 commit 2fbfb1b

File tree

3 files changed

+33
-9
lines changed

3 files changed

+33
-9
lines changed

src/pubkey.cpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -188,11 +188,25 @@ bool XOnlyPubKey::VerifySchnorr(const uint256& msg, Span<const unsigned char> si
188188
return secp256k1_schnorrsig_verify(secp256k1_context_verify, sigbytes.data(), msg.begin(), &pubkey);
189189
}
190190

191-
bool XOnlyPubKey::CheckPayToContract(const XOnlyPubKey& base, const uint256& hash, bool parity) const
191+
static const CHashWriter HASHER_TAPTWEAK = TaggedHash("TapTweak");
192+
193+
uint256 XOnlyPubKey::ComputeTapTweakHash(const uint256* merkle_root) const
194+
{
195+
if (merkle_root == nullptr) {
196+
// We have no scripts. The actual tweak does not matter, but follow BIP341 here to
197+
// allow for reproducible tweaking.
198+
return (CHashWriter(HASHER_TAPTWEAK) << m_keydata).GetSHA256();
199+
} else {
200+
return (CHashWriter(HASHER_TAPTWEAK) << m_keydata << *merkle_root).GetSHA256();
201+
}
202+
}
203+
204+
bool XOnlyPubKey::CheckTapTweak(const XOnlyPubKey& internal, const uint256& merkle_root, bool parity) const
192205
{
193-
secp256k1_xonly_pubkey base_point;
194-
if (!secp256k1_xonly_pubkey_parse(secp256k1_context_verify, &base_point, base.data())) return false;
195-
return secp256k1_xonly_pubkey_tweak_add_check(secp256k1_context_verify, m_keydata.begin(), parity, &base_point, hash.begin());
206+
secp256k1_xonly_pubkey internal_key;
207+
if (!secp256k1_xonly_pubkey_parse(secp256k1_context_verify, &internal_key, internal.data())) return false;
208+
uint256 tweak = internal.ComputeTapTweakHash(&merkle_root);
209+
return secp256k1_xonly_pubkey_tweak_add_check(secp256k1_context_verify, m_keydata.begin(), parity, &internal_key, tweak.begin());
196210
}
197211

198212
bool CPubKey::Verify(const uint256 &hash, const std::vector<unsigned char>& vchSig) const {

src/pubkey.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,20 @@ class XOnlyPubKey
236236
* sigbytes must be exactly 64 bytes.
237237
*/
238238
bool VerifySchnorr(const uint256& msg, Span<const unsigned char> sigbytes) const;
239-
bool CheckPayToContract(const XOnlyPubKey& base, const uint256& hash, bool parity) const;
239+
240+
/** Compute the Taproot tweak as specified in BIP341, with *this as internal
241+
* key:
242+
* - if merkle_root == nullptr: H_TapTweak(xonly_pubkey)
243+
* - otherwise: H_TapTweak(xonly_pubkey || *merkle_root)
244+
*
245+
* Note that the behavior of this function with merkle_root != nullptr is
246+
* consensus critical.
247+
*/
248+
uint256 ComputeTapTweakHash(const uint256* merkle_root) const;
249+
250+
/** Verify that this is a Taproot tweaked output point, against a specified internal key,
251+
* Merkle root, and parity. */
252+
bool CheckTapTweak(const XOnlyPubKey& internal, const uint256& merkle_root, bool parity) const;
240253

241254
const unsigned char& operator[](int pos) const { return *(m_keydata.begin() + pos); }
242255
const unsigned char* data() const { return m_keydata.begin(); }

src/script/interpreter.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1486,7 +1486,6 @@ template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTr
14861486
static const CHashWriter HASHER_TAPSIGHASH = TaggedHash("TapSighash");
14871487
static const CHashWriter HASHER_TAPLEAF = TaggedHash("TapLeaf");
14881488
static const CHashWriter HASHER_TAPBRANCH = TaggedHash("TapBranch");
1489-
static const CHashWriter HASHER_TAPTWEAK = TaggedHash("TapTweak");
14901489

14911490
static bool HandleMissingData(MissingDataBehavior mdb)
14921491
{
@@ -1869,10 +1868,8 @@ static bool VerifyTaprootCommitment(const std::vector<unsigned char>& control, c
18691868
}
18701869
k = ss_branch.GetSHA256();
18711870
}
1872-
// Compute the tweak from the Merkle root and the internal pubkey.
1873-
k = (CHashWriter(HASHER_TAPTWEAK) << MakeSpan(p) << k).GetSHA256();
18741871
// Verify that the output pubkey matches the tweaked internal pubkey, after correcting for parity.
1875-
return q.CheckPayToContract(p, k, control[0] & 1);
1872+
return q.CheckTapTweak(p, k, control[0] & 1);
18761873
}
18771874

18781875
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)

0 commit comments

Comments
 (0)