Move from_key_and_merkle_root to extension trait
What changed, and why it matters
This commit is a routine code reorganization. It moves one function, `from_key_and_merkle_root`, out of a low-level crate (`taproot-primitives`) and into a higher-level crate (`bitcoin`) as an 'extension trait.' The goal is to remove an internal dependency so the low-level crate can stabilize sooner. The actual computation performed by the function is unchanged.
No security action required. Treat as normal refactoring. Reviewers may optionally verify that downstream users of `taproot-primitives` still have access to equivalent functionality through the `bitcoin` crate's extension trait.
Security signals we found
No security-relevant behavioral change: the hashing algorithm, inputs, and output type remain identical.
No new dependencies introduced; instead, one dependency edge is removed from `taproot-primitives`.
No memory-safety, cryptographic, or input-validation changes are present in the diff.
No vendor disclosure, CVE, or researcher attribution is present in the commit or supplied references.
Evidence from the diff
The patch relocates TapTweakHash::from_key_and_merkle_root from taproot-primitives/src/lib.rs to a new TapTweakHashExt extension trait in bitcoin/src/taproot/mod.rs. The implementation body is copied verbatim, including the SHA-256 engine construction, key serialization, optional Merkle-root hashing, and scalar conversion. The change removes taproot-primitives’ dependency on crypto::key::UntweakedPublicKey and hashes::HashEngine under the alloc feature. A Sealed trait impl is added for TapTweakHash. Call sites in bitcoin/src/crypto/key.rs now import TapTweakHashExt as _.
Changed components
bitcoin/src/taproot/mod.rsbitcoin/src/crypto/key.rstaproot-primitives/src/lib.rsInspect captured patch +26 / −25
diff --git a/bitcoin/src/crypto/key.rs b/bitcoin/src/crypto/key.rs
index 3bcd92ba..c1549bf7 100644
--- a/bitcoin/src/crypto/key.rs
+++ b/bitcoin/src/crypto/key.rs
@@ -12,7 +12,7 @@ use crate::internal_macros::define_extension_trait;
use crate::script::{self, PushBytes, WitnessScriptBuf};
#[cfg(feature = "secp-recovery")]
use crate::sign_message::MessageSignature;
-use crate::taproot::{TapNodeHash, TapTweakHash};
+use crate::taproot::{TapNodeHash, TapTweakHash, TapTweakHashExt as _};
#[rustfmt::skip] // Keep public re-exports separate.
pub use secp256k1::{constants, Parity, Verification};
diff --git a/bitcoin/src/taproot/mod.rs b/bitcoin/src/taproot/mod.rs
index e5037fed..12f25de8 100644
--- a/bitcoin/src/taproot/mod.rs
+++ b/bitcoin/src/taproot/mod.rs
@@ -99,10 +99,35 @@ crate::internal_macros::define_extension_trait! {
}
}
+crate::internal_macros::define_extension_trait! {
+ /// Extension functionality for the [`TapTweakHash`] type.
+ pub trait TapTweakHashExt impl for TapTweakHash {
+ /// Constructs a new BIP-0341 [`TapTweakHash`] from key and Merkle root. Produces `H_taptweak(P||R)` where
+ /// `P` is the internal key and `R` is the Merkle root.
+ fn from_key_and_merkle_root<K: Into<UntweakedPublicKey>>(
+ internal_key: K,
+ merkle_root: Option<TapNodeHash>,
+ ) -> Self {
+ let internal_key = internal_key.into();
+ let mut eng = sha256t::Hash::<TapTweakTag>::engine();
+ // always hash the key
+ eng.input(&internal_key.serialize().0);
+ if let Some(h) = merkle_root {
+ eng.input(h.as_ref());
+ } else {
+ // nothing to hash
+ }
+ let inner = sha256t::Hash::<TapTweakTag>::from_engine(eng);
+ Self::from_byte_array(inner.to_byte_array())
+ }
+ }
+}
+
mod sealed {
pub trait Sealed {}
impl Sealed for super::TapLeafHash {}
impl Sealed for super::TapNodeHash {}
+ impl Sealed for super::TapTweakHash {}
}
/// Computes branch hash given two hashes of the nodes underneath it and returns
diff --git a/taproot-primitives/src/lib.rs b/taproot-primitives/src/lib.rs
index 1dc14e17..e07992d6 100644
--- a/taproot-primitives/src/lib.rs
+++ b/taproot-primitives/src/lib.rs
@@ -36,10 +36,6 @@ use core::fmt;
#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};
-#[cfg(feature = "alloc")]
-use crypto::key::UntweakedPublicKey;
-#[cfg(feature = "alloc")]
-use hashes::HashEngine;
use hashes::{hash_newtype, sha256t, sha256t_tag};
use secp256k1::Scalar;
@@ -122,26 +118,6 @@ impl From<TapLeafHash> for TapNodeHash {
}
impl TapTweakHash {
- /// Constructs a new BIP-0341 [`TapTweakHash`] from key and Merkle root. Produces `H_taptweak(P||R)` where
- /// `P` is the internal key and `R` is the Merkle root.
- #[cfg(feature = "alloc")]
- pub fn from_key_and_merkle_root<K: Into<UntweakedPublicKey>>(
- internal_key: K,
- merkle_root: Option<TapNodeHash>,
- ) -> Self {
- let internal_key = internal_key.into();
- let mut eng = sha256t::Hash::<TapTweakTag>::engine();
- // always hash the key
- eng.input(&internal_key.serialize().0);
- if let Some(h) = merkle_root {
- eng.input(h.as_ref());
- } else {
- // nothing to hash
- }
- let inner = sha256t::Hash::<TapTweakTag>::from_engine(eng);
- Self::from_byte_array(inner.to_byte_array())
- }
-
/// Converts a `TapTweakHash` into a `Scalar` ready for use with key tweaking API.
#[allow(clippy::missing_panics_doc)]
pub fn to_scalar(self) -> Scalar {
Why this scored 18/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.