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 a1e32cdf8..0b3159ab6 100644 --- a/src/descriptor/bare.rs +++ b/src/descriptor/bare.rs @@ -31,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 @@ -180,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")) } } @@ -345,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 c7a85b3cb..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); - let mut keymap_pk = KeyMap::new(); - let mut keymap_pkh = KeyMap::new(); + 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) + } + + 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() @@ -731,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 = @@ -1468,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 225ae5412..070d23934 100644 --- a/src/descriptor/segwitv0.rs +++ b/src/descriptor/segwitv0.rs @@ -29,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)] @@ -265,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 }) } @@ -456,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 1d200e120..d9202e67e 100644 --- a/src/descriptor/sh.rs +++ b/src/descriptor/sh.rs @@ -32,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 @@ -398,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 b7edad6d0..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) } @@ -610,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/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 88f563c27..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 diff --git a/src/macros.rs b/src/macros.rs index 2a11bea74..e192f341e 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -28,8 +28,10 @@ macro_rules! impl_from_tree { 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,)* { @@ -54,8 +56,10 @@ macro_rules! impl_from_str { 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; @@ -78,10 +82,12 @@ macro_rules! impl_block_str { ) => { impl $name where - Pk: MiniscriptKey + std::str::FromStr, - Pk::Hash : std::str::FromStr, - ::Err: std::string::ToString, - <::Hash as std::str::FromStr>::Err: std::string::ToString, + 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])* @@ -101,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> @@ -120,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 6d9d2d741..932d5135e 100644 --- a/src/miniscript/astelem.rs +++ b/src/miniscript/astelem.rs @@ -24,7 +24,7 @@ use core::str::FromStr; 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(); @@ -488,7 +470,7 @@ impl_from_tree!( 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) @@ -647,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 2c07c2bab..08c7f75d6 100644 --- a/src/miniscript/mod.rs +++ b/src/miniscript/mod.rs @@ -53,7 +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; @@ -287,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) } } @@ -316,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 @@ -473,9 +462,8 @@ mod tests { use crate::miniscript::Terminal; use crate::policy::Liftable; use crate::prelude::*; - use crate::{ - hex_script, DummyKey, DummyKeyHash, Satisfier, ToPublicKey, TranslatePk, TranslatePk2, - }; + use crate::test_utils::{StrKeyTranslator, StrXOnlyKeyTranslator}; + use crate::{hex_script, DummyKey, DummyKeyHash, Satisfier, ToPublicKey, TranslatePk}; type Segwitv0Script = Miniscript; type Tapscript = Miniscript; @@ -1033,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(), @@ -1077,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 8691237f3..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), @@ -752,7 +780,7 @@ impl_block_str!( 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) 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 103abee41..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), @@ -312,7 +340,7 @@ impl_from_tree!( 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) diff --git a/src/psbt/mod.rs b/src/psbt/mod.rs index 9aff0c379..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; @@ -368,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) } @@ -930,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, @@ -938,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() { @@ -992,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, @@ -1020,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() { @@ -1036,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") }