Split TapNodeHash and TapLeafHash logic into extension traits
What changed, and why it matters
This commit is a routine internal code reorganization. It moves some Taproot hash helper methods from direct implementations on two types into Rust 'extension traits' so the underlying types can live in a more basic crate while the extra logic stays in the main bitcoin crate. There is no change to what the code computes or any security behavior.
No security action needed. Treat as normal refactoring.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch refactors TapLeafHash::from_script and TapNodeHash methods (from_node_hashes, assume_hidden, from_script) into new extension traits TapLeafHashExt and TapNodeHashExt, defined via an internal macro and sealed so only the crate can implement them. The actual hashing algorithms, consensus encoding, and public APIs remain functionally identical; callers now import and use the extension trait. A sealed submodule is added to prevent external implementations. No logic changes or bug fixes are present.
Changed components
bitcoin/src/taproot/mod.rsbitcoin/src/blockdata/script/borrowed.rsInspect captured patch +38 / −24
diff --git a/bitcoin/src/blockdata/script/borrowed.rs b/bitcoin/src/blockdata/script/borrowed.rs
index 7205ca56..6f6c6d4e 100644
--- a/bitcoin/src/blockdata/script/borrowed.rs
+++ b/bitcoin/src/blockdata/script/borrowed.rs
@@ -18,7 +18,7 @@ use crate::opcodes::{self, Opcode};
use crate::policy::{DUST_RELAY_TX_FEE, MAX_OP_RETURN_RELAY};
use crate::prelude::{sink, String, ToString};
use crate::script::{self, ScriptPubKeyBufExt as _};
-use crate::taproot::{LeafVersion, TapLeafHash, TapNodeHash};
+use crate::taproot::{LeafVersion, TapLeafHash, TapLeafHashExt as _, TapNodeHash};
use crate::witness_program::P2A_PROGRAM;
use crate::{internal_macros, Amount, FeeRate, ScriptPubKeyBuf, WitnessScriptBuf};
diff --git a/bitcoin/src/taproot/mod.rs b/bitcoin/src/taproot/mod.rs
index c94941ab..dd300d69 100644
--- a/bitcoin/src/taproot/mod.rs
+++ b/bitcoin/src/taproot/mod.rs
@@ -127,17 +127,6 @@ impl TapTweakHash {
}
}
-impl TapLeafHash {
- /// Computes the leaf hash from components.
- pub fn from_script(script: &TapScript, ver: LeafVersion) -> Self {
- let mut eng = sha256t::Hash::<TapLeafTag>::engine();
- ver.to_consensus().consensus_encode(&mut eng).expect("engines don't error");
- script.consensus_encode(&mut eng).expect("engines don't error");
- let inner = sha256t::Hash::<TapLeafTag>::from_engine(eng);
- Self::from_byte_array(inner.to_byte_array())
- }
-}
-
impl From<LeafNode> for TapNodeHash {
fn from(leaf: LeafNode) -> Self { leaf.node_hash() }
}
@@ -146,23 +135,48 @@ impl From<&LeafNode> for TapNodeHash {
fn from(leaf: &LeafNode) -> Self { leaf.node_hash() }
}
-impl TapNodeHash {
- /// Computes branch hash given two hashes of the nodes underneath it.
- pub fn from_node_hashes(a: Self, b: Self) -> Self { combine_node_hashes(a, b).0 }
+crate::internal_macros::define_extension_trait! {
+ /// Extension functionality for the [`TapLeafHash`] type.
+ pub trait TapLeafHashExt impl for TapLeafHash {
+ /// Computes the leaf hash from components.
+ fn from_script(script: &TapScript, ver: LeafVersion) -> Self {
+ let mut eng = sha256t::Hash::<TapLeafTag>::engine();
+ ver.to_consensus().consensus_encode(&mut eng).expect("engines don't error");
+ script.consensus_encode(&mut eng).expect("engines don't error");
+ let inner = sha256t::Hash::<TapLeafTag>::from_engine(eng);
+ Self::from_byte_array(inner.to_byte_array())
+ }
+ }
+}
- /// Assumes the given 32 byte array as hidden [`TapNodeHash`].
- ///
- /// Similar to [`TapLeafHash::from_byte_array`], but explicitly conveys that the
- /// hash is constructed from a hidden node. This also has better ergonomics
- /// because it does not require the caller to import the Hash trait.
- pub fn assume_hidden(hash: [u8; 32]) -> Self { Self::from_byte_array(hash) }
+crate::internal_macros::define_extension_trait! {
+ /// Extension functionality for the [`TapNodeHash`] type.
+ pub trait TapNodeHashExt impl for TapNodeHash {
+ /// Computes branch hash given two hashes of the nodes underneath it.
+ fn from_node_hashes(a: Self, b: Self) -> Self {
+ combine_node_hashes(a, b).0
+ }
+
+ /// Assumes the given 32 byte array as hidden [`TapNodeHash`].
+ ///
+ /// Similar to [`TapLeafHash::from_byte_array`], but explicitly conveys that the
+ /// hash is constructed from a hidden node. This also has better ergonomics
+ /// because it does not require the caller to import the Hash trait.
+ fn assume_hidden(hash: [u8; 32]) -> Self { Self::from_byte_array(hash) }
- /// Computes the [`TapNodeHash`] from a script and a leaf version.
- pub fn from_script(script: &TapScript, ver: LeafVersion) -> Self {
- Self::from(TapLeafHash::from_script(script, ver))
+ /// Computes the [`TapNodeHash`] from a script and a leaf version.
+ fn from_script(script: &TapScript, ver: LeafVersion) -> Self {
+ Self::from(TapLeafHash::from_script(script, ver))
+ }
}
}
+mod sealed {
+ pub trait Sealed {}
+ impl Sealed for super::TapLeafHash {}
+ impl Sealed for super::TapNodeHash {}
+}
+
/// Computes branch hash given two hashes of the nodes underneath it and returns
/// whether the left node was the one hashed first.
fn combine_node_hashes(a: TapNodeHash, b: TapNodeHash) -> (TapNodeHash, bool) {
Why this scored 15/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.