Remove parity argument and return on add_tweak and tweak_add_check
What changed, and why it matters
This commit is a routine API cleanup in the rust-bitcoin library. It removes an extra 'parity' argument and return value from functions that tweak public keys for Taproot, because the parity information can now be stored directly inside the XOnlyPublicKey type. There is no indication this fixes a security bug; it is a design simplification that may require downstream code to update how it calls these functions.
Treat as a normal API-breaking refactor. Downstream projects using add_tweak or tweak_add_check will need to update call sites to the new signatures. No urgent security action is indicated by the commit itself.
Security signals we found
No security-relevant keywords in commit title or message
No bounds, memory, or input-validation changes
API signature change only; cryptographic operations delegated unchanged to secp256k1
No incident, CVE, or advisory references present in commit or supplied materials
Evidence from the diff
The patch refactors XOnlyPublicKey::add_tweak and XOnlyPublicKey::tweak_add_check so that add_tweak returns only an XOnlyPublicKey (with parity embedded via with_parity) and tweak_add_check reads parity from the tweaked key rather than requiring a separate Parity argument. The TapTweak trait’s associated type TweakedAux changes from (TweakedPublicKey, Parity) to TweakedPublicKey. Call sites in script construction, witness program creation, and TaprootSpendInfo are updated accordingly. The change is source-compatible-breaking for consumers of these APIs but does not alter cryptographic behavior.
Changed components
bitcoin/src/crypto/key.rsbitcoin/src/blockdata/script/owned.rsbitcoin/src/blockdata/script/witness_program.rsbitcoin/src/taproot/mod.rsInspect captured patch +21 / −21
diff --git a/bitcoin/src/blockdata/script/owned.rs b/bitcoin/src/blockdata/script/owned.rs
index b910dced..2203701c 100644
--- a/bitcoin/src/blockdata/script/owned.rs
+++ b/bitcoin/src/blockdata/script/owned.rs
@@ -229,7 +229,7 @@ crate::internal_macros::define_extension_trait! {
merkle_root: Option<TapNodeHash>,
) -> Self {
let internal_key = internal_key.into();
- let (output_key, _) = internal_key.tap_tweak(merkle_root);
+ let output_key = internal_key.tap_tweak(merkle_root);
// output key is 32 bytes long, so it's safe to use `new_witness_program_unchecked` (Segwitv1)
script::new_witness_program_unchecked(WitnessVersion::V1, output_key.serialize())
}
diff --git a/bitcoin/src/blockdata/script/witness_program.rs b/bitcoin/src/blockdata/script/witness_program.rs
index c970edec..39be3e7d 100644
--- a/bitcoin/src/blockdata/script/witness_program.rs
+++ b/bitcoin/src/blockdata/script/witness_program.rs
@@ -95,7 +95,7 @@ impl WitnessProgram {
merkle_root: Option<TapNodeHash>,
) -> Self {
let internal_key = internal_key.into();
- let (output_key, _parity) = internal_key.tap_tweak(merkle_root);
+ let output_key = internal_key.tap_tweak(merkle_root);
let (pubkey, _) = output_key.as_x_only_public_key().serialize();
Self::new_p2tr(pubkey)
}
diff --git a/bitcoin/src/crypto/key.rs b/bitcoin/src/crypto/key.rs
index 773f07bd..696f707d 100644
--- a/bitcoin/src/crypto/key.rs
+++ b/bitcoin/src/crypto/key.rs
@@ -246,25 +246,24 @@ impl XOnlyPublicKey {
/// Verifies that a tweak produced by [`XOnlyPublicKey::add_tweak`] was computed correctly.
///
- /// Should be called on the original untweaked key. Takes the tweaked key and output parity from
+ /// Should be called on the original untweaked key. Takes the tweaked key with its output parity from
/// [`XOnlyPublicKey::add_tweak`] as input.
#[inline]
pub fn tweak_add_check(
&self,
tweaked_key: &Self,
- tweaked_parity: Parity,
tweak: secp256k1::Scalar,
) -> bool {
- self.as_inner().tweak_add_check(tweaked_key.as_inner(), tweaked_parity, tweak)
+ self.as_inner().tweak_add_check(tweaked_key.as_inner(), tweaked_key.parity(), tweak)
}
/// Tweaks an [`XOnlyPublicKey`] by adding the generator multiplied with the given tweak to it.
///
/// # Returns
///
- /// The newly tweaked key plus an opaque type representing the parity of the tweaked key, this
- /// should be provided to `tweak_add_check` which can be used to verify a tweak more efficiently
- /// than regenerating it and checking equality.
+ /// The newly tweaked key. This key has its parity set according to the parity following the
+ /// tweak. This key should be provided to `tweak_add_check` which can be used to verify a tweak
+ /// more efficiently than regenerating it and checking equality.
///
/// # Errors
///
@@ -273,9 +272,9 @@ impl XOnlyPublicKey {
pub fn add_tweak(
&self,
tweak: &secp256k1::Scalar,
- ) -> Result<(Self, Parity), TweakXOnlyPublicKeyError> {
+ ) -> Result<Self, TweakXOnlyPublicKeyError> {
match self.as_inner().add_tweak(tweak) {
- Ok((xonly, parity)) => Ok((Self::from_secp(xonly), parity)),
+ Ok((xonly, parity)) => Ok(Self::from_secp(xonly).with_parity(parity)),
Err(secp256k1::Error::InvalidTweak) => Err(TweakXOnlyPublicKeyError::BadTweak),
Err(secp256k1::Error::InvalidParityValue(_)) =>
Err(TweakXOnlyPublicKeyError::ParityError),
@@ -1133,7 +1132,7 @@ pub trait TapTweak {
///
/// # Returns
///
- /// The tweaked key and its parity.
+ /// The tweaked key, with the required parity.
fn tap_tweak(self, merkle_root: Option<TapNodeHash>) -> Self::TweakedAux;
/// Directly converts an [`UntweakedPublicKey`] to a [`TweakedPublicKey`].
@@ -1144,7 +1143,7 @@ pub trait TapTweak {
}
impl TapTweak for UntweakedPublicKey {
- type TweakedAux = (TweakedPublicKey, Parity);
+ type TweakedAux = TweakedPublicKey;
type TweakedKey = TweakedPublicKey;
/// Tweaks an untweaked public key with corresponding public key value and optional script tree
@@ -1160,12 +1159,12 @@ impl TapTweak for UntweakedPublicKey {
/// # Returns
///
/// The tweaked key and its parity.
- fn tap_tweak(self, merkle_root: Option<TapNodeHash>) -> (TweakedPublicKey, Parity) {
+ fn tap_tweak(self, merkle_root: Option<TapNodeHash>) -> TweakedPublicKey {
let tweak = TapTweakHash::from_key_and_merkle_root(self, merkle_root).to_scalar();
- let (output_key, parity) = self.add_tweak(&tweak).expect("Tap tweak failed");
+ let output_key = self.add_tweak(&tweak).expect("Tap tweak failed");
- debug_assert!(self.tweak_add_check(&output_key, parity, tweak));
- (TweakedPublicKey::dangerous_assume_tweaked(output_key), parity)
+ debug_assert!(self.tweak_add_check(&output_key, tweak));
+ TweakedPublicKey::dangerous_assume_tweaked(output_key)
}
fn dangerous_assume_tweaked(self) -> TweakedPublicKey {
diff --git a/bitcoin/src/taproot/mod.rs b/bitcoin/src/taproot/mod.rs
index 923c8f6b..41650c71 100644
--- a/bitcoin/src/taproot/mod.rs
+++ b/bitcoin/src/taproot/mod.rs
@@ -275,11 +275,11 @@ impl TaprootSpendInfo {
merkle_root: Option<TapNodeHash>,
) -> Self {
let internal_key = internal_key.into();
- let (output_key, parity) = internal_key.tap_tweak(merkle_root);
+ let output_key = internal_key.tap_tweak(merkle_root);
Self {
internal_key,
merkle_root,
- output_key_parity: parity,
+ output_key_parity: output_key.as_x_only_public_key().parity(),
output_key,
script_map: BTreeMap::new(),
}
@@ -1282,7 +1282,7 @@ impl<Branch: AsRef<TaprootMerkleBranch> + ?Sized> ControlBlock<Branch> {
// compute the taptweak
let tweak =
TapTweakHash::from_key_and_merkle_root(self.internal_key, Some(curr_hash)).to_scalar();
- self.internal_key.tweak_add_check(&output_key, self.output_key_parity, tweak)
+ self.internal_key.tweak_add_check(&output_key.with_parity(self.output_key_parity), tweak)
}
}
@@ -2047,11 +2047,12 @@ mod test {
.assume_checked();
let tweak = TapTweakHash::from_key_and_merkle_root(internal_key, merkle_root);
- let (output_key, _parity) = internal_key.tap_tweak(merkle_root);
+ let output_key = internal_key.tap_tweak(merkle_root);
let addr = Address::p2tr(internal_key, merkle_root, KnownHrp::Mainnet);
let spk = addr.script_pubkey();
- assert_eq!(expected_output_key, output_key.to_x_only_public_key());
+ // Compare just the key bytes, not the parity
+ assert_eq!(expected_output_key.serialize().0, output_key.to_x_only_public_key().serialize().0);
assert_eq!(expected_tweak, tweak);
assert_eq!(expected_addr, addr);
assert_eq!(expected_spk, spk);
Why this scored 17/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.