Skip to content

Commit 998b423

Browse files
committed
descriptor: introduce several Taproot accessors
When working with Taproot descriptors you typically need to do an annoying (and hard to discover) `match` statement to get the `Tr` out of the descriptor, and then call accessors on that to get the actual data out. Add two new methods to `Descriptor` that directly access the internal key and the taptree. Document that the actual leaves can be obtained by calling `.iter` on the taptree. Next, when a user is trying to sign a Taproot branch, they need to obtain a TapLeafHash. We have internal code which does this (which I have pulled into a helper function since there is some room to optimize it there..) but no exposed code, forcing the user to go digging through the rust-bitcoin docs to figure it out (including knowing the standard Taproot leaf version, which is an arcane detail of the sort that Miniscript otherwise hides). Add a new method `leaf_hash` on Taproot miniscripts, so that the user can directly obtain the leaf hashes. Now you can write e.g. for script in trdesc.tap_tree_iter() { let leaf_hash = script.leaf_hash(); // Do whatever you want... } vs the previous code which was roughly let tr = match trdesc { Descriptor::Tr(ref tr) => tr, _ => unreachable!("I know this is a Taproot descriptor"), }; // Or tr.tap_tree().unwrap().iter() in case you miss the weirdly-named // Tr::iter_scripts for script in tr.iter_scripts() { // Hope you know your rust-bitcoin docs by heart, and also that // .encode is the way to convert a Miniscript to a Script! let leaf_hash = TapLeafHash::from_script( LeafVersion::TapScript, script.encode(), ); }
1 parent 98104b9 commit 998b423

File tree

3 files changed

+69
-10
lines changed

3 files changed

+69
-10
lines changed

src/descriptor/mod.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,40 @@ impl<Pk: MiniscriptKey> Descriptor<Pk> {
241241
Ok(Descriptor::Tr(Tr::new(key, script)?))
242242
}
243243

244+
/// For a Taproot descriptor, returns the internal key.
245+
pub fn internal_key(&self) -> Option<&Pk> {
246+
if let Descriptor::Tr(ref tr) = self {
247+
Some(tr.internal_key())
248+
} else {
249+
None
250+
}
251+
}
252+
253+
/// For a Taproot descriptor, returns the [`TapTree`] describing the Taproot tree.
254+
///
255+
/// To obtain the individual leaves of the tree, call [`TapTree::iter`] on the
256+
/// returned value.
257+
pub fn tap_tree(&self) -> Option<&TapTree<Pk>> {
258+
if let Descriptor::Tr(ref tr) = self {
259+
tr.tap_tree().as_ref()
260+
} else {
261+
None
262+
}
263+
}
264+
265+
/// For a Taproot descriptor, returns an iterator over the scripts in the Taptree.
266+
///
267+
/// If the descriptor is not a Taproot descriptor, **or** if the descriptor is a
268+
/// Taproot descriptor containing only a keyspend, returns an empty iterator.
269+
pub fn tap_tree_iter(&self) -> tr::TapTreeIter<Pk> {
270+
if let Descriptor::Tr(ref tr) = self {
271+
if let Some(ref tree) = tr.tap_tree() {
272+
return tree.iter();
273+
}
274+
}
275+
tr::TapTreeIter::empty()
276+
}
277+
244278
/// Get the [DescriptorType] of [Descriptor]
245279
pub fn desc_type(&self) -> DescriptorType {
246280
match *self {

src/descriptor/tr.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,11 @@ pub struct TapTreeIter<'a, Pk: MiniscriptKey> {
465465
stack: Vec<(u8, &'a TapTree<Pk>)>,
466466
}
467467

468+
impl<'a, Pk: MiniscriptKey> TapTreeIter<'a, Pk> {
469+
/// Helper function to return an empty iterator from Descriptor::tap_tree_iter.
470+
pub(super) fn empty() -> Self { Self { stack: vec![] } }
471+
}
472+
468473
impl<'a, Pk> Iterator for TapTreeIter<'a, Pk>
469474
where
470475
Pk: MiniscriptKey + 'a,

src/miniscript/mod.rs

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

3-
//! # Abstract Syntax Tree
3+
//! Abstract Syntax Tree
44
//!
55
//! Defines a variety of data structures for describing Miniscript, a subset of
66
//! Bitcoin Script which can be efficiently parsed and serialized from Script,
@@ -289,16 +289,27 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
289289
Ctx::max_satisfaction_size(self).ok_or(Error::ImpossibleSatisfaction)
290290
}
291291

292+
/// Helper function to produce Taproot leaf hashes
293+
fn leaf_hash_internal(&self) -> TapLeafHash
294+
where
295+
Pk: ToPublicKey,
296+
{
297+
TapLeafHash::from_script(&self.encode(), LeafVersion::TapScript)
298+
}
299+
292300
/// Attempt to produce non-malleable satisfying witness for the
293301
/// witness script represented by the parse tree
294302
pub fn satisfy<S: satisfy::Satisfier<Pk>>(&self, satisfier: S) -> Result<Vec<Vec<u8>>, Error>
295303
where
296304
Pk: ToPublicKey,
297305
{
298306
// Only satisfactions for default versions (0xc0) are allowed.
299-
let leaf_hash = TapLeafHash::from_script(&self.encode(), LeafVersion::TapScript);
300-
let satisfaction =
301-
satisfy::Satisfaction::satisfy(&self.node, &satisfier, self.ty.mall.safe, &leaf_hash);
307+
let satisfaction = satisfy::Satisfaction::satisfy(
308+
&self.node,
309+
&satisfier,
310+
self.ty.mall.safe,
311+
&self.leaf_hash_internal(),
312+
);
302313
self._satisfy(satisfaction)
303314
}
304315

@@ -311,12 +322,11 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
311322
where
312323
Pk: ToPublicKey,
313324
{
314-
let leaf_hash = TapLeafHash::from_script(&self.encode(), LeafVersion::TapScript);
315325
let satisfaction = satisfy::Satisfaction::satisfy_mall(
316326
&self.node,
317327
&satisfier,
318328
self.ty.mall.safe,
319-
&leaf_hash,
329+
&self.leaf_hash_internal(),
320330
);
321331
self._satisfy(satisfaction)
322332
}
@@ -344,8 +354,12 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
344354
where
345355
Pk: ToPublicKey,
346356
{
347-
let leaf_hash = TapLeafHash::from_script(&self.encode(), LeafVersion::TapScript);
348-
satisfy::Satisfaction::build_template(&self.node, provider, self.ty.mall.safe, &leaf_hash)
357+
satisfy::Satisfaction::build_template(
358+
&self.node,
359+
provider,
360+
self.ty.mall.safe,
361+
&self.leaf_hash_internal(),
362+
)
349363
}
350364

351365
/// Attempt to produce a malleable witness template given the assets available
@@ -356,16 +370,22 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
356370
where
357371
Pk: ToPublicKey,
358372
{
359-
let leaf_hash = TapLeafHash::from_script(&self.encode(), LeafVersion::TapScript);
360373
satisfy::Satisfaction::build_template_mall(
361374
&self.node,
362375
provider,
363376
self.ty.mall.safe,
364-
&leaf_hash,
377+
&self.leaf_hash_internal(),
365378
)
366379
}
367380
}
368381

382+
impl Miniscript<<Tap as ScriptContext>::Key, Tap> {
383+
/// Returns the leaf hash used within a Taproot signature for this script.
384+
///
385+
/// Note that this method is only implemented for Taproot Miniscripts.
386+
pub fn leaf_hash(&self) -> TapLeafHash { self.leaf_hash_internal() }
387+
}
388+
369389
impl<Ctx: ScriptContext> Miniscript<Ctx::Key, Ctx> {
370390
/// Attempt to parse an insane(scripts don't clear sanity checks)
371391
/// script into a Miniscript representation.

0 commit comments

Comments
 (0)