Skip to content

Commit 3a85952

Browse files
committed
Merge #751: descriptor: introduce several Taproot accessors
86305f0 ci: clippy: remove a ton of now-elidable lifetimes (Andrew Poelstra) dd3396e ci: fix new clippy lint related to doccomments (Andrew Poelstra) 998b423 descriptor: introduce several Taproot accessors (Andrew Poelstra) Pull request description: 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. ```rust for script in trdesc.tap_tree_iter() { let leaf_hash = script.leaf_hash(); // Do whatever you want... } ``` vs the previous code which was roughly ```rust 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(), ); } ``` ACKs for top commit: sanket1729: ACK 86305f0 Tree-SHA512: a6d5ca4d63222e03520f08ab1835d5ad292f6fcd021f517427a56d7d53ecf28c975acd2c8bdd0e2d0b9741cf79d34d352cb775b59bd21600b0fb9daa06aced8d
2 parents aa3691d + 86305f0 commit 3a85952

File tree

10 files changed

+87
-24
lines changed

10 files changed

+87
-24
lines changed

src/descriptor/checksum.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ impl<'f, 'a> Formatter<'f, 'a> {
177177
}
178178
}
179179

180-
impl<'f, 'a> fmt::Write for Formatter<'f, 'a> {
180+
impl fmt::Write for Formatter<'_, '_> {
181181
fn write_str(&mut self, s: &str) -> fmt::Result {
182182
self.fmt.write_str(s)?;
183183
self.eng.input(s).map_err(|_| fmt::Error)

src/descriptor/mod.rs

+37-3
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 {
@@ -698,7 +732,7 @@ impl Descriptor<DescriptorPublicKey> {
698732

699733
struct KeyMapWrapper<'a, C: secp256k1::Signing>(KeyMap, &'a secp256k1::Secp256k1<C>);
700734

701-
impl<'a, C: secp256k1::Signing> Translator<String> for KeyMapWrapper<'a, C> {
735+
impl<C: secp256k1::Signing> Translator<String> for KeyMapWrapper<'_, C> {
702736
type TargetPk = DescriptorPublicKey;
703737
type Error = Error;
704738

@@ -746,7 +780,7 @@ impl Descriptor<DescriptorPublicKey> {
746780
pub fn to_string_with_secret(&self, key_map: &KeyMap) -> String {
747781
struct KeyMapLookUp<'a>(&'a KeyMap);
748782

749-
impl<'a> Translator<DescriptorPublicKey> for KeyMapLookUp<'a> {
783+
impl Translator<DescriptorPublicKey> for KeyMapLookUp<'_> {
750784
type TargetPk = String;
751785
type Error = core::convert::Infallible;
752786

@@ -909,7 +943,7 @@ impl Descriptor<DefiniteDescriptorKey> {
909943
) -> Result<Descriptor<bitcoin::PublicKey>, ConversionError> {
910944
struct Derivator<'a, C: secp256k1::Verification>(&'a secp256k1::Secp256k1<C>);
911945

912-
impl<'a, C: secp256k1::Verification> Translator<DefiniteDescriptorKey> for Derivator<'a, C> {
946+
impl<C: secp256k1::Verification> Translator<DefiniteDescriptorKey> for Derivator<'_, C> {
913947
type TargetPk = bitcoin::PublicKey;
914948
type Error = ConversionError;
915949

src/descriptor/tr.rs

+5
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<Pk: MiniscriptKey> TapTreeIter<'_, 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/iter.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> PkIter<'a, Pk, Ctx> {
176176
}
177177
}
178178

179-
impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> Iterator for PkIter<'a, Pk, Ctx> {
179+
impl<Pk: MiniscriptKey, Ctx: ScriptContext> Iterator for PkIter<'_, Pk, Ctx> {
180180
type Item = Pk;
181181

182182
fn next(&mut self) -> Option<Self::Item> {
@@ -199,22 +199,24 @@ impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> Iterator for PkIter<'a, Pk, Ctx>
199199
}
200200
}
201201

202-
// Module is public since it export testcase generation which may be used in
203-
// dependent libraries for their own tasts based on Miniscript AST
202+
/// Module is public since it export testcase generation which may be used in
203+
/// dependent libraries for their own tasts based on Miniscript AST
204204
#[cfg(test)]
205205
pub mod test {
206206
use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d, Hash};
207207

208208
use super::Miniscript;
209209
use crate::miniscript::context::Segwitv0;
210210

211+
/// Test case.
211212
pub type TestData = (
212213
Miniscript<bitcoin::PublicKey, Segwitv0>,
213214
Vec<bitcoin::PublicKey>,
214215
Vec<hash160::Hash>,
215216
bool, // Indicates that the top-level contains public key or hashes
216217
);
217218

219+
/// Generate a deterministic list of public keys of the given length.
218220
pub fn gen_secp_pubkeys(n: usize) -> Vec<secp256k1::PublicKey> {
219221
let mut ret = Vec::with_capacity(n);
220222
let secp = secp256k1::Secp256k1::new();
@@ -233,13 +235,15 @@ pub mod test {
233235
ret
234236
}
235237

238+
/// Generate a deterministic list of Bitcoin public keys of the given length.
236239
pub fn gen_bitcoin_pubkeys(n: usize, compressed: bool) -> Vec<bitcoin::PublicKey> {
237240
gen_secp_pubkeys(n)
238241
.into_iter()
239242
.map(|inner| bitcoin::PublicKey { inner, compressed })
240243
.collect()
241244
}
242245

246+
/// Generate a deterministic list of test cases of the given length.
243247
pub fn gen_testcases() -> Vec<TestData> {
244248
let k = gen_bitcoin_pubkeys(10, true);
245249
let _h: Vec<hash160::Hash> = k

src/miniscript/lex.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ pub enum Token<'s> {
5050
Bytes65(&'s [u8]),
5151
}
5252

53-
impl<'s> fmt::Display for Token<'s> {
53+
impl fmt::Display for Token<'_> {
5454
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
5555
match *self {
5656
Token::Num(n) => write!(f, "#{}", n),

src/miniscript/mod.rs

+30-10
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.

src/miniscript/satisfy.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ impl_satisfier_for_map_hash_tapleafhash_to_key_taproot_sig! {
262262
impl Satisfier<Pk> for HashMap<(hash160::Hash, TapLeafHash), (Pk, bitcoin::taproot::Signature)>
263263
}
264264

265-
impl<'a, Pk: MiniscriptKey + ToPublicKey, S: Satisfier<Pk>> Satisfier<Pk> for &'a S {
265+
impl<Pk: MiniscriptKey + ToPublicKey, S: Satisfier<Pk>> Satisfier<Pk> for &S {
266266
fn lookup_ecdsa_sig(&self, p: &Pk) -> Option<bitcoin::ecdsa::Signature> {
267267
(**self).lookup_ecdsa_sig(p)
268268
}
@@ -322,7 +322,7 @@ impl<'a, Pk: MiniscriptKey + ToPublicKey, S: Satisfier<Pk>> Satisfier<Pk> for &'
322322
fn check_after(&self, n: absolute::LockTime) -> bool { (**self).check_after(n) }
323323
}
324324

325-
impl<'a, Pk: MiniscriptKey + ToPublicKey, S: Satisfier<Pk>> Satisfier<Pk> for &'a mut S {
325+
impl<Pk: MiniscriptKey + ToPublicKey, S: Satisfier<Pk>> Satisfier<Pk> for &mut S {
326326
fn lookup_ecdsa_sig(&self, p: &Pk) -> Option<bitcoin::ecdsa::Signature> {
327327
(**self).lookup_ecdsa_sig(p)
328328
}

src/plan.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ macro_rules! impl_log_method {
129129
}
130130

131131
#[cfg(feature = "std")]
132-
impl<'a> AssetProvider<DefiniteDescriptorKey> for LoggerAssetProvider<'a> {
132+
impl AssetProvider<DefiniteDescriptorKey> for LoggerAssetProvider<'_> {
133133
impl_log_method!(provider_lookup_ecdsa_sig, pk: &DefiniteDescriptorKey, -> bool);
134134
impl_log_method!(provider_lookup_tap_key_spend_sig, pk: &DefiniteDescriptorKey, -> Option<usize>);
135135
impl_log_method!(provider_lookup_tap_leaf_script_sig, pk: &DefiniteDescriptorKey, leaf_hash: &TapLeafHash, -> Option<usize>);

src/primitives/threshold.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ struct ThreshDisplay<'t, 's, T, const MAX: usize> {
263263
show_k: bool,
264264
}
265265

266-
impl<'t, 's, T, const MAX: usize> fmt::Display for ThreshDisplay<'t, 's, T, MAX>
266+
impl<T, const MAX: usize> fmt::Display for ThreshDisplay<'_, '_, T, MAX>
267267
where
268268
T: fmt::Display,
269269
{
@@ -286,7 +286,7 @@ where
286286
}
287287
}
288288

289-
impl<'t, 's, T, const MAX: usize> fmt::Debug for ThreshDisplay<'t, 's, T, MAX>
289+
impl<T, const MAX: usize> fmt::Debug for ThreshDisplay<'_, '_, T, MAX>
290290
where
291291
T: fmt::Debug,
292292
{

src/psbt/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ impl<'psbt> PsbtInputSatisfier<'psbt> {
254254
pub fn new(psbt: &'psbt Psbt, index: usize) -> Self { Self { psbt, index } }
255255
}
256256

257-
impl<'psbt, Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for PsbtInputSatisfier<'psbt> {
257+
impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for PsbtInputSatisfier<'_> {
258258
fn lookup_tap_key_spend_sig(&self) -> Option<bitcoin::taproot::Signature> {
259259
self.psbt.inputs[self.index].tap_key_sig
260260
}

0 commit comments

Comments
 (0)