From de4b3e3b4bcd30dfeef5378e23f01762071dc7e2 Mon Sep 17 00:00:00 2001 From: Nadav Ivgi Date: Fri, 11 Oct 2024 02:47:00 +0300 Subject: [PATCH 01/12] Document an error case for at_derivation_index() --- src/descriptor/key.rs | 1 + src/descriptor/mod.rs | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/descriptor/key.rs b/src/descriptor/key.rs index 7e034ef99..bafe4c2d6 100644 --- a/src/descriptor/key.rs +++ b/src/descriptor/key.rs @@ -627,6 +627,7 @@ impl DescriptorPublicKey { /// # Errors /// /// - If `index` is hardened. + /// - If the key contains multi-path derivations pub fn at_derivation_index(self, index: u32) -> Result { let definite = match self { DescriptorPublicKey::Single(_) => self, diff --git a/src/descriptor/mod.rs b/src/descriptor/mod.rs index 487bbcc45..862fc58c6 100644 --- a/src/descriptor/mod.rs +++ b/src/descriptor/mod.rs @@ -596,6 +596,7 @@ impl Descriptor { /// /// # Errors /// - If index ≥ 2^31 + /// - If the descriptor contains multi-path derivations pub fn at_derivation_index( &self, index: u32, @@ -650,7 +651,8 @@ impl Descriptor { /// /// # Errors /// - /// This function will return an error if hardened derivation is attempted. + /// This function will return an error for multi-path descriptors + /// or if hardened derivation is attempted, pub fn derived_descriptor( &self, secp: &secp256k1::Secp256k1, From e9cb7ec4a1e955aeafaaf160eedb8107f8f1d711 Mon Sep 17 00:00:00 2001 From: Nadav Ivgi Date: Mon, 14 Oct 2024 04:46:59 +0300 Subject: [PATCH 02/12] Fix decoding of WIF with BIP32 origin --- src/descriptor/key.rs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/descriptor/key.rs b/src/descriptor/key.rs index bafe4c2d6..bcb74cdf6 100644 --- a/src/descriptor/key.rs +++ b/src/descriptor/key.rs @@ -703,7 +703,7 @@ impl FromStr for DescriptorSecretKey { if key_part.len() <= 52 { let sk = bitcoin::PrivateKey::from_str(key_part) .map_err(|_| DescriptorKeyParseError("Error while parsing a WIF private key"))?; - Ok(DescriptorSecretKey::Single(SinglePriv { key: sk, origin: None })) + Ok(DescriptorSecretKey::Single(SinglePriv { key: sk, origin })) } else { let (xpriv, derivation_paths, wildcard) = parse_xkey_deriv::(key_part)?; if derivation_paths.len() > 1 { @@ -1489,6 +1489,27 @@ mod test { DescriptorPublicKey::from_str("tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/2/4/<0;1;>").unwrap_err(); } + #[test] + fn test_parse_wif() { + let secret_key = "[0dd03d09/0'/1/2']5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ" + .parse() + .unwrap(); + if let DescriptorSecretKey::Single(single) = secret_key { + assert_eq!( + single.key.inner, + "0C28FCA386C7A227600B2FE50B7CAE11EC86D3BF1FBE471BE89827E19D72AA1D" + .parse() + .unwrap() + ); + assert_eq!( + single.origin, + Some(("0dd03d09".parse().unwrap(), "m/0'/1/2'".parse().unwrap())) + ); + } else { + panic!("expected a DescriptorSecretKey::Single"); + } + } + #[test] #[cfg(feature = "serde")] fn test_descriptor_public_key_serde() { From 88beb44bf86aa144b992d35a6aa9bf35d952efa9 Mon Sep 17 00:00:00 2001 From: ChrisCho-H Date: Thu, 17 Oct 2024 00:12:52 +0900 Subject: [PATCH 03/12] feat: add MaxBareScriptSizeExceeded err for bare consensus check --- src/miniscript/context.rs | 11 ++++++++++- src/miniscript/mod.rs | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/miniscript/context.rs b/src/miniscript/context.rs index bb53ed67a..45b4a03a2 100644 --- a/src/miniscript/context.rs +++ b/src/miniscript/context.rs @@ -52,6 +52,9 @@ pub enum ScriptContextError { /// The Miniscript (under p2sh context) corresponding Script would be /// larger than `MAX_SCRIPT_ELEMENT_SIZE` bytes. MaxRedeemScriptSizeExceeded, + /// The Miniscript(under bare context) corresponding + /// Script would be larger than `MAX_SCRIPT_SIZE` bytes. + MaxBareScriptSizeExceeded, /// The policy rules of bitcoin core only permit Script size upto 1650 bytes MaxScriptSigSizeExceeded, /// Impossible to satisfy the miniscript under the current context @@ -80,6 +83,7 @@ impl error::Error for ScriptContextError { | MaxOpCountExceeded | MaxWitnessScriptSizeExceeded | MaxRedeemScriptSizeExceeded + | MaxBareScriptSizeExceeded | MaxScriptSigSizeExceeded | ImpossibleSatisfaction | TaprootMultiDisabled @@ -127,6 +131,11 @@ impl fmt::Display for ScriptContextError { "The Miniscript corresponding Script would be larger than \ MAX_SCRIPT_ELEMENT_SIZE bytes." ), + ScriptContextError::MaxBareScriptSizeExceeded => write!( + f, + "The Miniscript corresponding Script would be larger than \ + MAX_SCRIPT_SIZE bytes." + ), ScriptContextError::MaxScriptSigSizeExceeded => write!( f, "At least one satisfaction in Miniscript would be larger than \ @@ -740,7 +749,7 @@ impl ScriptContext for BareCtx { match node_checked { Ok(_) => { if ms.ext.pk_cost > MAX_SCRIPT_SIZE { - Err(ScriptContextError::MaxWitnessScriptSizeExceeded) + Err(ScriptContextError::MaxBareScriptSizeExceeded) } else { Ok(()) } diff --git a/src/miniscript/mod.rs b/src/miniscript/mod.rs index 02829ac5f..79aa0a682 100644 --- a/src/miniscript/mod.rs +++ b/src/miniscript/mod.rs @@ -1694,7 +1694,7 @@ mod tests { ); assert_eq!( bare_multi_ms.unwrap_err().to_string(), - "The Miniscript corresponding Script would be larger than MAX_STANDARD_P2WSH_SCRIPT_SIZE bytes." + "The Miniscript corresponding Script would be larger than MAX_SCRIPT_SIZE bytes." ); } } From 63814698afa126e8dad8f8d87154a21b92cffb68 Mon Sep 17 00:00:00 2001 From: Nadav Ivgi Date: Thu, 17 Oct 2024 03:14:21 +0300 Subject: [PATCH 04/12] Support conversion of multi-Xprivs into multi-Xpubs Possible when all hardened derivation steps are shared among all paths (or if there are none). Errors otherwise. --- src/descriptor/key.rs | 108 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 101 insertions(+), 7 deletions(-) diff --git a/src/descriptor/key.rs b/src/descriptor/key.rs index bcb74cdf6..7e317f3a4 100644 --- a/src/descriptor/key.rs +++ b/src/descriptor/key.rs @@ -252,6 +252,89 @@ impl DescriptorXKey { } } +impl DescriptorMultiXKey { + /// Returns the public version of this multi-key, applying all the hardened derivation steps that + /// are shared among all derivation paths before turning it into a public key. + /// + /// Errors if there are hardened derivation steps that are not shared among all paths. + fn to_public( + &self, + secp: &Secp256k1, + ) -> Result, DescriptorKeyParseError> { + let deriv_paths = self.derivation_paths.paths(); + + let shared_prefix: Vec<_> = deriv_paths[0] + .into_iter() + .enumerate() + .take_while(|(index, child_num)| { + deriv_paths[1..].iter().all(|other_path| { + other_path.len() > *index && other_path[*index] == **child_num + }) + }) + .map(|(_, child_num)| *child_num) + .collect(); + + let suffixes: Vec> = deriv_paths + .iter() + .map(|path| { + path.into_iter() + .skip(shared_prefix.len()) + .map(|child_num| { + if child_num.is_normal() { + Ok(*child_num) + } else { + Err(DescriptorKeyParseError("Can't make a multi-xpriv with hardened derivation steps that are not shared among all paths into a public key.")) + } + }) + .collect() + }) + .collect::>()?; + + let unhardened = shared_prefix + .iter() + .rev() + .take_while(|c| c.is_normal()) + .count(); + let last_hardened_idx = shared_prefix.len() - unhardened; + let hardened_path = &shared_prefix[..last_hardened_idx]; + let unhardened_path = &shared_prefix[last_hardened_idx..]; + + let xprv = self + .xkey + .derive_priv(secp, &hardened_path) + .map_err(|_| DescriptorKeyParseError("Unable to derive the hardened steps"))?; + let xpub = bip32::Xpub::from_priv(secp, &xprv); + + let origin = match &self.origin { + Some((fingerprint, path)) => Some(( + *fingerprint, + path.into_iter() + .chain(hardened_path.iter()) + .copied() + .collect(), + )), + None if !hardened_path.is_empty() => { + Some((self.xkey.fingerprint(secp), hardened_path.into())) + } + None => None, + }; + let new_deriv_paths = suffixes + .into_iter() + .map(|suffix| { + let path = unhardened_path.iter().copied().chain(suffix); + path.collect::>().into() + }) + .collect(); + + Ok(DescriptorMultiXKey { + origin, + xkey: xpub, + derivation_paths: DerivPaths::new(new_deriv_paths).expect("not empty"), + wildcard: self.wildcard, + }) + } +} + /// Descriptor Key parsing errors // FIXME: replace with error enums #[derive(Debug, PartialEq, Clone, Copy)] @@ -309,9 +392,8 @@ impl DescriptorSecretKey { /// If the key is an "XPrv", the hardened derivation steps will be applied /// before converting it to a public key. /// - /// It will return an error if the key is a "multi-xpriv", as we wouldn't - /// always be able to apply hardened derivation steps if there are multiple - /// paths. + /// It will return an error if the key is a "multi-xpriv" that includes + /// hardened derivation steps not shared for all paths. pub fn to_public( &self, secp: &Secp256k1, @@ -319,10 +401,8 @@ impl DescriptorSecretKey { let pk = match self { DescriptorSecretKey::Single(prv) => DescriptorPublicKey::Single(prv.to_public(secp)), DescriptorSecretKey::XPrv(xprv) => DescriptorPublicKey::XPub(xprv.to_public(secp)?), - DescriptorSecretKey::MultiXPrv(_) => { - return Err(DescriptorKeyParseError( - "Can't make an extended private key with multiple paths into a public key.", - )) + DescriptorSecretKey::MultiXPrv(xprv) => { + DescriptorPublicKey::MultiXPub(xprv.to_public(secp)?) } }; @@ -1489,6 +1569,20 @@ mod test { DescriptorPublicKey::from_str("tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/2/4/<0;1;>").unwrap_err(); } + #[test] + fn test_multixprv_to_public() { + let secp = secp256k1::Secp256k1::signing_only(); + + // Works if all hardended derivation steps are part of the shared path + let xprv = get_multipath_xprv("[01020304/5]tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/1'/2'/3/<4;5>/6"); + let xpub = DescriptorPublicKey::MultiXPub(xprv.to_public(&secp).unwrap()); // wrap in a DescriptorPublicKey to have Display + assert_eq!(xpub.to_string(), "[01020304/5/1'/2']tpubDBTRkEMEFkUbk3WTz6CFSULyswkTPpPr38AWibf5TVkB5GxuBxbSbmdFGr3jmswwemknyYxAGoX7BJnKfyPy4WXaHmcrxZhfzFwoUFvFtm5/3/<4;5>/6"); + + // Fails if they're part of the multi-path specifier or following it + get_multipath_xprv("tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/1/2/<3';4'>/5").to_public(&secp).unwrap_err(); + get_multipath_xprv("tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/1/2/<3;4>/5/6'").to_public(&secp).unwrap_err(); + } + #[test] fn test_parse_wif() { let secret_key = "[0dd03d09/0'/1/2']5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ" From e20118e1640159413125e8a952f26ce3b23446e0 Mon Sep 17 00:00:00 2001 From: Nadav Ivgi Date: Thu, 17 Oct 2024 20:33:04 +0300 Subject: [PATCH 05/12] Some minor formatting changes --- src/descriptor/key.rs | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/src/descriptor/key.rs b/src/descriptor/key.rs index 7e317f3a4..c2e1fa1cc 100644 --- a/src/descriptor/key.rs +++ b/src/descriptor/key.rs @@ -227,20 +227,13 @@ impl DescriptorXKey { let xpub = bip32::Xpub::from_priv(secp, &xprv); let origin = match &self.origin { - Some((fingerprint, path)) => Some(( - *fingerprint, - path.into_iter() - .chain(hardened_path.iter()) - .cloned() - .collect(), - )), - None => { - if hardened_path.is_empty() { - None - } else { - Some((self.xkey.fingerprint(secp), hardened_path.into())) - } + Some((fingerprint, path)) => { + Some((*fingerprint, path.into_iter().chain(hardened_path).copied().collect())) } + None if !hardened_path.is_empty() => { + Some((self.xkey.fingerprint(secp), hardened_path.into())) + } + None => None, }; Ok(DescriptorXKey { @@ -306,13 +299,9 @@ impl DescriptorMultiXKey { let xpub = bip32::Xpub::from_priv(secp, &xprv); let origin = match &self.origin { - Some((fingerprint, path)) => Some(( - *fingerprint, - path.into_iter() - .chain(hardened_path.iter()) - .copied() - .collect(), - )), + Some((fingerprint, path)) => { + Some((*fingerprint, path.into_iter().chain(hardened_path).copied().collect())) + } None if !hardened_path.is_empty() => { Some((self.xkey.fingerprint(secp), hardened_path.into())) } From 6529a8701e3a02388e1a2ab4a22bc474f0decaea Mon Sep 17 00:00:00 2001 From: ChrisCho-H Date: Fri, 18 Oct 2024 14:02:07 +0900 Subject: [PATCH 06/12] fix: fix typo for MaxWitnessItemsExceeded --- src/miniscript/context.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/miniscript/context.rs b/src/miniscript/context.rs index 45b4a03a2..d394b116a 100644 --- a/src/miniscript/context.rs +++ b/src/miniscript/context.rs @@ -42,7 +42,7 @@ pub enum ScriptContextError { UncompressedKeysNotAllowed, /// At least one satisfaction path in the Miniscript fragment has more than /// `MAX_STANDARD_P2WSH_STACK_ITEMS` (100) witness elements. - MaxWitnessItemssExceeded { actual: usize, limit: usize }, + MaxWitnessItemsExceeded { actual: usize, limit: usize }, /// At least one satisfaction path in the Miniscript fragment contains more /// than `MAX_OPS_PER_SCRIPT`(201) opcodes. MaxOpCountExceeded, @@ -79,7 +79,7 @@ impl error::Error for ScriptContextError { | CompressedOnly(_) | XOnlyKeysNotAllowed(_, _) | UncompressedKeysNotAllowed - | MaxWitnessItemssExceeded { .. } + | MaxWitnessItemsExceeded { .. } | MaxOpCountExceeded | MaxWitnessScriptSizeExceeded | MaxRedeemScriptSizeExceeded @@ -110,7 +110,7 @@ impl fmt::Display for ScriptContextError { ScriptContextError::UncompressedKeysNotAllowed => { write!(f, "uncompressed keys cannot be used in Taproot descriptors.") } - ScriptContextError::MaxWitnessItemssExceeded { actual, limit } => write!( + ScriptContextError::MaxWitnessItemsExceeded { actual, limit } => write!( f, "At least one spending path in the Miniscript fragment has {} more \ witness items than limit {}.", @@ -498,7 +498,7 @@ impl ScriptContext for Segwitv0 { fn check_witness(witness: &[Vec]) -> Result<(), ScriptContextError> { if witness.len() > MAX_STANDARD_P2WSH_STACK_ITEMS { - return Err(ScriptContextError::MaxWitnessItemssExceeded { + return Err(ScriptContextError::MaxWitnessItemsExceeded { actual: witness.len(), limit: MAX_STANDARD_P2WSH_STACK_ITEMS, }); @@ -565,7 +565,7 @@ impl ScriptContext for Segwitv0 { // No possible satisfactions Err(_e) => Err(ScriptContextError::ImpossibleSatisfaction), Ok(max_witness_items) if max_witness_items > MAX_STANDARD_P2WSH_STACK_ITEMS => { - Err(ScriptContextError::MaxWitnessItemssExceeded { + Err(ScriptContextError::MaxWitnessItemsExceeded { actual: max_witness_items, limit: MAX_STANDARD_P2WSH_STACK_ITEMS, }) @@ -612,7 +612,7 @@ impl ScriptContext for Tap { fn check_witness(witness: &[Vec]) -> Result<(), ScriptContextError> { // Note that tapscript has a 1000 limit compared to 100 of segwitv0 if witness.len() > MAX_STACK_SIZE { - return Err(ScriptContextError::MaxWitnessItemssExceeded { + return Err(ScriptContextError::MaxWitnessItemsExceeded { actual: witness.len(), limit: MAX_STACK_SIZE, }); From d8c32e882316c34647f4223dc9014df6164fe0cf Mon Sep 17 00:00:00 2001 From: ChrisCho-H Date: Fri, 18 Oct 2024 13:39:59 +0900 Subject: [PATCH 07/12] fix: differentiate max witness script size upon context --- src/miniscript/context.rs | 29 ++++++++++++++++++++--------- src/miniscript/mod.rs | 2 +- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/miniscript/context.rs b/src/miniscript/context.rs index d394b116a..ad810da1f 100644 --- a/src/miniscript/context.rs +++ b/src/miniscript/context.rs @@ -47,8 +47,9 @@ pub enum ScriptContextError { /// than `MAX_OPS_PER_SCRIPT`(201) opcodes. MaxOpCountExceeded, /// The Miniscript(under segwit context) corresponding - /// Script would be larger than `MAX_STANDARD_P2WSH_SCRIPT_SIZE` bytes. - MaxWitnessScriptSizeExceeded, + /// Script would be larger than `MAX_STANDARD_P2WSH_SCRIPT_SIZE`, + /// `MAX_SCRIPT_SIZE` or `MAX_BLOCK`(`Tap`) bytes. + MaxWitnessScriptSizeExceeded { max: usize, got: usize }, /// The Miniscript (under p2sh context) corresponding Script would be /// larger than `MAX_SCRIPT_ELEMENT_SIZE` bytes. MaxRedeemScriptSizeExceeded, @@ -81,7 +82,7 @@ impl error::Error for ScriptContextError { | UncompressedKeysNotAllowed | MaxWitnessItemsExceeded { .. } | MaxOpCountExceeded - | MaxWitnessScriptSizeExceeded + | MaxWitnessScriptSizeExceeded { .. } | MaxRedeemScriptSizeExceeded | MaxBareScriptSizeExceeded | MaxScriptSigSizeExceeded @@ -121,10 +122,11 @@ impl fmt::Display for ScriptContextError { "At least one satisfaction path in the Miniscript fragment contains \ more than MAX_OPS_PER_SCRIPT opcodes." ), - ScriptContextError::MaxWitnessScriptSizeExceeded => write!( + ScriptContextError::MaxWitnessScriptSizeExceeded { max, got } => write!( f, - "The Miniscript corresponding Script would be larger than \ - MAX_STANDARD_P2WSH_SCRIPT_SIZE bytes." + "The Miniscript corresponding Script cannot be larger than \ + {} bytes, but got {} bytes.", + max, got ), ScriptContextError::MaxRedeemScriptSizeExceeded => write!( f, @@ -525,7 +527,10 @@ impl ScriptContext for Segwitv0 { match node_checked { Ok(_) => { if ms.ext.pk_cost > MAX_SCRIPT_SIZE { - Err(ScriptContextError::MaxWitnessScriptSizeExceeded) + Err(ScriptContextError::MaxWitnessScriptSizeExceeded { + max: MAX_SCRIPT_SIZE, + got: ms.ext.pk_cost, + }) } else { Ok(()) } @@ -550,7 +555,10 @@ impl ScriptContext for Segwitv0 { ms: &Miniscript, ) -> Result<(), ScriptContextError> { if ms.ext.pk_cost > MAX_STANDARD_P2WSH_SCRIPT_SIZE { - return Err(ScriptContextError::MaxWitnessScriptSizeExceeded); + return Err(ScriptContextError::MaxWitnessScriptSizeExceeded { + max: MAX_STANDARD_P2WSH_SCRIPT_SIZE, + got: ms.ext.pk_cost, + }); } Ok(()) } @@ -644,7 +652,10 @@ impl ScriptContext for Tap { // some guarantees are not easy to satisfy because of knapsack // constraints if ms.ext.pk_cost as u64 > Weight::MAX_BLOCK.to_wu() { - Err(ScriptContextError::MaxWitnessScriptSizeExceeded) + Err(ScriptContextError::MaxWitnessScriptSizeExceeded { + max: Weight::MAX_BLOCK.to_wu() as usize, + got: ms.ext.pk_cost, + }) } else { Ok(()) } diff --git a/src/miniscript/mod.rs b/src/miniscript/mod.rs index 79aa0a682..c2da2855f 100644 --- a/src/miniscript/mod.rs +++ b/src/miniscript/mod.rs @@ -1690,7 +1690,7 @@ mod tests { ); assert_eq!( segwit_multi_ms.unwrap_err().to_string(), - "The Miniscript corresponding Script would be larger than MAX_STANDARD_P2WSH_SCRIPT_SIZE bytes." + "The Miniscript corresponding Script cannot be larger than 3600 bytes, but got 4110 bytes." ); assert_eq!( bare_multi_ms.unwrap_err().to_string(), From d2fe4cd029348d1ec0ec2504fc0faa5963f984d8 Mon Sep 17 00:00:00 2001 From: ChrisCho-H Date: Sat, 19 Oct 2024 20:34:31 +0900 Subject: [PATCH 08/12] fix: use ImpossibleSatisfaction when op_count is None --- src/miniscript/context.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/miniscript/context.rs b/src/miniscript/context.rs index ad810da1f..980b20b58 100644 --- a/src/miniscript/context.rs +++ b/src/miniscript/context.rs @@ -434,7 +434,7 @@ impl ScriptContext for Legacy { ms: &Miniscript, ) -> Result<(), ScriptContextError> { match ms.ext.ops.op_count() { - None => Err(ScriptContextError::MaxOpCountExceeded), + None => Err(ScriptContextError::ImpossibleSatisfaction), Some(op_count) if op_count > MAX_OPS_PER_SCRIPT => { Err(ScriptContextError::MaxOpCountExceeded) } @@ -543,7 +543,7 @@ impl ScriptContext for Segwitv0 { ms: &Miniscript, ) -> Result<(), ScriptContextError> { match ms.ext.ops.op_count() { - None => Err(ScriptContextError::MaxOpCountExceeded), + None => Err(ScriptContextError::ImpossibleSatisfaction), Some(op_count) if op_count > MAX_OPS_PER_SCRIPT => { Err(ScriptContextError::MaxOpCountExceeded) } @@ -773,7 +773,7 @@ impl ScriptContext for BareCtx { ms: &Miniscript, ) -> Result<(), ScriptContextError> { match ms.ext.ops.op_count() { - None => Err(ScriptContextError::MaxOpCountExceeded), + None => Err(ScriptContextError::ImpossibleSatisfaction), Some(op_count) if op_count > MAX_OPS_PER_SCRIPT => { Err(ScriptContextError::MaxOpCountExceeded) } From f3410012100dcfd1dbd214075acf05ba66d38c1c Mon Sep 17 00:00:00 2001 From: ChrisCho-H Date: Sun, 20 Oct 2024 10:21:27 +0900 Subject: [PATCH 09/12] feat: unify err msg to contain helpful info --- src/miniscript/context.rs | 88 +++++++++++++++++++++++++-------------- src/miniscript/mod.rs | 4 +- 2 files changed, 59 insertions(+), 33 deletions(-) diff --git a/src/miniscript/context.rs b/src/miniscript/context.rs index 980b20b58..9ddb5322e 100644 --- a/src/miniscript/context.rs +++ b/src/miniscript/context.rs @@ -45,19 +45,19 @@ pub enum ScriptContextError { MaxWitnessItemsExceeded { actual: usize, limit: usize }, /// At least one satisfaction path in the Miniscript fragment contains more /// than `MAX_OPS_PER_SCRIPT`(201) opcodes. - MaxOpCountExceeded, + MaxOpCountExceeded { actual: usize, limit: usize }, /// The Miniscript(under segwit context) corresponding /// Script would be larger than `MAX_STANDARD_P2WSH_SCRIPT_SIZE`, /// `MAX_SCRIPT_SIZE` or `MAX_BLOCK`(`Tap`) bytes. MaxWitnessScriptSizeExceeded { max: usize, got: usize }, /// The Miniscript (under p2sh context) corresponding Script would be /// larger than `MAX_SCRIPT_ELEMENT_SIZE` bytes. - MaxRedeemScriptSizeExceeded, + MaxRedeemScriptSizeExceeded { max: usize, got: usize }, /// The Miniscript(under bare context) corresponding /// Script would be larger than `MAX_SCRIPT_SIZE` bytes. - MaxBareScriptSizeExceeded, + MaxBareScriptSizeExceeded { max: usize, got: usize }, /// The policy rules of bitcoin core only permit Script size upto 1650 bytes - MaxScriptSigSizeExceeded, + MaxScriptSigSizeExceeded { actual: usize, limit: usize }, /// Impossible to satisfy the miniscript under the current context ImpossibleSatisfaction, /// No Multi Node in Taproot context @@ -81,11 +81,11 @@ impl error::Error for ScriptContextError { | XOnlyKeysNotAllowed(_, _) | UncompressedKeysNotAllowed | MaxWitnessItemsExceeded { .. } - | MaxOpCountExceeded + | MaxOpCountExceeded { .. } | MaxWitnessScriptSizeExceeded { .. } - | MaxRedeemScriptSizeExceeded - | MaxBareScriptSizeExceeded - | MaxScriptSigSizeExceeded + | MaxRedeemScriptSizeExceeded { .. } + | MaxBareScriptSizeExceeded { .. } + | MaxScriptSigSizeExceeded { .. } | ImpossibleSatisfaction | TaprootMultiDisabled | StackSizeLimitExceeded { .. } @@ -113,35 +113,39 @@ impl fmt::Display for ScriptContextError { } ScriptContextError::MaxWitnessItemsExceeded { actual, limit } => write!( f, - "At least one spending path in the Miniscript fragment has {} more \ - witness items than limit {}.", + "At least one satisfaction path in the Miniscript fragment has {} witness items \ + (limit: {}).", actual, limit ), - ScriptContextError::MaxOpCountExceeded => write!( + ScriptContextError::MaxOpCountExceeded { actual, limit } => write!( f, - "At least one satisfaction path in the Miniscript fragment contains \ - more than MAX_OPS_PER_SCRIPT opcodes." + "At least one satisfaction path in the Miniscript fragment contains {} opcodes \ + (limit: {}).", + actual, limit ), ScriptContextError::MaxWitnessScriptSizeExceeded { max, got } => write!( f, "The Miniscript corresponding Script cannot be larger than \ - {} bytes, but got {} bytes.", + {} bytes, but got {} bytes.", max, got ), - ScriptContextError::MaxRedeemScriptSizeExceeded => write!( + ScriptContextError::MaxRedeemScriptSizeExceeded { max, got } => write!( f, - "The Miniscript corresponding Script would be larger than \ - MAX_SCRIPT_ELEMENT_SIZE bytes." + "The Miniscript corresponding Script cannot be larger than \ + {} bytes, but got {} bytes.", + max, got ), - ScriptContextError::MaxBareScriptSizeExceeded => write!( + ScriptContextError::MaxBareScriptSizeExceeded { max, got } => write!( f, - "The Miniscript corresponding Script would be larger than \ - MAX_SCRIPT_SIZE bytes." + "The Miniscript corresponding Script cannot be larger than \ + {} bytes, but got {} bytes.", + max, got ), - ScriptContextError::MaxScriptSigSizeExceeded => write!( + ScriptContextError::MaxScriptSigSizeExceeded { actual, limit } => write!( f, - "At least one satisfaction in Miniscript would be larger than \ - MAX_SCRIPTSIG_SIZE scriptsig" + "At least one satisfaction path in the Miniscript fragment has {} bytes \ + (limit: {}).", + actual, limit ), ScriptContextError::ImpossibleSatisfaction => { write!(f, "Impossible to satisfy Miniscript under the current context") @@ -396,8 +400,12 @@ impl ScriptContext for Legacy { fn check_witness(witness: &[Vec]) -> Result<(), ScriptContextError> { // In future, we could avoid by having a function to count only // len of script instead of converting it. - if witness_to_scriptsig(witness).len() > MAX_SCRIPTSIG_SIZE { - return Err(ScriptContextError::MaxScriptSigSizeExceeded); + let script_sig = witness_to_scriptsig(witness); + if script_sig.len() > MAX_SCRIPTSIG_SIZE { + return Err(ScriptContextError::MaxScriptSigSizeExceeded { + actual: script_sig.len(), + limit: MAX_SCRIPTSIG_SIZE, + }); } Ok(()) } @@ -421,7 +429,10 @@ impl ScriptContext for Legacy { match node_checked { Ok(_) => { if ms.ext.pk_cost > MAX_SCRIPT_ELEMENT_SIZE { - Err(ScriptContextError::MaxRedeemScriptSizeExceeded) + Err(ScriptContextError::MaxRedeemScriptSizeExceeded { + max: MAX_SCRIPT_ELEMENT_SIZE, + got: ms.ext.pk_cost, + }) } else { Ok(()) } @@ -436,7 +447,10 @@ impl ScriptContext for Legacy { match ms.ext.ops.op_count() { None => Err(ScriptContextError::ImpossibleSatisfaction), Some(op_count) if op_count > MAX_OPS_PER_SCRIPT => { - Err(ScriptContextError::MaxOpCountExceeded) + Err(ScriptContextError::MaxOpCountExceeded { + actual: op_count, + limit: MAX_OPS_PER_SCRIPT, + }) } _ => Ok(()), } @@ -451,7 +465,10 @@ impl ScriptContext for Legacy { match ms.max_satisfaction_size() { Err(_e) => Err(ScriptContextError::ImpossibleSatisfaction), Ok(size) if size > MAX_SCRIPTSIG_SIZE => { - Err(ScriptContextError::MaxScriptSigSizeExceeded) + Err(ScriptContextError::MaxScriptSigSizeExceeded { + actual: size, + limit: MAX_SCRIPTSIG_SIZE, + }) } _ => Ok(()), } @@ -545,7 +562,10 @@ impl ScriptContext for Segwitv0 { match ms.ext.ops.op_count() { None => Err(ScriptContextError::ImpossibleSatisfaction), Some(op_count) if op_count > MAX_OPS_PER_SCRIPT => { - Err(ScriptContextError::MaxOpCountExceeded) + Err(ScriptContextError::MaxOpCountExceeded { + actual: op_count, + limit: MAX_OPS_PER_SCRIPT, + }) } _ => Ok(()), } @@ -760,7 +780,10 @@ impl ScriptContext for BareCtx { match node_checked { Ok(_) => { if ms.ext.pk_cost > MAX_SCRIPT_SIZE { - Err(ScriptContextError::MaxBareScriptSizeExceeded) + Err(ScriptContextError::MaxBareScriptSizeExceeded { + max: MAX_SCRIPT_SIZE, + got: ms.ext.pk_cost, + }) } else { Ok(()) } @@ -775,7 +798,10 @@ impl ScriptContext for BareCtx { match ms.ext.ops.op_count() { None => Err(ScriptContextError::ImpossibleSatisfaction), Some(op_count) if op_count > MAX_OPS_PER_SCRIPT => { - Err(ScriptContextError::MaxOpCountExceeded) + Err(ScriptContextError::MaxOpCountExceeded { + actual: op_count, + limit: MAX_OPS_PER_SCRIPT, + }) } _ => Ok(()), } diff --git a/src/miniscript/mod.rs b/src/miniscript/mod.rs index c2da2855f..c03fbf218 100644 --- a/src/miniscript/mod.rs +++ b/src/miniscript/mod.rs @@ -1686,7 +1686,7 @@ mod tests { // Should panic for exceeding the max consensus size, as multi properly used assert_eq!( legacy_multi_ms.unwrap_err().to_string(), - "The Miniscript corresponding Script would be larger than MAX_SCRIPT_ELEMENT_SIZE bytes." + "The Miniscript corresponding Script cannot be larger than 520 bytes, but got 685 bytes." ); assert_eq!( segwit_multi_ms.unwrap_err().to_string(), @@ -1694,7 +1694,7 @@ mod tests { ); assert_eq!( bare_multi_ms.unwrap_err().to_string(), - "The Miniscript corresponding Script would be larger than MAX_SCRIPT_SIZE bytes." + "The Miniscript corresponding Script cannot be larger than 10000 bytes, but got 10275 bytes." ); } } From a46b0961fd1751bb9a1965a684a8a229b4c622ce Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Thu, 10 Oct 2024 13:12:06 +0000 Subject: [PATCH 10/12] 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 862fc58c6..34ef35961 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 c03fbf218..79a9e9d57 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 cb5c372f3c673dd9fc6ed54db36e026884a02474 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Thu, 10 Oct 2024 13:39:37 +0000 Subject: [PATCH 11/12] 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 9c08c0feb390a9949b6ca6aa685bd289abb70a4b Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Thu, 10 Oct 2024 13:43:32 +0000 Subject: [PATCH 12/12] 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 34ef35961..b172aa69e 100644 --- a/src/descriptor/mod.rs +++ b/src/descriptor/mod.rs @@ -732,7 +732,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; @@ -780,7 +780,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; @@ -943,7 +943,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 }