Skip to content

Commit c15d4ef

Browse files
committed
Merge #588: Add height to tap tree
476e358 Add height to tap tree (Tobin C. Harding) fe6e8ad Add basic tap_tree_height unit test (Tobin C. Harding) edf9373 Separate taptree in identifiers and docs (Tobin C. Harding) ecf876e Rename function taptree_height to height (Tobin C. Harding) Pull request description: I stumbled across a `todo` in the code last week and thought I'd hack it up. - Patch 1: Do identifier style fix - Patch 2: Add a unit test to verify change in patch 3 is correct - Patch 3: Remove recursive call to `tap_tree_height` From commit log of patch 3 Convert tuple `TapTree` tuple to a struct and add `height` to it. This saves recusive call in `tap_tree_height` function. While we are at it rename private function `TapTree::tap_tree_height` to `TapTree::height` because it stutters. ACKs for top commit: apoelstra: ACK 476e358 sanket1729: ACK 476e358 Tree-SHA512: 107275086a4be95dc1926fbe598937b125b76f98b3b04a5add579fe2a33fa178e9a72fcded083f731510151e409d02513477317c50492ea10107d4349afe3cb8
2 parents feae54c + 476e358 commit c15d4ef

File tree

5 files changed

+112
-62
lines changed

5 files changed

+112
-62
lines changed

examples/taproot.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ fn main() {
101101
let real_desc = desc.translate_pk(&mut t).unwrap();
102102

103103
// Max satisfaction weight for compilation, corresponding to the script-path spend
104-
// `multi_a(2,PUBKEY_1,PUBKEY_2) at taptree depth 1, having:
104+
// `multi_a(2,PUBKEY_1,PUBKEY_2) at tap tree depth 1, having:
105105
//
106106
// max_witness_size = varint(control_block_size) + control_block size +
107107
// varint(script_size) + script_size + max_satisfaction_size

src/descriptor/tr.rs

+88-34
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
114-
fn taptree_height(&self) -> usize {
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.
128+
fn height(&self) -> usize {
115129
match *self {
116-
TapTree::Tree(ref left_tree, ref right_tree) => {
117-
1 + max(left_tree.taptree_height(), right_tree.taptree_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
}
@@ -167,7 +196,7 @@ impl<Pk: MiniscriptKey> Tr<Pk> {
167196
/// Create a new [`Tr`] descriptor from internal key and [`TapTree`]
168197
pub fn new(internal_key: Pk, tree: Option<TapTree<Pk>>) -> Result<Self, Error> {
169198
Tap::check_pk(&internal_key)?;
170-
let nodes = tree.as_ref().map(|t| t.taptree_height()).unwrap_or(0);
199+
let nodes = tree.as_ref().map(|t| t.height()).unwrap_or(0);
171200

172201
if nodes <= TAPROOT_CONTROL_MAX_NODE_COUNT {
173202
Ok(Self {
@@ -186,10 +215,16 @@ impl<Pk: MiniscriptKey> Tr<Pk> {
186215
}
187216

188217
/// Obtain the [`TapTree`] of the [`Tr`] descriptor
189-
pub fn taptree(&self) -> &Option<TapTree<Pk>> {
218+
pub fn tap_tree(&self) -> &Option<TapTree<Pk>> {
190219
&self.tree
191220
}
192221

222+
/// Obtain the [`TapTree`] of the [`Tr`] descriptor
223+
#[deprecated(since = "11.0.0", note = "use tap_tree instead")]
224+
pub fn taptree(&self) -> &Option<TapTree<Pk>> {
225+
self.tap_tree()
226+
}
227+
193228
/// Iterate over all scripts in merkle tree. If there is no script path, the iterator
194229
/// yields [`None`]
195230
pub fn iter_scripts(&self) -> TapTreeIter<Pk> {
@@ -258,7 +293,7 @@ impl<Pk: MiniscriptKey> Tr<Pk> {
258293
/// # Errors
259294
/// When the descriptor is impossible to safisfy (ex: sh(OP_FALSE)).
260295
pub fn max_weight_to_satisfy(&self) -> Result<usize, Error> {
261-
let tree = match self.taptree() {
296+
let tree = match self.tap_tree() {
262297
None => {
263298
// key spend path
264299
// item: varint(sig+sigHash) + <sig(64)+sigHash(1)>
@@ -309,7 +344,7 @@ impl<Pk: MiniscriptKey> Tr<Pk> {
309344
/// When the descriptor is impossible to safisfy (ex: sh(OP_FALSE)).
310345
#[deprecated(note = "use max_weight_to_satisfy instead")]
311346
pub fn max_satisfaction_weight(&self) -> Result<usize, Error> {
312-
let tree = match self.taptree() {
347+
let tree = match self.tap_tree() {
313348
// key spend path:
314349
// scriptSigLen(4) + stackLen(1) + stack[Sig]Len(1) + stack[Sig](65)
315350
None => return Ok(4 + 1 + 1 + 65),
@@ -407,9 +442,13 @@ where
407442
fn next(&mut self) -> Option<Self::Item> {
408443
while let Some((depth, last)) = self.stack.pop() {
409444
match *last {
410-
TapTree::Tree(ref l, ref r) => {
411-
self.stack.push((depth + 1, r));
412-
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));
413452
}
414453
TapTree::Leaf(ref ms) => return Some((depth, ms)),
415454
}
@@ -431,7 +470,7 @@ impl_block_str!(
431470
expression::Tree { name, args } if name.is_empty() && args.len() == 2 => {
432471
let left = Self::parse_tr_script_spend(&args[0])?;
433472
let right = Self::parse_tr_script_spend(&args[1])?;
434-
Ok(TapTree::Tree(Arc::new(left), Arc::new(right)))
473+
Ok(TapTree::combine(left, right))
435474
}
436475
_ => Err(Error::Unexpected(
437476
"unknown format for script spending paths while parsing taproot descriptor"
@@ -589,10 +628,15 @@ fn split_once(inp: &str, delim: char) -> Option<(&str, &str)> {
589628
impl<Pk: MiniscriptKey> Liftable<Pk> for TapTree<Pk> {
590629
fn lift(&self) -> Result<Policy<Pk>, Error> {
591630
fn lift_helper<Pk: MiniscriptKey>(s: &TapTree<Pk>) -> Result<Policy<Pk>, Error> {
592-
match s {
593-
TapTree::Tree(ref l, ref r) => {
594-
Ok(Policy::Threshold(1, vec![lift_helper(l)?, lift_helper(r)?]))
595-
}
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+
)),
596640
TapTree::Leaf(ref leaf) => leaf.lift(),
597641
}
598642
}
@@ -713,10 +757,8 @@ where
713757
#[cfg(test)]
714758
mod tests {
715759
use super::*;
716-
use crate::ForEachKey;
717760

718-
#[test]
719-
fn test_for_each() {
761+
fn descriptor() -> String {
720762
let desc = "tr(acc0, {
721763
multi_a(3, acc10, acc11, acc12), {
722764
and_v(
@@ -729,9 +771,21 @@ mod tests {
729771
)
730772
}
731773
})";
732-
let desc = desc.replace(&[' ', '\n'][..], "");
774+
desc.replace(&[' ', '\n'][..], "")
775+
}
776+
777+
#[test]
778+
fn for_each() {
779+
let desc = descriptor();
733780
let tr = Tr::<String>::from_str(&desc).unwrap();
734781
// Note the last ac12 only has ac and fails the predicate
735782
assert!(!tr.for_each_key(|k| k.starts_with("acc")));
736783
}
784+
785+
#[test]
786+
fn height() {
787+
let desc = descriptor();
788+
let tr = Tr::<String>::from_str(&desc).unwrap();
789+
assert_eq!(tr.tap_tree().as_ref().unwrap().height(), 2);
790+
}
737791
}

src/policy/concrete.rs

+5-8
Original file line numberDiff line numberDiff line change
@@ -403,8 +403,8 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
403403
compilation.sanity_check()?;
404404
leaf_compilations.push((OrdF64(prob), compilation));
405405
}
406-
let taptree = with_huffman_tree::<Pk>(leaf_compilations)?;
407-
Some(taptree)
406+
let tap_tree = with_huffman_tree::<Pk>(leaf_compilations)?;
407+
Some(tap_tree)
408408
}
409409
},
410410
)?;
@@ -462,8 +462,8 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
462462
)
463463
})
464464
.collect();
465-
let taptree = with_huffman_tree::<Pk>(leaf_compilations).unwrap();
466-
Some(taptree)
465+
let tap_tree = with_huffman_tree::<Pk>(leaf_compilations).unwrap();
466+
Some(tap_tree)
467467
}
468468
},
469469
)?;
@@ -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();

src/psbt/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1222,7 +1222,7 @@ fn update_item_with_descriptor_helper<F: PsbtFields>(
12221222
match item.tap_tree() {
12231223
// Only set the tap_tree if the item supports it (it's an output) and the descriptor actually
12241224
// contains one, otherwise it'll just be empty
1225-
Some(tap_tree) if tr_derived.taptree().is_some() => {
1225+
Some(tap_tree) if tr_derived.tap_tree().is_some() => {
12261226
*tap_tree = Some(
12271227
taproot::TapTree::try_from(builder)
12281228
.expect("The tree should always be valid"),

0 commit comments

Comments
 (0)