Skip to content

Commit 9bf3f10

Browse files
committed
Merge #808: [Taproot API Project] flatten internal representation of TapTree
adf3164 tr: improve TapTreeIter a bunch (Andrew Poelstra) 960a6cc tr: optimize parsing of taptrees (Andrew Poelstra) 552e844 tr: flatten TapTree type (Andrew Poelstra) 5abc70c tr: break Tr API in a couple ways (Andrew Poelstra) d2e30d5 tr: introduce TapTree:leaf constructor (Andrew Poelstra) c362d90 tr: move TapTree depth-checking to construction rather than embedding in Tr (Andrew Poelstra) Pull request description: This PR completely rewrites the `TapTree` module to store a taptree as a flat list of leaves and heights. For a non-`ToPublicKey` key this is the only useful information that we ever extract from this, so storing a big recursive structure is wasteful. We also eliminate recursive code from everything `TapTree` related, including the implicit `Drop` impl. In the next PR we'll introduce a new tree structure that contains control block information -- but `TapTree` can't be this structure because `TapTree` doesn't assume that its keys can be serialized. Also breaks the `iter_scripts` iterator API in a couple small ways. ACKs for top commit: sanket1729: ACK adf3164 Tree-SHA512: edcdf66cb59423f530aadad7c48849195766fae378bad22d1f297f161429086346c6fecb3e1d1cd161822c816c7e8cdbf187401e2077830d5614480542b80197
2 parents 6a3f0f7 + adf3164 commit 9bf3f10

File tree

6 files changed

+219
-161
lines changed

6 files changed

+219
-161
lines changed

src/descriptor/mod.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ pub use self::bare::{Bare, Pkh};
4242
pub use self::segwitv0::{Wpkh, Wsh, WshInner};
4343
pub use self::sh::{Sh, ShInner};
4444
pub use self::sortedmulti::SortedMultiVec;
45-
pub use self::tr::{TapTree, TapTreeIter, TapTreeIterItem, Tr};
45+
pub use self::tr::{TapTree, TapTreeDepthError, TapTreeIter, TapTreeIterItem, Tr};
4646

4747
pub mod checksum;
4848
mod key;
@@ -256,7 +256,7 @@ impl<Pk: MiniscriptKey> Descriptor<Pk> {
256256
/// returned value.
257257
pub fn tap_tree(&self) -> Option<&TapTree<Pk>> {
258258
if let Descriptor::Tr(ref tr) = self {
259-
tr.tap_tree().as_ref()
259+
tr.tap_tree()
260260
} else {
261261
None
262262
}
@@ -268,7 +268,7 @@ impl<Pk: MiniscriptKey> Descriptor<Pk> {
268268
/// Taproot descriptor containing only a keyspend, returns an empty iterator.
269269
pub fn tap_tree_iter(&self) -> tr::TapTreeIter<Pk> {
270270
if let Descriptor::Tr(ref tr) = self {
271-
if let Some(ref tree) = tr.tap_tree() {
271+
if let Some(tree) = tr.tap_tree() {
272272
return tree.leaves();
273273
}
274274
}
@@ -988,7 +988,7 @@ impl<Pk: FromStrKey> FromStr for Descriptor<Pk> {
988988
// FIXME preserve weird/broken behavior from 12.x.
989989
// See https://github.com/rust-bitcoin/rust-miniscript/issues/734
990990
ret.sanity_check()?;
991-
for item in inner.iter_scripts() {
991+
for item in inner.leaves() {
992992
item.miniscript()
993993
.ext_check(&crate::miniscript::analyzable::ExtParams::sane())?;
994994
}

src/descriptor/tr/mod.rs

+22-54
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use core::{cmp, fmt, hash};
66
use bitcoin::secp256k1;
77
use bitcoin::taproot::{
88
LeafVersion, TaprootBuilder, TaprootSpendInfo, TAPROOT_CONTROL_BASE_SIZE,
9-
TAPROOT_CONTROL_MAX_NODE_COUNT, TAPROOT_CONTROL_NODE_SIZE,
9+
TAPROOT_CONTROL_NODE_SIZE,
1010
};
1111
use bitcoin::{opcodes, Address, Network, ScriptBuf, Weight};
1212
use sync::Arc;
@@ -28,7 +28,7 @@ use crate::{
2828

2929
mod taptree;
3030

31-
pub use self::taptree::{TapTree, TapTreeIter, TapTreeIterItem};
31+
pub use self::taptree::{TapTree, TapTreeDepthError, TapTreeIter, TapTreeIterItem};
3232

3333
/// A taproot descriptor
3434
pub struct Tr<Pk: MiniscriptKey> {
@@ -98,29 +98,14 @@ impl<Pk: MiniscriptKey> Tr<Pk> {
9898
/// Create a new [`Tr`] descriptor from internal key and [`TapTree`]
9999
pub fn new(internal_key: Pk, tree: Option<TapTree<Pk>>) -> Result<Self, Error> {
100100
Tap::check_pk(&internal_key)?;
101-
let nodes = tree.as_ref().map(|t| t.height()).unwrap_or(0);
102-
103-
if nodes <= TAPROOT_CONTROL_MAX_NODE_COUNT {
104-
Ok(Self { internal_key, tree, spend_info: Mutex::new(None) })
105-
} else {
106-
Err(Error::MaxRecursiveDepthExceeded)
107-
}
101+
Ok(Self { internal_key, tree, spend_info: Mutex::new(None) })
108102
}
109103

110104
/// Obtain the internal key of [`Tr`] descriptor
111105
pub fn internal_key(&self) -> &Pk { &self.internal_key }
112106

113107
/// Obtain the [`TapTree`] of the [`Tr`] descriptor
114-
pub fn tap_tree(&self) -> &Option<TapTree<Pk>> { &self.tree }
115-
116-
/// Obtain the [`TapTree`] of the [`Tr`] descriptor
117-
#[deprecated(since = "11.0.0", note = "use tap_tree instead")]
118-
pub fn taptree(&self) -> &Option<TapTree<Pk>> { self.tap_tree() }
119-
120-
/// Iterate over all scripts in merkle tree. If there is no script path, the iterator
121-
/// yields [`None`]
122-
#[deprecated(since = "TBD", note = "use `leaves` instead")]
123-
pub fn iter_scripts(&self) -> TapTreeIter<Pk> { self.leaves() }
108+
pub fn tap_tree(&self) -> Option<&TapTree<Pk>> { self.tree.as_ref() }
124109

125110
/// Iterates over all the leaves of the tree in depth-first preorder.
126111
///
@@ -291,7 +276,7 @@ impl<Pk: MiniscriptKey> Tr<Pk> {
291276
T: Translator<Pk>,
292277
{
293278
let tree = match &self.tree {
294-
Some(tree) => Some(tree.translate_helper(translate)?),
279+
Some(tree) => Some(tree.translate_pk(translate)?),
295280
None => None,
296281
};
297282
let translate_desc =
@@ -392,33 +377,6 @@ impl<Pk: FromStrKey> crate::expression::FromTree for Tr<Pk> {
392377
fn from_tree(root: expression::TreeIterItem) -> Result<Self, Error> {
393378
use crate::expression::{Parens, ParseTreeError};
394379

395-
struct TreeStack<'s, Pk: MiniscriptKey> {
396-
inner: Vec<(expression::TreeIterItem<'s>, TapTree<Pk>)>,
397-
}
398-
399-
impl<'s, Pk: MiniscriptKey> TreeStack<'s, Pk> {
400-
fn new() -> Self { Self { inner: Vec::with_capacity(128) } }
401-
402-
fn push(&mut self, parent: expression::TreeIterItem<'s>, tree: TapTree<Pk>) {
403-
let mut next_push = (parent, tree);
404-
while let Some(top) = self.inner.pop() {
405-
if next_push.0.index() == top.0.index() {
406-
next_push.0 = top.0.parent().unwrap();
407-
next_push.1 = TapTree::combine(top.1, next_push.1);
408-
} else {
409-
self.inner.push(top);
410-
break;
411-
}
412-
}
413-
self.inner.push(next_push);
414-
}
415-
416-
fn pop_final(&mut self) -> Option<TapTree<Pk>> {
417-
assert_eq!(self.inner.len(), 1);
418-
self.inner.pop().map(|x| x.1)
419-
}
420-
}
421-
422380
root.verify_toplevel("tr", 1..=2)
423381
.map_err(From::from)
424382
.map_err(Error::Parse)?;
@@ -435,7 +393,7 @@ impl<Pk: FromStrKey> crate::expression::FromTree for Tr<Pk> {
435393
Some(tree) => tree,
436394
};
437395

438-
let mut tree_stack = TreeStack::new();
396+
let mut tree_builder = taptree::TapTreeBuilder::new();
439397
let mut tap_tree_iter = tap_tree.pre_order_iter();
440398
// while let construction needed because we modify the iterator inside the loop
441399
// (by calling skip_descendants to skip over the contents of the tapscripts).
@@ -450,18 +408,19 @@ impl<Pk: FromStrKey> crate::expression::FromTree for Tr<Pk> {
450408
node.verify_n_children("taptree branch", 2..=2)
451409
.map_err(From::from)
452410
.map_err(Error::Parse)?;
411+
tree_builder.push_inner_node()?;
453412
} else {
454413
let script = Miniscript::from_tree(node)?;
455414
// FIXME hack for https://github.com/rust-bitcoin/rust-miniscript/issues/734
456415
if script.ty.corr.base != crate::miniscript::types::Base::B {
457416
return Err(Error::NonTopLevel(format!("{:?}", script)));
458417
};
459418

460-
tree_stack.push(node.parent().unwrap(), TapTree::Leaf(Arc::new(script)));
419+
tree_builder.push_leaf(script);
461420
tap_tree_iter.skip_descendants();
462421
}
463422
}
464-
Tr::new(internal_key, tree_stack.pop_final())
423+
Tr::new(internal_key, Some(tree_builder.finalize()))
465424
}
466425
}
467426

@@ -616,9 +575,18 @@ mod tests {
616575
}
617576

618577
#[test]
619-
fn height() {
620-
let desc = descriptor();
621-
let tr = Tr::<String>::from_str(&desc).unwrap();
622-
assert_eq!(tr.tap_tree().as_ref().unwrap().height(), 2);
578+
fn tr_maximum_depth() {
579+
// Copied from integration tests
580+
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)}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}})";
581+
descriptor128.parse::<crate::Descriptor<String>>().unwrap();
582+
583+
// Copied from integration tests
584+
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)}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}})";
585+
assert!(matches!(
586+
descriptor129
587+
.parse::<crate::Descriptor::<String>>()
588+
.unwrap_err(),
589+
crate::Error::TapTreeDepthError(TapTreeDepthError),
590+
));
623591
}
624592
}

0 commit comments

Comments
 (0)