Skip to content

Commit 552e844

Browse files
committed
tr: flatten TapTree type
This replaces the internal representation of `TapTree` with one that matches the BIP371 iterator: it's a vector of depths and leaves and nothing else. It does not track its maximum height (except during construction to return an error if 128 is exceeded); it does not track "internal nodes"; it does not offer an API to iterate through its "internal nodes" except by calling Liftable::lift to get a Semantic copy of the tree. This greatly simplifies the type and many algorithms associated with it. It automatically eliminates recursion from display, lifting, dropping, and so on. As it turns out, we never use TapTrees except to iterate over the leaves, and there is no data about internal nodes that we can even compute, let alone store, so there's no reason to have nodes for them. In later commit/PR we will create a second TapTree-like type, which can be constructed from a TapTree for ToPublicKey keys, and can be used to extract Merkle paths and control blocks. But without ToPublicKey we can't compute these, so TapTree is not the type for that.
1 parent 5abc70c commit 552e844

File tree

3 files changed

+77
-102
lines changed

3 files changed

+77
-102
lines changed

src/descriptor/tr/mod.rs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ impl<Pk: MiniscriptKey> Tr<Pk> {
276276
T: Translator<Pk>,
277277
{
278278
let tree = match &self.tree {
279-
Some(tree) => Some(tree.translate_helper(translate)?),
279+
Some(tree) => Some(tree.translate_pk(translate)?),
280280
None => None,
281281
};
282282
let translate_desc =
@@ -604,11 +604,4 @@ mod tests {
604604
// Note the last ac12 only has ac and fails the predicate
605605
assert!(!tr.for_each_key(|k| k.starts_with("acc")));
606606
}
607-
608-
#[test]
609-
fn height() {
610-
let desc = descriptor();
611-
let tr = Tr::<String>::from_str(&desc).unwrap();
612-
assert_eq!(tr.tap_tree().as_ref().unwrap().height(), 2);
613-
}
614607
}

src/descriptor/tr/taptree.rs

Lines changed: 74 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
// SPDX-License-Identifier: CC0-1.0
22

3-
use core::{cmp, fmt};
3+
use core::fmt;
44

55
use bitcoin::taproot::{LeafVersion, TapLeafHash, TAPROOT_CONTROL_MAX_NODE_COUNT};
66

77
use crate::miniscript::context::Tap;
88
use crate::policy::{Liftable, Semantic};
99
use crate::prelude::Vec;
1010
use crate::sync::Arc;
11-
use crate::{Miniscript, MiniscriptKey, Threshold, ToPublicKey, TranslateErr, Translator};
11+
use crate::{Miniscript, MiniscriptKey, Threshold, ToPublicKey};
1212

1313
/// Tried to construct Taproot tree which was too deep.
1414
#[derive(PartialEq, Eq, Debug)]
@@ -25,46 +25,28 @@ impl fmt::Display for TapTreeDepthError {
2525
impl std::error::Error for TapTreeDepthError {}
2626

2727
/// A Taproot Tree representation.
28-
// Hidden leaves are not yet supported in descriptor spec. Conceptually, it should
29-
// be simple to integrate those here, but it is best to wait on core for the exact syntax.
30-
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
31-
pub enum TapTree<Pk: MiniscriptKey> {
32-
/// A taproot tree structure
33-
Tree {
34-
/// Left tree branch.
35-
left: Arc<TapTree<Pk>>,
36-
/// Right tree branch.
37-
right: Arc<TapTree<Pk>>,
38-
/// Tree height, defined as `1 + max(left_height, right_height)`.
39-
height: usize,
40-
},
41-
/// A taproot leaf denoting a spending condition
42-
// A new leaf version would require a new Context, therefore there is no point
43-
// in adding a LeafVersion with Leaf type here. All Miniscripts right now
44-
// are of Leafversion::default
45-
Leaf(Arc<Miniscript<Pk, Tap>>),
28+
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
29+
pub struct TapTree<Pk: MiniscriptKey> {
30+
depths_leaves: Vec<(u8, Arc<Miniscript<Pk, Tap>>)>,
4631
}
4732

4833
impl<Pk: MiniscriptKey> TapTree<Pk> {
4934
/// Creates a `TapTree` leaf from a Miniscript.
50-
pub fn leaf<A: Into<Arc<Miniscript<Pk, Tap>>>>(ms: A) -> Self { TapTree::Leaf(ms.into()) }
35+
pub fn leaf<A: Into<Arc<Miniscript<Pk, Tap>>>>(ms: A) -> Self {
36+
TapTree { depths_leaves: vec![(0, ms.into())] }
37+
}
5138

5239
/// Creates a `TapTree` by combining `left` and `right` tree nodes.
5340
pub fn combine(left: TapTree<Pk>, right: TapTree<Pk>) -> Result<Self, TapTreeDepthError> {
54-
let height = 1 + cmp::max(left.height(), right.height());
55-
if height <= TAPROOT_CONTROL_MAX_NODE_COUNT {
56-
Ok(TapTree::Tree { left: Arc::new(left), right: Arc::new(right), height })
57-
} else {
58-
Err(TapTreeDepthError)
59-
}
60-
}
61-
62-
/// Returns the height of this tree.
63-
pub fn height(&self) -> usize {
64-
match *self {
65-
TapTree::Tree { left: _, right: _, height } => height,
66-
TapTree::Leaf(..) => 0,
41+
let mut depths_leaves =
42+
Vec::with_capacity(left.depths_leaves.len() + right.depths_leaves.len());
43+
for (depth, leaf) in left.depths_leaves.iter().chain(right.depths_leaves.iter()) {
44+
if usize::from(*depth) > TAPROOT_CONTROL_MAX_NODE_COUNT - 1 {
45+
return Err(TapTreeDepthError);
46+
}
47+
depths_leaves.push((*depth + 1, Arc::clone(leaf)));
6748
}
49+
Ok(Self { depths_leaves })
6850
}
6951

7052
/// Iterates over all the leaves of the tree in depth-first preorder.
@@ -73,61 +55,73 @@ impl<Pk: MiniscriptKey> TapTree<Pk> {
7355
/// in the tree, which is the data required by PSBT (BIP 371).
7456
pub fn leaves(&self) -> TapTreeIter<Pk> { TapTreeIter::from_tree(self) }
7557

76-
// Helper function to translate keys
77-
pub(super) fn translate_helper<T>(
58+
/// Converts keys from one type of public key to another.
59+
pub fn translate_pk<T>(
7860
&self,
79-
t: &mut T,
80-
) -> Result<TapTree<T::TargetPk>, TranslateErr<T::Error>>
61+
translate: &mut T,
62+
) -> Result<TapTree<T::TargetPk>, crate::TranslateErr<T::Error>>
8163
where
82-
T: Translator<Pk>,
64+
T: crate::Translator<Pk>,
8365
{
84-
let frag = match *self {
85-
TapTree::Tree { ref left, ref right, ref height } => TapTree::Tree {
86-
left: Arc::new(left.translate_helper(t)?),
87-
right: Arc::new(right.translate_helper(t)?),
88-
height: *height,
89-
},
90-
TapTree::Leaf(ref ms) => TapTree::Leaf(Arc::new(ms.translate_pk(t)?)),
91-
};
92-
Ok(frag)
66+
let mut ret = TapTree { depths_leaves: Vec::with_capacity(self.depths_leaves.len()) };
67+
for (depth, leaf) in &self.depths_leaves {
68+
ret.depths_leaves
69+
.push((*depth, Arc::new(leaf.translate_pk(translate)?)));
70+
}
71+
72+
Ok(ret)
9373
}
9474
}
9575

9676
impl<Pk: MiniscriptKey> Liftable<Pk> for TapTree<Pk> {
9777
fn lift(&self) -> Result<Semantic<Pk>, crate::Error> {
98-
fn lift_helper<Pk: MiniscriptKey>(s: &TapTree<Pk>) -> Result<Semantic<Pk>, crate::Error> {
99-
match *s {
100-
TapTree::Tree { ref left, ref right, height: _ } => Ok(Semantic::Thresh(
101-
Threshold::or(Arc::new(lift_helper(left)?), Arc::new(lift_helper(right)?)),
102-
)),
103-
TapTree::Leaf(ref leaf) => leaf.lift(),
104-
}
78+
let thresh_vec = self
79+
.leaves()
80+
.map(|item| item.miniscript().lift().map(Arc::new))
81+
.collect::<Result<Vec<_>, _>>()?;
82+
let thresh = Threshold::new(1, thresh_vec).expect("no size limit on Semantic threshold");
83+
Ok(Semantic::Thresh(thresh).normalized())
84+
}
85+
}
86+
87+
fn fmt_helper<Pk: MiniscriptKey>(
88+
view: &TapTree<Pk>,
89+
f: &mut fmt::Formatter,
90+
mut fmt_ms: impl FnMut(&mut fmt::Formatter, &Miniscript<Pk, Tap>) -> fmt::Result,
91+
) -> fmt::Result {
92+
let mut last_depth = 0;
93+
for item in view.leaves() {
94+
if last_depth > 0 {
95+
f.write_str(",")?;
10596
}
10697

107-
let pol = lift_helper(self)?;
108-
Ok(pol.normalized())
98+
while last_depth < item.depth() {
99+
f.write_str("{")?;
100+
last_depth += 1;
101+
}
102+
fmt_ms(f, item.miniscript())?;
103+
while last_depth > item.depth() {
104+
f.write_str("}")?;
105+
last_depth -= 1;
106+
}
107+
}
108+
109+
while last_depth > 0 {
110+
f.write_str("}")?;
111+
last_depth -= 1;
109112
}
113+
Ok(())
110114
}
111115

112116
impl<Pk: MiniscriptKey> fmt::Display for TapTree<Pk> {
113-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114-
match self {
115-
TapTree::Tree { ref left, ref right, height: _ } => {
116-
write!(f, "{{{},{}}}", *left, *right)
117-
}
118-
TapTree::Leaf(ref script) => write!(f, "{}", *script),
119-
}
117+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
118+
fmt_helper(self, f, |f, ms| write!(f, "{}", ms))
120119
}
121120
}
122121

123122
impl<Pk: MiniscriptKey> fmt::Debug for TapTree<Pk> {
124-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125-
match self {
126-
TapTree::Tree { ref left, ref right, height: _ } => {
127-
write!(f, "{{{:?},{:?}}}", *left, *right)
128-
}
129-
TapTree::Leaf(ref script) => write!(f, "{:?}", *script),
130-
}
123+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
124+
fmt_helper(self, f, |f, ms| write!(f, "{:?}", ms))
131125
}
132126
}
133127

@@ -145,35 +139,25 @@ impl<Pk: MiniscriptKey> fmt::Debug for TapTree<Pk> {
145139
/// would yield (2, A), (2, B), (2,C), (3, D), (3, E).
146140
///
147141
#[derive(Debug, Clone)]
148-
pub struct TapTreeIter<'a, Pk: MiniscriptKey> {
149-
stack: Vec<(u8, &'a TapTree<Pk>)>,
142+
pub struct TapTreeIter<'tr, Pk: MiniscriptKey> {
143+
inner: core::slice::Iter<'tr, (u8, Arc<Miniscript<Pk, Tap>>)>,
150144
}
151145

152146
impl<'tr, Pk: MiniscriptKey> TapTreeIter<'tr, Pk> {
153147
/// An empty iterator.
154-
pub fn empty() -> Self { Self { stack: vec![] } }
148+
pub fn empty() -> Self { Self { inner: [].iter() } }
155149

156150
/// An iterator over a given tree.
157-
fn from_tree(tree: &'tr TapTree<Pk>) -> Self { Self { stack: vec![(0, tree)] } }
151+
fn from_tree(tree: &'tr TapTree<Pk>) -> Self { Self { inner: tree.depths_leaves.iter() } }
158152
}
159153

160-
impl<'a, Pk> Iterator for TapTreeIter<'a, Pk>
161-
where
162-
Pk: MiniscriptKey + 'a,
163-
{
164-
type Item = TapTreeIterItem<'a, Pk>;
154+
impl<'tr, Pk: MiniscriptKey> Iterator for TapTreeIter<'tr, Pk> {
155+
type Item = TapTreeIterItem<'tr, Pk>;
165156

166157
fn next(&mut self) -> Option<Self::Item> {
167-
while let Some((depth, last)) = self.stack.pop() {
168-
match *last {
169-
TapTree::Tree { ref left, ref right, height: _ } => {
170-
self.stack.push((depth + 1, right));
171-
self.stack.push((depth + 1, left));
172-
}
173-
TapTree::Leaf(ref ms) => return Some(TapTreeIterItem { node: ms, depth }),
174-
}
175-
}
176-
None
158+
self.inner
159+
.next()
160+
.map(|&(depth, ref node)| TapTreeIterItem { depth, node })
177161
}
178162
}
179163

src/policy/mod.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -386,10 +386,8 @@ mod tests {
386386
let policy: Concrete<String> = policy_str!("or(and(pk(A),pk(B)),and(pk(C),pk(D)))");
387387
let descriptor = policy.compile_tr(Some(unspendable_key.clone())).unwrap();
388388

389-
let left_ms_compilation: Arc<Miniscript<String, Tap>> =
390-
Arc::new(ms_str!("and_v(v:pk(C),pk(D))"));
391-
let right_ms_compilation: Arc<Miniscript<String, Tap>> =
392-
Arc::new(ms_str!("and_v(v:pk(A),pk(B))"));
389+
let left_ms_compilation: Miniscript<String, Tap> = ms_str!("and_v(v:pk(C),pk(D))");
390+
let right_ms_compilation: Miniscript<String, Tap> = ms_str!("and_v(v:pk(A),pk(B))");
393391

394392
let left = TapTree::leaf(left_ms_compilation);
395393
let right = TapTree::leaf(right_ms_compilation);

0 commit comments

Comments
 (0)