From bb0f839c2f14022ce217e0a55d7ed868f4977d13 Mon Sep 17 00:00:00 2001 From: Martin Habovstiak Date: Fri, 8 Dec 2023 12:42:18 +0100 Subject: [PATCH 1/5] Lint with nightly While `clippy` now allows `TBD` to be used in `since` parameter of `deprecated` attribute it is only available in the newest, nightly, version. Switch `clippy` version to nightly to enable the `TBD` value. --- .github/workflows/rust.yml | 4 +++- bitcoin/src/lib.rs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 09c6000b..02aedb92 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -22,7 +22,6 @@ jobs: - name: Running test script env: DO_COV: true - DO_LINT: true AS_DEPENDENCY: false DO_DOCS: true DO_FEATURE_MATRIX: true @@ -54,8 +53,11 @@ jobs: uses: actions/checkout@v4 - name: Checkout Toolchain uses: dtolnay/rust-toolchain@nightly + - name: Install clippy + run: rustup component add clippy - name: Running test script env: + DO_LINT: true DO_FMT: false DO_BENCH: true AS_DEPENDENCY: false diff --git a/bitcoin/src/lib.rs b/bitcoin/src/lib.rs index 1dbd6a58..70aedcc4 100644 --- a/bitcoin/src/lib.rs +++ b/bitcoin/src/lib.rs @@ -171,7 +171,7 @@ mod prelude { pub use alloc::sync; #[cfg(any(feature = "std", test))] - pub use std::{string::{String, ToString}, vec::Vec, boxed::Box, borrow::{Borrow, BorrowMut, Cow, ToOwned}, slice, rc, sync}; + pub use std::{string::{String, ToString}, vec::Vec, boxed::Box, borrow::{Borrow, BorrowMut, Cow, ToOwned}, rc, sync}; #[cfg(all(not(feature = "std"), not(test)))] pub use alloc::collections::{BTreeMap, BTreeSet, btree_map, BinaryHeap}; From 93b415589d306aea0a1839053f4c77edf18becb9 Mon Sep 17 00:00:00 2001 From: Martin Habovstiak Date: Thu, 7 Dec 2023 23:25:10 +0100 Subject: [PATCH 2/5] Rename `inner` to `slice`/`vec` These names are more descriptive. --- bitcoin/src/taproot/mod.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/bitcoin/src/taproot/mod.rs b/bitcoin/src/taproot/mod.rs index f43cf130..ef816b7d 100644 --- a/bitcoin/src/taproot/mod.rs +++ b/bitcoin/src/taproot/mod.rs @@ -1058,9 +1058,13 @@ impl<'leaf> ScriptLeaf<'leaf> { pub struct TaprootMerkleBranch(Vec); impl TaprootMerkleBranch { - /// Returns a reference to the inner vector of hashes. + /// Returns a reference to the slice of hashes. + #[deprecated(since = "TBD", note = "Use `as_slice` instead")] pub fn as_inner(&self) -> &[TapNodeHash] { &self.0 } + /// Returns a reference to the slice of hashes. + pub fn as_slice(&self) -> &[TapNodeHash] { &self.0 } + /// Returns the number of nodes in this merkle proof. pub fn len(&self) -> usize { self.0.len() } @@ -1136,7 +1140,11 @@ impl TaprootMerkleBranch { } /// Returns the inner list of hashes. + #[deprecated(since = "TBD", note = "Use `into_vec` instead")] pub fn into_inner(self) -> Vec { self.0 } + + /// Returns the list of hashes stored in a `Vec`. + pub fn into_vec(self) -> Vec { self.0 } } macro_rules! impl_try_from { @@ -1233,7 +1241,7 @@ impl ControlBlock { /// Returns the size of control block. Faster and more efficient than calling /// `Self::serialize().len()`. Can be handy for fee estimation. pub fn size(&self) -> usize { - TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * self.merkle_branch.as_inner().len() + TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * self.merkle_branch.len() } /// Serializes to a writer. @@ -1275,7 +1283,7 @@ impl ControlBlock { // Initially the curr_hash is the leaf hash let mut curr_hash = TapNodeHash::from_script(script, self.leaf_version); // Verify the proof - for elem in self.merkle_branch.as_inner() { + for elem in self.merkle_branch.as_slice() { // Recalculate the curr hash as parent hash curr_hash = TapNodeHash::from_node_hashes(curr_hash, *elem); } From 9d23c1d0a8d39fffd14d49910e7b354f00c336e4 Mon Sep 17 00:00:00 2001 From: Martin Habovstiak Date: Thu, 7 Dec 2023 23:45:15 +0100 Subject: [PATCH 3/5] Implement std traits for `TaprootMerkleBranch` The type is naturally a collection of hashes so make it behave that way by implementing `Deref`, `AsRef`, `Borrow` and their mutable versions as well as `IntoIterator` for its reference. `IntoIterator` for itself is not yet implemented because it's a bit more complicated. --- bitcoin/src/taproot/mod.rs | 66 +++++++++++++++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 5 deletions(-) diff --git a/bitcoin/src/taproot/mod.rs b/bitcoin/src/taproot/mod.rs index ef816b7d..4ebf9b83 100644 --- a/bitcoin/src/taproot/mod.rs +++ b/bitcoin/src/taproot/mod.rs @@ -1118,20 +1118,20 @@ impl TaprootMerkleBranch { /// /// The number of bytes written to the writer. pub fn encode(&self, writer: &mut Write) -> io::Result { - for hash in self.0.iter() { + for hash in self { writer.write_all(hash.as_ref())?; } - Ok(self.0.len() * TapNodeHash::LEN) + Ok(self.len() * TapNodeHash::LEN) } /// Serializes `self` as bytes. pub fn serialize(&self) -> Vec { - self.0.iter().flat_map(|e| e.as_byte_array()).copied().collect::>() + self.iter().flat_map(|e| e.as_byte_array()).copied().collect::>() } /// Appends elements to proof. fn push(&mut self, h: TapNodeHash) -> Result<(), TaprootBuilderError> { - if self.0.len() >= TAPROOT_CONTROL_MAX_NODE_COUNT { + if self.len() >= TAPROOT_CONTROL_MAX_NODE_COUNT { Err(TaprootBuilderError::InvalidMerkleTreeDepth(self.0.len())) } else { self.0.push(h); @@ -1194,6 +1194,62 @@ impl From for Vec { fn from(branch: TaprootMerkleBranch) -> Self { branch.0 } } +impl<'a> IntoIterator for &'a TaprootMerkleBranch { + type IntoIter = core::slice::Iter<'a, TapNodeHash>; + type Item = &'a TapNodeHash; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter() + } +} + +impl<'a> IntoIterator for &'a mut TaprootMerkleBranch { + type IntoIter = core::slice::IterMut<'a, TapNodeHash>; + type Item = &'a mut TapNodeHash; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter_mut() + } +} + +impl core::ops::Deref for TaprootMerkleBranch { + type Target = [TapNodeHash]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl core::ops::DerefMut for TaprootMerkleBranch { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl AsRef<[TapNodeHash]> for TaprootMerkleBranch { + fn as_ref(&self) -> &[TapNodeHash] { + &self.0 + } +} + +impl AsMut<[TapNodeHash]> for TaprootMerkleBranch { + fn as_mut(&mut self) -> &mut [TapNodeHash] { + &mut self.0 + } +} + +impl Borrow<[TapNodeHash]> for TaprootMerkleBranch { + fn borrow(&self) -> &[TapNodeHash] { + &self.0 + } +} + +impl BorrowMut<[TapNodeHash]> for TaprootMerkleBranch { + fn borrow_mut(&mut self) -> &mut [TapNodeHash] { + &mut self.0 + } +} + /// Control block data structure used in Tapscript satisfaction. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -1283,7 +1339,7 @@ impl ControlBlock { // Initially the curr_hash is the leaf hash let mut curr_hash = TapNodeHash::from_script(script, self.leaf_version); // Verify the proof - for elem in self.merkle_branch.as_slice() { + for elem in &self.merkle_branch { // Recalculate the curr hash as parent hash curr_hash = TapNodeHash::from_node_hashes(curr_hash, *elem); } From e531fa612b0aa602d3ad05aa2f06c6e7166611b4 Mon Sep 17 00:00:00 2001 From: Martin Habovstiak Date: Fri, 8 Dec 2023 00:12:45 +0100 Subject: [PATCH 4/5] Move `TaprootMerkleBranch` and impl `IntoIterator` Since the iterator created by `IntoIterator` should be called `IntoIter` we move the whole `TaprootMerkleBranch` to its own module which contains the type to avoid confusion. This has an additional benefit of reducing the scope where the invariant could be broken. This already uncovered that our internal code was abusing access to the private field (although the code was correct). To implement the iterator we simply delegate to `vec::IntoIter`, including overriding the default method which are likely to be implemented by `Vec` more optimally. We avoid exposing `vec::IntoIter` directly since we may want to change the representation (e.g. to `ArrayVec`). --- bitcoin/src/taproot/merkle_branch.rs | 276 +++++++++++++++++++++++++++ bitcoin/src/taproot/mod.rs | 215 +-------------------- 2 files changed, 284 insertions(+), 207 deletions(-) create mode 100644 bitcoin/src/taproot/merkle_branch.rs diff --git a/bitcoin/src/taproot/merkle_branch.rs b/bitcoin/src/taproot/merkle_branch.rs new file mode 100644 index 00000000..287c303a --- /dev/null +++ b/bitcoin/src/taproot/merkle_branch.rs @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: CC0-1.0 + +//! Contains `TaprootMerkleBranch` and its associated types. + +use alloc::boxed::Box; +use alloc::vec::Vec; +use core::borrow::{Borrow, BorrowMut}; + +use super::{TapNodeHash, TaprootError, TaprootBuilderError, TAPROOT_CONTROL_NODE_SIZE, TAPROOT_CONTROL_MAX_NODE_COUNT}; +use hashes::Hash; + +/// The merkle proof for inclusion of a tree in a taptree hash. +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))] +#[cfg_attr(feature = "serde", serde(into = "Vec"))] +#[cfg_attr(feature = "serde", serde(try_from = "Vec"))] +pub struct TaprootMerkleBranch(Vec); + +impl TaprootMerkleBranch { + /// Returns a reference to the slice of hashes. + #[deprecated(since = "TBD", note = "Use `as_slice` instead")] + pub fn as_inner(&self) -> &[TapNodeHash] { &self.0 } + + /// Returns a reference to the slice of hashes. + pub fn as_slice(&self) -> &[TapNodeHash] { &self.0 } + + /// Returns the number of nodes in this merkle proof. + pub fn len(&self) -> usize { self.0.len() } + + /// Checks if this merkle proof is empty. + pub fn is_empty(&self) -> bool { self.0.is_empty() } + + /// Decodes bytes from control block. + /// + /// This reads the branch as encoded in the control block: the concatenated 32B byte chunks - + /// one for each hash. + /// + /// # Errors + /// + /// The function returns an error if the the number of bytes is not an integer multiple of 32 or + /// if the number of hashes exceeds 128. + pub fn decode(sl: &[u8]) -> Result { + if sl.len() % TAPROOT_CONTROL_NODE_SIZE != 0 { + Err(TaprootError::InvalidMerkleBranchSize(sl.len())) + } else if sl.len() > TAPROOT_CONTROL_NODE_SIZE * TAPROOT_CONTROL_MAX_NODE_COUNT { + Err(TaprootError::InvalidMerkleTreeDepth(sl.len() / TAPROOT_CONTROL_NODE_SIZE)) + } else { + let inner = sl + .chunks_exact(TAPROOT_CONTROL_NODE_SIZE) + .map(|chunk| { + TapNodeHash::from_slice(chunk) + .expect("chunks_exact always returns the correct size") + }) + .collect(); + + Ok(TaprootMerkleBranch(inner)) + } + } + + /// Creates a merkle proof from list of hashes. + /// + /// # Errors + /// If inner proof length is more than [`TAPROOT_CONTROL_MAX_NODE_COUNT`] (128). + fn from_collection + Into>>( + collection: T, + ) -> Result { + if collection.as_ref().len() > TAPROOT_CONTROL_MAX_NODE_COUNT { + Err(TaprootError::InvalidMerkleTreeDepth(collection.as_ref().len())) + } else { + Ok(TaprootMerkleBranch(collection.into())) + } + } + + /// Serializes to a writer. + /// + /// # Returns + /// + /// The number of bytes written to the writer. + pub fn encode(&self, writer: &mut Write) -> io::Result { + for hash in self { + writer.write_all(hash.as_ref())?; + } + Ok(self.len() * TapNodeHash::LEN) + } + + /// Serializes `self` as bytes. + pub fn serialize(&self) -> Vec { + self.iter().flat_map(|e| e.as_byte_array()).copied().collect::>() + } + + /// Appends elements to proof. + pub(super) fn push(&mut self, h: TapNodeHash) -> Result<(), TaprootBuilderError> { + if self.len() >= TAPROOT_CONTROL_MAX_NODE_COUNT { + Err(TaprootBuilderError::InvalidMerkleTreeDepth(self.0.len())) + } else { + self.0.push(h); + Ok(()) + } + } + + /// Returns the inner list of hashes. + #[deprecated(since = "TBD", note = "Use `into_vec` instead")] + pub fn into_inner(self) -> Vec { self.0 } + + /// Returns the list of hashes stored in a `Vec`. + pub fn into_vec(self) -> Vec { self.0 } +} + +macro_rules! impl_try_from { + ($from:ty) => { + impl TryFrom<$from> for TaprootMerkleBranch { + type Error = TaprootError; + + /// Creates a merkle proof from list of hashes. + /// + /// # Errors + /// If inner proof length is more than [`TAPROOT_CONTROL_MAX_NODE_COUNT`] (128). + fn try_from(v: $from) -> Result { + TaprootMerkleBranch::from_collection(v) + } + } + }; +} +impl_try_from!(&[TapNodeHash]); +impl_try_from!(Vec); +impl_try_from!(Box<[TapNodeHash]>); + +macro_rules! impl_try_from_array { + ($($len:expr),* $(,)?) => { + $( + impl From<[TapNodeHash; $len]> for TaprootMerkleBranch { + fn from(a: [TapNodeHash; $len]) -> Self { + Self(a.to_vec()) + } + } + )* + } +} +// Implement for all values [0, 128] inclusive. +// +// The reason zero is included is that `TaprootMerkleBranch` doesn't contain the hash of the node +// that's being proven - it's not needed because the script is already right before control block. +impl_try_from_array!( + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, + 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128 +); + +impl From for Vec { + fn from(branch: TaprootMerkleBranch) -> Self { branch.0 } +} + +impl IntoIterator for TaprootMerkleBranch { + type IntoIter = IntoIter; + type Item = TapNodeHash; + + fn into_iter(self) -> Self::IntoIter { + IntoIter(self.0.into_iter()) + } +} + +impl<'a> IntoIterator for &'a TaprootMerkleBranch { + type IntoIter = core::slice::Iter<'a, TapNodeHash>; + type Item = &'a TapNodeHash; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter() + } +} + +impl<'a> IntoIterator for &'a mut TaprootMerkleBranch { + type IntoIter = core::slice::IterMut<'a, TapNodeHash>; + type Item = &'a mut TapNodeHash; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter_mut() + } +} + +impl core::ops::Deref for TaprootMerkleBranch { + type Target = [TapNodeHash]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl core::ops::DerefMut for TaprootMerkleBranch { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl AsRef<[TapNodeHash]> for TaprootMerkleBranch { + fn as_ref(&self) -> &[TapNodeHash] { + &self.0 + } +} + +impl AsMut<[TapNodeHash]> for TaprootMerkleBranch { + fn as_mut(&mut self) -> &mut [TapNodeHash] { + &mut self.0 + } +} + +impl Borrow<[TapNodeHash]> for TaprootMerkleBranch { + fn borrow(&self) -> &[TapNodeHash] { + &self.0 + } +} + +impl BorrowMut<[TapNodeHash]> for TaprootMerkleBranch { + fn borrow_mut(&mut self) -> &mut [TapNodeHash] { + &mut self.0 + } +} + +/// Iterator over node hashes within Taproot merkle branch. +/// +/// This is created by `into_iter` method on `TaprootMerkleBranch` (via `IntoIterator` trait). +#[derive(Clone, Debug)] +pub struct IntoIter(alloc::vec::IntoIter); + +impl IntoIter { + /// Returns the remaining items of this iterator as a slice. + pub fn as_slice(&self) -> &[TapNodeHash] { + self.0.as_slice() + } + + /// Returns the remaining items of this iterator as a mutable slice. + pub fn as_mut_slice(&mut self) -> &mut [TapNodeHash] { + self.0.as_mut_slice() + } +} + +impl Iterator for IntoIter { + type Item = TapNodeHash; + + fn next(&mut self) -> Option { + self.0.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } + + fn nth(&mut self, n: usize) -> Option { + self.0.nth(n) + } + + fn last(self) -> Option { + self.0.last() + } + + fn count(self) -> usize { + self.0.count() + } +} + +impl DoubleEndedIterator for IntoIter { + fn next_back(&mut self) -> Option { + self.0.next_back() + } + + fn nth_back(&mut self, n: usize) -> Option { + self.0.nth_back(n) + } +} + +impl ExactSizeIterator for IntoIter {} + +impl core::iter::FusedIterator for IntoIter {} diff --git a/bitcoin/src/taproot/mod.rs b/bitcoin/src/taproot/mod.rs index 4ebf9b83..b223a019 100644 --- a/bitcoin/src/taproot/mod.rs +++ b/bitcoin/src/taproot/mod.rs @@ -6,6 +6,7 @@ //! pub mod serialized_signature; +pub mod merkle_branch; use core::cmp::Reverse; use core::fmt; @@ -24,6 +25,8 @@ use crate::{io, Script, ScriptBuf}; #[rustfmt::skip] #[doc(inline)] pub use crate::crypto::taproot::{SigFromSliceError, Signature}; +#[doc(inline)] +pub use merkle_branch::TaprootMerkleBranch; // Taproot test vectors from BIP-341 state the hashes without any reversing sha256t_hash_newtype! { @@ -306,7 +309,7 @@ impl TaprootSpendInfo { // Choose the smallest one amongst the multiple script maps let smallest = merkle_branch_set .iter() - .min_by(|x, y| x.0.len().cmp(&y.0.len())) + .min_by(|x, y| x.len().cmp(&y.len())) .expect("Invariant: ScriptBuf map key must contain non-empty set value"); Some(ControlBlock { internal_key: self.internal_key, @@ -963,19 +966,19 @@ pub struct LeafNode { impl LeafNode { /// Creates an new [`ScriptLeaf`] from `script` and `ver` and no merkle branch. pub fn new_script(script: ScriptBuf, ver: LeafVersion) -> Self { - Self { leaf: TapLeaf::Script(script, ver), merkle_branch: TaprootMerkleBranch(vec![]) } + Self { leaf: TapLeaf::Script(script, ver), merkle_branch: Default::default() } } /// Creates an new [`ScriptLeaf`] from `hash` and no merkle branch. pub fn new_hidden(hash: TapNodeHash) -> Self { - Self { leaf: TapLeaf::Hidden(hash), merkle_branch: TaprootMerkleBranch(vec![]) } + Self { leaf: TapLeaf::Hidden(hash), merkle_branch: Default::default() } } /// Returns the depth of this script leaf in the tap tree. #[inline] pub fn depth(&self) -> u8 { // Depth is guarded by TAPROOT_CONTROL_MAX_NODE_COUNT. - u8::try_from(self.merkle_branch().0.len()).expect("depth is guaranteed to fit in a u8") + u8::try_from(self.merkle_branch().len()).expect("depth is guaranteed to fit in a u8") } /// Computes a leaf hash for this [`ScriptLeaf`] if the leaf is known. @@ -1049,207 +1052,6 @@ impl<'leaf> ScriptLeaf<'leaf> { } } -/// The merkle proof for inclusion of a tree in a taptree hash. -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))] -#[cfg_attr(feature = "serde", serde(into = "Vec"))] -#[cfg_attr(feature = "serde", serde(try_from = "Vec"))] -pub struct TaprootMerkleBranch(Vec); - -impl TaprootMerkleBranch { - /// Returns a reference to the slice of hashes. - #[deprecated(since = "TBD", note = "Use `as_slice` instead")] - pub fn as_inner(&self) -> &[TapNodeHash] { &self.0 } - - /// Returns a reference to the slice of hashes. - pub fn as_slice(&self) -> &[TapNodeHash] { &self.0 } - - /// Returns the number of nodes in this merkle proof. - pub fn len(&self) -> usize { self.0.len() } - - /// Checks if this merkle proof is empty. - pub fn is_empty(&self) -> bool { self.0.is_empty() } - - /// Decodes bytes from control block. - /// - /// This reads the branch as encoded in the control block: the concatenated 32B byte chunks - - /// one for each hash. - /// - /// # Errors - /// - /// The function returns an error if the the number of bytes is not an integer multiple of 32 or - /// if the number of hashes exceeds 128. - pub fn decode(sl: &[u8]) -> Result { - if sl.len() % TAPROOT_CONTROL_NODE_SIZE != 0 { - Err(TaprootError::InvalidMerkleBranchSize(sl.len())) - } else if sl.len() > TAPROOT_CONTROL_NODE_SIZE * TAPROOT_CONTROL_MAX_NODE_COUNT { - Err(TaprootError::InvalidMerkleTreeDepth(sl.len() / TAPROOT_CONTROL_NODE_SIZE)) - } else { - let inner = sl - .chunks_exact(TAPROOT_CONTROL_NODE_SIZE) - .map(|chunk| { - TapNodeHash::from_slice(chunk) - .expect("chunks_exact always returns the correct size") - }) - .collect(); - - Ok(TaprootMerkleBranch(inner)) - } - } - - /// Creates a merkle proof from list of hashes. - /// - /// # Errors - /// If inner proof length is more than [`TAPROOT_CONTROL_MAX_NODE_COUNT`] (128). - fn from_collection + Into>>( - collection: T, - ) -> Result { - if collection.as_ref().len() > TAPROOT_CONTROL_MAX_NODE_COUNT { - Err(TaprootError::InvalidMerkleTreeDepth(collection.as_ref().len())) - } else { - Ok(TaprootMerkleBranch(collection.into())) - } - } - - /// Serializes to a writer. - /// - /// # Returns - /// - /// The number of bytes written to the writer. - pub fn encode(&self, writer: &mut Write) -> io::Result { - for hash in self { - writer.write_all(hash.as_ref())?; - } - Ok(self.len() * TapNodeHash::LEN) - } - - /// Serializes `self` as bytes. - pub fn serialize(&self) -> Vec { - self.iter().flat_map(|e| e.as_byte_array()).copied().collect::>() - } - - /// Appends elements to proof. - fn push(&mut self, h: TapNodeHash) -> Result<(), TaprootBuilderError> { - if self.len() >= TAPROOT_CONTROL_MAX_NODE_COUNT { - Err(TaprootBuilderError::InvalidMerkleTreeDepth(self.0.len())) - } else { - self.0.push(h); - Ok(()) - } - } - - /// Returns the inner list of hashes. - #[deprecated(since = "TBD", note = "Use `into_vec` instead")] - pub fn into_inner(self) -> Vec { self.0 } - - /// Returns the list of hashes stored in a `Vec`. - pub fn into_vec(self) -> Vec { self.0 } -} - -macro_rules! impl_try_from { - ($from:ty) => { - impl TryFrom<$from> for TaprootMerkleBranch { - type Error = TaprootError; - - /// Creates a merkle proof from list of hashes. - /// - /// # Errors - /// If inner proof length is more than [`TAPROOT_CONTROL_MAX_NODE_COUNT`] (128). - fn try_from(v: $from) -> Result { - TaprootMerkleBranch::from_collection(v) - } - } - }; -} -impl_try_from!(&[TapNodeHash]); -impl_try_from!(Vec); -impl_try_from!(Box<[TapNodeHash]>); - -macro_rules! impl_try_from_array { - ($($len:expr),* $(,)?) => { - $( - impl From<[TapNodeHash; $len]> for TaprootMerkleBranch { - fn from(a: [TapNodeHash; $len]) -> Self { - Self(a.to_vec()) - } - } - )* - } -} -// Implement for all values [0, 128] inclusive. -// -// The reason zero is included is that `TaprootMerkleBranch` doesn't contain the hash of the node -// that's being proven - it's not needed because the script is already right before control block. -impl_try_from_array!( - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, - 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, - 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, - 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, - 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, - 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128 -); - -impl From for Vec { - fn from(branch: TaprootMerkleBranch) -> Self { branch.0 } -} - -impl<'a> IntoIterator for &'a TaprootMerkleBranch { - type IntoIter = core::slice::Iter<'a, TapNodeHash>; - type Item = &'a TapNodeHash; - - fn into_iter(self) -> Self::IntoIter { - self.0.iter() - } -} - -impl<'a> IntoIterator for &'a mut TaprootMerkleBranch { - type IntoIter = core::slice::IterMut<'a, TapNodeHash>; - type Item = &'a mut TapNodeHash; - - fn into_iter(self) -> Self::IntoIter { - self.0.iter_mut() - } -} - -impl core::ops::Deref for TaprootMerkleBranch { - type Target = [TapNodeHash]; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl core::ops::DerefMut for TaprootMerkleBranch { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl AsRef<[TapNodeHash]> for TaprootMerkleBranch { - fn as_ref(&self) -> &[TapNodeHash] { - &self.0 - } -} - -impl AsMut<[TapNodeHash]> for TaprootMerkleBranch { - fn as_mut(&mut self) -> &mut [TapNodeHash] { - &mut self.0 - } -} - -impl Borrow<[TapNodeHash]> for TaprootMerkleBranch { - fn borrow(&self) -> &[TapNodeHash] { - &self.0 - } -} - -impl BorrowMut<[TapNodeHash]> for TaprootMerkleBranch { - fn borrow_mut(&mut self) -> &mut [TapNodeHash] { - &mut self.0 - } -} - /// Control block data structure used in Tapscript satisfaction. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -1826,7 +1628,6 @@ mod test { .iter() .next() .expect("Present Path") - .0 .len() ); } @@ -1940,7 +1741,7 @@ mod test { let hash1 = TapNodeHash::from_slice(&dummy_hash).unwrap(); let dummy_hash = hex!("8d79dedc2fa0b55167b5d28c61dbad9ce1191a433f3a1a6c8ee291631b2c94c9"); let hash2 = TapNodeHash::from_slice(&dummy_hash).unwrap(); - let merkle_branch = TaprootMerkleBranch::from_collection(vec![hash1, hash2]).unwrap(); + let merkle_branch = TaprootMerkleBranch::from([hash1, hash2]); // use serde_test to test serialization and deserialization serde_test::assert_tokens( &merkle_branch.readable(), From e1cc98986c4a6eaf53f179199aea6c3da10dcb7e Mon Sep 17 00:00:00 2001 From: Martin Habovstiak Date: Sat, 9 Dec 2023 00:35:18 +0100 Subject: [PATCH 5/5] Put `#[inline]` on trivial functions These functions either delegate to functions with identical signature or contain condition that may be optimized-out after inlining. --- bitcoin/src/taproot/merkle_branch.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/bitcoin/src/taproot/merkle_branch.rs b/bitcoin/src/taproot/merkle_branch.rs index 287c303a..83002d86 100644 --- a/bitcoin/src/taproot/merkle_branch.rs +++ b/bitcoin/src/taproot/merkle_branch.rs @@ -20,15 +20,19 @@ pub struct TaprootMerkleBranch(Vec); impl TaprootMerkleBranch { /// Returns a reference to the slice of hashes. #[deprecated(since = "TBD", note = "Use `as_slice` instead")] + #[inline] pub fn as_inner(&self) -> &[TapNodeHash] { &self.0 } /// Returns a reference to the slice of hashes. + #[inline] pub fn as_slice(&self) -> &[TapNodeHash] { &self.0 } /// Returns the number of nodes in this merkle proof. + #[inline] pub fn len(&self) -> usize { self.0.len() } /// Checks if this merkle proof is empty. + #[inline] pub fn is_empty(&self) -> bool { self.0.is_empty() } /// Decodes bytes from control block. @@ -62,6 +66,7 @@ impl TaprootMerkleBranch { /// /// # Errors /// If inner proof length is more than [`TAPROOT_CONTROL_MAX_NODE_COUNT`] (128). + #[inline] fn from_collection + Into>>( collection: T, ) -> Result { @@ -101,9 +106,11 @@ impl TaprootMerkleBranch { /// Returns the inner list of hashes. #[deprecated(since = "TBD", note = "Use `into_vec` instead")] + #[inline] pub fn into_inner(self) -> Vec { self.0 } /// Returns the list of hashes stored in a `Vec`. + #[inline] pub fn into_vec(self) -> Vec { self.0 } } @@ -116,6 +123,7 @@ macro_rules! impl_try_from { /// /// # Errors /// If inner proof length is more than [`TAPROOT_CONTROL_MAX_NODE_COUNT`] (128). + #[inline] fn try_from(v: $from) -> Result { TaprootMerkleBranch::from_collection(v) } @@ -130,6 +138,7 @@ macro_rules! impl_try_from_array { ($($len:expr),* $(,)?) => { $( impl From<[TapNodeHash; $len]> for TaprootMerkleBranch { + #[inline] fn from(a: [TapNodeHash; $len]) -> Self { Self(a.to_vec()) } @@ -151,6 +160,7 @@ impl_try_from_array!( ); impl From for Vec { + #[inline] fn from(branch: TaprootMerkleBranch) -> Self { branch.0 } } @@ -158,6 +168,7 @@ impl IntoIterator for TaprootMerkleBranch { type IntoIter = IntoIter; type Item = TapNodeHash; + #[inline] fn into_iter(self) -> Self::IntoIter { IntoIter(self.0.into_iter()) } @@ -167,6 +178,7 @@ impl<'a> IntoIterator for &'a TaprootMerkleBranch { type IntoIter = core::slice::Iter<'a, TapNodeHash>; type Item = &'a TapNodeHash; + #[inline] fn into_iter(self) -> Self::IntoIter { self.0.iter() } @@ -176,6 +188,7 @@ impl<'a> IntoIterator for &'a mut TaprootMerkleBranch { type IntoIter = core::slice::IterMut<'a, TapNodeHash>; type Item = &'a mut TapNodeHash; + #[inline] fn into_iter(self) -> Self::IntoIter { self.0.iter_mut() } @@ -184,36 +197,42 @@ impl<'a> IntoIterator for &'a mut TaprootMerkleBranch { impl core::ops::Deref for TaprootMerkleBranch { type Target = [TapNodeHash]; + #[inline] fn deref(&self) -> &Self::Target { &self.0 } } impl core::ops::DerefMut for TaprootMerkleBranch { + #[inline] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } impl AsRef<[TapNodeHash]> for TaprootMerkleBranch { + #[inline] fn as_ref(&self) -> &[TapNodeHash] { &self.0 } } impl AsMut<[TapNodeHash]> for TaprootMerkleBranch { + #[inline] fn as_mut(&mut self) -> &mut [TapNodeHash] { &mut self.0 } } impl Borrow<[TapNodeHash]> for TaprootMerkleBranch { + #[inline] fn borrow(&self) -> &[TapNodeHash] { &self.0 } } impl BorrowMut<[TapNodeHash]> for TaprootMerkleBranch { + #[inline] fn borrow_mut(&mut self) -> &mut [TapNodeHash] { &mut self.0 } @@ -227,11 +246,13 @@ pub struct IntoIter(alloc::vec::IntoIter); impl IntoIter { /// Returns the remaining items of this iterator as a slice. + #[inline] pub fn as_slice(&self) -> &[TapNodeHash] { self.0.as_slice() } /// Returns the remaining items of this iterator as a mutable slice. + #[inline] pub fn as_mut_slice(&mut self) -> &mut [TapNodeHash] { self.0.as_mut_slice() } @@ -240,32 +261,39 @@ impl IntoIter { impl Iterator for IntoIter { type Item = TapNodeHash; + #[inline] fn next(&mut self) -> Option { self.0.next() } + #[inline] fn size_hint(&self) -> (usize, Option) { self.0.size_hint() } + #[inline] fn nth(&mut self, n: usize) -> Option { self.0.nth(n) } + #[inline] fn last(self) -> Option { self.0.last() } + #[inline] fn count(self) -> usize { self.0.count() } } impl DoubleEndedIterator for IntoIter { + #[inline] fn next_back(&mut self) -> Option { self.0.next_back() } + #[inline] fn nth_back(&mut self, n: usize) -> Option { self.0.nth_back(n) }