From 998b423a44573020ceb27b349520b19897f8c9a0 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Thu, 10 Oct 2024 13:12:06 +0000 Subject: [PATCH 1/3] 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(), ); } --- src/descriptor/mod.rs | 34 ++++++++++++++++++++++++++++++++++ src/descriptor/tr.rs | 5 +++++ src/miniscript/mod.rs | 40 ++++++++++++++++++++++++++++++---------- 3 files changed, 69 insertions(+), 10 deletions(-) diff --git a/src/descriptor/mod.rs b/src/descriptor/mod.rs index 487bbcc45..721a264c1 100644 --- a/src/descriptor/mod.rs +++ b/src/descriptor/mod.rs @@ -241,6 +241,40 @@ impl Descriptor { Ok(Descriptor::Tr(Tr::new(key, script)?)) } + /// For a Taproot descriptor, returns the internal key. + pub fn internal_key(&self) -> Option<&Pk> { + if let Descriptor::Tr(ref tr) = self { + Some(tr.internal_key()) + } else { + None + } + } + + /// For a Taproot descriptor, returns the [`TapTree`] describing the Taproot tree. + /// + /// To obtain the individual leaves of the tree, call [`TapTree::iter`] on the + /// returned value. + pub fn tap_tree(&self) -> Option<&TapTree> { + if let Descriptor::Tr(ref tr) = self { + tr.tap_tree().as_ref() + } else { + None + } + } + + /// For a Taproot descriptor, returns an iterator over the scripts in the Taptree. + /// + /// If the descriptor is not a Taproot descriptor, **or** if the descriptor is a + /// Taproot descriptor containing only a keyspend, returns an empty iterator. + pub fn tap_tree_iter(&self) -> tr::TapTreeIter { + if let Descriptor::Tr(ref tr) = self { + if let Some(ref tree) = tr.tap_tree() { + return tree.iter(); + } + } + tr::TapTreeIter::empty() + } + /// Get the [DescriptorType] of [Descriptor] pub fn desc_type(&self) -> DescriptorType { match *self { diff --git a/src/descriptor/tr.rs b/src/descriptor/tr.rs index 41845f174..0348368e8 100644 --- a/src/descriptor/tr.rs +++ b/src/descriptor/tr.rs @@ -465,6 +465,11 @@ pub struct TapTreeIter<'a, Pk: MiniscriptKey> { stack: Vec<(u8, &'a TapTree)>, } +impl<'a, Pk: MiniscriptKey> TapTreeIter<'a, Pk> { + /// Helper function to return an empty iterator from Descriptor::tap_tree_iter. + pub(super) fn empty() -> Self { Self { stack: vec![] } } +} + impl<'a, Pk> Iterator for TapTreeIter<'a, Pk> where Pk: MiniscriptKey + 'a, diff --git a/src/miniscript/mod.rs b/src/miniscript/mod.rs index 02829ac5f..a0d5d5802 100644 --- a/src/miniscript/mod.rs +++ b/src/miniscript/mod.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: CC0-1.0 -//! # Abstract Syntax Tree +//! Abstract Syntax Tree //! //! Defines a variety of data structures for describing Miniscript, a subset of //! Bitcoin Script which can be efficiently parsed and serialized from Script, @@ -289,6 +289,14 @@ impl Miniscript { Ctx::max_satisfaction_size(self).ok_or(Error::ImpossibleSatisfaction) } + /// Helper function to produce Taproot leaf hashes + fn leaf_hash_internal(&self) -> TapLeafHash + where + Pk: ToPublicKey, + { + TapLeafHash::from_script(&self.encode(), LeafVersion::TapScript) + } + /// Attempt to produce non-malleable satisfying witness for the /// witness script represented by the parse tree pub fn satisfy>(&self, satisfier: S) -> Result>, Error> @@ -296,9 +304,12 @@ impl Miniscript { Pk: ToPublicKey, { // Only satisfactions for default versions (0xc0) are allowed. - let leaf_hash = TapLeafHash::from_script(&self.encode(), LeafVersion::TapScript); - let satisfaction = - satisfy::Satisfaction::satisfy(&self.node, &satisfier, self.ty.mall.safe, &leaf_hash); + let satisfaction = satisfy::Satisfaction::satisfy( + &self.node, + &satisfier, + self.ty.mall.safe, + &self.leaf_hash_internal(), + ); self._satisfy(satisfaction) } @@ -311,12 +322,11 @@ impl Miniscript { where Pk: ToPublicKey, { - let leaf_hash = TapLeafHash::from_script(&self.encode(), LeafVersion::TapScript); let satisfaction = satisfy::Satisfaction::satisfy_mall( &self.node, &satisfier, self.ty.mall.safe, - &leaf_hash, + &self.leaf_hash_internal(), ); self._satisfy(satisfaction) } @@ -344,8 +354,12 @@ impl Miniscript { where Pk: ToPublicKey, { - let leaf_hash = TapLeafHash::from_script(&self.encode(), LeafVersion::TapScript); - satisfy::Satisfaction::build_template(&self.node, provider, self.ty.mall.safe, &leaf_hash) + satisfy::Satisfaction::build_template( + &self.node, + provider, + self.ty.mall.safe, + &self.leaf_hash_internal(), + ) } /// Attempt to produce a malleable witness template given the assets available @@ -356,16 +370,22 @@ impl Miniscript { where Pk: ToPublicKey, { - let leaf_hash = TapLeafHash::from_script(&self.encode(), LeafVersion::TapScript); satisfy::Satisfaction::build_template_mall( &self.node, provider, self.ty.mall.safe, - &leaf_hash, + &self.leaf_hash_internal(), ) } } +impl Miniscript<::Key, Tap> { + /// Returns the leaf hash used within a Taproot signature for this script. + /// + /// Note that this method is only implemented for Taproot Miniscripts. + pub fn leaf_hash(&self) -> TapLeafHash { self.leaf_hash_internal() } +} + impl Miniscript { /// Attempt to parse an insane(scripts don't clear sanity checks) /// script into a Miniscript representation. From dd3396e7667f9dea369f2c7c0c23e121e4e20c34 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Thu, 10 Oct 2024 13:39:37 +0000 Subject: [PATCH 2/3] ci: fix new clippy lint related to doccomments --- src/miniscript/iter.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/miniscript/iter.rs b/src/miniscript/iter.rs index a9373abf5..175fbfe4a 100644 --- a/src/miniscript/iter.rs +++ b/src/miniscript/iter.rs @@ -199,8 +199,8 @@ impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> Iterator for PkIter<'a, Pk, Ctx> } } -// Module is public since it export testcase generation which may be used in -// dependent libraries for their own tasts based on Miniscript AST +/// Module is public since it export testcase generation which may be used in +/// dependent libraries for their own tasts based on Miniscript AST #[cfg(test)] pub mod test { use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d, Hash}; @@ -208,6 +208,7 @@ pub mod test { use super::Miniscript; use crate::miniscript::context::Segwitv0; + /// Test case. pub type TestData = ( Miniscript, Vec, @@ -215,6 +216,7 @@ pub mod test { bool, // Indicates that the top-level contains public key or hashes ); + /// Generate a deterministic list of public keys of the given length. pub fn gen_secp_pubkeys(n: usize) -> Vec { let mut ret = Vec::with_capacity(n); let secp = secp256k1::Secp256k1::new(); @@ -233,6 +235,7 @@ pub mod test { ret } + /// Generate a deterministic list of Bitcoin public keys of the given length. pub fn gen_bitcoin_pubkeys(n: usize, compressed: bool) -> Vec { gen_secp_pubkeys(n) .into_iter() @@ -240,6 +243,7 @@ pub mod test { .collect() } + /// Generate a deterministic list of test cases of the given length. pub fn gen_testcases() -> Vec { let k = gen_bitcoin_pubkeys(10, true); let _h: Vec = k From 86305f0ad499b486d00bea8e7a9dca14cfe7a836 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Thu, 10 Oct 2024 13:43:32 +0000 Subject: [PATCH 3/3] ci: clippy: remove a ton of now-elidable lifetimes --- src/descriptor/checksum.rs | 2 +- src/descriptor/mod.rs | 6 +++--- src/descriptor/tr.rs | 2 +- src/miniscript/iter.rs | 2 +- src/miniscript/lex.rs | 2 +- src/miniscript/satisfy.rs | 4 ++-- src/plan.rs | 2 +- src/primitives/threshold.rs | 4 ++-- src/psbt/mod.rs | 2 +- 9 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/descriptor/checksum.rs b/src/descriptor/checksum.rs index ec286f7e5..6a79194c2 100644 --- a/src/descriptor/checksum.rs +++ b/src/descriptor/checksum.rs @@ -177,7 +177,7 @@ impl<'f, 'a> Formatter<'f, 'a> { } } -impl<'f, 'a> fmt::Write for Formatter<'f, 'a> { +impl fmt::Write for Formatter<'_, '_> { fn write_str(&mut self, s: &str) -> fmt::Result { self.fmt.write_str(s)?; self.eng.input(s).map_err(|_| fmt::Error) diff --git a/src/descriptor/mod.rs b/src/descriptor/mod.rs index 721a264c1..26ca9438a 100644 --- a/src/descriptor/mod.rs +++ b/src/descriptor/mod.rs @@ -730,7 +730,7 @@ impl Descriptor { struct KeyMapWrapper<'a, C: secp256k1::Signing>(KeyMap, &'a secp256k1::Secp256k1); - impl<'a, C: secp256k1::Signing> Translator for KeyMapWrapper<'a, C> { + impl Translator for KeyMapWrapper<'_, C> { type TargetPk = DescriptorPublicKey; type Error = Error; @@ -778,7 +778,7 @@ impl Descriptor { pub fn to_string_with_secret(&self, key_map: &KeyMap) -> String { struct KeyMapLookUp<'a>(&'a KeyMap); - impl<'a> Translator for KeyMapLookUp<'a> { + impl Translator for KeyMapLookUp<'_> { type TargetPk = String; type Error = core::convert::Infallible; @@ -941,7 +941,7 @@ impl Descriptor { ) -> Result, ConversionError> { struct Derivator<'a, C: secp256k1::Verification>(&'a secp256k1::Secp256k1); - impl<'a, C: secp256k1::Verification> Translator for Derivator<'a, C> { + impl Translator for Derivator<'_, C> { type TargetPk = bitcoin::PublicKey; type Error = ConversionError; diff --git a/src/descriptor/tr.rs b/src/descriptor/tr.rs index 0348368e8..30d6c5c74 100644 --- a/src/descriptor/tr.rs +++ b/src/descriptor/tr.rs @@ -465,7 +465,7 @@ pub struct TapTreeIter<'a, Pk: MiniscriptKey> { stack: Vec<(u8, &'a TapTree)>, } -impl<'a, Pk: MiniscriptKey> TapTreeIter<'a, Pk> { +impl TapTreeIter<'_, Pk> { /// Helper function to return an empty iterator from Descriptor::tap_tree_iter. pub(super) fn empty() -> Self { Self { stack: vec![] } } } diff --git a/src/miniscript/iter.rs b/src/miniscript/iter.rs index 175fbfe4a..f82e329aa 100644 --- a/src/miniscript/iter.rs +++ b/src/miniscript/iter.rs @@ -176,7 +176,7 @@ impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> PkIter<'a, Pk, Ctx> { } } -impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> Iterator for PkIter<'a, Pk, Ctx> { +impl Iterator for PkIter<'_, Pk, Ctx> { type Item = Pk; fn next(&mut self) -> Option { diff --git a/src/miniscript/lex.rs b/src/miniscript/lex.rs index 6184572dd..30dab98e9 100644 --- a/src/miniscript/lex.rs +++ b/src/miniscript/lex.rs @@ -50,7 +50,7 @@ pub enum Token<'s> { Bytes65(&'s [u8]), } -impl<'s> fmt::Display for Token<'s> { +impl fmt::Display for Token<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Token::Num(n) => write!(f, "#{}", n), diff --git a/src/miniscript/satisfy.rs b/src/miniscript/satisfy.rs index 79c30613d..d1413401b 100644 --- a/src/miniscript/satisfy.rs +++ b/src/miniscript/satisfy.rs @@ -262,7 +262,7 @@ impl_satisfier_for_map_hash_tapleafhash_to_key_taproot_sig! { impl Satisfier for HashMap<(hash160::Hash, TapLeafHash), (Pk, bitcoin::taproot::Signature)> } -impl<'a, Pk: MiniscriptKey + ToPublicKey, S: Satisfier> Satisfier for &'a S { +impl> Satisfier for &S { fn lookup_ecdsa_sig(&self, p: &Pk) -> Option { (**self).lookup_ecdsa_sig(p) } @@ -322,7 +322,7 @@ impl<'a, Pk: MiniscriptKey + ToPublicKey, S: Satisfier> Satisfier for &' fn check_after(&self, n: absolute::LockTime) -> bool { (**self).check_after(n) } } -impl<'a, Pk: MiniscriptKey + ToPublicKey, S: Satisfier> Satisfier for &'a mut S { +impl> Satisfier for &mut S { fn lookup_ecdsa_sig(&self, p: &Pk) -> Option { (**self).lookup_ecdsa_sig(p) } diff --git a/src/plan.rs b/src/plan.rs index f3f148273..bcdfb311b 100644 --- a/src/plan.rs +++ b/src/plan.rs @@ -129,7 +129,7 @@ macro_rules! impl_log_method { } #[cfg(feature = "std")] -impl<'a> AssetProvider for LoggerAssetProvider<'a> { +impl AssetProvider for LoggerAssetProvider<'_> { impl_log_method!(provider_lookup_ecdsa_sig, pk: &DefiniteDescriptorKey, -> bool); impl_log_method!(provider_lookup_tap_key_spend_sig, pk: &DefiniteDescriptorKey, -> Option); impl_log_method!(provider_lookup_tap_leaf_script_sig, pk: &DefiniteDescriptorKey, leaf_hash: &TapLeafHash, -> Option); diff --git a/src/primitives/threshold.rs b/src/primitives/threshold.rs index 9a5315030..0045f9183 100644 --- a/src/primitives/threshold.rs +++ b/src/primitives/threshold.rs @@ -263,7 +263,7 @@ struct ThreshDisplay<'t, 's, T, const MAX: usize> { show_k: bool, } -impl<'t, 's, T, const MAX: usize> fmt::Display for ThreshDisplay<'t, 's, T, MAX> +impl fmt::Display for ThreshDisplay<'_, '_, T, MAX> where T: fmt::Display, { @@ -286,7 +286,7 @@ where } } -impl<'t, 's, T, const MAX: usize> fmt::Debug for ThreshDisplay<'t, 's, T, MAX> +impl fmt::Debug for ThreshDisplay<'_, '_, T, MAX> where T: fmt::Debug, { diff --git a/src/psbt/mod.rs b/src/psbt/mod.rs index 96615df4c..655878062 100644 --- a/src/psbt/mod.rs +++ b/src/psbt/mod.rs @@ -254,7 +254,7 @@ impl<'psbt> PsbtInputSatisfier<'psbt> { pub fn new(psbt: &'psbt Psbt, index: usize) -> Self { Self { psbt, index } } } -impl<'psbt, Pk: MiniscriptKey + ToPublicKey> Satisfier for PsbtInputSatisfier<'psbt> { +impl Satisfier for PsbtInputSatisfier<'_> { fn lookup_tap_key_spend_sig(&self) -> Option { self.psbt.inputs[self.index].tap_key_sig }