diff --git a/src/descriptor/mod.rs b/src/descriptor/mod.rs index 47da23b8b..8bf75e1d5 100644 --- a/src/descriptor/mod.rs +++ b/src/descriptor/mod.rs @@ -42,7 +42,7 @@ pub use self::bare::{Bare, Pkh}; pub use self::segwitv0::{Wpkh, Wsh, WshInner}; pub use self::sh::{Sh, ShInner}; pub use self::sortedmulti::SortedMultiVec; -pub use self::tr::{TapTree, TapTreeIter, TapTreeIterItem, Tr}; +pub use self::tr::{TapTree, TapTreeDepthError, TapTreeIter, TapTreeIterItem, Tr}; pub mod checksum; mod key; @@ -256,7 +256,7 @@ impl Descriptor { /// returned value. pub fn tap_tree(&self) -> Option<&TapTree> { if let Descriptor::Tr(ref tr) = self { - tr.tap_tree().as_ref() + tr.tap_tree() } else { None } @@ -268,7 +268,7 @@ impl Descriptor { /// Taproot descriptor containing only a keyspend, returns an empty iterator. pub fn tap_tree_iter(&self) -> tr::TapTreeIter { if let Descriptor::Tr(ref tr) = self { - if let Some(ref tree) = tr.tap_tree() { + if let Some(tree) = tr.tap_tree() { return tree.leaves(); } } @@ -988,7 +988,7 @@ impl FromStr for Descriptor { // FIXME preserve weird/broken behavior from 12.x. // See https://github.com/rust-bitcoin/rust-miniscript/issues/734 ret.sanity_check()?; - for item in inner.iter_scripts() { + for item in inner.leaves() { item.miniscript() .ext_check(&crate::miniscript::analyzable::ExtParams::sane())?; } diff --git a/src/descriptor/tr/mod.rs b/src/descriptor/tr/mod.rs index 143376334..c2cadc04a 100644 --- a/src/descriptor/tr/mod.rs +++ b/src/descriptor/tr/mod.rs @@ -6,7 +6,7 @@ use core::{cmp, fmt, hash}; use bitcoin::secp256k1; use bitcoin::taproot::{ LeafVersion, TaprootBuilder, TaprootSpendInfo, TAPROOT_CONTROL_BASE_SIZE, - TAPROOT_CONTROL_MAX_NODE_COUNT, TAPROOT_CONTROL_NODE_SIZE, + TAPROOT_CONTROL_NODE_SIZE, }; use bitcoin::{opcodes, Address, Network, ScriptBuf, Weight}; use sync::Arc; @@ -28,7 +28,7 @@ use crate::{ mod taptree; -pub use self::taptree::{TapTree, TapTreeIter, TapTreeIterItem}; +pub use self::taptree::{TapTree, TapTreeDepthError, TapTreeIter, TapTreeIterItem}; /// A taproot descriptor pub struct Tr { @@ -98,29 +98,14 @@ impl Tr { /// Create a new [`Tr`] descriptor from internal key and [`TapTree`] pub fn new(internal_key: Pk, tree: Option>) -> Result { Tap::check_pk(&internal_key)?; - let nodes = tree.as_ref().map(|t| t.height()).unwrap_or(0); - - if nodes <= TAPROOT_CONTROL_MAX_NODE_COUNT { - Ok(Self { internal_key, tree, spend_info: Mutex::new(None) }) - } else { - Err(Error::MaxRecursiveDepthExceeded) - } + Ok(Self { internal_key, tree, spend_info: Mutex::new(None) }) } /// Obtain the internal key of [`Tr`] descriptor pub fn internal_key(&self) -> &Pk { &self.internal_key } /// Obtain the [`TapTree`] of the [`Tr`] descriptor - pub fn tap_tree(&self) -> &Option> { &self.tree } - - /// Obtain the [`TapTree`] of the [`Tr`] descriptor - #[deprecated(since = "11.0.0", note = "use tap_tree instead")] - pub fn taptree(&self) -> &Option> { self.tap_tree() } - - /// Iterate over all scripts in merkle tree. If there is no script path, the iterator - /// yields [`None`] - #[deprecated(since = "TBD", note = "use `leaves` instead")] - pub fn iter_scripts(&self) -> TapTreeIter { self.leaves() } + pub fn tap_tree(&self) -> Option<&TapTree> { self.tree.as_ref() } /// Iterates over all the leaves of the tree in depth-first preorder. /// @@ -291,7 +276,7 @@ impl Tr { T: Translator, { let tree = match &self.tree { - Some(tree) => Some(tree.translate_helper(translate)?), + Some(tree) => Some(tree.translate_pk(translate)?), None => None, }; let translate_desc = @@ -392,33 +377,6 @@ impl crate::expression::FromTree for Tr { fn from_tree(root: expression::TreeIterItem) -> Result { use crate::expression::{Parens, ParseTreeError}; - struct TreeStack<'s, Pk: MiniscriptKey> { - inner: Vec<(expression::TreeIterItem<'s>, TapTree)>, - } - - impl<'s, Pk: MiniscriptKey> TreeStack<'s, Pk> { - fn new() -> Self { Self { inner: Vec::with_capacity(128) } } - - fn push(&mut self, parent: expression::TreeIterItem<'s>, tree: TapTree) { - let mut next_push = (parent, tree); - while let Some(top) = self.inner.pop() { - if next_push.0.index() == top.0.index() { - next_push.0 = top.0.parent().unwrap(); - next_push.1 = TapTree::combine(top.1, next_push.1); - } else { - self.inner.push(top); - break; - } - } - self.inner.push(next_push); - } - - fn pop_final(&mut self) -> Option> { - assert_eq!(self.inner.len(), 1); - self.inner.pop().map(|x| x.1) - } - } - root.verify_toplevel("tr", 1..=2) .map_err(From::from) .map_err(Error::Parse)?; @@ -435,7 +393,7 @@ impl crate::expression::FromTree for Tr { Some(tree) => tree, }; - let mut tree_stack = TreeStack::new(); + let mut tree_builder = taptree::TapTreeBuilder::new(); let mut tap_tree_iter = tap_tree.pre_order_iter(); // while let construction needed because we modify the iterator inside the loop // (by calling skip_descendants to skip over the contents of the tapscripts). @@ -450,6 +408,7 @@ impl crate::expression::FromTree for Tr { node.verify_n_children("taptree branch", 2..=2) .map_err(From::from) .map_err(Error::Parse)?; + tree_builder.push_inner_node()?; } else { let script = Miniscript::from_tree(node)?; // FIXME hack for https://github.com/rust-bitcoin/rust-miniscript/issues/734 @@ -457,11 +416,11 @@ impl crate::expression::FromTree for Tr { return Err(Error::NonTopLevel(format!("{:?}", script))); }; - tree_stack.push(node.parent().unwrap(), TapTree::Leaf(Arc::new(script))); + tree_builder.push_leaf(script); tap_tree_iter.skip_descendants(); } } - Tr::new(internal_key, tree_stack.pop_final()) + Tr::new(internal_key, Some(tree_builder.finalize())) } } @@ -616,9 +575,18 @@ mod tests { } #[test] - fn height() { - let desc = descriptor(); - let tr = Tr::::from_str(&desc).unwrap(); - assert_eq!(tr.tap_tree().as_ref().unwrap().height(), 2); + fn tr_maximum_depth() { + // Copied from integration tests + let descriptor128 = "tr(X!,{pk(X1!),{pk(X2!),{pk(X3!),{pk(X4!),{pk(X5!),{pk(X6!),{pk(X7!),{pk(X8!),{pk(X9!),{pk(X10!),{pk(X11!),{pk(X12!),{pk(X13!),{pk(X14!),{pk(X15!),{pk(X16!),{pk(X17!),{pk(X18!),{pk(X19!),{pk(X20!),{pk(X21!),{pk(X22!),{pk(X23!),{pk(X24!),{pk(X25!),{pk(X26!),{pk(X27!),{pk(X28!),{pk(X29!),{pk(X30!),{pk(X31!),{pk(X32!),{pk(X33!),{pk(X34!),{pk(X35!),{pk(X36!),{pk(X37!),{pk(X38!),{pk(X39!),{pk(X40!),{pk(X41!),{pk(X42!),{pk(X43!),{pk(X44!),{pk(X45!),{pk(X46!),{pk(X47!),{pk(X48!),{pk(X49!),{pk(X50!),{pk(X51!),{pk(X52!),{pk(X53!),{pk(X54!),{pk(X55!),{pk(X56!),{pk(X57!),{pk(X58!),{pk(X59!),{pk(X60!),{pk(X61!),{pk(X62!),{pk(X63!),{pk(X64!),{pk(X65!),{pk(X66!),{pk(X67!),{pk(X68!),{pk(X69!),{pk(X70!),{pk(X71!),{pk(X72!),{pk(X73!),{pk(X74!),{pk(X75!),{pk(X76!),{pk(X77!),{pk(X78!),{pk(X79!),{pk(X80!),{pk(X81!),{pk(X82!),{pk(X83!),{pk(X84!),{pk(X85!),{pk(X86!),{pk(X87!),{pk(X88!),{pk(X89!),{pk(X90!),{pk(X91!),{pk(X92!),{pk(X93!),{pk(X94!),{pk(X95!),{pk(X96!),{pk(X97!),{pk(X98!),{pk(X99!),{pk(X100!),{pk(X101!),{pk(X102!),{pk(X103!),{pk(X104!),{pk(X105!),{pk(X106!),{pk(X107!),{pk(X108!),{pk(X109!),{pk(X110!),{pk(X111!),{pk(X112!),{pk(X113!),{pk(X114!),{pk(X115!),{pk(X116!),{pk(X117!),{pk(X118!),{pk(X119!),{pk(X120!),{pk(X121!),{pk(X122!),{pk(X123!),{pk(X124!),{pk(X125!),{pk(X126!),{pk(X127!),{pk(X128!),pk(X129)}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}})"; + descriptor128.parse::>().unwrap(); + + // Copied from integration tests + let descriptor129 = "tr(X!,{pk(X1!),{pk(X2!),{pk(X3!),{pk(X4!),{pk(X5!),{pk(X6!),{pk(X7!),{pk(X8!),{pk(X9!),{pk(X10!),{pk(X11!),{pk(X12!),{pk(X13!),{pk(X14!),{pk(X15!),{pk(X16!),{pk(X17!),{pk(X18!),{pk(X19!),{pk(X20!),{pk(X21!),{pk(X22!),{pk(X23!),{pk(X24!),{pk(X25!),{pk(X26!),{pk(X27!),{pk(X28!),{pk(X29!),{pk(X30!),{pk(X31!),{pk(X32!),{pk(X33!),{pk(X34!),{pk(X35!),{pk(X36!),{pk(X37!),{pk(X38!),{pk(X39!),{pk(X40!),{pk(X41!),{pk(X42!),{pk(X43!),{pk(X44!),{pk(X45!),{pk(X46!),{pk(X47!),{pk(X48!),{pk(X49!),{pk(X50!),{pk(X51!),{pk(X52!),{pk(X53!),{pk(X54!),{pk(X55!),{pk(X56!),{pk(X57!),{pk(X58!),{pk(X59!),{pk(X60!),{pk(X61!),{pk(X62!),{pk(X63!),{pk(X64!),{pk(X65!),{pk(X66!),{pk(X67!),{pk(X68!),{pk(X69!),{pk(X70!),{pk(X71!),{pk(X72!),{pk(X73!),{pk(X74!),{pk(X75!),{pk(X76!),{pk(X77!),{pk(X78!),{pk(X79!),{pk(X80!),{pk(X81!),{pk(X82!),{pk(X83!),{pk(X84!),{pk(X85!),{pk(X86!),{pk(X87!),{pk(X88!),{pk(X89!),{pk(X90!),{pk(X91!),{pk(X92!),{pk(X93!),{pk(X94!),{pk(X95!),{pk(X96!),{pk(X97!),{pk(X98!),{pk(X99!),{pk(X100!),{pk(X101!),{pk(X102!),{pk(X103!),{pk(X104!),{pk(X105!),{pk(X106!),{pk(X107!),{pk(X108!),{pk(X109!),{pk(X110!),{pk(X111!),{pk(X112!),{pk(X113!),{pk(X114!),{pk(X115!),{pk(X116!),{pk(X117!),{pk(X118!),{pk(X119!),{pk(X120!),{pk(X121!),{pk(X122!),{pk(X123!),{pk(X124!),{pk(X125!),{pk(X126!),{pk(X127!),{pk(X128!),{pk(X129),pk(X130)}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}})"; + assert!(matches!( + descriptor129 + .parse::>() + .unwrap_err(), + crate::Error::TapTreeDepthError(TapTreeDepthError), + )); } } diff --git a/src/descriptor/tr/taptree.rs b/src/descriptor/tr/taptree.rs index c68033f54..431c1ccab 100644 --- a/src/descriptor/tr/taptree.rs +++ b/src/descriptor/tr/taptree.rs @@ -1,49 +1,52 @@ // SPDX-License-Identifier: CC0-1.0 -use core::{cmp, fmt}; +use core::fmt; -use bitcoin::taproot::{LeafVersion, TapLeafHash}; +use bitcoin::taproot::{LeafVersion, TapLeafHash, TAPROOT_CONTROL_MAX_NODE_COUNT}; use crate::miniscript::context::Tap; use crate::policy::{Liftable, Semantic}; use crate::prelude::Vec; use crate::sync::Arc; -use crate::{Miniscript, MiniscriptKey, Threshold, ToPublicKey, TranslateErr, Translator}; +use crate::{Miniscript, MiniscriptKey, Threshold, ToPublicKey}; + +/// Tried to construct Taproot tree which was too deep. +#[derive(PartialEq, Eq, Debug)] +#[non_exhaustive] +pub struct TapTreeDepthError; + +impl fmt::Display for TapTreeDepthError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("maximum Taproot tree depth (128) exceeded") + } +} + +#[cfg(feature = "std")] +impl std::error::Error for TapTreeDepthError {} /// A Taproot Tree representation. -// Hidden leaves are not yet supported in descriptor spec. Conceptually, it should -// be simple to integrate those here, but it is best to wait on core for the exact syntax. -#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] -pub enum TapTree { - /// A taproot tree structure - Tree { - /// Left tree branch. - left: Arc>, - /// Right tree branch. - right: Arc>, - /// Tree height, defined as `1 + max(left_height, right_height)`. - height: usize, - }, - /// A taproot leaf denoting a spending condition - // A new leaf version would require a new Context, therefore there is no point - // in adding a LeafVersion with Leaf type here. All Miniscripts right now - // are of Leafversion::default - Leaf(Arc>), +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct TapTree { + depths_leaves: Vec<(u8, Arc>)>, } impl TapTree { - /// Creates a `TapTree` by combining `left` and `right` tree nodes. - pub fn combine(left: TapTree, right: TapTree) -> Self { - let height = 1 + cmp::max(left.height(), right.height()); - TapTree::Tree { left: Arc::new(left), right: Arc::new(right), height } + /// Creates a `TapTree` leaf from a Miniscript. + pub fn leaf>>>(ms: A) -> Self { + TapTree { depths_leaves: vec![(0, ms.into())] } } - /// Returns the height of this tree. - pub fn height(&self) -> usize { - match *self { - TapTree::Tree { left: _, right: _, height } => height, - TapTree::Leaf(..) => 0, + /// Creates a `TapTree` by combining `left` and `right` tree nodes. + pub fn combine(left: TapTree, right: TapTree) -> Result { + let mut depths_leaves = + Vec::with_capacity(left.depths_leaves.len() + right.depths_leaves.len()); + for (depth, leaf) in left.depths_leaves.iter().chain(right.depths_leaves.iter()) { + if usize::from(*depth) > TAPROOT_CONTROL_MAX_NODE_COUNT - 1 { + return Err(TapTreeDepthError); + } + depths_leaves.push((*depth + 1, Arc::clone(leaf))); } + Ok(Self { depths_leaves }) } /// Iterates over all the leaves of the tree in depth-first preorder. @@ -52,61 +55,73 @@ impl TapTree { /// in the tree, which is the data required by PSBT (BIP 371). pub fn leaves(&self) -> TapTreeIter { TapTreeIter::from_tree(self) } - // Helper function to translate keys - pub(super) fn translate_helper( + /// Converts keys from one type of public key to another. + pub fn translate_pk( &self, - t: &mut T, - ) -> Result, TranslateErr> + translate: &mut T, + ) -> Result, crate::TranslateErr> where - T: Translator, + T: crate::Translator, { - let frag = match *self { - TapTree::Tree { ref left, ref right, ref height } => TapTree::Tree { - left: Arc::new(left.translate_helper(t)?), - right: Arc::new(right.translate_helper(t)?), - height: *height, - }, - TapTree::Leaf(ref ms) => TapTree::Leaf(Arc::new(ms.translate_pk(t)?)), - }; - Ok(frag) + let mut ret = TapTree { depths_leaves: Vec::with_capacity(self.depths_leaves.len()) }; + for (depth, leaf) in &self.depths_leaves { + ret.depths_leaves + .push((*depth, Arc::new(leaf.translate_pk(translate)?))); + } + + Ok(ret) } } impl Liftable for TapTree { fn lift(&self) -> Result, crate::Error> { - fn lift_helper(s: &TapTree) -> Result, crate::Error> { - match *s { - TapTree::Tree { ref left, ref right, height: _ } => Ok(Semantic::Thresh( - Threshold::or(Arc::new(lift_helper(left)?), Arc::new(lift_helper(right)?)), - )), - TapTree::Leaf(ref leaf) => leaf.lift(), - } + let thresh_vec = self + .leaves() + .map(|item| item.miniscript().lift().map(Arc::new)) + .collect::, _>>()?; + let thresh = Threshold::new(1, thresh_vec).expect("no size limit on Semantic threshold"); + Ok(Semantic::Thresh(thresh).normalized()) + } +} + +fn fmt_helper( + view: &TapTree, + f: &mut fmt::Formatter, + mut fmt_ms: impl FnMut(&mut fmt::Formatter, &Miniscript) -> fmt::Result, +) -> fmt::Result { + let mut last_depth = 0; + for item in view.leaves() { + if last_depth > 0 { + f.write_str(",")?; + } + + while last_depth < item.depth() { + f.write_str("{")?; + last_depth += 1; } + fmt_ms(f, item.miniscript())?; + while last_depth > item.depth() { + f.write_str("}")?; + last_depth -= 1; + } + } - let pol = lift_helper(self)?; - Ok(pol.normalized()) + while last_depth > 0 { + f.write_str("}")?; + last_depth -= 1; } + Ok(()) } impl fmt::Display for TapTree { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - TapTree::Tree { ref left, ref right, height: _ } => { - write!(f, "{{{},{}}}", *left, *right) - } - TapTree::Leaf(ref script) => write!(f, "{}", *script), - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt_helper(self, f, |f, ms| write!(f, "{}", ms)) } } impl fmt::Debug for TapTree { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - TapTree::Tree { ref left, ref right, height: _ } => { - write!(f, "{{{:?},{:?}}}", *left, *right) - } - TapTree::Leaf(ref script) => write!(f, "{:?}", *script), - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt_helper(self, f, |f, ms| write!(f, "{:?}", ms)) } } @@ -124,38 +139,42 @@ impl fmt::Debug for TapTree { /// would yield (2, A), (2, B), (2,C), (3, D), (3, E). /// #[derive(Debug, Clone)] -pub struct TapTreeIter<'a, Pk: MiniscriptKey> { - stack: Vec<(u8, &'a TapTree)>, +pub struct TapTreeIter<'tr, Pk: MiniscriptKey> { + inner: core::slice::Iter<'tr, (u8, Arc>)>, } impl<'tr, Pk: MiniscriptKey> TapTreeIter<'tr, Pk> { /// An empty iterator. - pub fn empty() -> Self { Self { stack: vec![] } } + pub fn empty() -> Self { Self { inner: [].iter() } } /// An iterator over a given tree. - fn from_tree(tree: &'tr TapTree) -> Self { Self { stack: vec![(0, tree)] } } + fn from_tree(tree: &'tr TapTree) -> Self { Self { inner: tree.depths_leaves.iter() } } } -impl<'a, Pk> Iterator for TapTreeIter<'a, Pk> -where - Pk: MiniscriptKey + 'a, -{ - type Item = TapTreeIterItem<'a, Pk>; +impl<'tr, Pk: MiniscriptKey> Iterator for TapTreeIter<'tr, Pk> { + type Item = TapTreeIterItem<'tr, Pk>; fn next(&mut self) -> Option { - while let Some((depth, last)) = self.stack.pop() { - match *last { - TapTree::Tree { ref left, ref right, height: _ } => { - self.stack.push((depth + 1, right)); - self.stack.push((depth + 1, left)); - } - TapTree::Leaf(ref ms) => return Some(TapTreeIterItem { node: ms, depth }), - } - } - None + self.inner + .next() + .map(|&(depth, ref node)| TapTreeIterItem { depth, node }) + } +} + +impl<'tr, Pk: MiniscriptKey> DoubleEndedIterator for TapTreeIter<'tr, Pk> { + fn next_back(&mut self) -> Option { + self.inner + .next_back() + .map(|&(depth, ref node)| TapTreeIterItem { depth, node }) } } +impl<'tr, Pk: MiniscriptKey> ExactSizeIterator for TapTreeIter<'tr, Pk> { + fn len(&self) -> usize { self.inner.len() } +} + +impl<'tr, Pk: MiniscriptKey> core::iter::FusedIterator for TapTreeIter<'tr, Pk> {} + /// Iterator over all of the leaves of a Taproot tree. /// /// If there is no tree (i.e. this is a keyspend-only Taproot descriptor) @@ -206,3 +225,58 @@ impl TapTreeIterItem<'_, Pk> { TapLeafHash::from_script(&self.compute_script(), self.leaf_version()) } } + +pub(super) struct TapTreeBuilder { + depths_leaves: Vec<(u8, Arc>)>, + complete_heights: u128, // ArrayVec represented as a bitmap...and a bool. + complete_128: bool, // BIP341 says depths are in [0,128] *inclusive* so 129 possibilities. + current_height: u8, +} + +impl TapTreeBuilder { + pub(super) fn new() -> Self { + Self { + depths_leaves: vec![], + complete_heights: 0, + complete_128: false, + current_height: 0, + } + } + + #[inline] + pub(super) fn push_inner_node(&mut self) -> Result<(), TapTreeDepthError> { + self.current_height += 1; + if usize::from(self.current_height) > TAPROOT_CONTROL_MAX_NODE_COUNT { + return Err(TapTreeDepthError); + } + Ok(()) + } + + #[inline] + pub(super) fn push_leaf>>>(&mut self, ms: A) { + self.depths_leaves.push((self.current_height, ms.into())); + + // Special-case 128 which doesn't fit into the `complete_heights` bitmap + if usize::from(self.current_height) == TAPROOT_CONTROL_MAX_NODE_COUNT { + if self.complete_128 { + self.complete_128 = false; + self.current_height -= 1; + } else { + self.complete_128 = true; + return; + } + } + // Then deal with all other nonzero heights + while self.current_height > 0 { + if self.complete_heights & (1 << self.current_height) == 0 { + self.complete_heights |= 1 << self.current_height; + break; + } + self.complete_heights &= !(1 << self.current_height); + self.current_height -= 1; + } + } + + #[inline] + pub(super) fn finalize(self) -> TapTree { TapTree { depths_leaves: self.depths_leaves } } +} diff --git a/src/lib.rs b/src/lib.rs index 3b0c0d3bf..fe6490d8c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -450,6 +450,8 @@ pub enum Error { LiftError(policy::LiftError), /// Forward script context related errors ContextError(miniscript::context::ScriptContextError), + /// Tried to construct a Taproot tree which was too deep. + TapTreeDepthError(crate::descriptor::TapTreeDepthError), /// Recursion depth exceeded when parsing policy/miniscript from string MaxRecursiveDepthExceeded, /// Anything but c:pk(key) (P2PK), c:pk_h(key) (P2PKH), and thresh_m(k,...) @@ -509,6 +511,7 @@ impl fmt::Display for Error { Error::TypeCheck(ref e) => write!(f, "typecheck: {}", e), Error::Secp(ref e) => fmt::Display::fmt(e, f), Error::ContextError(ref e) => fmt::Display::fmt(e, f), + Error::TapTreeDepthError(ref e) => fmt::Display::fmt(e, f), #[cfg(feature = "compiler")] Error::CompilerError(ref e) => fmt::Display::fmt(e, f), Error::ConcretePolicy(ref e) => fmt::Display::fmt(e, f), @@ -573,6 +576,7 @@ impl std::error::Error for Error { ConcretePolicy(e) => Some(e), LiftError(e) => Some(e), ContextError(e) => Some(e), + TapTreeDepthError(e) => Some(e), AnalysisError(e) => Some(e), PubKeyCtxError(e, _) => Some(e), AbsoluteLockTime(e) => Some(e), @@ -594,6 +598,11 @@ impl From for Error { fn from(e: policy::LiftError) -> Error { Error::LiftError(e) } } +#[doc(hidden)] +impl From for Error { + fn from(e: crate::descriptor::TapTreeDepthError) -> Error { Error::TapTreeDepthError(e) } +} + #[doc(hidden)] impl From for Error { fn from(e: miniscript::context::ScriptContextError) -> Error { Error::ContextError(e) } diff --git a/src/policy/concrete.rs b/src/policy/concrete.rs index 2fe0748b8..ccd666e80 100644 --- a/src/policy/concrete.rs +++ b/src/policy/concrete.rs @@ -589,7 +589,7 @@ impl Policy { .collect() } - /// Gets the number of [TapLeaf](`TapTree::Leaf`)s considering exhaustive root-level [`Policy::Or`] + /// Gets the number of [TapLeaf](`TapTree::leaf`)s considering exhaustive root-level [`Policy::Or`] /// and [`Policy::Thresh`] disjunctions for the `TapTree`. #[cfg(feature = "compiler")] fn num_tap_leaves(&self) -> usize { self.tapleaf_probability_iter().count() } @@ -957,7 +957,7 @@ impl expression::FromTree for Policy { fn with_huffman_tree(ms: Vec<(OrdF64, Miniscript)>) -> TapTree { let mut node_weights = BinaryHeap::<(Reverse, TapTree)>::new(); for (prob, script) in ms { - node_weights.push((Reverse(prob), TapTree::Leaf(Arc::new(script)))); + node_weights.push((Reverse(prob), TapTree::leaf(script))); } assert_ne!(node_weights.len(), 0, "empty Miniscript compilation"); while node_weights.len() > 1 { @@ -965,7 +965,11 @@ fn with_huffman_tree(ms: Vec<(OrdF64, Miniscript)>) let (p2, s2) = node_weights.pop().expect("len must at least be two"); let p = (p1.0).0 + (p2.0).0; - node_weights.push((Reverse(OrdF64(p)), TapTree::combine(s1, s2))); + node_weights.push(( + Reverse(OrdF64(p)), + TapTree::combine(s1, s2) + .expect("huffman tree cannot produce depth > 128 given sane weights"), + )); } debug_assert!(node_weights.len() == 1); diff --git a/src/policy/mod.rs b/src/policy/mod.rs index 0b2d0c10a..64dd380e8 100644 --- a/src/policy/mod.rs +++ b/src/policy/mod.rs @@ -375,7 +375,7 @@ mod tests { let descriptor = policy.compile_tr(Some(unspendable_key.clone())).unwrap(); let ms_compilation: Miniscript = ms_str!("multi_a(2,A,B,C,D)"); - let tree: TapTree = TapTree::Leaf(Arc::new(ms_compilation)); + let tree: TapTree = TapTree::leaf(ms_compilation); let expected_descriptor = Descriptor::new_tr(unspendable_key.clone(), Some(tree)).unwrap(); assert_eq!(descriptor, expected_descriptor); @@ -386,14 +386,12 @@ mod tests { let policy: Concrete = policy_str!("or(and(pk(A),pk(B)),and(pk(C),pk(D)))"); let descriptor = policy.compile_tr(Some(unspendable_key.clone())).unwrap(); - let left_ms_compilation: Arc> = - Arc::new(ms_str!("and_v(v:pk(C),pk(D))")); - let right_ms_compilation: Arc> = - Arc::new(ms_str!("and_v(v:pk(A),pk(B))")); + let left_ms_compilation: Miniscript = ms_str!("and_v(v:pk(C),pk(D))"); + let right_ms_compilation: Miniscript = ms_str!("and_v(v:pk(A),pk(B))"); - let left = TapTree::Leaf(left_ms_compilation); - let right = TapTree::Leaf(right_ms_compilation); - let tree = TapTree::combine(left, right); + let left = TapTree::leaf(left_ms_compilation); + let right = TapTree::leaf(right_ms_compilation); + let tree = TapTree::combine(left, right).unwrap(); let expected_descriptor = Descriptor::new_tr(unspendable_key.clone(), Some(tree)).unwrap(); @@ -454,24 +452,29 @@ mod tests { .into_iter() .map(|x| { let leaf_policy: Concrete = policy_str!("{}", x); - TapTree::Leaf(Arc::from(leaf_policy.compile::().unwrap())) + TapTree::leaf(leaf_policy.compile::().unwrap()) }) .collect::>(); // Arrange leaf compilations (acc. to probabilities) using huffman encoding into a TapTree let tree = TapTree::combine( - TapTree::combine(node_compilations[4].clone(), node_compilations[5].clone()), + TapTree::combine(node_compilations[4].clone(), node_compilations[5].clone()) + .unwrap(), TapTree::combine( TapTree::combine( TapTree::combine( node_compilations[0].clone(), node_compilations[1].clone(), - ), + ) + .unwrap(), node_compilations[3].clone(), - ), + ) + .unwrap(), node_compilations[6].clone(), - ), - ); + ) + .unwrap(), + ) + .unwrap(); let expected_descriptor = Descriptor::new_tr("E".to_string(), Some(tree)).unwrap(); assert_eq!(descriptor, expected_descriptor);