rust-bitcoin-unsafe-fast/bitcoin/src/merkle_tree/mod.rs

116 lines
4.5 KiB
Rust

// SPDX-License-Identifier: CC0-1.0
//! Bitcoin Merkle tree functions.
//!
//! # Examples
//!
//! ```
//! # use bitcoin::Txid;
//! # use bitcoin::merkle_tree::{MerkleNode as _, TxMerkleNode};
//! # let tx1 = Txid::from_byte_array([0xAA; 32]); // Arbitrary dummy hash values.
//! # let tx2 = Txid::from_byte_array([0xFF; 32]);
//! let tx_hashes = [tx1, tx2]; // All the hashes we wish to merkelize.
//! let root = TxMerkleNode::calculate_root(tx_hashes.into_iter());
//! assert!(root.is_some());
//! ```
mod block;
use hashes::{sha256d, HashEngine as _};
use crate::internal_macros::impl_hashencode;
use crate::prelude::Vec;
use crate::transaction::TxIdentifier;
use crate::{Txid, Wtxid};
#[rustfmt::skip]
#[doc(inline)]
pub use self::block::{MerkleBlock, MerkleBlockError, PartialMerkleTree};
pub use primitives::merkle_tree::{TxMerkleNode, WitnessMerkleNode};
impl_hashencode!(TxMerkleNode);
impl_hashencode!(WitnessMerkleNode);
/// A node in a Merkle tree of transactions or witness data within a block.
///
/// This trait is used to compute the transaction Merkle root contained in
/// a block header. This is a particularly weird algorithm -- it interprets
/// the list of transactions as a balanced binary tree, duplicating branches
/// as needed to fill out the tree to a power of two size.
///
/// Other Merkle trees in Bitcoin, such as those used in Taproot commitments,
/// do not use this algorithm and cannot use this trait.
pub trait MerkleNode: Copy {
/// The hash (TXID or WTXID) of a transaciton in the tree.
type Leaf: TxIdentifier;
/// Convert a hash to a leaf node of the tree.
fn from_leaf(leaf: Self::Leaf) -> Self;
/// Combine two nodes to get a single node. The final node of a tree is called the "root".
fn combine(&self, other: &Self) -> Self;
/// Given an iterator of leaves, compute the Merkle root.
///
/// Returns `None` iff the iterator was empty.
fn calculate_root<I: Iterator<Item = Self::Leaf>>(iter: I) -> Option<Self> {
let mut stack = Vec::<(usize, Self)>::with_capacity(32);
// Start with a standard Merkle tree root computation...
for (mut n, leaf) in iter.enumerate() {
stack.push((0, Self::from_leaf(leaf)));
while n & 1 == 1 {
let right = stack.pop().unwrap();
let left = stack.pop().unwrap();
debug_assert_eq!(left.0, right.0);
stack.push((left.0 + 1, left.1.combine(&right.1)));
n >>= 1;
}
}
// ...then, deal with incomplete trees. Bitcoin does a weird thing in
// which it doubles-up nodes of the tree to fill out the tree, rather
// than treating incomplete branches specially. This, along with its
// conflation of leaves with leaf hashes, makes its Merkle tree
// construction theoretically (though probably not practically)
// vulnerable to collisions. This is consensus logic so we just have
// to accept it.
while stack.len() > 1 {
let mut right = stack.pop().unwrap();
let left = stack.pop().unwrap();
while right.0 != left.0 {
assert!(right.0 < left.0);
right = (right.0 + 1, right.1.combine(&right.1)); // combine with self
}
stack.push((left.0 + 1, left.1.combine(&right.1)));
}
stack.pop().map(|(_, h)| h)
}
}
// These two impl blocks are identical. FIXME once we have nailed down
// our hash traits, it should be possible to put bounds on `MerkleNode`
// and `MerkleNode::Leaf` which are sufficient to turn both methods into
// provided methods in the trait definition.
impl MerkleNode for TxMerkleNode {
type Leaf = Txid;
fn from_leaf(leaf: Self::Leaf) -> Self { Self::from_byte_array(leaf.to_byte_array()) }
fn combine(&self, other: &Self) -> Self {
let mut encoder = sha256d::Hash::engine();
encoder.input(self.as_byte_array());
encoder.input(other.as_byte_array());
Self::from_byte_array(sha256d::Hash::from_engine(encoder).to_byte_array())
}
}
impl MerkleNode for WitnessMerkleNode {
type Leaf = Wtxid;
fn from_leaf(leaf: Self::Leaf) -> Self { Self::from_byte_array(leaf.to_byte_array()) }
fn combine(&self, other: &Self) -> Self {
let mut encoder = sha256d::Hash::engine();
encoder.input(self.as_byte_array());
encoder.input(other.as_byte_array());
Self::from_byte_array(sha256d::Hash::from_engine(encoder).to_byte_array())
}
}