Skip to content

Commit 68961ca

Browse files
apoelstrasanket1729
authored andcommitted
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). bitcoin/bitcoin#22051 (5/9) We actually preserve the "unrelated tweaking" method so we can use it in OP_TWEAKVERIFY
1 parent c3945cb commit 68961ca

3 files changed

Lines changed: 39 additions & 3 deletions

File tree

src/pubkey.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,13 +181,35 @@ bool XOnlyPubKey::VerifySchnorr(const Span<const unsigned char> msg, Span<const
181181
return secp256k1_schnorrsig_verify(secp256k1_context_verify, sigbytes.data(), msg.data(), msg.size(), &pubkey);
182182
}
183183

184+
// ELEMENTS: this is preserved from an old version of the Taproot code for use in OP_TWEAKVERIFY
184185
bool XOnlyPubKey::CheckPayToContract(const XOnlyPubKey& base, const uint256& hash, bool parity) const
185186
{
186187
secp256k1_xonly_pubkey base_point;
187188
if (!secp256k1_xonly_pubkey_parse(secp256k1_context_verify, &base_point, base.data())) return false;
188189
return secp256k1_xonly_pubkey_tweak_add_check(secp256k1_context_verify, m_keydata.begin(), parity, &base_point, hash.begin());
189190
}
190191

192+
static const CHashWriter HASHER_TAPTWEAK_ELEMENTS = TaggedHash("TapTweak/elements");
193+
194+
uint256 XOnlyPubKey::ComputeTapTweakHash(const uint256* merkle_root) const
195+
{
196+
if (merkle_root == nullptr) {
197+
// We have no scripts. The actual tweak does not matter, but follow BIP341 here to
198+
// allow for reproducible tweaking.
199+
return (CHashWriter(HASHER_TAPTWEAK_ELEMENTS) << m_keydata).GetSHA256();
200+
} else {
201+
return (CHashWriter(HASHER_TAPTWEAK_ELEMENTS) << m_keydata << *merkle_root).GetSHA256();
202+
}
203+
}
204+
205+
bool XOnlyPubKey::CheckTapTweak(const XOnlyPubKey& internal, const uint256& merkle_root, bool parity) const
206+
{
207+
secp256k1_xonly_pubkey internal_key;
208+
if (!secp256k1_xonly_pubkey_parse(secp256k1_context_verify, &internal_key, internal.data())) return false;
209+
uint256 tweak = internal.ComputeTapTweakHash(&merkle_root);
210+
return secp256k1_xonly_pubkey_tweak_add_check(secp256k1_context_verify, m_keydata.begin(), parity, &internal_key, tweak.begin());
211+
}
212+
191213
bool CPubKey::TweakMulVerify(const CPubKey& untweaked, const uint256& tweak) const
192214
{
193215
assert(this->IsCompressed());

src/pubkey.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,8 +231,23 @@ class XOnlyPubKey
231231
* sigbytes must be exactly 64 bytes.
232232
*/
233233
bool VerifySchnorr(const Span<const unsigned char> msg, Span<const unsigned char> sigbytes) const;
234+
// ELEMENTS: this is preserved from an old version of the Taproot code for use in OP_TWEAKVERIFY
234235
bool CheckPayToContract(const XOnlyPubKey& base, const uint256& hash, bool parity) const;
235236

237+
/** Compute the Taproot tweak as specified in BIP341, with *this as internal
238+
* key:
239+
* - if merkle_root == nullptr: H_TapTweak(xonly_pubkey)
240+
* - otherwise: H_TapTweak(xonly_pubkey || *merkle_root)
241+
*
242+
* Note that the behavior of this function with merkle_root != nullptr is
243+
* consensus critical.
244+
*/
245+
uint256 ComputeTapTweakHash(const uint256* merkle_root) const;
246+
247+
/** Verify that this is a Taproot tweaked output point, against a specified internal key,
248+
* Merkle root, and parity. */
249+
bool CheckTapTweak(const XOnlyPubKey& internal, const uint256& merkle_root, bool parity) const;
250+
236251
const unsigned char& operator[](int pos) const { return *(m_keydata.begin() + pos); }
237252
const unsigned char* data() const { return m_keydata.begin(); }
238253
static constexpr size_t size() { return decltype(m_keydata)::size(); }

src/script/interpreter.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,6 @@ static bool EvalChecksig(const valtype& sig, const valtype& pubkey, CScript::con
569569

570570
static const CHashWriter HASHER_TAPLEAF_ELEMENTS = TaggedHash("TapLeaf/elements");
571571
static const CHashWriter HASHER_TAPBRANCH_ELEMENTS = TaggedHash("TapBranch/elements");
572-
static const CHashWriter HASHER_TAPTWEAK_ELEMENTS = TaggedHash("TapTweak/elements");
573572
static const CHashWriter HASHER_TAPSIGHASH_ELEMENTS = TaggedHash("TapSighash/elements");
574573

575574
bool EvalScript(std::vector<std::vector<unsigned char> >& 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<unsigned char>& control, c
30743073
}
30753074
k = ss_branch.GetSHA256();
30763075
}
3077-
k = (CHashWriter(HASHER_TAPTWEAK_ELEMENTS) << MakeSpan(p) << k).GetSHA256();
3078-
return q.CheckPayToContract(p, k, control[0] & 1);
3076+
// Verify that the output pubkey matches the tweaked internal pubkey, after correcting for parity.
3077+
return q.CheckTapTweak(p, k, control[0] & 1);
30793078
}
30803079

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