Skip to content

Commit 2232979

Browse files
committed
tr: encapsulate object which is yielded by TapTreeIter
All this does is wrap the existing (depth, script) return value in a structure that has a pile of accessors on it. The accessors include a couple of expensive computations which are currently done manually. A later commit/PR will introduce a `SpendInfo` structure which will cache these computations and amortize much of the hashing needed to produce control blocks. But these new accessors simplify some things.
1 parent e5c9bc3 commit 2232979

File tree

6 files changed

+103
-36
lines changed

6 files changed

+103
-36
lines changed

bitcoind-tests/tests/test_desc.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,9 @@ pub fn test_desc_satisfy(
186186
// ------------------ script spend -------------
187187
let x_only_keypairs_reqd: Vec<(secp256k1::Keypair, TapLeafHash)> = tr
188188
.iter_scripts()
189-
.flat_map(|(_depth, ms)| {
190-
let leaf_hash = TapLeafHash::from_script(&ms.encode(), LeafVersion::TapScript);
191-
ms.iter_pk().filter_map(move |pk| {
189+
.flat_map(|leaf| {
190+
let leaf_hash = TapLeafHash::from_script(&leaf.compute_script(), LeafVersion::TapScript);
191+
leaf.miniscript().iter_pk().filter_map(move |pk| {
192192
let i = x_only_pks.iter().position(|&x| x.to_public_key() == pk);
193193
i.map(|idx| (xonly_keypairs[idx], leaf_hash))
194194
})

examples/taproot.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,17 @@ fn main() {
6767

6868
// Iterate through scripts
6969
let mut iter = p.iter_scripts();
70+
let mut next = iter.next().unwrap();
7071
assert_eq!(
71-
iter.next().unwrap(),
72+
(next.depth(), next.miniscript().as_ref()),
7273
(
7374
1u8,
7475
&Miniscript::<String, Tap>::from_str("and_v(vc:pk_k(In),older(9))").unwrap()
7576
)
7677
);
78+
next = iter.next().unwrap();
7779
assert_eq!(
78-
iter.next().unwrap(),
80+
(next.depth(), next.miniscript().as_ref()),
7981
(1u8, &Miniscript::<String, Tap>::from_str("and_v(v:pk(hA),pk(S))").unwrap())
8082
);
8183
assert_eq!(iter.next(), None);

src/descriptor/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -988,8 +988,9 @@ 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 (_, ms) in inner.iter_scripts() {
992-
ms.ext_check(&crate::miniscript::analyzable::ExtParams::sane())?;
991+
for item in inner.iter_scripts() {
992+
item.miniscript()
993+
.ext_check(&crate::miniscript::analyzable::ExtParams::sane())?;
993994
}
994995
}
995996
Ok(ret)

src/descriptor/tr/mod.rs

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ use crate::{
2626
Threshold, ToPublicKey, TranslateErr, Translator,
2727
};
2828

29+
mod taptree;
30+
31+
pub use self::taptree::TapTreeIterItem;
32+
2933
/// A Taproot Tree representation.
3034
// Hidden leaves are not yet supported in descriptor spec. Conceptually, it should
3135
// be simple to integrate those here, but it is best to wait on core for the exact syntax.
@@ -226,10 +230,10 @@ impl<Pk: MiniscriptKey> Tr<Pk> {
226230
TaprootSpendInfo::new_key_spend(&secp, self.internal_key.to_x_only_pubkey(), None)
227231
} else {
228232
let mut builder = TaprootBuilder::new();
229-
for (depth, ms) in self.iter_scripts() {
230-
let script = ms.encode();
233+
for leaf in self.iter_scripts() {
234+
let script = leaf.miniscript().encode();
231235
builder = builder
232-
.add_leaf(depth, script)
236+
.add_leaf(leaf.depth(), script)
233237
.expect("Computing spend data on a valid Tree should always succeed");
234238
}
235239
// Assert builder cannot error here because we have a well formed descriptor
@@ -245,8 +249,8 @@ impl<Pk: MiniscriptKey> Tr<Pk> {
245249

246250
/// Checks whether the descriptor is safe.
247251
pub fn sanity_check(&self) -> Result<(), Error> {
248-
for (_depth, ms) in self.iter_scripts() {
249-
ms.sanity_check()?;
252+
for leaf in self.iter_scripts() {
253+
leaf.miniscript().sanity_check()?;
250254
}
251255
Ok(())
252256
}
@@ -276,11 +280,11 @@ impl<Pk: MiniscriptKey> Tr<Pk> {
276280

277281
let wu = tree
278282
.iter()
279-
.filter_map(|(depth, ms)| {
280-
let script_size = ms.script_size();
281-
let max_sat_elems = ms.max_satisfaction_witness_elements().ok()?;
282-
let max_sat_size = ms.max_satisfaction_size().ok()?;
283-
let control_block_size = control_block_len(depth);
283+
.filter_map(|leaf| {
284+
let script_size = leaf.miniscript().script_size();
285+
let max_sat_elems = leaf.miniscript().max_satisfaction_witness_elements().ok()?;
286+
let max_sat_size = leaf.miniscript().max_satisfaction_size().ok()?;
287+
let control_block_size = control_block_len(leaf.depth());
284288

285289
// stack varint difference (+1 for ctrl block, witness script already included)
286290
let stack_varint_diff = varint_len(max_sat_elems + 1) - varint_len(0);
@@ -326,11 +330,11 @@ impl<Pk: MiniscriptKey> Tr<Pk> {
326330
};
327331

328332
tree.iter()
329-
.filter_map(|(depth, ms)| {
330-
let script_size = ms.script_size();
331-
let max_sat_elems = ms.max_satisfaction_witness_elements().ok()?;
332-
let max_sat_size = ms.max_satisfaction_size().ok()?;
333-
let control_block_size = control_block_len(depth);
333+
.filter_map(|leaf| {
334+
let script_size = leaf.miniscript().script_size();
335+
let max_sat_elems = leaf.miniscript().max_satisfaction_witness_elements().ok()?;
336+
let max_sat_size = leaf.miniscript().max_satisfaction_size().ok()?;
337+
let control_block_size = control_block_len(leaf.depth());
334338
Some(
335339
// scriptSig len byte
336340
4 +
@@ -473,7 +477,7 @@ impl<'a, Pk> Iterator for TapTreeIter<'a, Pk>
473477
where
474478
Pk: MiniscriptKey + 'a,
475479
{
476-
type Item = (u8, &'a Miniscript<Pk, Tap>);
480+
type Item = TapTreeIterItem<'a, Pk>;
477481

478482
fn next(&mut self) -> Option<Self::Item> {
479483
while let Some((depth, last)) = self.stack.pop() {
@@ -482,7 +486,7 @@ where
482486
self.stack.push((depth + 1, right));
483487
self.stack.push((depth + 1, left));
484488
}
485-
TapTree::Leaf(ref ms) => return Some((depth, ms)),
489+
TapTree::Leaf(ref ms) => return Some(TapTreeIterItem { node: ms, depth }),
486490
}
487491
}
488492
None
@@ -629,7 +633,7 @@ impl<Pk: MiniscriptKey> ForEachKey<Pk> for Tr<Pk> {
629633
fn for_each_key<'a, F: FnMut(&'a Pk) -> bool>(&'a self, mut pred: F) -> bool {
630634
let script_keys_res = self
631635
.iter_scripts()
632-
.all(|(_d, ms)| ms.for_each_key(&mut pred));
636+
.all(|leaf| leaf.miniscript().for_each_key(&mut pred));
633637
script_keys_res && pred(&self.internal_key)
634638
}
635639
}
@@ -673,14 +677,14 @@ where
673677
absolute_timelock: None,
674678
};
675679
let mut min_wit_len = None;
676-
for (_depth, ms) in desc.iter_scripts() {
680+
for leaf in desc.iter_scripts() {
677681
let mut satisfaction = if allow_mall {
678-
match ms.build_template(provider) {
682+
match leaf.miniscript().build_template(provider) {
679683
s @ Satisfaction { stack: Witness::Stack(_), .. } => s,
680684
_ => continue, // No witness for this script in tr descriptor, look for next one
681685
}
682686
} else {
683-
match ms.build_template_mall(provider) {
687+
match leaf.miniscript().build_template_mall(provider) {
684688
s @ Satisfaction { stack: Witness::Stack(_), .. } => s,
685689
_ => continue, // No witness for this script in tr descriptor, look for next one
686690
}
@@ -690,7 +694,7 @@ where
690694
_ => unreachable!(),
691695
};
692696

693-
let leaf_script = (ms.encode(), LeafVersion::TapScript);
697+
let leaf_script = (leaf.compute_script(), LeafVersion::TapScript);
694698
let control_block = spend_info
695699
.control_block(&leaf_script)
696700
.expect("Control block must exist in script map for every known leaf");

src/descriptor/tr/taptree.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// SPDX-License-Identifier: CC0-1.0
2+
3+
use bitcoin::taproot::{LeafVersion, TapLeafHash};
4+
5+
use crate::miniscript::context::Tap;
6+
use crate::sync::Arc;
7+
use crate::{Miniscript, MiniscriptKey, ToPublicKey};
8+
9+
/// Iterator over all of the leaves of a Taproot tree.
10+
///
11+
/// If there is no tree (i.e. this is a keyspend-only Taproot descriptor)
12+
/// then the iterator will yield nothing.
13+
#[derive(Clone, PartialEq, Eq, Debug)]
14+
pub struct TapTreeIterItem<'tr, Pk: MiniscriptKey> {
15+
pub(super) node: &'tr Arc<Miniscript<Pk, Tap>>,
16+
pub(super) depth: u8,
17+
}
18+
19+
impl<'tr, Pk: MiniscriptKey> TapTreeIterItem<'tr, Pk> {
20+
/// The Tapscript in the leaf.
21+
///
22+
/// To obtain a [`bitcoin::Script`] from this node, call [`Miniscript::encode`]
23+
/// on the returned value.
24+
#[inline]
25+
pub fn miniscript(&self) -> &'tr Arc<Miniscript<Pk, Tap>> { self.node }
26+
27+
/// The depth of this leaf.
28+
///
29+
/// This is useful for reconstructing the shape of the tree.
30+
#[inline]
31+
pub fn depth(&self) -> u8 { self.depth }
32+
33+
/// The Tapleaf version of this leaf.
34+
///
35+
/// This function returns a constant value, since there is only one version in use
36+
/// on the Bitcoin network; however, it may be useful to use this method in case
37+
/// you wish to be forward-compatible with future versions supported by this
38+
/// library.
39+
#[inline]
40+
pub fn leaf_version(&self) -> LeafVersion { LeafVersion::TapScript }
41+
}
42+
43+
impl<Pk: ToPublicKey> TapTreeIterItem<'_, Pk> {
44+
/// Computes the Bitcoin Script of the leaf.
45+
///
46+
/// This function is potentially expensive.
47+
#[inline]
48+
pub fn compute_script(&self) -> bitcoin::ScriptBuf { self.node.encode() }
49+
50+
/// Computes the [`TapLeafHash`] of the leaf.
51+
///
52+
/// This function is potentially expensive, since it serializes the full Bitcoin
53+
/// Script of the leaf and hashes this data.
54+
#[inline]
55+
pub fn compute_tap_leaf_hash(&self) -> TapLeafHash {
56+
TapLeafHash::from_script(&self.compute_script(), self.leaf_version())
57+
}
58+
}

src/psbt/mod.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,14 +1112,12 @@ fn update_item_with_descriptor_helper<F: PsbtFields>(
11121112

11131113
let mut builder = taproot::TaprootBuilder::new();
11141114

1115-
for ((_depth_der, ms_derived), (depth, ms)) in
1116-
tr_derived.iter_scripts().zip(tr_xpk.iter_scripts())
1117-
{
1118-
debug_assert_eq!(_depth_der, depth);
1119-
let leaf_script = (ms_derived.encode(), LeafVersion::TapScript);
1115+
for (leaf_derived, leaf) in tr_derived.iter_scripts().zip(tr_xpk.iter_scripts()) {
1116+
debug_assert_eq!(leaf_derived.depth(), leaf.depth());
1117+
let leaf_script = (leaf_derived.compute_script(), leaf_derived.leaf_version());
11201118
let tapleaf_hash = TapLeafHash::from_script(&leaf_script.0, leaf_script.1);
11211119
builder = builder
1122-
.add_leaf(depth, leaf_script.0.clone())
1120+
.add_leaf(leaf_derived.depth(), leaf_script.0.clone())
11231121
.expect("Computing spend data on a valid tree should always succeed");
11241122
if let Some(tap_scripts) = item.tap_scripts() {
11251123
let control_block = spend_info
@@ -1128,7 +1126,11 @@ fn update_item_with_descriptor_helper<F: PsbtFields>(
11281126
tap_scripts.insert(control_block, leaf_script);
11291127
}
11301128

1131-
for (pk_pkh_derived, pk_pkh_xpk) in ms_derived.iter_pk().zip(ms.iter_pk()) {
1129+
for (pk_pkh_derived, pk_pkh_xpk) in leaf_derived
1130+
.miniscript()
1131+
.iter_pk()
1132+
.zip(leaf.miniscript().iter_pk())
1133+
{
11321134
let (xonly, xpk) = (pk_pkh_derived.to_x_only_pubkey(), pk_pkh_xpk);
11331135

11341136
let xpk_full_derivation_path = xpk

0 commit comments

Comments
 (0)