Skip to content

Commit 56c5423

Browse files
committed
Merge rust-bitcoin#5346: primitives: Move MerkleNode and TxIdentifier into primitives
a874123 primitives: Move MerkleNode and TxIdentifier into primitives (Mitchell Bagot) Pull request description: 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. Further, move TxIdentifier to primitives and re-export from bitcoin. ACKs for top commit: tcharding: ACK a874123 apoelstra: ACK a874123; successfully ran local tests Tree-SHA512: 41fef68ebfb01e04b4258d62f35ad078ac4853537d3c27e3ca6f9121b5454285179ee91b10512e22ab310833a88b6be2759b202c878e2d2dab362496f51dfbca
2 parents fd82512 + a874123 commit 56c5423

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
@@ -1330,13 +1330,16 @@ pub fn bitcoin_primitives::TxMerkleNode::as_ref(&self) -> &[u8; 32]
13301330
pub fn bitcoin_primitives::TxMerkleNode::as_ref(&self) -> &[u8]
13311331
pub fn bitcoin_primitives::TxMerkleNode::borrow(&self) -> &[u8; 32]
13321332
pub fn bitcoin_primitives::TxMerkleNode::borrow(&self) -> &[u8]
1333+
pub fn bitcoin_primitives::TxMerkleNode::calculate_root<I: core::iter::traits::iterator::Iterator<Item = bitcoin_primitives::Txid>>(iter: I) -> core::option::Option<Self>
13331334
pub fn bitcoin_primitives::TxMerkleNode::clone(&self) -> bitcoin_primitives::TxMerkleNode
13341335
pub fn bitcoin_primitives::TxMerkleNode::cmp(&self, other: &bitcoin_primitives::TxMerkleNode) -> core::cmp::Ordering
1336+
pub fn bitcoin_primitives::TxMerkleNode::combine(&self, other: &Self) -> Self
13351337
pub fn bitcoin_primitives::TxMerkleNode::decoder() -> Self::Decoder
13361338
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>
13371339
pub fn bitcoin_primitives::TxMerkleNode::encoder(&self) -> Self::Encoder
13381340
pub fn bitcoin_primitives::TxMerkleNode::eq(&self, other: &bitcoin_primitives::TxMerkleNode) -> bool
13391341
pub fn bitcoin_primitives::TxMerkleNode::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
1342+
pub fn bitcoin_primitives::TxMerkleNode::from_leaf(leaf: bitcoin_primitives::Txid) -> Self
13401343
pub fn bitcoin_primitives::TxMerkleNode::from_str(s: &str) -> core::result::Result<Self, Self::Err>
13411344
pub fn bitcoin_primitives::TxMerkleNode::hash<__H: core::hash::Hasher>(&self, state: &mut __H)
13421345
pub fn bitcoin_primitives::TxMerkleNode::partial_cmp(&self, other: &bitcoin_primitives::TxMerkleNode) -> core::option::Option<core::cmp::Ordering>
@@ -1376,11 +1379,14 @@ pub fn bitcoin_primitives::WitnessMerkleNode::as_ref(&self) -> &[u8; 32]
13761379
pub fn bitcoin_primitives::WitnessMerkleNode::as_ref(&self) -> &[u8]
13771380
pub fn bitcoin_primitives::WitnessMerkleNode::borrow(&self) -> &[u8; 32]
13781381
pub fn bitcoin_primitives::WitnessMerkleNode::borrow(&self) -> &[u8]
1382+
pub fn bitcoin_primitives::WitnessMerkleNode::calculate_root<I: core::iter::traits::iterator::Iterator<Item = bitcoin_primitives::Wtxid>>(iter: I) -> core::option::Option<Self>
13791383
pub fn bitcoin_primitives::WitnessMerkleNode::clone(&self) -> bitcoin_primitives::WitnessMerkleNode
13801384
pub fn bitcoin_primitives::WitnessMerkleNode::cmp(&self, other: &bitcoin_primitives::WitnessMerkleNode) -> core::cmp::Ordering
1385+
pub fn bitcoin_primitives::WitnessMerkleNode::combine(&self, other: &Self) -> Self
13811386
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>
13821387
pub fn bitcoin_primitives::WitnessMerkleNode::eq(&self, other: &bitcoin_primitives::WitnessMerkleNode) -> bool
13831388
pub fn bitcoin_primitives::WitnessMerkleNode::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
1389+
pub fn bitcoin_primitives::WitnessMerkleNode::from_leaf(leaf: bitcoin_primitives::Wtxid) -> Self
13841390
pub fn bitcoin_primitives::WitnessMerkleNode::from_str(s: &str) -> core::result::Result<Self, Self::Err>
13851391
pub fn bitcoin_primitives::WitnessMerkleNode::hash<__H: core::hash::Hasher>(&self, state: &mut __H)
13861392
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
@@ -1187,12 +1187,15 @@ pub fn bitcoin_primitives::TxMerkleNode::as_ref(&self) -> &[u8; 32]
11871187
pub fn bitcoin_primitives::TxMerkleNode::as_ref(&self) -> &[u8]
11881188
pub fn bitcoin_primitives::TxMerkleNode::borrow(&self) -> &[u8; 32]
11891189
pub fn bitcoin_primitives::TxMerkleNode::borrow(&self) -> &[u8]
1190+
pub fn bitcoin_primitives::TxMerkleNode::calculate_root<I: core::iter::traits::iterator::Iterator<Item = bitcoin_primitives::Txid>>(iter: I) -> core::option::Option<Self>
11901191
pub fn bitcoin_primitives::TxMerkleNode::clone(&self) -> bitcoin_primitives::TxMerkleNode
11911192
pub fn bitcoin_primitives::TxMerkleNode::cmp(&self, other: &bitcoin_primitives::TxMerkleNode) -> core::cmp::Ordering
1193+
pub fn bitcoin_primitives::TxMerkleNode::combine(&self, other: &Self) -> Self
11921194
pub fn bitcoin_primitives::TxMerkleNode::decoder() -> Self::Decoder
11931195
pub fn bitcoin_primitives::TxMerkleNode::encoder(&self) -> Self::Encoder
11941196
pub fn bitcoin_primitives::TxMerkleNode::eq(&self, other: &bitcoin_primitives::TxMerkleNode) -> bool
11951197
pub fn bitcoin_primitives::TxMerkleNode::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
1198+
pub fn bitcoin_primitives::TxMerkleNode::from_leaf(leaf: bitcoin_primitives::Txid) -> Self
11961199
pub fn bitcoin_primitives::TxMerkleNode::hash<__H: core::hash::Hasher>(&self, state: &mut __H)
11971200
pub fn bitcoin_primitives::TxMerkleNode::partial_cmp(&self, other: &bitcoin_primitives::TxMerkleNode) -> core::option::Option<core::cmp::Ordering>
11981201
pub fn bitcoin_primitives::Txid::as_ref(&self) -> &[u8; 32]
@@ -1221,10 +1224,13 @@ pub fn bitcoin_primitives::WitnessMerkleNode::as_ref(&self) -> &[u8; 32]
12211224
pub fn bitcoin_primitives::WitnessMerkleNode::as_ref(&self) -> &[u8]
12221225
pub fn bitcoin_primitives::WitnessMerkleNode::borrow(&self) -> &[u8; 32]
12231226
pub fn bitcoin_primitives::WitnessMerkleNode::borrow(&self) -> &[u8]
1227+
pub fn bitcoin_primitives::WitnessMerkleNode::calculate_root<I: core::iter::traits::iterator::Iterator<Item = bitcoin_primitives::Wtxid>>(iter: I) -> core::option::Option<Self>
12241228
pub fn bitcoin_primitives::WitnessMerkleNode::clone(&self) -> bitcoin_primitives::WitnessMerkleNode
12251229
pub fn bitcoin_primitives::WitnessMerkleNode::cmp(&self, other: &bitcoin_primitives::WitnessMerkleNode) -> core::cmp::Ordering
1230+
pub fn bitcoin_primitives::WitnessMerkleNode::combine(&self, other: &Self) -> Self
12261231
pub fn bitcoin_primitives::WitnessMerkleNode::eq(&self, other: &bitcoin_primitives::WitnessMerkleNode) -> bool
12271232
pub fn bitcoin_primitives::WitnessMerkleNode::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
1233+
pub fn bitcoin_primitives::WitnessMerkleNode::from_leaf(leaf: bitcoin_primitives::Wtxid) -> Self
12281234
pub fn bitcoin_primitives::WitnessMerkleNode::hash<__H: core::hash::Hasher>(&self, state: &mut __H)
12291235
pub fn bitcoin_primitives::WitnessMerkleNode::partial_cmp(&self, other: &bitcoin_primitives::WitnessMerkleNode) -> core::option::Option<core::cmp::Ordering>
12301236
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)