Skip to content

Commit a874123

Browse files
committed
primitives: Move MerkleNode and TxIdentifier into primitives
The MerkleNode trait in bitcoin provides convenience functions for the TxMerkleNode and WitnessMerkleNode hash types to work with the nodes as a tree. Moving the functionality of this trait into primitives acts as a precursor to allow other functionality to move also. Move the MerkleNode trait to primitives, making it private. Provide the same interface for users through pub functions that wrap the private trait. Introduce tests to cover mutants. Introduce TxIdentifier in primitives.
1 parent 372b2a1 commit a874123

File tree

10 files changed

+325
-110
lines changed

10 files changed

+325
-110
lines changed

.cargo/mutants.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ exclude_re = [
5050
"primitives/.* <impl Encoder for .*Encoder<'_>>::advance", # Replacing the return with true causes an infinite loop.
5151
"primitives/.* <impl Decoder for WitnessDecoder>::push_bytes", # Replacing == with != causes an infinite loop
5252
"primitives/.* WitnessDecoder::resize_if_needed", # Replacing *= with += still resizes the buffer making the mutant untestable.
53+
"primitives/.* replace \\+ with \\* in MerkleNode::calculate_root", # Replacing + with * causes an infinite loop
5354

5455
# consensus_encoding - most of these are for mutations in the logic used to determine when to stop encoding or decoding.
5556
"consensus_encoding/.* <impl Decoder for ArrayDecoder<N>>::push_bytes", # Mutations cause an infinite loop

api/primitives/all-features.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1318,13 +1318,16 @@ pub fn bitcoin_primitives::TxMerkleNode::as_ref(&self) -> &[u8; 32]
13181318
pub fn bitcoin_primitives::TxMerkleNode::as_ref(&self) -> &[u8]
13191319
pub fn bitcoin_primitives::TxMerkleNode::borrow(&self) -> &[u8; 32]
13201320
pub fn bitcoin_primitives::TxMerkleNode::borrow(&self) -> &[u8]
1321+
pub fn bitcoin_primitives::TxMerkleNode::calculate_root<I: core::iter::traits::iterator::Iterator<Item = bitcoin_primitives::Txid>>(iter: I) -> core::option::Option<Self>
13211322
pub fn bitcoin_primitives::TxMerkleNode::clone(&self) -> bitcoin_primitives::TxMerkleNode
13221323
pub fn bitcoin_primitives::TxMerkleNode::cmp(&self, other: &bitcoin_primitives::TxMerkleNode) -> core::cmp::Ordering
1324+
pub fn bitcoin_primitives::TxMerkleNode::combine(&self, other: &Self) -> Self
13231325
pub fn bitcoin_primitives::TxMerkleNode::decoder() -> Self::Decoder
13241326
pub fn bitcoin_primitives::TxMerkleNode::deserialize<D: serde::de::Deserializer<'de>>(d: D) -> core::result::Result<bitcoin_primitives::TxMerkleNode, <D as serde::de::Deserializer>::Error>
13251327
pub fn bitcoin_primitives::TxMerkleNode::encoder(&self) -> Self::Encoder
13261328
pub fn bitcoin_primitives::TxMerkleNode::eq(&self, other: &bitcoin_primitives::TxMerkleNode) -> bool
13271329
pub fn bitcoin_primitives::TxMerkleNode::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
1330+
pub fn bitcoin_primitives::TxMerkleNode::from_leaf(leaf: bitcoin_primitives::Txid) -> Self
13281331
pub fn bitcoin_primitives::TxMerkleNode::from_str(s: &str) -> core::result::Result<Self, Self::Err>
13291332
pub fn bitcoin_primitives::TxMerkleNode::hash<__H: core::hash::Hasher>(&self, state: &mut __H)
13301333
pub fn bitcoin_primitives::TxMerkleNode::partial_cmp(&self, other: &bitcoin_primitives::TxMerkleNode) -> core::option::Option<core::cmp::Ordering>
@@ -1364,11 +1367,14 @@ pub fn bitcoin_primitives::WitnessMerkleNode::as_ref(&self) -> &[u8; 32]
13641367
pub fn bitcoin_primitives::WitnessMerkleNode::as_ref(&self) -> &[u8]
13651368
pub fn bitcoin_primitives::WitnessMerkleNode::borrow(&self) -> &[u8; 32]
13661369
pub fn bitcoin_primitives::WitnessMerkleNode::borrow(&self) -> &[u8]
1370+
pub fn bitcoin_primitives::WitnessMerkleNode::calculate_root<I: core::iter::traits::iterator::Iterator<Item = bitcoin_primitives::Wtxid>>(iter: I) -> core::option::Option<Self>
13671371
pub fn bitcoin_primitives::WitnessMerkleNode::clone(&self) -> bitcoin_primitives::WitnessMerkleNode
13681372
pub fn bitcoin_primitives::WitnessMerkleNode::cmp(&self, other: &bitcoin_primitives::WitnessMerkleNode) -> core::cmp::Ordering
1373+
pub fn bitcoin_primitives::WitnessMerkleNode::combine(&self, other: &Self) -> Self
13691374
pub fn bitcoin_primitives::WitnessMerkleNode::deserialize<D: serde::de::Deserializer<'de>>(d: D) -> core::result::Result<bitcoin_primitives::WitnessMerkleNode, <D as serde::de::Deserializer>::Error>
13701375
pub fn bitcoin_primitives::WitnessMerkleNode::eq(&self, other: &bitcoin_primitives::WitnessMerkleNode) -> bool
13711376
pub fn bitcoin_primitives::WitnessMerkleNode::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
1377+
pub fn bitcoin_primitives::WitnessMerkleNode::from_leaf(leaf: bitcoin_primitives::Wtxid) -> Self
13721378
pub fn bitcoin_primitives::WitnessMerkleNode::from_str(s: &str) -> core::result::Result<Self, Self::Err>
13731379
pub fn bitcoin_primitives::WitnessMerkleNode::hash<__H: core::hash::Hasher>(&self, state: &mut __H)
13741380
pub fn bitcoin_primitives::WitnessMerkleNode::partial_cmp(&self, other: &bitcoin_primitives::WitnessMerkleNode) -> core::option::Option<core::cmp::Ordering>

api/primitives/alloc-only.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1175,12 +1175,15 @@ pub fn bitcoin_primitives::TxMerkleNode::as_ref(&self) -> &[u8; 32]
11751175
pub fn bitcoin_primitives::TxMerkleNode::as_ref(&self) -> &[u8]
11761176
pub fn bitcoin_primitives::TxMerkleNode::borrow(&self) -> &[u8; 32]
11771177
pub fn bitcoin_primitives::TxMerkleNode::borrow(&self) -> &[u8]
1178+
pub fn bitcoin_primitives::TxMerkleNode::calculate_root<I: core::iter::traits::iterator::Iterator<Item = bitcoin_primitives::Txid>>(iter: I) -> core::option::Option<Self>
11781179
pub fn bitcoin_primitives::TxMerkleNode::clone(&self) -> bitcoin_primitives::TxMerkleNode
11791180
pub fn bitcoin_primitives::TxMerkleNode::cmp(&self, other: &bitcoin_primitives::TxMerkleNode) -> core::cmp::Ordering
1181+
pub fn bitcoin_primitives::TxMerkleNode::combine(&self, other: &Self) -> Self
11801182
pub fn bitcoin_primitives::TxMerkleNode::decoder() -> Self::Decoder
11811183
pub fn bitcoin_primitives::TxMerkleNode::encoder(&self) -> Self::Encoder
11821184
pub fn bitcoin_primitives::TxMerkleNode::eq(&self, other: &bitcoin_primitives::TxMerkleNode) -> bool
11831185
pub fn bitcoin_primitives::TxMerkleNode::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
1186+
pub fn bitcoin_primitives::TxMerkleNode::from_leaf(leaf: bitcoin_primitives::Txid) -> Self
11841187
pub fn bitcoin_primitives::TxMerkleNode::hash<__H: core::hash::Hasher>(&self, state: &mut __H)
11851188
pub fn bitcoin_primitives::TxMerkleNode::partial_cmp(&self, other: &bitcoin_primitives::TxMerkleNode) -> core::option::Option<core::cmp::Ordering>
11861189
pub fn bitcoin_primitives::Txid::as_ref(&self) -> &[u8; 32]
@@ -1209,10 +1212,13 @@ pub fn bitcoin_primitives::WitnessMerkleNode::as_ref(&self) -> &[u8; 32]
12091212
pub fn bitcoin_primitives::WitnessMerkleNode::as_ref(&self) -> &[u8]
12101213
pub fn bitcoin_primitives::WitnessMerkleNode::borrow(&self) -> &[u8; 32]
12111214
pub fn bitcoin_primitives::WitnessMerkleNode::borrow(&self) -> &[u8]
1215+
pub fn bitcoin_primitives::WitnessMerkleNode::calculate_root<I: core::iter::traits::iterator::Iterator<Item = bitcoin_primitives::Wtxid>>(iter: I) -> core::option::Option<Self>
12121216
pub fn bitcoin_primitives::WitnessMerkleNode::clone(&self) -> bitcoin_primitives::WitnessMerkleNode
12131217
pub fn bitcoin_primitives::WitnessMerkleNode::cmp(&self, other: &bitcoin_primitives::WitnessMerkleNode) -> core::cmp::Ordering
1218+
pub fn bitcoin_primitives::WitnessMerkleNode::combine(&self, other: &Self) -> Self
12141219
pub fn bitcoin_primitives::WitnessMerkleNode::eq(&self, other: &bitcoin_primitives::WitnessMerkleNode) -> bool
12151220
pub fn bitcoin_primitives::WitnessMerkleNode::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
1221+
pub fn bitcoin_primitives::WitnessMerkleNode::from_leaf(leaf: bitcoin_primitives::Wtxid) -> Self
12161222
pub fn bitcoin_primitives::WitnessMerkleNode::hash<__H: core::hash::Hasher>(&self, state: &mut __H)
12171223
pub fn bitcoin_primitives::WitnessMerkleNode::partial_cmp(&self, other: &bitcoin_primitives::WitnessMerkleNode) -> core::option::Option<core::cmp::Ordering>
12181224
pub fn bitcoin_primitives::Wtxid::as_ref(&self) -> &[u8; 32]

bitcoin/src/blockdata/block.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use internals::{compact_size, ToU64};
1515
use io::{BufRead, Write};
1616

1717
use crate::consensus::encode::{self, Decodable, Encodable, WriteExt as _};
18-
use crate::merkle_tree::{MerkleNode as _, TxMerkleNode, WitnessMerkleNode};
18+
use crate::merkle_tree::{TxMerkleNode, WitnessMerkleNode};
1919
use crate::network::Params;
2020
use crate::prelude::Vec;
2121
use crate::script::{self, ScriptIntError, ScriptExt as _};

bitcoin/src/merkle_tree/block.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use io::{BufRead, Write};
1919

2020
use crate::block::{self, Block, Checked};
2121
use crate::consensus::encode::{self, Decodable, Encodable, ReadExt, WriteExt, MAX_VEC_SIZE};
22-
use crate::merkle_tree::{MerkleNode as _, TxMerkleNode};
22+
use crate::merkle_tree::TxMerkleNode;
2323
use crate::prelude::Vec;
2424
use crate::transaction::{Transaction, Txid};
2525
use crate::Weight;

bitcoin/src/merkle_tree/mod.rs

Lines changed: 1 addition & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
//!
77
//! ```
88
//! # use bitcoin::Txid;
9-
//! # use bitcoin::merkle_tree::{MerkleNode as _, TxMerkleNode};
9+
//! # use bitcoin::merkle_tree::TxMerkleNode;
1010
//! # let tx1 = Txid::from_byte_array([0xAA; 32]); // Arbitrary dummy hash values.
1111
//! # let tx2 = Txid::from_byte_array([0xFF; 32]);
1212
//! let tx_hashes = [tx1, tx2]; // All the hashes we wish to merkelize.
@@ -16,13 +16,8 @@
1616
1717
mod block;
1818

19-
use hashes::{sha256d, HashEngine as _};
2019
use io::{BufRead, Write};
2120

22-
use crate::prelude::Vec;
23-
use crate::transaction::TxIdentifier;
24-
use crate::{Txid, Wtxid};
25-
2621
#[rustfmt::skip]
2722
#[doc(inline)]
2823
pub use self::block::{MerkleBlock, MerkleBlockError, PartialMerkleTree};
@@ -53,105 +48,3 @@ impl Decodable for WitnessMerkleNode {
5348
Ok(Self::from_byte_array(<[u8; 32]>::consensus_decode(r)?))
5449
}
5550
}
56-
57-
/// A node in a Merkle tree of transactions or witness data within a block.
58-
///
59-
/// This trait is used to compute the transaction Merkle root contained in
60-
/// a block header. This is a particularly weird algorithm -- it interprets
61-
/// the list of transactions as a balanced binary tree, duplicating branches
62-
/// as needed to fill out the tree to a power of two size.
63-
///
64-
/// Other Merkle trees in Bitcoin, such as those used in Taproot commitments,
65-
/// do not use this algorithm and cannot use this trait.
66-
pub trait MerkleNode: Copy + PartialEq {
67-
/// The hash (TXID or WTXID) of a transaction in the tree.
68-
type Leaf: TxIdentifier;
69-
70-
/// Convert a hash to a leaf node of the tree.
71-
fn from_leaf(leaf: Self::Leaf) -> Self;
72-
/// Combine two nodes to get a single node. The final node of a tree is called the "root".
73-
fn combine(&self, other: &Self) -> Self;
74-
75-
/// Given an iterator of leaves, compute the Merkle root.
76-
///
77-
/// Returns `None` if the iterator was empty, or if the transaction list contains
78-
/// consecutive duplicates which would trigger CVE 2012-2459. Blocks with duplicate
79-
/// transactions will always be invalid, so there is no harm in us refusing to
80-
/// compute their merkle roots.
81-
///
82-
/// Unless you are certain your transaction list is nonempty and has no duplicates,
83-
/// you should not unwrap the `Option` returned by this method!
84-
fn calculate_root<I: Iterator<Item = Self::Leaf>>(iter: I) -> Option<Self> {
85-
let mut stack = Vec::<(usize, Self)>::with_capacity(32);
86-
// Start with a standard Merkle tree root computation...
87-
for (mut n, leaf) in iter.enumerate() {
88-
stack.push((0, Self::from_leaf(leaf)));
89-
90-
while n & 1 == 1 {
91-
let right = stack.pop().unwrap();
92-
let left = stack.pop().unwrap();
93-
if left.1 == right.1 {
94-
// Reject duplicate trees since they are guaranteed-invalid (Bitcoin does
95-
// not allow duplicate transactions in block) but can be used to confuse
96-
// nodes about legitimate blocks. See CVE 2012-2459 and the block comment
97-
// below.
98-
return None;
99-
}
100-
debug_assert_eq!(left.0, right.0);
101-
stack.push((left.0 + 1, left.1.combine(&right.1)));
102-
n >>= 1;
103-
}
104-
}
105-
// ...then, deal with incomplete trees. Bitcoin does a weird thing in
106-
// which it doubles-up nodes of the tree to fill out the tree, rather
107-
// than treating incomplete branches specially. This makes this tree
108-
// construction vulnerable to collisions (see CVE 2012-2459).
109-
//
110-
// (It is also vulnerable to collisions because it does not distinguish
111-
// between internal nodes and transactions, but this collisions of this
112-
// form are probably impractical. It is likely that 64-byte transactions
113-
// will be forbidden in the future which will close this for good.)
114-
//
115-
// This is consensus logic so we cannot fix the Merkle tree construction.
116-
// Instead we just have to reject the clearly-invalid half of the collision
117-
// (see previous comment).
118-
while stack.len() > 1 {
119-
let mut right = stack.pop().unwrap();
120-
let left = stack.pop().unwrap();
121-
while right.0 != left.0 {
122-
assert!(right.0 < left.0);
123-
right = (right.0 + 1, right.1.combine(&right.1)); // combine with self
124-
}
125-
stack.push((left.0 + 1, left.1.combine(&right.1)));
126-
}
127-
128-
stack.pop().map(|(_, h)| h)
129-
}
130-
}
131-
132-
// These two impl blocks are identical. FIXME once we have nailed down
133-
// our hash traits, it should be possible to put bounds on `MerkleNode`
134-
// and `MerkleNode::Leaf` which are sufficient to turn both methods into
135-
// provided methods in the trait definition.
136-
impl MerkleNode for TxMerkleNode {
137-
type Leaf = Txid;
138-
fn from_leaf(leaf: Self::Leaf) -> Self { Self::from_byte_array(leaf.to_byte_array()) }
139-
140-
fn combine(&self, other: &Self) -> Self {
141-
let mut encoder = sha256d::Hash::engine();
142-
encoder.input(self.as_byte_array());
143-
encoder.input(other.as_byte_array());
144-
Self::from_byte_array(sha256d::Hash::from_engine(encoder).to_byte_array())
145-
}
146-
}
147-
impl MerkleNode for WitnessMerkleNode {
148-
type Leaf = Wtxid;
149-
fn from_leaf(leaf: Self::Leaf) -> Self { Self::from_byte_array(leaf.to_byte_array()) }
150-
151-
fn combine(&self, other: &Self) -> Self {
152-
let mut encoder = sha256d::Hash::engine();
153-
encoder.input(self.as_byte_array());
154-
encoder.input(other.as_byte_array());
155-
Self::from_byte_array(sha256d::Hash::from_engine(encoder).to_byte_array())
156-
}
157-
}

primitives/src/hash_types/transaction_merkle_node.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ use arbitrary::{Arbitrary, Unstructured};
1212
use hashes::sha256d;
1313
use internals::write_err;
1414

15+
#[cfg(feature = "alloc")]
16+
use crate::merkle_tree::MerkleNode;
17+
#[cfg(feature = "alloc")]
18+
use crate::Txid;
19+
1520
/// A hash of the Merkle tree branch or root for transactions.
1621
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1722
pub struct TxMerkleNode(sha256d::Hash);
@@ -23,6 +28,27 @@ type Inner = sha256d::Hash;
2328

2429
include!("./generic.rs");
2530

31+
#[cfg(feature = "alloc")]
32+
impl TxMerkleNode {
33+
/// Convert a [`Txid`] hash to a leaf node of the tree.
34+
pub fn from_leaf(leaf: Txid) -> Self { MerkleNode::from_leaf(leaf) }
35+
36+
/// Combine two nodes to get a single node. The final node of a tree is called the "root".
37+
#[must_use]
38+
pub fn combine(&self, other: &Self) -> Self { MerkleNode::combine(self, other) }
39+
40+
/// Given an iterator of leaves, compute the Merkle root.
41+
///
42+
/// Returns `None` if the iterator was empty, or if the transaction list contains
43+
/// consecutive duplicates which would trigger CVE 2012-2459. Blocks with duplicate
44+
/// transactions will always be invalid, so there is no harm in us refusing to
45+
/// compute their merkle roots.
46+
///
47+
/// Unless you are certain your transaction list is nonempty and has no duplicates,
48+
/// you should not unwrap the `Option` returned by this method!
49+
pub fn calculate_root<I: Iterator<Item = Txid>>(iter: I) -> Option<Self> { MerkleNode::calculate_root(iter) }
50+
}
51+
2652
encoding::encoder_newtype! {
2753
/// The encoder for the [`TxMerkleNode`] type.
2854
pub struct TxMerkleNodeEncoder(encoding::ArrayEncoder<32>);

primitives/src/hash_types/witness_merkle_node.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ use core::str;
1111
use arbitrary::{Arbitrary, Unstructured};
1212
use hashes::sha256d;
1313

14+
#[cfg(feature = "alloc")]
15+
use crate::merkle_tree::MerkleNode;
16+
#[cfg(feature = "alloc")]
17+
use crate::Wtxid;
18+
1419
/// A hash corresponding to the Merkle tree root for witness data.
1520
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1621
pub struct WitnessMerkleNode(sha256d::Hash);
@@ -21,3 +26,24 @@ type HashType = WitnessMerkleNode;
2126
type Inner = sha256d::Hash;
2227

2328
include!("./generic.rs");
29+
30+
#[cfg(feature = "alloc")]
31+
impl WitnessMerkleNode {
32+
/// Convert a [`Wtxid`] hash to a leaf node of the tree.
33+
pub fn from_leaf(leaf: Wtxid) -> Self { MerkleNode::from_leaf(leaf) }
34+
35+
/// Combine two nodes to get a single node. The final node of a tree is called the "root".
36+
#[must_use]
37+
pub fn combine(&self, other: &Self) -> Self { MerkleNode::combine(self, other) }
38+
39+
/// Given an iterator of leaves, compute the Merkle root.
40+
///
41+
/// Returns `None` if the iterator was empty, or if the transaction list contains
42+
/// consecutive duplicates which would trigger CVE 2012-2459. Blocks with duplicate
43+
/// transactions will always be invalid, so there is no harm in us refusing to
44+
/// compute their merkle roots.
45+
///
46+
/// Unless you are certain your transaction list is nonempty and has no duplicates,
47+
/// you should not unwrap the `Option` returned by this method!
48+
pub fn calculate_root<I: Iterator<Item = Wtxid>>(iter: I) -> Option<Self> { MerkleNode::calculate_root(iter) }
49+
}

0 commit comments

Comments
 (0)