Skip to content

Commit 476e358

Browse files
committed
Add height to tap tree
Currently calculating the height of a `TapTree::Tree` uses a recursive call, we can optimize this out by storing the height along with the tree nodes. Convert the `TapTree::Tree` tuple struct to a struct with named fields. Include a `height` field in the new struct.
1 parent fe6e8ad commit 476e358

File tree

3 files changed

+81
-47
lines changed

3 files changed

+81
-47
lines changed

src/descriptor/tr.rs

+63-25
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
// SPDX-License-Identifier: CC0-1.0
22

3-
use core::cmp::{self, max};
43
use core::str::FromStr;
5-
use core::{fmt, hash};
4+
use core::{cmp, fmt, hash};
65

76
use bitcoin::taproot::{
87
LeafVersion, TaprootBuilder, TaprootSpendInfo, TAPROOT_CONTROL_BASE_SIZE,
@@ -29,7 +28,14 @@ use crate::{
2928
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
3029
pub enum TapTree<Pk: MiniscriptKey> {
3130
/// A taproot tree structure
32-
Tree(Arc<TapTree<Pk>>, Arc<TapTree<Pk>>),
31+
Tree {
32+
/// Left tree branch.
33+
left: Arc<TapTree<Pk>>,
34+
/// Right tree branch.
35+
right: Arc<TapTree<Pk>>,
36+
/// Tree height, defined as `1 + max(left_height, right_height)`.
37+
height: usize,
38+
},
3339
/// A taproot leaf denoting a spending condition
3440
// A new leaf version would require a new Context, therefore there is no point
3541
// in adding a LeafVersion with Leaf type here. All Miniscripts right now
@@ -108,14 +114,24 @@ impl<Pk: MiniscriptKey> hash::Hash for Tr<Pk> {
108114
}
109115

110116
impl<Pk: MiniscriptKey> TapTree<Pk> {
111-
// Helper function to compute height
112-
// TODO: Instead of computing this every time we add a new leaf, we should
113-
// add height as a separate field in taptree
117+
/// Creates a `TapTree` by combining `left` and `right` tree nodes.
118+
pub(crate) fn combine(left: TapTree<Pk>, right: TapTree<Pk>) -> Self {
119+
let height = 1 + cmp::max(left.height(), right.height());
120+
TapTree::Tree {
121+
left: Arc::new(left),
122+
right: Arc::new(right),
123+
height,
124+
}
125+
}
126+
127+
/// Returns the height of this tree.
114128
fn height(&self) -> usize {
115129
match *self {
116-
TapTree::Tree(ref left_tree, ref right_tree) => {
117-
1 + max(left_tree.height(), right_tree.height())
118-
}
130+
TapTree::Tree {
131+
left: _,
132+
right: _,
133+
height,
134+
} => height,
119135
TapTree::Leaf(..) => 0,
120136
}
121137
}
@@ -134,12 +150,17 @@ impl<Pk: MiniscriptKey> TapTree<Pk> {
134150
T: Translator<Pk, Q, E>,
135151
Q: MiniscriptKey,
136152
{
137-
let frag = match self {
138-
TapTree::Tree(l, r) => TapTree::Tree(
139-
Arc::new(l.translate_helper(t)?),
140-
Arc::new(r.translate_helper(t)?),
141-
),
142-
TapTree::Leaf(ms) => TapTree::Leaf(Arc::new(ms.translate_pk(t)?)),
153+
let frag = match *self {
154+
TapTree::Tree {
155+
ref left,
156+
ref right,
157+
ref height,
158+
} => TapTree::Tree {
159+
left: Arc::new(left.translate_helper(t)?),
160+
right: Arc::new(right.translate_helper(t)?),
161+
height: *height,
162+
},
163+
TapTree::Leaf(ref ms) => TapTree::Leaf(Arc::new(ms.translate_pk(t)?)),
143164
};
144165
Ok(frag)
145166
}
@@ -148,7 +169,11 @@ impl<Pk: MiniscriptKey> TapTree<Pk> {
148169
impl<Pk: MiniscriptKey> fmt::Display for TapTree<Pk> {
149170
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150171
match self {
151-
TapTree::Tree(ref left, ref right) => write!(f, "{{{},{}}}", *left, *right),
172+
TapTree::Tree {
173+
ref left,
174+
ref right,
175+
height: _,
176+
} => write!(f, "{{{},{}}}", *left, *right),
152177
TapTree::Leaf(ref script) => write!(f, "{}", *script),
153178
}
154179
}
@@ -157,7 +182,11 @@ impl<Pk: MiniscriptKey> fmt::Display for TapTree<Pk> {
157182
impl<Pk: MiniscriptKey> fmt::Debug for TapTree<Pk> {
158183
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159184
match self {
160-
TapTree::Tree(ref left, ref right) => write!(f, "{{{:?},{:?}}}", *left, *right),
185+
TapTree::Tree {
186+
ref left,
187+
ref right,
188+
height: _,
189+
} => write!(f, "{{{:?},{:?}}}", *left, *right),
161190
TapTree::Leaf(ref script) => write!(f, "{:?}", *script),
162191
}
163192
}
@@ -413,9 +442,13 @@ where
413442
fn next(&mut self) -> Option<Self::Item> {
414443
while let Some((depth, last)) = self.stack.pop() {
415444
match *last {
416-
TapTree::Tree(ref l, ref r) => {
417-
self.stack.push((depth + 1, r));
418-
self.stack.push((depth + 1, l));
445+
TapTree::Tree {
446+
ref left,
447+
ref right,
448+
height: _,
449+
} => {
450+
self.stack.push((depth + 1, right));
451+
self.stack.push((depth + 1, left));
419452
}
420453
TapTree::Leaf(ref ms) => return Some((depth, ms)),
421454
}
@@ -437,7 +470,7 @@ impl_block_str!(
437470
expression::Tree { name, args } if name.is_empty() && args.len() == 2 => {
438471
let left = Self::parse_tr_script_spend(&args[0])?;
439472
let right = Self::parse_tr_script_spend(&args[1])?;
440-
Ok(TapTree::Tree(Arc::new(left), Arc::new(right)))
473+
Ok(TapTree::combine(left, right))
441474
}
442475
_ => Err(Error::Unexpected(
443476
"unknown format for script spending paths while parsing taproot descriptor"
@@ -595,10 +628,15 @@ fn split_once(inp: &str, delim: char) -> Option<(&str, &str)> {
595628
impl<Pk: MiniscriptKey> Liftable<Pk> for TapTree<Pk> {
596629
fn lift(&self) -> Result<Policy<Pk>, Error> {
597630
fn lift_helper<Pk: MiniscriptKey>(s: &TapTree<Pk>) -> Result<Policy<Pk>, Error> {
598-
match s {
599-
TapTree::Tree(ref l, ref r) => {
600-
Ok(Policy::Threshold(1, vec![lift_helper(l)?, lift_helper(r)?]))
601-
}
631+
match *s {
632+
TapTree::Tree {
633+
ref left,
634+
ref right,
635+
height: _,
636+
} => Ok(Policy::Threshold(
637+
1,
638+
vec![lift_helper(left)?, lift_helper(right)?],
639+
)),
602640
TapTree::Leaf(ref leaf) => leaf.lift(),
603641
}
604642
}

src/policy/concrete.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -1202,10 +1202,7 @@ fn with_huffman_tree<Pk: MiniscriptKey>(
12021202
let (p2, s2) = node_weights.pop().expect("len must atleast be two");
12031203

12041204
let p = (p1.0).0 + (p2.0).0;
1205-
node_weights.push((
1206-
Reverse(OrdF64(p)),
1207-
TapTree::Tree(Arc::from(s1), Arc::from(s2)),
1208-
));
1205+
node_weights.push((Reverse(OrdF64(p)), TapTree::combine(s1, s2)));
12091206
}
12101207

12111208
debug_assert!(node_weights.len() == 1);

src/policy/mod.rs

+17-18
Original file line numberDiff line numberDiff line change
@@ -387,9 +387,11 @@ mod tests {
387387
Arc::new(ms_str!("and_v(v:pk(C),pk(D))"));
388388
let right_ms_compilation: Arc<Miniscript<String, Tap>> =
389389
Arc::new(ms_str!("and_v(v:pk(A),pk(B))"));
390-
let left_node: Arc<TapTree<String>> = Arc::from(TapTree::Leaf(left_ms_compilation));
391-
let right_node: Arc<TapTree<String>> = Arc::from(TapTree::Leaf(right_ms_compilation));
392-
let tree: TapTree<String> = TapTree::Tree(left_node, right_node);
390+
391+
let left = TapTree::Leaf(left_ms_compilation);
392+
let right = TapTree::Leaf(right_ms_compilation);
393+
let tree = TapTree::combine(left, right);
394+
393395
let expected_descriptor =
394396
Descriptor::new_tr(unspendable_key.clone(), Some(tree)).unwrap();
395397
assert_eq!(descriptor, expected_descriptor);
@@ -457,21 +459,18 @@ mod tests {
457459
.collect::<Vec<_>>();
458460

459461
// Arrange leaf compilations (acc. to probabilities) using huffman encoding into a TapTree
460-
let tree = TapTree::Tree(
461-
Arc::from(TapTree::Tree(
462-
Arc::from(node_compilations[4].clone()),
463-
Arc::from(node_compilations[5].clone()),
464-
)),
465-
Arc::from(TapTree::Tree(
466-
Arc::from(TapTree::Tree(
467-
Arc::from(TapTree::Tree(
468-
Arc::from(node_compilations[0].clone()),
469-
Arc::from(node_compilations[1].clone()),
470-
)),
471-
Arc::from(node_compilations[3].clone()),
472-
)),
473-
Arc::from(node_compilations[6].clone()),
474-
)),
462+
let tree = TapTree::combine(
463+
TapTree::combine(node_compilations[4].clone(), node_compilations[5].clone()),
464+
TapTree::combine(
465+
TapTree::combine(
466+
TapTree::combine(
467+
node_compilations[0].clone(),
468+
node_compilations[1].clone(),
469+
),
470+
node_compilations[3].clone(),
471+
),
472+
node_compilations[6].clone(),
473+
),
475474
);
476475

477476
let expected_descriptor = Descriptor::new_tr("E".to_string(), Some(tree)).unwrap();

0 commit comments

Comments
 (0)