What changed, and why it matters
This commit is a routine API cleanup: it makes a few Bitcoin merkle-root functions accept a broader range of input types (generic iterators and borrowed transactions) instead of only slices or exact iterator types. There is no security bug being fixed and no behavior change for callers that already used the old API. The tests were updated only to remove unnecessary `.into_iter()` calls.
No security action required; treat as a normal API ergonomics/refactoring change.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch generalizes function signatures in rust-bitcoin primitives: Block::compute_merkle_root and compute_witness_root now take any T: IntoIterator<Item: Borrow<Transaction>> rather than &[Transaction]; TxMerkleNode::calculate_root and WitnessMerkleNode::calculate_root now accept I: IntoIterator<Item = Txid/Wtxid> rather than I: Iterator<...>. Internally the code calls .into_iter() or .borrow() as needed. Test call sites drop explicit .into_iter(). No logic inside the merkle computation changed.
Changed components
primitives/src/block.rsprimitives/src/hash_types/transaction_merkle_node.rsprimitives/src/hash_types/witness_merkle_node.rsprimitives/src/merkle_tree.rs (tests only)Inspect captured patch +26 / −18
diff --git a/primitives/src/block.rs b/primitives/src/block.rs
index d3c49484..273e90e4 100644
--- a/primitives/src/block.rs
+++ b/primitives/src/block.rs
@@ -7,6 +7,8 @@
//! module describes structures and functions needed to describe
//! these blocks and the blockchain.
+#[cfg(feature = "alloc")]
+use core::borrow::Borrow;
use core::fmt;
#[cfg(feature = "alloc")]
use core::marker::PhantomData;
@@ -385,8 +387,12 @@ crate::decoder_newtype! {
/// Unless you are certain your transaction list is nonempty and has no duplicates,
/// you should not unwrap the `Option` returned by this method!
#[cfg(feature = "alloc")]
-pub fn compute_merkle_root(transactions: &[Transaction]) -> Option<TxMerkleNode> {
- let hashes = transactions.iter().map(Transaction::compute_txid);
+pub fn compute_merkle_root<T>(transactions: T) -> Option<TxMerkleNode>
+where
+ T: IntoIterator,
+ T::Item: Borrow<Transaction>,
+{
+ let hashes = transactions.into_iter().map(|t| t.borrow().compute_txid());
TxMerkleNode::calculate_root(hashes)
}
@@ -400,13 +406,17 @@ pub fn compute_merkle_root(transactions: &[Transaction]) -> Option<TxMerkleNode>
/// Unless you are certain your transaction list is nonempty and has no duplicates,
/// you should not unwrap the `Option` returned by this method!
#[cfg(feature = "alloc")]
-pub fn compute_witness_root(transactions: &[Transaction]) -> Option<WitnessMerkleNode> {
- let hashes = transactions.iter().enumerate().map(|(i, t)| {
+pub fn compute_witness_root<T>(transactions: T) -> Option<WitnessMerkleNode>
+where
+ T: IntoIterator,
+ T::Item: Borrow<Transaction>,
+{
+ let hashes = transactions.into_iter().enumerate().map(|(i, t)| {
if i == 0 {
// Replace the first hash with zeroes.
Wtxid::COINBASE
} else {
- t.compute_wtxid()
+ t.borrow().compute_wtxid()
}
});
WitnessMerkleNode::calculate_root(hashes)
diff --git a/primitives/src/hash_types/transaction_merkle_node.rs b/primitives/src/hash_types/transaction_merkle_node.rs
index c3e35069..ba3ddbbc 100644
--- a/primitives/src/hash_types/transaction_merkle_node.rs
+++ b/primitives/src/hash_types/transaction_merkle_node.rs
@@ -45,8 +45,8 @@ impl TxMerkleNode {
///
/// Unless you are certain your transaction list is nonempty and has no duplicates,
/// you should not unwrap the `Option` returned by this method!
- pub fn calculate_root<I: Iterator<Item = Txid>>(iter: I) -> Option<Self> {
- MerkleNode::calculate_root(iter)
+ pub fn calculate_root<I: IntoIterator<Item = Txid>>(iter: I) -> Option<Self> {
+ MerkleNode::calculate_root(iter.into_iter())
}
}
diff --git a/primitives/src/hash_types/witness_merkle_node.rs b/primitives/src/hash_types/witness_merkle_node.rs
index 9dcb46bc..2d92543c 100644
--- a/primitives/src/hash_types/witness_merkle_node.rs
+++ b/primitives/src/hash_types/witness_merkle_node.rs
@@ -45,8 +45,8 @@ impl WitnessMerkleNode {
///
/// Unless you are certain your transaction list is nonempty and has no duplicates,
/// you should not unwrap the `Option` returned by this method!
- pub fn calculate_root<I: Iterator<Item = Wtxid>>(iter: I) -> Option<Self> {
- MerkleNode::calculate_root(iter)
+ pub fn calculate_root<I: IntoIterator<Item = Wtxid>>(iter: I) -> Option<Self> {
+ MerkleNode::calculate_root(iter.into_iter())
}
}
diff --git a/primitives/src/merkle_tree.rs b/primitives/src/merkle_tree.rs
index eca3de5a..206c403c 100644
--- a/primitives/src/merkle_tree.rs
+++ b/primitives/src/merkle_tree.rs
@@ -218,7 +218,7 @@ mod tests {
#[test]
fn tx_merkle_node_single_leaf() {
let (leaf, node) = make_leaf_node(1);
- let root = TxMerkleNode::calculate_root([leaf].into_iter());
+ let root = TxMerkleNode::calculate_root([leaf]);
assert!(root.is_some(), "Root should exist for a single leaf");
assert_eq!(root.unwrap(), node, "Root should equal the leaf node");
}
@@ -229,7 +229,7 @@ mod tests {
let (leaf2, node2) = make_leaf_node(2);
let combined = node1.combine(&node2);
- let root = TxMerkleNode::calculate_root([leaf1, leaf2].into_iter());
+ let root = TxMerkleNode::calculate_root([leaf1, leaf2]);
assert_eq!(
root.unwrap(),
combined,
@@ -241,7 +241,7 @@ mod tests {
fn tx_merkle_node_duplicate_leaves() {
let leaf = Txid::from_byte_array([3; 32]);
// Duplicate transaction list should be rejected (CVE 2012‑2459).
- let root = TxMerkleNode::calculate_root([leaf, leaf].into_iter());
+ let root = TxMerkleNode::calculate_root([leaf, leaf]);
assert!(root.is_none(), "Duplicate leaves should return None");
}
@@ -275,9 +275,7 @@ mod tests {
let subtree_cd = subtree_c.combine(&subtree_d);
let expected = subtree_ab.combine(&subtree_cd);
- let root = TxMerkleNode::calculate_root(
- [leaf1, leaf2, leaf3, leaf4, leaf5, leaf6, leaf7].into_iter(),
- );
+ let root = TxMerkleNode::calculate_root([leaf1, leaf2, leaf3, leaf4, leaf5, leaf6, leaf7]);
assert_eq!(root, Some(expected));
}
@@ -299,7 +297,7 @@ mod tests {
// Take the final node, which should be the root of the full tree.
let expected = level.pop().unwrap();
- let root = TxMerkleNode::calculate_root(leaves.into_iter());
+ let root = TxMerkleNode::calculate_root(leaves);
assert_eq!(root, Some(expected));
}
@@ -386,7 +384,7 @@ mod tests {
#[test]
fn witness_merkle_node_single_leaf() {
let leaf = Wtxid::from_byte_array([1; 32]);
- let root = WitnessMerkleNode::calculate_root([leaf].into_iter());
+ let root = WitnessMerkleNode::calculate_root([leaf]);
assert!(root.is_some(), "Root should exist for a single witness leaf");
let node = WitnessMerkleNode::from_leaf(leaf);
assert_eq!(root.unwrap(), node, "Root should equal the leaf node");
@@ -395,7 +393,7 @@ mod tests {
#[test]
fn witness_merkle_node_duplicate_leaves() {
let leaf = Wtxid::from_byte_array([2; 32]);
- let root = WitnessMerkleNode::calculate_root([leaf, leaf].into_iter());
+ let root = WitnessMerkleNode::calculate_root([leaf, leaf]);
assert!(root.is_none(), "Duplicate witness leaves should return None");
}
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.