diff --git a/fuzz/fuzz_targets/compile_descriptor.rs b/fuzz/fuzz_targets/compile_descriptor.rs index 03c7871f..e4cbe18d 100644 --- a/fuzz/fuzz_targets/compile_descriptor.rs +++ b/fuzz/fuzz_targets/compile_descriptor.rs @@ -4,9 +4,10 @@ use miniscript::Segwitv0; use miniscript::{policy, DummyKey, Miniscript}; use policy::Liftable; +use miniscript::NoExt; use std::str::FromStr; -type DummyScript = Miniscript; +type DummyScript = Miniscript; type DummyPolicy = policy::Concrete; fn do_test(data: &[u8]) { diff --git a/fuzz/fuzz_targets/roundtrip_miniscript_script.rs b/fuzz/fuzz_targets/roundtrip_miniscript_script.rs index 4c9423c2..13ba6532 100644 --- a/fuzz/fuzz_targets/roundtrip_miniscript_script.rs +++ b/fuzz/fuzz_targets/roundtrip_miniscript_script.rs @@ -1,6 +1,7 @@ extern crate elements_miniscript as miniscript; use miniscript::elements::script; +use miniscript::AllExt; use miniscript::Miniscript; use miniscript::Segwitv0; @@ -8,7 +9,7 @@ fn do_test(data: &[u8]) { // Try round-tripping as a script let script = script::Script::from(data.to_owned()); - if let Ok(pt) = Miniscript::<_, Segwitv0>::parse(&script) { + if let Ok(pt) = Miniscript::<_, Segwitv0, AllExt>::parse(&script) { let output = pt.encode(); assert_eq!(pt.script_size(), output.len()); assert_eq!(output, script); diff --git a/fuzz/fuzz_targets/roundtrip_miniscript_str.rs b/fuzz/fuzz_targets/roundtrip_miniscript_str.rs index d20aea30..569b98bb 100644 --- a/fuzz/fuzz_targets/roundtrip_miniscript_str.rs +++ b/fuzz/fuzz_targets/roundtrip_miniscript_str.rs @@ -4,13 +4,14 @@ extern crate regex; use regex::Regex; use std::str::FromStr; +use miniscript::AllExt; use miniscript::DummyKey; use miniscript::Miniscript; use miniscript::Segwitv0; fn do_test(data: &[u8]) { let s = String::from_utf8_lossy(data); - if let Ok(desc) = Miniscript::::from_str(&s) { + if let Ok(desc) = Miniscript::::from_str(&s) { let output = desc.to_string(); let multi_wrap_pk_re = Regex::new("([a-z]+)c:pk_k\\(").unwrap(); diff --git a/src/descriptor/bare.rs b/src/descriptor/bare.rs index e32b7352..c1dc3ae1 100644 --- a/src/descriptor/bare.rs +++ b/src/descriptor/bare.rs @@ -32,6 +32,8 @@ use { TranslatePk, }; +use NoExt; + use super::{ checksum::{desc_checksum, verify_checksum}, DescriptorTrait, ElementsTrait, ELMTS_STR, @@ -42,24 +44,24 @@ use super::{ #[derive(Clone, Ord, PartialOrd, Eq, PartialEq)] pub struct Bare { /// underlying miniscript - ms: Miniscript, + ms: Miniscript, } impl Bare { /// Create a new raw descriptor - pub fn new(ms: Miniscript) -> Result { + pub fn new(ms: Miniscript) -> Result { // do the top-level checks BareCtx::top_level_checks(&ms)?; Ok(Self { ms: ms }) } /// get the inner - pub fn into_inner(self) -> Miniscript { + pub fn into_inner(self) -> Miniscript { self.ms } /// get the inner - pub fn as_inner(&self) -> &Miniscript { + pub fn as_inner(&self) -> &Miniscript { &self.ms } } @@ -98,7 +100,7 @@ where name: top.name.split_at(2).1, args: top.args.clone(), }; - let sub = Miniscript::::from_tree(&new_tree)?; + let sub = Miniscript::::from_tree(&new_tree)?; BareCtx::top_level_checks(&sub)?; Bare::new(sub) } else { diff --git a/src/descriptor/covenants/cov.rs b/src/descriptor/covenants/cov.rs index 363b3bd8..3e6ccfa2 100644 --- a/src/descriptor/covenants/cov.rs +++ b/src/descriptor/covenants/cov.rs @@ -65,6 +65,8 @@ use { ForEach, ForEachKey, Miniscript, ScriptContext, Segwitv0, TranslatePk, }; +use Extension; + use super::super::{ checksum::{desc_checksum, verify_checksum}, ElementsTrait, ELMTS_STR, @@ -85,33 +87,34 @@ pub(crate) const COV_SCRIPT_SIZE: usize = 120; pub(crate) const COV_SCRIPT_OPCODE_COST: usize = 74; /// The covenant descriptor #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] -pub struct CovenantDescriptor { +pub struct CovenantDescriptor> { /// the pk constraining the Covenant /// The key over which we want CHECKSIGFROMSTACK pub(crate) pk: Pk, /// the underlying Miniscript /// Must be under segwit context - pub(crate) ms: Miniscript, + // All known extensions are enabled in covenant descriptor + pub(crate) ms: Miniscript, } -impl CovenantDescriptor { +impl> CovenantDescriptor { /// Get the pk from covenant pub fn pk(&self) -> &Pk { &self.pk } /// Get a reference to Miniscript inside covenant - pub fn to_ms(&self) -> &Miniscript { + pub fn to_ms(&self) -> &Miniscript { &self.ms } /// Consume self and return inner miniscript - pub fn into_ms(self) -> Miniscript { + pub fn into_ms(self) -> Miniscript { self.ms } /// Create a new Self from components - pub fn new(pk: Pk, ms: Miniscript) -> Result { + pub fn new(pk: Pk, ms: Miniscript) -> Result { // // 1) Check the 201 opcode count here let ms_op_count = ms.ext.ops_count_sat; // statically computed @@ -205,7 +208,7 @@ impl CovenantDescriptor { } } -impl CovenantDescriptor { +impl> CovenantDescriptor { /// Check if the given script is a covenant descriptor /// Consumes the iterator so that only remaining miniscript /// needs to be parsed from the iterator @@ -244,7 +247,7 @@ impl CovenantDescriptor { // All code for covenants can thus be separated in a module // This parsing is parse_insane pub fn parse_insane(script: &script::Script) -> Result { - let (pk, ms) = Self::parse_cov_components(script)?; + let (pk, ms) = Self::parse_cov_components::<_>(script)?; Self::new(pk, ms) } @@ -252,13 +255,17 @@ impl CovenantDescriptor { // descriptor. This allows us to parse Miniscript with // it's context so that it can be used with NoChecks // context while using the interpreter - pub(crate) fn parse_cov_components( + pub(crate) fn parse_cov_components( script: &script::Script, - ) -> Result<(bitcoin::PublicKey, Miniscript), Error> { + ) -> Result<(bitcoin::PublicKey, Miniscript), Error> + where + Ctx: ScriptContext, + Ext: Extension, + { let tokens = lex(script)?; let mut iter = TokenIter::new(tokens); - let pk = CovenantDescriptor::::check_cov_script(&mut iter)?; + let pk = CovenantDescriptor::::check_cov_script(&mut iter)?; let ms = decode::parse(&mut iter)?; Segwitv0::check_global_validity(&ms)?; if ms.ty.corr.base != types::Base::B { @@ -281,10 +288,11 @@ impl CovenantDescriptor { } } -impl FromTree for CovenantDescriptor +impl FromTree for CovenantDescriptor where Pk: FromStr, Pk::Hash: FromStr, + Ext: Extension, ::Err: ToString, <::Hash as FromStr>::Err: ToString, { @@ -304,13 +312,21 @@ where } } } -impl fmt::Debug for CovenantDescriptor { +impl fmt::Debug for CovenantDescriptor +where + Pk: MiniscriptKey, + Ext: Extension, +{ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}covwsh({},{})", ELMTS_STR, self.pk, self.ms) } } -impl fmt::Display for CovenantDescriptor { +impl fmt::Display for CovenantDescriptor +where + Pk: MiniscriptKey, + Ext: Extension, +{ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let desc = format!("{}covwsh({},{})", ELMTS_STR, self.pk, self.ms); let checksum = desc_checksum(&desc).map_err(|_| fmt::Error)?; @@ -318,28 +334,32 @@ impl fmt::Display for CovenantDescriptor { } } -impl FromStr for CovenantDescriptor +impl FromStr for CovenantDescriptor where Pk: FromStr, Pk::Hash: FromStr, ::Err: ToString, <::Hash as FromStr>::Err: ToString, + Pk: MiniscriptKey, + Ext: Extension, { type Err = Error; fn from_str(s: &str) -> Result { let desc_str = verify_checksum(s)?; let top = expression::Tree::from_str(desc_str)?; - CovenantDescriptor::::from_tree(&top) + CovenantDescriptor::::from_tree(&top) } } -impl ElementsTrait for CovenantDescriptor +impl ElementsTrait for CovenantDescriptor where Pk: FromStr, Pk::Hash: FromStr, ::Err: ToString, <::Hash as FromStr>::Err: ToString, + Pk: MiniscriptKey, + Ext: Extension, { fn blind_addr( &self, @@ -357,12 +377,14 @@ where } } -impl DescriptorTrait for CovenantDescriptor +impl DescriptorTrait for CovenantDescriptor where Pk: FromStr, Pk::Hash: FromStr, ::Err: ToString, <::Hash as FromStr>::Err: ToString, + Pk: MiniscriptKey, + Ext: Extension, { fn sanity_check(&self) -> Result<(), Error> { self.ms.sanity_check()?; @@ -443,7 +465,7 @@ where } } -impl ForEachKey for CovenantDescriptor { +impl> ForEachKey for CovenantDescriptor { fn for_each_key<'a, F: FnMut(ForEach<'a, Pk>) -> bool>(&'a self, mut pred: F) -> bool where Pk: 'a, @@ -453,8 +475,14 @@ impl ForEachKey for CovenantDescriptor { } } -impl TranslatePk for CovenantDescriptor

{ - type Output = CovenantDescriptor; +impl TranslatePk for CovenantDescriptor +where + P: MiniscriptKey, + Q: MiniscriptKey, + Ext: Extension

+ TranslatePk, + >::Output: Extension, +{ + type Output = CovenantDescriptor>::Output>; fn translate_pk( &self, diff --git a/src/descriptor/covenants/mod.rs b/src/descriptor/covenants/mod.rs index 69619d6b..4b4c3f52 100644 --- a/src/descriptor/covenants/mod.rs +++ b/src/descriptor/covenants/mod.rs @@ -55,6 +55,8 @@ pub use self::script_internals::CovOperations; #[allow(unused_imports)] mod tests { + use AllExt; + use super::cov::*; use super::*; use bitcoin; @@ -101,7 +103,8 @@ mod tests { assert_eq!(desc.desc_type(), DescriptorType::Cov); let script = desc.explicit_script(); - let cov_desc = CovenantDescriptor::::parse_insane(&script).unwrap(); + let cov_desc = + CovenantDescriptor::::parse_insane(&script).unwrap(); assert_eq!(cov_desc.to_string(), desc.to_string()); } diff --git a/src/descriptor/mod.rs b/src/descriptor/mod.rs index 2ec91c4e..adebd9be 100644 --- a/src/descriptor/mod.rs +++ b/src/descriptor/mod.rs @@ -37,6 +37,9 @@ use elements; use elements::secp256k1_zkp; use elements::Script; +use AllExt; +use NoExt; + use self::checksum::verify_checksum; use expression; use miniscript; @@ -344,8 +347,9 @@ pub enum Descriptor { Sh(Sh), /// Pay-to-Witness-ScriptHash with Segwitv0 context Wsh(Wsh), - /// Covenant descriptor - Cov(CovenantDescriptor), + /// Covenant descriptor with all known extensions + /// Downstream implementations of extensions should implement directly use descriptor API + Cov(CovenantDescriptor), } impl Descriptor { @@ -354,7 +358,7 @@ impl Descriptor { /// Create a new pk descriptor pub fn new_pk(pk: Pk) -> Self { // roundabout way to constuct `c:pk_k(pk)` - let ms: Miniscript = + let ms: Miniscript = Miniscript::from_ast(miniscript::decode::Terminal::Check(Arc::new( Miniscript::from_ast(miniscript::decode::Terminal::PkK(pk)) .expect("Type check cannot fail"), @@ -385,28 +389,28 @@ impl Descriptor { /// Create a new sh for a given redeem script /// Errors when miniscript exceeds resource limits under p2sh context /// or does not type check at the top level - pub fn new_sh(ms: Miniscript) -> Result { + pub fn new_sh(ms: Miniscript) -> Result { Ok(Descriptor::Sh(Sh::new(ms)?)) } /// Create a new wsh descriptor from witness script /// Errors when miniscript exceeds resource limits under p2sh context /// or does not type check at the top level - pub fn new_wsh(ms: Miniscript) -> Result { + pub fn new_wsh(ms: Miniscript) -> Result { Ok(Descriptor::Wsh(Wsh::new(ms)?)) } /// Create a new sh wrapped wsh descriptor with witness script /// Errors when miniscript exceeds resource limits under wsh context /// or does not type check at the top level - pub fn new_sh_wsh(ms: Miniscript) -> Result { + pub fn new_sh_wsh(ms: Miniscript) -> Result { Ok(Descriptor::Sh(Sh::new_wsh(ms)?)) } /// Create a new bare descriptor from witness script /// Errors when miniscript exceeds resource limits under bare context /// or does not type check at the top level - pub fn new_bare(ms: Miniscript) -> Result { + pub fn new_bare(ms: Miniscript) -> Result { Ok(Descriptor::Bare(Bare::new(ms)?)) } @@ -433,7 +437,8 @@ impl Descriptor { } /// Create a new covenant descriptor - pub fn new_cov_wsh(pk: Pk, ms: Miniscript) -> Result { + // All extensions are supported in wsh descriptor + pub fn new_cov_wsh(pk: Pk, ms: Miniscript) -> Result { let cov = CovenantDescriptor::new(pk, ms)?; Ok(Descriptor::Cov(cov)) } @@ -462,7 +467,7 @@ impl Descriptor { } /// Tries to convert descriptor as a covenant descriptor - pub fn as_cov(&self) -> Result<&CovenantDescriptor, Error> { + pub fn as_cov(&self) -> Result<&CovenantDescriptor, Error> { if let Descriptor::Cov(cov) = self { Ok(cov) } else { diff --git a/src/descriptor/segwitv0.rs b/src/descriptor/segwitv0.rs index a5817f49..3dca7aac 100644 --- a/src/descriptor/segwitv0.rs +++ b/src/descriptor/segwitv0.rs @@ -30,6 +30,8 @@ use { TranslatePk, }; +use NoExt; + use super::{ checksum::{desc_checksum, verify_checksum}, DescriptorTrait, ElementsTrait, SortedMultiVec, ELMTS_STR, @@ -53,7 +55,7 @@ impl Wsh { } /// Create a new wsh descriptor - pub fn new(ms: Miniscript) -> Result { + pub fn new(ms: Miniscript) -> Result { // do the top-level checks Segwitv0::top_level_checks(&ms)?; Ok(Self { @@ -114,7 +116,9 @@ pub enum WshInner { /// Sorted Multi SortedMulti(SortedMultiVec), /// Wsh Miniscript - Ms(Miniscript), + // no extensions in regular wsh or shwsh + // extensions are only supported in cov descriptors + Ms(Miniscript), } impl Liftable for Wsh { diff --git a/src/descriptor/sh.rs b/src/descriptor/sh.rs index d9150c60..ac857faa 100644 --- a/src/descriptor/sh.rs +++ b/src/descriptor/sh.rs @@ -33,6 +33,8 @@ use { ToPublicKey, TranslatePk, }; +use NoExt; + use super::{ checksum::{desc_checksum, verify_checksum}, DescriptorTrait, ElementsTrait, SortedMultiVec, Wpkh, Wsh, ELMTS_STR, @@ -55,7 +57,8 @@ pub enum ShInner { /// Inner Sorted Multi SortedMulti(SortedMultiVec), /// p2sh miniscript - Ms(Miniscript), + // p2sh has no extension support + Ms(Miniscript), } impl Liftable for Sh { @@ -151,7 +154,7 @@ impl Sh { } /// Create a new p2sh descriptor with the raw miniscript - pub fn new(ms: Miniscript) -> Result { + pub fn new(ms: Miniscript) -> Result { // do the top-level checks Legacy::top_level_checks(&ms)?; Ok(Self { @@ -170,7 +173,7 @@ impl Sh { } /// Create a new p2sh wrapped wsh descriptor with the raw miniscript - pub fn new_wsh(ms: Miniscript) -> Result { + pub fn new_wsh(ms: Miniscript) -> Result { Ok(Self { inner: ShInner::Wsh(Wsh::new(ms)?), }) diff --git a/src/descriptor/sortedmulti.rs b/src/descriptor/sortedmulti.rs index 4943e504..ca6279d6 100644 --- a/src/descriptor/sortedmulti.rs +++ b/src/descriptor/sortedmulti.rs @@ -24,6 +24,8 @@ use expression; use miniscript::{self, context::ScriptContext, decode::Terminal}; use policy; use script_num_size; + +use NoExt; use {errstr, Error, ForEach, ForEachKey, Miniscript, MiniscriptKey, Satisfier, ToPublicKey}; /// Contents of a "sortedmulti" descriptor @@ -50,7 +52,8 @@ impl SortedMultiVec { // Check the limits before creating a new SortedMultiVec // For example, under p2sh context the scriptlen can only be // upto 520 bytes. - let term: miniscript::decode::Terminal = Terminal::Multi(k, pks.clone()); + // sorted_multi has no extensions enabled + let term: miniscript::decode::Terminal = Terminal::Multi(k, pks.clone()); let ms = Miniscript::from_ast(term)?; // This would check all the consensus rules for p2sh/p2wsh and @@ -119,7 +122,7 @@ impl ForEachKey for SortedMultiVec SortedMultiVec { /// utility function to sanity a sorted multi vec pub fn sanity_check(&self) -> Result<(), Error> { - let ms: Miniscript = + let ms: Miniscript = Miniscript::from_ast(Terminal::Multi(self.k, self.pks.clone())) .expect("Must typecheck"); // '?' for doing From conversion @@ -130,7 +133,7 @@ impl SortedMultiVec { impl SortedMultiVec { /// Create Terminal::Multi containing sorted pubkeys - pub fn sorted_node(&self) -> Terminal + pub fn sorted_node(&self) -> Terminal where Pk: ToPublicKey, { diff --git a/src/extensions/mod.rs b/src/extensions/mod.rs new file mode 100644 index 00000000..2f4a8b8b --- /dev/null +++ b/src/extensions/mod.rs @@ -0,0 +1,348 @@ +//! Extensions to elements-miniscript +//! Users should implement the [`Extension`] trait to extend miniscript to have newer leaf nodes +//! Look at examples for implementation of ver_eq fragment + +use std::{fmt, hash}; + +use elements::script::Builder; +use policy; +use Error; +use MiniscriptKey; +use ToPublicKey; +use {ForEach, TranslatePk}; + +use {expression::Tree, policy::Liftable, Satisfier}; + +use miniscript::{ + context::ScriptContextError, + lex::TokenIter, + satisfy::Satisfaction, + types::{Correctness, ExtData, Malleability}, +}; + +use interpreter::{self, Stack}; +mod outputs_pref; +mod tx_ver; +pub use self::outputs_pref::OutputsPref; +pub use self::tx_ver::VerEq; + +/// Extensions to elements-miniscript. +/// Refer to implementations(unimplemented!) for example and tutorials +pub trait Extension: + Clone + Eq + Ord + fmt::Debug + fmt::Display + hash::Hash + Liftable +{ + /// Calculate the correctness property for the leaf fragment. + /// See miniscript reference for more info on different types + fn corr_prop(&self) -> Correctness; + + /// Calculate the malleability property for the leaf fragment. + /// See miniscript reference for more info on different types + fn mall_prop(&self) -> Malleability; + + /// Calculate the Extra properties property for the leaf fragment. + /// See current implementation for different fragments in extra_props.rs + fn extra_prop(&self) -> ExtData; + + /// Produce a satisfaction for this from satisfier. + /// See satisfaction code in satisfy.rs for example + /// Note that the [`Satisfaction`] struct also covers the case when + /// satisfaction is impossible/unavailable + fn satisfy(&self, _sat: &S) -> Satisfaction + where + Pk: ToPublicKey, + S: Satisfier; + + /// Produce a satisfaction for this from satisfier. + /// See satisfaction code in satisfy.rs for example + /// Note that the [`Satisfaction`] struct also covers the case when + /// dissatisfaction is impossible/unavailable + fn dissatisfy(&self, _sat: &S) -> Satisfaction + where + Pk: ToPublicKey, + S: Satisfier; + + /// Check if the predicate holds for all keys + fn real_for_each_key<'a, F: FnMut(ForEach<'a, Pk>) -> bool>(&'a self, _pred: &mut F) -> bool + where + Pk: 'a, + Pk::Hash: 'a; + + /// Encoding of the current fragment + fn push_to_builder(&self, builder: Builder) -> Builder + where + Pk: ToPublicKey; + + /// Get the script size of the current fragment + fn script_size(&self) -> usize; + + /// Validity rules for fragment in segwit context + fn segwit_ctx_checks(&self) -> Result<(), ScriptContextError> { + Ok(()) + } + + //unimplemented: Add checks after we introduce Tap ctx + + /// Parse the terminal from [`TokenIter`]. Implementers of this trait are responsible + /// for making sure tokens is mutated correctly. If parsing is not successful, the tokens + /// should not be consumed. + fn from_token_iter(_tokens: &mut TokenIter) -> Result; + + /// Create an instance of this object from a Tree with root name and children as + /// Vec. + // Ideally, we would want a FromTree implementation here, but that is not possible + // as we would need to create a new Tree by removing wrappers from root. + fn from_name_tree(_name: &str, children: &[Tree]) -> Result; + + /// Interpreter support + /// Evaluate the fragment based on inputs from stack. If an implementation of this + /// is provided the user can use the interpreter API to parse scripts from blockchain + /// and check which constraints are satisfied + /// Output [`None`] when the ext fragment is dissatisfied, output Some(Err) when there is + /// an error in interpreter value. Finally, if the evaluation is successful output Some(Ok()) + /// After taproot this should also access to the transaction data + fn evaluate<'intp, 'txin>( + &'intp self, + stack: &mut Stack<'txin>, + ) -> Option>; +} + +/// No Extensions for elements-miniscript +/// All the implementations for the this function are unreachable +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)] +pub struct NoExt; + +impl Extension for NoExt { + fn corr_prop(&self) -> Correctness { + unreachable!() + } + + fn mall_prop(&self) -> Malleability { + unreachable!() + } + + fn extra_prop(&self) -> ExtData { + unreachable!() + } + + fn satisfy(&self, _sat: &S) -> Satisfaction + where + Pk: ToPublicKey, + S: Satisfier, + { + unreachable!() + } + + fn dissatisfy(&self, _sat: &S) -> Satisfaction + where + Pk: ToPublicKey, + S: Satisfier, + { + unreachable!() + } + + fn real_for_each_key<'a, F: FnMut(ForEach<'a, Pk>) -> bool>(&'a self, _pred: &mut F) -> bool + where + Pk: 'a, + Pk::Hash: 'a, + { + unreachable!() + } + + fn push_to_builder(&self, _builder: Builder) -> Builder + where + Pk: ToPublicKey, + { + unreachable!() + } + + fn script_size(&self) -> usize { + unreachable!() + } + + fn from_token_iter(_tokens: &mut TokenIter) -> Result { + // No extensions should return Err on parsing + Err(()) + } + + fn from_name_tree(_name: &str, _children: &[Tree]) -> Result { + // No extensions should not parse any extensions from String + Err(()) + } + + fn evaluate<'intp, 'txin>( + &'intp self, + _stack: &mut Stack<'txin>, + ) -> Option> { + unreachable!() + } +} + +impl Liftable for NoExt { + fn lift(&self) -> Result, Error> { + unreachable!() + } +} + +impl fmt::Display for NoExt { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + unreachable!() + } +} + +impl TranslatePk for NoExt { + type Output = NoExt; + + fn translate_pk( + &self, + mut _translatefpk: Fpk, + _translatefpkh: Fpkh, + ) -> Result + where + Fpk: FnMut(&P) -> Result, + Fpkh: FnMut(&P::Hash) -> Result, + Q: MiniscriptKey, + { + unreachable!() + } +} + +/// All known Extensions for elements-miniscript +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)] +pub enum AllExt { + /// Version Equal + VerEq(VerEq), + /// Outputs Prefix equal + OutputsPref(OutputsPref), +} + +// Apply the function on each arm +macro_rules! all_arms_fn { + ($slf: ident, $f: ident, $($args:ident, )* ) => { + match $slf { + AllExt::VerEq(v) => >::$f(v, $($args, )*), + AllExt::OutputsPref(p) => >::$f(p, $($args, )*), + } + }; +} + +// try all extensions one by one +// Self::$f(args) +macro_rules! try_from_arms { + ($f: ident, $($args: ident, )*) => { + if let Ok(v) = >::$f($($args, )*) { + Ok(AllExt::VerEq(v)) + } else if let Ok(v) = >::$f($($args, )*) { + Ok(AllExt::OutputsPref(v)) + } else { + Err(()) + } + }; +} + +impl Extension for AllExt +where + Pk: MiniscriptKey, +{ + fn corr_prop(&self) -> Correctness { + all_arms_fn!(self, corr_prop,) + } + + fn mall_prop(&self) -> Malleability { + all_arms_fn!(self, mall_prop,) + } + + fn extra_prop(&self) -> ExtData { + all_arms_fn!(self, extra_prop,) + } + + fn satisfy(&self, sat: &S) -> Satisfaction + where + Pk: ToPublicKey, + S: Satisfier, + { + all_arms_fn!(self, satisfy, sat,) + } + + fn dissatisfy(&self, sat: &S) -> Satisfaction + where + Pk: ToPublicKey, + S: Satisfier, + { + all_arms_fn!(self, dissatisfy, sat,) + } + + fn real_for_each_key<'a, F: FnMut(ForEach<'a, Pk>) -> bool>(&'a self, pred: &mut F) -> bool + where + Pk: 'a, + Pk::Hash: 'a, + { + all_arms_fn!(self, real_for_each_key, pred,) + } + + fn push_to_builder(&self, builder: Builder) -> Builder + where + Pk: ToPublicKey, + { + all_arms_fn!(self, push_to_builder, builder,) + } + + fn script_size(&self) -> usize { + all_arms_fn!(self, script_size,) + } + + fn from_token_iter(tokens: &mut TokenIter) -> Result { + try_from_arms!(from_token_iter, tokens,) + } + + fn from_name_tree(name: &str, children: &[Tree]) -> Result { + try_from_arms!(from_name_tree, name, children,) + } + + fn evaluate<'intp, 'txin>( + &'intp self, + stack: &mut Stack<'txin>, + ) -> Option> { + all_arms_fn!(self, evaluate, stack,) + } +} + +impl fmt::Display for AllExt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + AllExt::VerEq(v) => v.fmt(f), + AllExt::OutputsPref(p) => p.fmt(f), + } + } +} + +impl Liftable for AllExt { + fn lift(&self) -> Result, Error> { + match self { + AllExt::VerEq(v) => v.lift(), + AllExt::OutputsPref(p) => p.lift(), + } + } +} + +impl TranslatePk for AllExt { + type Output = AllExt; + + fn translate_pk( + &self, + translatefpk: Fpk, + translatefpkh: Fpkh, + ) -> Result + where + Fpk: FnMut(&P) -> Result, + Fpkh: FnMut(&P::Hash) -> Result, + Q: MiniscriptKey, + { + let ext = match self { + AllExt::VerEq(v) => AllExt::VerEq(v.translate_pk(translatefpk, translatefpkh)?), + AllExt::OutputsPref(p) => { + AllExt::OutputsPref(p.translate_pk(translatefpk, translatefpkh)?) + } + }; + Ok(ext) + } +} diff --git a/src/extensions/outputs_pref.rs b/src/extensions/outputs_pref.rs new file mode 100644 index 00000000..db32d0c3 --- /dev/null +++ b/src/extensions/outputs_pref.rs @@ -0,0 +1,339 @@ +//! Miniscript extension: outputs_pref +//! Note that this fragment is only supported for Segwit context +//! You are most likely looking for taproot direct tx introspection + +use std::fmt; + +use MiniscriptKey; + +use elements::hashes::hex::FromHex; +use elements::hashes::hex::ToHex; +use elements::hashes::sha256d; +use elements::hashes::Hash; +use elements::{self, encode::serialize}; +use Extension; +use ForEach; + +use miniscript::context::ScriptContextError; +use ToPublicKey; +use TranslatePk; +use { + descriptor::CovError, + expression, interpreter, + miniscript::{ + astelem::StackCtxOperations, + lex::{Token as Tk, TokenIter}, + limits::{MAX_SCRIPT_ELEMENT_SIZE, MAX_STANDARD_P2WSH_STACK_ITEM_SIZE}, + satisfy::{Satisfaction, Witness}, + types::{ + extra_props::TimeLockInfo, Base, Correctness, Dissat, ExtData, Input, Malleability, + }, + }, + policy::{self, Liftable}, + Error, Satisfier, +}; + +/// Prefix is initally encoded in the script pubkey +/// User provides a suffix such that hash of (prefix || suffix) +/// is equal to hashOutputs +/// Since, there is a policy restriction that initial pushes must be +/// only 80 bytes, we need user to provide suffix in separate items +/// There can be atmost 7 cats, because the script element must be less +/// than 520 bytes total in order to compute an hash256 on it. +/// Even if the witness does not require 7 pushes, the user should push +/// 7 elements with possibly empty values. +/// +/// CAT CAT CAT CAT CAT CAT SWAP CAT /*Now we hashoutputs on stack */ +/// HASH256 +/// DEPTH <10> SUB PICK EQUALVERIFY +#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone)] +pub struct OutputsPref { + /// the version of transaction + pub pref: Vec, +} + +impl fmt::Display for OutputsPref { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "outputs_pref({})", self.pref.to_hex()) + } +} + +impl Liftable for OutputsPref { + fn lift(&self) -> Result, Error> { + Err(Error::CovError(CovError::CovenantLift)) + } +} + +impl Extension for OutputsPref { + fn real_for_each_key<'a, F>(&'a self, _pred: &mut F) -> bool + where + Pk: 'a, + Pk::Hash: 'a, + F: FnMut(ForEach<'a, Pk>) -> bool, + { + true + } + + fn segwit_ctx_checks(&self) -> Result<(), ScriptContextError> { + if self.pref.len() > MAX_SCRIPT_ELEMENT_SIZE { + Err(ScriptContextError::CovElementSizeExceeded) + } else { + Ok(()) + } + } + + fn corr_prop(&self) -> Correctness { + Correctness { + base: Base::B, + input: Input::Any, // 7 outputs + dissatisfiable: true, // Any 7 elements that don't cat + unit: true, + } + } + + fn mall_prop(&self) -> Malleability { + Malleability { + dissat: Dissat::Unknown, + safe: false, + non_malleable: true, + } + } + + fn extra_prop(&self) -> ExtData { + // Assume txouts fill out all the 520 bytes + let max_wit_sz = MAX_SCRIPT_ELEMENT_SIZE - self.pref.len(); + ExtData { + pk_cost: 8 + self.pref.len() + 1 + 6, // See script_size() in astelem.rs + has_free_verify: true, + ops_count_static: 13, + ops_count_sat: Some(13), + ops_count_nsat: Some(13), + stack_elem_count_sat: Some(7), + stack_elem_count_dissat: Some(7), + max_sat_size: Some((max_wit_sz, max_wit_sz)), + max_dissat_size: Some((0, 0)), // all empty should dissatisfy + timelock_info: TimeLockInfo::default(), + } + } + + fn satisfy(&self, sat: &S) -> Satisfaction + where + Pk: ToPublicKey, + S: Satisfier, + { + let wit = match sat.lookup_outputs() { + Some(outs) => { + let mut ser_out = Vec::new(); + let num_wit_elems = + MAX_SCRIPT_ELEMENT_SIZE / MAX_STANDARD_P2WSH_STACK_ITEM_SIZE + 1; + let mut witness = Vec::with_capacity(num_wit_elems); + for out in outs { + ser_out.extend(serialize(out)); + } + // We need less than 520 bytes of serialized hashoutputs + // in order to compute hash256 inside script + if ser_out.len() > MAX_SCRIPT_ELEMENT_SIZE { + Witness::Impossible + } else if ser_out.starts_with(&self.pref) { + let mut iter = ser_out.into_iter().skip(self.pref.len()).peekable(); + + while iter.peek().is_some() { + let chk_size = MAX_STANDARD_P2WSH_STACK_ITEM_SIZE; + let chunk: Vec = iter.by_ref().take(chk_size).collect(); + witness.push(chunk); + } + // Append empty elems to make for extra cats + // in the spk + while witness.len() < num_wit_elems { + witness.push(vec![]); + } + Witness::Stack(witness) + } else { + Witness::Impossible + } + } + // Note the unavailable instead of impossible because we don't know + // the hashoutputs yet + None => Witness::Unavailable, + }; + Satisfaction { + stack: wit, + has_sig: false, + } + } + + fn dissatisfy(&self, sat: &S) -> Satisfaction + where + Pk: ToPublicKey, + S: Satisfier, + { + let wit = match sat.lookup_outputs() { + Some(outs) => { + let mut ser_out = Vec::new(); + for out in outs { + ser_out.extend(serialize(out)); + } + let num_wit_elems = MAX_SCRIPT_ELEMENT_SIZE / MAX_STANDARD_P2WSH_STACK_ITEM_SIZE; + let mut witness = Vec::with_capacity(num_wit_elems); + if self.pref != ser_out.as_slice() { + while witness.len() < num_wit_elems { + witness.push(vec![]); + } + Witness::Stack(witness) + } else if self.pref.len() != MAX_SCRIPT_ELEMENT_SIZE { + // Case when prefix == ser_out and it is possible + // to add more witness + witness.push(vec![1]); + while witness.len() < num_wit_elems { + witness.push(vec![]); + } + Witness::Stack(witness) + } else { + // case when pref == ser_out and len of both is 520 + Witness::Impossible + } + } + // Note the unavailable instead of impossible because we don't know + // the hashoutputs yet + None => Witness::Unavailable, + }; + Satisfaction { + stack: wit, + has_sig: false, + } + } + + fn push_to_builder(&self, builder: elements::script::Builder) -> elements::script::Builder + where + Pk: ToPublicKey, + { + builder.check_item_pref(4, &self.pref) + } + + fn script_size(&self) -> usize { + // CAT CAT CAT CAT CAT CAT SWAP CAT /*Now we hashoutputs on stack */ + // HASH256 DEPTH <10> SUB PICK EQUAL + 8 + self.pref.len() + 1 /* line1 opcodes + pref.push */ + + 6 /* line 2 */ + } + + fn from_token_iter(tokens: &mut TokenIter) -> Result { + let outputs_pref = { + let sl = tokens.peek_slice(15).ok_or(())?; + if let Tk::Push(pref) = &sl[6] { + if sl[0] == Tk::Cat + && sl[1] == Tk::Cat + && sl[2] == Tk::Cat + && sl[3] == Tk::Cat + && sl[4] == Tk::Cat + && sl[5] == Tk::Cat + && sl[7] == Tk::Swap + && sl[8] == Tk::Cat + && sl[9] == Tk::Hash256 + && sl[11] == Tk::Num(4) + && sl[12] == Tk::Sub + && sl[13] == Tk::Pick + && sl[14] == Tk::Equal + { + Self { pref: pref.clone() } + } else { + return Err(()); + } + } else { + return Err(()); + } + }; + tokens.advance(15).expect("Size checked previously"); + Ok(outputs_pref) + } + + fn from_name_tree(name: &str, children: &[expression::Tree]) -> Result { + if children.len() == 1 && name == "outputs_pref" { + let pref = expression::terminal(&children[0], Vec::::from_hex).map_err(|_| ())?; + Ok(Self { pref }) + } else { + // Correct error handling while parsing fromtree + Err(()) + } + } + + fn evaluate<'intp, 'txin>( + &'intp self, + stack: &mut interpreter::Stack<'txin>, + ) -> Option> { + // Hash Outputs is at index 3 + let hash_outputs = stack[3]; + if let Err(e) = hash_outputs.try_push() { + return Some(Err(e)); + } + // Maximum number of suffix elements + let max_elems = MAX_SCRIPT_ELEMENT_SIZE / MAX_STANDARD_P2WSH_STACK_ITEM_SIZE + 1; + let hash_outputs = hash_outputs.as_push(); + if hash_outputs.len() == 32 { + // We want to cat the last 6 elements(5 cats) in suffix + if stack.len() < max_elems { + return Some(Err(interpreter::Error::UnexpectedStackEnd)); + } + let mut outputs_builder = Vec::new(); + outputs_builder.extend(&self.pref); + let len = stack.len(); + // Add the max_elems suffix elements + for i in 0..max_elems { + outputs_builder.extend(stack[len - max_elems + i].into_slice()); + } + // Pop the max_elems suffix elements + for _ in 0..max_elems { + stack.pop().unwrap(); + } + if sha256d::Hash::hash(&outputs_builder).as_inner() == hash_outputs { + stack.push(interpreter::Element::Satisfied); + Some(Ok(())) + } else { + None + } + } else { + Some(Err(interpreter::Error::CovWitnessSizeErr { + pos: 9, + expected: 32, + actual: hash_outputs.len(), + })) + } + } +} + +impl TranslatePk for OutputsPref { + type Output = OutputsPref; + + fn translate_pk( + &self, + mut _translatefpk: Fpk, + _translatefpkh: Fpkh, + ) -> Result + where + Fpk: FnMut(&P) -> Result, + Fpkh: FnMut(&P::Hash) -> Result, + Q: MiniscriptKey, + { + Ok(Self { + pref: self.pref.clone(), + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use bitcoin::PublicKey; + use {Miniscript, Segwitv0}; + + #[test] + fn test_outputs_pref() { + type MsExtVer = Miniscript; + + let ms = MsExtVer::from_str_insane("outputs_pref(aa)").unwrap(); + // test string rtt + assert_eq!(ms.to_string(), "outputs_pref(aa)"); + // script rtt + assert_eq!(ms, MsExtVer::parse_insane(&ms.encode()).unwrap()) + } +} diff --git a/src/extensions/tx_ver.rs b/src/extensions/tx_ver.rs new file mode 100644 index 00000000..2ba387f8 --- /dev/null +++ b/src/extensions/tx_ver.rs @@ -0,0 +1,246 @@ +//! Miniscript extension: ver_eq +//! Note that this fragment is only supported for Segwit context +//! You are most likely looking for taproot direct tx introspection + +use std::fmt; + +use MiniscriptKey; + +use elements::{self, encode::serialize}; +use miniscript; +use Extension; +use ForEach; +use TranslatePk; + +use ToPublicKey; + +use util; + +use { + descriptor::CovError, + expression, interpreter, + miniscript::{ + astelem::StackCtxOperations, + lex::{Token as Tk, TokenIter}, + satisfy::{Satisfaction, Witness}, + types::{ + extra_props::TimeLockInfo, Base, Correctness, Dissat, ExtData, Input, Malleability, + }, + }, + policy::{self, Liftable}, + Error, Satisfier, +}; + +/// Version struct +/// `DEPTH <12> SUB PICK EQUAL` +#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)] +pub struct VerEq { + /// the version of transaction + pub n: u32, // it's i32 in bitcoin core +} + +impl fmt::Display for VerEq { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ver_eq({})", self.n) + } +} + +impl Liftable for VerEq { + fn lift(&self) -> Result, Error> { + Err(Error::CovError(CovError::CovenantLift)) + } +} + +impl Extension for VerEq { + fn real_for_each_key<'a, F>(&'a self, _pred: &mut F) -> bool + where + Pk: 'a, + Pk::Hash: 'a, + F: FnMut(ForEach<'a, Pk>) -> bool, + { + true + } + + fn segwit_ctx_checks(&self) -> Result<(), miniscript::context::ScriptContextError> { + Ok(()) + } + + fn corr_prop(&self) -> Correctness { + Correctness { + base: Base::B, + input: Input::Zero, + dissatisfiable: true, + unit: true, + } + } + + fn mall_prop(&self) -> Malleability { + Malleability { + dissat: Dissat::Unknown, // multi-dissat + safe: false, + non_malleable: true, + } + } + + fn extra_prop(&self) -> ExtData { + ExtData { + pk_cost: 4 + 1 + 1 + 4, // 4 opcodes, 1 push, (5) 4 byte push + has_free_verify: true, + ops_count_static: 4, + ops_count_sat: Some(4), + ops_count_nsat: Some(4), + stack_elem_count_sat: Some(0), + stack_elem_count_dissat: Some(0), + max_sat_size: Some((0, 0)), + max_dissat_size: Some((0, 0)), + timelock_info: TimeLockInfo::default(), + } + } + + fn satisfy(&self, sat: &S) -> Satisfaction + where + Pk: ToPublicKey, + S: Satisfier, + { + let wit = match sat.lookup_nversion() { + Some(k) => { + if k == self.n { + Witness::empty() + } else { + Witness::Impossible + } + } + // Note the unavailable instead of impossible because we don't know + // the version + None => Witness::Unavailable, + }; + Satisfaction { + stack: wit, + has_sig: false, + } + } + + fn dissatisfy(&self, sat: &S) -> Satisfaction + where + Pk: ToPublicKey, + S: Satisfier, + { + let wit = if let Some(k) = sat.lookup_nversion() { + if k == self.n { + Witness::Impossible + } else { + Witness::empty() + } + } else { + Witness::empty() + }; + Satisfaction { + stack: wit, + has_sig: false, + } + } + + fn push_to_builder(&self, builder: elements::script::Builder) -> elements::script::Builder + where + Pk: ToPublicKey, + { + builder.check_item_eq(12, &serialize(&self.n)) + } + + fn script_size(&self) -> usize { + 4 + 1 + 1 + 4 // opcodes + push opcodes + target size + } + + fn from_token_iter(tokens: &mut TokenIter) -> Result { + let ver = { + let sl = tokens.peek_slice(5).ok_or(())?; + if let Tk::PickPush4(ver) = sl[3] { + if sl[0] == Tk::Depth + && sl[1] == Tk::Num(12) + && sl[2] == Tk::Sub + && sl[4] == Tk::Equal + { + Self { n: ver } + } else { + return Err(()); + } + } else { + return Err(()); + } + }; + tokens.advance(5).expect("Size checked previously"); + Ok(ver) + } + + fn from_name_tree(name: &str, children: &[expression::Tree]) -> Result { + if children.len() == 1 && name == "ver_eq" { + let n = expression::terminal(&children[0], expression::parse_num).map_err(|_| ())?; + Ok(Self { n }) + } else { + // Correct error handling while parsing fromtree + Err(()) + } + } + + fn evaluate<'intp, 'txin>( + &'intp self, + stack: &mut interpreter::Stack<'txin>, + ) -> Option> { + // Version is at index 11 + let ver = stack[11]; + if let Err(e) = ver.try_push() { + return Some(Err(e)); + } + let elem = ver.as_push(); + if elem.len() == 4 { + let wit_ver = util::slice_to_u32_le(elem); + if wit_ver == self.n { + stack.push(interpreter::Element::Satisfied); + Some(Ok(())) + } else { + None + } + } else { + Some(Err(interpreter::Error::CovWitnessSizeErr { + pos: 1, + expected: 4, + actual: elem.len(), + })) + } + } +} + +impl TranslatePk for VerEq { + type Output = VerEq; + + fn translate_pk( + &self, + mut _translatefpk: Fpk, + _translatefpkh: Fpkh, + ) -> Result + where + Fpk: FnMut(&P) -> Result, + Fpkh: FnMut(&P::Hash) -> Result, + Q: MiniscriptKey, + { + Ok(Self { n: self.n }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use bitcoin::PublicKey; + use {Miniscript, Segwitv0}; + + #[test] + fn test_ver_eq() { + type MsExtVer = Miniscript; + + let ms = MsExtVer::from_str_insane("ver_eq(8)").unwrap(); + // test string rtt + assert_eq!(ms.to_string(), "ver_eq(8)"); + // script rtt + assert_eq!(ms, MsExtVer::parse_insane(&ms.encode()).unwrap()) + } +} diff --git a/src/interpreter/inner.rs b/src/interpreter/inner.rs index 54222ad4..7b36e899 100644 --- a/src/interpreter/inner.rs +++ b/src/interpreter/inner.rs @@ -16,6 +16,8 @@ use bitcoin; use elements::hashes::{hash160, sha256, Hash}; use elements::{self, script}; +use Extension; + use super::{stack, Error, Stack}; use descriptor::{CovOperations, CovenantDescriptor}; use miniscript::context::NoChecks; @@ -47,9 +49,9 @@ fn pk_from_stackelem<'a>( pk_from_slice(slice, require_compressed) } -fn script_from_stackelem<'a>( +fn script_from_stackelem<'a, Ext: Extension>( elem: &stack::Element<'a>, -) -> Result, Error> { +) -> Result, Error> { match *elem { stack::Element::Push(sl) => { Miniscript::parse_insane(&elements::Script::from(sl.to_owned())).map_err(Error::from) @@ -63,9 +65,12 @@ fn script_from_stackelem<'a>( // Try to parse covenant components from witness script // stack element -fn cov_components_from_stackelem<'a>( +fn cov_components_from_stackelem<'a, Ext: Extension>( elem: &stack::Element<'a>, -) -> Option<(bitcoin::PublicKey, Miniscript)> { +) -> Option<( + bitcoin::PublicKey, + Miniscript, +)> { match *elem { stack::Element::Push(sl) => { CovenantDescriptor::parse_cov_components(&elements::Script::from(sl.to_owned())).ok() @@ -94,20 +99,21 @@ pub enum ScriptType { /// Structure representing a script under evaluation as a Miniscript #[derive(Clone, PartialEq, Eq, Debug)] -pub enum Inner { +pub enum Inner> { /// The script being evaluated is a simple public key check (pay-to-pk, /// pay-to-pkhash or pay-to-witness-pkhash) PublicKey(bitcoin::PublicKey, PubkeyType), - /// The script being evaluated is an actual script - Script(Miniscript, ScriptType), + /// The script being evaluated is an actual script without any extensions + Script(Miniscript, ScriptType), /// The Covenant Miniscript /// Only Witnessv0 scripts are supported for now CovScript( bitcoin::PublicKey, - Miniscript, + Miniscript, // Add scriptType when we support additional things here // ScriptType, ), + // todo: add extensions support as explicit enum } // The `Script` returned by this method is always generated/cloned ... when @@ -116,11 +122,11 @@ pub enum Inner { // possible /// Parses an `Inner` and appropriate `Stack` from completed transaction data, /// as well as the script that should be used as a scriptCode in a sighash -pub fn from_txdata<'txin>( +pub fn from_txdata<'txin, Ext: Extension>( spk: &elements::Script, script_sig: &'txin elements::Script, witness: &'txin [Vec], -) -> Result<(Inner, Stack<'txin>, elements::Script), Error> { +) -> Result<(Inner, Stack<'txin>, elements::Script), Error> { let mut ssig_stack: Stack = script_sig .instructions_minimal() .map(stack::Element::from_instruction) @@ -321,6 +327,7 @@ mod tests { use elements::script; use elements::{self, Script}; use std::str::FromStr; + use AllExt; struct KeyTestData { pk_spk: elements::Script, @@ -407,28 +414,28 @@ mod tests { // Compressed pk, empty scriptsig let (inner, stack, script_code) = - from_txdata(&comp.pk_spk, &blank_script, &[]).expect("parse txdata"); + from_txdata::(&comp.pk_spk, &blank_script, &[]).expect("parse txdata"); assert_eq!(inner, Inner::PublicKey(fixed.pk_comp, PubkeyType::Pk)); assert_eq!(stack, Stack::from(vec![])); assert_eq!(script_code, comp.pk_spk); // Uncompressed pk, empty scriptsig let (inner, stack, script_code) = - from_txdata(&uncomp.pk_spk, &blank_script, &[]).expect("parse txdata"); + from_txdata::(&uncomp.pk_spk, &blank_script, &[]).expect("parse txdata"); assert_eq!(inner, Inner::PublicKey(fixed.pk_uncomp, PubkeyType::Pk)); assert_eq!(stack, Stack::from(vec![])); assert_eq!(script_code, uncomp.pk_spk); // Compressed pk, correct scriptsig let (inner, stack, script_code) = - from_txdata(&comp.pk_spk, &comp.pk_sig, &[]).expect("parse txdata"); + from_txdata::(&comp.pk_spk, &comp.pk_sig, &[]).expect("parse txdata"); assert_eq!(inner, Inner::PublicKey(fixed.pk_comp, PubkeyType::Pk)); assert_eq!(stack, Stack::from(vec![comp.pk_sig[1..].into()])); assert_eq!(script_code, comp.pk_spk); // Uncompressed pk, correct scriptsig let (inner, stack, script_code) = - from_txdata(&uncomp.pk_spk, &uncomp.pk_sig, &[]).expect("parse txdata"); + from_txdata::(&uncomp.pk_spk, &uncomp.pk_sig, &[]).expect("parse txdata"); assert_eq!(inner, Inner::PublicKey(fixed.pk_uncomp, PubkeyType::Pk)); assert_eq!(stack, Stack::from(vec![uncomp.pk_sig[1..].into()])); assert_eq!(script_code, uncomp.pk_spk); @@ -437,18 +444,18 @@ mod tests { let mut spk = comp.pk_spk.to_bytes(); spk[1] = 5; let spk = elements::Script::from(spk); - let err = from_txdata(&spk, &elements::Script::new(), &[]).unwrap_err(); + let err = from_txdata::(&spk, &elements::Script::new(), &[]).unwrap_err(); assert_eq!(err.to_string(), "could not parse pubkey"); // Scriptpubkey has invalid script let mut spk = comp.pk_spk.to_bytes(); spk[0] = 100; let spk = elements::Script::from(spk); - let err = from_txdata(&spk, &elements::Script::new(), &[]).unwrap_err(); + let err = from_txdata::(&spk, &elements::Script::new(), &[]).unwrap_err(); assert_eq!(&err.to_string()[0..12], "parse error:"); // Witness is nonempty - let err = from_txdata(&comp.pk_spk, &comp.pk_sig, &[vec![]]).unwrap_err(); + let err = from_txdata::(&comp.pk_spk, &comp.pk_sig, &[vec![]]).unwrap_err(); assert_eq!(err.to_string(), "legacy spend had nonempty witness"); } @@ -459,41 +466,43 @@ mod tests { let uncomp = KeyTestData::from_key(fixed.pk_uncomp); // pkh, empty scriptsig; this time it errors out - let err = from_txdata(&comp.pkh_spk, &elements::Script::new(), &[]).unwrap_err(); + let err = from_txdata::(&comp.pkh_spk, &elements::Script::new(), &[]).unwrap_err(); assert_eq!(err.to_string(), "unexpected end of stack"); // pkh, wrong pubkey - let err = from_txdata(&comp.pkh_spk, &uncomp.pkh_sig_justkey, &[]).unwrap_err(); + let err = from_txdata::(&comp.pkh_spk, &uncomp.pkh_sig_justkey, &[]).unwrap_err(); assert_eq!(err.to_string(), "public key did not match scriptpubkey"); // pkh, right pubkey, no signature let (inner, stack, script_code) = - from_txdata(&comp.pkh_spk, &comp.pkh_sig_justkey, &[]).expect("parse txdata"); + from_txdata::(&comp.pkh_spk, &comp.pkh_sig_justkey, &[]).expect("parse txdata"); assert_eq!(inner, Inner::PublicKey(fixed.pk_comp, PubkeyType::Pkh)); assert_eq!(stack, Stack::from(vec![])); assert_eq!(script_code, comp.pkh_spk); let (inner, stack, script_code) = - from_txdata(&uncomp.pkh_spk, &uncomp.pkh_sig_justkey, &[]).expect("parse txdata"); + from_txdata::(&uncomp.pkh_spk, &uncomp.pkh_sig_justkey, &[]) + .expect("parse txdata"); assert_eq!(inner, Inner::PublicKey(fixed.pk_uncomp, PubkeyType::Pkh)); assert_eq!(stack, Stack::from(vec![])); assert_eq!(script_code, uncomp.pkh_spk); // pkh, right pubkey, signature let (inner, stack, script_code) = - from_txdata(&comp.pkh_spk, &comp.pkh_sig_justkey, &[]).expect("parse txdata"); + from_txdata::(&comp.pkh_spk, &comp.pkh_sig_justkey, &[]).expect("parse txdata"); assert_eq!(inner, Inner::PublicKey(fixed.pk_comp, PubkeyType::Pkh)); assert_eq!(stack, Stack::from(vec![])); assert_eq!(script_code, comp.pkh_spk); let (inner, stack, script_code) = - from_txdata(&uncomp.pkh_spk, &uncomp.pkh_sig_justkey, &[]).expect("parse txdata"); + from_txdata::(&uncomp.pkh_spk, &uncomp.pkh_sig_justkey, &[]) + .expect("parse txdata"); assert_eq!(inner, Inner::PublicKey(fixed.pk_uncomp, PubkeyType::Pkh)); assert_eq!(stack, Stack::from(vec![])); assert_eq!(script_code, uncomp.pkh_spk); // Witness is nonempty - let err = from_txdata(&comp.pkh_spk, &comp.pkh_sig, &[vec![]]).unwrap_err(); + let err = from_txdata::(&comp.pkh_spk, &comp.pkh_sig, &[vec![]]).unwrap_err(); assert_eq!(err.to_string(), "legacy spend had nonempty witness"); } @@ -505,20 +514,20 @@ mod tests { let blank_script = elements::Script::new(); // wpkh, empty witness; this time it errors out - let err = from_txdata(&comp.wpkh_spk, &blank_script, &[]).unwrap_err(); + let err = from_txdata::(&comp.wpkh_spk, &blank_script, &[]).unwrap_err(); assert_eq!(err.to_string(), "unexpected end of stack"); // wpkh, uncompressed pubkey - let err = - from_txdata(&comp.wpkh_spk, &blank_script, &uncomp.wpkh_stack_justkey).unwrap_err(); + let err = from_txdata::(&comp.wpkh_spk, &blank_script, &uncomp.wpkh_stack_justkey) + .unwrap_err(); assert_eq!( err.to_string(), "uncompressed pubkey in non-legacy descriptor" ); // wpkh, wrong pubkey - let err = - from_txdata(&uncomp.wpkh_spk, &blank_script, &comp.wpkh_stack_justkey).unwrap_err(); + let err = from_txdata::(&uncomp.wpkh_spk, &blank_script, &comp.wpkh_stack_justkey) + .unwrap_err(); assert_eq!( err.to_string(), "public key did not match scriptpubkey (segwit v0)" @@ -526,7 +535,7 @@ mod tests { // wpkh, right pubkey, no signature let (inner, stack, script_code) = - from_txdata(&comp.wpkh_spk, &blank_script, &comp.wpkh_stack_justkey) + from_txdata::(&comp.wpkh_spk, &blank_script, &comp.wpkh_stack_justkey) .expect("parse txdata"); assert_eq!(inner, Inner::PublicKey(fixed.pk_comp, PubkeyType::Wpkh)); assert_eq!(stack, Stack::from(vec![])); @@ -534,13 +543,15 @@ mod tests { // wpkh, right pubkey, signature let (inner, stack, script_code) = - from_txdata(&comp.wpkh_spk, &blank_script, &comp.wpkh_stack).expect("parse txdata"); + from_txdata::(&comp.wpkh_spk, &blank_script, &comp.wpkh_stack) + .expect("parse txdata"); assert_eq!(inner, Inner::PublicKey(fixed.pk_comp, PubkeyType::Wpkh)); assert_eq!(stack, Stack::from(vec![comp.wpkh_stack[0][..].into()])); assert_eq!(script_code, comp.pkh_spk); // Scriptsig is nonempty - let err = from_txdata(&comp.wpkh_spk, &comp.pk_sig, &comp.wpkh_stack_justkey).unwrap_err(); + let err = from_txdata::(&comp.wpkh_spk, &comp.pk_sig, &comp.wpkh_stack_justkey) + .unwrap_err(); assert_eq!(err.to_string(), "segwit spend had nonempty scriptsig"); } @@ -552,15 +563,16 @@ mod tests { let blank_script = elements::Script::new(); // sh_wpkh, missing witness or scriptsig - let err = from_txdata(&comp.sh_wpkh_spk, &blank_script, &[]).unwrap_err(); + let err = from_txdata::(&comp.sh_wpkh_spk, &blank_script, &[]).unwrap_err(); assert_eq!(err.to_string(), "unexpected end of stack"); - let err = from_txdata(&comp.sh_wpkh_spk, &comp.sh_wpkh_sig, &[]).unwrap_err(); + let err = from_txdata::(&comp.sh_wpkh_spk, &comp.sh_wpkh_sig, &[]).unwrap_err(); assert_eq!(err.to_string(), "unexpected end of stack"); - let err = from_txdata(&comp.sh_wpkh_spk, &blank_script, &comp.sh_wpkh_stack).unwrap_err(); + let err = from_txdata::(&comp.sh_wpkh_spk, &blank_script, &comp.sh_wpkh_stack) + .unwrap_err(); assert_eq!(err.to_string(), "unexpected end of stack"); // sh_wpkh, uncompressed pubkey - let err = from_txdata( + let err = from_txdata::( &uncomp.sh_wpkh_spk, &uncomp.sh_wpkh_sig, &uncomp.sh_wpkh_stack_justkey, @@ -572,7 +584,7 @@ mod tests { ); // sh_wpkh, wrong redeem script for scriptpubkey - let err = from_txdata( + let err = from_txdata::( &uncomp.sh_wpkh_spk, &comp.sh_wpkh_sig, &comp.sh_wpkh_stack_justkey, @@ -581,7 +593,7 @@ mod tests { assert_eq!(err.to_string(), "redeem script did not match scriptpubkey",); // sh_wpkh, wrong redeem script for witness script - let err = from_txdata( + let err = from_txdata::( &uncomp.sh_wpkh_spk, &uncomp.sh_wpkh_sig, &comp.sh_wpkh_stack_justkey, @@ -590,7 +602,7 @@ mod tests { assert_eq!(err.to_string(), "witness script did not match scriptpubkey",); // sh_wpkh, right pubkey, no signature - let (inner, stack, script_code) = from_txdata( + let (inner, stack, script_code) = from_txdata::( &comp.sh_wpkh_spk, &comp.sh_wpkh_sig, &comp.sh_wpkh_stack_justkey, @@ -602,7 +614,7 @@ mod tests { // sh_wpkh, right pubkey, signature let (inner, stack, script_code) = - from_txdata(&comp.sh_wpkh_spk, &comp.sh_wpkh_sig, &comp.sh_wpkh_stack) + from_txdata::(&comp.sh_wpkh_spk, &comp.sh_wpkh_sig, &comp.sh_wpkh_stack) .expect("parse txdata"); assert_eq!(inner, Inner::PublicKey(fixed.pk_comp, PubkeyType::ShWpkh)); assert_eq!(stack, Stack::from(vec![comp.sh_wpkh_stack[0][..].into()])); @@ -613,7 +625,7 @@ mod tests { fn script_bare() { let preimage = b"12345678----____12345678----____"; let hash = hash160::Hash::hash(&preimage[..]); - let miniscript: ::Miniscript = + let miniscript: ::Miniscript = ::Miniscript::from_str_insane(&format!("hash160({})", hash)).unwrap(); let spk = miniscript.encode(); @@ -621,16 +633,16 @@ mod tests { // bare script has no validity requirements beyond being a sane script let (inner, stack, script_code) = - from_txdata(&spk, &blank_script, &[]).expect("parse txdata"); + from_txdata::(&spk, &blank_script, &[]).expect("parse txdata"); assert_eq!(inner, Inner::Script(miniscript, ScriptType::Bare)); assert_eq!(stack, Stack::from(vec![])); assert_eq!(script_code, spk); - let err = from_txdata(&blank_script, &blank_script, &[]).unwrap_err(); + let err = from_txdata::(&blank_script, &blank_script, &[]).unwrap_err(); assert_eq!(&err.to_string()[0..12], "parse error:"); // nonempty witness - let err = from_txdata(&spk, &blank_script, &[vec![]]).unwrap_err(); + let err = from_txdata::(&spk, &blank_script, &[vec![]]).unwrap_err(); assert_eq!(&err.to_string(), "legacy spend had nonempty witness"); } @@ -638,7 +650,7 @@ mod tests { fn script_sh() { let preimage = b"12345678----____12345678----____"; let hash = hash160::Hash::hash(&preimage[..]); - let miniscript: ::Miniscript = + let miniscript: ::Miniscript = ::Miniscript::from_str_insane(&format!("hash160({})", hash)).unwrap(); let redeem_script = miniscript.encode(); @@ -651,22 +663,22 @@ mod tests { let blank_script = elements::Script::new(); // sh without scriptsig - let err = from_txdata(&spk, &blank_script, &[]).unwrap_err(); + let err = from_txdata::(&spk, &blank_script, &[]).unwrap_err(); assert_eq!(&err.to_string(), "unexpected end of stack"); // with incorrect scriptsig - let err = from_txdata(&spk, &spk, &[]).unwrap_err(); + let err = from_txdata::(&spk, &spk, &[]).unwrap_err(); assert_eq!(&err.to_string(), "expected push in script"); // with correct scriptsig let (inner, stack, script_code) = - from_txdata(&spk, &script_sig, &[]).expect("parse txdata"); + from_txdata::(&spk, &script_sig, &[]).expect("parse txdata"); assert_eq!(inner, Inner::Script(miniscript, ScriptType::Sh)); assert_eq!(stack, Stack::from(vec![])); assert_eq!(script_code, redeem_script); // nonempty witness - let err = from_txdata(&spk, &script_sig, &[vec![]]).unwrap_err(); + let err = from_txdata::(&spk, &script_sig, &[vec![]]).unwrap_err(); assert_eq!(&err.to_string(), "legacy spend had nonempty witness"); } @@ -674,7 +686,7 @@ mod tests { fn script_wsh() { let preimage = b"12345678----____12345678----____"; let hash = hash160::Hash::hash(&preimage[..]); - let miniscript: ::Miniscript = + let miniscript: ::Miniscript = ::Miniscript::from_str_insane(&format!("hash160({})", hash)).unwrap(); let witness_script = miniscript.encode(); @@ -685,16 +697,16 @@ mod tests { let blank_script = elements::Script::new(); // wsh without witness - let err = from_txdata(&spk, &blank_script, &[]).unwrap_err(); + let err = from_txdata::(&spk, &blank_script, &[]).unwrap_err(); assert_eq!(&err.to_string(), "unexpected end of stack"); // with incorrect witness - let err = from_txdata(&spk, &blank_script, &[spk.to_bytes()]).unwrap_err(); + let err = from_txdata::(&spk, &blank_script, &[spk.to_bytes()]).unwrap_err(); assert_eq!(&err.to_string()[0..12], "parse error:"); // with correct witness let (inner, stack, script_code) = - from_txdata(&spk, &blank_script, &wit_stack).expect("parse txdata"); + from_txdata::(&spk, &blank_script, &wit_stack).expect("parse txdata"); assert_eq!(inner, Inner::Script(miniscript, ScriptType::Wsh)); assert_eq!(stack, Stack::from(vec![])); assert_eq!(script_code, witness_script); @@ -703,7 +715,7 @@ mod tests { let script_sig = script::Builder::new() .push_slice(&witness_script[..]) .into_script(); - let err = from_txdata(&spk, &script_sig, &wit_stack).unwrap_err(); + let err = from_txdata::(&spk, &script_sig, &wit_stack).unwrap_err(); assert_eq!(&err.to_string(), "segwit spend had nonempty scriptsig"); } @@ -711,7 +723,7 @@ mod tests { fn script_sh_wsh() { let preimage = b"12345678----____12345678----____"; let hash = hash160::Hash::hash(&preimage[..]); - let miniscript: ::Miniscript = + let miniscript: ::Miniscript = ::Miniscript::from_str_insane(&format!("hash160({})", hash)).unwrap(); let witness_script = miniscript.encode(); @@ -728,24 +740,24 @@ mod tests { let spk = Script::new_p2sh(&rs_hash); // shwsh without witness or scriptsig - let err = from_txdata(&spk, &blank_script, &[]).unwrap_err(); + let err = from_txdata::(&spk, &blank_script, &[]).unwrap_err(); assert_eq!(&err.to_string(), "unexpected end of stack"); - let err = from_txdata(&spk, &script_sig, &[]).unwrap_err(); + let err = from_txdata::(&spk, &script_sig, &[]).unwrap_err(); assert_eq!(&err.to_string(), "unexpected end of stack"); - let err = from_txdata(&spk, &blank_script, &wit_stack).unwrap_err(); + let err = from_txdata::(&spk, &blank_script, &wit_stack).unwrap_err(); assert_eq!(&err.to_string(), "unexpected end of stack"); // with incorrect witness - let err = from_txdata(&spk, &script_sig, &[spk.to_bytes()]).unwrap_err(); + let err = from_txdata::(&spk, &script_sig, &[spk.to_bytes()]).unwrap_err(); assert_eq!(&err.to_string()[0..12], "parse error:"); // with incorrect scriptsig - let err = from_txdata(&spk, &redeem_script, &wit_stack).unwrap_err(); + let err = from_txdata::(&spk, &redeem_script, &wit_stack).unwrap_err(); assert_eq!(&err.to_string(), "redeem script did not match scriptpubkey"); // with correct witness let (inner, stack, script_code) = - from_txdata(&spk, &script_sig, &wit_stack).expect("parse txdata"); + from_txdata::(&spk, &script_sig, &wit_stack).expect("parse txdata"); assert_eq!(inner, Inner::Script(miniscript, ScriptType::ShWsh)); assert_eq!(stack, Stack::from(vec![])); assert_eq!(script_code, witness_script); diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 22cf6f65..45bc2402 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -19,7 +19,7 @@ //! assuming that the spent coin was descriptor controlled. //! -use bitcoin; +use bitcoin::PublicKey; use elements::{self, secp256k1_zkp, SigHash}; use elements::{confidential, sighash}; use elements::{ @@ -37,31 +37,55 @@ mod error; mod inner; mod stack; +use {AllExt, Extension}; + pub use self::error::Error; -use self::stack::Stack; +pub use self::stack::{Element, Stack}; /// An iterable Miniscript-structured representation of the spending of a coin -pub struct Interpreter<'txin> { - inner: inner::Inner, +pub struct Interpreter<'txin, Ext: Extension> { + inner: inner::Inner, stack: Stack<'txin>, script_code: elements::Script, age: u32, height: u32, } -impl<'txin> Interpreter<'txin> { +impl<'txin> Interpreter<'txin, AllExt> { /// Constructs an interpreter from the data of a spending transaction /// /// Accepts a signature-validating function. If you are willing to trust /// that ECSDA signatures are valid, this can be set to the constant true /// function; otherwise, it should be a closure containing a sighash and /// secp context, which can actually verify a given signature. + /// For downstream cursom implementations of [`Extension`], use [`Interpreter::from_txdata_ext`] pub fn from_txdata( spk: &elements::Script, script_sig: &'txin elements::Script, witness: &'txin [Vec], age: u32, height: u32, + ) -> Result { + Interpreter::from_txdata_ext(spk, script_sig, witness, age, height) + } +} + +impl<'txin, Ext> Interpreter<'txin, Ext> +where + Ext: Extension, +{ + /// Constructs an interpreter from the data of a spending transaction + /// + /// Accepts a signature-validating function. If you are willing to trust + /// that ECSDA signatures are valid, this can be set to the constant true + /// function; otherwise, it should be a closure containing a sighash and + /// secp context, which can actually verify a given signature. + pub fn from_txdata_ext( + spk: &elements::Script, + script_sig: &'txin elements::Script, + witness: &'txin [Vec], + age: u32, + height: u32, ) -> Result { let (inner, stack, script_code) = inner::from_txdata(spk, script_sig, witness)?; Ok(Interpreter { @@ -86,10 +110,11 @@ impl<'txin> Interpreter<'txin> { /// /// Running the iterator through will consume the internal stack of the /// `Iterpreter`, and it should not be used again after this. - pub fn iter<'iter, F: FnMut(&bitcoin::PublicKey, ElementsSig) -> bool>( - &'iter mut self, - verify_sig: F, - ) -> Iter<'txin, 'iter, F> { + pub fn iter<'iter, F>(&'iter mut self, verify_sig: F) -> Iter<'txin, 'iter, Ext, F> + where + Ext: Extension, + F: FnMut(&PublicKey, ElementsSig) -> bool, + { Iter { verify_sig: verify_sig, public_key: if let inner::Inner::PublicKey(ref pk, _) = self.inner { @@ -172,7 +197,7 @@ impl<'txin> Interpreter<'txin> { /// This may not represent the original descriptor used to produce the transaction, /// since it cannot distinguish between sorted and unsorted multisigs (and anyway /// it can only see the final keys, keyorigin info is lost in serializing to Bitcoin). - pub fn inferred_descriptor(&self) -> Result, ::Error> { + pub fn inferred_descriptor(&self) -> Result, ::Error> { use std::str::FromStr; Descriptor::from_str(&self.inferred_descriptor_string()) } @@ -208,7 +233,7 @@ impl<'txin> Interpreter<'txin> { unsigned_tx: &'a elements::Transaction, input_idx: usize, amount: confidential::Value, - ) -> impl Fn(&bitcoin::PublicKey, ElementsSig) -> bool + 'a { + ) -> impl Fn(&PublicKey, ElementsSig) -> bool + 'a { // Precompute all sighash types because the borrowck doesn't like us // pulling self into the closure let sighashes = [ @@ -240,7 +265,7 @@ impl<'txin> Interpreter<'txin> { ), ]; - move |pk: &bitcoin::PublicKey, (sig, sighash_type)| { + move |pk: &PublicKey, (sig, sighash_type)| { // This is an awkward way to do this lookup, but it lets us do exhaustiveness // checking in case future rust-bitcoin versions add new sighash types let sighash = match sighash_type { @@ -273,11 +298,11 @@ pub enum HashLockType<'intp> { /// 'intp represents the lifetime of descriptor and `stack represents /// the lifetime of witness #[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum SatisfiedConstraint<'intp, 'txin> { +pub enum SatisfiedConstraint<'intp, 'txin, Ext: 'intp + Extension> { ///Public key and corresponding signature PublicKey { /// The bitcoin key - key: &'intp bitcoin::PublicKey, + key: &'intp PublicKey, /// corresponding signature sig: secp256k1_zkp::Signature, }, @@ -286,7 +311,7 @@ pub enum SatisfiedConstraint<'intp, 'txin> { /// The pubkey hash keyhash: &'intp hash160::Hash, /// Corresponding public key - key: bitcoin::PublicKey, + key: PublicKey, /// Corresponding signature for the hash sig: secp256k1_zkp::Signature, }, @@ -321,6 +346,12 @@ pub enum SatisfiedConstraint<'intp, 'txin> { /// The version of transaction pref: &'intp [u8], }, + + /// Extension Interpreter + Ext { + /// Extension + ext: &'intp Ext, + }, } ///This is used by the interpreter to know which evaluation state a AstemElem is. @@ -329,9 +360,12 @@ pub enum SatisfiedConstraint<'intp, 'txin> { ///the top of the stack, we need to decide whether to execute right child or not. ///This is also useful for wrappers and thresholds which push a value on the stack ///depending on evaluation of the children. -struct NodeEvaluationState<'intp> { +struct NodeEvaluationState<'intp, Ext> +where + Ext: 'intp + Extension, +{ ///The node which is being evaluated - node: &'intp Miniscript, + node: &'intp Miniscript, ///number of children evaluated n_evaluated: usize, ///number of children satisfied @@ -349,24 +383,29 @@ struct NodeEvaluationState<'intp> { /// /// In case the script is actually dissatisfied, this may return several values /// before ultimately returning an error. -pub struct Iter<'intp, 'txin: 'intp, F: FnMut(&bitcoin::PublicKey, ElementsSig) -> bool> { +pub struct Iter<'intp, 'txin: 'intp, Ext, F> +where + Ext: 'intp + Extension, + F: FnMut(&PublicKey, ElementsSig) -> bool, +{ verify_sig: F, - public_key: Option<&'intp bitcoin::PublicKey>, - state: Vec>, + public_key: Option<&'intp PublicKey>, + state: Vec>, stack: &'intp mut Stack<'txin>, age: u32, height: u32, - cov: Option<&'intp bitcoin::PublicKey>, + cov: Option<&'intp PublicKey>, has_errored: bool, } ///Iterator for Iter -impl<'intp, 'txin: 'intp, F> Iterator for Iter<'intp, 'txin, F> +impl<'intp, 'txin: 'intp, Ext, F> Iterator for Iter<'intp, 'txin, Ext, F> where NoChecks: ScriptContext, - F: FnMut(&bitcoin::PublicKey, ElementsSig) -> bool, + Ext: Extension, + F: FnMut(&PublicKey, ElementsSig) -> bool, { - type Item = Result, Error>; + type Item = Result, Error>; fn next(&mut self) -> Option { if self.has_errored { @@ -382,15 +421,16 @@ where } } -impl<'intp, 'txin: 'intp, F> Iter<'intp, 'txin, F> +impl<'intp, 'txin: 'intp, F, Ext> Iter<'intp, 'txin, Ext, F> where NoChecks: ScriptContext, - F: FnMut(&bitcoin::PublicKey, ElementsSig) -> bool, + F: FnMut(&PublicKey, ElementsSig) -> bool, + Ext: Extension, { /// Helper function to push a NodeEvaluationState on state stack fn push_evaluation_state( &mut self, - node: &'intp Miniscript, + node: &'intp Miniscript, n_evaluated: usize, n_satisfied: usize, ) -> () { @@ -402,7 +442,7 @@ where } /// Helper function to step the iterator - fn iter_next(&mut self) -> Option, Error>> { + fn iter_next(&mut self) -> Option, Error>> { while let Some(node_state) = self.state.pop() { //non-empty stack match node_state.node.node { @@ -480,20 +520,12 @@ where return res; } } - Terminal::Version(ref ver) => { - debug_assert_eq!(node_state.n_evaluated, 0); - debug_assert_eq!(node_state.n_satisfied, 0); - let res = self.stack.evaluate_ver(ver); - if res.is_some() { - return res; - } - } - Terminal::OutputsPref(ref pref) => { - debug_assert_eq!(node_state.n_evaluated, 0); - debug_assert_eq!(node_state.n_satisfied, 0); - let res = self.stack.evaluate_outputs_pref(pref); - if res.is_some() { - return res; + Terminal::Ext(ref ext) => { + let res = ext.evaluate(self.stack); + match res { + Some(Ok(())) => return Some(Ok(SatisfiedConstraint::Ext { ext: ext })), + Some(Err(e)) => return Some(Err(e)), + None => {} } } Terminal::Alt(ref sub) | Terminal::Swap(ref sub) | Terminal::Check(ref sub) => { @@ -862,11 +894,11 @@ where /// Helper function to verify serialized signature fn verify_sersig<'txin, F>( verify_sig: F, - pk: &bitcoin::PublicKey, + pk: &PublicKey, sigser: &[u8], ) -> Result where - F: FnOnce(&bitcoin::PublicKey, ElementsSig) -> bool, + F: FnOnce(&PublicKey, ElementsSig) -> bool, { if let Some((sighash_byte, sig)) = sigser.split_last() { let sighashtype = elements::SigHashType::from_u32(*sighash_byte as u32); @@ -884,6 +916,8 @@ where #[cfg(test)] mod tests { + use AllExt; + use super::*; use bitcoin; use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d, Hash}; @@ -940,8 +974,8 @@ mod tests { fn from_stack<'txin, 'elem, F>( verify_fn: F, stack: &'elem mut Stack<'txin>, - ms: &'elem Miniscript, - ) -> Iter<'elem, 'txin, F> + ms: &'elem Miniscript, + ) -> Iter<'elem, 'txin, AllExt, F> where F: FnMut(&bitcoin::PublicKey, ElementsSig) -> bool, { @@ -983,7 +1017,7 @@ mod tests { let mut stack = Stack::from(vec![stack::Element::Push(&der_sigs[0])]); let mut vfyfn = vfyfn_.clone(); // sigh rust 1.29... let constraints = from_stack(&mut vfyfn, &mut stack, &pk); - let pk_satisfied: Result, Error> = constraints.collect(); + let pk_satisfied: Result>, Error> = constraints.collect(); assert_eq!( pk_satisfied.unwrap(), vec![SatisfiedConstraint::PublicKey { @@ -996,7 +1030,7 @@ mod tests { let mut stack = Stack::from(vec![stack::Element::Dissatisfied]); let mut vfyfn = vfyfn_.clone(); // sigh rust 1.29... let constraints = from_stack(&mut vfyfn, &mut stack, &pk); - let pk_err: Result, Error> = constraints.collect(); + let pk_err: Result>, Error> = constraints.collect(); assert!(pk_err.is_err()); //Check Pkh @@ -1007,7 +1041,7 @@ mod tests { ]); let mut vfyfn = vfyfn_.clone(); // sigh rust 1.29... let constraints = from_stack(&mut vfyfn, &mut stack, &pkh); - let pkh_satisfied: Result, Error> = constraints.collect(); + let pkh_satisfied: Result>, Error> = constraints.collect(); assert_eq!( pkh_satisfied.unwrap(), vec![SatisfiedConstraint::PublicKeyHash { @@ -1021,7 +1055,8 @@ mod tests { let mut stack = Stack::from(vec![]); let mut vfyfn = vfyfn_.clone(); // sigh rust 1.29... let constraints = from_stack(&mut vfyfn, &mut stack, &after); - let after_satisfied: Result, Error> = constraints.collect(); + let after_satisfied: Result>, Error> = + constraints.collect(); assert_eq!( after_satisfied.unwrap(), vec![SatisfiedConstraint::AbsoluteTimeLock { time: &1000 }] @@ -1031,7 +1066,8 @@ mod tests { let mut stack = Stack::from(vec![]); let mut vfyfn = vfyfn_.clone(); // sigh rust 1.29... let constraints = from_stack(&mut vfyfn, &mut stack, &older); - let older_satisfied: Result, Error> = constraints.collect(); + let older_satisfied: Result>, Error> = + constraints.collect(); assert_eq!( older_satisfied.unwrap(), vec![SatisfiedConstraint::RelativeTimeLock { time: &1000 }] @@ -1041,7 +1077,8 @@ mod tests { let mut stack = Stack::from(vec![stack::Element::Push(&preimage)]); let mut vfyfn = vfyfn_.clone(); // sigh rust 1.29... let constraints = from_stack(&mut vfyfn, &mut stack, &sha256); - let sah256_satisfied: Result, Error> = constraints.collect(); + let sah256_satisfied: Result>, Error> = + constraints.collect(); assert_eq!( sah256_satisfied.unwrap(), vec![SatisfiedConstraint::HashLock { @@ -1054,7 +1091,8 @@ mod tests { let mut stack = Stack::from(vec![stack::Element::Push(&preimage)]); let mut vfyfn = vfyfn_.clone(); // sigh rust 1.29... let constraints = from_stack(&mut vfyfn, &mut stack, &hash256); - let sha256d_satisfied: Result, Error> = constraints.collect(); + let sha256d_satisfied: Result>, Error> = + constraints.collect(); assert_eq!( sha256d_satisfied.unwrap(), vec![SatisfiedConstraint::HashLock { @@ -1067,7 +1105,8 @@ mod tests { let mut stack = Stack::from(vec![stack::Element::Push(&preimage)]); let mut vfyfn = vfyfn_.clone(); // sigh rust 1.29... let constraints = from_stack(&mut vfyfn, &mut stack, &hash160); - let hash160_satisfied: Result, Error> = constraints.collect(); + let hash160_satisfied: Result>, Error> = + constraints.collect(); assert_eq!( hash160_satisfied.unwrap(), vec![SatisfiedConstraint::HashLock { @@ -1080,7 +1119,8 @@ mod tests { let mut stack = Stack::from(vec![stack::Element::Push(&preimage)]); let mut vfyfn = vfyfn_.clone(); // sigh rust 1.29... let constraints = from_stack(&mut vfyfn, &mut stack, &ripemd160); - let ripemd160_satisfied: Result, Error> = constraints.collect(); + let ripemd160_satisfied: Result>, Error> = + constraints.collect(); assert_eq!( ripemd160_satisfied.unwrap(), vec![SatisfiedConstraint::HashLock { @@ -1104,7 +1144,8 @@ mod tests { let mut vfyfn = vfyfn_.clone(); // sigh rust 1.29... let constraints = from_stack(&mut vfyfn, &mut stack, &elem); - let and_v_satisfied: Result, Error> = constraints.collect(); + let and_v_satisfied: Result>, Error> = + constraints.collect(); assert_eq!( and_v_satisfied.unwrap(), vec![ @@ -1129,7 +1170,8 @@ mod tests { let mut vfyfn = vfyfn_.clone(); // sigh rust 1.29... let constraints = from_stack(&mut vfyfn, &mut stack, &elem); - let and_b_satisfied: Result, Error> = constraints.collect(); + let and_b_satisfied: Result>, Error> = + constraints.collect(); assert_eq!( and_b_satisfied.unwrap(), vec![ @@ -1158,7 +1200,8 @@ mod tests { let mut vfyfn = vfyfn_.clone(); // sigh rust 1.29... let constraints = from_stack(&mut vfyfn, &mut stack, &elem); - let and_or_satisfied: Result, Error> = constraints.collect(); + let and_or_satisfied: Result>, Error> = + constraints.collect(); assert_eq!( and_or_satisfied.unwrap(), vec![ @@ -1183,7 +1226,8 @@ mod tests { let mut vfyfn = vfyfn_.clone(); // sigh rust 1.29... let constraints = from_stack(&mut vfyfn, &mut stack, &elem); - let and_or_satisfied: Result, Error> = constraints.collect(); + let and_or_satisfied: Result>, Error> = + constraints.collect(); assert_eq!( and_or_satisfied.unwrap(), vec![SatisfiedConstraint::PublicKeyHash { @@ -1202,7 +1246,7 @@ mod tests { let mut vfyfn = vfyfn_.clone(); // sigh rust 1.29... let constraints = from_stack(&mut vfyfn, &mut stack, &elem); - let or_b_satisfied: Result, Error> = constraints.collect(); + let or_b_satisfied: Result>, Error> = constraints.collect(); assert_eq!( or_b_satisfied.unwrap(), vec![SatisfiedConstraint::HashLock { @@ -1217,7 +1261,7 @@ mod tests { let mut vfyfn = vfyfn_.clone(); // sigh rust 1.29... let constraints = from_stack(&mut vfyfn, &mut stack, &elem); - let or_d_satisfied: Result, Error> = constraints.collect(); + let or_d_satisfied: Result>, Error> = constraints.collect(); assert_eq!( or_d_satisfied.unwrap(), vec![SatisfiedConstraint::PublicKey { @@ -1235,7 +1279,7 @@ mod tests { let mut vfyfn = vfyfn_.clone(); // sigh rust 1.29... let constraints = from_stack(&mut vfyfn, &mut stack, &elem); - let or_c_satisfied: Result, Error> = constraints.collect(); + let or_c_satisfied: Result>, Error> = constraints.collect(); assert_eq!( or_c_satisfied.unwrap(), vec![SatisfiedConstraint::PublicKey { @@ -1253,7 +1297,7 @@ mod tests { let mut vfyfn = vfyfn_.clone(); // sigh rust 1.29... let constraints = from_stack(&mut vfyfn, &mut stack, &elem); - let or_i_satisfied: Result, Error> = constraints.collect(); + let or_i_satisfied: Result>, Error> = constraints.collect(); assert_eq!( or_i_satisfied.unwrap(), vec![SatisfiedConstraint::PublicKey { @@ -1281,7 +1325,8 @@ mod tests { let mut vfyfn = vfyfn_.clone(); // sigh rust 1.29... let constraints = from_stack(&mut vfyfn, &mut stack, &elem); - let thresh_satisfied: Result, Error> = constraints.collect(); + let thresh_satisfied: Result>, Error> = + constraints.collect(); assert_eq!( thresh_satisfied.unwrap(), vec![ @@ -1318,7 +1363,8 @@ mod tests { let mut vfyfn = vfyfn_.clone(); // sigh rust 1.29... let constraints = from_stack(&mut vfyfn, &mut stack, &elem); - let multi_satisfied: Result, Error> = constraints.collect(); + let multi_satisfied: Result>, Error> = + constraints.collect(); assert_eq!( multi_satisfied.unwrap(), vec![ @@ -1355,7 +1401,7 @@ mod tests { let mut vfyfn = vfyfn_.clone(); // sigh rust 1.29... let constraints = from_stack(&mut vfyfn, &mut stack, &elem); - let multi_error: Result, Error> = constraints.collect(); + let multi_error: Result>, Error> = constraints.collect(); assert!(multi_error.is_err()); } } diff --git a/src/interpreter/stack.rs b/src/interpreter/stack.rs index de343aae..1e684670 100644 --- a/src/interpreter/stack.rs +++ b/src/interpreter/stack.rs @@ -16,15 +16,15 @@ use std::ops::Index; -use bitcoin; +use bitcoin::{self, PublicKey}; use elements::hashes::{hash160, ripemd160, sha256, sha256d, Hash}; use elements::{self, opcodes, script}; use {ElementsSig, ToPublicKey}; +use Extension; + use super::{verify_sersig, Error, HashLockType, SatisfiedConstraint}; -use miniscript::limits::{MAX_SCRIPT_ELEMENT_SIZE, MAX_STANDARD_P2WSH_STACK_ITEM_SIZE}; -use util; /// Definition of Stack Element of the Stack used for interpretation of Miniscript. /// All stack elements with vec![] go to Dissatisfied and vec![1] are marked to Satisfied. /// Others are directly pushed as witness @@ -162,11 +162,11 @@ impl<'txin> Stack<'txin> { /// Unsat: For empty witness a 0 is pushed /// Err: All of other witness result in errors. /// `pk` CHECKSIG - pub fn evaluate_pk<'intp, F>( + pub fn evaluate_pk<'intp, F, Ext: Extension>( &mut self, verify_sig: F, pk: &'intp bitcoin::PublicKey, - ) -> Option, Error>> + ) -> Option, Error>> where F: FnMut(&bitcoin::PublicKey, ElementsSig) -> bool, { @@ -201,11 +201,11 @@ impl<'txin> Stack<'txin> { /// Unsat: For an empty witness /// Err: All of other witness result in errors. /// `DUP HASH160 EQUALVERIY CHECKSIG` - pub fn evaluate_pkh<'intp, F>( + pub fn evaluate_pkh<'intp, F, Ext: Extension>( &mut self, verify_sig: F, pkh: &'intp hash160::Hash, - ) -> Option, Error>> + ) -> Option, Error>> where F: FnOnce(&bitcoin::PublicKey, ElementsSig) -> bool, { @@ -259,11 +259,11 @@ impl<'txin> Stack<'txin> { /// The reason we don't need to copy the Script semantics is that /// Miniscript never evaluates integers and it is safe to treat them as /// booleans - pub fn evaluate_after<'intp>( + pub fn evaluate_after<'intp, Ext: Extension>( &mut self, n: &'intp u32, age: u32, - ) -> Option, Error>> { + ) -> Option, Error>> { if age >= *n { self.push(Element::Satisfied); Some(Ok(SatisfiedConstraint::AbsoluteTimeLock { time: n })) @@ -278,11 +278,11 @@ impl<'txin> Stack<'txin> { /// The reason we don't need to copy the Script semantics is that /// Miniscript never evaluates integers and it is safe to treat them as /// booleans - pub fn evaluate_older<'intp>( + pub fn evaluate_older<'intp, Ext: Extension>( &mut self, n: &'intp u32, height: u32, - ) -> Option, Error>> { + ) -> Option, Error>> { if height >= *n { self.push(Element::Satisfied); Some(Ok(SatisfiedConstraint::RelativeTimeLock { time: n })) @@ -293,10 +293,10 @@ impl<'txin> Stack<'txin> { /// Helper function to evaluate a Sha256 Node. /// `SIZE 32 EQUALVERIFY SHA256 h EQUAL` - pub fn evaluate_sha256<'intp>( + pub fn evaluate_sha256<'intp, Ext: Extension>( &mut self, hash: &'intp sha256::Hash, - ) -> Option, Error>> { + ) -> Option, Error>> { if let Some(Element::Push(preimage)) = self.pop() { if preimage.len() != 32 { return Some(Err(Error::HashPreimageLengthMismatch)); @@ -318,10 +318,10 @@ impl<'txin> Stack<'txin> { /// Helper function to evaluate a Hash256 Node. /// `SIZE 32 EQUALVERIFY HASH256 h EQUAL` - pub fn evaluate_hash256<'intp>( + pub fn evaluate_hash256<'intp, Ext: Extension>( &mut self, hash: &'intp sha256d::Hash, - ) -> Option, Error>> { + ) -> Option, Error>> { if let Some(Element::Push(preimage)) = self.pop() { if preimage.len() != 32 { return Some(Err(Error::HashPreimageLengthMismatch)); @@ -343,10 +343,10 @@ impl<'txin> Stack<'txin> { /// Helper function to evaluate a Hash160 Node. /// `SIZE 32 EQUALVERIFY HASH160 h EQUAL` - pub fn evaluate_hash160<'intp>( + pub fn evaluate_hash160<'intp, Ext: Extension>( &mut self, hash: &'intp hash160::Hash, - ) -> Option, Error>> { + ) -> Option, Error>> { if let Some(Element::Push(preimage)) = self.pop() { if preimage.len() != 32 { return Some(Err(Error::HashPreimageLengthMismatch)); @@ -368,10 +368,10 @@ impl<'txin> Stack<'txin> { /// Helper function to evaluate a RipeMd160 Node. /// `SIZE 32 EQUALVERIFY RIPEMD160 h EQUAL` - pub fn evaluate_ripemd160<'intp>( + pub fn evaluate_ripemd160<'intp, Ext: Extension>( &mut self, hash: &'intp ripemd160::Hash, - ) -> Option, Error>> { + ) -> Option, Error>> { if let Some(Element::Push(preimage)) = self.pop() { if preimage.len() != 32 { return Some(Err(Error::HashPreimageLengthMismatch)); @@ -391,92 +391,17 @@ impl<'txin> Stack<'txin> { } } - /// Evaluate a ver fragment. Get the version from the global stack - /// context and check equality - pub fn evaluate_ver<'intp>( - &mut self, - n: &'intp u32, - ) -> Option, Error>> { - // Version is at index 11 - let ver = self[11]; - if let Err(e) = ver.try_push() { - return Some(Err(e)); - } - let elem = ver.as_push(); - if elem.len() == 4 { - let wit_ver = util::slice_to_u32_le(elem); - if wit_ver == *n { - self.push(Element::Satisfied); - Some(Ok(SatisfiedConstraint::VerEq { n: n })) - } else { - None - } - } else { - Some(Err(Error::CovWitnessSizeErr { - pos: 1, - expected: 4, - actual: elem.len(), - })) - } - } - - /// Evaluate a output_pref fragment. Get the hashoutputs from the global - /// stack context and check it's preimage starts with prefix. - /// The user provides the suffix as witness in 6 different elements - pub fn evaluate_outputs_pref<'intp>( - &mut self, - pref: &'intp [u8], - ) -> Option, Error>> { - // Version is at index 1 - let hash_outputs = self[3]; - if let Err(e) = hash_outputs.try_push() { - return Some(Err(e)); - } - // Maximum number of suffix elements - let max_elems = MAX_SCRIPT_ELEMENT_SIZE / MAX_STANDARD_P2WSH_STACK_ITEM_SIZE + 1; - let hash_outputs = hash_outputs.as_push(); - if hash_outputs.len() == 32 { - // We want to cat the last 6 elements(5 cats) in suffix - if self.len() < max_elems { - return Some(Err(Error::UnexpectedStackEnd)); - } - let mut outputs_builder = Vec::new(); - outputs_builder.extend(pref); - let len = self.len(); - // Add the max_elems suffix elements - for i in 0..max_elems { - outputs_builder.extend(self[len - max_elems + i].into_slice()); - } - // Pop the max_elems suffix elements - for _ in 0..max_elems { - self.pop().unwrap(); - } - if sha256d::Hash::hash(&outputs_builder).as_inner() == hash_outputs { - self.push(Element::Satisfied); - Some(Ok(SatisfiedConstraint::OutputsPref { pref: pref })) - } else { - None - } - } else { - Some(Err(Error::CovWitnessSizeErr { - pos: 9, - expected: 32, - actual: hash_outputs.len(), - })) - } - } - /// Helper function to evaluate a checkmultisig which takes the top of the /// stack as input signatures and validates it in order of pubkeys. /// For example, if the first signature is satisfied by second public key, /// other signatures are not checked against the first pubkey. /// `multi(2,pk1,pk2)` would be satisfied by `[0 sig2 sig1]` and Err on /// `[0 sig2 sig1]` - pub fn evaluate_multi<'intp, F>( + pub fn evaluate_multi<'intp, F, Ext: Extension>( &mut self, verify_sig: F, pk: &'intp bitcoin::PublicKey, - ) -> Option, Error>> + ) -> Option, Error>> where F: FnOnce(&bitcoin::PublicKey, ElementsSig) -> bool, { diff --git a/src/lib.rs b/src/lib.rs index 6e4454ab..3216dcfb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -144,6 +144,7 @@ mod macros; pub mod descriptor; pub mod expression; +pub mod extensions; pub mod interpreter; pub mod miniscript; pub mod policy; @@ -160,6 +161,7 @@ use elements::hashes::sha256; use elements::{opcodes, script, secp256k1_zkp, secp256k1_zkp::Secp256k1}; pub use descriptor::{Descriptor, DescriptorPublicKey, DescriptorTrait}; +pub use extensions::{AllExt, Extension, NoExt}; pub use interpreter::Interpreter; pub use miniscript::context::{BareCtx, Legacy, ScriptContext, Segwitv0}; pub use miniscript::decode::Terminal; @@ -167,6 +169,38 @@ pub use miniscript::satisfy::{elementssig_from_rawsig, elementssig_to_rawsig}; pub use miniscript::satisfy::{ElementsSig, Preimage32, Satisfier}; pub use miniscript::Miniscript; +/// Same as upstream [`TranslatePk`] but with support for extensions +pub trait TranslatePkExt> { + /// The associated output type. This must be Self + type Output; + + /// Translate a struct from one Generic to another where the + /// translation for Pk is provided by translatefpk, and translation for + /// PkH is provided by translatefpkh + fn translate_pk( + &self, + translatefpk: Fpk, + translatefpkh: Fpkh, + ) -> Result + where + Fpk: FnMut(&P) -> Result, + Fpkh: FnMut(&P::Hash) -> Result; + + /// Calls `translate_pk` with conversion functions that cannot fail + fn translate_pk_infallible( + &self, + mut translatefpk: Fpk, + mut translatefpkh: Fpkh, + ) -> Self::Output + where + Fpk: FnMut(&P) -> Q, + Fpkh: FnMut(&P::Hash) -> Q::Hash, + { + self.translate_pk::<_, _, ()>(|pk| Ok(translatefpk(pk)), |pkh| Ok(translatefpkh(pkh))) + .expect("infallible translation function") + } +} + /// Tweak a MiniscriptKey to obtain the tweaked key // Ideally, we want this in a trait, but doing so we cannot // use it in the implementation of DescriptorTrait from @@ -270,12 +304,13 @@ pub enum Error { } #[doc(hidden)] -impl From> for Error +impl From> for Error where Pk: MiniscriptKey, Ctx: ScriptContext, + Ext: Extension, { - fn from(e: miniscript::types::Error) -> Error { + fn from(e: miniscript::types::Error) -> Error { Error::TypeCheck(e.to_string()) } } diff --git a/src/macros.rs b/src/macros.rs index a9bdfaa6..6d757ae4 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -19,9 +19,9 @@ macro_rules! policy_str { /// A macro that implements serde serialization and deserialization using the /// `fmt::Display` and `str::FromStr` traits. macro_rules! serde_string_impl_pk { - ($name:ident, $expecting:expr $(, $gen:ident; $gen_con:ident)*) => { + ($name:ident, $expecting:expr $(, $gen:ident; $gen_con:ident)* $(=> $ext:ident ; $ext_bound:ident)*) => { #[cfg(feature = "serde")] - impl<'de, Pk $(, $gen)*> $crate::serde::Deserialize<'de> for $name + impl<'de, Pk $(, $gen)* $(, $ext)*> $crate::serde::Deserialize<'de> for $name where Pk: $crate::MiniscriptKey + $crate::std::str::FromStr, Pk::Hash: $crate::std::str::FromStr, @@ -29,8 +29,9 @@ macro_rules! serde_string_impl_pk { <::Hash as $crate::std::str::FromStr>::Err: $crate::std::fmt::Display, $($gen : $gen_con,)* + $($ext : $ext_bound,)* { - fn deserialize(deserializer: D) -> Result<$name, D::Error> + fn deserialize(deserializer: D) -> Result<$name, D::Error> where D: $crate::serde::de::Deserializer<'de>, { @@ -39,8 +40,8 @@ macro_rules! serde_string_impl_pk { use $crate::std::str::FromStr; #[allow(unused_parens)] - struct Visitor(PhantomData<(Pk $(, $gen)*)>); - impl<'de, Pk $(, $gen)*> $crate::serde::de::Visitor<'de> for Visitor + struct Visitor(PhantomData<(Pk $(, $gen)* $(, $ext)*)>); + impl<'de, Pk $(, $gen)* $(, $ext)*> $crate::serde::de::Visitor<'de> for Visitor where Pk: $crate::MiniscriptKey + $crate::std::str::FromStr, Pk::Hash: $crate::std::str::FromStr, @@ -48,8 +49,9 @@ macro_rules! serde_string_impl_pk { <::Hash as $crate::std::str::FromStr>::Err: $crate::std::fmt::Display, $($gen: $gen_con,)* + $($ext : $ext_bound,)* { - type Value = $name; + type Value = $name; fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { formatter.write_str($expecting) @@ -82,10 +84,11 @@ macro_rules! serde_string_impl_pk { } #[cfg(feature = "serde")] - impl<'de, Pk $(, $gen)*> $crate::serde::Serialize for $name + impl<'de, Pk $(, $gen)* $(, $ext)*> $crate::serde::Serialize for $name where Pk: $crate::MiniscriptKey, $($gen: $gen_con,)* + $($ext : $ext_bound,)* { fn serialize(&self, serializer: S) -> Result where diff --git a/src/miniscript/analyzable.rs b/src/miniscript/analyzable.rs index 9ed62acf..0032dba4 100644 --- a/src/miniscript/analyzable.rs +++ b/src/miniscript/analyzable.rs @@ -21,6 +21,8 @@ use error; use miniscript::iter::PkPkh; use std::collections::HashSet; use std::fmt; + +use Extension; use {Miniscript, MiniscriptKey, ScriptContext}; /// Possible reasons Miniscript guarantees can fail /// We currently mark Miniscript as Non-Analyzable if @@ -67,7 +69,7 @@ impl fmt::Display for AnalysisError { impl error::Error for AnalysisError {} -impl Miniscript { +impl> Miniscript { /// Whether all spend paths of miniscript require a signature pub fn requires_sig(&self) -> bool { self.ty.mall.safe diff --git a/src/miniscript/astelem.rs b/src/miniscript/astelem.rs index 6dfec013..f9ddcbf0 100644 --- a/src/miniscript/astelem.rs +++ b/src/miniscript/astelem.rs @@ -23,8 +23,7 @@ use std::str::FromStr; use std::sync::Arc; use std::{fmt, str}; -use elements::encode::serialize; -use elements::hashes::hex::{FromHex, ToHex}; +use elements::hashes::hex::FromHex; use elements::hashes::{hash160, ripemd160, sha256, sha256d, Hash}; use elements::{opcodes, script}; @@ -35,13 +34,15 @@ use miniscript::ScriptContext; use script_num_size; use {Error, ForEach, ForEachKey, Miniscript, MiniscriptKey, Terminal, ToPublicKey, TranslatePk}; +use Extension; + use super::limits::{MAX_SCRIPT_ELEMENT_SIZE, MAX_STANDARD_P2WSH_STACK_ITEM_SIZE}; -impl Terminal { +impl> Terminal { /// Internal helper function for displaying wrapper types; returns /// a character to display before the `:` as well as a reference /// to the wrapped type to allow easy recursion - fn wrap_char(&self) -> Option<(char, &Arc>)> { + fn wrap_char(&self) -> Option<(char, &Arc>)> { match *self { Terminal::Alt(ref sub) => Some(('a', sub)), Terminal::Swap(ref sub) => Some(('s', sub)), @@ -58,10 +59,16 @@ impl Terminal { } } -impl TranslatePk - for Terminal +impl TranslatePk for Terminal +where + Pk: MiniscriptKey, + Q: MiniscriptKey, + Ctx: ScriptContext, + Ext: Extension + Extension + TranslatePk, + Ext: TranslatePk, + >::Output: Extension, { - type Output = Terminal; + type Output = Terminal>::Output>; /// Convert an AST element with one public key type to one of another /// public key type .This will panic while converting to @@ -79,7 +86,7 @@ impl TranslatePk } } -impl Terminal { +impl> Terminal { pub(super) fn real_for_each_key<'a, F: FnMut(ForEach<'a, Pk>) -> bool>( &'a self, pred: &mut F, @@ -98,9 +105,7 @@ impl Terminal { | Terminal::Ripemd160(..) | Terminal::Hash160(..) | Terminal::True - | Terminal::False - | Terminal::Version(..) - | Terminal::OutputsPref(..) => true, + | Terminal::False => true, Terminal::Alt(ref sub) | Terminal::Swap(ref sub) | Terminal::Check(ref sub) @@ -123,17 +128,20 @@ impl Terminal { } Terminal::Thresh(_, ref subs) => subs.iter().all(|sub| sub.real_for_each_key(pred)), Terminal::Multi(_, ref keys) => keys.iter().all(|key| pred(ForEach::Key(key))), + Terminal::Ext(ref e) => e.real_for_each_key(pred), } } pub(super) fn real_translate_pk( &self, translatefpk: &mut FPk, translatefpkh: &mut FPkh, - ) -> Result, Error> + ) -> Result>::Output>, Error> where FPk: FnMut(&Pk) -> Result, FPkh: FnMut(&Pk::Hash) -> Result, Q: MiniscriptKey, + Ext: TranslatePk, + >::Output: Extension, { let frag = match *self { Terminal::PkK(ref p) => Terminal::PkK(translatefpk(p)?), @@ -146,8 +154,6 @@ impl Terminal { Terminal::Hash160(x) => Terminal::Hash160(x), Terminal::True => Terminal::True, Terminal::False => Terminal::False, - Terminal::Version(n) => Terminal::Version(n), - Terminal::OutputsPref(ref pref) => Terminal::OutputsPref(pref.clone()), Terminal::Alt(ref sub) => Terminal::Alt(Arc::new( sub.real_translate_pk(translatefpk, translatefpkh)?, )), @@ -201,7 +207,7 @@ impl Terminal { Arc::new(right.real_translate_pk(translatefpk, translatefpkh)?), ), Terminal::Thresh(k, ref subs) => { - let subs: Result>>, _> = subs + let subs: Result>>, _> = subs .iter() .map(|s| { s.real_translate_pk(&mut *translatefpk, &mut *translatefpkh) @@ -214,12 +220,18 @@ impl Terminal { let keys: Result, _> = keys.iter().map(&mut *translatefpk).collect(); Terminal::Multi(k, keys?) } + Terminal::Ext(ref e) => Terminal::Ext(e.translate_pk(translatefpk, translatefpkh)?), }; Ok(frag) } } -impl ForEachKey for Terminal { +impl ForEachKey for Terminal +where + Pk: MiniscriptKey, + Ctx: ScriptContext, + Ext: Extension, +{ fn for_each_key<'a, F: FnMut(ForEach<'a, Pk>) -> bool>(&'a self, mut pred: F) -> bool where Pk: 'a, @@ -229,7 +241,12 @@ impl ForEachKey for Terminal } } -impl fmt::Debug for Terminal { +impl fmt::Debug for Terminal +where + Pk: MiniscriptKey, + Ctx: ScriptContext, + Ext: Extension, +{ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("[")?; if let Ok(type_map) = types::Type::type_check(self, |_| None) { @@ -290,8 +307,7 @@ impl fmt::Debug for Terminal { Terminal::Hash160(h) => write!(f, "hash160({})", h), Terminal::True => f.write_str("1"), Terminal::False => f.write_str("0"), - Terminal::Version(k) => write!(f, "ver_eq({})", k), - Terminal::OutputsPref(ref pref) => write!(f, "outputs_pref({})", pref.to_hex()), + Terminal::Ext(ref e) => write!(f, "{:?}", e), Terminal::AndV(ref l, ref r) => write!(f, "and_v({:?},{:?})", l, r), Terminal::AndB(ref l, ref r) => write!(f, "and_b({:?},{:?})", l, r), Terminal::AndOr(ref a, ref b, ref c) => { @@ -325,7 +341,12 @@ impl fmt::Debug for Terminal { } } -impl fmt::Display for Terminal { +impl fmt::Display for Terminal +where + Pk: MiniscriptKey, + Ctx: ScriptContext, + Ext: Extension, +{ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Terminal::PkK(ref pk) => write!(f, "pk_k({})", pk), @@ -342,8 +363,7 @@ impl fmt::Display for Terminal { Terminal::Hash160(h) => write!(f, "hash160({})", h), Terminal::True => f.write_str("1"), Terminal::False => f.write_str("0"), - Terminal::Version(n) => write!(f, "ver_eq({})", n), - Terminal::OutputsPref(ref pref) => write!(f, "outputs_pref({})", pref.to_hex()), + Terminal::Ext(ref e) => write!(f, "{}", e), Terminal::AndV(ref l, ref r) if r.node != Terminal::True => { write!(f, "and_v({},{})", l, r) } @@ -415,28 +435,30 @@ impl fmt::Display for Terminal { } } -impl expression::FromTree for Arc> +impl expression::FromTree for Arc> where Pk: MiniscriptKey + str::FromStr, Pk::Hash: str::FromStr, Ctx: ScriptContext, + Ext: Extension, ::Err: ToString, <::Hash as str::FromStr>::Err: ToString, { - fn from_tree(top: &expression::Tree) -> Result>, Error> { + fn from_tree(top: &expression::Tree) -> Result>, Error> { Ok(Arc::new(expression::FromTree::from_tree(top)?)) } } -impl expression::FromTree for Terminal +impl expression::FromTree for Terminal where Pk: MiniscriptKey + str::FromStr, Pk::Hash: str::FromStr, Ctx: ScriptContext, + Ext: Extension, ::Err: ToString, <::Hash as str::FromStr>::Err: ToString, { - fn from_tree(top: &expression::Tree) -> Result, Error> { + fn from_tree(top: &expression::Tree) -> Result, Error> { let mut aliased_wrap; let frag_name; let frag_wrap; @@ -514,13 +536,6 @@ where }), ("1", 0) => Ok(Terminal::True), ("0", 0) => Ok(Terminal::False), - ("ver_eq", 1) => { - let n = expression::terminal(&top.args[0], expression::parse_num)?; - Ok(Terminal::Version(n)) - } - ("outputs_pref", 1) => expression::terminal(&top.args[0], |x| { - Vec::::from_hex(x).map(Terminal::OutputsPref) - }), ("and_v", 2) => { let expr = expression::binary(top, Terminal::AndV)?; if let Terminal::AndV(_, ref right) = expr { @@ -565,7 +580,7 @@ where return Err(errstr("empty thresholds not allowed in descriptors")); } - let subs: Result>>, _> = top.args[1..] + let subs: Result>>, _> = top.args[1..] .iter() .map(|sub| expression::FromTree::from_tree(sub)) .collect(); @@ -588,11 +603,17 @@ where pks.map(|pks| Terminal::Multi(k, pks)) } - _ => Err(Error::Unexpected(format!( - "{}({} args) while parsing Miniscript", - top.name, - top.args.len(), - ))), + (name, _num_child) => { + // If nothing matches try to parse as extension + match Ext::from_name_tree(&name, &top.args) { + Ok(e) => Ok(Terminal::Ext(e)), + Err(..) => Err(Error::Unexpected(format!( + "{}({} args) while parsing Miniscript", + top.name, + top.args.len(), + ))), + } + } }?; for ch in frag_wrap.chars().rev() { // Check whether the wrapper is valid under the current context @@ -638,14 +659,16 @@ where } /// Helper trait to add a `push_astelem` method to `script::Builder` -trait PushAstElem { - fn push_astelem(self, ast: &Miniscript) -> Self +trait PushAstElem> { + fn push_astelem(self, ast: &Miniscript) -> Self where Pk: ToPublicKey; } -impl PushAstElem for script::Builder { - fn push_astelem(self, ast: &Miniscript) -> Self +impl> PushAstElem + for script::Builder +{ + fn push_astelem(self, ast: &Miniscript) -> Self where Pk: ToPublicKey, { @@ -710,7 +733,7 @@ impl StackCtxOperations for script::Builder { } } -impl Terminal { +impl> Terminal { /// Encode the element as a fragment of Bitcoin Script. The inverse /// function, from Script to an AST element, is implemented in the /// `parse` module. @@ -759,8 +782,6 @@ impl Terminal { .push_opcode(opcodes::all::OP_EQUAL), Terminal::True => builder.push_opcode(opcodes::OP_TRUE), Terminal::False => builder.push_opcode(opcodes::OP_FALSE), - Terminal::Version(n) => builder.check_item_eq(12, &serialize(&n)), - Terminal::OutputsPref(ref pref) => builder.check_item_pref(4, pref), Terminal::Alt(ref sub) => builder .push_opcode(opcodes::all::OP_TOALTSTACK) .push_astelem(sub) @@ -835,6 +856,7 @@ impl Terminal { .push_int(keys.len() as i64) .push_opcode(opcodes::all::OP_CHECKMULTISIG) } + Terminal::Ext(ref e) => e.push_to_builder(builder), } } @@ -857,13 +879,6 @@ impl Terminal { Terminal::Hash160(..) => 21 + 6, Terminal::True => 1, Terminal::False => 1, - Terminal::Version(_n) => 4 + 1 + 1 + 4, // opcodes + push opcodes + target size - Terminal::OutputsPref(ref pref) => { - // CAT CAT CAT CAT CAT CAT SWAP CAT /*Now we hashoutputs on stack */ - // HASH256 DEPTH <10> SUB PICK EQUAL - 8 + pref.len() + 1 /* line1 opcodes + pref.push */ - + 6 /* line 2 */ - } Terminal::Alt(ref sub) => sub.node.script_size() + 2, Terminal::Swap(ref sub) => sub.node.script_size() + 1, Terminal::Check(ref sub) => sub.node.script_size() + 1, @@ -896,6 +911,7 @@ impl Terminal { + script_num_size(pks.len()) + pks.iter().map(|pk| pk.serialized_len()).sum::() } + Terminal::Ext(ref e) => e.script_size(), } } } diff --git a/src/miniscript/context.rs b/src/miniscript/context.rs index 64d3a476..638b7737 100644 --- a/src/miniscript/context.rs +++ b/src/miniscript/context.rs @@ -20,9 +20,11 @@ use miniscript::types; use std::fmt; use util::witness_to_scriptsig; use Error; + +use Extension; use {Miniscript, MiniscriptKey, Terminal}; /// Error for Script Context -#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[derive(Clone, PartialEq, Eq, Debug)] pub enum ScriptContextError { /// Script Context does not permit PkH for non-malleability /// It is not possible to estimate the pubkey size at the creation @@ -55,6 +57,8 @@ pub enum ScriptContextError { ImpossibleSatisfaction, /// Covenant Prefix/ Suffix maximum allowed stack element exceeds 520 bytes CovElementSizeExceeded, + /// Extension Error for Downstream implementations, includes a string + ExtensionError(String), } impl fmt::Display for ScriptContextError { @@ -105,6 +109,7 @@ impl fmt::Display for ScriptContextError { "Prefix/Suffix len in sighash covenents exceeds 520 bytes" ) } + ScriptContextError::ExtensionError(ref s) => write!(f, "Extension Error: {}", s), } } } @@ -124,16 +129,23 @@ pub trait ScriptContext: /// This does NOT recursively check if the children of the fragment are /// valid or not. Since the compilation proceeds in a leaf to root fashion, /// a recursive check is unnecessary. - fn check_terminal_non_malleable( - _frag: &Terminal, - ) -> Result<(), ScriptContextError>; + fn check_terminal_non_malleable( + _frag: &Terminal, + ) -> Result<(), ScriptContextError> + where + Pk: MiniscriptKey, + Ctx: ScriptContext, + Ext: Extension; /// Check whether the given satisfaction is valid under the ScriptContext /// For example, segwit satisfactions may fail if the witness len is more /// 3600 or number of stack elements are more than 100. - fn check_witness( - _witness: &[Vec], - ) -> Result<(), ScriptContextError> { + fn check_witness(_witness: &[Vec]) -> Result<(), ScriptContextError> + where + Pk: MiniscriptKey, + Ctx: ScriptContext, + Ext: Extension, + { // Only really need to do this for segwitv0 and legacy // Bare is already restrcited by standardness rules // and would reach these limits. @@ -141,9 +153,11 @@ pub trait ScriptContext: } /// Depending on script context, the size of a satifaction witness may slightly differ. - fn max_satisfaction_size( - ms: &Miniscript, - ) -> Option; + fn max_satisfaction_size(ms: &Miniscript) -> Option + where + Pk: MiniscriptKey, + Ctx: ScriptContext, + Ext: Extension; /// Depending on script Context, some of the Terminals might not /// be valid under the current consensus rules. /// Or some of the script resource limits may have been exceeded. @@ -154,8 +168,12 @@ pub trait ScriptContext: /// In LegacyP2SH context, scripts above 520 bytes are invalid. /// Post Tapscript upgrade, this would have to consider other nodes. /// This does *NOT* recursively check the miniscript fragments. - fn check_global_consensus_validity( - _ms: &Miniscript, + fn check_global_consensus_validity< + Pk: MiniscriptKey, + Ctx: ScriptContext, + Ext: Extension, + >( + _ms: &Miniscript, ) -> Result<(), ScriptContextError> { Ok(()) } @@ -168,8 +186,8 @@ pub trait ScriptContext: /// scripts over 3600 bytes are invalid. /// Post Tapscript upgrade, this would have to consider other nodes. /// This does *NOT* recursively check the miniscript fragments. - fn check_global_policy_validity( - _ms: &Miniscript, + fn check_global_policy_validity>( + _ms: &Miniscript, ) -> Result<(), ScriptContextError> { Ok(()) } @@ -178,8 +196,8 @@ pub trait ScriptContext: /// It is possible that some paths of miniscript may exceed resource limits /// and our current satisfier and lifting analysis would not work correctly. /// For example, satisfaction path(Legacy/Segwitv0) may require more than 201 opcodes. - fn check_local_consensus_validity( - _ms: &Miniscript, + fn check_local_consensus_validity>( + _ms: &Miniscript, ) -> Result<(), ScriptContextError> { Ok(()) } @@ -189,17 +207,22 @@ pub trait ScriptContext: /// and our current satisfier and lifting analysis would not work correctly. /// For example, satisfaction path in Legacy context scriptSig more /// than 1650 bytes - fn check_local_policy_validity( - _ms: &Miniscript, + fn check_local_policy_validity>( + _ms: &Miniscript, ) -> Result<(), ScriptContextError> { Ok(()) } /// Check the consensus + policy(if not disabled) rules that are not based /// satisfaction - fn check_global_validity( - ms: &Miniscript, - ) -> Result<(), ScriptContextError> { + fn check_global_validity( + ms: &Miniscript, + ) -> Result<(), ScriptContextError> + where + Pk: MiniscriptKey, + Ctx: ScriptContext, + Ext: Extension, + { Self::check_global_consensus_validity(ms)?; Self::check_global_policy_validity(ms)?; Ok(()) @@ -207,8 +230,8 @@ pub trait ScriptContext: /// Check the consensus + policy(if not disabled) rules including the /// ones for satisfaction - fn check_local_validity( - ms: &Miniscript, + fn check_local_validity>( + ms: &Miniscript, ) -> Result<(), ScriptContextError> { Self::check_global_consensus_validity(ms)?; Self::check_global_policy_validity(ms)?; @@ -218,8 +241,8 @@ pub trait ScriptContext: } /// Check whether the top-level is type B - fn top_level_type_check( - ms: &Miniscript, + fn top_level_type_check>( + ms: &Miniscript, ) -> Result<(), Error> { if ms.ty.corr.base != types::Base::B { return Err(Error::NonTopLevel(format!("{:?}", ms))); @@ -228,9 +251,12 @@ pub trait ScriptContext: } /// Other top level checks that are context specific - fn other_top_level_checks( - _ms: &Miniscript, - ) -> Result<(), Error> { + fn other_top_level_checks(_ms: &Miniscript) -> Result<(), Error> + where + Pk: MiniscriptKey, + Ctx: ScriptContext, + Ext: Extension, + { Ok(()) } @@ -243,8 +269,8 @@ pub trait ScriptContext: // that are only applicable at the top-level // We can also combine the top-level check for Base::B here // even though it does not depend on context, but helps in cleaner code - fn top_level_checks( - ms: &Miniscript, + fn top_level_checks>( + ms: &Miniscript, ) -> Result<(), Error> { Self::top_level_type_check(ms)?; Self::other_top_level_checks(ms) @@ -259,8 +285,8 @@ pub trait ScriptContext: pub enum Legacy {} impl ScriptContext for Legacy { - fn check_terminal_non_malleable( - frag: &Terminal, + fn check_terminal_non_malleable>( + frag: &Terminal, ) -> Result<(), ScriptContextError> { match *frag { Terminal::PkH(ref _pkh) => Err(ScriptContextError::MalleablePkH), @@ -270,7 +296,7 @@ impl ScriptContext for Legacy { } } - fn check_witness( + fn check_witness>( witness: &[Vec], ) -> Result<(), ScriptContextError> { // In future, we could avoid by having a function to count only @@ -281,17 +307,26 @@ impl ScriptContext for Legacy { Ok(()) } - fn check_global_consensus_validity( - ms: &Miniscript, + fn check_global_consensus_validity< + Pk: MiniscriptKey, + Ctx: ScriptContext, + Ext: Extension, + >( + ms: &Miniscript, ) -> Result<(), ScriptContextError> { if ms.ext.pk_cost > MAX_SCRIPT_ELEMENT_SIZE { return Err(ScriptContextError::MaxRedeemScriptSizeExceeded); } + if let Terminal::Ext(ref _e) = ms.node { + return Err(ScriptContextError::ExtensionError(String::from( + "No Extensions in Legacy context", + ))); + } Ok(()) } - fn check_local_consensus_validity( - ms: &Miniscript, + fn check_local_consensus_validity>( + ms: &Miniscript, ) -> Result<(), ScriptContextError> { match ms.ext.ops_count_sat { None => Err(ScriptContextError::MaxOpCountExceeded), @@ -302,8 +337,8 @@ impl ScriptContext for Legacy { } } - fn check_local_policy_validity( - ms: &Miniscript, + fn check_local_policy_validity>( + ms: &Miniscript, ) -> Result<(), ScriptContextError> { // Legacy scripts permit upto 1000 stack elements, 520 bytes consensus limits // on P2SH size, it is not possible to reach the 1000 elements limit and hence @@ -317,8 +352,8 @@ impl ScriptContext for Legacy { } } - fn max_satisfaction_size( - ms: &Miniscript, + fn max_satisfaction_size>( + ms: &Miniscript, ) -> Option { // The scriptSig cost is the second element of the tuple ms.ext.max_sat_size.map(|x| x.1) @@ -330,13 +365,13 @@ impl ScriptContext for Legacy { pub enum Segwitv0 {} impl ScriptContext for Segwitv0 { - fn check_terminal_non_malleable( - _frag: &Terminal, + fn check_terminal_non_malleable>( + _frag: &Terminal, ) -> Result<(), ScriptContextError> { Ok(()) } - fn check_witness( + fn check_witness>( witness: &[Vec], ) -> Result<(), ScriptContextError> { if witness.len() > MAX_STANDARD_P2WSH_STACK_ITEMS { @@ -345,8 +380,12 @@ impl ScriptContext for Segwitv0 { Ok(()) } - fn check_global_consensus_validity( - ms: &Miniscript, + fn check_global_consensus_validity< + Pk: MiniscriptKey, + Ctx: ScriptContext, + Ext: Extension, + >( + ms: &Miniscript, ) -> Result<(), ScriptContextError> { if ms.ext.pk_cost > MAX_SCRIPT_SIZE { return Err(ScriptContextError::MaxWitnessScriptSizeExceeded); @@ -359,18 +398,16 @@ impl ScriptContext for Segwitv0 { } Ok(()) } - Terminal::OutputsPref(ref pref) => { - if pref.len() > MAX_SCRIPT_ELEMENT_SIZE { - return Err(ScriptContextError::CovElementSizeExceeded); - } + Terminal::Ext(ref e) => { + e.segwit_ctx_checks()?; Ok(()) } _ => Ok(()), } } - fn check_local_consensus_validity( - ms: &Miniscript, + fn check_local_consensus_validity>( + ms: &Miniscript, ) -> Result<(), ScriptContextError> { match ms.ext.ops_count_sat { None => Err(ScriptContextError::MaxOpCountExceeded), @@ -381,8 +418,8 @@ impl ScriptContext for Segwitv0 { } } - fn check_global_policy_validity( - ms: &Miniscript, + fn check_global_policy_validity>( + ms: &Miniscript, ) -> Result<(), ScriptContextError> { if ms.ext.pk_cost > MAX_STANDARD_P2WSH_SCRIPT_SIZE { return Err(ScriptContextError::MaxWitnessScriptSizeExceeded); @@ -390,8 +427,8 @@ impl ScriptContext for Segwitv0 { Ok(()) } - fn check_local_policy_validity( - ms: &Miniscript, + fn check_local_policy_validity>( + ms: &Miniscript, ) -> Result<(), ScriptContextError> { // We don't need to know if this is actually a p2wsh as the standard satisfaction for // other Segwitv0 defined programs all require (much) less than 100 elements. @@ -406,8 +443,8 @@ impl ScriptContext for Segwitv0 { } } - fn max_satisfaction_size( - ms: &Miniscript, + fn max_satisfaction_size>( + ms: &Miniscript, ) -> Option { // The witness stack cost is the first element of the tuple ms.ext.max_sat_size.map(|x| x.0) @@ -422,8 +459,8 @@ impl ScriptContext for Segwitv0 { pub enum BareCtx {} impl ScriptContext for BareCtx { - fn check_terminal_non_malleable( - _frag: &Terminal, + fn check_terminal_non_malleable>( + _frag: &Terminal, ) -> Result<(), ScriptContextError> { // Bare fragments can't contain miniscript because of standardness rules // This function is only used in compiler which already checks the standardness @@ -432,17 +469,26 @@ impl ScriptContext for BareCtx { Ok(()) } - fn check_global_consensus_validity( - ms: &Miniscript, + fn check_global_consensus_validity< + Pk: MiniscriptKey, + Ctx: ScriptContext, + Ext: Extension, + >( + ms: &Miniscript, ) -> Result<(), ScriptContextError> { if ms.ext.pk_cost > MAX_SCRIPT_SIZE { return Err(ScriptContextError::MaxWitnessScriptSizeExceeded); } + if let Terminal::Ext(ref _e) = ms.node { + return Err(ScriptContextError::ExtensionError(String::from( + "No Extensions in Bare context", + ))); + } Ok(()) } - fn check_local_consensus_validity( - ms: &Miniscript, + fn check_local_consensus_validity>( + ms: &Miniscript, ) -> Result<(), ScriptContextError> { match ms.ext.ops_count_sat { None => Err(ScriptContextError::MaxOpCountExceeded), @@ -453,8 +499,8 @@ impl ScriptContext for BareCtx { } } - fn other_top_level_checks( - ms: &Miniscript, + fn other_top_level_checks>( + ms: &Miniscript, ) -> Result<(), Error> { match &ms.node { Terminal::Check(ref ms) => match &ms.node { @@ -467,8 +513,8 @@ impl ScriptContext for BareCtx { } } - fn max_satisfaction_size( - ms: &Miniscript, + fn max_satisfaction_size>( + ms: &Miniscript, ) -> Option { // The witness stack cost is the first element of the tuple ms.ext.max_sat_size.map(|x| x.1) @@ -483,38 +529,42 @@ impl ScriptContext for BareCtx { #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] pub enum NoChecks {} impl ScriptContext for NoChecks { - fn check_terminal_non_malleable( - _frag: &Terminal, + fn check_terminal_non_malleable>( + _frag: &Terminal, ) -> Result<(), ScriptContextError> { Ok(()) } - fn check_global_policy_validity( - _ms: &Miniscript, + fn check_global_policy_validity>( + _ms: &Miniscript, ) -> Result<(), ScriptContextError> { Ok(()) } - fn check_global_consensus_validity( - _ms: &Miniscript, + fn check_global_consensus_validity< + Pk: MiniscriptKey, + Ctx: ScriptContext, + Ext: Extension, + >( + _ms: &Miniscript, ) -> Result<(), ScriptContextError> { Ok(()) } - fn check_local_policy_validity( - _ms: &Miniscript, + fn check_local_policy_validity>( + _ms: &Miniscript, ) -> Result<(), ScriptContextError> { Ok(()) } - fn check_local_consensus_validity( - _ms: &Miniscript, + fn check_local_consensus_validity>( + _ms: &Miniscript, ) -> Result<(), ScriptContextError> { Ok(()) } - fn max_satisfaction_size( - _ms: &Miniscript, + fn max_satisfaction_size>( + _ms: &Miniscript, ) -> Option { panic!("Tried to compute a satisfaction size bound on a no-checks miniscript") } diff --git a/src/miniscript/decode.rs b/src/miniscript/decode.rs index 25705f8c..2c2a83fe 100644 --- a/src/miniscript/decode.rs +++ b/src/miniscript/decode.rs @@ -30,6 +30,8 @@ use std::sync::Arc; use Error; use MiniscriptKey; +use Extension; + fn return_none(_: usize) -> Option { None } @@ -63,7 +65,7 @@ enum NonTerm { /// All AST elements #[allow(broken_intra_doc_links)] #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum Terminal { +pub enum Terminal> { /// `1` True, /// `0` @@ -87,76 +89,64 @@ pub enum Terminal { Ripemd160(ripemd160::Hash), /// `SIZE 32 EQUALVERIFY HASH160 EQUAL` Hash160(hash160::Hash), - // Elements - /// `DEPTH <12> SUB PICK EQUAL` - Version(u32), - /// Prefix is initally encoded in the script pubkey - /// User provides a suffix such that hash of (prefix || suffix) - /// is equal to hashOutputs - /// Since, there is a policy restriction that initial pushes must be - /// only 80 bytes, we need user to provide suffix in separate items - /// There can be atmost 7 cats, because the script element must be less - /// than 520 bytes total in order to compute an hash256 on it. - /// Even if the witness does not require 7 pushes, the user should push - /// 7 elements with possibly empty values. - /// - /// CAT CAT CAT CAT CAT CAT SWAP CAT /*Now we hashoutputs on stack */ - /// HASH256 - /// DEPTH <10> SUB PICK EQUALVERIFY - OutputsPref(Vec), // Wrappers /// `TOALTSTACK [E] FROMALTSTACK` - Alt(Arc>), + Alt(Arc>), /// `SWAP [E1]` - Swap(Arc>), + Swap(Arc>), /// `[Kt]/[Ke] CHECKSIG` - Check(Arc>), + Check(Arc>), /// `DUP IF [V] ENDIF` - DupIf(Arc>), + DupIf(Arc>), /// [T] VERIFY - Verify(Arc>), + Verify(Arc>), /// SIZE 0NOTEQUAL IF [Fn] ENDIF - NonZero(Arc>), + NonZero(Arc>), /// [X] 0NOTEQUAL - ZeroNotEqual(Arc>), + ZeroNotEqual(Arc>), // Conjunctions /// [V] [T]/[V]/[F]/[Kt] - AndV(Arc>, Arc>), + AndV(Arc>, Arc>), /// [E] [W] BOOLAND - AndB(Arc>, Arc>), + AndB(Arc>, Arc>), /// [various] NOTIF [various] ELSE [various] ENDIF AndOr( - Arc>, - Arc>, - Arc>, + Arc>, + Arc>, + Arc>, ), // Disjunctions /// [E] [W] BOOLOR - OrB(Arc>, Arc>), + OrB(Arc>, Arc>), /// [E] IFDUP NOTIF [T]/[E] ENDIF - OrD(Arc>, Arc>), + OrD(Arc>, Arc>), /// [E] NOTIF [V] ENDIF - OrC(Arc>, Arc>), + OrC(Arc>, Arc>), /// IF [various] ELSE [various] ENDIF - OrI(Arc>, Arc>), + OrI(Arc>, Arc>), // Thresholds /// [E] ([W] ADD)* k EQUAL - Thresh(usize, Vec>>), + Thresh(usize, Vec>>), /// k ()* n CHECKMULTISIG Multi(usize, Vec), + /// Extensions + Ext(Ext), } ///Vec representing terminals stack while decoding. -struct TerminalStack(Vec>); +#[derive(Debug)] +struct TerminalStack>( + Vec>, +); -impl TerminalStack { +impl> TerminalStack { ///Wrapper around self.0.pop() - fn pop(&mut self) -> Option> { + fn pop(&mut self) -> Option> { self.0.pop() } ///reduce, type check and push a 0-arg node - fn reduce0(&mut self, ms: Terminal) -> Result<(), Error> { + fn reduce0(&mut self, ms: Terminal) -> Result<(), Error> { let ty = Type::type_check(&ms, return_none)?; let ext = ExtData::type_check(&ms, return_none)?; let ms = Miniscript { @@ -173,7 +163,7 @@ impl TerminalStack { ///reduce, type check and push a 1-arg node fn reduce1(&mut self, wrap: F) -> Result<(), Error> where - F: FnOnce(Arc>) -> Terminal, + F: FnOnce(Arc>) -> Terminal, { let top = self.pop().unwrap(); let wrapped_ms = wrap(Arc::new(top)); @@ -194,7 +184,10 @@ impl TerminalStack { ///reduce, type check and push a 2-arg node fn reduce2(&mut self, wrap: F) -> Result<(), Error> where - F: FnOnce(Arc>, Arc>) -> Terminal, + F: FnOnce( + Arc>, + Arc>, + ) -> Terminal, { let left = self.pop().unwrap(); let right = self.pop().unwrap(); @@ -217,9 +210,9 @@ impl TerminalStack { /// Parse a script fragment into an `Terminal` #[allow(unreachable_patterns)] -pub fn parse( +pub fn parse>( tokens: &mut TokenIter, -) -> Result, Error> { +) -> Result, Error> { let mut non_term = Vec::with_capacity(tokens.len()); let mut term = TerminalStack(Vec::with_capacity(tokens.len())); @@ -227,6 +220,18 @@ pub fn parse( non_term.push(NonTerm::MaybeSwap); non_term.push(NonTerm::Expression); loop { + // Parse extensions as expressions + if let Some(NonTerm::Expression) = non_term.last() { + match Ext::from_token_iter(tokens) { + Ok(ext) => { + // Since we successfully parsed the expression, pop it + non_term.pop(); + term.reduce0(Terminal::Ext(ext))?; + continue; + } + Err(..) => {} + } + } match non_term.pop() { Some(NonTerm::Expression) => { match_token!( @@ -282,24 +287,6 @@ pub fn parse( ))? }, ), - Tk::PickPush4(ver), Tk::Sub=> match_token!( - tokens, - Tk::Num(12), Tk::Depth => { - non_term.push(NonTerm::Verify); - term.reduce0(Terminal::Version(ver))? - }, - ), - Tk::Pick, Tk::Sub => match_token!( - tokens, - Tk::Num(4), Tk::Depth => match_token!( - tokens, - Tk::Hash256, Tk::Cat, Tk::Swap, Tk::Push(bytes), Tk::Cat, Tk::Cat, Tk::Cat, Tk::Cat, Tk::Cat, Tk::Cat => - { - non_term.push(NonTerm::Verify); - term.reduce0(Terminal::OutputsPref(bytes))? - }, - ), - ), Tk::Num(k) => { non_term.push(NonTerm::Verify); non_term.push(NonTerm::ThreshW { @@ -307,6 +294,12 @@ pub fn parse( n: 0 }); }, + x => { + tokens.un_next(x); + tokens.un_next(Tk::Equal); + non_term.push(NonTerm::Verify); + non_term.push(NonTerm::Expression); + }, ), x => { tokens.un_next(x); @@ -360,18 +353,6 @@ pub fn parse( hash160::Hash::from_inner(hash) ))?, ), - Tk::PickPush4(ver), Tk::Sub => match_token!( - tokens, - Tk::Num(12), Tk::Depth => term.reduce0(Terminal::Version(ver))?, - ), - Tk::Pick, Tk::Sub => match_token!( - tokens, - Tk::Num(4), Tk::Depth => match_token!( - tokens, - Tk::Hash256, Tk::Cat, Tk::Swap, Tk::Push(bytes), Tk::Cat, Tk::Cat, Tk::Cat, Tk::Cat, Tk::Cat, Tk::Cat => - term.reduce0(Terminal::OutputsPref(bytes))?, - ), - ), // thresholds Tk::Num(k) => { non_term.push(NonTerm::ThreshW { diff --git a/src/miniscript/iter.rs b/src/miniscript/iter.rs index 09b151e9..37eadd1f 100644 --- a/src/miniscript/iter.rs +++ b/src/miniscript/iter.rs @@ -16,44 +16,46 @@ //! //! Iterators for Miniscript with special functions for iterating //! over Public Keys, Public Key Hashes or both. +use Extension; + use super::decode::Terminal; use super::{Miniscript, MiniscriptKey, ScriptContext}; use std::ops::Deref; use std::sync::Arc; /// Iterator-related extensions for [Miniscript] -impl Miniscript { +impl> Miniscript { /// Creates a new [Iter] iterator that will iterate over all [Miniscript] items within /// AST by traversing its branches. For the specific algorithm please see /// [Iter::next] function. - pub fn iter(&self) -> Iter { + pub fn iter(&self) -> Iter { Iter::new(self) } /// Creates a new [PkIter] iterator that will iterate over all plain public keys (and not /// key hash values) present in [Miniscript] items within AST by traversing all its branches. /// For the specific algorithm please see [PkIter::next] function. - pub fn iter_pk(&self) -> PkIter { + pub fn iter_pk(&self) -> PkIter { PkIter::new(self) } /// Creates a new [PkhIter] iterator that will iterate over all public keys hashes (and not /// plain public keys) present in Miniscript items within AST by traversing all its branches. /// For the specific algorithm please see [PkhIter::next] function. - pub fn iter_pkh(&self) -> PkhIter { + pub fn iter_pkh(&self) -> PkhIter { PkhIter::new(self) } /// Creates a new [PkPkhIter] iterator that will iterate over all plain public keys and /// key hash values present in Miniscript items within AST by traversing all its branches. /// For the specific algorithm please see [PkPkhIter::next] function. - pub fn iter_pk_pkh(&self) -> PkPkhIter { + pub fn iter_pk_pkh(&self) -> PkPkhIter { PkPkhIter::new(self) } /// Enumerates all child nodes of the current AST node (`self`) and returns a `Vec` referencing /// them. - pub fn branches(&self) -> Vec<&Miniscript> { + pub fn branches(&self) -> Vec<&Miniscript> { match self.node { Terminal::PkK(_) | Terminal::PkH(_) | Terminal::Multi(_, _) => vec![], @@ -81,7 +83,7 @@ impl Miniscript { } /// Returns child node with given index, if any - pub fn get_nth_child(&self, n: usize) -> Option<&Miniscript> { + pub fn get_nth_child(&self, n: usize) -> Option<&Miniscript> { match (n, &self.node) { (0, &Terminal::Alt(ref node)) | (0, &Terminal::Swap(ref node)) @@ -209,16 +211,16 @@ impl Miniscript { /// Iterator for traversing all [Miniscript] miniscript AST references starting from some specific /// node which constructs the iterator via [Miniscript::iter] method. -pub struct Iter<'a, Pk: 'a + MiniscriptKey, Ctx: 'a + ScriptContext> { - next: Option<&'a Miniscript>, +pub struct Iter<'a, Pk: 'a + MiniscriptKey, Ctx: 'a + ScriptContext, Ext: 'a + Extension> { + next: Option<&'a Miniscript>, // Here we store vec of path elements, where each element is a tuple, consisting of: // 1. Miniscript node on the path // 2. Index of the current branch - path: Vec<(&'a Miniscript, usize)>, + path: Vec<(&'a Miniscript, usize)>, } -impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> Iter<'a, Pk, Ctx> { - fn new(miniscript: &'a Miniscript) -> Self { +impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext, Ext: Extension> Iter<'a, Pk, Ctx, Ext> { + fn new(miniscript: &'a Miniscript) -> Self { Iter { next: Some(miniscript), path: vec![], @@ -226,8 +228,10 @@ impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> Iter<'a, Pk, Ctx> { } } -impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> Iterator for Iter<'a, Pk, Ctx> { - type Item = &'a Miniscript; +impl<'a, Pk: MiniscriptKey, Ctx: 'a + ScriptContext, Ext: 'a + Extension> Iterator + for Iter<'a, Pk, Ctx, Ext> +{ + type Item = &'a Miniscript; /// First, the function returns `self`, then the first child of the self (if any), /// then proceeds to the child of the child — down to a leaf of the tree in its first branch. @@ -271,14 +275,14 @@ impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> Iterator for Iter<'a, Pk, Ctx> { /// Iterator for traversing all [MiniscriptKey]'s in AST starting from some specific node which /// constructs the iterator via [Miniscript::iter_pk] method. -pub struct PkIter<'a, Pk: 'a + MiniscriptKey, Ctx: 'a + ScriptContext> { - node_iter: Iter<'a, Pk, Ctx>, - curr_node: Option<&'a Miniscript>, +pub struct PkIter<'a, Pk: 'a + MiniscriptKey, Ctx: 'a + ScriptContext, Ext: 'a + Extension> { + node_iter: Iter<'a, Pk, Ctx, Ext>, + curr_node: Option<&'a Miniscript>, key_index: usize, } -impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> PkIter<'a, Pk, Ctx> { - fn new(miniscript: &'a Miniscript) -> Self { +impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext, Ext: Extension> PkIter<'a, Pk, Ctx, Ext> { + fn new(miniscript: &'a Miniscript) -> Self { let mut iter = Iter::new(miniscript); PkIter { curr_node: iter.next(), @@ -288,7 +292,9 @@ impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> PkIter<'a, Pk, Ctx> { } } -impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> Iterator for PkIter<'a, Pk, Ctx> { +impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext, Ext: Extension> Iterator + for PkIter<'a, Pk, Ctx, Ext> +{ type Item = Pk; fn next(&mut self) -> Option { @@ -313,14 +319,14 @@ impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> Iterator for PkIter<'a, Pk, Ctx> /// Iterator for traversing all [MiniscriptKey] hashes in AST starting from some specific node which /// constructs the iterator via [Miniscript::iter_pkh] method. -pub struct PkhIter<'a, Pk: 'a + MiniscriptKey, Ctx: 'a + ScriptContext> { - node_iter: Iter<'a, Pk, Ctx>, - curr_node: Option<&'a Miniscript>, +pub struct PkhIter<'a, Pk: 'a + MiniscriptKey, Ctx: 'a + ScriptContext, Ext: 'a + Extension> { + node_iter: Iter<'a, Pk, Ctx, Ext>, + curr_node: Option<&'a Miniscript>, key_index: usize, } -impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> PkhIter<'a, Pk, Ctx> { - fn new(miniscript: &'a Miniscript) -> Self { +impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext, Ext: Extension> PkhIter<'a, Pk, Ctx, Ext> { + fn new(miniscript: &'a Miniscript) -> Self { let mut iter = Iter::new(miniscript); PkhIter { curr_node: iter.next(), @@ -330,7 +336,9 @@ impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> PkhIter<'a, Pk, Ctx> { } } -impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> Iterator for PkhIter<'a, Pk, Ctx> { +impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext, Ext: Extension> Iterator + for PkhIter<'a, Pk, Ctx, Ext> +{ type Item = Pk::Hash; fn next(&mut self) -> Option { @@ -365,14 +373,14 @@ pub enum PkPkh { /// Iterator for traversing all [MiniscriptKey]'s and hashes, depending what data are present in AST, /// starting from some specific node which constructs the iterator via /// [Miniscript::iter_pk_pkh] method. -pub struct PkPkhIter<'a, Pk: 'a + MiniscriptKey, Ctx: 'a + ScriptContext> { - node_iter: Iter<'a, Pk, Ctx>, - curr_node: Option<&'a Miniscript>, +pub struct PkPkhIter<'a, Pk: 'a + MiniscriptKey, Ctx: 'a + ScriptContext, Ext: 'a + Extension> { + node_iter: Iter<'a, Pk, Ctx, Ext>, + curr_node: Option<&'a Miniscript>, key_index: usize, } -impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> PkPkhIter<'a, Pk, Ctx> { - fn new(miniscript: &'a Miniscript) -> Self { +impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext, Ext: Extension> PkPkhIter<'a, Pk, Ctx, Ext> { + fn new(miniscript: &'a Miniscript) -> Self { let mut iter = Iter::new(miniscript); PkPkhIter { curr_node: iter.next(), @@ -406,7 +414,9 @@ impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> PkPkhIter<'a, Pk, Ctx> { } } -impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> Iterator for PkPkhIter<'a, Pk, Ctx> { +impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext, Ext: Extension> Iterator + for PkPkhIter<'a, Pk, Ctx, Ext> +{ type Item = PkPkh; fn next(&mut self) -> Option { @@ -438,9 +448,10 @@ pub mod test { use elements::hashes::{hash160, ripemd160, sha256, sha256d, Hash}; use elements::secp256k1_zkp; use miniscript::context::Segwitv0; + use AllExt; pub type TestData = ( - Miniscript, + Miniscript, Vec, Vec, bool, // Indicates that the top-level contains public key or hashes diff --git a/src/miniscript/lex.rs b/src/miniscript/lex.rs index 5a463ba6..2b16dc51 100644 --- a/src/miniscript/lex.rs +++ b/src/miniscript/lex.rs @@ -97,6 +97,9 @@ impl fmt::Display for Token { #[derive(Debug, Clone)] /// Iterator that goes through a vector of tokens backward (our parser wants to read /// backward and this is more efficient anyway since we can use `Vec::pop()`). +// This really does not need to be an iterator because the way we are using it, we are +// actually collecting lexed symbols into a vector. If that is the case, might as well +// use the inner vector directly pub struct TokenIter(Vec); impl TokenIter { @@ -110,6 +113,28 @@ impl TokenIter { self.0.last() } + /// Look at the slice with the last n elements + pub fn peek_slice(&self, n: usize) -> Option<&[Token]> { + if n <= self.len() { + Some(self.0[self.len() - n..].as_ref()) + } else { + None + } + } + + /// Advance the iterator n times + /// Returns Some(()) if the iterator can be advanced n times + pub fn advance(&mut self, n: usize) -> Option<()> { + if n <= self.len() { + for _ in 0..n { + self.next(); + } + Some(()) + } else { + None + } + } + /// Push a value to the iterator /// This will be first value consumed by popun_ pub fn un_next(&mut self, tok: Token) { diff --git a/src/miniscript/mod.rs b/src/miniscript/mod.rs index b6134f95..3c0152f2 100644 --- a/src/miniscript/mod.rs +++ b/src/miniscript/mod.rs @@ -48,6 +48,7 @@ pub use miniscript::context::ScriptContext; use miniscript::decode::Terminal; use miniscript::types::extra_props::ExtData; use miniscript::types::Type; +use Extension; use std::cmp; use std::sync::Arc; @@ -56,9 +57,9 @@ use {expression, Error, ForEach, ForEachKey, ToPublicKey, TranslatePk}; /// Top-level script AST type #[derive(Clone, Hash)] -pub struct Miniscript { +pub struct Miniscript> { ///A node in the Abstract Syntax Tree( - pub node: Terminal, + pub node: Terminal, ///The correctness and malleability type information for the AST node pub ty: types::Type, ///Additional information helpful for extra analysis. @@ -70,8 +71,10 @@ pub struct Miniscript { /// `PartialOrd` of `Miniscript` must depend only on node and not the type information. /// The type information and extra_properties can be deterministically determined /// by the ast. -impl PartialOrd for Miniscript { - fn partial_cmp(&self, other: &Miniscript) -> Option { +impl> PartialOrd + for Miniscript +{ + fn partial_cmp(&self, other: &Miniscript) -> Option { Some(self.node.cmp(&other.node)) } } @@ -79,8 +82,8 @@ impl PartialOrd for Miniscript { /// `Ord` of `Miniscript` must depend only on node and not the type information. /// The type information and extra_properties can be deterministically determined /// by the ast. -impl Ord for Miniscript { - fn cmp(&self, other: &Miniscript) -> cmp::Ordering { +impl> Ord for Miniscript { + fn cmp(&self, other: &Miniscript) -> cmp::Ordering { self.node.cmp(&other.node) } } @@ -88,8 +91,10 @@ impl Ord for Miniscript { /// `PartialEq` of `Miniscript` must depend only on node and not the type information. /// The type information and extra_properties can be deterministically determined /// by the ast. -impl PartialEq for Miniscript { - fn eq(&self, other: &Miniscript) -> bool { +impl> PartialEq + for Miniscript +{ + fn eq(&self, other: &Miniscript) -> bool { self.node.eq(&other.node) } } @@ -97,19 +102,21 @@ impl PartialEq for Miniscript { /// `Eq` of `Miniscript` must depend only on node and not the type information. /// The type information and extra_properties can be deterministically determined /// by the ast. -impl Eq for Miniscript {} +impl> Eq for Miniscript {} -impl fmt::Debug for Miniscript { +impl> fmt::Debug + for Miniscript +{ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}", self.node) } } -impl Miniscript { +impl> Miniscript { /// Add type information(Type and Extdata) to Miniscript based on /// `AstElem` fragment. Dependent on display and clone because of Error /// Display code of type_check. - pub fn from_ast(t: Terminal) -> Result, Error> { + pub fn from_ast(t: Terminal) -> Result, Error> { Ok(Miniscript { ty: Type::type_check(&t, |_| None)?, ext: ExtData::type_check(&t, |_| None)?, @@ -119,25 +126,31 @@ impl Miniscript { } } -impl fmt::Display for Miniscript { +impl> fmt::Display + for Miniscript +{ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.node) } } -impl Miniscript { +impl> Miniscript { /// Extracts the `AstElem` representing the root of the miniscript - pub fn into_inner(self) -> Terminal { + pub fn into_inner(self) -> Terminal { self.node } /// Get a reference to the inner `AstElem` representing the root of miniscript - pub fn as_inner(&self) -> &Terminal { + pub fn as_inner(&self) -> &Terminal { &self.node } } -impl Miniscript { +impl Miniscript +where + Ctx: ScriptContext, + Ext: Extension, +{ /// Attempt to parse an insane(scripts don't clear sanity checks) /// script into a Miniscript representation. /// Use this to parse scripts with repeated pubkeys, timelock mixing, malleable @@ -147,7 +160,7 @@ impl Miniscript { /// accept sane scripts. pub fn parse_insane( script: &script::Script, - ) -> Result, Error> { + ) -> Result, Error> { let tokens = lex(script)?; let mut iter = TokenIter::new(tokens); @@ -168,17 +181,20 @@ impl Miniscript { /// This function will fail parsing for scripts that do not clear /// the [Miniscript::sanity_check] checks. Use [Miniscript::parse_insane] to /// parse such scripts. - pub fn parse(script: &script::Script) -> Result, Error> { + pub fn parse( + script: &script::Script, + ) -> Result, Error> { let ms = Self::parse_insane(script)?; ms.sanity_check()?; Ok(ms) } } -impl Miniscript +impl Miniscript where Pk: MiniscriptKey, Ctx: ScriptContext, + Ext: Extension, { /// Encode as a Bitcoin script pub fn encode(&self) -> script::Script @@ -200,7 +216,7 @@ where } } -impl Miniscript { +impl> Miniscript { /// Maximum number of witness elements used to satisfy the Miniscript /// fragment, including the witness script itself. Used to estimate /// the weight of the `VarInt` that specifies this number in a serialized @@ -228,11 +244,13 @@ impl Miniscript { /// length prefix (segwit) or push opcode (pre-segwit) and sighash /// postfix. pub fn max_satisfaction_size(&self) -> Result { - Ctx::max_satisfaction_size(self).ok_or(Error::ImpossibleSatisfaction) + Ctx::max_satisfaction_size::(self).ok_or(Error::ImpossibleSatisfaction) } } -impl ForEachKey for Miniscript { +impl> ForEachKey + for Miniscript +{ fn for_each_key<'a, F: FnMut(ForEach<'a, Pk>) -> bool>(&'a self, mut pred: F) -> bool where Pk: 'a, @@ -242,10 +260,15 @@ impl ForEachKey for Miniscript TranslatePk - for Miniscript +impl TranslatePk for Miniscript +where + Pk: MiniscriptKey, + Q: MiniscriptKey, + Ctx: ScriptContext, + Ext: Extension + TranslatePk, + >::Output: Extension, { - type Output = Miniscript; + type Output = Miniscript>::Output>; /// This will panic if translatefpk returns an uncompressed key when /// converting to a Segwit descriptor. To prevent this panic, ensure @@ -263,7 +286,7 @@ impl TranslatePk } } -impl Miniscript { +impl> Miniscript { fn real_for_each_key<'a, F: FnMut(ForEach<'a, Pk>) -> bool>(&'a self, pred: &mut F) -> bool where Pk: 'a, @@ -276,11 +299,13 @@ impl Miniscript { &self, translatefpk: &mut FPk, translatefpkh: &mut FPkh, - ) -> Result, FuncError> + ) -> Result>::Output>, FuncError> where FPk: FnMut(&Pk) -> Result, FPkh: FnMut(&Pk::Hash) -> Result, Q: MiniscriptKey, + Ext: TranslatePk, + >::Output: Extension, { let inner = self.node.real_translate_pk(translatefpk, translatefpkh)?; let ms = Miniscript { @@ -301,7 +326,7 @@ impl Miniscript { /// Some of the analysis guarantees of miniscript are lost when dealing with /// insane scripts. In general, in a multi-party setting users should only /// accept sane scripts. - pub fn from_str_insane(s: &str) -> Result, Error> + pub fn from_str_insane(s: &str) -> Result, Error> where Pk: str::FromStr, Pk::Hash: str::FromStr, @@ -315,7 +340,7 @@ impl Miniscript { } let top = expression::Tree::from_str(s)?; - let ms: Miniscript = expression::FromTree::from_tree(&top)?; + let ms: Miniscript = expression::FromTree::from_tree(&top)?; if ms.ty.corr.base != types::Base::B { Err(Error::NonTopLevel(format!("{:?}", ms))) @@ -325,7 +350,7 @@ impl Miniscript { } } -impl Miniscript { +impl> Miniscript { /// Attempt to produce non-malleable satisfying witness for the /// witness script represented by the parse tree pub fn satisfy>(&self, satisfier: S) -> Result>, Error> @@ -334,7 +359,7 @@ impl Miniscript { { match satisfy::Satisfaction::satisfy(&self.node, &satisfier, self.ty.mall.safe).stack { satisfy::Witness::Stack(stack) => { - Ctx::check_witness::(&stack)?; + Ctx::check_witness::(&stack)?; Ok(stack) } satisfy::Witness::Unavailable | satisfy::Witness::Impossible => { @@ -354,7 +379,7 @@ impl Miniscript { { match satisfy::Satisfaction::satisfy_mall(&self.node, &satisfier, self.ty.mall.safe).stack { satisfy::Witness::Stack(stack) => { - Ctx::check_witness::(&stack)?; + Ctx::check_witness::(&stack)?; Ok(stack) } satisfy::Witness::Unavailable | satisfy::Witness::Impossible => { @@ -364,31 +389,33 @@ impl Miniscript { } } -impl expression::FromTree for Arc> +impl expression::FromTree for Arc> where Pk: MiniscriptKey + str::FromStr, Pk::Hash: str::FromStr, Ctx: ScriptContext, + Ext: Extension, ::Err: ToString, <::Hash as str::FromStr>::Err: ToString, { - fn from_tree(top: &expression::Tree) -> Result>, Error> { + fn from_tree(top: &expression::Tree) -> Result>, Error> { Ok(Arc::new(expression::FromTree::from_tree(top)?)) } } -impl expression::FromTree for Miniscript +impl expression::FromTree for Miniscript where Pk: MiniscriptKey + str::FromStr, Pk::Hash: str::FromStr, Ctx: ScriptContext, + Ext: Extension, ::Err: ToString, <::Hash as str::FromStr>::Err: ToString, { /// Parse an expression tree into a Miniscript. As a general rule, this /// should not be called directly; rather go through the descriptor API. - fn from_tree(top: &expression::Tree) -> Result, Error> { - let inner: Terminal = expression::FromTree::from_tree(top)?; + fn from_tree(top: &expression::Tree) -> Result, Error> { + let inner: Terminal = expression::FromTree::from_tree(top)?; Ok(Miniscript { ty: Type::type_check(&inner, |_| None)?, ext: ExtData::type_check(&inner, |_| None)?, @@ -401,24 +428,25 @@ where /// Parse a Miniscript from string and perform sanity checks /// See [Miniscript::from_str_insane] to parse scripts from string that /// do not clear the [Miniscript::sanity_check] checks. -impl str::FromStr for Miniscript +impl str::FromStr for Miniscript where Pk: MiniscriptKey + str::FromStr, Pk::Hash: str::FromStr, Ctx: ScriptContext, + Ext: Extension, ::Err: ToString, <::Hash as str::FromStr>::Err: ToString, { type Err = Error; - fn from_str(s: &str) -> Result, Error> { + fn from_str(s: &str) -> Result, Error> { let ms = Self::from_str_insane(s)?; ms.sanity_check()?; Ok(ms) } } -serde_string_impl_pk!(Miniscript, "a miniscript", Ctx; ScriptContext); +serde_string_impl_pk!(Miniscript, "a miniscript", Ctx; ScriptContext => Ext2 ; Extension); #[cfg(test)] mod tests { @@ -437,8 +465,9 @@ mod tests { use std::str; use std::str::FromStr; use std::sync::Arc; + use AllExt; - type Segwitv0Script = Miniscript; + type Segwitv0Script = Miniscript; fn pubkeys(n: usize) -> Vec { let mut ret = Vec::with_capacity(n); @@ -462,7 +491,7 @@ mod tests { } fn string_rtt( - script: Miniscript, + script: Miniscript, expected_debug: Str1, expected_display: Str2, ) where @@ -587,7 +616,7 @@ mod tests { #[test] fn recursive_key_parsing() { - type MsStr = Miniscript; + type MsStr = Miniscript; assert!(MsStr::from_str("pk(slip77(k))").is_ok()); assert!(MsStr::from_str("pk(musig(a))").is_ok()); assert!(MsStr::from_str("pk(musig(a,b))").is_ok()); @@ -611,7 +640,7 @@ mod tests { .unwrap(); let hash = hash160::Hash::from_inner([17; 20]); - let pkk_ms: Miniscript = Miniscript { + let pkk_ms: Miniscript = Miniscript { node: Terminal::Check(Arc::new(Miniscript { node: Terminal::PkK(DummyKey), ty: Type::from_pk_k(), @@ -624,7 +653,7 @@ mod tests { }; string_rtt(pkk_ms, "[B/onduesm]c:[K/onduesm]pk_k(DummyKey)", "pk()"); - let pkh_ms: Miniscript = Miniscript { + let pkh_ms: Miniscript = Miniscript { node: Terminal::Check(Arc::new(Miniscript { node: Terminal::PkH(DummyKeyHash), ty: Type::from_pk_h(), diff --git a/src/miniscript/satisfy.rs b/src/miniscript/satisfy.rs index 2e7f2ce6..39709060 100644 --- a/src/miniscript/satisfy.rs +++ b/src/miniscript/satisfy.rs @@ -23,17 +23,13 @@ use std::sync::Arc; use std::{cmp, i64, mem}; use bitcoin; +use elements::hashes::{hash160, ripemd160, sha256, sha256d}; use elements::{self, secp256k1_zkp}; use elements::{confidential, OutPoint, Script}; -use elements::{ - encode::serialize, - hashes::{hash160, ripemd160, sha256, sha256d}, -}; use {MiniscriptKey, ToPublicKey}; use miniscript::limits::{ - HEIGHT_TIME_THRESHOLD, MAX_SCRIPT_ELEMENT_SIZE, MAX_STANDARD_P2WSH_STACK_ITEM_SIZE, - SEQUENCE_LOCKTIME_DISABLE_FLAG, SEQUENCE_LOCKTIME_TYPE_FLAG, + HEIGHT_TIME_THRESHOLD, SEQUENCE_LOCKTIME_DISABLE_FLAG, SEQUENCE_LOCKTIME_TYPE_FLAG, }; use util::witness_size; use Error; @@ -41,6 +37,8 @@ use Miniscript; use ScriptContext; use Terminal; +use Extension; + /// Type alias for a signature/hashtype pair pub type ElementsSig = (secp256k1_zkp::Signature, elements::SigHashType); /// Type alias for 32 byte Preimage. @@ -669,7 +667,7 @@ impl Ord for Witness { impl Witness { /// Turn a signature into (part of) a satisfaction - fn signature>(sat: S, pk: &Pk) -> Self { + pub fn signature>(sat: S, pk: &Pk) -> Self { match sat.lookup_sig(pk) { Some((sig, hashtype)) => { let mut ret = sig.serialize_der().to_vec(); @@ -682,7 +680,7 @@ impl Witness { } /// Turn a public key related to a pkh into (part of) a satisfaction - fn pkh_public_key>(sat: S, pkh: &Pk::Hash) -> Self { + pub fn pkh_public_key>(sat: S, pkh: &Pk::Hash) -> Self { match sat.lookup_pkh_pk(pkh) { Some(pk) => Witness::Stack(vec![pk.to_public_key().to_bytes()]), // public key hashes are assumed to be unavailable @@ -692,7 +690,7 @@ impl Witness { } /// Turn a key/signature pair related to a pkh into (part of) a satisfaction - fn pkh_signature>(sat: S, pkh: &Pk::Hash) -> Self { + pub fn pkh_signature>(sat: S, pkh: &Pk::Hash) -> Self { match sat.lookup_pkh_sig(pkh) { Some((pk, (sig, hashtype))) => { let mut ret = sig.serialize_der().to_vec(); @@ -704,7 +702,10 @@ impl Witness { } /// Turn a hash preimage into (part of) a satisfaction - fn ripemd160_preimage>(sat: S, h: ripemd160::Hash) -> Self { + pub fn ripemd160_preimage>( + sat: S, + h: ripemd160::Hash, + ) -> Self { match sat.lookup_ripemd160(h) { Some(pre) => Witness::Stack(vec![pre.to_vec()]), // Note hash preimages are unavailable instead of impossible @@ -713,7 +714,7 @@ impl Witness { } /// Turn a hash preimage into (part of) a satisfaction - fn hash160_preimage>(sat: S, h: hash160::Hash) -> Self { + pub fn hash160_preimage>(sat: S, h: hash160::Hash) -> Self { match sat.lookup_hash160(h) { Some(pre) => Witness::Stack(vec![pre.to_vec()]), // Note hash preimages are unavailable instead of impossible @@ -722,7 +723,7 @@ impl Witness { } /// Turn a hash preimage into (part of) a satisfaction - fn sha256_preimage>(sat: S, h: sha256::Hash) -> Self { + pub fn sha256_preimage>(sat: S, h: sha256::Hash) -> Self { match sat.lookup_sha256(h) { Some(pre) => Witness::Stack(vec![pre.to_vec()]), // Note hash preimages are unavailable instead of impossible @@ -731,141 +732,38 @@ impl Witness { } /// Turn a hash preimage into (part of) a satisfaction - fn hash256_preimage>(sat: S, h: sha256d::Hash) -> Self { + pub fn hash256_preimage>(sat: S, h: sha256d::Hash) -> 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, } } - - /// Turn a version into (part of) a satisfaction - fn ver_eq_satisfy>(sat: S, n: u32) -> Self { - match sat.lookup_nversion() { - Some(k) => { - if k == n { - Witness::empty() - } else { - Witness::Impossible - } - } - // Note the unavailable instead of impossible because we don't know - // the version - None => Witness::Unavailable, - } - } - - /// Turn a output prefix into (part of) a satisfaction - fn output_pref_satisfy>(sat: S, pref: &[u8]) -> Self { - match sat.lookup_outputs() { - Some(outs) => { - let mut ser_out = Vec::new(); - let num_wit_elems = - MAX_SCRIPT_ELEMENT_SIZE / MAX_STANDARD_P2WSH_STACK_ITEM_SIZE + 1; - let mut witness = Vec::with_capacity(num_wit_elems); - for out in outs { - ser_out.extend(serialize(out)); - } - // We need less than 520 bytes of serialized hashoutputs - // in order to compute hash256 inside script - if ser_out.len() > MAX_SCRIPT_ELEMENT_SIZE { - return Witness::Impossible; - } - if ser_out.starts_with(pref) { - let mut iter = ser_out.into_iter().skip(pref.len()).peekable(); - - while iter.peek().is_some() { - let chk_size = MAX_STANDARD_P2WSH_STACK_ITEM_SIZE; - let chunk: Vec = iter.by_ref().take(chk_size).collect(); - witness.push(chunk); - } - // Append empty elems to make for extra cats - // in the spk - while witness.len() < num_wit_elems { - witness.push(vec![]); - } - Witness::Stack(witness) - } else { - Witness::Impossible - } - } - // Note the unavailable instead of impossible because we don't know - // the hashoutputs yet - None => Witness::Unavailable, - } - } - - /// Dissatisfy ver fragment - fn ver_eq_dissatisfy>(sat: S, n: u32) -> Self { - if let Some(k) = sat.lookup_nversion() { - if k == n { - Witness::Impossible - } else { - Witness::empty() - } - } else { - Witness::empty() - } - } - - /// Turn a output prefix into (part of) a satisfaction - fn output_pref_dissatisfy>(sat: S, pref: &[u8]) -> Self { - match sat.lookup_outputs() { - Some(outs) => { - let mut ser_out = Vec::new(); - for out in outs { - ser_out.extend(serialize(out)); - } - let num_wit_elems = MAX_SCRIPT_ELEMENT_SIZE / MAX_STANDARD_P2WSH_STACK_ITEM_SIZE; - let mut witness = Vec::with_capacity(num_wit_elems); - if pref != ser_out.as_slice() { - while witness.len() < num_wit_elems { - witness.push(vec![]); - } - Witness::Stack(witness) - } else if pref.len() != MAX_SCRIPT_ELEMENT_SIZE { - // Case when prefix == ser_out and it is possible - // to add more witness - witness.push(vec![1]); - while witness.len() < num_wit_elems { - witness.push(vec![]); - } - Witness::Stack(witness) - } else { - // case when pref == ser_out and len of both is 520 - Witness::Impossible - } - } - // Note the unavailable instead of impossible because we don't know - // the hashoutputs yet - None => Witness::Unavailable, - } - } } impl Witness { /// Produce something like a 32-byte 0 push - fn hash_dissatisfaction() -> Self { + pub fn hash_dissatisfaction() -> Self { Witness::Stack(vec![vec![0; 32]]) } /// Construct a satisfaction equivalent to an empty stack - fn empty() -> Self { + pub fn empty() -> Self { Witness::Stack(vec![]) } /// Construct a satisfaction equivalent to `OP_1` - fn push_1() -> Self { + pub fn push_1() -> Self { Witness::Stack(vec![vec![1]]) } /// Construct a satisfaction equivalent to a single empty push - fn push_0() -> Self { + pub fn push_0() -> Self { Witness::Stack(vec![vec![]]) } /// Concatenate, or otherwise combine, two satisfactions - fn combine(one: Self, two: Self) -> Self { + pub fn combine(one: Self, two: Self) -> Self { match (one, two) { (Witness::Impossible, _) | (_, Witness::Impossible) => Witness::Impossible, (Witness::Unavailable, _) | (_, Witness::Unavailable) => Witness::Unavailable, @@ -889,9 +787,9 @@ pub struct Satisfaction { impl Satisfaction { // produce a non-malleable satisafaction for thesh frag - fn thresh( + fn thresh( k: usize, - subs: &[Arc>], + subs: &[Arc>], stfr: &Sat, root_has_sig: bool, min_fn: &mut F, @@ -900,6 +798,7 @@ impl Satisfaction { Pk: MiniscriptKey + ToPublicKey, Ctx: ScriptContext, Sat: Satisfier, + Ext: Extension, F: FnMut(Satisfaction, Satisfaction) -> Satisfaction, { let mut sats = subs @@ -990,9 +889,9 @@ impl Satisfaction { } // produce a possily malleable satisafaction for thesh frag - fn thresh_mall( + fn thresh_mall( k: usize, - subs: &[Arc>], + subs: &[Arc>], stfr: &Sat, root_has_sig: bool, min_fn: &mut F, @@ -1001,6 +900,7 @@ impl Satisfaction { Pk: MiniscriptKey + ToPublicKey, Ctx: ScriptContext, Sat: Satisfier, + Ext: Extension, F: FnMut(Satisfaction, Satisfaction) -> Satisfaction, { let mut sats = subs @@ -1104,8 +1004,8 @@ impl Satisfaction { } // produce a non-malleable satisfaction - fn satisfy_helper( - term: &Terminal, + fn satisfy_helper( + term: &Terminal, stfr: &Sat, root_has_sig: bool, min_fn: &mut F, @@ -1115,8 +1015,9 @@ impl Satisfaction { Pk: MiniscriptKey + ToPublicKey, Ctx: ScriptContext, Sat: Satisfier, + Ext: Extension, F: FnMut(Satisfaction, Satisfaction) -> Satisfaction, - G: FnMut(usize, &[Arc>], &Sat, bool, &mut F) -> Satisfaction, + G: FnMut(usize, &[Arc>], &Sat, bool, &mut F) -> Satisfaction, { match *term { Terminal::PkK(ref pk) => Satisfaction { @@ -1182,14 +1083,6 @@ impl Satisfaction { stack: Witness::Impossible, has_sig: false, }, - Terminal::Version(n) => Satisfaction { - stack: Witness::ver_eq_satisfy(stfr, n), - has_sig: false, - }, - Terminal::OutputsPref(ref pref) => Satisfaction { - stack: Witness::output_pref_satisfy(stfr, pref), - has_sig: false, - }, Terminal::Alt(ref sub) | Terminal::Swap(ref sub) | Terminal::Check(ref sub) @@ -1326,12 +1219,13 @@ impl Satisfaction { } } } + Terminal::Ext(ref e) => e.satisfy(stfr), } } // Helper function to produce a dissatisfaction - fn dissatisfy_helper( - term: &Terminal, + fn dissatisfy_helper( + term: &Terminal, stfr: &Sat, root_has_sig: bool, min_fn: &mut F, @@ -1341,8 +1235,9 @@ impl Satisfaction { Pk: MiniscriptKey + ToPublicKey, Ctx: ScriptContext, Sat: Satisfier, + Ext: Extension, F: FnMut(Satisfaction, Satisfaction) -> Satisfaction, - G: FnMut(usize, &[Arc>], &Sat, bool, &mut F) -> Satisfaction, + G: FnMut(usize, &[Arc>], &Sat, bool, &mut F) -> Satisfaction, { match *term { Terminal::PkK(..) => Satisfaction { @@ -1376,14 +1271,6 @@ impl Satisfaction { stack: Witness::hash_dissatisfaction(), has_sig: false, }, - Terminal::Version(n) => Satisfaction { - stack: Witness::ver_eq_dissatisfy(stfr, n), - has_sig: false, - }, - Terminal::OutputsPref(ref pref) => Satisfaction { - stack: Witness::output_pref_dissatisfy(stfr, pref), - has_sig: false, - }, Terminal::Alt(ref sub) | Terminal::Swap(ref sub) | Terminal::Check(ref sub) @@ -1450,19 +1337,22 @@ impl Satisfaction { stack: Witness::Stack(vec![vec![]; k + 1]), has_sig: false, }, + Terminal::Ext(ref e) => e.dissatisfy(stfr), } } /// Produce a satisfaction non-malleable satisfaction - pub(super) fn satisfy< + pub(super) fn satisfy( + term: &Terminal, + stfr: &Sat, + root_has_sig: bool, + ) -> Self + where Pk: MiniscriptKey + ToPublicKey, Ctx: ScriptContext, Sat: Satisfier, - >( - term: &Terminal, - stfr: &Sat, - root_has_sig: bool, - ) -> Self { + Ext: Extension, + { Self::satisfy_helper( term, stfr, @@ -1477,8 +1367,9 @@ impl Satisfaction { Pk: MiniscriptKey + ToPublicKey, Ctx: ScriptContext, Sat: Satisfier, + Ext: Extension, >( - term: &Terminal, + term: &Terminal, stfr: &Sat, root_has_sig: bool, ) -> Self { diff --git a/src/miniscript/types/correctness.rs b/src/miniscript/types/correctness.rs index 8681eeba..ac8339b3 100644 --- a/src/miniscript/types/correctness.rs +++ b/src/miniscript/types/correctness.rs @@ -15,6 +15,7 @@ //! Correctness/Soundness type properties use super::{ErrorKind, Property}; +use {Extension, MiniscriptKey}; /// Basic type representing where the fragment can go #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] @@ -490,6 +491,10 @@ impl Property for Correctness { }) } + fn from_ext>(e: &E) -> Self { + e.corr_prop() + } + fn threshold(k: usize, n: usize, mut sub_ck: S) -> Result where S: FnMut(usize) -> Result, diff --git a/src/miniscript/types/extra_props.rs b/src/miniscript/types/extra_props.rs index 677f640b..a99345e8 100644 --- a/src/miniscript/types/extra_props.rs +++ b/src/miniscript/types/extra_props.rs @@ -5,6 +5,8 @@ use miniscript::limits::{ HEIGHT_TIME_THRESHOLD, MAX_SCRIPT_ELEMENT_SIZE, SEQUENCE_LOCKTIME_TYPE_FLAG, }; +use Extension; + use super::{Error, ErrorKind, Property, ScriptContext}; use script_num_size; use std::cmp; @@ -854,16 +856,21 @@ impl Property for ExtData { }) } + fn from_ext>(e: &E) -> Self { + e.extra_prop() + } + /// Compute the type of a fragment assuming all the children of /// Miniscript have been computed already. - fn type_check( - fragment: &Terminal, + fn type_check( + fragment: &Terminal, _child: C, - ) -> Result> + ) -> Result> where C: FnMut(usize) -> Option, Ctx: ScriptContext, Pk: MiniscriptKey, + Ext: Extension, { let wrap_err = |result: Result| { result.map_err(|kind| Error { @@ -915,8 +922,6 @@ impl Property for ExtData { Terminal::Hash256(..) => Ok(Self::from_hash256()), Terminal::Ripemd160(..) => Ok(Self::from_ripemd160()), Terminal::Hash160(..) => Ok(Self::from_hash160()), - Terminal::Version(..) => Ok(Self::from_ver_eq()), - Terminal::OutputsPref(ref pref) => Ok(Self::from_output_pref(pref)), Terminal::Alt(ref sub) => wrap_err(Self::cast_alt(sub.ext.clone())), Terminal::Swap(ref sub) => wrap_err(Self::cast_swap(sub.ext.clone())), Terminal::Check(ref sub) => wrap_err(Self::cast_check(sub.ext.clone())), @@ -981,6 +986,7 @@ impl Property for ExtData { error: kind, }) } + Terminal::Ext(ref e) => Ok(Self::from_ext(e)), }; if let Ok(ref ret) = ret { ret.sanity_checks() diff --git a/src/miniscript/types/malleability.rs b/src/miniscript/types/malleability.rs index f0cf22c9..40946a8c 100644 --- a/src/miniscript/types/malleability.rs +++ b/src/miniscript/types/malleability.rs @@ -15,6 +15,7 @@ //! Malleability-related Type properties use super::{ErrorKind, Property}; +use {Extension, MiniscriptKey}; /// Whether the fragment has a dissatisfaction, and if so, whether /// it is unique. Affects both correctness and malleability-freeness, @@ -319,6 +320,10 @@ impl Property for Malleability { }) } + fn from_ext>(e: &E) -> Self { + e.mall_prop() + } + fn threshold(k: usize, n: usize, mut sub_ck: S) -> Result where S: FnMut(usize) -> Result, diff --git a/src/miniscript/types/mod.rs b/src/miniscript/types/mod.rs index e55fbb2f..95fb0fef 100644 --- a/src/miniscript/types/mod.rs +++ b/src/miniscript/types/mod.rs @@ -22,6 +22,8 @@ pub mod malleability; use std::{error, fmt}; +use Extension; + pub use self::correctness::{Base, Correctness, Input}; pub use self::extra_props::ExtData; pub use self::malleability::{Dissat, Malleability}; @@ -97,14 +99,19 @@ pub enum ErrorKind { /// Error type for typechecking #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -pub struct Error { +pub struct Error> { /// The fragment that failed typecheck - pub fragment: Terminal, + pub fragment: Terminal, /// The reason that typechecking failed pub error: ErrorKind, } -impl error::Error for Error { +impl error::Error for Error +where + Pk: MiniscriptKey, + Ctx: ScriptContext, + Ext: Extension, +{ fn cause(&self) -> Option<&error::Error> { None } @@ -114,7 +121,12 @@ impl error::Error for Error { } } -impl fmt::Display for Error { +impl fmt::Display for Error +where + Pk: MiniscriptKey, + Ctx: ScriptContext, + Ext: Extension, +{ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.error { ErrorKind::ZeroTime => write!( @@ -399,17 +411,23 @@ pub trait Property: Sized { where S: FnMut(usize) -> Result; + /// Compute the type of an extension + /// Extensions are always leaf, they should have a fixed type property and hence + /// should not fail + fn from_ext>(e: &E) -> Self; + /// Compute the type of a fragment, given a function to look up /// the types of its children, if available and relevant for the /// given fragment - fn type_check( - fragment: &Terminal, + fn type_check( + fragment: &Terminal, mut child: C, - ) -> Result> + ) -> Result> where C: FnMut(usize) -> Option, Pk: MiniscriptKey, Ctx: ScriptContext, + Ext: Extension, { let mut get_child = |sub, n| { child(n) @@ -466,8 +484,6 @@ pub trait Property: Sized { Terminal::Hash256(..) => Ok(Self::from_hash256()), Terminal::Ripemd160(..) => Ok(Self::from_ripemd160()), Terminal::Hash160(..) => Ok(Self::from_hash160()), - Terminal::Version(..) => Ok(Self::from_ver_eq()), - Terminal::OutputsPref(ref pref) => Ok(Self::from_output_pref(pref)), Terminal::Alt(ref sub) => wrap_err(Self::cast_alt(get_child(&sub.node, 0)?)), Terminal::Swap(ref sub) => wrap_err(Self::cast_swap(get_child(&sub.node, 0)?)), Terminal::Check(ref sub) => wrap_err(Self::cast_check(get_child(&sub.node, 0)?)), @@ -541,6 +557,7 @@ pub trait Property: Sized { error: kind, }) } + Terminal::Ext(ref ext) => Ok(Self::from_ext(ext)), }; if let Ok(ref ret) = ret { ret.sanity_checks() @@ -798,16 +815,24 @@ impl Property for Type { }) } + fn from_ext>(e: &E) -> Self { + Type { + corr: Property::from_ext(e), + mall: Property::from_ext(e), + } + } + /// Compute the type of a fragment assuming all the children of /// Miniscript have been computed already. - fn type_check( - fragment: &Terminal, + fn type_check( + fragment: &Terminal, _child: C, - ) -> Result> + ) -> Result> where C: FnMut(usize) -> Option, Pk: MiniscriptKey, Ctx: ScriptContext, + Ext: Extension, { let wrap_err = |result: Result| { result.map_err(|kind| Error { @@ -859,8 +884,6 @@ impl Property for Type { Terminal::Hash256(..) => Ok(Self::from_hash256()), Terminal::Ripemd160(..) => Ok(Self::from_ripemd160()), Terminal::Hash160(..) => Ok(Self::from_hash160()), - Terminal::Version(..) => Ok(Self::from_item_eq()), - Terminal::OutputsPref(ref pref) => Ok(Self::from_item_pref(pref)), Terminal::Alt(ref sub) => wrap_err(Self::cast_alt(sub.ty.clone())), Terminal::Swap(ref sub) => wrap_err(Self::cast_swap(sub.ty.clone())), Terminal::Check(ref sub) => wrap_err(Self::cast_check(sub.ty.clone())), @@ -925,6 +948,7 @@ impl Property for Type { error: kind, }) } + Terminal::Ext(ref e) => Ok(Self::from_ext(e)), }; if let Ok(ref ret) = ret { ret.sanity_checks() diff --git a/src/policy/compiler.rs b/src/policy/compiler.rs index 78a9f937..32ed148a 100644 --- a/src/policy/compiler.rs +++ b/src/policy/compiler.rs @@ -14,8 +14,8 @@ //! # Policy Compiler //! -//! Optimizing compiler from concrete policies to Miniscript -//! +//! Optimizing compiler from concrete policies to Miniscript. +//! Currently the policy compiler does not support any extensions use std::collections::BTreeMap; use std::convert::From; @@ -29,6 +29,8 @@ use policy::Concrete; use std::collections::vec_deque::VecDeque; use std::hash; use std::sync::Arc; +use Extension; +use NoExt; use {policy, Terminal}; use {Miniscript, MiniscriptKey}; @@ -229,6 +231,10 @@ impl Property for CompilerExtData { } } + fn from_ext>(_e: &E) -> Self { + unreachable!("NoExt context should not have extensions") + } + fn cast_alt(self) -> Result { Ok(CompilerExtData { branch_prob: None, @@ -457,7 +463,7 @@ impl Property for CompilerExtData { #[derive(Clone, Debug)] struct AstElemExt { /// The actual Miniscript fragment with type information - ms: Arc>, + ms: Arc>, /// Its "type" in terms of compiler data comp_ext_data: CompilerExtData, } @@ -479,7 +485,7 @@ impl AstElemExt { } impl AstElemExt { - fn terminal(ast: Terminal) -> AstElemExt { + fn terminal(ast: Terminal) -> AstElemExt { AstElemExt { comp_ext_data: CompilerExtData::type_check(&ast, |_| None).unwrap(), ms: Arc::new(Miniscript::from_ast(ast).expect("Terminal creation must always succeed")), @@ -487,10 +493,10 @@ impl AstElemExt { } fn binary( - ast: Terminal, + ast: Terminal, l: &AstElemExt, r: &AstElemExt, - ) -> Result, types::Error> { + ) -> Result, types::Error> { let lookup_ext = |n| match n { 0 => Some(l.comp_ext_data), 1 => Some(r.comp_ext_data), @@ -513,11 +519,11 @@ impl AstElemExt { } fn ternary( - ast: Terminal, + ast: Terminal, a: &AstElemExt, b: &AstElemExt, c: &AstElemExt, - ) -> Result, types::Error> { + ) -> Result, types::Error> { let lookup_ext = |n| match n { 0 => Some(a.comp_ext_data), 1 => Some(b.comp_ext_data), @@ -544,7 +550,7 @@ impl AstElemExt { /// Different types of casts possible for each node. #[derive(Copy, Clone)] struct Cast { - node: fn(Arc>) -> Terminal, + node: fn(Arc>) -> Terminal, ast_type: fn(types::Type) -> Result, ext_data: fn(types::ExtData) -> Result, comp_ext_data: fn(CompilerExtData) -> Result, @@ -1067,7 +1073,10 @@ fn compile_binary( where Pk: MiniscriptKey, Ctx: ScriptContext, - F: Fn(Arc>, Arc>) -> Terminal, + F: Fn( + Arc>, + Arc>, + ) -> Terminal, { for l in left_comp.values_mut() { let lref = Arc::clone(&l.ms); @@ -1120,7 +1129,7 @@ fn compile_tern( /// Obtain the best compilation of for p=1.0 and q=0 pub fn best_compilation( policy: &Concrete, -) -> Result, CompilerError> { +) -> Result, CompilerError> { let mut policy_cache = PolicyCache::::new(); let x = &*best_t(&mut policy_cache, policy, 1.0, None)?.ms; if !x.ty.mall.safe { @@ -1189,6 +1198,7 @@ mod tests { use std::collections::HashMap; use std::str::FromStr; use std::string::String; + use NoExt; use miniscript::{satisfy, Legacy, Segwitv0}; use policy::Liftable; @@ -1198,7 +1208,7 @@ mod tests { type SPolicy = Concrete; type BPolicy = Concrete; type DummySegwitAstElemExt = policy::compiler::AstElemExt; - type SegwitMiniScript = Miniscript; + type SegwitMiniScript = Miniscript; fn pubkeys_and_a_sig(n: usize) -> (Vec, secp256k1_zkp::Signature) { let mut ret = Vec::with_capacity(n); @@ -1227,7 +1237,7 @@ mod tests { fn policy_compile_lift_check(s: &str) -> Result<(), CompilerError> { let policy = SPolicy::from_str(s).expect("parse"); - let miniscript: Miniscript = policy.compile()?; + let miniscript: Miniscript = policy.compile()?; assert_eq!( policy.lift().unwrap().sorted(), @@ -1359,7 +1369,7 @@ mod tests { let ms: SegwitMiniScript = policy.compile().unwrap(); - let ms_comp_res: Miniscript = ms_str!( + let ms_comp_res: Miniscript = ms_str!( "or_d(multi(3,{},{},{},{},{}),\ and_v(v:thresh(2,c:pk_h({}),\ ac:pk_h({}),ac:pk_h({})),older(10000)))", @@ -1587,7 +1597,9 @@ mod benches { use super::{CompilerError, Concrete}; use miniscript::Segwitv0; use Miniscript; - type SegwitMsRes = Result, CompilerError>; + use NoExt; + + type SegwitMsRes = Result, CompilerError>; #[bench] pub fn compile_basic(bh: &mut Bencher) { let h = (0..64).map(|_| "a").collect::(); diff --git a/src/policy/concrete.rs b/src/policy/concrete.rs index 38d03a40..4ee2d76a 100644 --- a/src/policy/concrete.rs +++ b/src/policy/concrete.rs @@ -26,13 +26,9 @@ use expression::{self, FromTree}; use miniscript::limits::{HEIGHT_TIME_THRESHOLD, SEQUENCE_LOCKTIME_TYPE_FLAG}; use miniscript::types::extra_props::TimeLockInfo; #[cfg(feature = "compiler")] -use miniscript::ScriptContext; -#[cfg(feature = "compiler")] -use policy::compiler; -#[cfg(feature = "compiler")] -use policy::compiler::CompilerError; -#[cfg(feature = "compiler")] -use Miniscript; +use { + miniscript::ScriptContext, policy::compiler, policy::compiler::CompilerError, Miniscript, NoExt, +}; use {Error, ForEach, ForEachKey, MiniscriptKey}; /// Concrete policy which corresponds directly to a Miniscript structure, /// and whose disjunctions are annotated with satisfaction probabilities @@ -130,7 +126,7 @@ impl fmt::Display for PolicyError { impl Policy { /// Compile the descriptor into an optimized `Miniscript` representation #[cfg(feature = "compiler")] - pub fn compile(&self) -> Result, CompilerError> { + pub fn compile(&self) -> Result, CompilerError> { self.is_valid()?; match self.is_safe_nonmalleable() { (false, _) => Err(CompilerError::TopLevelNonSafe), diff --git a/src/policy/mod.rs b/src/policy/mod.rs index 9c1f237a..cd1ac35f 100644 --- a/src/policy/mod.rs +++ b/src/policy/mod.rs @@ -36,6 +36,8 @@ use Terminal; use descriptor::CovError; +use Extension; + pub use self::concrete::Policy as Concrete; /// Semantic policies are "abstract" policies elsewhere; but we /// avoid this word because it is a reserved keyword in Rust @@ -95,7 +97,7 @@ impl fmt::Display for LiftError { } } -impl Miniscript { +impl> Miniscript { /// Lifting corresponds conversion of miniscript into Policy /// [policy.semantic.Policy] for human readable or machine analysis. /// However, naively lifting miniscripts can result in incorrect @@ -115,7 +117,9 @@ impl Miniscript { } } -impl Liftable for Miniscript { +impl> Liftable + for Miniscript +{ fn lift(&self) -> Result, Error> { // check whether the root miniscript can have a spending path that is // a combination of heightlock and timelock @@ -124,7 +128,12 @@ impl Liftable for Miniscript } } -impl Liftable for Terminal { +impl Liftable for Terminal +where + Pk: MiniscriptKey, + Ctx: ScriptContext, + Ext: Extension, +{ fn lift(&self) -> Result, Error> { let ret = match *self { Terminal::PkK(ref pk) => Semantic::KeyHash(pk.to_pubkeyhash()), @@ -137,7 +146,7 @@ impl Liftable for Terminal { Terminal::Hash160(h) => Semantic::Hash160(h), Terminal::True => Semantic::Trivial, Terminal::False => Semantic::Unsatisfiable, - Terminal::Version(_) | Terminal::OutputsPref(_) => return Err(CovError::CovenantLift)?, + // Terminal::Version(_) | Terminal::OutputsPref(_) => return Err(CovError::CovenantLift)?, Terminal::Alt(ref sub) | Terminal::Swap(ref sub) | Terminal::Check(ref sub) @@ -172,6 +181,7 @@ impl Liftable for Terminal { .map(|k| Semantic::KeyHash(k.to_pubkeyhash())) .collect(), ), + Terminal::Ext(ref e) => e.lift()?, } .normalized(); Ok(ret) diff --git a/src/pset/finalizer.rs b/src/pset/finalizer.rs index def827c9..3db84312 100644 --- a/src/pset/finalizer.rs +++ b/src/pset/finalizer.rs @@ -19,6 +19,8 @@ //! `https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki` //! +use NoExt; + use super::{sanity_check, Pset}; use super::{Error, InputError, PsetInputSatisfier}; use bitcoin::{self, PublicKey}; @@ -128,8 +130,9 @@ pub(super) fn get_descriptor( match CovenantDescriptor::parse_insane(witness_script) { Ok(cov) => Ok(Descriptor::Cov(cov)), Err(_) => { - let ms = - Miniscript::::parse_insane(witness_script)?; + let ms = Miniscript::::parse_insane( + witness_script, + )?; Ok(Descriptor::new_wsh(ms)?) } } @@ -155,7 +158,7 @@ pub(super) fn get_descriptor( p2wsh_expected: redeem_script.clone(), }); } - let ms = Miniscript::::parse_insane( + let ms = Miniscript::::parse_insane( witness_script, )?; Ok(Descriptor::new_sh_wsh(ms)?) @@ -182,8 +185,9 @@ pub(super) fn get_descriptor( return Err(InputError::NonEmptyWitnessScript); } if let Some(ref redeem_script) = inp.redeem_script { - let ms = - Miniscript::::parse_insane(redeem_script)?; + let ms = Miniscript::::parse_insane( + redeem_script, + )?; Ok(Descriptor::new_sh(ms)?) } else { Err(InputError::MissingWitnessScript) @@ -199,7 +203,7 @@ pub(super) fn get_descriptor( if inp.redeem_script.is_some() { return Err(InputError::NonEmptyRedeemScript); } - let ms = Miniscript::::parse_insane(script_pubkey)?; + let ms = Miniscript::::parse_insane(script_pubkey)?; Ok(Descriptor::new_bare(ms)?) } }