From 22dd904735270e96dfa5c0d907e85fe15fb31485 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Fri, 21 Oct 2022 09:30:20 +1100 Subject: [PATCH 1/3] Rename util::hash module The `util::hash` module provides two functions for computing a merkle root from a list/iterator of hashes. Rename the module to `merkle_root` and move it to the crate root, deprecate the original functions. Done as part of flattening the `util` module. --- bitcoin/src/blockdata/block.rs | 2 +- bitcoin/src/lib.rs | 1 + bitcoin/src/{util/hash.rs => merkle_tree.rs} | 7 ++--- bitcoin/src/util/merkleblock.rs | 2 +- bitcoin/src/util/mod.rs | 29 +++++++++++++++++++- 5 files changed, 33 insertions(+), 8 deletions(-) rename bitcoin/src/{util/hash.rs => merkle_tree.rs} (93%) diff --git a/bitcoin/src/blockdata/block.rs b/bitcoin/src/blockdata/block.rs index 9ab112a8..24b70a52 100644 --- a/bitcoin/src/blockdata/block.rs +++ b/bitcoin/src/blockdata/block.rs @@ -15,7 +15,7 @@ use core::fmt; use crate::util; use crate::util::Error::{BlockBadTarget, BlockBadProofOfWork}; -use crate::util::hash::bitcoin_merkle_root; +use crate::merkle_tree::bitcoin_merkle_root; use crate::hashes::{Hash, HashEngine}; use crate::hash_types::{Wtxid, BlockHash, TxMerkleNode, WitnessMerkleNode, WitnessCommitment}; use crate::consensus::{encode, Encodable, Decodable}; diff --git a/bitcoin/src/lib.rs b/bitcoin/src/lib.rs index a7dfc845..1f42d543 100644 --- a/bitcoin/src/lib.rs +++ b/bitcoin/src/lib.rs @@ -92,6 +92,7 @@ pub mod blockdata; pub mod consensus; pub mod error; pub mod hash_types; +pub mod merkle_tree; pub mod policy; pub mod pow; pub mod sighash; diff --git a/bitcoin/src/util/hash.rs b/bitcoin/src/merkle_tree.rs similarity index 93% rename from bitcoin/src/util/hash.rs rename to bitcoin/src/merkle_tree.rs index 79f75c77..c6fc3818 100644 --- a/bitcoin/src/util/hash.rs +++ b/bitcoin/src/merkle_tree.rs @@ -1,10 +1,7 @@ // Written in 2014 by Andrew Poelstra // SPDX-License-Identifier: CC0-1.0 -//! Bitcoin hash functions. -//! -//! This module provides utility functions related to hashing data, including -//! merkleization. +//! Bitcoin merkle tree functions. //! use core::iter; @@ -108,7 +105,7 @@ mod tests { #[test] fn both_merkle_root_functions_return_the_same_result() { // testnet block 000000000000045e0b1660b6445b5e5c5ab63c9a4f956be7e1e69be04fa4497b - let segwit_block = include_bytes!("../../tests/data/testnet_block_000000000000045e0b1660b6445b5e5c5ab63c9a4f956be7e1e69be04fa4497b.raw"); + let segwit_block = include_bytes!("../tests/data/testnet_block_000000000000045e0b1660b6445b5e5c5ab63c9a4f956be7e1e69be04fa4497b.raw"); let block: Block = deserialize(&segwit_block[..]).expect("Failed to deserialize block"); assert!(block.check_merkle_root()); // Sanity check. diff --git a/bitcoin/src/util/merkleblock.rs b/bitcoin/src/util/merkleblock.rs index b1f9fd36..25ac7532 100644 --- a/bitcoin/src/util/merkleblock.rs +++ b/bitcoin/src/util/merkleblock.rs @@ -528,7 +528,7 @@ mod tests { use secp256k1::rand::prelude::*; use crate::consensus::encode::{deserialize, serialize}; - use crate::util::hash::bitcoin_merkle_root; + use crate::merkle_tree::bitcoin_merkle_root; use crate::util::merkleblock::{MerkleBlock, PartialMerkleTree}; use crate::Block; diff --git a/bitcoin/src/util/mod.rs b/bitcoin/src/util/mod.rs index 3f96ab3e..50187490 100644 --- a/bitcoin/src/util/mod.rs +++ b/bitcoin/src/util/mod.rs @@ -11,7 +11,6 @@ pub mod ecdsa; pub mod schnorr; pub mod amount; pub mod base58; -pub mod hash; pub mod merkleblock; pub mod psbt; pub mod taproot; @@ -95,6 +94,34 @@ pub use crate::bip32; #[deprecated(since = "0.30.0", note = "Please use crate::bip158")] pub use crate::bip158; +/// Functions from the `hash` module were renamed and moved to `../merkle_tree`. +pub mod hash { + use crate::consensus::encode::Encodable; + use crate::hashes::Hash; + use crate::io; + + /// Calculates the merkle root of a list of *hashes*, inline (in place) in `hashes`. + #[deprecated(since = "0.30.0", note = "Please use crate::merkle_tree::bitcoin_merkle_root_inline")] + pub fn bitcoin_merkle_root_inline(hashes: &mut [T]) -> Option + where + T: Hash + Encodable, + ::Engine: io::Write, + { + crate::merkle_tree::bitcoin_merkle_root_inline(hashes) + } + + /// Calculates the merkle root of an iterator of *hashes*. + #[deprecated(since = "0.30.0", note = "Please use crate::merkle_tree::bitcoin_merkle_root")] + pub fn bitcoin_merkle_root(hashes: I) -> Option + where + T: Hash + Encodable, + ::Engine: io::Write, + I: Iterator, + { + crate::merkle_tree::bitcoin_merkle_root(hashes) + } +} + /// The `misc` module was moved and re-named to `sign_message`. pub mod misc { use crate::prelude::*; From 2dbc7fdf215fa97303442b232c1cb69613370442 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Fri, 21 Oct 2022 10:08:43 +1100 Subject: [PATCH 2/3] Rename merkle_root functions Recently we renamed the `hash` module to `merkle_root`, this makes the public functions provided stutter if used with one layer of path as is Rust convention: `merkle_root::bitcoin_merkle_root` We can improve on this by renaming the functions to 'calculate', then we get - `merkle_root::calculate()` - `merkle_root::calculate_inline()` --- bitcoin/src/blockdata/block.rs | 7 +++---- bitcoin/src/merkle_tree.rs | 20 +++++++++++++++----- bitcoin/src/util/merkleblock.rs | 5 ++--- bitcoin/src/util/mod.rs | 10 +++++----- 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/bitcoin/src/blockdata/block.rs b/bitcoin/src/blockdata/block.rs index 24b70a52..143bc208 100644 --- a/bitcoin/src/blockdata/block.rs +++ b/bitcoin/src/blockdata/block.rs @@ -13,9 +13,8 @@ use crate::prelude::*; use core::fmt; -use crate::util; +use crate::{merkle_tree, util}; use crate::util::Error::{BlockBadTarget, BlockBadProofOfWork}; -use crate::merkle_tree::bitcoin_merkle_root; use crate::hashes::{Hash, HashEngine}; use crate::hash_types::{Wtxid, BlockHash, TxMerkleNode, WitnessMerkleNode, WitnessCommitment}; use crate::consensus::{encode, Encodable, Decodable}; @@ -235,7 +234,7 @@ impl Block { /// Computes the transaction merkle root. pub fn compute_merkle_root(&self) -> Option { let hashes = self.txdata.iter().map(|obj| obj.txid().as_hash()); - bitcoin_merkle_root(hashes).map(|h| h.into()) + merkle_tree::calculate_root(hashes).map(|h| h.into()) } /// Computes the witness commitment for the block's transaction list. @@ -256,7 +255,7 @@ impl Block { t.wtxid().as_hash() } }); - bitcoin_merkle_root(hashes).map(|h| h.into()) + merkle_tree::calculate_root(hashes).map(|h| h.into()) } /// base_size == size of header + size of encoded transaction count. diff --git a/bitcoin/src/merkle_tree.rs b/bitcoin/src/merkle_tree.rs index c6fc3818..ddfa559b 100644 --- a/bitcoin/src/merkle_tree.rs +++ b/bitcoin/src/merkle_tree.rs @@ -3,6 +3,16 @@ //! Bitcoin merkle tree functions. //! +//! # Examples +//! +//! ``` +//! # use bitcoin::{merkle_tree, Txid}; +//! # use bitcoin::hashes::Hash; +//! # let tx1 = Txid::all_zeros(); // Dummy hash values. +//! # let tx2 = Txid::all_zeros(); +//! let tx_hashes = vec![tx1, tx2]; // All the hashes we wish to merkelize. +//! let root = merkle_tree::calculate_root(tx_hashes.into_iter()); +//! ``` use core::iter; @@ -16,13 +26,13 @@ use crate::consensus::encode::Encodable; /// Calculates the merkle root of a list of *hashes*, inline (in place) in `hashes`. /// -/// In most cases, you'll want to use [bitcoin_merkle_root] instead. +/// In most cases, you'll want to use [`calculate_root`] instead. /// /// # Returns /// - `None` if `hashes` is empty. The merkle root of an empty tree of hashes is undefined. /// - `Some(hash)` if `hashes` contains one element. A single hash is by definition the merkle root. /// - `Some(merkle_root)` if length of `hashes` is greater than one. -pub fn bitcoin_merkle_root_inline(hashes: &mut [T]) -> Option +pub fn calculate_root_inline(hashes: &mut [T]) -> Option where T: Hash + Encodable, ::Engine: io::Write, @@ -40,7 +50,7 @@ where /// - `None` if `hashes` is empty. The merkle root of an empty tree of hashes is undefined. /// - `Some(hash)` if `hashes` contains one element. A single hash is by definition the merkle root. /// - `Some(merkle_root)` if length of `hashes` is greater than one. -pub fn bitcoin_merkle_root(mut hashes: I) -> Option +pub fn calculate_root(mut hashes: I) -> Option where T: Hash + Encodable, ::Engine: io::Write, @@ -116,8 +126,8 @@ mod tests { hashes_array[i] = hash; } - let from_iter = bitcoin_merkle_root(hashes_iter); - let from_array = bitcoin_merkle_root_inline(&mut hashes_array); + let from_iter = calculate_root(hashes_iter); + let from_array = calculate_root_inline(&mut hashes_array); assert_eq!(from_iter, from_array); } } diff --git a/bitcoin/src/util/merkleblock.rs b/bitcoin/src/util/merkleblock.rs index 25ac7532..057cb0ba 100644 --- a/bitcoin/src/util/merkleblock.rs +++ b/bitcoin/src/util/merkleblock.rs @@ -528,9 +528,8 @@ mod tests { use secp256k1::rand::prelude::*; use crate::consensus::encode::{deserialize, serialize}; - use crate::merkle_tree::bitcoin_merkle_root; use crate::util::merkleblock::{MerkleBlock, PartialMerkleTree}; - use crate::Block; + use crate::{merkle_tree, Block}; /// accepts `pmt_test_$num` fn pmt_test_from_name(name: &str) { @@ -559,7 +558,7 @@ mod tests { // Calculate the merkle root and height let hashes = tx_ids.iter().map(|t| t.as_hash()); - let merkle_root_1: TxMerkleNode = bitcoin_merkle_root(hashes).expect("hashes is not empty").into(); + let merkle_root_1: TxMerkleNode = merkle_tree::calculate_root(hashes).expect("hashes is not empty").into(); let mut height = 1; let mut ntx = tx_count; while ntx > 1 { diff --git a/bitcoin/src/util/mod.rs b/bitcoin/src/util/mod.rs index 50187490..06ea34ba 100644 --- a/bitcoin/src/util/mod.rs +++ b/bitcoin/src/util/mod.rs @@ -98,27 +98,27 @@ pub use crate::bip158; pub mod hash { use crate::consensus::encode::Encodable; use crate::hashes::Hash; - use crate::io; + use crate::{io, merkle_tree}; /// Calculates the merkle root of a list of *hashes*, inline (in place) in `hashes`. - #[deprecated(since = "0.30.0", note = "Please use crate::merkle_tree::bitcoin_merkle_root_inline")] + #[deprecated(since = "0.30.0", note = "Please use crate::merkle_tree::calculate_root_inline")] pub fn bitcoin_merkle_root_inline(hashes: &mut [T]) -> Option where T: Hash + Encodable, ::Engine: io::Write, { - crate::merkle_tree::bitcoin_merkle_root_inline(hashes) + crate::merkle_tree::calculate_root_inline(hashes) } /// Calculates the merkle root of an iterator of *hashes*. - #[deprecated(since = "0.30.0", note = "Please use crate::merkle_tree::bitcoin_merkle_root")] + #[deprecated(since = "0.30.0", note = "Please use crate::merkle_tree::calculate_root")] pub fn bitcoin_merkle_root(hashes: I) -> Option where T: Hash + Encodable, ::Engine: io::Write, I: Iterator, { - crate::merkle_tree::bitcoin_merkle_root(hashes) + merkle_tree::calculate_root(hashes) } } From 29df410ea330c9a0feae8416c826d3711a3502bf Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Sat, 29 Oct 2022 07:50:09 +1100 Subject: [PATCH 3/3] Document state after call to calculate_root_inline The function call `calculate_root_inline` calculates the merkle root using the input array as a scratch buffer, i.e., we trash the data during recursive calls to `merkle_root_r`. Add explicit documentation to the function so its super clear not to use the hashes again after calling this function. --- bitcoin/src/merkle_tree.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bitcoin/src/merkle_tree.rs b/bitcoin/src/merkle_tree.rs index ddfa559b..a9cfb385 100644 --- a/bitcoin/src/merkle_tree.rs +++ b/bitcoin/src/merkle_tree.rs @@ -26,7 +26,9 @@ use crate::consensus::encode::Encodable; /// Calculates the merkle root of a list of *hashes*, inline (in place) in `hashes`. /// -/// In most cases, you'll want to use [`calculate_root`] instead. +/// In most cases, you'll want to use [`calculate_root`] instead. Please note, calling this function +/// trashes the data in `hashes` (i.e. the `hashes` is left in an undefined state at conclusion of +/// this method and should not be used again afterwards). /// /// # Returns /// - `None` if `hashes` is empty. The merkle root of an empty tree of hashes is undefined.