diff --git a/bitcoin/src/psbt/map/mod.rs b/bitcoin/src/psbt/map/mod.rs index ebc1c236..4a2a3d94 100644 --- a/bitcoin/src/psbt/map/mod.rs +++ b/bitcoin/src/psbt/map/mod.rs @@ -9,7 +9,7 @@ mod input; mod output; pub use self::input::{Input, PsbtSighashType}; -pub use self::output::{Output, TapTree, IncompleteTapTree}; +pub use self::output::Output; use super::serialize::Serialize; diff --git a/bitcoin/src/psbt/map/output.rs b/bitcoin/src/psbt/map/output.rs index 6389da0d..4621bc79 100644 --- a/bitcoin/src/psbt/map/output.rs +++ b/bitcoin/src/psbt/map/output.rs @@ -12,7 +12,7 @@ use crate::psbt::map::Map; use crate::psbt::raw; use crate::psbt::Error; -use crate::taproot::{ScriptLeaf, TapLeafHash, NodeInfo, TaprootBuilder}; +use crate::taproot::{TapTree, TapLeafHash}; /// Type: Redeem ScriptBuf PSBT_OUT_REDEEM_SCRIPT = 0x00 const PSBT_OUT_REDEEM_SCRIPT: u8 = 0x00; @@ -64,145 +64,6 @@ pub struct Output { pub unknown: BTreeMap>, } -/// Error happening when [`TapTree`] is constructed from a [`TaprootBuilder`] -/// having hidden branches or not being finalized. -#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] -#[non_exhaustive] -pub enum IncompleteTapTree { - /// Indicates an attempt to construct a tap tree from a builder containing incomplete branches. - NotFinalized(TaprootBuilder), - /// Indicates an attempt to construct a tap tree from a builder containing hidden parts. - HiddenParts(TaprootBuilder), -} - -impl IncompleteTapTree { - /// Converts error into the original incomplete [`TaprootBuilder`] instance. - pub fn into_builder(self) -> TaprootBuilder { - match self { - IncompleteTapTree::NotFinalized(builder) | - IncompleteTapTree::HiddenParts(builder) => builder - } - } -} - -impl core::fmt::Display for IncompleteTapTree { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.write_str(match self { - IncompleteTapTree::NotFinalized(_) => "an attempt to construct a tap tree from a builder containing incomplete branches.", - IncompleteTapTree::HiddenParts(_) => "an attempt to construct a tap tree from a builder containing hidden parts.", - }) - } -} - -#[cfg(feature = "std")] -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] -impl std::error::Error for IncompleteTapTree { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - use self::IncompleteTapTree::*; - - match self { - NotFinalized(_) | HiddenParts(_) => None, - } - } -} - -/// Taproot Tree representing a finalized [`TaprootBuilder`] (a complete binary tree). -#[derive(Clone, Debug)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))] -pub struct TapTree(pub(crate) TaprootBuilder); - -impl PartialEq for TapTree { - fn eq(&self, other: &Self) -> bool { - self.node_info().hash.eq(&other.node_info().hash) - } -} - -impl core::hash::Hash for TapTree { - fn hash(&self, state: &mut H) { - self.node_info().hash(state) - } -} - -impl Eq for TapTree {} - -impl From for TaprootBuilder { - #[inline] - fn from(tree: TapTree) -> Self { - tree.into_builder() - } -} - -impl TapTree { - /// Gets the inner node info as the builder is finalized. - pub fn node_info(&self) -> &NodeInfo { - // The builder algorithm invariant guarantees that is_complete builder - // have only 1 element in branch and that is not None. - // We make sure that we only allow is_complete builders via the from_inner - // constructor - self.0.branch()[0].as_ref().expect("from_inner only parses is_complete builders") - } - - /// Converts self into builder [`TaprootBuilder`]. The builder is guaranteed to be finalized. - pub fn into_builder(self) -> TaprootBuilder { - self.0 - } - - /// Constructs [`TaprootBuilder`] by internally cloning the `self`. The builder is guaranteed - /// to be finalized. - pub fn to_builder(&self) -> TaprootBuilder { - self.0.clone() - } - - /// Returns [`TapTreeIter<'_>`] iterator for a taproot script tree, operating in DFS order over - /// tree [`ScriptLeaf`]s. - pub fn script_leaves(&self) -> TapTreeIter { - match (self.0.branch().len(), self.0.branch().last()) { - (1, Some(Some(root))) => { - TapTreeIter { - leaf_iter: root.leaves.iter() - } - } - // This should be unreachable as we Taptree is already finalized - _ => unreachable!("non-finalized tree builder inside TapTree"), - } - } -} - -impl TryFrom for TapTree { - type Error = IncompleteTapTree; - - /// Constructs [`TapTree`] from a [`TaprootBuilder`] if it is complete binary tree. - /// - /// # Returns - /// A [`TapTree`] iff the `builder` is complete, otherwise return [`IncompleteTapTree`] - /// error with the content of incomplete `builder` instance. - fn try_from(builder: TaprootBuilder) -> Result { - if !builder.is_finalizable() { - Err(IncompleteTapTree::NotFinalized(builder)) - } else if builder.has_hidden_nodes() { - Err(IncompleteTapTree::HiddenParts(builder)) - } else { - Ok(TapTree(builder)) - } - } -} - -/// Iterator for a taproot script tree, operating in DFS order over leaf depth and -/// leaf script pairs. -pub struct TapTreeIter<'tree> { - leaf_iter: core::slice::Iter<'tree, ScriptLeaf>, -} - -impl<'tree> Iterator for TapTreeIter<'tree> { - type Item = &'tree ScriptLeaf; - - #[inline] - fn next(&mut self) -> Option { - self.leaf_iter.next() - } -} - impl Output { pub(super) fn insert_pair(&mut self, pair: raw::Pair) -> Result<(), Error> { let raw::Pair { diff --git a/bitcoin/src/psbt/mod.rs b/bitcoin/src/psbt/mod.rs index d2fba494..e2bf4f54 100644 --- a/bitcoin/src/psbt/mod.rs +++ b/bitcoin/src/psbt/mod.rs @@ -35,7 +35,7 @@ mod error; pub use self::error::Error; mod map; -pub use self::map::{Input, Output, TapTree, PsbtSighashType, IncompleteTapTree}; +pub use self::map::{Input, Output, PsbtSighashType}; /// Partially signed transaction, commonly referred to as a PSBT. pub type Psbt = PartiallySignedTransaction; diff --git a/bitcoin/src/psbt/serialize.rs b/bitcoin/src/psbt/serialize.rs index 0c1b568c..566e34b2 100644 --- a/bitcoin/src/psbt/serialize.rs +++ b/bitcoin/src/psbt/serialize.rs @@ -16,6 +16,7 @@ use crate::blockdata::script::ScriptBuf; use crate::blockdata::witness::Witness; use crate::blockdata::transaction::{Transaction, TxOut}; use crate::consensus::encode::{self, serialize, Decodable, Encodable, deserialize_partial}; +use crate::taproot::TapTree; use secp256k1::{self, XOnlyPublicKey}; use crate::bip32::{ChildNumber, Fingerprint, KeySource}; use crate::hashes::{hash160, ripemd160, sha256, sha256d, Hash}; @@ -24,7 +25,7 @@ use crate::psbt::{Error, PartiallySignedTransaction}; use crate::taproot::{TapNodeHash, TapLeafHash, ControlBlock, LeafVersion}; use crate::crypto::key::PublicKey; -use super::map::{Map, Input, Output, TapTree, PsbtSighashType}; +use super::map::{Map, Input, Output, PsbtSighashType}; use super::Psbt; use crate::taproot::TaprootBuilder; diff --git a/bitcoin/src/taproot.rs b/bitcoin/src/taproot.rs index e16a41b2..c432fca1 100644 --- a/bitcoin/src/taproot.rs +++ b/bitcoin/src/taproot.rs @@ -378,7 +378,7 @@ impl TaprootBuilder { /// If the script weight calculations overflow, a sub-optimal tree may be generated. This should /// not happen unless you are dealing with billions of branches with weights close to 2^32. /// - /// [`TapTree`]: crate::psbt::TapTree + /// [`TapTree`]: crate::taproot::TapTree pub fn with_huffman_tree(script_weights: I) -> Result where I: IntoIterator, @@ -521,6 +521,132 @@ impl Default for TaprootBuilder { fn default() -> Self { Self::new() } } +/// Error happening when [`TapTree`] is constructed from a [`TaprootBuilder`] +/// having hidden branches or not being finalized. +#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] +#[non_exhaustive] +pub enum IncompleteTapTree { + /// Indicates an attempt to construct a tap tree from a builder containing incomplete branches. + NotFinalized(TaprootBuilder), + /// Indicates an attempt to construct a tap tree from a builder containing hidden parts. + HiddenParts(TaprootBuilder), +} + +impl IncompleteTapTree { + /// Converts error into the original incomplete [`TaprootBuilder`] instance. + pub fn into_builder(self) -> TaprootBuilder { + match self { + IncompleteTapTree::NotFinalized(builder) | IncompleteTapTree::HiddenParts(builder) => + builder, + } + } +} + +impl core::fmt::Display for IncompleteTapTree { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str(match self { + IncompleteTapTree::NotFinalized(_) => + "an attempt to construct a tap tree from a builder containing incomplete branches.", + IncompleteTapTree::HiddenParts(_) => + "an attempt to construct a tap tree from a builder containing hidden parts.", + }) + } +} + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl std::error::Error for IncompleteTapTree { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use self::IncompleteTapTree::*; + + match self { + NotFinalized(_) | HiddenParts(_) => None, + } + } +} + +/// Taproot Tree representing a finalized [`TaprootBuilder`] (a complete binary tree). +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))] +pub struct TapTree(pub(crate) TaprootBuilder); + +impl PartialEq for TapTree { + fn eq(&self, other: &Self) -> bool { self.node_info().hash.eq(&other.node_info().hash) } +} + +impl core::hash::Hash for TapTree { + fn hash(&self, state: &mut H) { self.node_info().hash(state) } +} + +impl Eq for TapTree {} + +impl From for TaprootBuilder { + #[inline] + fn from(tree: TapTree) -> Self { tree.into_builder() } +} + +impl TapTree { + /// Gets the inner node info as the builder is finalized. + pub fn node_info(&self) -> &NodeInfo { + // The builder algorithm invariant guarantees that is_complete builder + // have only 1 element in branch and that is not None. + // We make sure that we only allow is_complete builders via the from_inner + // constructor + self.0.branch()[0].as_ref().expect("from_inner only parses is_complete builders") + } + + /// Converts self into builder [`TaprootBuilder`]. The builder is guaranteed to be finalized. + pub fn into_builder(self) -> TaprootBuilder { self.0 } + + /// Constructs [`TaprootBuilder`] by internally cloning the `self`. The builder is guaranteed + /// to be finalized. + pub fn to_builder(&self) -> TaprootBuilder { self.0.clone() } + + /// Returns [`TapTreeIter<'_>`] iterator for a taproot script tree, operating in DFS order over + /// tree [`ScriptLeaf`]s. + pub fn script_leaves(&self) -> TapTreeIter { + match (self.0.branch().len(), self.0.branch().last()) { + (1, Some(Some(root))) => TapTreeIter { leaf_iter: root.leaves.iter() }, + // This should be unreachable as we Taptree is already finalized + _ => unreachable!("non-finalized tree builder inside TapTree"), + } + } +} + +impl TryFrom for TapTree { + type Error = IncompleteTapTree; + + /// Constructs [`TapTree`] from a [`TaprootBuilder`] if it is complete binary tree. + /// + /// # Returns + /// + /// A [`TapTree`] iff the `builder` is complete, otherwise return [`IncompleteBuilder`] + /// error with the content of incomplete `builder` instance. + fn try_from(builder: TaprootBuilder) -> Result { + if !builder.is_finalizable() { + Err(IncompleteTapTree::NotFinalized(builder)) + } else if builder.has_hidden_nodes() { + Err(IncompleteTapTree::HiddenParts(builder)) + } else { + Ok(TapTree(builder)) + } + } +} + +/// Iterator for a taproot script tree, operating in DFS order over leaf depth and +/// leaf script pairs. +pub struct TapTreeIter<'tree> { + leaf_iter: core::slice::Iter<'tree, ScriptLeaf>, +} + +impl<'tree> Iterator for TapTreeIter<'tree> { + type Item = &'tree ScriptLeaf; + + #[inline] + fn next(&mut self) -> Option { self.leaf_iter.next() } +} + /// Represents the node information in taproot tree. /// /// Helper type used in merkle tree construction allowing one to build sparse merkle trees. The node