diff --git a/bitcoind-tests/tests/test_desc.rs b/bitcoind-tests/tests/test_desc.rs index 99b93584b..30c54f169 100644 --- a/bitcoind-tests/tests/test_desc.rs +++ b/bitcoind-tests/tests/test_desc.rs @@ -185,10 +185,10 @@ pub fn test_desc_satisfy( } // ------------------ script spend ------------- let x_only_keypairs_reqd: Vec<(secp256k1::Keypair, TapLeafHash)> = tr - .iter_scripts() - .flat_map(|(_depth, ms)| { - let leaf_hash = TapLeafHash::from_script(&ms.encode(), LeafVersion::TapScript); - ms.iter_pk().filter_map(move |pk| { + .leaves() + .flat_map(|leaf| { + let leaf_hash = TapLeafHash::from_script(&leaf.compute_script(), LeafVersion::TapScript); + leaf.miniscript().iter_pk().filter_map(move |pk| { let i = x_only_pks.iter().position(|&x| x.to_public_key() == pk); i.map(|idx| (xonly_keypairs[idx], leaf_hash)) }) diff --git a/examples/taproot.rs b/examples/taproot.rs index 1e04f7b60..aea13f6ce 100644 --- a/examples/taproot.rs +++ b/examples/taproot.rs @@ -66,16 +66,18 @@ fn main() { assert_eq!(p.internal_key(), "Ca"); // Iterate through scripts - let mut iter = p.iter_scripts(); + let mut iter = p.leaves(); + let mut next = iter.next().unwrap(); assert_eq!( - iter.next().unwrap(), + (next.depth(), next.miniscript().as_ref()), ( 1u8, &Miniscript::::from_str("and_v(vc:pk_k(In),older(9))").unwrap() ) ); + next = iter.next().unwrap(); assert_eq!( - iter.next().unwrap(), + (next.depth(), next.miniscript().as_ref()), (1u8, &Miniscript::::from_str("and_v(v:pk(hA),pk(S))").unwrap()) ); assert_eq!(iter.next(), None); diff --git a/src/descriptor/mod.rs b/src/descriptor/mod.rs index d85834358..47da23b8b 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, Tr}; +pub use self::tr::{TapTree, TapTreeIter, TapTreeIterItem, Tr}; pub mod checksum; mod key; @@ -252,7 +252,7 @@ impl Descriptor { /// For a Taproot descriptor, returns the [`TapTree`] describing the Taproot tree. /// - /// To obtain the individual leaves of the tree, call [`TapTree::iter`] on the + /// To obtain the individual leaves of the tree, call [`TapTree::leaves`] on the /// returned value. pub fn tap_tree(&self) -> Option<&TapTree> { if let Descriptor::Tr(ref tr) = self { @@ -269,7 +269,7 @@ impl Descriptor { pub fn tap_tree_iter(&self) -> tr::TapTreeIter { if let Descriptor::Tr(ref tr) = self { if let Some(ref tree) = tr.tap_tree() { - return tree.iter(); + return tree.leaves(); } } tr::TapTreeIter::empty() @@ -988,8 +988,9 @@ 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 (_, ms) in inner.iter_scripts() { - ms.ext_check(&crate::miniscript::analyzable::ExtParams::sane())?; + for item in inner.iter_scripts() { + item.miniscript() + .ext_check(&crate::miniscript::analyzable::ExtParams::sane())?; } } Ok(ret) diff --git a/src/descriptor/tr.rs b/src/descriptor/tr/mod.rs similarity index 77% rename from src/descriptor/tr.rs rename to src/descriptor/tr/mod.rs index ab385c940..143376334 100644 --- a/src/descriptor/tr.rs +++ b/src/descriptor/tr/mod.rs @@ -26,26 +26,9 @@ use crate::{ Threshold, ToPublicKey, TranslateErr, Translator, }; -/// 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>), -} +mod taptree; + +pub use self::taptree::{TapTree, TapTreeIter, TapTreeIterItem}; /// A taproot descriptor pub struct Tr { @@ -111,64 +94,6 @@ impl hash::Hash for Tr { } } -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 } - } - - /// Returns the height of this tree. - pub fn height(&self) -> usize { - match *self { - TapTree::Tree { left: _, right: _, height } => height, - TapTree::Leaf(..) => 0, - } - } - - /// Iterates over all miniscripts in DFS walk order compatible with the - /// PSBT requirements (BIP 371). - pub fn iter(&self) -> TapTreeIter { TapTreeIter { stack: vec![(0, self)] } } - - // Helper function to translate keys - fn translate_helper(&self, t: &mut T) -> Result, TranslateErr> - where - T: 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) - } -} - -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), - } - } -} - -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), - } - } -} - impl Tr { /// Create a new [`Tr`] descriptor from internal key and [`TapTree`] pub fn new(internal_key: Pk, tree: Option>) -> Result { @@ -194,10 +119,17 @@ impl Tr { /// Iterate over all scripts in merkle tree. If there is no script path, the iterator /// yields [`None`] - pub fn iter_scripts(&self) -> TapTreeIter { + #[deprecated(since = "TBD", note = "use `leaves` instead")] + pub fn iter_scripts(&self) -> TapTreeIter { self.leaves() } + + /// Iterates over all the leaves of the tree in depth-first preorder. + /// + /// The yielded elements include the Miniscript for each leave as well as its depth + /// in the tree, which is the data required by PSBT (BIP 371). + pub fn leaves(&self) -> TapTreeIter { match self.tree { - Some(ref t) => t.iter(), - None => TapTreeIter { stack: vec![] }, + Some(ref t) => t.leaves(), + None => TapTreeIter::empty(), } } @@ -226,10 +158,10 @@ impl Tr { TaprootSpendInfo::new_key_spend(&secp, self.internal_key.to_x_only_pubkey(), None) } else { let mut builder = TaprootBuilder::new(); - for (depth, ms) in self.iter_scripts() { - let script = ms.encode(); + for leaf in self.leaves() { + let script = leaf.miniscript().encode(); builder = builder - .add_leaf(depth, script) + .add_leaf(leaf.depth(), script) .expect("Computing spend data on a valid Tree should always succeed"); } // Assert builder cannot error here because we have a well formed descriptor @@ -245,8 +177,8 @@ impl Tr { /// Checks whether the descriptor is safe. pub fn sanity_check(&self) -> Result<(), Error> { - for (_depth, ms) in self.iter_scripts() { - ms.sanity_check()?; + for leaf in self.leaves() { + leaf.miniscript().sanity_check()?; } Ok(()) } @@ -275,12 +207,12 @@ impl Tr { }; let wu = tree - .iter() - .filter_map(|(depth, ms)| { - let script_size = ms.script_size(); - let max_sat_elems = ms.max_satisfaction_witness_elements().ok()?; - let max_sat_size = ms.max_satisfaction_size().ok()?; - let control_block_size = control_block_len(depth); + .leaves() + .filter_map(|leaf| { + let script_size = leaf.miniscript().script_size(); + let max_sat_elems = leaf.miniscript().max_satisfaction_witness_elements().ok()?; + let max_sat_size = leaf.miniscript().max_satisfaction_size().ok()?; + let control_block_size = control_block_len(leaf.depth()); // stack varint difference (+1 for ctrl block, witness script already included) let stack_varint_diff = varint_len(max_sat_elems + 1) - varint_len(0); @@ -325,12 +257,12 @@ impl Tr { Some(tree) => tree, }; - tree.iter() - .filter_map(|(depth, ms)| { - let script_size = ms.script_size(); - let max_sat_elems = ms.max_satisfaction_witness_elements().ok()?; - let max_sat_size = ms.max_satisfaction_size().ok()?; - let control_block_size = control_block_len(depth); + tree.leaves() + .filter_map(|leaf| { + let script_size = leaf.miniscript().script_size(); + let max_sat_elems = leaf.miniscript().max_satisfaction_witness_elements().ok()?; + let max_sat_size = leaf.miniscript().max_satisfaction_size().ok()?; + let control_block_size = control_block_len(leaf.depth()); Some( // scriptSig len byte 4 + @@ -447,48 +379,6 @@ impl Tr { } } -/// Iterator for Taproot structures -/// Yields a pair of (depth, miniscript) in a depth first walk -/// For example, this tree: -/// - N0 - -/// / \\ -/// N1 N2 -/// / \ / \\ -/// A B C N3 -/// / \\ -/// D E -/// 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)>, -} - -impl TapTreeIter<'_, Pk> { - /// Helper function to return an empty iterator from Descriptor::tap_tree_iter. - pub(super) fn empty() -> Self { Self { stack: vec![] } } -} - -impl<'a, Pk> Iterator for TapTreeIter<'a, Pk> -where - Pk: MiniscriptKey + 'a, -{ - type Item = (u8, &'a Miniscript); - - 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((depth, ms)), - } - } - None - } -} - impl core::str::FromStr for Tr { type Err = Error; @@ -597,22 +487,6 @@ impl fmt::Display for Tr { } } -impl Liftable for TapTree { - fn lift(&self) -> Result, Error> { - fn lift_helper(s: &TapTree) -> Result, Error> { - match *s { - TapTree::Tree { ref left, ref right, height: _ } => Ok(Policy::Thresh( - Threshold::or(Arc::new(lift_helper(left)?), Arc::new(lift_helper(right)?)), - )), - TapTree::Leaf(ref leaf) => leaf.lift(), - } - } - - let pol = lift_helper(self)?; - Ok(pol.normalized()) - } -} - impl Liftable for Tr { fn lift(&self) -> Result, Error> { match &self.tree { @@ -628,8 +502,8 @@ impl Liftable for Tr { impl ForEachKey for Tr { fn for_each_key<'a, F: FnMut(&'a Pk) -> bool>(&'a self, mut pred: F) -> bool { let script_keys_res = self - .iter_scripts() - .all(|(_d, ms)| ms.for_each_key(&mut pred)); + .leaves() + .all(|leaf| leaf.miniscript().for_each_key(&mut pred)); script_keys_res && pred(&self.internal_key) } } @@ -673,14 +547,14 @@ where absolute_timelock: None, }; let mut min_wit_len = None; - for (_depth, ms) in desc.iter_scripts() { + for leaf in desc.leaves() { let mut satisfaction = if allow_mall { - match ms.build_template(provider) { + match leaf.miniscript().build_template(provider) { s @ Satisfaction { stack: Witness::Stack(_), .. } => s, _ => continue, // No witness for this script in tr descriptor, look for next one } } else { - match ms.build_template_mall(provider) { + match leaf.miniscript().build_template_mall(provider) { s @ Satisfaction { stack: Witness::Stack(_), .. } => s, _ => continue, // No witness for this script in tr descriptor, look for next one } @@ -690,7 +564,7 @@ where _ => unreachable!(), }; - let leaf_script = (ms.encode(), LeafVersion::TapScript); + let leaf_script = (leaf.compute_script(), LeafVersion::TapScript); let control_block = spend_info .control_block(&leaf_script) .expect("Control block must exist in script map for every known leaf"); diff --git a/src/descriptor/tr/taptree.rs b/src/descriptor/tr/taptree.rs new file mode 100644 index 000000000..c68033f54 --- /dev/null +++ b/src/descriptor/tr/taptree.rs @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: CC0-1.0 + +use core::{cmp, fmt}; + +use bitcoin::taproot::{LeafVersion, TapLeafHash}; + +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}; + +/// 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>), +} + +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 } + } + + /// Returns the height of this tree. + pub fn height(&self) -> usize { + match *self { + TapTree::Tree { left: _, right: _, height } => height, + TapTree::Leaf(..) => 0, + } + } + + /// Iterates over all the leaves of the tree in depth-first preorder. + /// + /// The yielded elements include the Miniscript for each leave as well as its depth + /// 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( + &self, + t: &mut T, + ) -> Result, TranslateErr> + where + T: 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) + } +} + +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 pol = lift_helper(self)?; + Ok(pol.normalized()) + } +} + +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), + } + } +} + +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), + } + } +} + +/// Iterator over the leaves of a Taptree. +/// +/// Yields a pair of (depth, miniscript) in a depth first walk +/// For example, this tree: +/// - N0 - +/// / \\ +/// N1 N2 +/// / \ / \\ +/// A B C N3 +/// / \\ +/// D E +/// 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)>, +} + +impl<'tr, Pk: MiniscriptKey> TapTreeIter<'tr, Pk> { + /// An empty iterator. + pub fn empty() -> Self { Self { stack: vec![] } } + + /// An iterator over a given tree. + fn from_tree(tree: &'tr TapTree) -> Self { Self { stack: vec![(0, tree)] } } +} + +impl<'a, Pk> Iterator for TapTreeIter<'a, Pk> +where + Pk: MiniscriptKey + 'a, +{ + type Item = TapTreeIterItem<'a, 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 + } +} + +/// Iterator over all of the leaves of a Taproot tree. +/// +/// If there is no tree (i.e. this is a keyspend-only Taproot descriptor) +/// then the iterator will yield nothing. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct TapTreeIterItem<'tr, Pk: MiniscriptKey> { + node: &'tr Arc>, + depth: u8, +} + +impl<'tr, Pk: MiniscriptKey> TapTreeIterItem<'tr, Pk> { + /// The Tapscript in the leaf. + /// + /// To obtain a [`bitcoin::Script`] from this node, call [`Miniscript::encode`] + /// on the returned value. + #[inline] + pub fn miniscript(&self) -> &'tr Arc> { self.node } + + /// The depth of this leaf. + /// + /// This is useful for reconstructing the shape of the tree. + #[inline] + pub fn depth(&self) -> u8 { self.depth } + + /// The Tapleaf version of this leaf. + /// + /// This function returns a constant value, since there is only one version in use + /// on the Bitcoin network; however, it may be useful to use this method in case + /// you wish to be forward-compatible with future versions supported by this + /// library. + #[inline] + pub fn leaf_version(&self) -> LeafVersion { LeafVersion::TapScript } +} + +impl TapTreeIterItem<'_, Pk> { + /// Computes the Bitcoin Script of the leaf. + /// + /// This function is potentially expensive. + #[inline] + pub fn compute_script(&self) -> bitcoin::ScriptBuf { self.node.encode() } + + /// Computes the [`TapLeafHash`] of the leaf. + /// + /// This function is potentially expensive, since it serializes the full Bitcoin + /// Script of the leaf and hashes this data. + #[inline] + pub fn compute_tap_leaf_hash(&self) -> TapLeafHash { + TapLeafHash::from_script(&self.compute_script(), self.leaf_version()) + } +} diff --git a/src/psbt/mod.rs b/src/psbt/mod.rs index 4eaf2f186..84924f42e 100644 --- a/src/psbt/mod.rs +++ b/src/psbt/mod.rs @@ -1112,14 +1112,12 @@ fn update_item_with_descriptor_helper( let mut builder = taproot::TaprootBuilder::new(); - for ((_depth_der, ms_derived), (depth, ms)) in - tr_derived.iter_scripts().zip(tr_xpk.iter_scripts()) - { - debug_assert_eq!(_depth_der, depth); - let leaf_script = (ms_derived.encode(), LeafVersion::TapScript); + for (leaf_derived, leaf) in tr_derived.leaves().zip(tr_xpk.leaves()) { + debug_assert_eq!(leaf_derived.depth(), leaf.depth()); + let leaf_script = (leaf_derived.compute_script(), leaf_derived.leaf_version()); let tapleaf_hash = TapLeafHash::from_script(&leaf_script.0, leaf_script.1); builder = builder - .add_leaf(depth, leaf_script.0.clone()) + .add_leaf(leaf_derived.depth(), leaf_script.0.clone()) .expect("Computing spend data on a valid tree should always succeed"); if let Some(tap_scripts) = item.tap_scripts() { let control_block = spend_info @@ -1128,7 +1126,11 @@ fn update_item_with_descriptor_helper( tap_scripts.insert(control_block, leaf_script); } - for (pk_pkh_derived, pk_pkh_xpk) in ms_derived.iter_pk().zip(ms.iter_pk()) { + for (pk_pkh_derived, pk_pkh_xpk) in leaf_derived + .miniscript() + .iter_pk() + .zip(leaf.miniscript().iter_pk()) + { let (xonly, xpk) = (pk_pkh_derived.to_x_only_pubkey(), pk_pkh_xpk); let xpk_full_derivation_path = xpk