Skip to content

Commit

Permalink
Make consensus checking of tweaks in pubkey.* Taproot-specific
Browse files Browse the repository at this point in the history
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
  • Loading branch information
apoelstra authored and sanket1729 committed Aug 23, 2021
1 parent c7c763c commit 86f35b2
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 4 deletions.
25 changes: 24 additions & 1 deletion src/pubkey.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,11 +181,34 @@ bool XOnlyPubKey::VerifySchnorr(const Span<const unsigned char> msg, Span<const
return secp256k1_schnorrsig_verify(secp256k1_context_verify, sigbytes.data(), msg.begin(), msg.size(), &pubkey);
}

// ELEMENTS: this is preserved from an old version of the Taproot code for use in OP_TWEAKVERIFY
bool XOnlyPubKey::CheckPayToContract(const XOnlyPubKey& base, const uint256& hash, 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());
return secp256k1_xonly_pubkey_tweak_add_check(secp256k1_context_verify, m_keydata.begin(), parity, &base_point
, hash.begin());
}

static const CHashWriter HASHER_TAPTWEAK_ELEMENTS = TaggedHash("TapTweak/elements");

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_ELEMENTS) << m_keydata).GetSHA256();
} else {
return (CHashWriter(HASHER_TAPTWEAK_ELEMENTS) << m_keydata << *merkle_root).GetSHA256();
}
}

bool XOnlyPubKey::CheckTapTweak(const XOnlyPubKey& internal, const uint256& merkle_root, bool parity) const
{
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::TweakMulVerify(const CPubKey& untweaked, const uint256& tweak) const
Expand Down
15 changes: 15 additions & 0 deletions src/pubkey.h
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,23 @@ class XOnlyPubKey
* sigbytes must be exactly 64 bytes.
*/
bool VerifySchnorr(const Span<const unsigned char> msg, Span<const unsigned char> 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(); }
Expand Down
5 changes: 2 additions & 3 deletions src/script/interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror)
Expand Down Expand Up @@ -3086,8 +3085,8 @@ static bool VerifyTaprootCommitment(const std::vector<unsigned char>& 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<unsigned char>& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror, bool is_p2sh)
Expand Down

0 comments on commit 86f35b2

Please sign in to comment.