diff --git a/src/descriptor/bare.rs b/src/descriptor/bare.rs index 65fa01a66..65f5e2b65 100644 --- a/src/descriptor/bare.rs +++ b/src/descriptor/bare.rs @@ -24,8 +24,11 @@ use bitcoin::blockdata::script; use bitcoin::{Address, Network, Script}; use super::checksum::{self, verify_checksum}; +use crate::descriptor::{DefiniteDescriptorKey, DescriptorType}; use crate::expression::{self, FromTree}; use crate::miniscript::context::ScriptContext; +use crate::miniscript::satisfy::{Placeholder, WitnessTemplate}; +use crate::plan::{AssetProvider, Plan}; use crate::policy::{semantic, Liftable}; use crate::prelude::*; use crate::util::{varint_len, witness_to_scriptsig}; @@ -124,6 +127,28 @@ impl Bare { } } +impl Bare { + /// Returns a plan if the provided assets are sufficient to produce a non-malleable satisfaction + pub fn get_plan

(&self, provider: &P) -> Option + where + P: AssetProvider, + { + self.ms + .build_template(provider) + .into_plan(DescriptorType::Bare) + } + + /// Returns a plan if the provided assets are sufficient to produce a malleable satisfaction + pub fn get_plan_mall

(&self, provider: &P) -> Option + where + P: AssetProvider, + { + self.ms + .build_template_mall(provider) + .into_plan(DescriptorType::Bare) + } +} + impl fmt::Debug for Bare { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}", self.ms) @@ -278,6 +303,37 @@ impl Pkh { } } +impl Pkh { + /// Returns a plan if the provided assets are sufficient to produce a non-malleable satisfaction + pub fn get_plan

(&self, provider: &P) -> Option + where + P: AssetProvider, + { + if provider.lookup_ecdsa_sig(&self.pk) { + let stack = vec![ + Placeholder::EcdsaSigPk(self.pk.clone()), + Placeholder::Pubkey(self.pk.clone(), BareCtx::pk_len(&self.pk)), + ]; + Some(Plan { + relative_timelock: None, + absolute_timelock: None, + desc_type: DescriptorType::Pkh, + template: WitnessTemplate::from_placeholder_stack(stack), + }) + } else { + None + } + } + + /// Returns a plan if the provided assets are sufficient to produce a malleable satisfaction + pub fn get_plan_mall

(&self, provider: &P) -> Option + where + P: AssetProvider, + { + self.get_plan(provider) + } +} + impl fmt::Debug for Pkh { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "pkh({:?})", self.pk) diff --git a/src/descriptor/key.rs b/src/descriptor/key.rs index 6af0146e0..5cf1e6be2 100644 --- a/src/descriptor/key.rs +++ b/src/descriptor/key.rs @@ -72,7 +72,7 @@ pub enum SinglePubKey { /// A [`DescriptorPublicKey`] without any wildcards. #[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash)] -pub struct DefiniteDescriptorKey(DescriptorPublicKey); +pub struct DefiniteDescriptorKey(pub(super) DescriptorPublicKey); impl fmt::Display for DescriptorSecretKey { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { diff --git a/src/descriptor/mod.rs b/src/descriptor/mod.rs index 31a30b2e3..cb5caa6a2 100644 --- a/src/descriptor/mod.rs +++ b/src/descriptor/mod.rs @@ -35,6 +35,7 @@ use sync::Arc; use self::checksum::verify_checksum; use crate::miniscript::{Legacy, Miniscript, Segwitv0}; +use crate::plan::{AssetProvider, Assets, IntoAssets, Plan}; use crate::prelude::*; use crate::{ expression, hash256, miniscript, BareCtx, Error, ForEachKey, MiniscriptKey, Satisfier, @@ -435,7 +436,7 @@ impl Descriptor { Descriptor::Wpkh(ref wpkh) => wpkh.get_satisfaction(satisfier), Descriptor::Wsh(ref wsh) => wsh.get_satisfaction(satisfier), Descriptor::Sh(ref sh) => sh.get_satisfaction(satisfier), - Descriptor::Tr(ref tr) => tr.get_satisfaction(satisfier), + Descriptor::Tr(ref tr) => tr.get_satisfaction(&satisfier), } } @@ -452,7 +453,7 @@ impl Descriptor { Descriptor::Wpkh(ref wpkh) => wpkh.get_satisfaction_mall(satisfier), Descriptor::Wsh(ref wsh) => wsh.get_satisfaction_mall(satisfier), Descriptor::Sh(ref sh) => sh.get_satisfaction_mall(satisfier), - Descriptor::Tr(ref tr) => tr.get_satisfaction_mall(satisfier), + Descriptor::Tr(ref tr) => tr.get_satisfaction_mall(&satisfier), } } @@ -470,6 +471,38 @@ impl Descriptor { } } +impl Descriptor { + /// Returns a plan if the provided assets are sufficient to produce a non-malleable satisfaction + pub fn get_plan

(&self, provider: &P) -> Option + where + P: AssetProvider, + { + match *self { + Descriptor::Bare(ref bare) => bare.get_plan(provider), + Descriptor::Pkh(ref pkh) => pkh.get_plan(provider), + Descriptor::Wpkh(ref wpkh) => wpkh.get_plan(provider), + Descriptor::Wsh(ref wsh) => wsh.get_plan(provider), + Descriptor::Sh(ref sh) => sh.get_plan(provider), + Descriptor::Tr(ref tr) => tr.get_plan(provider), + } + } + + /// Returns a plan if the provided assets are sufficient to produce a malleable satisfaction + pub fn get_plan_mall

(&self, provider: &P) -> Option + where + P: AssetProvider, + { + match *self { + Descriptor::Bare(ref bare) => bare.get_plan_mall(provider), + Descriptor::Pkh(ref pkh) => pkh.get_plan_mall(provider), + Descriptor::Wpkh(ref wpkh) => wpkh.get_plan_mall(provider), + Descriptor::Wsh(ref wsh) => wsh.get_plan_mall(provider), + Descriptor::Sh(ref sh) => sh.get_plan_mall(provider), + Descriptor::Tr(ref tr) => tr.get_plan_mall(provider), + } + } +} + impl TranslatePk for Descriptor

where P: MiniscriptKey, @@ -771,6 +804,46 @@ impl Descriptor { let derived = self.translate_pk(&mut Derivator(secp))?; Ok(derived) } + + /// Returns the set of keys which are available in the [`KeyMap`] in form of [`Assets`] + pub fn available_keys(&self, key_map: &KeyMap) -> Assets { + let mut available_keys = vec![]; + + self.for_each_key(|pk| { + let found = match &pk.0 { + s @ DescriptorPublicKey::Single(_) => key_map.contains_key(&s), + DescriptorPublicKey::XPub(xkey) => { + if key_map.contains_key(&DescriptorPublicKey::XPub(xkey.clone())) { + true + } else if xkey.derivation_path.len() > 0 { + let unwind_wildcard = DescriptorXKey { + origin: xkey.origin.clone(), + xkey: xkey.xkey, + wildcard: Wildcard::Unhardened, + derivation_path: xkey + .derivation_path + .into_iter() + .take(xkey.derivation_path.len() - 1) + .cloned() + .collect::>() + .into(), + }; + key_map.contains_key(&DescriptorPublicKey::XPub(unwind_wildcard)) + } else { + false + } + } + }; + + if found { + available_keys.push(pk.clone()); + } + + true + }); + + available_keys.into_assets() + } } impl_from_tree!( diff --git a/src/descriptor/segwitv0.rs b/src/descriptor/segwitv0.rs index 28d65b6f8..5e9445d34 100644 --- a/src/descriptor/segwitv0.rs +++ b/src/descriptor/segwitv0.rs @@ -22,8 +22,11 @@ use bitcoin::{self, Address, Network, Script}; use super::checksum::{self, verify_checksum}; use super::SortedMultiVec; +use crate::descriptor::{DefiniteDescriptorKey, DescriptorType}; use crate::expression::{self, FromTree}; use crate::miniscript::context::{ScriptContext, ScriptContextError}; +use crate::miniscript::satisfy::{Placeholder, WitnessTemplate}; +use crate::plan::{AssetProvider, Plan}; use crate::policy::{semantic, Liftable}; use crate::prelude::*; use crate::util::varint_len; @@ -173,6 +176,32 @@ impl Wsh { } } +impl Wsh { + /// Returns a plan if the provided assets are sufficient to produce a non-malleable satisfaction + pub fn get_plan

(&self, provider: &P) -> Option + where + P: AssetProvider, + { + match &self.inner { + WshInner::SortedMulti(sm) => sm.build_template(provider).into_plan(DescriptorType::Wsh), + WshInner::Ms(ms) => ms.build_template(provider).into_plan(DescriptorType::Wsh), + } + } + + /// Returns a plan if the provided assets are sufficient to produce a malleable satisfaction + pub fn get_plan_mall

(&self, provider: &P) -> Option + where + P: AssetProvider, + { + match &self.inner { + WshInner::SortedMulti(sm) => sm.build_template(provider).into_plan(DescriptorType::Wsh), + WshInner::Ms(ms) => ms + .build_template_mall(provider) + .into_plan(DescriptorType::Wsh), + } + } +} + /// Wsh Inner #[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] pub enum WshInner { @@ -393,6 +422,37 @@ impl Wpkh { } } +impl Wpkh { + /// Returns a plan if the provided assets are sufficient to produce a non-malleable satisfaction + pub fn get_plan

(&self, provider: &P) -> Option + where + P: AssetProvider, + { + if provider.lookup_ecdsa_sig(&self.pk) { + let stack = vec![ + Placeholder::EcdsaSigPk(self.pk.clone()), + Placeholder::Pubkey(self.pk.clone(), Segwitv0::pk_len(&self.pk)), + ]; + Some(Plan { + relative_timelock: None, + absolute_timelock: None, + desc_type: DescriptorType::Wpkh, + template: WitnessTemplate::from_placeholder_stack(stack), + }) + } else { + None + } + } + + /// Returns a plan if the provided assets are sufficient to produce a malleable satisfaction + pub fn get_plan_mall

(&self, provider: &P) -> Option + where + P: AssetProvider, + { + self.get_plan(provider) + } +} + impl fmt::Debug for Wpkh { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "wpkh({:?})", self.pk) diff --git a/src/descriptor/sh.rs b/src/descriptor/sh.rs index fd1f29d6f..c3a48284b 100644 --- a/src/descriptor/sh.rs +++ b/src/descriptor/sh.rs @@ -25,8 +25,10 @@ use bitcoin::{Address, Network, Script}; use super::checksum::{self, verify_checksum}; use super::{SortedMultiVec, Wpkh, Wsh}; +use crate::descriptor::{DefiniteDescriptorKey, DescriptorType}; use crate::expression::{self, FromTree}; use crate::miniscript::context::ScriptContext; +use crate::plan::{AssetProvider, Plan}; use crate::policy::{semantic, Liftable}; use crate::prelude::*; use crate::util::{varint_len, witness_to_scriptsig}; @@ -377,6 +379,44 @@ impl Sh { } } +impl Sh { + /// Returns a plan if the provided assets are sufficient to produce a non-malleable satisfaction + pub fn get_plan

(&self, provider: &P) -> Option + where + P: AssetProvider, + { + match &self.inner { + ShInner::Wsh(ref wsh) => wsh.get_plan(provider).map(|mut plan| { + plan.desc_type = DescriptorType::ShWsh; + plan + }), + ShInner::Wpkh(ref wpkh) => wpkh.get_plan(provider).map(|mut plan| { + plan.desc_type = DescriptorType::ShWpkh; + plan + }), + ShInner::SortedMulti(ref _smv) => todo!(), + ShInner::Ms(ref ms) => ms.build_template(provider).into_plan(DescriptorType::Sh), + } + } + + /// Returns a plan if the provided assets are sufficient to produce a malleable satisfaction + pub fn get_plan_mall

(&self, provider: &P) -> Option + where + P: AssetProvider, + { + match &self.inner { + ShInner::Wsh(ref wsh) => wsh.get_plan_mall(provider).map(|mut plan| { + plan.desc_type = DescriptorType::ShWsh; + plan + }), + ShInner::Ms(ref ms) => ms + .build_template_mall(provider) + .into_plan(DescriptorType::Sh), + _ => self.get_plan(provider), + } + } +} + impl ForEachKey for Sh { fn for_each_key<'a, F: FnMut(&'a Pk) -> bool>(&'a self, pred: F) -> bool where diff --git a/src/descriptor/sortedmulti.rs b/src/descriptor/sortedmulti.rs index 7857188b0..8c105894d 100644 --- a/src/descriptor/sortedmulti.rs +++ b/src/descriptor/sortedmulti.rs @@ -25,6 +25,8 @@ use bitcoin::blockdata::script; use crate::miniscript::context::ScriptContext; use crate::miniscript::decode::Terminal; use crate::miniscript::limits::MAX_PUBKEYS_PER_MULTISIG; +use crate::miniscript::satisfy::{Placeholder, Satisfaction}; +use crate::plan::AssetProvider; use crate::prelude::*; use crate::{ errstr, expression, miniscript, policy, script_num_size, Error, ForEachKey, Miniscript, @@ -171,6 +173,16 @@ impl SortedMultiVec { ms.satisfy(satisfier) } + /// Attempt to produce a witness template given the assets available + pub fn build_template

(&self, provider: &P) -> Satisfaction> + where + Pk: ToPublicKey, + P: AssetProvider, + { + let ms = Miniscript::from_ast(self.sorted_node()).expect("Multi node typecheck"); + ms.build_template(provider) + } + /// Size, in bytes of the script-pubkey. If this Miniscript is used outside /// of segwit (e.g. in a bare or P2SH descriptor), this quantity should be /// multiplied by 4 to compute the weight. diff --git a/src/descriptor/tr.rs b/src/descriptor/tr.rs index 3607461b9..d00563dda 100644 --- a/src/descriptor/tr.rs +++ b/src/descriptor/tr.rs @@ -12,8 +12,11 @@ use bitcoin::{secp256k1, Address, Network, Script}; use sync::Arc; use super::checksum::{self, verify_checksum}; +use crate::descriptor::{DefiniteDescriptorKey, DescriptorType}; use crate::expression::{self, FromTree}; +use crate::miniscript::satisfy::{Placeholder, Satisfaction, Witness, WitnessTemplate}; use crate::miniscript::Miniscript; +use crate::plan::{AssetProvider, Plan}; use crate::policy::semantic::Policy; use crate::policy::Liftable; use crate::prelude::*; @@ -310,21 +313,59 @@ impl Tr { /// Returns satisfying non-malleable witness and scriptSig with minimum /// weight to spend an output controlled by the given descriptor if it is /// possible to construct one using the `satisfier`. - pub fn get_satisfaction(&self, satisfier: S) -> Result<(Vec>, Script), Error> + pub fn get_satisfaction(&self, satisfier: &S) -> Result<(Vec>, Script), Error> where S: Satisfier, { - best_tap_spend(self, satisfier, false /* allow_mall */) + let satisfaction = + best_tap_spend(self, satisfier, false /* allow_mall */).map_stack(|stack| { + WitnessTemplate::from_placeholder_stack(stack) + .try_completing(satisfier) + .expect("the same satisfier should manage to complete the template") + }); + if let Witness::Stack(stack) = satisfaction.stack { + Ok((stack, Script::new())) + } else { + Err(Error::CouldNotSatisfy) + } } /// Returns satisfying, possibly malleable, witness and scriptSig with /// minimum weight to spend an output controlled by the given descriptor if /// it is possible to construct one using the `satisfier`. - pub fn get_satisfaction_mall(&self, satisfier: S) -> Result<(Vec>, Script), Error> + pub fn get_satisfaction_mall(&self, satisfier: &S) -> Result<(Vec>, Script), Error> where S: Satisfier, { - best_tap_spend(self, satisfier, true /* allow_mall */) + let satisfaction = + best_tap_spend(self, satisfier, true /* allow_mall */).map_stack(|stack| { + WitnessTemplate::from_placeholder_stack(stack) + .try_completing(satisfier) + .expect("the same satisfier should manage to complete the template") + }); + if let Witness::Stack(stack) = satisfaction.stack { + Ok((stack, Script::new())) + } else { + Err(Error::CouldNotSatisfy) + } + } +} + +impl Tr { + /// Returns a plan if the provided assets are sufficient to produce a non-malleable satisfaction + pub fn get_plan

(&self, provider: &P) -> Option + where + P: AssetProvider, + { + best_tap_spend(self, provider, false /* allow_mall */).into_plan(DescriptorType::Tr) + } + + /// Returns a plan if the provided assets are sufficient to produce a malleable satisfaction + pub fn get_plan_mall

(&self, provider: &P) -> Option + where + P: AssetProvider, + { + best_tap_spend(self, provider, true /* allow_mall */).into_plan(DescriptorType::Tr) } } @@ -604,62 +645,81 @@ fn control_block_len(depth: u8) -> usize { // Helper function to get a script spend satisfaction // try script spend -fn best_tap_spend( +fn best_tap_spend( desc: &Tr, - satisfier: S, + provider: &P, allow_mall: bool, -) -> Result<(Vec>, Script), Error> +) -> Satisfaction> where Pk: ToPublicKey, - S: Satisfier, + P: AssetProvider, { let spend_info = desc.spend_info(); // First try the key spend path - if let Some(sig) = satisfier.lookup_tap_key_spend_sig() { - Ok((vec![sig.to_vec()], Script::new())) + if provider.lookup_tap_key_spend_sig(&desc.internal_key) { + Satisfaction { + stack: Witness::Stack(vec![Placeholder::SchnorrSig( + desc.internal_key.clone(), + None, + )]), + has_sig: true, + absolute_timelock: None, + relative_timelock: None, + } } else { // Since we have the complete descriptor we can ignore the satisfier. We don't use the control block // map (lookup_control_block) from the satisfier here. - let (mut min_wit, mut min_wit_len) = (None, None); - for (depth, ms) in desc.iter_scripts() { - let mut wit = if allow_mall { - match ms.satisfy_malleable(&satisfier) { - Ok(wit) => wit, - Err(..) => continue, // No witness for this script in tr descriptor, look for next one + let mut min_satisfaction = Satisfaction { + stack: Witness::Unavailable, + has_sig: false, + relative_timelock: None, + absolute_timelock: None, + }; + let mut min_wit_len = None; + for (_depth, ms) in desc.iter_scripts() { + let mut satisfaction = if allow_mall { + match ms.build_template(provider) { + s @ Satisfaction { + stack: Witness::Stack(_), + .. + } => s, + _ => continue, // No witness for this script in tr descriptor, look for next one } } else { - match ms.satisfy(&satisfier) { - Ok(wit) => wit, - Err(..) => continue, // No witness for this script in tr descriptor, look for next one + match ms.build_template_mall(provider) { + s @ Satisfaction { + stack: Witness::Stack(_), + .. + } => s, + _ => continue, // No witness for this script in tr descriptor, look for next one } }; - // Compute the final witness size - // Control block len + script len + witnesssize + varint(wit.len + 2) - // The extra +2 elements are control block and script itself - let wit_size = witness_size(&wit) - + control_block_len(depth) - + ms.script_size() - + varint_len(ms.script_size()); + let wit = match satisfaction { + Satisfaction { + stack: Witness::Stack(ref mut wit), + .. + } => wit, + _ => unreachable!(), + }; + + let leaf_script = (ms.encode(), LeafVersion::TapScript); + let control_block = spend_info + .control_block(&leaf_script) + .expect("Control block must exist in script map for every known leaf"); + + wit.push(Placeholder::TapScript(leaf_script.0)); + wit.push(Placeholder::TapControlBlock(control_block)); + + let wit_size = witness_size(&wit); if min_wit_len.is_some() && Some(wit_size) > min_wit_len { continue; } else { - let leaf_script = (ms.encode(), LeafVersion::TapScript); - let control_block = spend_info - .control_block(&leaf_script) - .expect("Control block must exist in script map for every known leaf"); - wit.push(leaf_script.0.into_bytes()); // Push the leaf script - // There can be multiple control blocks for a (script, ver) pair - // Find the smallest one amongst those - wit.push(control_block.serialize()); - // Finally, save the minimum - min_wit = Some(wit); + min_satisfaction = satisfaction; min_wit_len = Some(wit_size); } } - match min_wit { - Some(wit) => Ok((wit, Script::new())), - None => Err(Error::CouldNotSatisfy), // Could not satisfy all miniscripts inside Tr - } + + min_satisfaction } } diff --git a/src/lib.rs b/src/lib.rs index 713d66eb9..46823e6f1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -124,6 +124,7 @@ pub mod descriptor; pub mod expression; pub mod interpreter; pub mod miniscript; +pub mod plan; pub mod policy; pub mod psbt; diff --git a/src/miniscript/mod.rs b/src/miniscript/mod.rs index 7c864adf2..e4c35b032 100644 --- a/src/miniscript/mod.rs +++ b/src/miniscript/mod.rs @@ -55,7 +55,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, ForEachKey, MiniscriptKey, ToPublicKey, TranslatePk, Translator}; +use crate::{ + expression, plan, Error, ForEachKey, MiniscriptKey, ToPublicKey, TranslatePk, Translator, +}; #[cfg(test)] mod ms_tests; @@ -429,6 +431,35 @@ impl Miniscript { } } } + + /// Attempt to produce a non-malleable witness template given the assets available + pub fn build_template>( + &self, + provider: &P, + ) -> satisfy::Satisfaction> + where + Pk: ToPublicKey, + { + let leaf_hash = TapLeafHash::from_script(&self.encode(), LeafVersion::TapScript); + satisfy::Satisfaction::build_template(&self.node, provider, self.ty.mall.safe, &leaf_hash) + } + + /// Attempt to produce a malleable witness template given the assets available + pub fn build_template_mall>( + &self, + provider: &P, + ) -> satisfy::Satisfaction> + where + Pk: ToPublicKey, + { + let leaf_hash = TapLeafHash::from_script(&self.encode(), LeafVersion::TapScript); + satisfy::Satisfaction::build_template_mall( + &self.node, + provider, + self.ty.mall.safe, + &leaf_hash, + ) + } } impl_from_tree!( diff --git a/src/miniscript/satisfy.rs b/src/miniscript/satisfy.rs index 116e70736..c85ab23b9 100644 --- a/src/miniscript/satisfy.rs +++ b/src/miniscript/satisfy.rs @@ -18,18 +18,22 @@ //! scriptpubkeys. //! -use core::{cmp, i64, mem}; +use core::{cmp, fmt, i64, mem}; use bitcoin::hashes::hash160; use bitcoin::secp256k1::XOnlyPublicKey; use bitcoin::util::taproot::{ControlBlock, LeafVersion, TapLeafHash}; -use bitcoin::{LockTime, PackedLockTime, Sequence}; +use bitcoin::{LockTime, PackedLockTime, Script, Sequence}; use sync::Arc; use super::context::SigType; +use crate::descriptor::DescriptorType; +use crate::plan::{AssetProvider, Plan}; use crate::prelude::*; use crate::util::witness_size; -use crate::{Miniscript, MiniscriptKey, ScriptContext, Terminal, ToPublicKey}; +use crate::{ + DefiniteDescriptorKey, Miniscript, MiniscriptKey, ScriptContext, Terminal, ToPublicKey, +}; /// Type alias for 32 byte Preimage. pub type Preimage32 = [u8; 32]; @@ -500,11 +504,115 @@ impl_tuple_satisfier!(A, B, C, D, E, F); impl_tuple_satisfier!(A, B, C, D, E, F, G); impl_tuple_satisfier!(A, B, C, D, E, F, G, H); +#[derive(Debug, Clone, PartialEq, Eq)] +/// Placeholder for some data in a [`WitnessTemplate`] +pub enum Placeholder { + /// Public key and its size + Pubkey(Pk, usize), + /// Public key hash and its size + PubkeyHash(hash160::Hash, usize), + /// ECDSA signature given the raw pubkey + EcdsaSigPk(Pk), + /// ECDSA signature given the pubkey hash + EcdsaSigHash(hash160::Hash), + /// Schnorr signature + SchnorrSig(Pk, Option), + /// SHA-256 preimage + Sha256Preimage(Pk::Sha256), + /// HASH256 preimage + Hash256Preimage(Pk::Hash256), + /// RIPEMD160 preimage + Ripemd160Preimage(Pk::Ripemd160), + /// HASH160 preimage + Hash160Preimage(Pk::Hash160), + /// Hash dissatisfaction (32 bytes of 0x00) + HashDissatisfaction, + /// OP_1 + PushOne, + /// + PushZero, + + /// Taproot leaf script + TapScript(Script), + /// Taproot control block + TapControlBlock(ControlBlock), +} + +impl fmt::Display for Placeholder { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use bitcoin::hashes::hex::ToHex; + use Placeholder::*; + match self { + Pubkey(pk, size) => write!(f, "Pubkey(pk: {}, size: {})", pk, size), + PubkeyHash(pkh, size) => write!(f, "PubkeyHash(pkh: {}, size: {})", pkh, size), + EcdsaSigPk(pk) => write!(f, "EcdsaSigPk(pk: {})", pk), + EcdsaSigHash(hash) => write!(f, "EcdsaSigHash(hash: {})", hash), + SchnorrSig(pk, tap_leaf_hash) => write!( + f, + "SchnorrSig(pk: {}, tap_leaf_hash: {:?})", + pk, tap_leaf_hash + ), + Sha256Preimage(hash) => write!(f, "Sha256Preimage(hash: {})", hash), + Hash256Preimage(hash) => write!(f, "Hash256Preimage(hash: {})", hash), + Ripemd160Preimage(hash) => write!(f, "Ripemd160Preimage(hash: {})", hash), + Hash160Preimage(hash) => write!(f, "Hash160Preimage(hash: {})", hash), + HashDissatisfaction => write!(f, "HashDissatisfaction"), + PushOne => write!(f, "PushOne"), + PushZero => write!(f, "PushZero"), + TapScript(script) => write!(f, "TapScript(script: {})", script), + TapControlBlock(control_block) => write!( + f, + "TapControlBlock(control_block: {})", + control_block.serialize().to_hex() + ), + } + } +} + +impl Placeholder { + /// Replaces the placeholders with the information given by the satisfier + fn satisfy_self>(&self, sat: &Sat) -> Option> { + match self { + Placeholder::Pubkey(pk, _) => Some(pk.to_public_key().to_bytes()), + // Placeholder::PubkeyHash might be created after a call to lookup_raw_pkh_pk (in + // pkh_public_key) or a call to lookup_raw_pkh_ecdsa_sig (in pkh_signature). + // For this reason, here we have to call both methods to find the key. + Placeholder::PubkeyHash(pkh, size) => sat + .lookup_raw_pkh_pk(pkh) + .map(|p| p.to_public_key()) + .or(sat.lookup_raw_pkh_ecdsa_sig(pkh).map(|(p, _)| p)) + .map(|pk| { + let pk = pk.to_bytes(); + // We have to add a 1-byte OP_PUSH + debug_assert!(1 + pk.len() == *size); + pk + }), + Placeholder::Hash256Preimage(h) => sat.lookup_hash256(h).map(|p| p.to_vec()), + Placeholder::Sha256Preimage(h) => sat.lookup_sha256(h).map(|p| p.to_vec()), + Placeholder::Hash160Preimage(h) => sat.lookup_hash160(h).map(|p| p.to_vec()), + Placeholder::Ripemd160Preimage(h) => sat.lookup_ripemd160(h).map(|p| p.to_vec()), + Placeholder::EcdsaSigPk(pk) => sat.lookup_ecdsa_sig(pk).map(|s| s.to_vec()), + Placeholder::EcdsaSigHash(pkh) => { + sat.lookup_raw_pkh_ecdsa_sig(pkh).map(|(_, s)| s.to_vec()) + } + Placeholder::SchnorrSig(pk, Some(leaf_hash)) => sat + .lookup_tap_leaf_script_sig(pk, leaf_hash) + .map(|s| s.to_vec()), + Placeholder::SchnorrSig(_, _) => sat.lookup_tap_key_spend_sig().map(|s| s.to_vec()), + Placeholder::HashDissatisfaction => Some(vec![0; 32]), + Placeholder::PushZero => Some(vec![]), + Placeholder::PushOne => Some(vec![1]), + Placeholder::TapScript(s) => Some(s.to_bytes()), + Placeholder::TapControlBlock(cb) => Some(cb.serialize()), + } + } +} + /// A witness, if available, for a Miniscript fragment #[derive(Clone, PartialEq, Eq, Debug)] -pub enum Witness { +pub enum Witness { /// Witness Available and the value of the witness - Stack(Vec>), + Stack(Vec), /// Third party can possibly satisfy the fragment but we cannot /// Witness Unavailable Unavailable, @@ -513,13 +621,128 @@ pub enum Witness { Impossible, } -impl PartialOrd for Witness { +/// Enum for partially satisfied witness templates +pub enum PartialSatisfaction { + /// Placeholder item (not yet satisfied) + Placeholder(Placeholder), + /// Actual data + Data(Vec), +} + +impl PartialSatisfaction { + /// Whether the item is a placeholder + pub fn is_placeholder(&self) -> bool { + match &self { + PartialSatisfaction::Placeholder(_) => true, + _ => false, + } + } + + /// Whether the item is data + pub fn is_data(&self) -> bool { + !self.is_placeholder() + } +} + +/// Template of a witness being constructed interactively +/// +/// The generic `I` type determines the available API: +/// - `Placeholder` indicates the witness only contains placeholders, i.e. it's just an empty +/// template +/// - `PartialSatisfaction` indicates the witness contains some placeholders and some actual +/// pieces of data +#[derive(Debug, Clone)] +pub struct WitnessTemplate { + stack: Vec, +} + +impl AsRef<[I]> for WitnessTemplate { + fn as_ref(&self) -> &[I] { + &self.stack + } +} + +impl WitnessTemplate> { + /// Construct an instance from a stack of placeholders + pub fn from_placeholder_stack(stack: Vec>) -> Self { + WitnessTemplate { stack } + } + + /// Try completing the witness in one go using a [`Satisfier`] + pub fn try_completing>(&self, stfr: &Sat) -> Option>> { + let stack = self + .stack + .iter() + .map(|placeholder| placeholder.satisfy_self(stfr)) + .collect::>()?; + + Some(stack) + } + + /// Being an interactive satisfaction session + pub fn interactive_satisfaction(self) -> WitnessTemplate> { + WitnessTemplate { + stack: self + .stack + .into_iter() + .map(PartialSatisfaction::Placeholder) + .collect(), + } + } +} + +impl WitnessTemplate> { + /// Apply the items needed from a satisfier + /// + /// Returns the completed witness if all the placeholders have been filled, or `Err` with itself a list of missing + /// items otherwise. + pub fn apply>( + self, + stfr: &Sat, + ) -> Result>, (Self, Vec>)> { + let mut unsatisfied = vec![]; + + let stack = self + .stack + .into_iter() + .map(|ps| { + let placeholder = match &ps { + PartialSatisfaction::Placeholder(p) => p, + PartialSatisfaction::Data(_) => return ps, + }; + + if let Some(data) = placeholder.satisfy_self(stfr) { + return PartialSatisfaction::Data(data); + } + + unsatisfied.push(placeholder.clone()); + ps + }) + .collect::>(); + + if unsatisfied.is_empty() { + Ok(stack + .into_iter() + .map(|ps| match ps { + PartialSatisfaction::Data(d) => d, + PartialSatisfaction::Placeholder(_) => { + unreachable!("there shouldn't be any placeholder left") + } + }) + .collect()) + } else { + Err((WitnessTemplate { stack }, unsatisfied)) + } + } +} + +impl PartialOrd for Witness> { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl Ord for Witness { +impl Ord for Witness> { fn cmp(&self, other: &Self) -> cmp::Ordering { match (self, other) { (&Witness::Stack(ref v1), &Witness::Stack(ref v2)) => { @@ -537,87 +760,110 @@ impl Ord for Witness { } } -impl Witness { +impl Witness> { /// Turn a signature into (part of) a satisfaction - fn signature, Ctx: ScriptContext>( - sat: S, + fn signature, Ctx: ScriptContext>( + sat: &S, pk: &Pk, leaf_hash: &TapLeafHash, ) -> Self { match Ctx::sig_type() { - super::context::SigType::Ecdsa => match sat.lookup_ecdsa_sig(pk) { - Some(sig) => Witness::Stack(vec![sig.to_vec()]), - // Signatures cannot be forged - None => Witness::Impossible, - }, - super::context::SigType::Schnorr => match sat.lookup_tap_leaf_script_sig(pk, leaf_hash) - { - Some(sig) => Witness::Stack(vec![sig.to_vec()]), - // Signatures cannot be forged - None => Witness::Impossible, - }, + super::context::SigType::Ecdsa => { + if sat.lookup_ecdsa_sig(pk) { + Witness::Stack(vec![Placeholder::EcdsaSigPk(pk.clone())]) + } else { + // Signatures cannot be forged + Witness::Impossible + } + } + super::context::SigType::Schnorr => { + if sat.lookup_tap_leaf_script_sig(pk, leaf_hash) { + Witness::Stack(vec![Placeholder::SchnorrSig( + pk.clone(), + Some(leaf_hash.clone()), + )]) + } else { + // Signatures cannot be forged + Witness::Impossible + } + } } } /// Turn a public key related to a pkh into (part of) a satisfaction - fn pkh_public_key>(sat: S, pkh: &hash160::Hash) -> Self { - match sat.lookup_raw_pkh_pk(pkh) { - Some(pk) => Witness::Stack(vec![pk.to_public_key().to_bytes()]), + fn pkh_public_key, Ctx: ScriptContext>( + sat: &S, + pkh: &hash160::Hash, + ) -> Self { + if let Some(pk_len) = sat.lookup_raw_pkh_pk::(pkh) { + Witness::Stack(vec![Placeholder::PubkeyHash(pkh.clone(), pk_len)]) + } else { // public key hashes are assumed to be unavailable // instead of impossible since it is the same as pub-key hashes - None => Witness::Unavailable, + Witness::Unavailable } } /// Turn a key/signature pair related to a pkh into (part of) a satisfaction - fn pkh_signature>(sat: S, pkh: &hash160::Hash) -> Self { - match sat.lookup_raw_pkh_ecdsa_sig(pkh) { - Some((pk, sig)) => Witness::Stack(vec![sig.to_vec(), pk.to_public_key().to_bytes()]), - None => Witness::Impossible, + fn pkh_signature, Ctx: ScriptContext>( + sat: &S, + pkh: &hash160::Hash, + ) -> Self { + if let Some(pk_len) = sat.lookup_raw_pkh_ecdsa_sig::(pkh) { + Witness::Stack(vec![ + Placeholder::EcdsaSigHash(pkh.clone()), + Placeholder::PubkeyHash(pkh.clone(), pk_len), + ]) + } else { + Witness::Impossible } } /// Turn a hash preimage into (part of) a satisfaction - fn ripemd160_preimage>(sat: S, h: &Pk::Ripemd160) -> Self { - match sat.lookup_ripemd160(h) { - Some(pre) => Witness::Stack(vec![pre.to_vec()]), - // Note hash preimages are unavailable instead of impossible - None => Witness::Unavailable, + fn ripemd160_preimage>(sat: &S, h: &Pk::Ripemd160) -> Self { + if sat.lookup_ripemd160(h) { + Witness::Stack(vec![Placeholder::Ripemd160Preimage(h.clone())]) + // Note hash preimages are unavailable instead of impossible + } else { + Witness::Unavailable } } /// Turn a hash preimage into (part of) a satisfaction - fn hash160_preimage>(sat: S, h: &Pk::Hash160) -> Self { - match sat.lookup_hash160(h) { - Some(pre) => Witness::Stack(vec![pre.to_vec()]), - // Note hash preimages are unavailable instead of impossible - None => Witness::Unavailable, + fn hash160_preimage>(sat: &S, h: &Pk::Hash160) -> Self { + if sat.lookup_hash160(h) { + Witness::Stack(vec![Placeholder::Hash160Preimage(h.clone())]) + // Note hash preimages are unavailable instead of impossible + } else { + Witness::Unavailable } } /// Turn a hash preimage into (part of) a satisfaction - 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 - None => Witness::Unavailable, + fn sha256_preimage>(sat: &S, h: &Pk::Sha256) -> Self { + if sat.lookup_sha256(h) { + Witness::Stack(vec![Placeholder::Sha256Preimage(h.clone())]) + // Note hash preimages are unavailable instead of impossible + } else { + Witness::Unavailable } } /// Turn a hash preimage into (part of) a satisfaction - fn hash256_preimage>(sat: S, h: &Pk::Hash256) -> Self { - match sat.lookup_hash256(h) { - Some(pre) => Witness::Stack(vec![pre.to_vec()]), - // Note hash preimages are unavailable instead of impossible - None => Witness::Unavailable, + fn hash256_preimage>(sat: &S, h: &Pk::Hash256) -> Self { + if sat.lookup_hash256(h) { + Witness::Stack(vec![Placeholder::Hash256Preimage(h.clone())]) + // Note hash preimages are unavailable instead of impossible + } else { + Witness::Unavailable } } } -impl Witness { +impl Witness> { /// Produce something like a 32-byte 0 push fn hash_dissatisfaction() -> Self { - Witness::Stack(vec![vec![0; 32]]) + Witness::Stack(vec![Placeholder::HashDissatisfaction]) } /// Construct a satisfaction equivalent to an empty stack @@ -627,12 +873,12 @@ impl Witness { /// Construct a satisfaction equivalent to `OP_1` fn push_1() -> Self { - Witness::Stack(vec![vec![1]]) + Witness::Stack(vec![Placeholder::PushOne]) } /// Construct a satisfaction equivalent to a single empty push fn push_0() -> Self { - Witness::Stack(vec![vec![]]) + Witness::Stack(vec![Placeholder::PushZero]) } /// Concatenate, or otherwise combine, two satisfactions @@ -650,9 +896,9 @@ impl Witness { /// A (dis)satisfaction of a Miniscript fragment #[derive(Clone, PartialEq, Eq, Debug)] -pub struct Satisfaction { +pub struct Satisfaction { /// The actual witness stack - pub stack: Witness, + pub stack: Witness, /// Whether or not this (dis)satisfaction has a signature somewhere /// in it pub has_sig: bool, @@ -664,9 +910,74 @@ pub struct Satisfaction { pub relative_timelock: Option, } -impl Satisfaction { +impl Satisfaction { + pub(crate) fn map_stack(self, mapfn: F) -> Satisfaction + where + F: Fn(Vec) -> Vec, + { + let Satisfaction { + stack, + has_sig, + relative_timelock, + absolute_timelock, + } = self; + let stack = match stack { + Witness::Stack(stack) => Witness::Stack(mapfn(stack)), + Witness::Unavailable => Witness::Unavailable, + Witness::Impossible => Witness::Impossible, + }; + Satisfaction { + stack, + has_sig, + relative_timelock, + absolute_timelock, + } + } +} + +impl Satisfaction> { + pub(crate) fn build_template( + term: &Terminal, + provider: &P, + root_has_sig: bool, + leaf_hash: &TapLeafHash, + ) -> Self + where + Ctx: ScriptContext, + P: AssetProvider, + { + Self::satisfy_helper( + term, + provider, + root_has_sig, + leaf_hash, + &mut Satisfaction::minimum, + &mut Satisfaction::thresh, + ) + } + + pub(crate) fn build_template_mall( + term: &Terminal, + provider: &P, + root_has_sig: bool, + leaf_hash: &TapLeafHash, + ) -> Self + where + Ctx: ScriptContext, + P: AssetProvider, + { + Self::satisfy_helper( + term, + provider, + root_has_sig, + leaf_hash, + &mut Satisfaction::minimum_mall, + &mut Satisfaction::thresh_mall, + ) + } + // produce a non-malleable satisafaction for thesh frag - fn thresh( + fn thresh( k: usize, subs: &[Arc>], stfr: &Sat, @@ -675,10 +986,12 @@ impl Satisfaction { min_fn: &mut F, ) -> Self where - Pk: MiniscriptKey + ToPublicKey, Ctx: ScriptContext, - Sat: Satisfier, - F: FnMut(Satisfaction, Satisfaction) -> Satisfaction, + Sat: AssetProvider, + F: FnMut( + Satisfaction>, + Satisfaction>, + ) -> Satisfaction>, { let mut sats = subs .iter() @@ -796,7 +1109,7 @@ impl Satisfaction { } // produce a possily malleable satisafaction for thesh frag - fn thresh_mall( + fn thresh_mall( k: usize, subs: &[Arc>], stfr: &Sat, @@ -805,10 +1118,12 @@ impl Satisfaction { min_fn: &mut F, ) -> Self where - Pk: MiniscriptKey + ToPublicKey, Ctx: ScriptContext, - Sat: Satisfier, - F: FnMut(Satisfaction, Satisfaction) -> Satisfaction, + Sat: AssetProvider, + F: FnMut( + Satisfaction>, + Satisfaction>, + ) -> Satisfaction>, { let mut sats = subs .iter() @@ -953,7 +1268,7 @@ impl Satisfaction { } // produce a non-malleable satisfaction - fn satisfy_helper( + fn satisfy_helper( term: &Terminal, stfr: &Sat, root_has_sig: bool, @@ -962,10 +1277,12 @@ impl Satisfaction { thresh_fn: &mut G, ) -> Self where - Pk: MiniscriptKey + ToPublicKey, Ctx: ScriptContext, - Sat: Satisfier, - F: FnMut(Satisfaction, Satisfaction) -> Satisfaction, + Sat: AssetProvider, + F: FnMut( + Satisfaction>, + Satisfaction>, + ) -> Satisfaction>, G: FnMut( usize, &[Arc>], @@ -973,23 +1290,23 @@ impl Satisfaction { bool, &TapLeafHash, &mut F, - ) -> Satisfaction, + ) -> Satisfaction>, { match *term { Terminal::PkK(ref pk) => Satisfaction { - stack: Witness::signature::<_, _, Ctx>(stfr, pk, leaf_hash), + stack: Witness::signature::<_, Ctx>(stfr, pk, leaf_hash), has_sig: true, relative_timelock: None, absolute_timelock: None, }, Terminal::PkH(ref pk) => Satisfaction { - stack: Witness::pkh_signature(stfr, &pk.to_pubkeyhash(Ctx::sig_type())), + stack: Witness::pkh_signature::<_, Ctx>(stfr, &pk.to_pubkeyhash(Ctx::sig_type())), has_sig: true, relative_timelock: None, absolute_timelock: None, }, Terminal::RawPkH(ref pkh) => Satisfaction { - stack: Witness::pkh_signature(stfr, pkh), + stack: Witness::pkh_signature::<_, Ctx>(stfr, pkh), has_sig: true, relative_timelock: None, absolute_timelock: None, @@ -1238,7 +1555,7 @@ impl Satisfaction { let mut sig_count = 0; let mut sigs = Vec::with_capacity(k); for pk in keys { - match Witness::signature::<_, _, Ctx>(stfr, pk, leaf_hash) { + match Witness::signature::<_, Ctx>(stfr, pk, leaf_hash) { Witness::Stack(sig) => { sigs.push(sig); sig_count += 1; @@ -1282,9 +1599,9 @@ impl Satisfaction { Terminal::MultiA(k, ref keys) => { // Collect all available signatures let mut sig_count = 0; - let mut sigs = vec![vec![vec![]]; keys.len()]; + let mut sigs = vec![vec![Placeholder::PushZero]; keys.len()]; for (i, pk) in keys.iter().rev().enumerate() { - match Witness::signature::<_, _, Ctx>(stfr, pk, leaf_hash) { + match Witness::signature::<_, Ctx>(stfr, pk, leaf_hash) { Witness::Stack(sig) => { sigs[i] = sig; sig_count += 1; @@ -1325,7 +1642,7 @@ impl Satisfaction { } // Helper function to produce a dissatisfaction - fn dissatisfy_helper( + fn dissatisfy_helper( term: &Terminal, stfr: &Sat, root_has_sig: bool, @@ -1334,10 +1651,12 @@ impl Satisfaction { thresh_fn: &mut G, ) -> Self where - Pk: MiniscriptKey + ToPublicKey, Ctx: ScriptContext, - Sat: Satisfier, - F: FnMut(Satisfaction, Satisfaction) -> Satisfaction, + Sat: AssetProvider, + F: FnMut( + Satisfaction>, + Satisfaction>, + ) -> Satisfaction>, G: FnMut( usize, &[Arc>], @@ -1345,7 +1664,7 @@ impl Satisfaction { bool, &TapLeafHash, &mut F, - ) -> Satisfaction, + ) -> Satisfaction>, { match *term { Terminal::PkK(..) => Satisfaction { @@ -1357,14 +1676,17 @@ impl Satisfaction { Terminal::PkH(ref pk) => Satisfaction { stack: Witness::combine( Witness::push_0(), - Witness::pkh_public_key(stfr, &pk.to_pubkeyhash(Ctx::sig_type())), + Witness::pkh_public_key::<_, Ctx>(stfr, &pk.to_pubkeyhash(Ctx::sig_type())), ), has_sig: false, relative_timelock: None, absolute_timelock: None, }, Terminal::RawPkH(ref pkh) => Satisfaction { - stack: Witness::combine(Witness::push_0(), Witness::pkh_public_key(stfr, pkh)), + stack: Witness::combine( + Witness::push_0(), + Witness::pkh_public_key::<_, Ctx>(stfr, pkh), + ), has_sig: false, relative_timelock: None, absolute_timelock: None, @@ -1523,59 +1845,74 @@ impl Satisfaction { absolute_timelock: None, }, Terminal::Multi(k, _) => Satisfaction { - stack: Witness::Stack(vec![vec![]; k + 1]), + stack: Witness::Stack(vec![Placeholder::PushZero; k + 1]), has_sig: false, relative_timelock: None, absolute_timelock: None, }, Terminal::MultiA(_, ref pks) => Satisfaction { - stack: Witness::Stack(vec![vec![]; pks.len()]), + stack: Witness::Stack(vec![Placeholder::PushZero; pks.len()]), has_sig: false, relative_timelock: None, absolute_timelock: None, }, } } +} +impl Satisfaction> { + pub(crate) fn into_plan(self, desc_type: DescriptorType) -> Option { + if let Witness::Stack(stack) = self.stack { + Some(Plan { + desc_type, + template: WitnessTemplate::from_placeholder_stack(stack), + absolute_timelock: self.absolute_timelock.map(Into::into), + relative_timelock: self.relative_timelock, + }) + } else { + None + } + } +} + +impl Satisfaction> { /// Produce a satisfaction non-malleable satisfaction - pub(super) fn satisfy< - Pk: MiniscriptKey + ToPublicKey, - Ctx: ScriptContext, - Sat: Satisfier, - >( + pub(super) fn satisfy( term: &Terminal, stfr: &Sat, root_has_sig: bool, leaf_hash: &TapLeafHash, - ) -> Self { - Self::satisfy_helper( - term, - stfr, - root_has_sig, - leaf_hash, - &mut Satisfaction::minimum, - &mut Satisfaction::thresh, - ) + ) -> Self + where + Ctx: ScriptContext, + Pk: MiniscriptKey + ToPublicKey, + Sat: Satisfier, + { + Satisfaction::>::build_template(term, &stfr, root_has_sig, leaf_hash) + .map_stack(|stack| { + WitnessTemplate::from_placeholder_stack(stack) + .try_completing(stfr) + .expect("the same satisfier should manage to complete the template") + }) } /// Produce a satisfaction(possibly malleable) - pub(super) fn satisfy_mall< - Pk: MiniscriptKey + ToPublicKey, - Ctx: ScriptContext, - Sat: Satisfier, - >( + pub(super) fn satisfy_mall( term: &Terminal, stfr: &Sat, root_has_sig: bool, leaf_hash: &TapLeafHash, - ) -> Self { - Self::satisfy_helper( - term, - stfr, - root_has_sig, - leaf_hash, - &mut Satisfaction::minimum_mall, - &mut Satisfaction::thresh_mall, - ) + ) -> Self + where + Ctx: ScriptContext, + Pk: MiniscriptKey + ToPublicKey, + Sat: Satisfier, + { + Satisfaction::>::build_template_mall(term, &stfr, root_has_sig, leaf_hash) + .map_stack(|stack| { + WitnessTemplate::from_placeholder_stack(stack) + .try_completing(stfr) + .expect("the same satisfier should manage to complete the template") + }) } } diff --git a/src/plan.rs b/src/plan.rs new file mode 100644 index 000000000..eebeea7e2 --- /dev/null +++ b/src/plan.rs @@ -0,0 +1,467 @@ +// Miniscript +// Written in 2022 by rust-miniscript developers +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to +// the public domain worldwide. This software is distributed without +// any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication +// along with this software. +// If not, see . +//! A spending plan or *plan* for short is a representation of a particular spending path on a +//! descriptor. This allows us to analayze a choice of spending path without producing any +//! signatures or other witness data for it. +//! +//! To make a plan you provide the descriptor with "assets" like which keys you are able to use, hash +//! pre-images you have access to, absolute/relative timelock constraints etc. +//! +//! Once you've got a plan it can tell you its expected satisfaction weight which can be useful for +//! doing coin selection. Furthermore it provides which subset of those keys and hash pre-images you +//! will actually need as well as what locktime or sequence number you need to set. +//! +//! Once you've obstained signatures, hash pre-images etc required by the plan, it can create a +//! witness/script_sig for the input. + +use core::cmp::Ordering; + +use bitcoin::hashes::{hash160, ripemd160, sha256}; +use bitcoin::util::address::WitnessVersion; +use bitcoin::util::taproot::TapLeafHash; +use bitcoin::{LockTime, Sequence}; + +use crate::descriptor::DescriptorType; +use crate::miniscript::context::SigType; +use crate::miniscript::hash256; +use crate::miniscript::satisfy::{Placeholder, Satisfier, WitnessTemplate}; +use crate::prelude::*; +use crate::util::witness_size; +use crate::{DefiniteDescriptorKey, MiniscriptKey, ScriptContext, ToPublicKey}; + +/// Trait describing a present/missing lookup table for constructing witness templates +/// +/// This trait mirrors the [`Satisfier`] trait with the difference that instad of returning the +/// item if it's present, it only returns a boolean to indicate its presence. +/// +/// This trait is automatically implemented for every type that is also a satisfier, and simply +/// proxies the queries to the satisfier and returns whether an item is available or not. +/// +/// All the methods have a default implementation that returns `false`. +pub trait AssetProvider { + /// Given a public key, look up an ECDSA signature with that key + fn lookup_ecdsa_sig(&self, _: &Pk) -> bool { + false + } + + /// Lookup the tap key spend sig + fn lookup_tap_key_spend_sig(&self, _: &Pk) -> bool { + false + } + + /// Given a public key and a associated leaf hash, look up an schnorr signature with that key + fn lookup_tap_leaf_script_sig(&self, _: &Pk, _: &TapLeafHash) -> bool { + false + } + + /// Given a raw `Pkh`, lookup corresponding `Pk`. If present, return its lenght. + fn lookup_raw_pkh_pk(&self, _: &hash160::Hash) -> Option { + None + } + + /// Given a keyhash, look up the EC signature and the associated key. If present, + /// return the key lenght. + /// Even if signatures for public key Hashes are not available, the users + /// can use this map to provide pkh -> pk mapping which can be useful + /// for dissatisfying pkh. + fn lookup_raw_pkh_ecdsa_sig(&self, _: &hash160::Hash) -> Option { + None + } + + /// Given a keyhash, look up the schnorr signature and the associated key + /// Even if signatures for public key Hashes are not available, the users + /// can use this map to provide pkh -> pk mapping which can be useful + /// for dissatisfying pkh. + fn lookup_raw_pkh_tap_leaf_script_sig(&self, _: &(hash160::Hash, TapLeafHash)) -> bool { + false + } + + /// Given a SHA256 hash, look up its preimage + fn lookup_sha256(&self, _: &Pk::Sha256) -> bool { + false + } + + /// Given a HASH256 hash, look up its preimage + fn lookup_hash256(&self, _: &Pk::Hash256) -> bool { + false + } + + /// Given a RIPEMD160 hash, look up its preimage + fn lookup_ripemd160(&self, _: &Pk::Ripemd160) -> bool { + false + } + + /// Given a HASH160 hash, look up its preimage + fn lookup_hash160(&self, _: &Pk::Hash160) -> bool { + false + } + + /// Assert whether a relative locktime is satisfied + fn check_older(&self, _: Sequence) -> bool { + false + } + + /// Assert whether an absolute locktime is satisfied + fn check_after(&self, _: LockTime) -> bool { + false + } +} + +/// Wrapper around [`Assets`] that logs every query and value returned +#[cfg(feature = "std")] +pub struct LoggerAssetProvider(Assets); + +#[cfg(feature = "std")] +macro_rules! impl_log_method { + ( $name:ident, $( <$ctx:ident: ScriptContext > )? $( $arg:ident : $ty:ty, )* -> $ret_ty:ty ) => { + fn $name $( <$ctx: ScriptContext> )? ( &self, $( $arg:$ty ),* ) -> $ret_ty { + let ret = (self.0).$name $( ::<$ctx> )*( $( $arg ),* ); + dbg!(stringify!( $name ), ( $( $arg ),* ), &ret); + + ret + } + } +} + +#[cfg(feature = "std")] +impl AssetProvider for LoggerAssetProvider { + impl_log_method!(lookup_ecdsa_sig, pk: &DefiniteDescriptorKey, -> bool); + impl_log_method!(lookup_tap_key_spend_sig, pk: &DefiniteDescriptorKey, -> bool); + impl_log_method!(lookup_tap_leaf_script_sig, pk: &DefiniteDescriptorKey, leaf_hash: &TapLeafHash, -> bool); + impl_log_method!(lookup_raw_pkh_pk, hash: &hash160::Hash, -> Option); + impl_log_method!(lookup_raw_pkh_ecdsa_sig, hash: &hash160::Hash, -> Option); + impl_log_method!(lookup_raw_pkh_tap_leaf_script_sig, hash: &(hash160::Hash, TapLeafHash), -> bool); + impl_log_method!(lookup_sha256, hash: &sha256::Hash, -> bool); + impl_log_method!(lookup_hash256, hash: &hash256::Hash, -> bool); + impl_log_method!(lookup_ripemd160, hash: &ripemd160::Hash, -> bool); + impl_log_method!(lookup_hash160, hash: &hash160::Hash, -> bool); + impl_log_method!(check_older, s: Sequence, -> bool); + impl_log_method!(check_after, t: LockTime, -> bool); +} + +impl AssetProvider for T +where + T: Satisfier, + Pk: MiniscriptKey + ToPublicKey, +{ + fn lookup_ecdsa_sig(&self, pk: &Pk) -> bool { + Satisfier::lookup_ecdsa_sig(self, pk).is_some() + } + + fn lookup_tap_key_spend_sig(&self, _: &Pk) -> bool { + Satisfier::lookup_tap_key_spend_sig(self).is_some() + } + + fn lookup_tap_leaf_script_sig(&self, pk: &Pk, leaf_hash: &TapLeafHash) -> bool { + Satisfier::lookup_tap_leaf_script_sig(self, pk, leaf_hash).is_some() + } + + fn lookup_raw_pkh_pk(&self, hash: &hash160::Hash) -> Option { + Satisfier::lookup_raw_pkh_pk(self, hash).map(|p| Ctx::pk_len(&p)) + } + + fn lookup_raw_pkh_ecdsa_sig(&self, hash: &hash160::Hash) -> Option { + Satisfier::lookup_raw_pkh_ecdsa_sig(self, hash).map(|(p, _)| Ctx::pk_len(&p)) + } + + fn lookup_raw_pkh_tap_leaf_script_sig(&self, hash: &(hash160::Hash, TapLeafHash)) -> bool { + Satisfier::lookup_raw_pkh_tap_leaf_script_sig(self, hash).is_some() + } + + fn lookup_sha256(&self, hash: &Pk::Sha256) -> bool { + Satisfier::lookup_sha256(self, hash).is_some() + } + + fn lookup_hash256(&self, hash: &Pk::Hash256) -> bool { + Satisfier::lookup_hash256(self, hash).is_some() + } + + fn lookup_ripemd160(&self, hash: &Pk::Ripemd160) -> bool { + Satisfier::lookup_ripemd160(self, hash).is_some() + } + + fn lookup_hash160(&self, hash: &Pk::Hash160) -> bool { + Satisfier::lookup_hash160(self, hash).is_some() + } + + fn check_older(&self, s: Sequence) -> bool { + Satisfier::check_older(self, s) + } + + fn check_after(&self, l: LockTime) -> bool { + Satisfier::check_after(self, l) + } +} + +/// Representation of a particular spending path on a descriptor. Contains the witness template +/// and the timelocks needed for satisfying the plan. +/// Calling `get_plan` on a Descriptor will return this structure, +/// containing the cheapest spending path possible (considering the `Assets` given) +#[derive(Debug, Clone)] +pub struct Plan { + /// This plan's witness template + pub template: WitnessTemplate>, + /// The absolute timelock this plan uses + pub absolute_timelock: Option, + /// The relative timelock this plan uses + pub relative_timelock: Option, + + pub(crate) desc_type: DescriptorType, +} + +impl Plan { + /// Returns the witness version + pub fn witness_version(&self) -> Option { + self.desc_type.segwit_version() + } + + /// The weight, in witness units, needed for satisfying this plan (includes both + /// the script sig weight and the witness weight) + pub fn satisfaction_weight(&self) -> usize { + self.witness_size() + self.scriptsig_size() * 4 + } + + /// The size in bytes of the script sig that satisfies this plan + pub fn scriptsig_size(&self) -> usize { + match (self.desc_type.segwit_version(), self.desc_type) { + // Entire witness goes in the script_sig + (None, _) => witness_size(self.template.as_ref()), + // Taproot doesn't have a "wrapped" version (scriptSig len (1)) + (Some(WitnessVersion::V1), _) => 1, + // scriptSig len (1) + OP_0 (1) + OP_PUSHBYTES_20 (1) + (20) + (_, DescriptorType::ShWpkh) => 1 + 1 + 1 + 20, + // scriptSig len (1) + OP_0 (1) + OP_PUSHBYTES_32 (1) +