diff --git a/examples/xpub_descriptors.rs b/examples/xpub_descriptors.rs index c66f26307..54f68ba29 100644 --- a/examples/xpub_descriptors.rs +++ b/examples/xpub_descriptors.rs @@ -18,7 +18,7 @@ use std::str::FromStr; use miniscript::bitcoin::secp256k1::{Secp256k1, Verification}; use miniscript::bitcoin::{Address, Network}; -use miniscript::{Descriptor, DescriptorPublicKey, TranslatePk2}; +use miniscript::{Descriptor, DescriptorPublicKey}; const XPUB_1: &str = "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB"; const XPUB_2: &str = "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH"; @@ -42,7 +42,7 @@ fn p2wsh(secp: &Secp256k1) -> Address { let address = Descriptor::::from_str(&s) .unwrap() - .translate_pk2(|xpk| xpk.derive_public_key(secp)) + .derived_descriptor(&secp, 0) // dummy index value if it not a wildcard .unwrap() .address(Network::Bitcoin) .unwrap(); diff --git a/src/descriptor/bare.rs b/src/descriptor/bare.rs index be5be5483..0b3159ab6 100644 --- a/src/descriptor/bare.rs +++ b/src/descriptor/bare.rs @@ -19,7 +19,6 @@ //! use core::fmt; -use core::str::FromStr; use bitcoin::blockdata::script; use bitcoin::{Address, Network, Script}; @@ -32,7 +31,7 @@ use crate::prelude::*; use crate::util::{varint_len, witness_to_scriptsig}; use crate::{ BareCtx, Error, ForEach, ForEachKey, Miniscript, MiniscriptKey, Satisfier, ToPublicKey, - TranslatePk, + TranslatePk, Translator, }; /// Create a Bare Descriptor. That is descriptor that is @@ -145,35 +144,24 @@ impl Liftable for Bare { } } -impl FromTree for Bare -where - Pk: MiniscriptKey + FromStr, - Pk::Hash: FromStr, - ::Err: ToString, - <::Hash as FromStr>::Err: ToString, -{ +impl_from_tree!( + Bare, fn from_tree(top: &expression::Tree) -> Result { let sub = Miniscript::::from_tree(top)?; BareCtx::top_level_checks(&sub)?; Bare::new(sub) } -} - -impl FromStr for Bare -where - Pk: MiniscriptKey + FromStr, - Pk::Hash: FromStr, - ::Err: ToString, - <::Hash as FromStr>::Err: ToString, -{ - type Err = Error; +); +impl_from_str!( + Bare, + type Err = Error;, fn from_str(s: &str) -> Result { let desc_str = verify_checksum(s)?; let top = expression::Tree::from_str(desc_str)?; Self::from_tree(&top) } -} +); impl ForEachKey for Bare { fn for_each_key<'a, F: FnMut(ForEach<'a, Pk>) -> bool>(&'a self, pred: F) -> bool @@ -192,14 +180,11 @@ where { type Output = Bare; - fn translate_pk(&self, mut fpk: Fpk, mut fpkh: Fpkh) -> Result + fn translate_pk(&self, t: &mut T) -> Result where - Fpk: FnMut(&P) -> Result, - Fpkh: FnMut(&P::Hash) -> Result, - Q: MiniscriptKey, + T: Translator, { - Ok(Bare::new(self.ms.translate_pk(&mut fpk, &mut fpkh)?) - .expect("Translation cannot fail inside Bare")) + Ok(Bare::new(self.ms.translate_pk(t)?).expect("Translation cannot fail inside Bare")) } } @@ -313,13 +298,8 @@ impl Liftable for Pkh { } } -impl FromTree for Pkh -where - Pk: MiniscriptKey + FromStr, - Pk::Hash: FromStr, - ::Err: ToString, - <::Hash as FromStr>::Err: ToString, -{ +impl_from_tree!( + Pkh, fn from_tree(top: &expression::Tree) -> Result { if top.name == "pkh" && top.args.len() == 1 { Ok(Pkh::new(expression::terminal(&top.args[0], |pk| { @@ -333,23 +313,17 @@ where ))) } } -} - -impl FromStr for Pkh -where - Pk: MiniscriptKey + FromStr, - Pk::Hash: FromStr, - ::Err: ToString, - <::Hash as FromStr>::Err: ToString, -{ - type Err = Error; +); +impl_from_str!( + Pkh, + type Err = Error;, fn from_str(s: &str) -> Result { let desc_str = verify_checksum(s)?; let top = expression::Tree::from_str(desc_str)?; Self::from_tree(&top) } -} +); impl ForEachKey for Pkh { fn for_each_key<'a, F: FnMut(ForEach<'a, Pk>) -> bool>(&'a self, mut pred: F) -> bool @@ -368,11 +342,10 @@ where { type Output = Pkh; - fn translate_pk(&self, mut fpk: Fpk, _fpkh: Fpkh) -> Result + fn translate_pk(&self, t: &mut T) -> Result where - Fpk: FnMut(&P) -> Result, - Fpkh: FnMut(&P::Hash) -> Result, + T: Translator, { - Ok(Pkh::new(fpk(&self.pk)?)) + Ok(Pkh::new(t.pk(&self.pk)?)) } } diff --git a/src/descriptor/key.rs b/src/descriptor/key.rs index 657100d6e..edcd43b6d 100644 --- a/src/descriptor/key.rs +++ b/src/descriptor/key.rs @@ -4,7 +4,7 @@ use core::str::FromStr; use std::error; use bitcoin::hashes::hex::FromHex; -use bitcoin::hashes::{hash160, Hash, HashEngine}; +use bitcoin::hashes::{hash160, sha256, Hash, HashEngine}; use bitcoin::secp256k1::{Secp256k1, Signing, Verification}; use bitcoin::util::bip32; use bitcoin::{self, XOnlyPublicKey, XpubIdentifier}; @@ -737,6 +737,7 @@ impl DescriptorXKey { impl MiniscriptKey for DescriptorPublicKey { // This allows us to be able to derive public keys even for PkH s type Hash = Self; + type Sha256 = bitcoin::hashes::sha256::Hash; fn is_uncompressed(&self) -> bool { match self { @@ -802,6 +803,7 @@ impl fmt::Display for DerivedDescriptorKey { impl MiniscriptKey for DerivedDescriptorKey { // This allows us to be able to derive public keys even for PkH s type Hash = Self; + type Sha256 = bitcoin::hashes::sha256::Hash; fn is_uncompressed(&self) -> bool { self.key.is_uncompressed() @@ -825,6 +827,10 @@ impl ToPublicKey for DerivedDescriptorKey { fn hash_to_hash160(hash: &Self) -> hash160::Hash { hash.to_public_key().to_pubkeyhash() } + + fn to_sha256(hash: &sha256::Hash) -> sha256::Hash { + *hash + } } #[cfg(test)] diff --git a/src/descriptor/mod.rs b/src/descriptor/mod.rs index 57bc91de2..46d1adfa9 100644 --- a/src/descriptor/mod.rs +++ b/src/descriptor/mod.rs @@ -28,6 +28,7 @@ use core::ops::Range; use core::str::{self, FromStr}; use bitcoin::blockdata::witness::Witness; +use bitcoin::hashes::sha256; use bitcoin::util::address::WitnessVersion; use bitcoin::{self, secp256k1, Address, Network, Script, TxIn}; use sync::Arc; @@ -36,8 +37,8 @@ use self::checksum::verify_checksum; use crate::miniscript::{Legacy, Miniscript, Segwitv0}; use crate::prelude::*; use crate::{ - expression, miniscript, BareCtx, Error, ForEach, ForEachKey, MiniscriptKey, Satisfier, - ToPublicKey, TranslatePk, TranslatePk2, + expression, miniscript, BareCtx, Error, ForEach, ForEachKey, MiniscriptKey, PkTranslator, + Satisfier, ToPublicKey, TranslatePk, Translator, }; mod bare; @@ -475,25 +476,19 @@ where Q: MiniscriptKey, { type Output = Descriptor; + /// Converts a descriptor using abstract keys to one using specific keys. - /// - /// # Panics - /// - /// If `fpk` returns an uncompressed key when converting to a Segwit descriptor. - /// To prevent this panic, ensure `fpk` returns an error in this case instead. - fn translate_pk(&self, mut fpk: Fpk, mut fpkh: Fpkh) -> Result, E> + fn translate_pk(&self, t: &mut T) -> Result where - Fpk: FnMut(&P) -> Result, - Fpkh: FnMut(&P::Hash) -> Result, - Q: MiniscriptKey, + T: Translator, { let desc = match *self { - Descriptor::Bare(ref bare) => Descriptor::Bare(bare.translate_pk(&mut fpk, &mut fpkh)?), - Descriptor::Pkh(ref pk) => Descriptor::Pkh(pk.translate_pk(&mut fpk, &mut fpkh)?), - Descriptor::Wpkh(ref pk) => Descriptor::Wpkh(pk.translate_pk(&mut fpk, &mut fpkh)?), - Descriptor::Sh(ref sh) => Descriptor::Sh(sh.translate_pk(&mut fpk, &mut fpkh)?), - Descriptor::Wsh(ref wsh) => Descriptor::Wsh(wsh.translate_pk(&mut fpk, &mut fpkh)?), - Descriptor::Tr(ref tr) => Descriptor::Tr(tr.translate_pk(&mut fpk, &mut fpkh)?), + Descriptor::Bare(ref bare) => Descriptor::Bare(bare.translate_pk(t)?), + Descriptor::Pkh(ref pk) => Descriptor::Pkh(pk.translate_pk(t)?), + Descriptor::Wpkh(ref pk) => Descriptor::Wpkh(pk.translate_pk(t)?), + Descriptor::Sh(ref sh) => Descriptor::Sh(sh.translate_pk(t)?), + Descriptor::Wsh(ref wsh) => Descriptor::Wsh(wsh.translate_pk(t)?), + Descriptor::Tr(ref tr) => Descriptor::Tr(tr.translate_pk(t)?), }; Ok(desc) } @@ -529,7 +524,19 @@ impl Descriptor { /// In most cases, you would want to use [`Self::derived_descriptor`] directly to obtain /// a [`Descriptor`] pub fn derive(&self, index: u32) -> Descriptor { - self.translate_pk2_infallible(|pk| pk.clone().derive(index)) + struct Derivator(u32); + + impl PkTranslator for Derivator { + fn pk(&mut self, pk: &DescriptorPublicKey) -> Result { + Ok(pk.clone().derive(self.0)) + } + + fn pkh(&mut self, pkh: &DescriptorPublicKey) -> Result { + Ok(pkh.clone().derive(self.0)) + } + } + self.translate_pk(&mut Derivator(index)) + .expect("BIP 32 key index substitution cannot fail") } /// Derive a [`Descriptor`] with a concrete [`bitcoin::PublicKey`] at a given index @@ -561,9 +568,28 @@ impl Descriptor { secp: &secp256k1::Secp256k1, index: u32, ) -> Result, ConversionError> { - let derived = self - .derive(index) - .translate_pk2(|xpk| xpk.derive_public_key(secp))?; + struct Derivator<'a, C: secp256k1::Verification>(&'a secp256k1::Secp256k1); + + impl<'a, C: secp256k1::Verification> + PkTranslator + for Derivator<'a, C> + { + fn pk( + &mut self, + pk: &DerivedDescriptorKey, + ) -> Result { + pk.derive_public_key(&self.0) + } + + fn pkh( + &mut self, + pkh: &DerivedDescriptorKey, + ) -> Result { + Ok(pkh.derive_public_key(&self.0)?.to_pubkeyhash()) + } + } + + let derived = self.derive(index).translate_pk(&mut Derivator(secp))?; Ok(derived) } @@ -575,12 +601,22 @@ impl Descriptor { secp: &secp256k1::Secp256k1, s: &str, ) -> Result<(Descriptor, KeyMap), Error> { - let parse_key = |s: &String, - key_map: &mut KeyMap| - -> Result { + fn parse_key( + s: &String, + key_map: &mut KeyMap, + secp: &secp256k1::Secp256k1, + ) -> Result { let (public_key, secret_key) = match DescriptorSecretKey::from_str(s) { - Ok(sk) => (sk.to_public(secp)?, Some(sk)), - Err(_) => (DescriptorPublicKey::from_str(s)?, None), + Ok(sk) => ( + sk.to_public(secp) + .map_err(|e| Error::Unexpected(e.to_string()))?, + Some(sk), + ), + Err(_) => ( + DescriptorPublicKey::from_str(s) + .map_err(|e| Error::Unexpected(e.to_string()))?, + None, + ), }; if let Some(secret_key) = secret_key { @@ -588,26 +624,56 @@ impl Descriptor { } Ok(public_key) - }; + } + + let mut keymap_pk = KeyMapWrapper(HashMap::new(), secp); + + struct KeyMapWrapper<'a, C: secp256k1::Signing>(KeyMap, &'a secp256k1::Secp256k1); + + impl<'a, C: secp256k1::Signing> Translator + for KeyMapWrapper<'a, C> + { + fn pk(&mut self, pk: &String) -> Result { + parse_key(pk, &mut self.0, self.1) + } + + fn pkh(&mut self, pkh: &String) -> Result { + parse_key(pkh, &mut self.0, self.1) + } - let mut keymap_pk = KeyMap::new(); - let mut keymap_pkh = KeyMap::new(); + fn sha256(&mut self, sha256: &String) -> Result { + let hash = + sha256::Hash::from_str(sha256).map_err(|e| Error::Unexpected(e.to_string()))?; + Ok(hash) + } + } let descriptor = Descriptor::::from_str(s)?; let descriptor = descriptor - .translate_pk( - |pk| parse_key(pk, &mut keymap_pk), - |pkh| parse_key(pkh, &mut keymap_pkh), - ) + .translate_pk(&mut keymap_pk) .map_err(|e| Error::Unexpected(e.to_string()))?; - keymap_pk.extend(keymap_pkh.into_iter()); - - Ok((descriptor, keymap_pk)) + Ok((descriptor, keymap_pk.0)) } /// Serialize a descriptor to string with its secret keys pub fn to_string_with_secret(&self, key_map: &KeyMap) -> String { + struct KeyMapLookUp<'a>(&'a KeyMap); + + impl<'a> Translator for KeyMapLookUp<'a> { + fn pk(&mut self, pk: &DescriptorPublicKey) -> Result { + key_to_string(pk, self.0) + } + + fn pkh(&mut self, pkh: &DescriptorPublicKey) -> Result { + key_to_string(pkh, self.0) + } + + fn sha256(&mut self, sha256: &sha256::Hash) -> Result { + Ok(sha256.to_string()) + } + } + fn key_to_string(pk: &DescriptorPublicKey, key_map: &KeyMap) -> Result { Ok(match key_map.get(pk) { Some(secret) => secret.to_string(), @@ -616,10 +682,7 @@ impl Descriptor { } let descriptor = self - .translate_pk::<_, _, ()>( - |pk| key_to_string(pk, key_map), - |pkh| key_to_string(pkh, key_map), - ) + .translate_pk(&mut KeyMapLookUp(key_map)) .expect("Translation to string cannot fail"); descriptor.to_string() @@ -651,14 +714,9 @@ impl Descriptor { } } -impl expression::FromTree for Descriptor -where - Pk: MiniscriptKey + str::FromStr, - Pk::Hash: str::FromStr, - ::Err: ToString, - <::Hash as FromStr>::Err: ToString, -{ - /// Parse an expression tree into a descriptor +impl_from_tree!( + Descriptor, + /// Parse an expression tree into a descriptor. fn from_tree(top: &expression::Tree) -> Result, Error> { Ok(match (top.name, top.args.len() as u32) { ("pkh", 1) => Descriptor::Pkh(Pkh::from_tree(top)?), @@ -669,17 +727,11 @@ where _ => Descriptor::Bare(Bare::from_tree(top)?), }) } -} - -impl FromStr for Descriptor -where - Pk: MiniscriptKey + str::FromStr, - Pk::Hash: str::FromStr, - ::Err: ToString, - <::Hash as FromStr>::Err: ToString, -{ - type Err = Error; +); +impl_from_str!( + Descriptor, + type Err = Error;, fn from_str(s: &str) -> Result, Error> { // tr tree parsing has special code // Tr::from_str will check the checksum @@ -692,7 +744,7 @@ where expression::FromTree::from_tree(&top) } } -} +); impl fmt::Debug for Descriptor { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -742,7 +794,7 @@ mod tests { use crate::descriptor::{DescriptorPublicKey, DescriptorSecretKey, DescriptorXKey, SinglePub}; #[cfg(feature = "compiler")] use crate::policy; - use crate::{hex_script, Descriptor, DummyKey, Error, Miniscript, Satisfier, TranslatePk2}; + use crate::{hex_script, Descriptor, DummyKey, Error, Miniscript, Satisfier}; type StdDescriptor = Descriptor; const TEST_PK: &'static str = @@ -1479,19 +1531,14 @@ mod tests { assert_eq!(desc_one.to_string(), raw_desc_one); assert_eq!(desc_two.to_string(), raw_desc_two); - // Derive a child in case the descriptor is ranged. If it's not this won't have any - // effect - let desc_one = desc_one.derive(index); - let desc_two = desc_two.derive(index); - // Same address let addr_one = desc_one - .translate_pk2(|xpk| xpk.derive_public_key(&secp_ctx)) + .derived_descriptor(&secp_ctx, index) .unwrap() .address(bitcoin::Network::Bitcoin) .unwrap(); let addr_two = desc_two - .translate_pk2(|xpk| xpk.derive_public_key(&secp_ctx)) + .derived_descriptor(&secp_ctx, index) .unwrap() .address(bitcoin::Network::Bitcoin) .unwrap(); diff --git a/src/descriptor/segwitv0.rs b/src/descriptor/segwitv0.rs index 8ec2e2348..070d23934 100644 --- a/src/descriptor/segwitv0.rs +++ b/src/descriptor/segwitv0.rs @@ -17,7 +17,6 @@ //! of wsh, wpkh and sortedmulti inside wsh. use core::fmt; -use core::str::FromStr; use bitcoin::{self, Address, Network, Script}; @@ -30,7 +29,7 @@ use crate::prelude::*; use crate::util::varint_len; use crate::{ Error, ForEach, ForEachKey, Miniscript, MiniscriptKey, Satisfier, Segwitv0, ToPublicKey, - TranslatePk, + TranslatePk, Translator, }; /// A Segwitv0 wsh descriptor #[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] @@ -194,13 +193,8 @@ impl Liftable for Wsh { } } -impl FromTree for Wsh -where - Pk: MiniscriptKey + FromStr, - Pk::Hash: FromStr, - ::Err: ToString, - <::Hash as FromStr>::Err: ToString, -{ +impl_from_tree!( + Wsh, fn from_tree(top: &expression::Tree) -> Result { if top.name == "wsh" && top.args.len() == 1 { let top = &top.args[0]; @@ -222,7 +216,8 @@ where ))) } } -} +); + impl fmt::Debug for Wsh { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.inner { @@ -240,21 +235,15 @@ impl fmt::Display for Wsh { } } -impl FromStr for Wsh -where - Pk: MiniscriptKey + FromStr, - Pk::Hash: FromStr, - ::Err: ToString, - <::Hash as FromStr>::Err: ToString, -{ - type Err = Error; - +impl_from_str!( + Wsh, + type Err = Error;, fn from_str(s: &str) -> Result { let desc_str = verify_checksum(s)?; let top = expression::Tree::from_str(desc_str)?; Wsh::::from_tree(&top) } -} +); impl ForEachKey for Wsh { fn for_each_key<'a, F: FnMut(ForEach<'a, Pk>) -> bool>(&'a self, pred: F) -> bool @@ -276,14 +265,13 @@ where { type Output = Wsh; - fn translate_pk(&self, mut fpk: Fpk, mut fpkh: Fpkh) -> Result + fn translate_pk(&self, t: &mut T) -> Result where - Fpk: FnMut(&P) -> Result, - Fpkh: FnMut(&P::Hash) -> Result, + T: Translator, { let inner = match self.inner { - WshInner::SortedMulti(ref smv) => WshInner::SortedMulti(smv.translate_pk(&mut fpk)?), - WshInner::Ms(ref ms) => WshInner::Ms(ms.translate_pk(&mut fpk, &mut fpkh)?), + WshInner::SortedMulti(ref smv) => WshInner::SortedMulti(smv.translate_pk(t)?), + WshInner::Ms(ref ms) => WshInner::Ms(ms.translate_pk(t)?), }; Ok(Wsh { inner }) } @@ -423,13 +411,8 @@ impl Liftable for Wpkh { } } -impl FromTree for Wpkh -where - Pk: MiniscriptKey + FromStr, - Pk::Hash: FromStr, - ::Err: ToString, - <::Hash as FromStr>::Err: ToString, -{ +impl_from_tree!( + Wpkh, fn from_tree(top: &expression::Tree) -> Result { if top.name == "wpkh" && top.args.len() == 1 { Ok(Wpkh::new(expression::terminal(&top.args[0], |pk| { @@ -443,23 +426,17 @@ where ))) } } -} - -impl FromStr for Wpkh -where - Pk: MiniscriptKey + FromStr, - Pk::Hash: FromStr, - ::Err: ToString, - <::Hash as FromStr>::Err: ToString, -{ - type Err = Error; +); +impl_from_str!( + Wpkh, + type Err = Error;, fn from_str(s: &str) -> Result { let desc_str = verify_checksum(s)?; let top = expression::Tree::from_str(desc_str)?; Self::from_tree(&top) } -} +); impl ForEachKey for Wpkh { fn for_each_key<'a, F: FnMut(ForEach<'a, Pk>) -> bool>(&'a self, mut pred: F) -> bool @@ -478,11 +455,10 @@ where { type Output = Wpkh; - fn translate_pk(&self, mut fpk: Fpk, _fpkh: Fpkh) -> Result + fn translate_pk(&self, t: &mut T) -> Result where - Fpk: FnMut(&P) -> Result, - Fpkh: FnMut(&P::Hash) -> Result, + T: Translator, { - Ok(Wpkh::new(fpk(&self.pk)?).expect("Uncompressed keys in Wpkh")) + Ok(Wpkh::new(t.pk(&self.pk)?).expect("Uncompressed keys in Wpkh")) } } diff --git a/src/descriptor/sh.rs b/src/descriptor/sh.rs index 332328bea..d9202e67e 100644 --- a/src/descriptor/sh.rs +++ b/src/descriptor/sh.rs @@ -19,7 +19,6 @@ //! use core::fmt; -use core::str::FromStr; use bitcoin::blockdata::script; use bitcoin::{Address, Network, Script}; @@ -33,7 +32,7 @@ use crate::prelude::*; use crate::util::{varint_len, witness_to_scriptsig}; use crate::{ push_opcode_size, Error, ForEach, ForEachKey, Legacy, Miniscript, MiniscriptKey, Satisfier, - Segwitv0, ToPublicKey, TranslatePk, + Segwitv0, ToPublicKey, TranslatePk, Translator, }; /// A Legacy p2sh Descriptor @@ -91,13 +90,8 @@ impl fmt::Display for Sh { } } -impl FromTree for Sh -where - Pk: MiniscriptKey + FromStr, - Pk::Hash: FromStr, - ::Err: ToString, - <::Hash as FromStr>::Err: ToString, -{ +impl_from_tree!( + Sh, fn from_tree(top: &expression::Tree) -> Result { if top.name == "sh" && top.args.len() == 1 { let top = &top.args[0]; @@ -120,22 +114,17 @@ where ))) } } -} +); -impl FromStr for Sh -where - Pk: MiniscriptKey + FromStr, - Pk::Hash: FromStr, - ::Err: ToString, - <::Hash as FromStr>::Err: ToString, -{ - type Err = Error; +impl_from_str!( + Sh, + type Err = Error;, fn from_str(s: &str) -> Result { let desc_str = verify_checksum(s)?; let top = expression::Tree::from_str(desc_str)?; Self::from_tree(&top) } -} +); impl Sh { /// Get the Inner @@ -409,16 +398,15 @@ where { type Output = Sh; - fn translate_pk(&self, mut fpk: Fpk, mut fpkh: Fpkh) -> Result + fn translate_pk(&self, t: &mut T) -> Result where - Fpk: FnMut(&P) -> Result, - Fpkh: FnMut(&P::Hash) -> Result, + T: Translator, { let inner = match self.inner { - ShInner::Wsh(ref wsh) => ShInner::Wsh(wsh.translate_pk(&mut fpk, &mut fpkh)?), - ShInner::Wpkh(ref wpkh) => ShInner::Wpkh(wpkh.translate_pk(&mut fpk, &mut fpkh)?), - ShInner::SortedMulti(ref smv) => ShInner::SortedMulti(smv.translate_pk(&mut fpk)?), - ShInner::Ms(ref ms) => ShInner::Ms(ms.translate_pk(&mut fpk, &mut fpkh)?), + ShInner::Wsh(ref wsh) => ShInner::Wsh(wsh.translate_pk(t)?), + ShInner::Wpkh(ref wpkh) => ShInner::Wpkh(wpkh.translate_pk(t)?), + ShInner::SortedMulti(ref smv) => ShInner::SortedMulti(smv.translate_pk(t)?), + ShInner::Ms(ref ms) => ShInner::Ms(ms.translate_pk(t)?), }; Ok(Sh { inner }) } diff --git a/src/descriptor/sortedmulti.rs b/src/descriptor/sortedmulti.rs index e0a6c0910..d3c21a239 100644 --- a/src/descriptor/sortedmulti.rs +++ b/src/descriptor/sortedmulti.rs @@ -28,7 +28,7 @@ use crate::miniscript::limits::MAX_PUBKEYS_PER_MULTISIG; use crate::prelude::*; use crate::{ errstr, expression, miniscript, policy, script_num_size, Error, ForEach, ForEachKey, - Miniscript, MiniscriptKey, Satisfier, ToPublicKey, + Miniscript, MiniscriptKey, Satisfier, ToPublicKey, Translator, }; /// Contents of a "sortedmulti" descriptor @@ -94,15 +94,15 @@ impl SortedMultiVec { /// This will panic if fpk returns an uncompressed key when /// converting to a Segwit descriptor. To prevent this panic, ensure /// fpk returns an error in this case instead. - pub fn translate_pk( + pub fn translate_pk( &self, - fpk: &mut FPk, + t: &mut T, ) -> Result, FuncError> where - FPk: FnMut(&Pk) -> Result, + T: Translator, Q: MiniscriptKey, { - let pks: Result, _> = self.pks.iter().map(&mut *fpk).collect(); + let pks: Result, _> = self.pks.iter().map(|pk| t.pk(pk)).collect(); Ok(SortedMultiVec { k: self.k, pks: pks?, diff --git a/src/descriptor/tr.rs b/src/descriptor/tr.rs index f0b40db5c..7fde28171 100644 --- a/src/descriptor/tr.rs +++ b/src/descriptor/tr.rs @@ -20,6 +20,7 @@ use crate::prelude::*; use crate::util::{varint_len, witness_size}; use crate::{ errstr, Error, ForEach, ForEachKey, MiniscriptKey, Satisfier, Tap, ToPublicKey, TranslatePk, + Translator, }; /// A Taproot Tree representation. @@ -127,22 +128,17 @@ impl TapTree { } // Helper function to translate keys - fn translate_helper( - &self, - fpk: &mut FPk, - fpkh: &mut FPkh, - ) -> Result, Error> + fn translate_helper(&self, t: &mut T) -> Result, Error> where - FPk: FnMut(&Pk) -> Result, - FPkh: FnMut(&Pk::Hash) -> Result, + T: Translator, Q: MiniscriptKey, { let frag = match self { TapTree::Tree(l, r) => TapTree::Tree( - Arc::new(l.translate_helper(fpk, fpkh)?), - Arc::new(r.translate_helper(fpk, fpkh)?), + Arc::new(l.translate_helper(t)?), + Arc::new(r.translate_helper(t)?), ), - TapTree::Leaf(ms) => TapTree::Leaf(Arc::new(ms.translate_pk(fpk, fpkh)?)), + TapTree::Leaf(ms) => TapTree::Leaf(Arc::new(ms.translate_pk(t)?)), }; Ok(frag) } @@ -388,41 +384,32 @@ where } } -impl FromTree for Tr -where - Pk: MiniscriptKey + FromStr, - Pk::Hash: FromStr, - ::Err: ToString, - <::Hash as FromStr>::Err: ToString, -{ - fn from_tree(top: &expression::Tree) -> Result { - // Helper function to parse taproot script path - fn parse_tr_script_spend( - tree: &expression::Tree, - ) -> Result, Error> - where - Pk: MiniscriptKey + FromStr, - Pk::Hash: FromStr, - ::Err: ToString, - <::Hash as FromStr>::Err: ToString, - { - match tree { - expression::Tree { name, args } if !name.is_empty() && args.is_empty() => { - let script = Miniscript::::from_str(name)?; - Ok(TapTree::Leaf(Arc::new(script))) - } - expression::Tree { name, args } if name.is_empty() && args.len() == 2 => { - let left = parse_tr_script_spend(&args[0])?; - let right = parse_tr_script_spend(&args[1])?; - Ok(TapTree::Tree(Arc::new(left), Arc::new(right))) - } - _ => Err(Error::Unexpected( - "unknown format for script spending paths while parsing taproot descriptor" - .to_string(), - )), +#[rustfmt::skip] +impl_block_str!( + Tr, + // Helper function to parse taproot script path + fn parse_tr_script_spend(tree: &expression::Tree,) -> Result, Error> { + match tree { + expression::Tree { name, args } if !name.is_empty() && args.is_empty() => { + let script = Miniscript::::from_str(name)?; + Ok(TapTree::Leaf(Arc::new(script))) + } + expression::Tree { name, args } if name.is_empty() && args.len() == 2 => { + let left = Self::parse_tr_script_spend(&args[0])?; + let right = Self::parse_tr_script_spend(&args[1])?; + Ok(TapTree::Tree(Arc::new(left), Arc::new(right))) } + _ => Err(Error::Unexpected( + "unknown format for script spending paths while parsing taproot descriptor" + .to_string(), + )), } + } +); +impl_from_tree!( + Tr, + fn from_tree(top: &expression::Tree) -> Result { if top.name == "tr" { match top.args.len() { 1 => { @@ -448,7 +435,7 @@ where ))); } let tree = &top.args[1]; - let ret = parse_tr_script_spend(tree)?; + let ret = Self::parse_tr_script_spend(tree)?; Ok(Tr { internal_key: expression::terminal(key, Pk::from_str)?, tree: Some(ret), @@ -471,23 +458,17 @@ where ))); } } -} - -impl FromStr for Tr -where - Pk: MiniscriptKey + FromStr, - Pk::Hash: FromStr, - ::Err: ToString, - <::Hash as FromStr>::Err: ToString, -{ - type Err = Error; +); +impl_from_str!( + Tr, + type Err = Error;, fn from_str(s: &str) -> Result { let desc_str = verify_checksum(s)?; let top = parse_tr_tree(desc_str)?; Self::from_tree(&top) } -} +); impl fmt::Debug for Tr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -625,15 +606,14 @@ where { type Output = Tr; - fn translate_pk(&self, mut fpk: Fpk, mut fpkh: Fpkh) -> Result + fn translate_pk(&self, translate: &mut T) -> Result where - Fpk: FnMut(&P) -> Result, - Fpkh: FnMut(&P::Hash) -> Result, + T: Translator, { let translate_desc = Tr { - internal_key: fpk(&self.internal_key)?, + internal_key: translate.pk(&self.internal_key)?, tree: match &self.tree { - Some(tree) => Some(tree.translate_helper(&mut fpk, &mut fpkh)?), + Some(tree) => Some(tree.translate_helper(translate)?), None => None, }, spend_info: Mutex::new(None), diff --git a/src/expression.rs b/src/expression.rs index bcecf2fe8..2c120ffb4 100644 --- a/src/expression.rs +++ b/src/expression.rs @@ -27,6 +27,11 @@ pub struct Tree<'a> { /// The comma-separated contents of the `(...)`, if any pub args: Vec>, } +// or_b(pk(A),pk(B)) +// +// A = musig(musig(B,C),D,E) +// or_b() +// pk(A), pk(B) /// A trait for extracting a structure from a Tree representation in token form pub trait FromTree: Sized { @@ -259,7 +264,4 @@ mod tests { assert!(parse_num("+6").is_err()); assert!(parse_num("-6").is_err()); } - - // Add tests for tapscript parsing - // tr(D,{or_i(pk(A),pk(B)),{after(9),pk(C)}}) } diff --git a/src/interpreter/error.rs b/src/interpreter/error.rs index 075b103aa..65fe2b848 100644 --- a/src/interpreter/error.rs +++ b/src/interpreter/error.rs @@ -145,22 +145,18 @@ impl fmt::Display for Error { } Error::IncorrectWScriptHash => f.write_str("witness script did not match scriptpubkey"), Error::InsufficientSignaturesMultiSig => f.write_str("Insufficient signatures for CMS"), - Error::InvalidSchnorrSighashType(ref sig) => { - write!( - f, - "Invalid sighash type for schnorr signature '{}'", - sig.to_hex() - ) - } + Error::InvalidSchnorrSighashType(ref sig) => write!( + f, + "Invalid sighash type for schnorr signature '{}'", + sig.to_hex() + ), Error::InvalidEcdsaSignature(pk) => write!(f, "bad ecdsa signature with pk {}", pk), Error::InvalidSchnorrSignature(pk) => write!(f, "bad schnorr signature with pk {}", pk), - Error::NonStandardSighash(ref sig) => { - write!( - f, - "Non standard sighash type for signature '{}'", - sig.to_hex() - ) - } + Error::NonStandardSighash(ref sig) => write!( + f, + "Non standard sighash type for signature '{}'", + sig.to_hex() + ), Error::NonEmptyWitness => f.write_str("legacy spend had nonempty witness"), Error::NonEmptyScriptSig => f.write_str("segwit spend had nonempty scriptsig"), Error::Miniscript(ref e) => write!(f, "parse error: {}", e), diff --git a/src/interpreter/inner.rs b/src/interpreter/inner.rs index e335107ec..efe6a44aa 100644 --- a/src/interpreter/inner.rs +++ b/src/interpreter/inner.rs @@ -20,7 +20,7 @@ use bitcoin::util::taproot::{ControlBlock, TAPROOT_ANNEX_PREFIX}; use super::{stack, BitcoinKey, Error, Stack, TypedHash160}; use crate::miniscript::context::{NoChecks, ScriptContext}; use crate::prelude::*; -use crate::{BareCtx, Legacy, Miniscript, MiniscriptKey, Segwitv0, Tap}; +use crate::{BareCtx, Legacy, Miniscript, MiniscriptKey, PkTranslator, Segwitv0, Tap}; /// Attempts to parse a slice as a Bitcoin public key, checking compressedness /// if asked to, but otherwise dropping it @@ -375,23 +375,39 @@ pub(super) trait ToNoChecks { impl ToNoChecks for Miniscript { fn to_no_checks_ms(&self) -> Miniscript { - // specify the () error type as this cannot error - self.real_translate_pk::<_, _, _, (), _>( - &mut |pk| Ok(BitcoinKey::Fullkey(*pk)), - &mut |pkh| Ok(TypedHash160::FullKey(*pkh)), - ) - .expect("Translation should succeed") + struct TranslateFullPk; + + impl PkTranslator for TranslateFullPk { + fn pk(&mut self, pk: &bitcoin::PublicKey) -> Result { + Ok(BitcoinKey::Fullkey(*pk)) + } + + fn pkh(&mut self, pkh: &hash160::Hash) -> Result { + Ok(TypedHash160::FullKey(*pkh)) + } + } + + self.real_translate_pk(&mut TranslateFullPk) + .expect("Translation should succeed") } } impl ToNoChecks for Miniscript { fn to_no_checks_ms(&self) -> Miniscript { // specify the () error type as this cannot error - self.real_translate_pk::<_, _, _, (), _>( - &mut |xpk| Ok(BitcoinKey::XOnlyPublicKey(*xpk)), - &mut |pkh| Ok(TypedHash160::XonlyKey(*pkh)), - ) - .expect("Translation should succeed") + struct TranslateXOnlyPk; + + impl PkTranslator for TranslateXOnlyPk { + fn pk(&mut self, pk: &bitcoin::XOnlyPublicKey) -> Result { + Ok(BitcoinKey::XOnlyPublicKey(*pk)) + } + + fn pkh(&mut self, pkh: &hash160::Hash) -> Result { + Ok(TypedHash160::XonlyKey(*pkh)) + } + } + self.real_translate_pk(&mut TranslateXOnlyPk) + .expect("Translation should succeed") } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 0c96da5fe..0e7af994f 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -149,6 +149,7 @@ impl TypedHash160 { impl MiniscriptKey for BitcoinKey { type Hash = TypedHash160; + type Sha256 = bitcoin::hashes::sha256::Hash; fn to_pubkeyhash(&self) -> Self::Hash { match self { diff --git a/src/lib.rs b/src/lib.rs index 9354c8f48..5bac1c908 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -118,6 +118,8 @@ pub mod policy; pub mod psbt; pub mod timelock; +#[cfg(test)] +mod test_utils; mod util; use core::str::FromStr; @@ -153,12 +155,17 @@ pub trait MiniscriptKey: Clone + Eq + Ord + fmt::Debug + fmt::Display + hash::Ha /// The associated [`Hash`] type for this pubkey. type Hash: Clone + Eq + Ord + fmt::Display + fmt::Debug + hash::Hash; + /// The associated [`sha256::Hash`] type for this [`MiniscriptKey`] type. + /// Used in the sha256 fragment + type Sha256: Clone + Eq + Ord + fmt::Display + fmt::Debug + hash::Hash; + /// Converts this key to the associated pubkey hash. fn to_pubkeyhash(&self) -> Self::Hash; } impl MiniscriptKey for bitcoin::secp256k1::PublicKey { type Hash = hash160::Hash; + type Sha256 = sha256::Hash; fn to_pubkeyhash(&self) -> Self::Hash { hash160::Hash::hash(&self.serialize()) @@ -172,6 +179,7 @@ impl MiniscriptKey for bitcoin::PublicKey { } type Hash = hash160::Hash; + type Sha256 = sha256::Hash; fn to_pubkeyhash(&self) -> Self::Hash { hash160::Hash::hash(&self.to_bytes()) @@ -180,6 +188,7 @@ impl MiniscriptKey for bitcoin::PublicKey { impl MiniscriptKey for bitcoin::secp256k1::XOnlyPublicKey { type Hash = hash160::Hash; + type Sha256 = sha256::Hash; fn to_pubkeyhash(&self) -> Self::Hash { hash160::Hash::hash(&self.serialize()) @@ -192,6 +201,7 @@ impl MiniscriptKey for bitcoin::secp256k1::XOnlyPublicKey { impl MiniscriptKey for String { type Hash = String; + type Sha256 = String; // specify hashes as string fn to_pubkeyhash(&self) -> Self::Hash { (&self).to_string() @@ -216,6 +226,9 @@ pub trait ToPublicKey: MiniscriptKey { /// should give the same result as calling `to_public_key` and hashing /// the result directly. fn hash_to_hash160(hash: &::Hash) -> hash160::Hash; + + /// Converts the generic associated [`MiniscriptKey::Sha256`] to [`sha256::Hash`] + fn to_sha256(hash: &::Sha256) -> sha256::Hash; } impl ToPublicKey for bitcoin::PublicKey { @@ -226,6 +239,10 @@ impl ToPublicKey for bitcoin::PublicKey { fn hash_to_hash160(hash: &hash160::Hash) -> hash160::Hash { *hash } + + fn to_sha256(hash: &sha256::Hash) -> sha256::Hash { + *hash + } } impl ToPublicKey for bitcoin::secp256k1::PublicKey { @@ -236,6 +253,10 @@ impl ToPublicKey for bitcoin::secp256k1::PublicKey { fn hash_to_hash160(hash: &hash160::Hash) -> hash160::Hash { *hash } + + fn to_sha256(hash: &sha256::Hash) -> sha256::Hash { + *hash + } } impl ToPublicKey for bitcoin::secp256k1::XOnlyPublicKey { @@ -255,6 +276,10 @@ impl ToPublicKey for bitcoin::secp256k1::XOnlyPublicKey { fn hash_to_hash160(hash: &hash160::Hash) -> hash160::Hash { *hash } + + fn to_sha256(hash: &sha256::Hash) -> sha256::Hash { + *hash + } } /// Dummy key which de/serializes to the empty string; useful sometimes for testing @@ -274,6 +299,7 @@ impl str::FromStr for DummyKey { impl MiniscriptKey for DummyKey { type Hash = DummyKeyHash; + type Sha256 = DummySha256Hash; fn to_pubkeyhash(&self) -> Self::Hash { DummyKeyHash @@ -303,6 +329,11 @@ impl ToPublicKey for DummyKey { fn hash_to_hash160(_: &DummyKeyHash) -> hash160::Hash { hash160::Hash::from_str("f54a5851e9372b87810a8e60cdd2e7cfd80b6e31").unwrap() } + + fn to_sha256(_hash: &DummySha256Hash) -> sha256::Hash { + sha256::Hash::from_str("50863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352") + .unwrap() + } } /// Dummy keyhash which de/serializes to the empty string; useful sometimes for testing @@ -332,134 +363,99 @@ impl hash::Hash for DummyKeyHash { } } -/// Converts a descriptor using abstract keys to one using specific keys. -/// -/// # Panics -/// -/// If `fpk` returns an uncompressed key when converting to a segwit descriptor. -/// To prevent this panic, ensure `fpk` returns an error in this case instead. -pub trait TranslatePk -where - P: MiniscriptKey, - Q: MiniscriptKey, -{ - /// The associated output type. This must be `Self`. - type Output; - - /// Translates a struct from one generic to another where the translation - /// for Pk is provided by function `fpk`, and translation for PkH is - /// provided by function `fpkh`. - fn translate_pk(&self, fpk: Fpk, fpkh: Fpkh) -> Result - where - Fpk: FnMut(&P) -> Result, - Fpkh: FnMut(&P::Hash) -> Result; +/// Dummy keyhash which de/serializes to the empty string; useful for testing +#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug)] +pub struct DummySha256Hash; - /// Calls `Self::translate_pk` with conversion functions that cannot fail. - fn translate_pk_infallible(&self, mut fpk: Fpk, mut fpkh: Fpkh) -> Self::Output - where - Fpk: FnMut(&P) -> Q, - Fpkh: FnMut(&P::Hash) -> Q::Hash, - { - self.translate_pk::<_, _, ()>(|pk| Ok(fpk(pk)), |pkh| Ok(fpkh(pkh))) - .expect("infallible translation function") +impl str::FromStr for DummySha256Hash { + type Err = &'static str; + fn from_str(x: &str) -> Result { + if x.is_empty() { + Ok(DummySha256Hash) + } else { + Err("non empty dummy hash") + } } } -/// Variant of `TranslatePk` where P and Q both have the same hash -/// type, and the hashes can be converted by just cloning them. -pub trait TranslatePk1: TranslatePk -where - P: MiniscriptKey, - Q: MiniscriptKey, -{ - /// Translates a struct from one generic to another where the translation - /// for Pk is provided by function `fpk`. - fn translate_pk1(&self, fpk: Fpk) -> Result<>::Output, E> - where - Fpk: FnMut(&P) -> Result, - { - self.translate_pk(fpk, |h| Ok(h.clone())) +impl fmt::Display for DummySha256Hash { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("") } +} - /// Translates a struct from one generic to another where the translation - /// for Pk is provided by function `fpk`. - fn translate_pk1_infallible(&self, fpk: Fpk) -> >::Output - where - Fpk: FnMut(&P) -> Q, - { - self.translate_pk_infallible(fpk, P::Hash::clone) +impl hash::Hash for DummySha256Hash { + fn hash(&self, state: &mut H) { + "DummySha256Hash".hash(state); } } -impl TranslatePk1 for T + +/// Describes an object that can translate various keys and hashes from one key to the type +/// associated with the other key. Used by the [`TranslatePk`] trait to do the actual translations. +pub trait Translator where P: MiniscriptKey, - Q: MiniscriptKey, - T: TranslatePk, + Q: MiniscriptKey, { + /// Translates public keys P -> Q. + fn pk(&mut self, pk: &P) -> Result; + + /// Translates public key hashes P::Hash -> Q::Hash. + fn pkh(&mut self, pkh: &P::Hash) -> Result; + + /// Translates sha256 hashes from P::Sha256 -> Q::Sha256 + fn sha256(&mut self, sha256: &P::Sha256) -> Result; } -/// Variant of `TranslatePk` where P's hash is P, so the hashes -/// can be converted by reusing the key-conversion function. -pub trait TranslatePk2: TranslatePk +/// Provides the conversion information required in [`TranslatePk`]. +/// Same as [`Translator`], but useful when all the associated types apart +/// from Pk/Pkh don't change in translation +pub trait PkTranslator where - P: MiniscriptKey, - Q: MiniscriptKey, + P: MiniscriptKey, + Q: MiniscriptKey, { - /// Translates a struct from one generic to another where the translation - /// for Pk is provided by function `fpk`. - fn translate_pk2(&self, fpk: Fpk) -> Result<>::Output, E> - where - Fpk: Fn(&P) -> Result, - { - self.translate_pk(&fpk, |h| fpk(h).map(|q| q.to_pubkeyhash())) - } + /// Provides the translation public keys P -> Q + fn pk(&mut self, pk: &P) -> Result; - /// Translates a struct from one generic to another where the translation - /// for Pk is provided by function `fpk`. - fn translate_pk2_infallible(&self, fpk: Fpk) -> >::Output - where - Fpk: Fn(&P) -> Q, - { - self.translate_pk_infallible(&fpk, |h| fpk(h).to_pubkeyhash()) - } + /// Provides the translation public keys hashes P::Hash -> Q::Hash + fn pkh(&mut self, pkh: &P::Hash) -> Result; } -impl TranslatePk2 for T + +impl Translator for T where - P: MiniscriptKey, - Q: MiniscriptKey, - T: TranslatePk, + T: PkTranslator, + P: MiniscriptKey, + Q: MiniscriptKey, { -} + fn pk(&mut self, pk: &P) -> Result { + >::pk(self, pk) + } -/// Variant of `TranslatePk` where Q's hash is `hash160` so we can -/// derive hashes by calling `hash_to_hash160`. -pub trait TranslatePk3>: - TranslatePk -{ - /// Translates a struct from one generic to another where the translation - /// for Pk is provided by function `fpk`. - fn translate_pk3(&self, fpk: Fpk) -> Result<>::Output, E> - where - Fpk: FnMut(&P) -> Result, - { - self.translate_pk(fpk, |h| Ok(P::hash_to_hash160(h))) + fn pkh(&mut self, pkh: &

::Hash) -> Result<::Hash, E> { + >::pkh(self, pkh) } - /// Translates a struct from one generic to another where the translation - /// for Pk is provided by function `fpk`. - fn translate_pk3_infallible(&self, fpk: Fpk) -> >::Output - where - Fpk: FnMut(&P) -> Q, - { - self.translate_pk_infallible(fpk, P::hash_to_hash160) + fn sha256(&mut self, sha256: &

::Sha256) -> Result<::Sha256, E> { + Ok(sha256.clone()) } } -impl TranslatePk3 for T + +/// Converts a descriptor using abstract keys to one using specific keys. Uses translator `t` to do +/// the actual translation function calls. +pub trait TranslatePk where - P: MiniscriptKey + ToPublicKey, - Q: MiniscriptKey, - T: TranslatePk, + P: MiniscriptKey, + Q: MiniscriptKey, { + /// The associated output type. This must be `Self`. + type Output; + + /// Translates a struct from one generic to another where the translations + /// for Pk are provided by the given [`Translator`]. + fn translate_pk(&self, translator: &mut T) -> Result + where + T: Translator; } /// Either a key or a keyhash @@ -658,18 +654,10 @@ impl fmt::Display for Error { Error::PubKeyCtxError(ref pk, ref ctx) => { write!(f, "Pubkey error: {} under {} scriptcontext", pk, ctx) } - Error::MultiATooManyKeys(k) => { - write!(f, "MultiA too many keys {}", k) - } - Error::TaprootSpendInfoUnavialable => { - write!(f, "Taproot Spend Info not computed.") - } - Error::TrNoScriptCode => { - write!(f, "No script code for Tr descriptors") - } - Error::TrNoExplicitScript => { - write!(f, "No script code for Tr descriptors") - } + Error::MultiATooManyKeys(k) => write!(f, "MultiA too many keys {}", k), + Error::TaprootSpendInfoUnavialable => write!(f, "Taproot Spend Info not computed."), + Error::TrNoScriptCode => write!(f, "No script code for Tr descriptors"), + Error::TrNoExplicitScript => write!(f, "No script code for Tr descriptors"), } } } diff --git a/src/macros.rs b/src/macros.rs index b154919b3..7f59aa9a2 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -16,6 +16,88 @@ macro_rules! policy_str { ($($arg:tt)*) => ($crate::policy::Concrete::from_str(&format!($($arg)*)).unwrap()) } +/// Macro for implementing FromTree trait. This avoids copying all the Pk::Associated type bounds +/// throughout the codebase. +macro_rules! impl_from_tree { + ($(;$gen:ident; $gen_con:ident, )* $name: ty, + $(#[$meta:meta])* + fn $fn:ident ( $($arg:ident : $type:ty),* ) -> $ret:ty + $body:block + ) => { + impl $crate::expression::FromTree for $name + where + Pk: MiniscriptKey + core::str::FromStr, + Pk::Hash: core::str::FromStr, + Pk::Sha256: core::str::FromStr, + ::Err: $crate::prelude::ToString, + <::Hash as core::str::FromStr>::Err: $crate::prelude::ToString, + <::Sha256 as core::str::FromStr>::Err: $crate::prelude::ToString, + $($gen : $gen_con,)* + { + + $(#[$meta])* + fn $fn($($arg: $type)* ) -> $ret { + $body + } + } + }; +} + +/// Macro for implementing FromStr trait. This avoids copying all the Pk::Associated type bounds +/// throughout the codebase. +macro_rules! impl_from_str { + ($(;$gen:ident; $gen_con:ident, )* $name: ty, + type Err = $err_ty:ty;, + $(#[$meta:meta])* + fn $fn:ident ( $($arg:ident : $type:ty),* ) -> $ret:ty + $body:block + ) => { + impl core::str::FromStr for $name + where + Pk: MiniscriptKey + core::str::FromStr, + Pk::Hash: core::str::FromStr, + Pk::Sha256: core::str::FromStr, + ::Err: $crate::prelude::ToString, + <::Hash as core::str::FromStr>::Err: $crate::prelude::ToString, + <::Sha256 as core::str::FromStr>::Err: $crate::prelude::ToString, + $($gen : $gen_con,)* + { + type Err = $err_ty; + + $(#[$meta])* + fn $fn($($arg: $type)* ) -> $ret { + $body + } + } + }; +} + +/// Macro for impl Struct with associated bounds. This avoids copying all the Pk::Associated type bounds +/// throughout the codebase. +macro_rules! impl_block_str { + ($(;$gen:ident; $gen_con:ident, )* $name: ty, + $(#[$meta:meta])* + $v:vis fn $fn:ident ( $($arg:ident : $type:ty, )* ) -> $ret:ty + $body:block + ) => { + impl $name + where + Pk: MiniscriptKey + core::str::FromStr, + Pk::Hash: core::str::FromStr, + Pk::Sha256: core::str::FromStr, + ::Err: $crate::prelude::ToString, + <::Hash as core::str::FromStr>::Err: $crate::prelude::ToString, + <::Sha256 as core::str::FromStr>::Err: $crate::prelude::ToString, + $($gen : $gen_con,)* + { + $(#[$meta])* + $v fn $fn($($arg: $type,)* ) -> $ret { + $body + } + } + }; +} + /// A macro that implements serde serialization and deserialization using the /// `fmt::Display` and `str::FromStr` traits. macro_rules! serde_string_impl_pk { @@ -25,9 +107,12 @@ macro_rules! serde_string_impl_pk { where Pk: $crate::MiniscriptKey + core::str::FromStr, Pk::Hash: core::str::FromStr, + Pk::Sha256: core::str::FromStr, ::Err: core::fmt::Display, <::Hash as core::str::FromStr>::Err: core::fmt::Display, + <::Sha256 as core::str::FromStr>::Err: + core::fmt::Display, $($gen : $gen_con,)* { fn deserialize(deserializer: D) -> Result<$name, D::Error> @@ -44,9 +129,12 @@ macro_rules! serde_string_impl_pk { where Pk: $crate::MiniscriptKey + core::str::FromStr, Pk::Hash: core::str::FromStr, + Pk::Sha256: core::str::FromStr, ::Err: core::fmt::Display, <::Hash as core::str::FromStr>::Err: core::fmt::Display, + <::Sha256 as core::str::FromStr>::Err: + core::fmt::Display, $($gen: $gen_con,)* { type Value = $name; diff --git a/src/miniscript/astelem.rs b/src/miniscript/astelem.rs index e5cf13f74..932d5135e 100644 --- a/src/miniscript/astelem.rs +++ b/src/miniscript/astelem.rs @@ -19,12 +19,12 @@ //! encoding in Bitcoin script, as well as a datatype. Full details //! are given on the Miniscript website. +use core::fmt; use core::str::FromStr; -use core::{fmt, str}; use bitcoin::blockdata::{opcodes, script}; use bitcoin::hashes::hex::FromHex; -use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d, Hash}; +use bitcoin::hashes::{hash160, ripemd160, sha256d, Hash}; use sync::Arc; use crate::miniscript::context::SigType; @@ -34,7 +34,7 @@ use crate::prelude::*; use crate::util::MsKeyBuilder; use crate::{ errstr, expression, script_num_size, Error, ForEach, ForEachKey, Miniscript, MiniscriptKey, - Terminal, ToPublicKey, TranslatePk, + Terminal, ToPublicKey, TranslatePk, Translator, }; impl Terminal { @@ -67,16 +67,11 @@ where type Output = Terminal; /// Converts an AST element with one public key type to one of another public key type. - /// - /// # Panics - /// - /// While converting to Segwit Miniscript using uncompressed public keys. - fn translate_pk(&self, mut fpk: Fpk, mut fpkh: Fpkh) -> Result + fn translate_pk(&self, translate: &mut T) -> Result where - Fpk: FnMut(&Pk) -> Result, - Fpkh: FnMut(&Pk::Hash) -> Result, + T: Translator, { - self.real_translate_pk(&mut fpk, &mut fpkh) + self.real_translate_pk(translate) } } @@ -127,87 +122,74 @@ impl Terminal { } } - pub(super) fn real_translate_pk( - &self, - fpk: &mut Fpk, - fpkh: &mut Fpkh, - ) -> Result, E> + pub(super) fn real_translate_pk(&self, t: &mut T) -> Result, E> where - Fpk: FnMut(&Pk) -> Result, - Fpkh: FnMut(&Pk::Hash) -> Result, Q: MiniscriptKey, CtxQ: ScriptContext, + T: Translator, { let frag: Terminal = match *self { - Terminal::PkK(ref p) => Terminal::PkK(fpk(p)?), - Terminal::PkH(ref p) => Terminal::PkH(fpkh(p)?), + Terminal::PkK(ref p) => Terminal::PkK(t.pk(p)?), + Terminal::PkH(ref p) => Terminal::PkH(t.pkh(p)?), Terminal::After(n) => Terminal::After(n), Terminal::Older(n) => Terminal::Older(n), - Terminal::Sha256(x) => Terminal::Sha256(x), + Terminal::Sha256(ref x) => Terminal::Sha256(t.sha256(&x)?), Terminal::Hash256(x) => Terminal::Hash256(x), Terminal::Ripemd160(x) => Terminal::Ripemd160(x), Terminal::Hash160(x) => Terminal::Hash160(x), Terminal::True => Terminal::True, Terminal::False => Terminal::False, - Terminal::Alt(ref sub) => Terminal::Alt(Arc::new(sub.real_translate_pk(fpk, fpkh)?)), - Terminal::Swap(ref sub) => Terminal::Swap(Arc::new(sub.real_translate_pk(fpk, fpkh)?)), - Terminal::Check(ref sub) => { - Terminal::Check(Arc::new(sub.real_translate_pk(fpk, fpkh)?)) - } - Terminal::DupIf(ref sub) => { - Terminal::DupIf(Arc::new(sub.real_translate_pk(fpk, fpkh)?)) - } - Terminal::Verify(ref sub) => { - Terminal::Verify(Arc::new(sub.real_translate_pk(fpk, fpkh)?)) - } - Terminal::NonZero(ref sub) => { - Terminal::NonZero(Arc::new(sub.real_translate_pk(fpk, fpkh)?)) - } + Terminal::Alt(ref sub) => Terminal::Alt(Arc::new(sub.real_translate_pk(t)?)), + Terminal::Swap(ref sub) => Terminal::Swap(Arc::new(sub.real_translate_pk(t)?)), + Terminal::Check(ref sub) => Terminal::Check(Arc::new(sub.real_translate_pk(t)?)), + Terminal::DupIf(ref sub) => Terminal::DupIf(Arc::new(sub.real_translate_pk(t)?)), + Terminal::Verify(ref sub) => Terminal::Verify(Arc::new(sub.real_translate_pk(t)?)), + Terminal::NonZero(ref sub) => Terminal::NonZero(Arc::new(sub.real_translate_pk(t)?)), Terminal::ZeroNotEqual(ref sub) => { - Terminal::ZeroNotEqual(Arc::new(sub.real_translate_pk(fpk, fpkh)?)) + Terminal::ZeroNotEqual(Arc::new(sub.real_translate_pk(t)?)) } Terminal::AndV(ref left, ref right) => Terminal::AndV( - Arc::new(left.real_translate_pk(&mut *fpk, &mut *fpkh)?), - Arc::new(right.real_translate_pk(fpk, fpkh)?), + Arc::new(left.real_translate_pk(t)?), + Arc::new(right.real_translate_pk(t)?), ), Terminal::AndB(ref left, ref right) => Terminal::AndB( - Arc::new(left.real_translate_pk(&mut *fpk, &mut *fpkh)?), - Arc::new(right.real_translate_pk(fpk, fpkh)?), + Arc::new(left.real_translate_pk(t)?), + Arc::new(right.real_translate_pk(t)?), ), Terminal::AndOr(ref a, ref b, ref c) => Terminal::AndOr( - Arc::new(a.real_translate_pk(&mut *fpk, &mut *fpkh)?), - Arc::new(b.real_translate_pk(&mut *fpk, &mut *fpkh)?), - Arc::new(c.real_translate_pk(fpk, fpkh)?), + Arc::new(a.real_translate_pk(t)?), + Arc::new(b.real_translate_pk(t)?), + Arc::new(c.real_translate_pk(t)?), ), Terminal::OrB(ref left, ref right) => Terminal::OrB( - Arc::new(left.real_translate_pk(&mut *fpk, &mut *fpkh)?), - Arc::new(right.real_translate_pk(fpk, fpkh)?), + Arc::new(left.real_translate_pk(t)?), + Arc::new(right.real_translate_pk(t)?), ), Terminal::OrD(ref left, ref right) => Terminal::OrD( - Arc::new(left.real_translate_pk(&mut *fpk, &mut *fpkh)?), - Arc::new(right.real_translate_pk(fpk, fpkh)?), + Arc::new(left.real_translate_pk(t)?), + Arc::new(right.real_translate_pk(t)?), ), Terminal::OrC(ref left, ref right) => Terminal::OrC( - Arc::new(left.real_translate_pk(&mut *fpk, &mut *fpkh)?), - Arc::new(right.real_translate_pk(fpk, fpkh)?), + Arc::new(left.real_translate_pk(t)?), + Arc::new(right.real_translate_pk(t)?), ), Terminal::OrI(ref left, ref right) => Terminal::OrI( - Arc::new(left.real_translate_pk(&mut *fpk, &mut *fpkh)?), - Arc::new(right.real_translate_pk(fpk, fpkh)?), + Arc::new(left.real_translate_pk(t)?), + Arc::new(right.real_translate_pk(t)?), ), Terminal::Thresh(k, ref subs) => { let subs: Result>>, _> = subs .iter() - .map(|s| s.real_translate_pk(&mut *fpk, &mut *fpkh).map(Arc::new)) + .map(|s| s.real_translate_pk(t).map(Arc::new)) .collect(); Terminal::Thresh(k, subs?) } Terminal::Multi(k, ref keys) => { - let keys: Result, _> = keys.iter().map(&mut *fpk).collect(); + let keys: Result, _> = keys.iter().map(|k| t.pk(k)).collect(); Terminal::Multi(k, keys?) } Terminal::MultiA(k, ref keys) => { - let keys: Result, _> = keys.iter().map(&mut *fpk).collect(); + let keys: Result, _> = keys.iter().map(|k| t.pk(k)).collect(); Terminal::MultiA(k, keys?) } }; @@ -276,7 +258,7 @@ impl fmt::Debug for Terminal { Terminal::PkH(ref pkh) => write!(f, "pk_h({:?})", pkh), Terminal::After(t) => write!(f, "after({})", t), Terminal::Older(t) => write!(f, "older({})", t), - Terminal::Sha256(h) => write!(f, "sha256({})", h), + Terminal::Sha256(ref h) => write!(f, "sha256({})", h), Terminal::Hash256(h) => { let mut x = h.into_inner(); x.reverse(); @@ -333,7 +315,7 @@ impl fmt::Display for Terminal { Terminal::PkH(ref pkh) => write!(f, "pk_h({})", pkh), Terminal::After(t) => write!(f, "after({})", t), Terminal::Older(t) => write!(f, "older({})", t), - Terminal::Sha256(h) => write!(f, "sha256({})", h), + Terminal::Sha256(ref h) => write!(f, "sha256({})", h), Terminal::Hash256(h) => { let mut x = h.into_inner(); x.reverse(); @@ -418,27 +400,17 @@ impl fmt::Display for Terminal { } } -impl expression::FromTree for Arc> -where - Pk: MiniscriptKey + str::FromStr, - Pk::Hash: str::FromStr, - Ctx: ScriptContext, - ::Err: ToString, - <::Hash as str::FromStr>::Err: ToString, -{ +impl_from_tree!( + ;Ctx; ScriptContext, + Arc>, fn from_tree(top: &expression::Tree) -> Result>, Error> { Ok(Arc::new(expression::FromTree::from_tree(top)?)) } -} +); -impl expression::FromTree for Terminal -where - Pk: MiniscriptKey + str::FromStr, - Pk::Hash: str::FromStr, - Ctx: ScriptContext, - ::Err: ToString, - <::Hash as str::FromStr>::Err: ToString, -{ +impl_from_tree!( + ;Ctx; ScriptContext, + Terminal, fn from_tree(top: &expression::Tree) -> Result, Error> { let mut aliased_wrap; let frag_name; @@ -498,7 +470,7 @@ where expression::parse_num(x).map(Terminal::Older) }), ("sha256", 1) => expression::terminal(&top.args[0], |x| { - sha256::Hash::from_hex(x).map(Terminal::Sha256) + Pk::Sha256::from_str(x).map(Terminal::Sha256) }), ("hash256", 1) => expression::terminal(&top.args[0], |x| { sha256d::Hash::from_hex(x) @@ -620,7 +592,7 @@ where Ctx::check_global_validity(&ms)?; Ok(ms.node) } -} +); /// Helper trait to add a `push_astelem` method to `script::Builder` trait PushAstElem { @@ -657,12 +629,12 @@ impl Terminal { .push_int(t as i64) .push_opcode(opcodes::all::OP_CLTV), Terminal::Older(t) => builder.push_int(t as i64).push_opcode(opcodes::all::OP_CSV), - Terminal::Sha256(h) => builder + Terminal::Sha256(ref h) => builder .push_opcode(opcodes::all::OP_SIZE) .push_int(32) .push_opcode(opcodes::all::OP_EQUALVERIFY) .push_opcode(opcodes::all::OP_SHA256) - .push_slice(&h[..]) + .push_slice(&Pk::to_sha256(&h)) .push_opcode(opcodes::all::OP_EQUAL), Terminal::Hash256(h) => builder .push_opcode(opcodes::all::OP_SIZE) diff --git a/src/miniscript/context.rs b/src/miniscript/context.rs index 8d0c451b8..9aeb2563c 100644 --- a/src/miniscript/context.rs +++ b/src/miniscript/context.rs @@ -191,6 +191,7 @@ pub trait ScriptContext: fmt::Debug + Clone + Ord + PartialOrd + Eq + PartialEq + hash::Hash + private::Sealed where Self::Key: MiniscriptKey, + Self::Key: MiniscriptKey, { /// The consensus key associated with the type. Must be a parseable key type Key: ParseableKey; diff --git a/src/miniscript/decode.rs b/src/miniscript/decode.rs index 2e10139ae..f7a94d42b 100644 --- a/src/miniscript/decode.rs +++ b/src/miniscript/decode.rs @@ -142,7 +142,7 @@ pub enum Terminal { Older(u32), // hashlocks /// `SIZE 32 EQUALVERIFY SHA256 EQUAL` - Sha256(sha256::Hash), + Sha256(Pk::Sha256), /// `SIZE 32 EQUALVERIFY HASH256 EQUAL` Hash256(sha256d::Hash), /// `SIZE 32 EQUALVERIFY RIPEMD160 EQUAL` diff --git a/src/miniscript/iter.rs b/src/miniscript/iter.rs index 5207c0ce7..fc9e28044 100644 --- a/src/miniscript/iter.rs +++ b/src/miniscript/iter.rs @@ -121,7 +121,7 @@ impl Miniscript { /// NB: The function analyzes only single miniscript item and not any of its descendants in AST. /// To obtain a list of all public keys within AST use [Miniscript::iter_pk()] function, for example /// `miniscript.iter_pubkeys().collect()`. - pub fn get_leaf_pk(&self) -> Vec { + pub fn get_leapk(&self) -> Vec { match self.node { Terminal::PkK(ref key) => vec![key.clone()], Terminal::Multi(_, ref keys) | Terminal::MultiA(_, ref keys) => keys.clone(), @@ -138,7 +138,7 @@ impl Miniscript { /// NB: The function analyzes only single miniscript item and not any of its descendants in AST. /// To obtain a list of all public key hashes within AST use [Miniscript::iter_pkh()] function, /// for example `miniscript.iter_pubkey_hashes().collect()`. - pub fn get_leaf_pkh(&self) -> Vec { + pub fn get_leapkh(&self) -> Vec { match self.node { Terminal::PkH(ref hash) => vec![hash.clone()], Terminal::PkK(ref key) => vec![key.to_pubkeyhash()], @@ -156,7 +156,7 @@ impl Miniscript { /// NB: The function analyzes only single miniscript item and not any of its descendants in AST. /// To obtain a list of all public keys or hashes within AST use [Miniscript::iter_pk_pkh()] /// function, for example `miniscript.iter_pubkeys_and_hashes().collect()`. - pub fn get_leaf_pk_pkh(&self) -> Vec> { + pub fn get_leapk_pkh(&self) -> Vec> { match self.node { Terminal::PkH(ref hash) => vec![PkPkh::HashedPubkey(hash.clone())], Terminal::PkK(ref key) => vec![PkPkh::PlainPubkey(key.clone())], @@ -605,7 +605,7 @@ pub mod test { return; } let ms = *ms.branches().first().unwrap_or(&&ms); - assert_eq!(ms.get_leaf_pk(), k); + assert_eq!(ms.get_leapk(), k); }) } @@ -624,7 +624,7 @@ pub mod test { .collect(); // In our test cases we always have plain keys going first all.extend(h); - assert_eq!(ms.get_leaf_pkh(), all); + assert_eq!(ms.get_leapkh(), all); }) } @@ -642,7 +642,7 @@ pub mod test { } else { k.into_iter().map(|k| PkPkh::PlainPubkey(k)).collect() }; - assert_eq!(ms.get_leaf_pk_pkh(), r); + assert_eq!(ms.get_leapk_pkh(), r); }) } diff --git a/src/miniscript/mod.rs b/src/miniscript/mod.rs index 66d0b1f86..08c7f75d6 100644 --- a/src/miniscript/mod.rs +++ b/src/miniscript/mod.rs @@ -53,8 +53,9 @@ pub use crate::miniscript::context::ScriptContext; use crate::miniscript::decode::Terminal; use crate::miniscript::types::extra_props::ExtData; use crate::miniscript::types::Type; -use crate::{expression, Error, ForEach, ForEachKey, MiniscriptKey, ToPublicKey, TranslatePk}; - +use crate::{ + expression, Error, ForEach, ForEachKey, MiniscriptKey, ToPublicKey, TranslatePk, Translator, +}; #[cfg(test)] mod ms_tests; @@ -288,23 +289,12 @@ where type Output = Miniscript; /// Translates a struct from one generic to another where the translation - /// for Pk is provided by function `fpk`, and translation for PkH is - /// provided by function `fpkh`. - /// - /// # Panics - /// - /// If `fpk` returns an uncompressed key when converting to a Segwit descriptor. - /// To prevent this panic, ensure `fpk` returns an error in this case instead. - fn translate_pk( - &self, - mut fpk: Fpk, - mut fpkh: Fpkh, - ) -> Result + /// for Pk is provided by [`Translator`] + fn translate_pk(&self, translate: &mut T) -> Result where - Fpk: FnMut(&Pk) -> Result, - Fpkh: FnMut(&Pk::Hash) -> Result, + T: Translator, { - self.real_translate_pk(&mut fpk, &mut fpkh) + self.real_translate_pk(translate) } } @@ -317,18 +307,16 @@ impl Miniscript { self.node.real_for_each_key(pred) } - pub(crate) fn real_translate_pk( + pub(super) fn real_translate_pk( &self, - fpk: &mut Fpk, - fpkh: &mut Fpkh, + t: &mut T, ) -> Result, FuncError> where - Fpk: FnMut(&Pk) -> Result, - Fpkh: FnMut(&Pk::Hash) -> Result, Q: MiniscriptKey, CtxQ: ScriptContext, + T: Translator, { - let inner = self.node.real_translate_pk(fpk, fpkh)?; + let inner = self.node.real_translate_pk(t)?; let ms = Miniscript { //directly copying the type and ext is safe because translating public //key should not change any properties @@ -339,7 +327,11 @@ impl Miniscript { }; Ok(ms) } +} +impl_block_str!( + ;Ctx; ScriptContext, + Miniscript, /// Attempt to parse an insane(scripts don't clear sanity checks) /// from string into a Miniscript representation. /// Use this to parse scripts with repeated pubkeys, timelock mixing, malleable @@ -347,12 +339,7 @@ impl Miniscript { /// Some of the analysis guarantees of miniscript are lost when dealing with /// insane scripts. In general, in a multi-party setting users should only /// accept sane scripts. - pub fn from_str_insane(s: &str) -> Result, Error> - where - Pk: str::FromStr, - Pk::Hash: str::FromStr, - ::Err: ToString, - <::Hash as str::FromStr>::Err: ToString, + pub fn from_str_insane(s: &str,) -> Result, Error> { // This checks for invalid ASCII chars let top = expression::Tree::from_str(s)?; @@ -364,7 +351,7 @@ impl Miniscript { Ok(ms) } } -} +); impl Miniscript { /// Attempt to produce non-malleable satisfying witness for the @@ -417,27 +404,17 @@ impl Miniscript { } } -impl expression::FromTree for Arc> -where - Pk: MiniscriptKey + str::FromStr, - Pk::Hash: str::FromStr, - Ctx: ScriptContext, - ::Err: ToString, - <::Hash as str::FromStr>::Err: ToString, -{ +impl_from_tree!( + ;Ctx; ScriptContext, + Arc>, fn from_tree(top: &expression::Tree) -> Result>, Error> { Ok(Arc::new(expression::FromTree::from_tree(top)?)) } -} +); -impl expression::FromTree for Miniscript -where - Pk: MiniscriptKey + str::FromStr, - Pk::Hash: str::FromStr, - Ctx: ScriptContext, - ::Err: ToString, - <::Hash as str::FromStr>::Err: ToString, -{ +impl_from_tree!( + ;Ctx; ScriptContext, + Miniscript, /// Parse an expression tree into a Miniscript. As a general rule, this /// should not be called directly; rather go through the descriptor API. fn from_tree(top: &expression::Tree) -> Result, Error> { @@ -449,27 +426,21 @@ where phantom: PhantomData, }) } -} - -/// Parse a Miniscript from string and perform sanity checks -/// See [Miniscript::from_str_insane] to parse scripts from string that -/// do not clear the [Miniscript::sanity_check] checks. -impl str::FromStr for Miniscript -where - Pk: MiniscriptKey + str::FromStr, - Pk::Hash: str::FromStr, - Ctx: ScriptContext, - ::Err: ToString, - <::Hash as str::FromStr>::Err: ToString, -{ - type Err = Error; - +); + +impl_from_str!( + ;Ctx; ScriptContext, + Miniscript, + type Err = Error;, + /// Parse a Miniscript from string and perform sanity checks + /// See [Miniscript::from_str_insane] to parse scripts from string that + /// do not clear the [Miniscript::sanity_check] checks. fn from_str(s: &str) -> Result, Error> { let ms = Self::from_str_insane(s)?; ms.sanity_check()?; Ok(ms) } -} +); serde_string_impl_pk!(Miniscript, "a miniscript", Ctx; ScriptContext); @@ -491,10 +462,8 @@ mod tests { use crate::miniscript::Terminal; use crate::policy::Liftable; use crate::prelude::*; - use crate::{ - hex_script, DummyKey, DummyKeyHash, MiniscriptKey, Satisfier, ToPublicKey, TranslatePk, - TranslatePk1, TranslatePk2, - }; + use crate::test_utils::{StrKeyTranslator, StrXOnlyKeyTranslator}; + use crate::{hex_script, DummyKey, DummyKeyHash, Satisfier, ToPublicKey, TranslatePk}; type Segwitv0Script = Miniscript; type Tapscript = Miniscript; @@ -520,19 +489,11 @@ mod tests { ret } - fn string_rtt( - script: Miniscript, - expected_debug: Str1, - expected_display: Str2, - ) where - Pk: MiniscriptKey + str::FromStr, - Pk::Hash: str::FromStr, - Ctx: ScriptContext, - ::Err: ToString, - <::Hash as str::FromStr>::Err: ToString, - Str1: Into>, - Str2: Into>, - { + fn string_rtt( + script: Miniscript, + expected_debug: &str, + expected_display: &str, + ) { assert_eq!(script.ty.corr.base, types::Base::B); let debug = format!("{:?}", script); let display = format!("{}", script); @@ -544,12 +505,24 @@ mod tests { } let roundtrip = Miniscript::from_str(&display).expect("parse string serialization"); assert_eq!(roundtrip, script); + } - let translated = script.translate_pk_infallible(Pk::clone, Pk::Hash::clone); - assert_eq!(translated, script); - - let translated = script.translate_pk1_infallible(Pk::clone); - assert_eq!(translated, script); + fn dummy_string_rtt( + script: Miniscript, + expected_debug: &str, + expected_display: &str, + ) { + assert_eq!(script.ty.corr.base, types::Base::B); + let debug = format!("{:?}", script); + let display = format!("{}", script); + if let Some(expected) = expected_debug.into() { + assert_eq!(debug, expected); + } + if let Some(expected) = expected_display.into() { + assert_eq!(display, expected); + } + let roundtrip = Miniscript::from_str(&display).expect("parse string serialization"); + assert_eq!(roundtrip, script); } fn script_rtt>>(script: Segwitv0Script, expected_hex: Str1) { @@ -665,7 +638,7 @@ mod tests { ext: ExtData::cast_check(ExtData::from_pk_k::()).unwrap(), phantom: PhantomData, }; - string_rtt(pkk_ms, "[B/onduesm]c:[K/onduesm]pk_k(DummyKey)", "pk()"); + dummy_string_rtt(pkk_ms, "[B/onduesm]c:[K/onduesm]pk_k(DummyKey)", "pk()"); let pkh_ms: Miniscript = Miniscript { node: Terminal::Check(Arc::new(Miniscript { @@ -678,7 +651,7 @@ mod tests { ext: ExtData::cast_check(ExtData::from_pk_h::()).unwrap(), phantom: PhantomData, }; - string_rtt(pkh_ms, "[B/nduesm]c:[K/nduesm]pk_h(DummyKeyHash)", "pkh()"); + dummy_string_rtt(pkh_ms, "[B/nduesm]c:[K/nduesm]pk_h(DummyKeyHash)", "pkh()"); let pkk_ms: Segwitv0Script = Miniscript { node: Terminal::Check(Arc::new(Miniscript { @@ -1048,12 +1021,9 @@ mod tests { assert_eq!(tap_multi_a_ms.to_string(), "multi_a(1,A,B,C)"); // Test encode/decode and translation tests - let tap_ms = tap_multi_a_ms.translate_pk2_infallible(|_| { - XOnlyPublicKey::from_str( - "e948a0bbf8b15ee47cf0851afbce8835b5f06d3003b8e7ed6104e82a1d41d6f8", - ) - .unwrap() - }); + let tap_ms = tap_multi_a_ms + .translate_pk(&mut StrXOnlyKeyTranslator::new()) + .unwrap(); // script rtt test assert_eq!( Miniscript::::parse_insane(&tap_ms.encode()).unwrap(), @@ -1092,15 +1062,7 @@ mod tests { "and_b(1,s:and_v(v:older(9),c:pk_k(A)))", ) .unwrap(); - let ms_trans = ms.translate_pk_infallible( - |_x| { - bitcoin::PublicKey::from_str( - "02fbcf092916824cc56c4591abeedd54586f5ffc73c6ba88118162e3500ad695ea", - ) - .unwrap() - }, - |_x| unreachable!(), - ); + let ms_trans = ms.translate_pk(&mut StrKeyTranslator::new()).unwrap(); let enc = ms_trans.encode(); let ms = Miniscript::::parse_insane(&enc).unwrap(); assert_eq!(ms_trans.encode(), ms.encode()); diff --git a/src/miniscript/satisfy.rs b/src/miniscript/satisfy.rs index 6f74ab61a..be1b477da 100644 --- a/src/miniscript/satisfy.rs +++ b/src/miniscript/satisfy.rs @@ -21,7 +21,7 @@ use core::{cmp, i64, mem}; use bitcoin; -use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d}; +use bitcoin::hashes::{hash160, ripemd160, sha256d}; use bitcoin::secp256k1::XOnlyPublicKey; use bitcoin::util::taproot::{ControlBlock, LeafVersion, TapLeafHash}; use sync::Arc; @@ -90,7 +90,7 @@ pub trait Satisfier { } /// Given a SHA256 hash, look up its preimage - fn lookup_sha256(&self, _: sha256::Hash) -> Option { + fn lookup_sha256(&self, _: &Pk::Sha256) -> Option { None } @@ -257,7 +257,7 @@ impl<'a, Pk: MiniscriptKey + ToPublicKey, S: Satisfier> Satisfier for &' (**self).lookup_tap_control_block_map() } - fn lookup_sha256(&self, h: sha256::Hash) -> Option { + fn lookup_sha256(&self, h: &Pk::Sha256) -> Option { (**self).lookup_sha256(h) } @@ -319,7 +319,7 @@ impl<'a, Pk: MiniscriptKey + ToPublicKey, S: Satisfier> Satisfier for &' (**self).lookup_tap_control_block_map() } - fn lookup_sha256(&self, h: sha256::Hash) -> Option { + fn lookup_sha256(&self, h: &Pk::Sha256) -> Option { (**self).lookup_sha256(h) } @@ -433,7 +433,7 @@ macro_rules! impl_tuple_satisfier { None } - fn lookup_sha256(&self, h: sha256::Hash) -> Option { + fn lookup_sha256(&self, h: &Pk::Sha256) -> Option { let &($(ref $ty,)*) = self; $( if let Some(result) = $ty.lookup_sha256(h) { @@ -601,7 +601,7 @@ impl Witness { } /// Turn a hash preimage into (part of) a satisfaction - fn sha256_preimage>(sat: S, h: sha256::Hash) -> Self { + fn sha256_preimage>(sat: S, h: &Pk::Sha256) -> Self { match sat.lookup_sha256(h) { Some(pre) => Witness::Stack(vec![pre.to_vec()]), // Note hash preimages are unavailable instead of impossible @@ -981,7 +981,7 @@ impl Satisfaction { stack: Witness::hash160_preimage(stfr, h), has_sig: false, }, - Terminal::Sha256(h) => Satisfaction { + Terminal::Sha256(ref h) => Satisfaction { stack: Witness::sha256_preimage(stfr, h), has_sig: false, }, diff --git a/src/policy/compiler.rs b/src/policy/compiler.rs index 90eec0e38..79e76d0d9 100644 --- a/src/policy/compiler.rs +++ b/src/policy/compiler.rs @@ -845,7 +845,10 @@ where } Concrete::After(n) => insert_wrap!(AstElemExt::terminal(Terminal::After(n))), Concrete::Older(n) => insert_wrap!(AstElemExt::terminal(Terminal::Older(n))), - Concrete::Sha256(hash) => insert_wrap!(AstElemExt::terminal(Terminal::Sha256(hash))), + Concrete::Sha256(ref hash) => { + insert_wrap!(AstElemExt::terminal(Terminal::Sha256(hash.clone()))) + } + // Satisfaction-cost + script-cost Concrete::Hash256(hash) => insert_wrap!(AstElemExt::terminal(Terminal::Hash256(hash))), Concrete::Ripemd160(hash) => insert_wrap!(AstElemExt::terminal(Terminal::Ripemd160(hash))), Concrete::Hash160(hash) => insert_wrap!(AstElemExt::terminal(Terminal::Hash160(hash))), @@ -946,6 +949,8 @@ where let r = best_compilations(policy_cache, &subs[1].1, rw * sat_prob, *dissat_prob)?; r_comp.push(r); } + + // or(sha256, pk) compile_binary!(&mut l_comp[0], &mut r_comp[0], [lw, rw], Terminal::OrB); compile_binary!(&mut r_comp[0], &mut l_comp[0], [rw, lw], Terminal::OrB); diff --git a/src/policy/concrete.rs b/src/policy/concrete.rs index f449b436b..a007ea881 100644 --- a/src/policy/concrete.rs +++ b/src/policy/concrete.rs @@ -20,7 +20,7 @@ use core::{fmt, str}; use std::error; use bitcoin::hashes::hex::FromHex; -use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d}; +use bitcoin::hashes::{hash160, ripemd160, sha256d}; #[cfg(feature = "compiler")] use { crate::descriptor::TapTree, @@ -40,7 +40,7 @@ use crate::expression::{self, FromTree}; use crate::miniscript::limits::{LOCKTIME_THRESHOLD, SEQUENCE_LOCKTIME_TYPE_FLAG}; use crate::miniscript::types::extra_props::TimelockInfo; use crate::prelude::*; -use crate::{errstr, Error, ForEach, ForEachKey, MiniscriptKey}; +use crate::{errstr, Error, ForEach, ForEachKey, MiniscriptKey, Translator}; /// Concrete policy which corresponds directly to a Miniscript structure, /// and whose disjunctions are annotated with satisfaction probabilities @@ -58,7 +58,7 @@ pub enum Policy { /// A relative locktime restriction Older(u32), /// A SHA256 whose preimage must be provided to satisfy the descriptor - Sha256(sha256::Hash), + Sha256(Pk::Sha256), /// A SHA256d whose preimage must be provided to satisfy the descriptor Hash256(sha256d::Hash), /// A RIPEMD160 whose preimage must be provided to satisfy the descriptor @@ -326,39 +326,67 @@ impl Policy { /// # Example /// /// ``` - /// use miniscript::{bitcoin::PublicKey, policy::concrete::Policy}; + /// use miniscript::{bitcoin::PublicKey, policy::concrete::Policy, Translator}; /// use std::str::FromStr; + /// use std::collections::HashMap; + /// use miniscript::bitcoin::hashes::{sha256, hash160}; /// let alice_key = "0270cf3c71f65a3d93d285d9149fddeeb638f87a2d4d8cf16c525f71c417439777"; /// let bob_key = "02f43b15c50a436f5335dbea8a64dd3b4e63e34c3b50c42598acb5f4f336b5d2fb"; /// let placeholder_policy = Policy::::from_str("and(pk(alice_key),pk(bob_key))").unwrap(); /// - /// let real_policy = placeholder_policy.translate_pk(|placeholder: &String| match placeholder.as_str() { - /// "alice_key" => PublicKey::from_str(alice_key), - /// "bob_key" => PublicKey::from_str(bob_key), - /// _ => panic!("unknown key!") - /// }).unwrap(); + /// // Information to translator abstract String type keys to concrete bitcoin::PublicKey. + /// // In practice, wallets would map from String key names to BIP32 keys + /// struct StrPkTranslator { + /// pk_map: HashMap + /// } + /// + /// // If we also wanted to provide mapping of other associated types(sha256, older etc), + /// // we would use the general Translator Trait. + /// impl Translator for StrPkTranslator { + /// // Provides the translation public keys P -> Q + /// fn pk(&mut self, pk: &String) -> Result { + /// self.pk_map.get(pk).copied().ok_or(()) // Dummy Err + /// } + /// + /// // If our policy also contained other fragments, we could provide the translation here. + /// fn pkh(&mut self, pkh: &String) -> Result { + /// unreachable!("Policy does not contain any pkh fragment"); + /// } + /// + /// // If our policy also contained other fragments, we could provide the translation here. + /// fn sha256(&mut self, sha256: &String) -> Result { + /// unreachable!("Policy does not contain any sha256 fragment"); + /// } + /// } + /// + /// let mut pk_map = HashMap::new(); + /// pk_map.insert(String::from("alice_key"), bitcoin::PublicKey::from_str(alice_key).unwrap()); + /// pk_map.insert(String::from("bob_key"), bitcoin::PublicKey::from_str(bob_key).unwrap()); + /// let mut t = StrPkTranslator { pk_map: pk_map }; + /// + /// let real_policy = placeholder_policy.translate_pk(&mut t).unwrap(); /// /// let expected_policy = Policy::from_str(&format!("and(pk({}),pk({}))", alice_key, bob_key)).unwrap(); /// assert_eq!(real_policy, expected_policy); /// ``` - pub fn translate_pk(&self, mut fpk: Fpk) -> Result, E> + pub fn translate_pk(&self, t: &mut T) -> Result, E> where - Fpk: FnMut(&Pk) -> Result, + T: Translator, Q: MiniscriptKey, { - self._translate_pk(&mut fpk) + self._translate_pk(t) } - fn _translate_pk(&self, fpk: &mut Fpk) -> Result, E> + fn _translate_pk(&self, t: &mut T) -> Result, E> where - Fpk: FnMut(&Pk) -> Result, + T: Translator, Q: MiniscriptKey, { match *self { Policy::Unsatisfiable => Ok(Policy::Unsatisfiable), Policy::Trivial => Ok(Policy::Trivial), - Policy::Key(ref pk) => fpk(pk).map(Policy::Key), - Policy::Sha256(ref h) => Ok(Policy::Sha256(*h)), + Policy::Key(ref pk) => t.pk(pk).map(Policy::Key), + Policy::Sha256(ref h) => t.sha256(h).map(Policy::Sha256), Policy::Hash256(ref h) => Ok(Policy::Hash256(*h)), Policy::Ripemd160(ref h) => Ok(Policy::Ripemd160(*h)), Policy::Hash160(ref h) => Ok(Policy::Hash160(*h)), @@ -366,17 +394,17 @@ impl Policy { Policy::Older(n) => Ok(Policy::Older(n)), Policy::Threshold(k, ref subs) => { let new_subs: Result>, _> = - subs.iter().map(|sub| sub._translate_pk(fpk)).collect(); + subs.iter().map(|sub| sub._translate_pk(t)).collect(); new_subs.map(|ok| Policy::Threshold(k, ok)) } Policy::And(ref subs) => Ok(Policy::And( subs.iter() - .map(|sub| sub._translate_pk(fpk)) + .map(|sub| sub._translate_pk(t)) .collect::>, E>>()?, )), Policy::Or(ref subs) => Ok(Policy::Or( subs.iter() - .map(|&(ref prob, ref sub)| Ok((*prob, sub._translate_pk(fpk)?))) + .map(|&(ref prob, ref sub)| Ok((*prob, sub._translate_pk(t)?))) .collect::)>, E>>()?, )), } @@ -601,7 +629,7 @@ impl fmt::Debug for Policy { Policy::Key(ref pk) => write!(f, "pk({:?})", pk), Policy::After(n) => write!(f, "after({})", n), Policy::Older(n) => write!(f, "older({})", n), - Policy::Sha256(h) => write!(f, "sha256({})", h), + Policy::Sha256(ref h) => write!(f, "sha256({})", h), Policy::Hash256(h) => write!(f, "hash256({})", h), Policy::Ripemd160(h) => write!(f, "ripemd160({})", h), Policy::Hash160(h) => write!(f, "hash160({})", h), @@ -644,7 +672,7 @@ impl fmt::Display for Policy { Policy::Key(ref pk) => write!(f, "pk({})", pk), Policy::After(n) => write!(f, "after({})", n), Policy::Older(n) => write!(f, "older({})", n), - Policy::Sha256(h) => write!(f, "sha256({})", h), + Policy::Sha256(ref h) => write!(f, "sha256({})", h), Policy::Hash256(h) => write!(f, "hash256({})", h), Policy::Ripemd160(h) => write!(f, "ripemd160({})", h), Policy::Hash160(h) => write!(f, "hash160({})", h), @@ -679,15 +707,9 @@ impl fmt::Display for Policy { } } -impl str::FromStr for Policy -where - Pk: MiniscriptKey + str::FromStr, - Pk::Hash: str::FromStr, - ::Err: ToString, - <::Hash as str::FromStr>::Err: ToString, -{ - type Err = Error; - +impl_from_str!( + Policy, + type Err = Error;, fn from_str(s: &str) -> Result, Error> { for ch in s.as_bytes() { if *ch < 20 || *ch > 127 { @@ -700,22 +722,18 @@ where policy.check_timelocks()?; Ok(policy) } -} +); serde_string_impl_pk!(Policy, "a miniscript concrete policy"); -impl Policy -where - Pk: MiniscriptKey + str::FromStr, - Pk::Hash: str::FromStr, - ::Err: ToString, -{ +#[rustfmt::skip] +impl_block_str!( + Policy, /// Helper function for `from_tree` to parse subexpressions with /// names of the form x@y - fn from_tree_prob( - top: &expression::Tree, - allow_prob: bool, - ) -> Result<(usize, Policy), Error> { + fn from_tree_prob(top: &expression::Tree, allow_prob: bool,) + -> Result<(usize, Policy), Error> + { let frag_prob; let frag_name; let mut name_split = top.name.split('@'); @@ -762,7 +780,7 @@ where Ok(Policy::Older(num)) } ("sha256", 1) => expression::terminal(&top.args[0], |x| { - sha256::Hash::from_hex(x).map(Policy::Sha256) + ::from_str(x).map(Policy::Sha256) }), ("hash256", 1) => expression::terminal(&top.args[0], |x| { sha256d::Hash::from_hex(x).map(Policy::Hash256) @@ -813,18 +831,14 @@ where } .map(|res| (frag_prob, res)) } -} +); -impl FromTree for Policy -where - Pk: MiniscriptKey + str::FromStr, - Pk::Hash: str::FromStr, - ::Err: ToString, -{ +impl_from_tree!( + Policy, fn from_tree(top: &expression::Tree) -> Result, Error> { Policy::from_tree_prob(top, false).map(|(_, result)| result) } -} +); /// Create a Huffman Tree from compiled [Miniscript] nodes #[cfg(feature = "compiler")] diff --git a/src/policy/mod.rs b/src/policy/mod.rs index 10ee3af61..29552da7b 100644 --- a/src/policy/mod.rs +++ b/src/policy/mod.rs @@ -128,7 +128,7 @@ impl Liftable for Terminal { Terminal::PkH(ref pkh) => Semantic::KeyHash(pkh.clone()), Terminal::After(t) => Semantic::After(t), Terminal::Older(t) => Semantic::Older(t), - Terminal::Sha256(h) => Semantic::Sha256(h), + Terminal::Sha256(ref h) => Semantic::Sha256(h.clone()), Terminal::Hash256(h) => Semantic::Hash256(h), Terminal::Ripemd160(h) => Semantic::Ripemd160(h), Terminal::Hash160(h) => Semantic::Hash160(h), @@ -203,7 +203,7 @@ impl Liftable for Concrete { Concrete::Key(ref pk) => Semantic::KeyHash(pk.to_pubkeyhash()), Concrete::After(t) => Semantic::After(t), Concrete::Older(t) => Semantic::Older(t), - Concrete::Sha256(h) => Semantic::Sha256(h), + Concrete::Sha256(ref h) => Semantic::Sha256(h.clone()), Concrete::Hash256(h) => Semantic::Hash256(h), Concrete::Ripemd160(h) => Semantic::Ripemd160(h), Concrete::Hash160(h) => Semantic::Hash160(h), diff --git a/src/policy/semantic.rs b/src/policy/semantic.rs index 83d60b172..58452429d 100644 --- a/src/policy/semantic.rs +++ b/src/policy/semantic.rs @@ -18,12 +18,12 @@ use core::str::FromStr; use core::{fmt, str}; use bitcoin::hashes::hex::FromHex; -use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d}; +use bitcoin::hashes::{hash160, ripemd160, sha256d}; use super::concrete::PolicyError; use super::ENTAILMENT_MAX_TERMINALS; use crate::prelude::*; -use crate::{errstr, expression, timelock, Error, ForEach, ForEachKey, MiniscriptKey}; +use crate::{errstr, expression, timelock, Error, ForEach, ForEachKey, MiniscriptKey, Translator}; /// Abstract policy which corresponds to the semantics of a Miniscript /// and which allows complex forms of analysis, e.g. filtering and @@ -44,7 +44,7 @@ pub enum Policy { /// A relative locktime restriction Older(u32), /// A SHA256 whose preimage must be provided to satisfy the descriptor - Sha256(sha256::Hash), + Sha256(Pk::Sha256), /// A SHA256d whose preimage must be provided to satisfy the descriptor Hash256(sha256d::Hash), /// A RIPEMD160 whose preimage must be provided to satisfy the descriptor @@ -82,39 +82,67 @@ impl Policy { /// # Example /// /// ``` - /// use miniscript::{bitcoin::{hashes::hash160, PublicKey}, policy::semantic::Policy}; + /// use miniscript::{bitcoin::{hashes::hash160, PublicKey}, policy::semantic::Policy, Translator}; /// use std::str::FromStr; + /// use std::collections::HashMap; + /// use miniscript::bitcoin::hashes::sha256; /// let alice_pkh = "236ada020df3208d2517f4b0db03e16f92cd8cf1"; /// let bob_pkh = "3e89b972416ae33870b4634d03b8cdc773200cac"; /// let placeholder_policy = Policy::::from_str("and(pkh(alice_pkh),pkh(bob_pkh))").unwrap(); /// - /// let real_policy = placeholder_policy.translate_pkh(|placeholder| match placeholder.as_str() { - /// "alice_pkh" => hash160::Hash::from_str(alice_pkh), - /// "bob_pkh" => hash160::Hash::from_str(bob_pkh), - /// _ => panic!("unknown key hash!") - /// }).unwrap(); + /// // Information to translator abstract String type keys to concrete bitcoin::PublicKey. + /// // In practice, wallets would map from String key names to BIP32 keys + /// struct StrPkTranslator { + /// pk_map: HashMap + /// } /// - /// let expected_policy = Policy::::from_str(&format!("and(pkh({}),pkh({}))", alice_pkh, bob_pkh)).unwrap(); + /// // If we also wanted to provide mapping of other associated types(sha256, older etc), + /// // we would use the general Translator Trait. + /// impl Translator for StrPkTranslator { + /// fn pk(&mut self, pk: &String) -> Result { + /// unreachable!("Policy does not contain any pk fragment"); + /// } + /// + /// // Provides the translation public keys P::Hash -> Q::Hash + /// // If our policy also contained other fragments, we could provide the translation here. + /// fn pkh(&mut self, pkh: &String) -> Result { + /// self.pk_map.get(pkh).copied().ok_or(()) // Dummy Err + /// } + /// + /// // If our policy also contained other fragments, we could provide the translation here. + /// fn sha256(&mut self, sha256: &String) -> Result { + /// unreachable!("Policy does not contain any sha256 fragment"); + /// } + /// } + /// + /// let mut pk_map = HashMap::new(); + /// pk_map.insert(String::from("alice_pkh"), hash160::Hash::from_str(alice_pkh).unwrap()); + /// pk_map.insert(String::from("bob_pkh"), hash160::Hash::from_str(bob_pkh).unwrap()); + /// let mut t = StrPkTranslator { pk_map: pk_map }; + /// + /// let real_policy = placeholder_policy.translate_pkh(&mut t).unwrap(); + /// + /// let expected_policy = Policy::from_str(&format!("and(pkh({}),pkh({}))", alice_pkh, bob_pkh)).unwrap(); /// assert_eq!(real_policy, expected_policy); /// ``` - pub fn translate_pkh(&self, mut fpkh: Fpkh) -> Result, E> + pub fn translate_pkh(&self, t: &mut T) -> Result, E> where - Fpkh: FnMut(&Pk::Hash) -> Result, + T: Translator, Q: MiniscriptKey, { - self._translate_pkh(&mut fpkh) + self._translate_pkh(t) } - fn _translate_pkh(&self, fpkh: &mut Fpkh) -> Result, E> + fn _translate_pkh(&self, t: &mut T) -> Result, E> where - Fpkh: FnMut(&Pk::Hash) -> Result, + T: Translator, Q: MiniscriptKey, { match *self { Policy::Unsatisfiable => Ok(Policy::Unsatisfiable), Policy::Trivial => Ok(Policy::Trivial), - Policy::KeyHash(ref pkh) => fpkh(pkh).map(Policy::KeyHash), - Policy::Sha256(ref h) => Ok(Policy::Sha256(*h)), + Policy::KeyHash(ref pkh) => t.pkh(pkh).map(Policy::KeyHash), + Policy::Sha256(ref h) => t.sha256(h).map(Policy::Sha256), Policy::Hash256(ref h) => Ok(Policy::Hash256(*h)), Policy::Ripemd160(ref h) => Ok(Policy::Ripemd160(*h)), Policy::Hash160(ref h) => Ok(Policy::Hash160(*h)), @@ -122,7 +150,7 @@ impl Policy { Policy::Older(n) => Ok(Policy::Older(n)), Policy::Threshold(k, ref subs) => { let new_subs: Result>, _> = - subs.iter().map(|sub| sub._translate_pkh(fpkh)).collect(); + subs.iter().map(|sub| sub._translate_pkh(t)).collect(); new_subs.map(|ok| Policy::Threshold(k, ok)) } } @@ -221,7 +249,7 @@ impl fmt::Debug for Policy { Policy::KeyHash(ref pkh) => write!(f, "pkh({:?})", pkh), Policy::After(n) => write!(f, "after({})", n), Policy::Older(n) => write!(f, "older({})", n), - Policy::Sha256(h) => write!(f, "sha256({})", h), + Policy::Sha256(ref h) => write!(f, "sha256({})", h), Policy::Hash256(h) => write!(f, "hash256({})", h), Policy::Ripemd160(h) => write!(f, "ripemd160({})", h), Policy::Hash160(h) => write!(f, "hash160({})", h), @@ -254,7 +282,7 @@ impl fmt::Display for Policy { Policy::KeyHash(ref pkh) => write!(f, "pkh({})", pkh), Policy::After(n) => write!(f, "after({})", n), Policy::Older(n) => write!(f, "older({})", n), - Policy::Sha256(h) => write!(f, "sha256({})", h), + Policy::Sha256(ref h) => write!(f, "sha256({})", h), Policy::Hash256(h) => write!(f, "hash256({})", h), Policy::Ripemd160(h) => write!(f, "ripemd160({})", h), Policy::Hash160(h) => write!(f, "hash160({})", h), @@ -279,15 +307,9 @@ impl fmt::Display for Policy { } } -impl str::FromStr for Policy -where - Pk: MiniscriptKey + str::FromStr, - Pk::Hash: str::FromStr, - ::Err: ToString, - <::Hash as str::FromStr>::Err: ToString, -{ - type Err = Error; - +impl_from_str!( + Policy, + type Err = Error;, fn from_str(s: &str) -> Result, Error> { for ch in s.as_bytes() { if *ch < 20 || *ch > 127 { @@ -298,17 +320,12 @@ where let tree = expression::Tree::from_str(s)?; expression::FromTree::from_tree(&tree) } -} +); serde_string_impl_pk!(Policy, "a miniscript semantic policy"); -impl expression::FromTree for Policy -where - Pk: MiniscriptKey + str::FromStr, - Pk::Hash: str::FromStr, - ::Err: ToString, - <::Hash as str::FromStr>::Err: ToString, -{ +impl_from_tree!( + Policy, fn from_tree(top: &expression::Tree) -> Result, Error> { match (top.name, top.args.len()) { ("UNSATISFIABLE", 0) => Ok(Policy::Unsatisfiable), @@ -323,7 +340,7 @@ where expression::parse_num(x).map(Policy::Older) }), ("sha256", 1) => expression::terminal(&top.args[0], |x| { - sha256::Hash::from_hex(x).map(Policy::Sha256) + Pk::Sha256::from_str(x).map(Policy::Sha256) }), ("hash256", 1) => expression::terminal(&top.args[0], |x| { sha256d::Hash::from_hex(x).map(Policy::Hash256) @@ -384,7 +401,7 @@ where _ => Err(errstr(top.name)), } } -} +); impl Policy { /// Flatten out trees of `And`s and `Or`s; eliminate `Trivial` and diff --git a/src/psbt/mod.rs b/src/psbt/mod.rs index 0a78ef6e2..cc8a9f7c1 100644 --- a/src/psbt/mod.rs +++ b/src/psbt/mod.rs @@ -24,8 +24,8 @@ use core::ops::Deref; #[cfg(feature = "std")] use std::error; -use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d}; -use bitcoin::secp256k1::{self, Secp256k1}; +use bitcoin::hashes::{hash160, ripemd160, sha256d}; +use bitcoin::secp256k1::{self, Secp256k1, VerifyOnly}; use bitcoin::util::psbt::{self, PartiallySignedTransaction as Psbt}; use bitcoin::util::sighash::SighashCache; use bitcoin::util::taproot::{self, ControlBlock, LeafVersion, TapLeafHash}; @@ -36,8 +36,8 @@ use crate::miniscript::limits::SEQUENCE_LOCKTIME_DISABLE_FLAG; use crate::miniscript::satisfy::{After, Older}; use crate::prelude::*; use crate::{ - descriptor, interpreter, Descriptor, DescriptorPublicKey, MiniscriptKey, Preimage32, Satisfier, - ToPublicKey, TranslatePk, TranslatePk2, + descriptor, interpreter, Descriptor, DescriptorPublicKey, MiniscriptKey, PkTranslator, + Preimage32, Satisfier, ToPublicKey, TranslatePk, }; mod finalizer; @@ -238,9 +238,7 @@ impl fmt::Display for InputError { sighashflag {:?} rather than required {:?}", pubkey, got, required ), - InputError::CouldNotSatisfyTr => { - write!(f, "Could not satisfy Tr descriptor") - } + InputError::CouldNotSatisfyTr => write!(f, "Could not satisfy Tr descriptor"), InputError::NonStandardSighashType(e) => write!(f, "Non-standard sighash type {}", e), } } @@ -370,10 +368,10 @@ impl<'psbt, Pk: MiniscriptKey + ToPublicKey> Satisfier for PsbtInputSatisfie .and_then(try_vec_as_preimage32) } - fn lookup_sha256(&self, h: sha256::Hash) -> Option { + fn lookup_sha256(&self, h: &Pk::Sha256) -> Option { self.psbt.inputs[self.index] .sha256_preimages - .get(&h) + .get(&Pk::to_sha256(h)) .and_then(try_vec_as_preimage32) } @@ -932,6 +930,65 @@ impl PsbtInputExt for psbt::Input { } } +// Traverse the pkh lookup while maintaining a reverse map for storing the map +// hash160 -> (XonlyPublicKey)/PublicKey +struct XOnlyHashLookUp( + pub BTreeMap, + pub secp256k1::Secp256k1, +); + +impl PkTranslator + for XOnlyHashLookUp +{ + fn pk( + &mut self, + xpk: &DescriptorPublicKey, + ) -> Result { + xpk.derive_public_key(&self.1) + } + + fn pkh( + &mut self, + xpk: &DescriptorPublicKey, + ) -> Result { + let pk = xpk.derive_public_key(&self.1)?; + let xonly = pk.to_x_only_pubkey(); + let hash = xonly.to_pubkeyhash(); + self.0.insert(hash, xonly); + Ok(hash) + } +} + +// Traverse the pkh lookup while maintaining a reverse map for storing the map +// hash160 -> (XonlyPublicKey)/PublicKey +struct KeySourceLookUp( + pub BTreeMap, + pub secp256k1::Secp256k1, +); + +impl PkTranslator + for KeySourceLookUp +{ + fn pk( + &mut self, + xpk: &DescriptorPublicKey, + ) -> Result { + let derived = xpk.derive_public_key(&self.1)?; + self.0.insert( + derived.to_public_key().inner, + (xpk.master_fingerprint(), xpk.full_derivation_path()), + ); + Ok(derived) + } + + fn pkh( + &mut self, + xpk: &DescriptorPublicKey, + ) -> Result { + Ok(self.pk(xpk)?.to_pubkeyhash()) + } +} + fn update_input_with_descriptor_helper( input: &mut psbt::Input, descriptor: &Descriptor, @@ -940,20 +997,12 @@ fn update_input_with_descriptor_helper( // One needs the derived descriptor and the other needs to know whether the script_pubkey check // failed. ) -> Result<(Descriptor, bool), descriptor::ConversionError> { - use core::cell::RefCell; let secp = secp256k1::Secp256k1::verification_only(); let derived = if let Descriptor::Tr(_) = &descriptor { - let mut hash_lookup = BTreeMap::new(); - let derived = descriptor.translate_pk( - |xpk| xpk.derive_public_key(&secp), - |xpk| { - let xonly = xpk.derive_public_key(&secp)?.to_x_only_pubkey(); - let hash = xonly.to_pubkeyhash(); - hash_lookup.insert(hash, xonly); - Ok(hash) - }, - )?; + let mut hash_lookup = XOnlyHashLookUp(BTreeMap::new(), secp); + // Feed in information about pkh -> pk mapping here + let derived = descriptor.translate_pk(&mut hash_lookup)?; if let Some(check_script) = check_script { if check_script != derived.script_pubkey() { @@ -994,6 +1043,7 @@ fn update_input_with_descriptor_helper( } (PkPkh::HashedPubkey(hash), PkPkh::HashedPubkey(xpk)) => ( *hash_lookup + .0 .get(&hash) .expect("translate_pk inserted an entry for every hash"), xpk, @@ -1022,15 +1072,8 @@ fn update_input_with_descriptor_helper( derived } else { // have to use a RefCell because we can't pass FnMut to translate_pk2 - let bip32_derivation = RefCell::new(BTreeMap::new()); - let derived = descriptor.translate_pk2(|xpk| { - let derived = xpk.derive_public_key(&secp)?; - bip32_derivation.borrow_mut().insert( - derived.to_public_key().inner, - (xpk.master_fingerprint(), xpk.full_derivation_path()), - ); - Ok(derived) - })?; + let mut bip32_derivation = KeySourceLookUp(BTreeMap::new(), Secp256k1::verification_only()); + let derived = descriptor.translate_pk(&mut bip32_derivation)?; if let Some(check_script) = check_script { if check_script != derived.script_pubkey() { @@ -1038,7 +1081,7 @@ fn update_input_with_descriptor_helper( } } - input.bip32_derivation = bip32_derivation.into_inner(); + input.bip32_derivation = bip32_derivation.0; match &derived { Descriptor::Bare(_) | Descriptor::Pkh(_) | Descriptor::Wpkh(_) => {} diff --git a/src/test_utils.rs b/src/test_utils.rs new file mode 100644 index 000000000..8098482b4 --- /dev/null +++ b/src/test_utils.rs @@ -0,0 +1,150 @@ +//! Generally useful utilities for test scripts + +use std::collections::HashMap; +use std::str::FromStr; + +use bitcoin::hashes::{hash160, sha256}; +use bitcoin::secp256k1; + +use crate::{MiniscriptKey, Translator}; + +/// Translate from a String MiniscriptKey type to bitcoin::PublicKey +/// If the hashmap is populated, this will lookup for keys in HashMap +/// Otherwise, this will return a translation to a random key +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct StrKeyTranslator { + pub pk_map: HashMap, + pub pkh_map: HashMap, + pub sha256_map: HashMap, +} + +impl Translator for StrKeyTranslator { + fn pk(&mut self, pk: &String) -> Result { + let key = self.pk_map.get(pk).copied().unwrap_or_else(|| { + bitcoin::PublicKey::from_str( + "02c2122e30e73f7fe37986e3f81ded00158e94b7ad472369b83bbdd28a9a198a39", + ) + .unwrap() + }); + Ok(key) + } + + fn pkh(&mut self, pkh: &String) -> Result { + let hash = self.pkh_map.get(pkh).copied().unwrap_or_else(|| { + hash160::Hash::from_str("be8f27af36217ba89c793c419f058cd4e2a54e26").unwrap() + }); + Ok(hash) + } + + fn sha256(&mut self, _sha256: &String) -> Result { + let hash = sha256::Hash::from_str( + "4ae81572f06e1b88fd5ced7a1a000945432e83e1551e6f721ee9c00b8cc33260", + ) + .unwrap(); + Ok(hash) + } +} + +/// Same as [`StrKeyTranslator`], but for [`bitcoin::XOnlyPublicKey`] +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct StrXOnlyKeyTranslator { + pub pk_map: HashMap, + pub pkh_map: HashMap, + pub sha256_map: HashMap, +} + +impl Translator for StrXOnlyKeyTranslator { + fn pk(&mut self, pk: &String) -> Result { + let key = self.pk_map.get(pk).copied().unwrap_or_else(|| { + bitcoin::XOnlyPublicKey::from_str( + "c2122e30e73f7fe37986e3f81ded00158e94b7ad472369b83bbdd28a9a198a39", + ) + .unwrap() + }); + Ok(key) + } + + fn pkh(&mut self, pkh: &String) -> Result { + let hash = self.pkh_map.get(pkh).copied().unwrap_or_else(|| { + hash160::Hash::from_str("be8f27af36217ba89c793c419f058cd4e2a54e26").unwrap() + }); + Ok(hash) + } + + fn sha256(&mut self, _sha256: &String) -> Result { + let hash = sha256::Hash::from_str( + "4ae81572f06e1b88fd5ced7a1a000945432e83e1551e6f721ee9c00b8cc33260", + ) + .unwrap(); + Ok(hash) + } +} + +// Deterministically sample keys to allow reproducible tests +fn random_sks(n: usize) -> Vec { + let mut sk = [0; 32]; + let mut sks = vec![]; + for i in 1..n + 1 { + sk[0] = i as u8; + sk[1] = (i >> 8) as u8; + sk[2] = (i >> 16) as u8; + sk[3] = (i >> 24) as u8; + + let sk = secp256k1::SecretKey::from_slice(&sk[..]).expect("secret key"); + sks.push(sk) + } + sks +} + +impl StrKeyTranslator { + pub fn new() -> Self { + let secp = secp256k1::Secp256k1::new(); + let sks = random_sks(26); + let pks: Vec<_> = sks + .iter() + .map(|sk| bitcoin::PublicKey::new(secp256k1::PublicKey::from_secret_key(&secp, sk))) + .collect(); + let mut pk_map = HashMap::new(); + let mut pkh_map = HashMap::new(); + for (i, c) in (b'A'..b'Z').enumerate() { + let key = String::from_utf8(vec![c]).unwrap(); + pk_map.insert(key.clone(), pks[i]); + pkh_map.insert(key, pks[i].to_pubkeyhash()); + } + // We don't bother filling in sha256_map preimages in default implementation as it not unnecessary + // for sane miniscripts + Self { + pk_map, + pkh_map, + sha256_map: HashMap::new(), + } + } +} + +impl StrXOnlyKeyTranslator { + pub fn new() -> Self { + let secp = secp256k1::Secp256k1::new(); + let sks = random_sks(26); + let pks: Vec<_> = sks + .iter() + .map(|sk| { + let keypair = secp256k1::KeyPair::from_secret_key(&secp, *sk); + bitcoin::XOnlyPublicKey::from_keypair(&keypair) + }) + .collect(); + let mut pk_map = HashMap::new(); + let mut pkh_map = HashMap::new(); + for (i, c) in (b'A'..b'Z').enumerate() { + let key = String::from_utf8(vec![c]).unwrap(); + pk_map.insert(key.clone(), pks[i]); + pkh_map.insert(key, pks[i].to_pubkeyhash()); + } + // We don't bother filling in sha256_map preimages in default implementation as it not unnecessary + // for sane miniscripts + Self { + pk_map, + pkh_map, + sha256_map: HashMap::new(), + } + } +} diff --git a/tests/setup/test_util.rs b/tests/setup/test_util.rs index 019b7c179..820d5c811 100644 --- a/tests/setup/test_util.rs +++ b/tests/setup/test_util.rs @@ -24,7 +24,9 @@ use bitcoin::hashes::hex::ToHex; use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d, Hash}; use bitcoin::secp256k1; use miniscript::descriptor::{SinglePub, SinglePubKey}; -use miniscript::{Descriptor, DescriptorPublicKey, Miniscript, ScriptContext, TranslatePk}; +use miniscript::{ + Descriptor, DescriptorPublicKey, Miniscript, ScriptContext, TranslatePk, Translator, +}; use rand::RngCore; #[derive(Clone, Debug)] @@ -153,127 +155,105 @@ pub fn parse_insane_ms( let ms = subs_hash_frag(ms, pubdata); let ms = Miniscript::::from_str_insane(&ms).expect("only parsing valid minsicripts"); - let mut i = 0; - let mut j = pubdata.pks.len(); - let ms = ms.translate_pk_infallible( - &mut |pk_str: &String| { - let avail = !pk_str.ends_with("!"); - if avail { - i = i + 1; - if pk_str.starts_with("K") { - DescriptorPublicKey::Single(SinglePub { - origin: None, - key: SinglePubKey::FullKey(pubdata.pks[i]), - }) - } else if pk_str.starts_with("X") { - DescriptorPublicKey::Single(SinglePub { - origin: None, - key: SinglePubKey::XOnly(pubdata.x_only_pks[i]), - }) - } else { - // Parse any other keys as known to allow compatibility with existing tests - DescriptorPublicKey::Single(SinglePub { - origin: None, - key: SinglePubKey::FullKey(pubdata.pks[i]), - }) - } - } else { - DescriptorPublicKey::Single(SinglePub { + let mut translator = StrTranslatorLoose(0, pubdata); + let ms = ms.translate_pk(&mut translator).unwrap(); + ms +} + +// Translate Str to DescriptorPublicKey +#[derive(Debug, Clone)] +struct StrDescPubKeyTranslator<'a>(usize, &'a PubData); + +impl<'a> Translator for StrDescPubKeyTranslator<'a> { + fn pk(&mut self, pk_str: &String) -> Result { + let avail = !pk_str.ends_with("!"); + if avail { + self.0 = self.0 + 1; + if pk_str.starts_with("K") { + Ok(DescriptorPublicKey::Single(SinglePub { origin: None, - key: SinglePubKey::FullKey(random_pk(59)), - }) - } - }, - &mut |pk_str: &String| { - let avail = !pk_str.ends_with("!"); - if avail { - j = j - 1; - if pk_str.starts_with("K") { - DescriptorPublicKey::Single(SinglePub { - origin: None, - key: SinglePubKey::FullKey(pubdata.pks[j]), - }) - } else if pk_str.starts_with("X") { - DescriptorPublicKey::Single(SinglePub { - origin: None, - key: SinglePubKey::XOnly(pubdata.x_only_pks[j]), - }) - } else { - // Parse any other keys as known to allow compatibility with existing tests - DescriptorPublicKey::Single(SinglePub { - origin: None, - key: SinglePubKey::FullKey(pubdata.pks[j]), - }) - } - } else { - DescriptorPublicKey::Single(SinglePub { + key: SinglePubKey::FullKey(self.1.pks[self.0]), + })) + } else if pk_str.starts_with("X") { + Ok(DescriptorPublicKey::Single(SinglePub { origin: None, - key: SinglePubKey::FullKey(random_pk(59)), - }) + key: SinglePubKey::XOnly(self.1.x_only_pks[self.0]), + })) + } else { + panic!("Key must start with either K or X") } - }, - ); - ms + } else { + Ok(DescriptorPublicKey::Single(SinglePub { + origin: None, + key: SinglePubKey::FullKey(random_pk(59)), + })) + } + } + + fn pkh(&mut self, pkh: &String) -> Result { + self.pk(pkh) + } + + fn sha256(&mut self, sha256: &String) -> Result { + let sha = sha256::Hash::from_str(sha256).unwrap(); + Ok(sha) + } } -#[allow(dead_code)] -// https://github.com/rust-lang/rust/issues/46379. The code is pub fn and integration test, but still shows warnings -pub fn parse_test_desc(desc: &str, pubdata: &PubData) -> Descriptor { - let desc = subs_hash_frag(desc, pubdata); - let desc = - Descriptor::::from_str(&desc).expect("only parsing valid and sane descriptors"); - let mut i = 0; - let mut j = pubdata.pks.len(); - let desc: Result<_, ()> = desc.translate_pk( - &mut |pk_str: &'_ String| { - let avail = !pk_str.ends_with("!"); - if avail { - i = i + 1; - if pk_str.starts_with("K") { - Ok(DescriptorPublicKey::Single(SinglePub { - origin: None, - key: SinglePubKey::FullKey(pubdata.pks[i]), - })) - } else if pk_str.starts_with("X") { - Ok(DescriptorPublicKey::Single(SinglePub { - origin: None, - key: SinglePubKey::XOnly(pubdata.x_only_pks[i]), - })) - } else { - panic!("Key must start with either K or X") - } - } else { +// Translate Str to DescriptorPublicKey +// Same as StrDescPubKeyTranslator, but does not panic when Key is not starting with +// K or X. This is used when testing vectors from C++ to rust +#[derive(Debug, Clone)] +struct StrTranslatorLoose<'a>(usize, &'a PubData); + +impl<'a> Translator for StrTranslatorLoose<'a> { + fn pk(&mut self, pk_str: &String) -> Result { + let avail = !pk_str.ends_with("!"); + if avail { + self.0 = self.0 + 1; + if pk_str.starts_with("K") { Ok(DescriptorPublicKey::Single(SinglePub { origin: None, - key: SinglePubKey::FullKey(random_pk(59)), + key: SinglePubKey::FullKey(self.1.pks[self.0]), + })) + } else if pk_str.starts_with("X") { + Ok(DescriptorPublicKey::Single(SinglePub { + origin: None, + key: SinglePubKey::XOnly(self.1.x_only_pks[self.0]), })) - } - }, - &mut |pkh_str: &'_ String| { - let avail = !pkh_str.ends_with("!"); - if avail { - j = j - 1; - if pkh_str.starts_with("K") { - Ok(DescriptorPublicKey::Single(SinglePub { - origin: None, - key: SinglePubKey::FullKey(pubdata.pks[j]), - })) - } else if pkh_str.starts_with("X") { - Ok(DescriptorPublicKey::Single(SinglePub { - origin: None, - key: SinglePubKey::XOnly(pubdata.x_only_pks[j]), - })) - } else { - panic!("Key must start with either K or X") - } } else { + // Parse any other keys as known to allow compatibility with existing tests Ok(DescriptorPublicKey::Single(SinglePub { origin: None, - key: SinglePubKey::FullKey(random_pk(61)), + key: SinglePubKey::FullKey(self.1.pks[self.0]), })) } - }, - ); + } else { + Ok(DescriptorPublicKey::Single(SinglePub { + origin: None, + key: SinglePubKey::FullKey(random_pk(59)), + })) + } + } + + fn pkh(&mut self, pkh: &String) -> Result { + self.pk(pkh) + } + + fn sha256(&mut self, sha256: &String) -> Result { + let sha = sha256::Hash::from_str(sha256).unwrap(); + Ok(sha) + } +} + +#[allow(dead_code)] +// https://github.com/rust-lang/rust/issues/46379. The code is pub fn and integration test, but still shows warnings +pub fn parse_test_desc(desc: &str, pubdata: &PubData) -> Descriptor { + let desc = subs_hash_frag(desc, pubdata); + let desc = + Descriptor::::from_str(&desc).expect("only parsing valid and sane descriptors"); + let mut translator = StrDescPubKeyTranslator(0, pubdata); + let desc: Result<_, ()> = desc.translate_pk(&mut translator); desc.expect("Translate must succeed") }