From 02321525a9518da0a97d7c43df21d3a6ca32183d Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Thu, 28 Sep 2023 07:35:54 +1000 Subject: [PATCH 1/8] Use wilcard import in unit tests We can import types to test in unit tests using `use super::*`. --- src/descriptor/key.rs | 6 +----- src/expression.rs | 2 +- src/miniscript/mod.rs | 3 +-- src/policy/mod.rs | 6 ++---- 4 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/descriptor/key.rs b/src/descriptor/key.rs index cd4c828e8..0fff619fb 100644 --- a/src/descriptor/key.rs +++ b/src/descriptor/key.rs @@ -1153,11 +1153,7 @@ mod test { #[cfg(feature = "serde")] use serde_test::{assert_tokens, Token}; - use super::{ - DescriptorKeyParseError, DescriptorMultiXKey, DescriptorPublicKey, DescriptorSecretKey, - MiniscriptKey, Wildcard, - }; - use crate::prelude::*; + use super::*; #[test] fn parse_descriptor_key_errors() { diff --git a/src/expression.rs b/src/expression.rs index 7875284de..c452ec55f 100644 --- a/src/expression.rs +++ b/src/expression.rs @@ -260,7 +260,7 @@ where #[cfg(test)] mod tests { - use super::parse_num; + use super::*; #[test] fn test_parse_num() { diff --git a/src/miniscript/mod.rs b/src/miniscript/mod.rs index 28349e427..5eedc8091 100644 --- a/src/miniscript/mod.rs +++ b/src/miniscript/mod.rs @@ -606,11 +606,10 @@ mod tests { use bitcoin::{self, secp256k1, Sequence}; use sync::Arc; - use super::{Miniscript, ScriptContext, Segwitv0, Tap}; + use super::*; use crate::miniscript::types::{self, ExtData, Property, Type}; use crate::miniscript::Terminal; use crate::policy::Liftable; - use crate::prelude::*; use crate::test_utils::{StrKeyTranslator, StrXOnlyKeyTranslator}; use crate::{hex_script, ExtParams, Satisfier, ToPublicKey, TranslatePk}; diff --git a/src/policy/mod.rs b/src/policy/mod.rs index 138ec45b7..081cc5af1 100644 --- a/src/policy/mod.rs +++ b/src/policy/mod.rs @@ -226,9 +226,7 @@ mod tests { #[cfg(feature = "compiler")] use sync::Arc; - use super::super::miniscript::context::Segwitv0; - use super::super::miniscript::Miniscript; - use super::{Concrete, Liftable, Semantic}; + use super::*; #[cfg(feature = "compiler")] use crate::descriptor::Tr; use crate::prelude::*; @@ -340,7 +338,7 @@ mod tests { .parse() .unwrap(); - let ms_str: Miniscript = + let ms_str: Miniscript = format!("andor(multi(1,{}),older(42),c:pk_k({}))", key_a.inner, key_b.inner) .parse() .unwrap(); From b3f55f6ab1ae05426549488ccafa5af2f6909dd9 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Thu, 28 Sep 2023 07:14:16 +1000 Subject: [PATCH 2/8] Rename semantic module to abstract We used "semantic" so as not to clash with the reserved Rust keyword "abstract", however there is a language feature to deal with such name clashes - the raw identifier syntax `r#foo`. Use `r#abstract` and rename the `policy/semantic.rs` file to `policy/abstract.rs`. There is more re-naming to do to complete the semantic -> abstract change but here we _only_ do the module name to ease review. --- src/descriptor/bare.rs | 8 ++++---- src/descriptor/segwitv0.rs | 8 ++++---- src/descriptor/sh.rs | 6 +++--- src/descriptor/sortedmulti.rs | 7 ++++--- src/descriptor/tr.rs | 2 +- src/policy/{semantic.rs => abstract.rs} | 6 +++--- src/policy/concrete.rs | 2 +- src/policy/mod.rs | 6 +++--- 8 files changed, 23 insertions(+), 22 deletions(-) rename src/policy/{semantic.rs => abstract.rs} (99%) diff --git a/src/descriptor/bare.rs b/src/descriptor/bare.rs index c4ca14b14..080df024f 100644 --- a/src/descriptor/bare.rs +++ b/src/descriptor/bare.rs @@ -18,7 +18,7 @@ use crate::expression::{self, FromTree}; use crate::miniscript::context::{ScriptContext, ScriptContextError}; use crate::miniscript::satisfy::{Placeholder, Satisfaction, Witness}; use crate::plan::AssetProvider; -use crate::policy::{semantic, Liftable}; +use crate::policy::{r#abstract, Liftable}; use crate::prelude::*; use crate::util::{varint_len, witness_to_scriptsig}; use crate::{ @@ -165,7 +165,7 @@ impl fmt::Display for Bare { } impl Liftable for Bare { - fn lift(&self) -> Result, Error> { self.ms.lift() } + fn lift(&self) -> Result, Error> { self.ms.lift() } } impl_from_tree!( @@ -362,8 +362,8 @@ impl fmt::Display for Pkh { } impl Liftable for Pkh { - fn lift(&self) -> Result, Error> { - Ok(semantic::Policy::Key(self.pk.clone())) + fn lift(&self) -> Result, Error> { + Ok(r#abstract::Policy::Key(self.pk.clone())) } } diff --git a/src/descriptor/segwitv0.rs b/src/descriptor/segwitv0.rs index 0cc665468..513fb5fb6 100644 --- a/src/descriptor/segwitv0.rs +++ b/src/descriptor/segwitv0.rs @@ -16,7 +16,7 @@ use crate::expression::{self, FromTree}; use crate::miniscript::context::{ScriptContext, ScriptContextError}; use crate::miniscript::satisfy::{Placeholder, Satisfaction, Witness}; use crate::plan::AssetProvider; -use crate::policy::{semantic, Liftable}; +use crate::policy::{r#abstract, Liftable}; use crate::prelude::*; use crate::util::varint_len; use crate::{ @@ -220,7 +220,7 @@ pub enum WshInner { } impl Liftable for Wsh { - fn lift(&self) -> Result, Error> { + fn lift(&self) -> Result, Error> { match self.inner { WshInner::SortedMulti(ref smv) => smv.lift(), WshInner::Ms(ref ms) => ms.lift(), @@ -469,8 +469,8 @@ impl fmt::Display for Wpkh { } impl Liftable for Wpkh { - fn lift(&self) -> Result, Error> { - Ok(semantic::Policy::Key(self.pk.clone())) + fn lift(&self) -> Result, Error> { + Ok(r#abstract::Policy::Key(self.pk.clone())) } } diff --git a/src/descriptor/sh.rs b/src/descriptor/sh.rs index 53b4107ce..c0727dae4 100644 --- a/src/descriptor/sh.rs +++ b/src/descriptor/sh.rs @@ -20,7 +20,7 @@ use crate::expression::{self, FromTree}; use crate::miniscript::context::ScriptContext; use crate::miniscript::satisfy::{Placeholder, Satisfaction}; use crate::plan::AssetProvider; -use crate::policy::{semantic, Liftable}; +use crate::policy::{r#abstract, Liftable}; use crate::prelude::*; use crate::util::{varint_len, witness_to_scriptsig}; use crate::{ @@ -49,10 +49,10 @@ pub enum ShInner { } impl Liftable for Sh { - fn lift(&self) -> Result, Error> { + fn lift(&self) -> Result, Error> { match self.inner { ShInner::Wsh(ref wsh) => wsh.lift(), - ShInner::Wpkh(ref pk) => Ok(semantic::Policy::Key(pk.as_inner().clone())), + ShInner::Wpkh(ref pk) => Ok(r#abstract::Policy::Key(pk.as_inner().clone())), ShInner::SortedMulti(ref smv) => smv.lift(), ShInner::Ms(ref ms) => ms.lift(), } diff --git a/src/descriptor/sortedmulti.rs b/src/descriptor/sortedmulti.rs index 3bd5fa8e0..987ac29a6 100644 --- a/src/descriptor/sortedmulti.rs +++ b/src/descriptor/sortedmulti.rs @@ -16,6 +16,7 @@ use crate::miniscript::decode::Terminal; use crate::miniscript::limits::MAX_PUBKEYS_PER_MULTISIG; use crate::miniscript::satisfy::{Placeholder, Satisfaction}; use crate::plan::AssetProvider; +use crate::policy::r#abstract; use crate::prelude::*; use crate::{ errstr, expression, policy, script_num_size, Error, ForEachKey, Miniscript, MiniscriptKey, @@ -196,12 +197,12 @@ impl SortedMultiVec { } impl policy::Liftable for SortedMultiVec { - fn lift(&self) -> Result, Error> { - let ret = policy::semantic::Policy::Threshold( + fn lift(&self) -> Result, Error> { + let ret = r#abstract::Policy::Threshold( self.k, self.pks .iter() - .map(|k| policy::semantic::Policy::Key(k.clone())) + .map(|k| r#abstract::Policy::Key(k.clone())) .collect(), ); Ok(ret) diff --git a/src/descriptor/tr.rs b/src/descriptor/tr.rs index 48deb63e0..fe8ce20c6 100644 --- a/src/descriptor/tr.rs +++ b/src/descriptor/tr.rs @@ -16,7 +16,7 @@ use crate::expression::{self, FromTree}; use crate::miniscript::satisfy::{Placeholder, Satisfaction, SchnorrSigType, Witness}; use crate::miniscript::Miniscript; use crate::plan::AssetProvider; -use crate::policy::semantic::Policy; +use crate::policy::r#abstract::Policy; use crate::policy::Liftable; use crate::prelude::*; use crate::util::{varint_len, witness_size}; diff --git a/src/policy/semantic.rs b/src/policy/abstract.rs similarity index 99% rename from src/policy/semantic.rs rename to src/policy/abstract.rs index 9636c0d2a..3a0dfcddd 100644 --- a/src/policy/semantic.rs +++ b/src/policy/abstract.rs @@ -10,8 +10,8 @@ use core::{fmt, str}; use bitcoin::{absolute, Sequence}; -use super::concrete::PolicyError; -use super::ENTAILMENT_MAX_TERMINALS; +use crate::policy::concrete::PolicyError; +use crate::policy::ENTAILMENT_MAX_TERMINALS; use crate::prelude::*; use crate::{errstr, expression, AbsLockTime, Error, ForEachKey, MiniscriptKey, Translator}; @@ -93,7 +93,7 @@ impl Policy { /// use std::collections::HashMap; /// use std::str::FromStr; /// use miniscript::bitcoin::{hashes::hash160, PublicKey}; - /// use miniscript::{translate_hash_fail, policy::semantic::Policy, Translator}; + /// use miniscript::{translate_hash_fail, policy::r#abstract::Policy, Translator}; /// let alice_pk = "02c79ef3ede6d14f72a00d0e49b4becfb152197b64c0707425c4f231df29500ee7"; /// let bob_pk = "03d008a849fbf474bd17e9d2c1a827077a468150e58221582ec3410ab309f5afe4"; /// let placeholder_policy = Policy::::from_str("and(pk(alice_pk),pk(bob_pk))").unwrap(); diff --git a/src/policy/concrete.rs b/src/policy/concrete.rs index 23305087d..1117a0849 100644 --- a/src/policy/concrete.rs +++ b/src/policy/concrete.rs @@ -557,7 +557,7 @@ impl ForEachKey for Policy { impl Policy { /// Converts a policy using one kind of public key to another type of public key. /// - /// For example usage please see [`crate::policy::semantic::Policy::translate_pk`]. + /// For example usage please see [`crate::policy::abstract::Policy::translate_pk`]. pub fn translate_pk(&self, t: &mut T) -> Result, E> where T: Translator, diff --git a/src/policy/mod.rs b/src/policy/mod.rs index 081cc5af1..b11edc9bf 100644 --- a/src/policy/mod.rs +++ b/src/policy/mod.rs @@ -13,20 +13,20 @@ use core::fmt; #[cfg(feature = "std")] use std::error; +pub mod r#abstract; #[cfg(feature = "compiler")] pub mod compiler; pub mod concrete; -pub mod semantic; pub use self::concrete::Policy as Concrete; -pub use self::semantic::Policy as Semantic; +pub use self::r#abstract::Policy as Semantic; use crate::descriptor::Descriptor; use crate::miniscript::{Miniscript, ScriptContext}; use crate::sync::Arc; use crate::{Error, MiniscriptKey, Terminal}; /// Policy entailment algorithm maximum number of terminals allowed. -const ENTAILMENT_MAX_TERMINALS: usize = 20; +pub(crate) const ENTAILMENT_MAX_TERMINALS: usize = 20; /// Trait describing script representations which can be lifted into /// an abstract policy, by discarding information. From f00e53861246937e4e82c9efd5d97ea532c388b9 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Thu, 28 Sep 2023 08:01:43 +1000 Subject: [PATCH 3/8] Move Liftable to crate::abstract module The `Liftable` trait is its own thing, it defines a trait to lift miniscript, descriptors, and concrete policies to an abstract policy. Move the `Liftable` and associated error type and impls to a new `lift` module that lives under the recently created `crate::r#abstract` module. Note we alse remove the `Semantic` and `Concrete` public re-names/re-exports from `crate::policy` because we are transitioning away from using the term "semantic", however to minimise the changes in this patch we add a bunch of `use crate::policy::concrete::Policy as Concrete` statements. --- examples/htlc.rs | 3 +- examples/taproot.rs | 2 +- fuzz/fuzz_targets/compile_descriptor.rs | 4 +- fuzz/fuzz_targets/roundtrip_concrete.rs | 2 +- fuzz/fuzz_targets/roundtrip_semantic.rs | 2 +- src/abstract/lift.rs | 528 +++++++++++++++++++++++ src/abstract/mod.rs | 7 + src/descriptor/bare.rs | 3 +- src/descriptor/segwitv0.rs | 3 +- src/descriptor/sh.rs | 3 +- src/descriptor/sortedmulti.rs | 8 +- src/descriptor/tr.rs | 2 +- src/iter/mod.rs | 8 +- src/lib.rs | 7 +- src/macros.rs | 2 +- src/miniscript/mod.rs | 2 +- src/policy/compiler.rs | 4 +- src/policy/concrete.rs | 12 +- src/policy/mod.rs | 540 ------------------------ 19 files changed, 570 insertions(+), 572 deletions(-) create mode 100644 src/abstract/lift.rs create mode 100644 src/abstract/mod.rs diff --git a/examples/htlc.rs b/examples/htlc.rs index 8f864d639..9e476f25c 100644 --- a/examples/htlc.rs +++ b/examples/htlc.rs @@ -7,7 +7,8 @@ use std::str::FromStr; use miniscript::bitcoin::Network; use miniscript::descriptor::Wsh; -use miniscript::policy::{Concrete, Liftable}; +use miniscript::policy::concrete::Policy as Concrete; +use miniscript::r#abstract::Liftable; fn main() { // HTLC policy with 10:1 odds for happy (co-operative) case compared to uncooperative case. diff --git a/examples/taproot.rs b/examples/taproot.rs index 3482d3ddb..04ae0a54d 100644 --- a/examples/taproot.rs +++ b/examples/taproot.rs @@ -8,7 +8,7 @@ use miniscript::bitcoin::key::{KeyPair, XOnlyPublicKey}; use miniscript::bitcoin::secp256k1::rand; use miniscript::bitcoin::Network; use miniscript::descriptor::DescriptorType; -use miniscript::policy::Concrete; +use miniscript::policy::concrete::Policy as Concrete; use miniscript::{translate_hash_fail, Descriptor, Miniscript, Tap, TranslatePk, Translator}; // Refer to https://github.com/sanket1729/adv_btc_workshop/blob/master/workshop.md#creating-a-taproot-descriptor diff --git a/fuzz/fuzz_targets/compile_descriptor.rs b/fuzz/fuzz_targets/compile_descriptor.rs index cfd6c7f4f..54950ed88 100644 --- a/fuzz/fuzz_targets/compile_descriptor.rs +++ b/fuzz/fuzz_targets/compile_descriptor.rs @@ -1,11 +1,11 @@ use std::str::FromStr; use honggfuzz::fuzz; +use miniscript::r#abstract::Liftable; use miniscript::{policy, Miniscript, Segwitv0}; -use policy::Liftable; type Script = Miniscript; -type Policy = policy::Concrete; +type Policy = policy::concrete::Policy; fn do_test(data: &[u8]) { let data_str = String::from_utf8_lossy(data); diff --git a/fuzz/fuzz_targets/roundtrip_concrete.rs b/fuzz/fuzz_targets/roundtrip_concrete.rs index 41e437221..2d2c4564d 100644 --- a/fuzz/fuzz_targets/roundtrip_concrete.rs +++ b/fuzz/fuzz_targets/roundtrip_concrete.rs @@ -4,7 +4,7 @@ use honggfuzz::fuzz; use miniscript::policy; use regex::Regex; -type Policy = policy::Concrete; +type Policy = policy::concrete::Policy; fn do_test(data: &[u8]) { let data_str = String::from_utf8_lossy(data); diff --git a/fuzz/fuzz_targets/roundtrip_semantic.rs b/fuzz/fuzz_targets/roundtrip_semantic.rs index cebc223b2..3a5bd3e0c 100644 --- a/fuzz/fuzz_targets/roundtrip_semantic.rs +++ b/fuzz/fuzz_targets/roundtrip_semantic.rs @@ -3,7 +3,7 @@ use std::str::FromStr; use honggfuzz::fuzz; use miniscript::policy; -type Policy = policy::Semantic; +type Policy = policy::r#abstract::Policy; fn do_test(data: &[u8]) { let data_str = String::from_utf8_lossy(data); diff --git a/src/abstract/lift.rs b/src/abstract/lift.rs new file mode 100644 index 000000000..5f19b480d --- /dev/null +++ b/src/abstract/lift.rs @@ -0,0 +1,528 @@ +// SPDX-License-Identifier: CC0-1.0 + +//! Tools for representing Bitcoin scriptpubkeys as abstract spending policies. +//! +//! These may be compiled to Miniscript, which contains extra information to +//! describe the exact representation as Bitcoin script. +//! +//! The format represents EC public keys abstractly to allow wallets to replace +//! these with BIP32 paths, pay-to-contract instructions, etc. +//! +use core::fmt; +#[cfg(feature = "std")] +use std::error; + +use crate::descriptor::Descriptor; +use crate::miniscript::{Miniscript, ScriptContext}; +use crate::policy::concrete::Policy as Concrete; +use crate::policy::r#abstract::Policy as Semantic; +use crate::sync::Arc; +use crate::{Error, MiniscriptKey, Terminal}; + +/// Trait describing script representations which can be lifted into +/// an abstract policy, by discarding information. +/// +/// After Lifting all policies are converted into `KeyHash(Pk::HasH)` to +/// maintain the following invariant(modulo resource limits): +/// `Lift(Concrete) == Concrete -> Miniscript -> Script -> Miniscript -> Semantic` +/// +/// Lifting from [`Miniscript`] or [`Descriptor`] can fail if the miniscript +/// contains a timelock combination or if it contains a branch that exceeds +/// resource limits. +/// +/// Lifting from concrete policies can fail if the policy contains a timelock +/// combination. It is possible that a concrete policy has some branches that +/// exceed resource limits for any compilation but cannot detect such policies +/// while lifting. Note that our compiler would not succeed for any such +/// policies. +pub trait Liftable { + /// Converts this object into an abstract policy. + fn lift(&self) -> Result, Error>; +} + +/// Error occurring during lifting. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum LiftError { + /// Cannot lift policies that have a combination of height and timelocks. + HeightTimelockCombination, + /// Duplicate public keys. + BranchExceedResourceLimits, + /// Cannot lift raw descriptors. + RawDescriptorLift, +} + +impl fmt::Display for LiftError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + LiftError::HeightTimelockCombination => { + f.write_str("Cannot lift policies that have a heightlock and timelock combination") + } + LiftError::BranchExceedResourceLimits => f.write_str( + "Cannot lift policies containing one branch that exceeds resource limits", + ), + LiftError::RawDescriptorLift => f.write_str("Cannot lift raw descriptors"), + } + } +} + +#[cfg(feature = "std")] +impl error::Error for LiftError { + fn cause(&self) -> Option<&dyn error::Error> { + use self::LiftError::*; + + match self { + HeightTimelockCombination | BranchExceedResourceLimits | RawDescriptorLift => None, + } + } +} + +impl Miniscript { + /// Lifting corresponds to conversion of a miniscript into a [`Semantic`] + /// policy for human readable or machine analysis. However, naively lifting + /// miniscripts can result in incorrect interpretations that don't + /// correspond to the underlying semantics when we try to spend them on + /// bitcoin network. This can occur if the miniscript contains: + /// 1. A combination of timelocks + /// 2. A spend that exceeds resource limits + pub fn lift_check(&self) -> Result<(), LiftError> { + if !self.within_resource_limits() { + Err(LiftError::BranchExceedResourceLimits) + } else if self.has_mixed_timelocks() { + Err(LiftError::HeightTimelockCombination) + } else { + Ok(()) + } + } +} + +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 + self.lift_check()?; + self.as_inner().lift() + } +} + +impl Liftable for Terminal { + fn lift(&self) -> Result, Error> { + let ret = match *self { + Terminal::PkK(ref pk) | Terminal::PkH(ref pk) => Semantic::Key(pk.clone()), + Terminal::RawPkH(ref _pkh) => { + return Err(Error::LiftError(LiftError::RawDescriptorLift)) + } + Terminal::After(t) => Semantic::After(t), + Terminal::Older(t) => Semantic::Older(t), + Terminal::Sha256(ref h) => Semantic::Sha256(h.clone()), + Terminal::Hash256(ref h) => Semantic::Hash256(h.clone()), + Terminal::Ripemd160(ref h) => Semantic::Ripemd160(h.clone()), + Terminal::Hash160(ref h) => Semantic::Hash160(h.clone()), + Terminal::False => Semantic::Unsatisfiable, + Terminal::True => Semantic::Trivial, + Terminal::Alt(ref sub) + | Terminal::Swap(ref sub) + | Terminal::Check(ref sub) + | Terminal::DupIf(ref sub) + | Terminal::Verify(ref sub) + | Terminal::NonZero(ref sub) + | Terminal::ZeroNotEqual(ref sub) => sub.node.lift()?, + Terminal::AndV(ref left, ref right) | Terminal::AndB(ref left, ref right) => { + Semantic::Threshold(2, vec![left.node.lift()?, right.node.lift()?]) + } + Terminal::AndOr(ref a, ref b, ref c) => Semantic::Threshold( + 1, + vec![ + Semantic::Threshold(2, vec![a.node.lift()?, b.node.lift()?]), + c.node.lift()?, + ], + ), + Terminal::OrB(ref left, ref right) + | Terminal::OrD(ref left, ref right) + | Terminal::OrC(ref left, ref right) + | Terminal::OrI(ref left, ref right) => { + Semantic::Threshold(1, vec![left.node.lift()?, right.node.lift()?]) + } + Terminal::Thresh(k, ref subs) => { + let semantic_subs: Result<_, Error> = subs.iter().map(|s| s.node.lift()).collect(); + Semantic::Threshold(k, semantic_subs?) + } + Terminal::Multi(k, ref keys) | Terminal::MultiA(k, ref keys) => { + Semantic::Threshold(k, keys.iter().map(|k| Semantic::Key(k.clone())).collect()) + } + } + .normalized(); + Ok(ret) + } +} + +impl Liftable for Descriptor { + fn lift(&self) -> Result, Error> { + match *self { + Descriptor::Bare(ref bare) => bare.lift(), + Descriptor::Pkh(ref pkh) => pkh.lift(), + Descriptor::Wpkh(ref wpkh) => wpkh.lift(), + Descriptor::Wsh(ref wsh) => wsh.lift(), + Descriptor::Sh(ref sh) => sh.lift(), + Descriptor::Tr(ref tr) => tr.lift(), + } + } +} + +impl Liftable for Semantic { + fn lift(&self) -> Result, Error> { Ok(self.clone()) } +} + +impl Liftable for Concrete { + fn lift(&self) -> Result, Error> { + // do not lift if there is a possible satisfaction + // involving combination of timelocks and heightlocks + self.check_timelocks()?; + let ret = match *self { + Concrete::Unsatisfiable => Semantic::Unsatisfiable, + Concrete::Trivial => Semantic::Trivial, + Concrete::Key(ref pk) => Semantic::Key(pk.clone()), + Concrete::After(t) => Semantic::After(t), + Concrete::Older(t) => Semantic::Older(t), + Concrete::Sha256(ref h) => Semantic::Sha256(h.clone()), + Concrete::Hash256(ref h) => Semantic::Hash256(h.clone()), + Concrete::Ripemd160(ref h) => Semantic::Ripemd160(h.clone()), + Concrete::Hash160(ref h) => Semantic::Hash160(h.clone()), + Concrete::And(ref subs) => { + let semantic_subs: Result<_, Error> = subs.iter().map(Liftable::lift).collect(); + Semantic::Threshold(2, semantic_subs?) + } + Concrete::Or(ref subs) => { + let semantic_subs: Result<_, Error> = + subs.iter().map(|(_p, sub)| sub.lift()).collect(); + Semantic::Threshold(1, semantic_subs?) + } + Concrete::Threshold(k, ref subs) => { + let semantic_subs: Result<_, Error> = subs.iter().map(Liftable::lift).collect(); + Semantic::Threshold(k, semantic_subs?) + } + } + .normalized(); + Ok(ret) + } +} +impl Liftable for Arc> { + fn lift(&self) -> Result, Error> { self.as_ref().lift() } +} + +#[cfg(test)] +mod tests { + use core::str::FromStr; + + use bitcoin::Sequence; + #[cfg(feature = "compiler")] + use sync::Arc; + + use super::*; + #[cfg(feature = "compiler")] + use crate::descriptor::Tr; + use crate::miniscript::context::Segwitv0; + use crate::miniscript::Miniscript; + use crate::prelude::*; + #[cfg(feature = "compiler")] + use crate::{descriptor::TapTree, Descriptor, Tap}; + + type ConcretePol = crate::policy::concrete::Policy; + type SemanticPol = crate::policy::r#abstract::Policy; + + fn concrete_policy_rtt(s: &str) { + let conc = ConcretePol::from_str(s).unwrap(); + let output = conc.to_string(); + assert_eq!(s.to_lowercase(), output.to_lowercase()); + } + + fn semantic_policy_rtt(s: &str) { + let sem = SemanticPol::from_str(s).unwrap(); + let output = sem.normalized().to_string(); + assert_eq!(s.to_lowercase(), output.to_lowercase()); + } + + #[test] + fn test_timelock_validity() { + // only height + assert!(ConcretePol::from_str("after(100)").is_ok()); + // only time + assert!(ConcretePol::from_str("after(1000000000)").is_ok()); + // disjunction + assert!(ConcretePol::from_str("or(after(1000000000),after(100))").is_ok()); + // conjunction + assert!(ConcretePol::from_str("and(after(1000000000),after(100))").is_err()); + // thresh with k = 1 + assert!(ConcretePol::from_str("thresh(1,pk(),after(1000000000),after(100))").is_ok()); + // thresh with k = 2 + assert!(ConcretePol::from_str("thresh(2,after(1000000000),after(100),pk())").is_err()); + } + #[test] + fn policy_rtt_tests() { + concrete_policy_rtt("pk()"); + concrete_policy_rtt("or(1@pk(),1@pk())"); + concrete_policy_rtt("or(99@pk(),1@pk())"); + concrete_policy_rtt("and(pk(),or(99@pk(),1@older(12960)))"); + + semantic_policy_rtt("pk()"); + semantic_policy_rtt("or(pk(),pk())"); + semantic_policy_rtt("and(pk(),pk())"); + + //fuzzer crashes + assert!(ConcretePol::from_str("thresh()").is_err()); + assert!(SemanticPol::from_str("thresh(0)").is_err()); + assert!(SemanticPol::from_str("thresh()").is_err()); + concrete_policy_rtt("ripemd160()"); + } + + #[test] + fn compile_invalid() { + // Since the root Error does not support Eq type, we have to + // compare the string representations of the error + assert_eq!( + ConcretePol::from_str("thresh(2,pk(),thresh(0))") + .unwrap_err() + .to_string(), + "Threshold k must be greater than 0 and less than or equal to n 0 0" + ); + + assert_eq!( + ConcretePol::from_str("thresh(2,older(2147483650),pk(),pk())") + .unwrap_err() + .to_string(), + "Relative/Absolute time must be less than 2^31; n < 2^31" + ); + } + + //https://github.com/apoelstra/rust-miniscript/issues/41 + #[test] + fn heavy_nest() { + let policy_string = "thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk()))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))"; + ConcretePol::from_str(policy_string).unwrap_err(); + } + + #[test] + fn lift_andor() { + let key_a: bitcoin::PublicKey = + "02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e" + .parse() + .unwrap(); + let key_b: bitcoin::PublicKey = + "03b506a1dbe57b4bf48c95e0c7d417b87dd3b4349d290d2e7e9ba72c912652d80a" + .parse() + .unwrap(); + + let ms_str: Miniscript = + format!("andor(multi(1,{}),older(42),c:pk_k({}))", key_a.inner, key_b.inner) + .parse() + .unwrap(); + assert_eq!( + Semantic::Threshold( + 1, + vec![ + Semantic::Threshold( + 2, + vec![ + Semantic::Key(key_a), + Semantic::Older(Sequence::from_height(42)) + ] + ), + Semantic::Key(key_b) + ] + ), + ms_str.lift().unwrap() + ); + } + + #[test] + #[cfg(feature = "compiler")] + fn taproot_compile() { + // Trivial single-node compilation + let unspendable_key: String = "UNSPENDABLE".to_string(); + { + let policy: Concrete = policy_str!("thresh(2,pk(A),pk(B),pk(C),pk(D))"); + let descriptor = policy.compile_tr(Some(unspendable_key.clone())).unwrap(); + + let ms_compilation: Miniscript = ms_str!("multi_a(2,A,B,C,D)"); + let tree: TapTree = TapTree::Leaf(Arc::new(ms_compilation)); + let expected_descriptor = + Descriptor::new_tr(unspendable_key.clone(), Some(tree)).unwrap(); + assert_eq!(descriptor, expected_descriptor); + } + + // Trivial multi-node compilation + { + let policy: Concrete = policy_str!("or(and(pk(A),pk(B)),and(pk(C),pk(D)))"); + let descriptor = policy.compile_tr(Some(unspendable_key.clone())).unwrap(); + + let left_ms_compilation: Arc> = + Arc::new(ms_str!("and_v(v:pk(C),pk(D))")); + let right_ms_compilation: Arc> = + Arc::new(ms_str!("and_v(v:pk(A),pk(B))")); + + let left = TapTree::Leaf(left_ms_compilation); + let right = TapTree::Leaf(right_ms_compilation); + let tree = TapTree::combine(left, right); + + let expected_descriptor = + Descriptor::new_tr(unspendable_key.clone(), Some(tree)).unwrap(); + assert_eq!(descriptor, expected_descriptor); + } + + { + // Invalid policy compilation (Duplicate PubKeys) + let policy: Concrete = policy_str!("or(and(pk(A),pk(B)),and(pk(A),pk(D)))"); + let descriptor = policy.compile_tr(Some(unspendable_key.clone())); + + assert_eq!(descriptor.unwrap_err().to_string(), "Policy contains duplicate keys"); + } + + // Non-trivial multi-node compilation + { + let node_policies = [ + "and(pk(A),pk(B))", + "and(pk(C),older(12960))", + "pk(D)", + "pk(E)", + "thresh(3,pk(F),pk(G),pk(H))", + "and(and(or(2@pk(I),1@pk(J)),or(1@pk(K),20@pk(L))),pk(M))", + "pk(N)", + ]; + + // Floating-point precision errors cause the minor errors + let node_probabilities: [f64; 7] = + [0.12000002, 0.28, 0.08, 0.12, 0.19, 0.18999998, 0.02]; + + let policy: Concrete = policy_str!( + "{}", + &format!( + "or(4@or(3@{},7@{}),6@thresh(1,or(4@{},6@{}),{},or(9@{},1@{})))", + node_policies[0], + node_policies[1], + node_policies[2], + node_policies[3], + node_policies[4], + node_policies[5], + node_policies[6] + ) + ); + let descriptor = policy.compile_tr(Some(unspendable_key.clone())).unwrap(); + + let mut sorted_policy_prob = node_policies + .iter() + .zip(node_probabilities.iter()) + .collect::>(); + sorted_policy_prob.sort_by(|a, b| (a.1).partial_cmp(&b.1).unwrap()); + let sorted_policies = sorted_policy_prob + .into_iter() + .map(|(x, _prob)| x) + .collect::>(); + + // Generate TapTree leaves compilations from the given sub-policies + let node_compilations = sorted_policies + .into_iter() + .map(|x| { + let leaf_policy: Concrete = policy_str!("{}", x); + TapTree::Leaf(Arc::from(leaf_policy.compile::().unwrap())) + }) + .collect::>(); + + // Arrange leaf compilations (acc. to probabilities) using huffman encoding into a TapTree + let tree = TapTree::combine( + TapTree::combine(node_compilations[4].clone(), node_compilations[5].clone()), + TapTree::combine( + TapTree::combine( + TapTree::combine( + node_compilations[0].clone(), + node_compilations[1].clone(), + ), + node_compilations[3].clone(), + ), + node_compilations[6].clone(), + ), + ); + + let expected_descriptor = Descriptor::new_tr("E".to_string(), Some(tree)).unwrap(); + assert_eq!(descriptor, expected_descriptor); + } + } + + #[test] + #[cfg(feature = "compiler")] + fn experimental_taproot_compile() { + let unspendable_key = "UNSPEND".to_string(); + + { + let pol = Concrete::::from_str( + "thresh(7,pk(A),pk(B),pk(C),pk(D),pk(E),pk(F),pk(G),pk(H))", + ) + .unwrap(); + let desc = pol + .compile_tr_private_experimental(Some(unspendable_key.clone())) + .unwrap(); + let expected_desc = Descriptor::Tr( + Tr::::from_str( + "tr(UNSPEND ,{ + { + {multi_a(7,B,C,D,E,F,G,H),multi_a(7,A,C,D,E,F,G,H)}, + {multi_a(7,A,B,D,E,F,G,H),multi_a(7,A,B,C,E,F,G,H)} + }, + { + {multi_a(7,A,B,C,D,F,G,H),multi_a(7,A,B,C,D,E,G,H)} + ,{multi_a(7,A,B,C,D,E,F,H),multi_a(7,A,B,C,D,E,F,G)} + }})" + .replace(&['\t', ' ', '\n'][..], "") + .as_str(), + ) + .unwrap(), + ); + assert_eq!(desc, expected_desc); + } + + { + let pol = + Concrete::::from_str("thresh(3,pk(A),pk(B),pk(C),pk(D),pk(E))").unwrap(); + let desc = pol + .compile_tr_private_experimental(Some(unspendable_key.clone())) + .unwrap(); + let expected_desc = Descriptor::Tr( + Tr::::from_str( + "tr(UNSPEND, + {{ + {multi_a(3,A,D,E),multi_a(3,A,C,E)}, + {multi_a(3,A,C,D),multi_a(3,A,B,E)}\ + }, + { + {multi_a(3,A,B,D),multi_a(3,A,B,C)}, + { + {multi_a(3,C,D,E),multi_a(3,B,D,E)}, + {multi_a(3,B,C,E),multi_a(3,B,C,D)} + }}})" + .replace(&['\t', ' ', '\n'][..], "") + .as_str(), + ) + .unwrap(), + ); + assert_eq!(desc, expected_desc); + } + } +} diff --git a/src/abstract/mod.rs b/src/abstract/mod.rs new file mode 100644 index 000000000..9ce49b496 --- /dev/null +++ b/src/abstract/mod.rs @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: CC0-1.0 + +//! WIP - TODO: Describe this module. + +pub use self::lift::{LiftError, Liftable}; + +mod lift; diff --git a/src/descriptor/bare.rs b/src/descriptor/bare.rs index 080df024f..8afeb1a86 100644 --- a/src/descriptor/bare.rs +++ b/src/descriptor/bare.rs @@ -18,8 +18,9 @@ use crate::expression::{self, FromTree}; use crate::miniscript::context::{ScriptContext, ScriptContextError}; use crate::miniscript::satisfy::{Placeholder, Satisfaction, Witness}; use crate::plan::AssetProvider; -use crate::policy::{r#abstract, Liftable}; +use crate::policy::r#abstract; use crate::prelude::*; +use crate::r#abstract::Liftable; use crate::util::{varint_len, witness_to_scriptsig}; use crate::{ BareCtx, Error, ForEachKey, Miniscript, MiniscriptKey, Satisfier, ToPublicKey, TranslateErr, diff --git a/src/descriptor/segwitv0.rs b/src/descriptor/segwitv0.rs index 513fb5fb6..c93561199 100644 --- a/src/descriptor/segwitv0.rs +++ b/src/descriptor/segwitv0.rs @@ -16,8 +16,9 @@ use crate::expression::{self, FromTree}; use crate::miniscript::context::{ScriptContext, ScriptContextError}; use crate::miniscript::satisfy::{Placeholder, Satisfaction, Witness}; use crate::plan::AssetProvider; -use crate::policy::{r#abstract, Liftable}; +use crate::policy::r#abstract; use crate::prelude::*; +use crate::r#abstract::Liftable; use crate::util::varint_len; use crate::{ Error, ForEachKey, Miniscript, MiniscriptKey, Satisfier, Segwitv0, ToPublicKey, TranslateErr, diff --git a/src/descriptor/sh.rs b/src/descriptor/sh.rs index c0727dae4..c2ffb647e 100644 --- a/src/descriptor/sh.rs +++ b/src/descriptor/sh.rs @@ -20,8 +20,9 @@ use crate::expression::{self, FromTree}; use crate::miniscript::context::ScriptContext; use crate::miniscript::satisfy::{Placeholder, Satisfaction}; use crate::plan::AssetProvider; -use crate::policy::{r#abstract, Liftable}; +use crate::policy::r#abstract; use crate::prelude::*; +use crate::r#abstract::Liftable; use crate::util::{varint_len, witness_to_scriptsig}; use crate::{ push_opcode_size, Error, ForEachKey, Legacy, Miniscript, MiniscriptKey, Satisfier, Segwitv0, diff --git a/src/descriptor/sortedmulti.rs b/src/descriptor/sortedmulti.rs index 987ac29a6..6e8854a8a 100644 --- a/src/descriptor/sortedmulti.rs +++ b/src/descriptor/sortedmulti.rs @@ -19,8 +19,8 @@ use crate::plan::AssetProvider; use crate::policy::r#abstract; use crate::prelude::*; use crate::{ - errstr, expression, policy, script_num_size, Error, ForEachKey, Miniscript, MiniscriptKey, - Satisfier, ToPublicKey, TranslateErr, Translator, + errstr, expression, script_num_size, Error, ForEachKey, Miniscript, MiniscriptKey, Satisfier, + ToPublicKey, TranslateErr, Translator, }; /// Contents of a "sortedmulti" descriptor @@ -196,7 +196,9 @@ impl SortedMultiVec { pub fn max_satisfaction_size(&self) -> usize { 1 + 73 * self.k } } -impl policy::Liftable for SortedMultiVec { +impl crate::r#abstract::Liftable + for SortedMultiVec +{ fn lift(&self) -> Result, Error> { let ret = r#abstract::Policy::Threshold( self.k, diff --git a/src/descriptor/tr.rs b/src/descriptor/tr.rs index fe8ce20c6..47f982a03 100644 --- a/src/descriptor/tr.rs +++ b/src/descriptor/tr.rs @@ -17,8 +17,8 @@ use crate::miniscript::satisfy::{Placeholder, Satisfaction, SchnorrSigType, Witn use crate::miniscript::Miniscript; use crate::plan::AssetProvider; use crate::policy::r#abstract::Policy; -use crate::policy::Liftable; use crate::prelude::*; +use crate::r#abstract::Liftable; use crate::util::{varint_len, witness_size}; use crate::{ errstr, Error, ForEachKey, MiniscriptKey, Satisfier, ScriptContext, Tap, ToPublicKey, diff --git a/src/iter/mod.rs b/src/iter/mod.rs index f8dbed939..a86a44c0f 100644 --- a/src/iter/mod.rs +++ b/src/iter/mod.rs @@ -69,9 +69,9 @@ impl TreeLike for Arc } } -impl<'a, Pk: MiniscriptKey> TreeLike for &'a policy::Concrete { +impl<'a, Pk: MiniscriptKey> TreeLike for &'a policy::concrete::Policy { fn as_node(&self) -> Tree { - use policy::Concrete::*; + use policy::concrete::Policy::*; match *self { Unsatisfiable | Trivial | Key(_) | After(_) | Older(_) | Sha256(_) | Hash256(_) | Ripemd160(_) | Hash160(_) => Tree::Nullary, @@ -82,9 +82,9 @@ impl<'a, Pk: MiniscriptKey> TreeLike for &'a policy::Concrete { } } -impl<'a, Pk: MiniscriptKey> TreeLike for Arc> { +impl<'a, Pk: MiniscriptKey> TreeLike for Arc> { fn as_node(&self) -> Tree { - use policy::Concrete::*; + use policy::concrete::Policy::*; match self.as_ref() { Unsatisfiable | Trivial | Key(_) | After(_) | Older(_) | Sha256(_) | Hash256(_) | Ripemd160(_) | Hash160(_) => Tree::Nullary, diff --git a/src/lib.rs b/src/lib.rs index 7b798915e..e9ac52d71 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -118,6 +118,7 @@ mod pub_macros; use internals::hex::exts::DisplayHex; pub use pub_macros::*; +pub mod r#abstract; pub mod descriptor; pub mod expression; pub mod interpreter; @@ -474,7 +475,7 @@ pub enum Error { /// Errors related to policy PolicyError(policy::concrete::PolicyError), /// Errors related to lifting - LiftError(policy::LiftError), + LiftError(crate::r#abstract::LiftError), /// Forward script context related errors ContextError(miniscript::context::ScriptContextError), /// Recursion depth exceeded when parsing policy/miniscript from string @@ -644,8 +645,8 @@ where } #[doc(hidden)] -impl From for Error { - fn from(e: policy::LiftError) -> Error { Error::LiftError(e) } +impl From for Error { + fn from(e: crate::r#abstract::LiftError) -> Error { Error::LiftError(e) } } #[doc(hidden)] diff --git a/src/macros.rs b/src/macros.rs index 6d4b9156b..5658d8a27 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -15,7 +15,7 @@ macro_rules! ms_str { /// `policy_str!("wsh(c:or_i(pk({}),pk({})))", pk1, pk2)` #[cfg(all(feature = "compiler", test))] macro_rules! policy_str { - ($($arg:tt)*) => ($crate::policy::Concrete::from_str(&format!($($arg)*)).unwrap()) + ($($arg:tt)*) => ($crate::policy::concrete::Policy::from_str(&format!($($arg)*)).unwrap()) } /// Macro for implementing FromTree trait. This avoids copying all the Pk::Associated type bounds diff --git a/src/miniscript/mod.rs b/src/miniscript/mod.rs index 5eedc8091..8ed29f6c7 100644 --- a/src/miniscript/mod.rs +++ b/src/miniscript/mod.rs @@ -609,7 +609,7 @@ mod tests { use super::*; use crate::miniscript::types::{self, ExtData, Property, Type}; use crate::miniscript::Terminal; - use crate::policy::Liftable; + use crate::r#abstract::Liftable; use crate::test_utils::{StrKeyTranslator, StrXOnlyKeyTranslator}; use crate::{hex_script, ExtParams, Satisfier, ToPublicKey, TranslatePk}; diff --git a/src/policy/compiler.rs b/src/policy/compiler.rs index 66284f046..d11857a87 100644 --- a/src/policy/compiler.rs +++ b/src/policy/compiler.rs @@ -16,7 +16,7 @@ use crate::miniscript::context::SigType; use crate::miniscript::limits::MAX_PUBKEYS_PER_MULTISIG; use crate::miniscript::types::{self, ErrorKind, ExtData, Property, Type}; use crate::miniscript::ScriptContext; -use crate::policy::Concrete; +use crate::policy::concrete::Policy as Concrete; use crate::prelude::*; use crate::{policy, Miniscript, MiniscriptKey, Terminal}; @@ -1156,7 +1156,7 @@ mod tests { use super::*; use crate::miniscript::{Legacy, Segwitv0, Tap}; - use crate::policy::Liftable; + use crate::r#abstract::Liftable; use crate::{script_num_size, ToPublicKey}; type SPolicy = Concrete; diff --git a/src/policy/concrete.rs b/src/policy/concrete.rs index 1117a0849..af22c8066 100644 --- a/src/policy/concrete.rs +++ b/src/policy/concrete.rs @@ -10,14 +10,10 @@ use std::error; use bitcoin::{absolute, Sequence}; #[cfg(feature = "compiler")] use { - crate::descriptor::TapTree, - crate::miniscript::ScriptContext, - crate::policy::compiler::CompilerError, - crate::policy::compiler::OrdF64, - crate::policy::{compiler, Concrete, Liftable, Semantic}, - crate::Descriptor, - crate::Miniscript, - crate::Tap, + crate::descriptor::TapTree, crate::miniscript::ScriptContext, crate::policy::compiler, + crate::policy::compiler::CompilerError, crate::policy::compiler::OrdF64, + crate::policy::concrete::Policy as Concrete, crate::policy::r#abstract::Policy as Semantic, + crate::r#abstract::Liftable, crate::Descriptor, crate::Miniscript, crate::Tap, core::cmp::Reverse, }; diff --git a/src/policy/mod.rs b/src/policy/mod.rs index b11edc9bf..0ff94fdbf 100644 --- a/src/policy/mod.rs +++ b/src/policy/mod.rs @@ -9,551 +9,11 @@ //! The format represents EC public keys abstractly to allow wallets to replace //! these with BIP32 paths, pay-to-contract instructions, etc. //! -use core::fmt; -#[cfg(feature = "std")] -use std::error; pub mod r#abstract; #[cfg(feature = "compiler")] pub mod compiler; pub mod concrete; -pub use self::concrete::Policy as Concrete; -pub use self::r#abstract::Policy as Semantic; -use crate::descriptor::Descriptor; -use crate::miniscript::{Miniscript, ScriptContext}; -use crate::sync::Arc; -use crate::{Error, MiniscriptKey, Terminal}; - /// Policy entailment algorithm maximum number of terminals allowed. pub(crate) const ENTAILMENT_MAX_TERMINALS: usize = 20; - -/// Trait describing script representations which can be lifted into -/// an abstract policy, by discarding information. -/// -/// After Lifting all policies are converted into `KeyHash(Pk::HasH)` to -/// maintain the following invariant(modulo resource limits): -/// `Lift(Concrete) == Concrete -> Miniscript -> Script -> Miniscript -> Semantic` -/// -/// Lifting from [`Miniscript`] or [`Descriptor`] can fail if the miniscript -/// contains a timelock combination or if it contains a branch that exceeds -/// resource limits. -/// -/// Lifting from concrete policies can fail if the policy contains a timelock -/// combination. It is possible that a concrete policy has some branches that -/// exceed resource limits for any compilation but cannot detect such policies -/// while lifting. Note that our compiler would not succeed for any such -/// policies. -pub trait Liftable { - /// Converts this object into an abstract policy. - fn lift(&self) -> Result, Error>; -} - -/// Error occurring during lifting. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum LiftError { - /// Cannot lift policies that have a combination of height and timelocks. - HeightTimelockCombination, - /// Duplicate public keys. - BranchExceedResourceLimits, - /// Cannot lift raw descriptors. - RawDescriptorLift, -} - -impl fmt::Display for LiftError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - LiftError::HeightTimelockCombination => { - f.write_str("Cannot lift policies that have a heightlock and timelock combination") - } - LiftError::BranchExceedResourceLimits => f.write_str( - "Cannot lift policies containing one branch that exceeds resource limits", - ), - LiftError::RawDescriptorLift => f.write_str("Cannot lift raw descriptors"), - } - } -} - -#[cfg(feature = "std")] -impl error::Error for LiftError { - fn cause(&self) -> Option<&dyn error::Error> { - use self::LiftError::*; - - match self { - HeightTimelockCombination | BranchExceedResourceLimits | RawDescriptorLift => None, - } - } -} - -impl Miniscript { - /// Lifting corresponds to conversion of a miniscript into a [`Semantic`] - /// policy for human readable or machine analysis. However, naively lifting - /// miniscripts can result in incorrect interpretations that don't - /// correspond to the underlying semantics when we try to spend them on - /// bitcoin network. This can occur if the miniscript contains: - /// 1. A combination of timelocks - /// 2. A spend that exceeds resource limits - pub fn lift_check(&self) -> Result<(), LiftError> { - if !self.within_resource_limits() { - Err(LiftError::BranchExceedResourceLimits) - } else if self.has_mixed_timelocks() { - Err(LiftError::HeightTimelockCombination) - } else { - Ok(()) - } - } -} - -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 - self.lift_check()?; - self.as_inner().lift() - } -} - -impl Liftable for Terminal { - fn lift(&self) -> Result, Error> { - let ret = match *self { - Terminal::PkK(ref pk) | Terminal::PkH(ref pk) => Semantic::Key(pk.clone()), - Terminal::RawPkH(ref _pkh) => { - return Err(Error::LiftError(LiftError::RawDescriptorLift)) - } - Terminal::After(t) => Semantic::After(t), - Terminal::Older(t) => Semantic::Older(t), - Terminal::Sha256(ref h) => Semantic::Sha256(h.clone()), - Terminal::Hash256(ref h) => Semantic::Hash256(h.clone()), - Terminal::Ripemd160(ref h) => Semantic::Ripemd160(h.clone()), - Terminal::Hash160(ref h) => Semantic::Hash160(h.clone()), - Terminal::False => Semantic::Unsatisfiable, - Terminal::True => Semantic::Trivial, - Terminal::Alt(ref sub) - | Terminal::Swap(ref sub) - | Terminal::Check(ref sub) - | Terminal::DupIf(ref sub) - | Terminal::Verify(ref sub) - | Terminal::NonZero(ref sub) - | Terminal::ZeroNotEqual(ref sub) => sub.node.lift()?, - Terminal::AndV(ref left, ref right) | Terminal::AndB(ref left, ref right) => { - Semantic::Threshold(2, vec![left.node.lift()?, right.node.lift()?]) - } - Terminal::AndOr(ref a, ref b, ref c) => Semantic::Threshold( - 1, - vec![ - Semantic::Threshold(2, vec![a.node.lift()?, b.node.lift()?]), - c.node.lift()?, - ], - ), - Terminal::OrB(ref left, ref right) - | Terminal::OrD(ref left, ref right) - | Terminal::OrC(ref left, ref right) - | Terminal::OrI(ref left, ref right) => { - Semantic::Threshold(1, vec![left.node.lift()?, right.node.lift()?]) - } - Terminal::Thresh(k, ref subs) => { - let semantic_subs: Result<_, Error> = subs.iter().map(|s| s.node.lift()).collect(); - Semantic::Threshold(k, semantic_subs?) - } - Terminal::Multi(k, ref keys) | Terminal::MultiA(k, ref keys) => { - Semantic::Threshold(k, keys.iter().map(|k| Semantic::Key(k.clone())).collect()) - } - } - .normalized(); - Ok(ret) - } -} - -impl Liftable for Descriptor { - fn lift(&self) -> Result, Error> { - match *self { - Descriptor::Bare(ref bare) => bare.lift(), - Descriptor::Pkh(ref pkh) => pkh.lift(), - Descriptor::Wpkh(ref wpkh) => wpkh.lift(), - Descriptor::Wsh(ref wsh) => wsh.lift(), - Descriptor::Sh(ref sh) => sh.lift(), - Descriptor::Tr(ref tr) => tr.lift(), - } - } -} - -impl Liftable for Semantic { - fn lift(&self) -> Result, Error> { Ok(self.clone()) } -} - -impl Liftable for Concrete { - fn lift(&self) -> Result, Error> { - // do not lift if there is a possible satisfaction - // involving combination of timelocks and heightlocks - self.check_timelocks()?; - let ret = match *self { - Concrete::Unsatisfiable => Semantic::Unsatisfiable, - Concrete::Trivial => Semantic::Trivial, - Concrete::Key(ref pk) => Semantic::Key(pk.clone()), - Concrete::After(t) => Semantic::After(t), - Concrete::Older(t) => Semantic::Older(t), - Concrete::Sha256(ref h) => Semantic::Sha256(h.clone()), - Concrete::Hash256(ref h) => Semantic::Hash256(h.clone()), - Concrete::Ripemd160(ref h) => Semantic::Ripemd160(h.clone()), - Concrete::Hash160(ref h) => Semantic::Hash160(h.clone()), - Concrete::And(ref subs) => { - let semantic_subs: Result<_, Error> = subs.iter().map(Liftable::lift).collect(); - Semantic::Threshold(2, semantic_subs?) - } - Concrete::Or(ref subs) => { - let semantic_subs: Result<_, Error> = - subs.iter().map(|(_p, sub)| sub.lift()).collect(); - Semantic::Threshold(1, semantic_subs?) - } - Concrete::Threshold(k, ref subs) => { - let semantic_subs: Result<_, Error> = subs.iter().map(Liftable::lift).collect(); - Semantic::Threshold(k, semantic_subs?) - } - } - .normalized(); - Ok(ret) - } -} -impl Liftable for Arc> { - fn lift(&self) -> Result, Error> { self.as_ref().lift() } -} - -#[cfg(test)] -mod tests { - use core::str::FromStr; - - use bitcoin::Sequence; - #[cfg(feature = "compiler")] - use sync::Arc; - - use super::*; - #[cfg(feature = "compiler")] - use crate::descriptor::Tr; - use crate::prelude::*; - #[cfg(feature = "compiler")] - use crate::{descriptor::TapTree, Descriptor, Tap}; - - type ConcretePol = Concrete; - type SemanticPol = Semantic; - - fn concrete_policy_rtt(s: &str) { - let conc = ConcretePol::from_str(s).unwrap(); - let output = conc.to_string(); - assert_eq!(s.to_lowercase(), output.to_lowercase()); - } - - fn semantic_policy_rtt(s: &str) { - let sem = SemanticPol::from_str(s).unwrap(); - let output = sem.normalized().to_string(); - assert_eq!(s.to_lowercase(), output.to_lowercase()); - } - - #[test] - fn test_timelock_validity() { - // only height - assert!(ConcretePol::from_str("after(100)").is_ok()); - // only time - assert!(ConcretePol::from_str("after(1000000000)").is_ok()); - // disjunction - assert!(ConcretePol::from_str("or(after(1000000000),after(100))").is_ok()); - // conjunction - assert!(ConcretePol::from_str("and(after(1000000000),after(100))").is_err()); - // thresh with k = 1 - assert!(ConcretePol::from_str("thresh(1,pk(),after(1000000000),after(100))").is_ok()); - // thresh with k = 2 - assert!(ConcretePol::from_str("thresh(2,after(1000000000),after(100),pk())").is_err()); - } - #[test] - fn policy_rtt_tests() { - concrete_policy_rtt("pk()"); - concrete_policy_rtt("or(1@pk(),1@pk())"); - concrete_policy_rtt("or(99@pk(),1@pk())"); - concrete_policy_rtt("and(pk(),or(99@pk(),1@older(12960)))"); - - semantic_policy_rtt("pk()"); - semantic_policy_rtt("or(pk(),pk())"); - semantic_policy_rtt("and(pk(),pk())"); - - //fuzzer crashes - assert!(ConcretePol::from_str("thresh()").is_err()); - assert!(SemanticPol::from_str("thresh(0)").is_err()); - assert!(SemanticPol::from_str("thresh()").is_err()); - concrete_policy_rtt("ripemd160()"); - } - - #[test] - fn compile_invalid() { - // Since the root Error does not support Eq type, we have to - // compare the string representations of the error - assert_eq!( - ConcretePol::from_str("thresh(2,pk(),thresh(0))") - .unwrap_err() - .to_string(), - "Threshold k must be greater than 0 and less than or equal to n 0 0" - ); - - assert_eq!( - ConcretePol::from_str("thresh(2,older(2147483650),pk(),pk())") - .unwrap_err() - .to_string(), - "Relative/Absolute time must be less than 2^31; n < 2^31" - ); - } - - //https://github.com/apoelstra/rust-miniscript/issues/41 - #[test] - fn heavy_nest() { - let policy_string = "thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk(),thresh(1,pk(),pk(),pk()))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))"; - ConcretePol::from_str(policy_string).unwrap_err(); - } - - #[test] - fn lift_andor() { - let key_a: bitcoin::PublicKey = - "02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e" - .parse() - .unwrap(); - let key_b: bitcoin::PublicKey = - "03b506a1dbe57b4bf48c95e0c7d417b87dd3b4349d290d2e7e9ba72c912652d80a" - .parse() - .unwrap(); - - let ms_str: Miniscript = - format!("andor(multi(1,{}),older(42),c:pk_k({}))", key_a.inner, key_b.inner) - .parse() - .unwrap(); - assert_eq!( - Semantic::Threshold( - 1, - vec![ - Semantic::Threshold( - 2, - vec![ - Semantic::Key(key_a), - Semantic::Older(Sequence::from_height(42)) - ] - ), - Semantic::Key(key_b) - ] - ), - ms_str.lift().unwrap() - ); - } - - #[test] - #[cfg(feature = "compiler")] - fn taproot_compile() { - // Trivial single-node compilation - let unspendable_key: String = "UNSPENDABLE".to_string(); - { - let policy: Concrete = policy_str!("thresh(2,pk(A),pk(B),pk(C),pk(D))"); - let descriptor = policy.compile_tr(Some(unspendable_key.clone())).unwrap(); - - let ms_compilation: Miniscript = ms_str!("multi_a(2,A,B,C,D)"); - let tree: TapTree = TapTree::Leaf(Arc::new(ms_compilation)); - let expected_descriptor = - Descriptor::new_tr(unspendable_key.clone(), Some(tree)).unwrap(); - assert_eq!(descriptor, expected_descriptor); - } - - // Trivial multi-node compilation - { - let policy: Concrete = policy_str!("or(and(pk(A),pk(B)),and(pk(C),pk(D)))"); - let descriptor = policy.compile_tr(Some(unspendable_key.clone())).unwrap(); - - let left_ms_compilation: Arc> = - Arc::new(ms_str!("and_v(v:pk(C),pk(D))")); - let right_ms_compilation: Arc> = - Arc::new(ms_str!("and_v(v:pk(A),pk(B))")); - - let left = TapTree::Leaf(left_ms_compilation); - let right = TapTree::Leaf(right_ms_compilation); - let tree = TapTree::combine(left, right); - - let expected_descriptor = - Descriptor::new_tr(unspendable_key.clone(), Some(tree)).unwrap(); - assert_eq!(descriptor, expected_descriptor); - } - - { - // Invalid policy compilation (Duplicate PubKeys) - let policy: Concrete = policy_str!("or(and(pk(A),pk(B)),and(pk(A),pk(D)))"); - let descriptor = policy.compile_tr(Some(unspendable_key.clone())); - - assert_eq!(descriptor.unwrap_err().to_string(), "Policy contains duplicate keys"); - } - - // Non-trivial multi-node compilation - { - let node_policies = [ - "and(pk(A),pk(B))", - "and(pk(C),older(12960))", - "pk(D)", - "pk(E)", - "thresh(3,pk(F),pk(G),pk(H))", - "and(and(or(2@pk(I),1@pk(J)),or(1@pk(K),20@pk(L))),pk(M))", - "pk(N)", - ]; - - // Floating-point precision errors cause the minor errors - let node_probabilities: [f64; 7] = - [0.12000002, 0.28, 0.08, 0.12, 0.19, 0.18999998, 0.02]; - - let policy: Concrete = policy_str!( - "{}", - &format!( - "or(4@or(3@{},7@{}),6@thresh(1,or(4@{},6@{}),{},or(9@{},1@{})))", - node_policies[0], - node_policies[1], - node_policies[2], - node_policies[3], - node_policies[4], - node_policies[5], - node_policies[6] - ) - ); - let descriptor = policy.compile_tr(Some(unspendable_key.clone())).unwrap(); - - let mut sorted_policy_prob = node_policies - .iter() - .zip(node_probabilities.iter()) - .collect::>(); - sorted_policy_prob.sort_by(|a, b| (a.1).partial_cmp(&b.1).unwrap()); - let sorted_policies = sorted_policy_prob - .into_iter() - .map(|(x, _prob)| x) - .collect::>(); - - // Generate TapTree leaves compilations from the given sub-policies - let node_compilations = sorted_policies - .into_iter() - .map(|x| { - let leaf_policy: Concrete = policy_str!("{}", x); - TapTree::Leaf(Arc::from(leaf_policy.compile::().unwrap())) - }) - .collect::>(); - - // Arrange leaf compilations (acc. to probabilities) using huffman encoding into a TapTree - let tree = TapTree::combine( - TapTree::combine(node_compilations[4].clone(), node_compilations[5].clone()), - TapTree::combine( - TapTree::combine( - TapTree::combine( - node_compilations[0].clone(), - node_compilations[1].clone(), - ), - node_compilations[3].clone(), - ), - node_compilations[6].clone(), - ), - ); - - let expected_descriptor = Descriptor::new_tr("E".to_string(), Some(tree)).unwrap(); - assert_eq!(descriptor, expected_descriptor); - } - } - - #[test] - #[cfg(feature = "compiler")] - fn experimental_taproot_compile() { - let unspendable_key = "UNSPEND".to_string(); - - { - let pol = Concrete::::from_str( - "thresh(7,pk(A),pk(B),pk(C),pk(D),pk(E),pk(F),pk(G),pk(H))", - ) - .unwrap(); - let desc = pol - .compile_tr_private_experimental(Some(unspendable_key.clone())) - .unwrap(); - let expected_desc = Descriptor::Tr( - Tr::::from_str( - "tr(UNSPEND ,{ - { - {multi_a(7,B,C,D,E,F,G,H),multi_a(7,A,C,D,E,F,G,H)}, - {multi_a(7,A,B,D,E,F,G,H),multi_a(7,A,B,C,E,F,G,H)} - }, - { - {multi_a(7,A,B,C,D,F,G,H),multi_a(7,A,B,C,D,E,G,H)} - ,{multi_a(7,A,B,C,D,E,F,H),multi_a(7,A,B,C,D,E,F,G)} - }})" - .replace(&['\t', ' ', '\n'][..], "") - .as_str(), - ) - .unwrap(), - ); - assert_eq!(desc, expected_desc); - } - - { - let pol = - Concrete::::from_str("thresh(3,pk(A),pk(B),pk(C),pk(D),pk(E))").unwrap(); - let desc = pol - .compile_tr_private_experimental(Some(unspendable_key.clone())) - .unwrap(); - let expected_desc = Descriptor::Tr( - Tr::::from_str( - "tr(UNSPEND, - {{ - {multi_a(3,A,D,E),multi_a(3,A,C,E)}, - {multi_a(3,A,C,D),multi_a(3,A,B,E)}\ - }, - { - {multi_a(3,A,B,D),multi_a(3,A,B,C)}, - { - {multi_a(3,C,D,E),multi_a(3,B,D,E)}, - {multi_a(3,B,C,E),multi_a(3,B,C,D)} - }}})" - .replace(&['\t', ' ', '\n'][..], "") - .as_str(), - ) - .unwrap(), - ); - assert_eq!(desc, expected_desc); - } - } -} - -#[cfg(all(bench, feature = "compiler"))] -mod benches { - use core::str::FromStr; - - use test::{black_box, Bencher}; - - use super::{Concrete, Error}; - use crate::descriptor::Descriptor; - use crate::prelude::*; - type TapDesc = Result, Error>; - - #[bench] - pub fn compile_large_tap(bh: &mut Bencher) { - let pol = Concrete::::from_str( - "thresh(20,pk(A),pk(B),pk(C),pk(D),pk(E),pk(F),pk(G),pk(H),pk(I),pk(J),pk(K),pk(L),pk(M),pk(N),pk(O),pk(P),pk(Q),pk(R),pk(S),pk(T),pk(U),pk(V),pk(W),pk(X),pk(Y),pk(Z))", - ) - .expect("parsing"); - bh.iter(|| { - let pt: TapDesc = pol.compile_tr_private_experimental(Some("UNSPEND".to_string())); - black_box(pt).unwrap(); - }); - } -} From 08ddb6ca2a286b3ed161089f5cc3ff51d5268746 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Thu, 28 Sep 2023 08:09:24 +1000 Subject: [PATCH 4/8] Use Abstract instead of Semantic We are moving away from the usage of "semantic" as a synonym for "abstract". Use `Abstract` instead of `Semantic` as the type alias for `crate::policy::r#abstract::Policy`. --- src/abstract/lift.rs | 94 +++++++++++++++++++++--------------------- src/policy/abstract.rs | 7 +--- src/policy/concrete.rs | 14 +++---- 3 files changed, 56 insertions(+), 59 deletions(-) diff --git a/src/abstract/lift.rs b/src/abstract/lift.rs index 5f19b480d..9a4b094f9 100644 --- a/src/abstract/lift.rs +++ b/src/abstract/lift.rs @@ -15,7 +15,7 @@ use std::error; use crate::descriptor::Descriptor; use crate::miniscript::{Miniscript, ScriptContext}; use crate::policy::concrete::Policy as Concrete; -use crate::policy::r#abstract::Policy as Semantic; +use crate::policy::r#abstract::Policy as Abstract; use crate::sync::Arc; use crate::{Error, MiniscriptKey, Terminal}; @@ -24,7 +24,7 @@ use crate::{Error, MiniscriptKey, Terminal}; /// /// After Lifting all policies are converted into `KeyHash(Pk::HasH)` to /// maintain the following invariant(modulo resource limits): -/// `Lift(Concrete) == Concrete -> Miniscript -> Script -> Miniscript -> Semantic` +/// `Lift(Concrete) == Concrete -> Miniscript -> Script -> Miniscript -> Abstract` /// /// Lifting from [`Miniscript`] or [`Descriptor`] can fail if the miniscript /// contains a timelock combination or if it contains a branch that exceeds @@ -37,7 +37,7 @@ use crate::{Error, MiniscriptKey, Terminal}; /// policies. pub trait Liftable { /// Converts this object into an abstract policy. - fn lift(&self) -> Result, Error>; + fn lift(&self) -> Result, Error>; } /// Error occurring during lifting. @@ -77,7 +77,7 @@ impl error::Error for LiftError { } impl Miniscript { - /// Lifting corresponds to conversion of a miniscript into a [`Semantic`] + /// Lifting corresponds to conversion of a miniscript into a [`Abstract`] /// policy for human readable or machine analysis. However, naively lifting /// miniscripts can result in incorrect interpretations that don't /// correspond to the underlying semantics when we try to spend them on @@ -96,7 +96,7 @@ impl Miniscript { } impl Liftable for Miniscript { - fn lift(&self) -> Result, Error> { + fn lift(&self) -> Result, Error> { // check whether the root miniscript can have a spending path that is // a combination of heightlock and timelock self.lift_check()?; @@ -105,20 +105,20 @@ impl Liftable for Miniscript } impl Liftable for Terminal { - fn lift(&self) -> Result, Error> { + fn lift(&self) -> Result, Error> { let ret = match *self { - Terminal::PkK(ref pk) | Terminal::PkH(ref pk) => Semantic::Key(pk.clone()), + Terminal::PkK(ref pk) | Terminal::PkH(ref pk) => Abstract::Key(pk.clone()), Terminal::RawPkH(ref _pkh) => { return Err(Error::LiftError(LiftError::RawDescriptorLift)) } - Terminal::After(t) => Semantic::After(t), - Terminal::Older(t) => Semantic::Older(t), - Terminal::Sha256(ref h) => Semantic::Sha256(h.clone()), - Terminal::Hash256(ref h) => Semantic::Hash256(h.clone()), - Terminal::Ripemd160(ref h) => Semantic::Ripemd160(h.clone()), - Terminal::Hash160(ref h) => Semantic::Hash160(h.clone()), - Terminal::False => Semantic::Unsatisfiable, - Terminal::True => Semantic::Trivial, + Terminal::After(t) => Abstract::After(t), + Terminal::Older(t) => Abstract::Older(t), + Terminal::Sha256(ref h) => Abstract::Sha256(h.clone()), + Terminal::Hash256(ref h) => Abstract::Hash256(h.clone()), + Terminal::Ripemd160(ref h) => Abstract::Ripemd160(h.clone()), + Terminal::Hash160(ref h) => Abstract::Hash160(h.clone()), + Terminal::False => Abstract::Unsatisfiable, + Terminal::True => Abstract::Trivial, Terminal::Alt(ref sub) | Terminal::Swap(ref sub) | Terminal::Check(ref sub) @@ -127,12 +127,12 @@ impl Liftable for Terminal { | Terminal::NonZero(ref sub) | Terminal::ZeroNotEqual(ref sub) => sub.node.lift()?, Terminal::AndV(ref left, ref right) | Terminal::AndB(ref left, ref right) => { - Semantic::Threshold(2, vec![left.node.lift()?, right.node.lift()?]) + Abstract::Threshold(2, vec![left.node.lift()?, right.node.lift()?]) } - Terminal::AndOr(ref a, ref b, ref c) => Semantic::Threshold( + Terminal::AndOr(ref a, ref b, ref c) => Abstract::Threshold( 1, vec![ - Semantic::Threshold(2, vec![a.node.lift()?, b.node.lift()?]), + Abstract::Threshold(2, vec![a.node.lift()?, b.node.lift()?]), c.node.lift()?, ], ), @@ -140,14 +140,14 @@ impl Liftable for Terminal { | Terminal::OrD(ref left, ref right) | Terminal::OrC(ref left, ref right) | Terminal::OrI(ref left, ref right) => { - Semantic::Threshold(1, vec![left.node.lift()?, right.node.lift()?]) + Abstract::Threshold(1, vec![left.node.lift()?, right.node.lift()?]) } Terminal::Thresh(k, ref subs) => { let semantic_subs: Result<_, Error> = subs.iter().map(|s| s.node.lift()).collect(); - Semantic::Threshold(k, semantic_subs?) + Abstract::Threshold(k, semantic_subs?) } Terminal::Multi(k, ref keys) | Terminal::MultiA(k, ref keys) => { - Semantic::Threshold(k, keys.iter().map(|k| Semantic::Key(k.clone())).collect()) + Abstract::Threshold(k, keys.iter().map(|k| Abstract::Key(k.clone())).collect()) } } .normalized(); @@ -156,7 +156,7 @@ impl Liftable for Terminal { } impl Liftable for Descriptor { - fn lift(&self) -> Result, Error> { + fn lift(&self) -> Result, Error> { match *self { Descriptor::Bare(ref bare) => bare.lift(), Descriptor::Pkh(ref pkh) => pkh.lift(), @@ -168,37 +168,37 @@ impl Liftable for Descriptor { } } -impl Liftable for Semantic { - fn lift(&self) -> Result, Error> { Ok(self.clone()) } +impl Liftable for Abstract { + fn lift(&self) -> Result, Error> { Ok(self.clone()) } } impl Liftable for Concrete { - fn lift(&self) -> Result, Error> { + fn lift(&self) -> Result, Error> { // do not lift if there is a possible satisfaction // involving combination of timelocks and heightlocks self.check_timelocks()?; let ret = match *self { - Concrete::Unsatisfiable => Semantic::Unsatisfiable, - Concrete::Trivial => Semantic::Trivial, - Concrete::Key(ref pk) => Semantic::Key(pk.clone()), - Concrete::After(t) => Semantic::After(t), - Concrete::Older(t) => Semantic::Older(t), - Concrete::Sha256(ref h) => Semantic::Sha256(h.clone()), - Concrete::Hash256(ref h) => Semantic::Hash256(h.clone()), - Concrete::Ripemd160(ref h) => Semantic::Ripemd160(h.clone()), - Concrete::Hash160(ref h) => Semantic::Hash160(h.clone()), + Concrete::Unsatisfiable => Abstract::Unsatisfiable, + Concrete::Trivial => Abstract::Trivial, + Concrete::Key(ref pk) => Abstract::Key(pk.clone()), + Concrete::After(t) => Abstract::After(t), + Concrete::Older(t) => Abstract::Older(t), + Concrete::Sha256(ref h) => Abstract::Sha256(h.clone()), + Concrete::Hash256(ref h) => Abstract::Hash256(h.clone()), + Concrete::Ripemd160(ref h) => Abstract::Ripemd160(h.clone()), + Concrete::Hash160(ref h) => Abstract::Hash160(h.clone()), Concrete::And(ref subs) => { let semantic_subs: Result<_, Error> = subs.iter().map(Liftable::lift).collect(); - Semantic::Threshold(2, semantic_subs?) + Abstract::Threshold(2, semantic_subs?) } Concrete::Or(ref subs) => { let semantic_subs: Result<_, Error> = subs.iter().map(|(_p, sub)| sub.lift()).collect(); - Semantic::Threshold(1, semantic_subs?) + Abstract::Threshold(1, semantic_subs?) } Concrete::Threshold(k, ref subs) => { let semantic_subs: Result<_, Error> = subs.iter().map(Liftable::lift).collect(); - Semantic::Threshold(k, semantic_subs?) + Abstract::Threshold(k, semantic_subs?) } } .normalized(); @@ -206,7 +206,7 @@ impl Liftable for Concrete { } } impl Liftable for Arc> { - fn lift(&self) -> Result, Error> { self.as_ref().lift() } + fn lift(&self) -> Result, Error> { self.as_ref().lift() } } #[cfg(test)] @@ -227,7 +227,7 @@ mod tests { use crate::{descriptor::TapTree, Descriptor, Tap}; type ConcretePol = crate::policy::concrete::Policy; - type SemanticPol = crate::policy::r#abstract::Policy; + type AbstractPol = crate::policy::r#abstract::Policy; fn concrete_policy_rtt(s: &str) { let conc = ConcretePol::from_str(s).unwrap(); @@ -236,7 +236,7 @@ mod tests { } fn semantic_policy_rtt(s: &str) { - let sem = SemanticPol::from_str(s).unwrap(); + let sem = AbstractPol::from_str(s).unwrap(); let output = sem.normalized().to_string(); assert_eq!(s.to_lowercase(), output.to_lowercase()); } @@ -269,8 +269,8 @@ mod tests { //fuzzer crashes assert!(ConcretePol::from_str("thresh()").is_err()); - assert!(SemanticPol::from_str("thresh(0)").is_err()); - assert!(SemanticPol::from_str("thresh()").is_err()); + assert!(AbstractPol::from_str("thresh(0)").is_err()); + assert!(AbstractPol::from_str("thresh()").is_err()); concrete_policy_rtt("ripemd160()"); } @@ -336,17 +336,17 @@ mod tests { .parse() .unwrap(); assert_eq!( - Semantic::Threshold( + Abstract::Threshold( 1, vec![ - Semantic::Threshold( + Abstract::Threshold( 2, vec![ - Semantic::Key(key_a), - Semantic::Older(Sequence::from_height(42)) + Abstract::Key(key_a), + Abstract::Older(Sequence::from_height(42)) ] ), - Semantic::Key(key_b) + Abstract::Key(key_b) ] ), ms_str.lift().unwrap() diff --git a/src/policy/abstract.rs b/src/policy/abstract.rs index 3a0dfcddd..435eb2ca6 100644 --- a/src/policy/abstract.rs +++ b/src/policy/abstract.rs @@ -1,9 +1,6 @@ // SPDX-License-Identifier: CC0-1.0 //! Abstract Policies -//! -//! We use the terms "semantic" and "abstract" interchangeably because -//! "abstract" is a reserved keyword in Rust. use core::str::FromStr; use core::{fmt, str}; @@ -18,7 +15,7 @@ use crate::{errstr, expression, AbsLockTime, Error, ForEachKey, MiniscriptKey, T /// Abstract policy which corresponds to the semantics of a miniscript and /// which allows complex forms of analysis, e.g. filtering and normalization. /// -/// Semantic policies store only hashes of keys to ensure that objects +/// Abstract policies store only hashes of keys to ensure that objects /// representing the same policy are lifted to the same abstract `Policy`, /// regardless of their choice of `pk` or `pk_h` nodes. #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] @@ -380,7 +377,7 @@ impl_from_tree!( // thresh(1) and thresh(n) are disallowed in semantic policies if thresh <= 1 || thresh >= (nsubs as u32 - 1) { return Err(errstr( - "Semantic Policy thresh cannot have k = 1 or k =n, use `and`/`or` instead", + "Abstract Policy thresh cannot have k = 1 or k =n, use `and`/`or` instead", )); } if thresh >= (nsubs as u32) { diff --git a/src/policy/concrete.rs b/src/policy/concrete.rs index af22c8066..a67d4eba0 100644 --- a/src/policy/concrete.rs +++ b/src/policy/concrete.rs @@ -12,7 +12,7 @@ use bitcoin::{absolute, Sequence}; use { crate::descriptor::TapTree, crate::miniscript::ScriptContext, crate::policy::compiler, crate::policy::compiler::CompilerError, crate::policy::compiler::OrdF64, - crate::policy::concrete::Policy as Concrete, crate::policy::r#abstract::Policy as Semantic, + crate::policy::concrete::Policy as Concrete, crate::policy::r#abstract::Policy as Abstract, crate::r#abstract::Liftable, crate::Descriptor, crate::Miniscript, crate::Tap, core::cmp::Reverse, }; @@ -94,9 +94,9 @@ pub enum PolicyError { ZeroTime, /// `after` fragment can only have `n < 2^31`. TimeTooFar, - /// Semantic Policy Error: `And` `Or` fragments must take args: `k > 1`. + /// Abstract Policy Error: `And` `Or` fragments must take args: `k > 1`. InsufficientArgsforAnd, - /// Semantic policy error: `And` `Or` fragments must take args: `k > 1`. + /// Abstract policy error: `And` `Or` fragments must take args: `k > 1`. InsufficientArgsforOr, /// Entailment max terminals exceeded. EntailmentMaxTerminals, @@ -136,10 +136,10 @@ impl fmt::Display for PolicyError { } PolicyError::ZeroTime => f.write_str("Time must be greater than 0; n > 0"), PolicyError::InsufficientArgsforAnd => { - f.write_str("Semantic Policy 'And' fragment must have at least 2 args ") + f.write_str("Abstract Policy 'And' fragment must have at least 2 args ") } PolicyError::InsufficientArgsforOr => { - f.write_str("Semantic Policy 'Or' fragment must have at least 2 args ") + f.write_str("Abstract Policy 'Or' fragment must have at least 2 args ") } PolicyError::EntailmentMaxTerminals => { write!(f, "Policy entailment only supports {} terminals", ENTAILMENT_MAX_TERMINALS) @@ -234,8 +234,8 @@ impl Policy { for key in concrete_keys.into_iter() { if semantic_policy .clone() - .satisfy_constraint(&Semantic::Key(key.clone()), true) - == Semantic::Trivial + .satisfy_constraint(&Abstract::Key(key.clone()), true) + == Abstract::Trivial { match key_prob_map.get(&Concrete::Key(key.clone())) { Some(val) => { From 298e8d8d325247998e0e3f65dd363f8889531211 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Thu, 28 Sep 2023 08:16:13 +1000 Subject: [PATCH 5/8] Use Abstract type alias in Liftable for Tr Make it explicit that the `Policy` returned by `lift` in the `descriptor::tr` module is a `r#abstract::Policy`. --- src/descriptor/tr.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/descriptor/tr.rs b/src/descriptor/tr.rs index 47f982a03..6722332ab 100644 --- a/src/descriptor/tr.rs +++ b/src/descriptor/tr.rs @@ -16,7 +16,7 @@ use crate::expression::{self, FromTree}; use crate::miniscript::satisfy::{Placeholder, Satisfaction, SchnorrSigType, Witness}; use crate::miniscript::Miniscript; use crate::plan::AssetProvider; -use crate::policy::r#abstract::Policy; +use crate::policy::r#abstract::Policy as Abstract; use crate::prelude::*; use crate::r#abstract::Liftable; use crate::util::{varint_len, witness_size}; @@ -617,11 +617,11 @@ fn split_once(inp: &str, delim: char) -> Option<(&str, &str)> { } impl Liftable for TapTree { - fn lift(&self) -> Result, Error> { - fn lift_helper(s: &TapTree) -> Result, Error> { + fn lift(&self) -> Result, Error> { + fn lift_helper(s: &TapTree) -> Result, Error> { match *s { TapTree::Tree { ref left, ref right, height: _ } => { - Ok(Policy::Threshold(1, vec![lift_helper(left)?, lift_helper(right)?])) + Ok(Abstract::Threshold(1, vec![lift_helper(left)?, lift_helper(right)?])) } TapTree::Leaf(ref leaf) => leaf.lift(), } @@ -633,12 +633,12 @@ impl Liftable for TapTree { } impl Liftable for Tr { - fn lift(&self) -> Result, Error> { + fn lift(&self) -> Result, Error> { match &self.tree { Some(root) => { - Ok(Policy::Threshold(1, vec![Policy::Key(self.internal_key.clone()), root.lift()?])) + Ok(Abstract::Threshold(1, vec![Abstract::Key(self.internal_key.clone()), root.lift()?])) } - None => Ok(Policy::Key(self.internal_key.clone())), + None => Ok(Abstract::Key(self.internal_key.clone())), } } } From 463a41f9b921fa505fb905c6bfad53a05ae39616 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Thu, 28 Sep 2023 08:22:12 +1000 Subject: [PATCH 6/8] Move abstract::Policy to crate/abstract/mod.rs The abstract policy is different from the concrete policy because it is only part of rust-miniscript and not miniscript in general. To help differentiate it move it to a separate module. Note to reviewers, `git` couldn't work out that this is basically a file move so I've left the `src/policy/abstract.rs` file in the repo but it is not included in the build. Reviewers can do `diff src/policy/abstract.rs src/abstract/mod.rs` to ease review. It will be removed in the next patch. --- fuzz/fuzz_targets/roundtrip_semantic.rs | 3 +- src/abstract/lift.rs | 4 +- src/abstract/mod.rs | 962 +++++++++++++++++++++++- src/descriptor/bare.rs | 3 +- src/descriptor/segwitv0.rs | 3 +- src/descriptor/sh.rs | 3 +- src/descriptor/sortedmulti.rs | 2 +- src/descriptor/tr.rs | 2 +- src/policy/concrete.rs | 4 +- src/policy/mod.rs | 1 - 10 files changed, 970 insertions(+), 17 deletions(-) diff --git a/fuzz/fuzz_targets/roundtrip_semantic.rs b/fuzz/fuzz_targets/roundtrip_semantic.rs index 3a5bd3e0c..106830102 100644 --- a/fuzz/fuzz_targets/roundtrip_semantic.rs +++ b/fuzz/fuzz_targets/roundtrip_semantic.rs @@ -1,9 +1,8 @@ use std::str::FromStr; use honggfuzz::fuzz; -use miniscript::policy; -type Policy = policy::r#abstract::Policy; +type Policy = miniscript::r#abstract::Policy; fn do_test(data: &[u8]) { let data_str = String::from_utf8_lossy(data); diff --git a/src/abstract/lift.rs b/src/abstract/lift.rs index 9a4b094f9..99c2f8481 100644 --- a/src/abstract/lift.rs +++ b/src/abstract/lift.rs @@ -15,7 +15,7 @@ use std::error; use crate::descriptor::Descriptor; use crate::miniscript::{Miniscript, ScriptContext}; use crate::policy::concrete::Policy as Concrete; -use crate::policy::r#abstract::Policy as Abstract; +use crate::r#abstract::Policy as Abstract; use crate::sync::Arc; use crate::{Error, MiniscriptKey, Terminal}; @@ -227,7 +227,7 @@ mod tests { use crate::{descriptor::TapTree, Descriptor, Tap}; type ConcretePol = crate::policy::concrete::Policy; - type AbstractPol = crate::policy::r#abstract::Policy; + type AbstractPol = crate::r#abstract::Policy; fn concrete_policy_rtt(s: &str) { let conc = ConcretePol::from_str(s).unwrap(); diff --git a/src/abstract/mod.rs b/src/abstract/mod.rs index 9ce49b496..080eb4860 100644 --- a/src/abstract/mod.rs +++ b/src/abstract/mod.rs @@ -1,7 +1,965 @@ // SPDX-License-Identifier: CC0-1.0 -//! WIP - TODO: Describe this module. +//! Abstract Policies +mod lift; + +#[rustfmt::skip] // Keep public re-exports separate. pub use self::lift::{LiftError, Liftable}; -mod lift; +use core::str::FromStr; +use core::{fmt, str}; + +use bitcoin::{absolute, Sequence}; + +use crate::policy::concrete::PolicyError; +use crate::policy::ENTAILMENT_MAX_TERMINALS; +use crate::prelude::*; +use crate::{errstr, expression, AbsLockTime, Error, ForEachKey, MiniscriptKey, Translator}; + +/// Abstract policy which corresponds to the semantics of a miniscript and +/// which allows complex forms of analysis, e.g. filtering and normalization. +/// +/// Abstract policies store only hashes of keys to ensure that objects +/// representing the same policy are lifted to the same abstract `Policy`, +/// regardless of their choice of `pk` or `pk_h` nodes. +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum Policy { + /// Unsatisfiable. + Unsatisfiable, + /// Trivially satisfiable. + Trivial, + /// Signature and public key matching a given hash is required. + Key(Pk), + /// An absolute locktime restriction. + After(AbsLockTime), + /// A relative locktime restriction. + Older(Sequence), + /// A SHA256 whose preimage must be provided to satisfy the descriptor. + Sha256(Pk::Sha256), + /// A SHA256d whose preimage must be provided to satisfy the descriptor. + Hash256(Pk::Hash256), + /// A RIPEMD160 whose preimage must be provided to satisfy the descriptor. + Ripemd160(Pk::Ripemd160), + /// A HASH160 whose preimage must be provided to satisfy the descriptor. + Hash160(Pk::Hash160), + /// A set of descriptors, satisfactions must be provided for `k` of them. + Threshold(usize, Vec>), +} + +impl Policy +where + Pk: MiniscriptKey, +{ + /// Constructs a `Policy::After` from `n`. + /// + /// Helper function equivalent to `Policy::After(absolute::LockTime::from_consensus(n))`. + pub fn after(n: u32) -> Policy { + Policy::After(AbsLockTime::from(absolute::LockTime::from_consensus(n))) + } + + /// Construct a `Policy::Older` from `n`. + /// + /// Helper function equivalent to `Policy::Older(Sequence::from_consensus(n))`. + pub fn older(n: u32) -> Policy { Policy::Older(Sequence::from_consensus(n)) } +} + +impl ForEachKey for Policy { + fn for_each_key<'a, F: FnMut(&'a Pk) -> bool>(&'a self, mut pred: F) -> bool { + self.real_for_each_key(&mut pred) + } +} + +impl Policy { + fn real_for_each_key<'a, F: FnMut(&'a Pk) -> bool>(&'a self, pred: &mut F) -> bool { + match *self { + Policy::Unsatisfiable | Policy::Trivial => true, + Policy::Key(ref pk) => pred(pk), + Policy::Sha256(..) + | Policy::Hash256(..) + | Policy::Ripemd160(..) + | Policy::Hash160(..) + | Policy::After(..) + | Policy::Older(..) => true, + Policy::Threshold(_, ref subs) => { + subs.iter().all(|sub| sub.real_for_each_key(&mut *pred)) + } + } + } + + /// Converts a policy using one kind of public key to another type of public key. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// use std::str::FromStr; + /// use miniscript::bitcoin::{hashes::hash160, PublicKey}; + /// use miniscript::{translate_hash_fail, r#abstract::Policy, Translator}; + /// let alice_pk = "02c79ef3ede6d14f72a00d0e49b4becfb152197b64c0707425c4f231df29500ee7"; + /// let bob_pk = "03d008a849fbf474bd17e9d2c1a827077a468150e58221582ec3410ab309f5afe4"; + /// let placeholder_policy = Policy::::from_str("and(pk(alice_pk),pk(bob_pk))").unwrap(); + /// + /// // Information to translate abstract string type keys to concrete `bitcoin::PublicKey`s. + /// // In practice, wallets would map from string key names to BIP32 keys. + /// struct StrPkTranslator { + /// pk_map: HashMap + /// } + /// + /// // If we also wanted to provide mapping of other associated types (sha256, older etc), + /// // we would use the general [`Translator`] trait. + /// impl Translator for StrPkTranslator { + /// fn pk(&mut self, pk: &String) -> Result { + /// self.pk_map.get(pk).copied().ok_or(()) // Dummy Err + /// } + /// + /// // Handy macro for failing if we encounter any other fragment. + /// // See also [`translate_hash_clone!`] for cloning instead of failing. + /// translate_hash_fail!(String, bitcoin::PublicKey, ()); + /// } + /// + /// let mut pk_map = HashMap::new(); + /// pk_map.insert(String::from("alice_pk"), bitcoin::PublicKey::from_str(alice_pk).unwrap()); + /// pk_map.insert(String::from("bob_pk"), bitcoin::PublicKey::from_str(bob_pk).unwrap()); + /// let mut t = StrPkTranslator { pk_map }; + /// + /// let real_policy = placeholder_policy.translate_pk(&mut t).unwrap(); + /// + /// let expected_policy = Policy::from_str(&format!("and(pk({}),pk({}))", alice_pk, bob_pk)).unwrap(); + /// assert_eq!(real_policy, expected_policy); + /// ``` + pub fn translate_pk(&self, t: &mut T) -> Result, E> + where + T: Translator, + Q: MiniscriptKey, + { + self._translate_pk(t) + } + + fn _translate_pk(&self, t: &mut T) -> Result, E> + where + T: Translator, + Q: MiniscriptKey, + { + match *self { + Policy::Unsatisfiable => Ok(Policy::Unsatisfiable), + Policy::Trivial => Ok(Policy::Trivial), + Policy::Key(ref pk) => t.pk(pk).map(Policy::Key), + Policy::Sha256(ref h) => t.sha256(h).map(Policy::Sha256), + Policy::Hash256(ref h) => t.hash256(h).map(Policy::Hash256), + Policy::Ripemd160(ref h) => t.ripemd160(h).map(Policy::Ripemd160), + Policy::Hash160(ref h) => t.hash160(h).map(Policy::Hash160), + Policy::After(n) => Ok(Policy::After(n)), + Policy::Older(n) => Ok(Policy::Older(n)), + Policy::Threshold(k, ref subs) => { + let new_subs: Result>, _> = + subs.iter().map(|sub| sub._translate_pk(t)).collect(); + new_subs.map(|ok| Policy::Threshold(k, ok)) + } + } + } + + /// Computes whether the current policy entails the second one. + /// + /// A |- B means every satisfaction of A is also a satisfaction of B. + /// + /// This implementation will run slowly for larger policies but should be + /// sufficient for most practical policies. + // This algorithm has a naive implementation. It is possible to optimize this + // by memoizing and maintaining a hashmap. + pub fn entails(self, other: Policy) -> Result { + if self.n_terminals() > ENTAILMENT_MAX_TERMINALS { + return Err(PolicyError::EntailmentMaxTerminals); + } + match (self, other) { + (Policy::Unsatisfiable, _) => Ok(true), + (Policy::Trivial, Policy::Trivial) => Ok(true), + (Policy::Trivial, _) => Ok(false), + (_, Policy::Unsatisfiable) => Ok(false), + (a, b) => { + let (a_norm, b_norm) = (a.normalized(), b.normalized()); + let first_constraint = a_norm.first_constraint(); + let (a1, b1) = ( + a_norm.clone().satisfy_constraint(&first_constraint, true), + b_norm.clone().satisfy_constraint(&first_constraint, true), + ); + let (a2, b2) = ( + a_norm.satisfy_constraint(&first_constraint, false), + b_norm.satisfy_constraint(&first_constraint, false), + ); + Ok(Policy::entails(a1, b1)? && Policy::entails(a2, b2)?) + } + } + } + + // Helper function to compute the number of constraints in policy. + fn n_terminals(&self) -> usize { + match self { + &Policy::Threshold(_k, ref subs) => subs.iter().map(|sub| sub.n_terminals()).sum(), + &Policy::Trivial | &Policy::Unsatisfiable => 0, + _leaf => 1, + } + } + + // Helper function to get the first constraint in the policy. + // Returns the first leaf policy. Used in policy entailment. + // Assumes that the current policy is normalized. + fn first_constraint(&self) -> Policy { + debug_assert!(self.clone().normalized() == self.clone()); + match self { + &Policy::Threshold(_k, ref subs) => subs[0].first_constraint(), + first => first.clone(), + } + } + + // Helper function that takes in witness and its availability, changing it + // to true or false and returning the resultant normalized policy. Witness + // is currently encoded as policy. Only accepts leaf fragment and a + // normalized policy + pub(crate) fn satisfy_constraint(self, witness: &Policy, available: bool) -> Policy { + debug_assert!(self.clone().normalized() == self); + if let Policy::Threshold { .. } = *witness { + // We can't debug_assert on Policy::Threshold. + panic!("should be unreachable") + } + + let ret = match self { + Policy::Threshold(k, subs) => { + let mut ret_subs = vec![]; + for sub in subs { + ret_subs.push(sub.satisfy_constraint(witness, available)); + } + Policy::Threshold(k, ret_subs) + } + ref leaf if leaf == witness => { + if available { + Policy::Trivial + } else { + Policy::Unsatisfiable + } + } + x => x, + }; + ret.normalized() + } +} + +impl fmt::Debug for Policy { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Policy::Unsatisfiable => f.write_str("UNSATISFIABLE()"), + Policy::Trivial => f.write_str("TRIVIAL()"), + Policy::Key(ref pkh) => write!(f, "pk({:?})", pkh), + Policy::After(n) => write!(f, "after({})", n), + Policy::Older(n) => write!(f, "older({})", n), + Policy::Sha256(ref h) => write!(f, "sha256({})", h), + Policy::Hash256(ref h) => write!(f, "hash256({})", h), + Policy::Ripemd160(ref h) => write!(f, "ripemd160({})", h), + Policy::Hash160(ref h) => write!(f, "hash160({})", h), + Policy::Threshold(k, ref subs) => { + if k == subs.len() { + write!(f, "and(")?; + } else if k == 1 { + write!(f, "or(")?; + } else { + write!(f, "thresh({},", k)?; + } + for (i, sub) in subs.iter().enumerate() { + if i == 0 { + write!(f, "{}", sub)?; + } else { + write!(f, ",{}", sub)?; + } + } + f.write_str(")") + } + } + } +} + +impl fmt::Display for Policy { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Policy::Unsatisfiable => f.write_str("UNSATISFIABLE"), + Policy::Trivial => f.write_str("TRIVIAL"), + Policy::Key(ref pkh) => write!(f, "pk({})", pkh), + Policy::After(n) => write!(f, "after({})", n), + Policy::Older(n) => write!(f, "older({})", n), + Policy::Sha256(ref h) => write!(f, "sha256({})", h), + Policy::Hash256(ref h) => write!(f, "hash256({})", h), + Policy::Ripemd160(ref h) => write!(f, "ripemd160({})", h), + Policy::Hash160(ref h) => write!(f, "hash160({})", h), + Policy::Threshold(k, ref subs) => { + if k == subs.len() { + write!(f, "and(")?; + } else if k == 1 { + write!(f, "or(")?; + } else { + write!(f, "thresh({},", k)?; + } + for (i, sub) in subs.iter().enumerate() { + if i == 0 { + write!(f, "{}", sub)?; + } else { + write!(f, ",{}", sub)?; + } + } + f.write_str(")") + } + } + } +} + +impl_from_str!( + Policy, + type Err = Error;, + fn from_str(s: &str) -> Result, Error> { + expression::check_valid_chars(s)?; + + let tree = expression::Tree::from_str(s)?; + expression::FromTree::from_tree(&tree) + } +); + +serde_string_impl_pk!(Policy, "a miniscript semantic policy"); + +impl_from_tree!( + Policy, + fn from_tree(top: &expression::Tree) -> Result, Error> { + match (top.name, top.args.len()) { + ("UNSATISFIABLE", 0) => Ok(Policy::Unsatisfiable), + ("TRIVIAL", 0) => Ok(Policy::Trivial), + ("pk", 1) => expression::terminal(&top.args[0], |pk| Pk::from_str(pk).map(Policy::Key)), + ("after", 1) => expression::terminal(&top.args[0], |x| { + expression::parse_num(x).map(|x| Policy::after(x)) + }), + ("older", 1) => expression::terminal(&top.args[0], |x| { + expression::parse_num(x).map(|x| Policy::older(x)) + }), + ("sha256", 1) => { + expression::terminal(&top.args[0], |x| Pk::Sha256::from_str(x).map(Policy::Sha256)) + } + ("hash256", 1) => expression::terminal(&top.args[0], |x| { + Pk::Hash256::from_str(x).map(Policy::Hash256) + }), + ("ripemd160", 1) => expression::terminal(&top.args[0], |x| { + Pk::Ripemd160::from_str(x).map(Policy::Ripemd160) + }), + ("hash160", 1) => expression::terminal(&top.args[0], |x| { + Pk::Hash160::from_str(x).map(Policy::Hash160) + }), + ("and", nsubs) => { + if nsubs < 2 { + return Err(Error::PolicyError(PolicyError::InsufficientArgsforAnd)); + } + let mut subs = Vec::with_capacity(nsubs); + for arg in &top.args { + subs.push(Policy::from_tree(arg)?); + } + Ok(Policy::Threshold(nsubs, subs)) + } + ("or", nsubs) => { + if nsubs < 2 { + return Err(Error::PolicyError(PolicyError::InsufficientArgsforOr)); + } + let mut subs = Vec::with_capacity(nsubs); + for arg in &top.args { + subs.push(Policy::from_tree(arg)?); + } + Ok(Policy::Threshold(1, subs)) + } + ("thresh", nsubs) => { + if nsubs == 0 || nsubs == 1 { + // thresh() and thresh(k) are err + return Err(errstr("thresh without args")); + } + if !top.args[0].args.is_empty() { + return Err(errstr(top.args[0].args[0].name)); + } + + let thresh = expression::parse_num(top.args[0].name)?; + + // thresh(1) and thresh(n) are disallowed in semantic policies + if thresh <= 1 || thresh >= (nsubs as u32 - 1) { + return Err(errstr( + "Abstract Policy thresh cannot have k = 1 or k =n, use `and`/`or` instead", + )); + } + if thresh >= (nsubs as u32) { + return Err(errstr(top.args[0].name)); + } + + let mut subs = Vec::with_capacity(top.args.len() - 1); + for arg in &top.args[1..] { + subs.push(Policy::from_tree(arg)?); + } + Ok(Policy::Threshold(thresh as usize, subs)) + } + _ => Err(errstr(top.name)), + } + } +); + +impl Policy { + /// Flattens out trees of `And`s and `Or`s; eliminate `Trivial` and + /// `Unsatisfiable`s. Does not reorder any branches; use `.sort`. + pub fn normalized(self) -> Policy { + match self { + Policy::Threshold(k, subs) => { + let mut ret_subs = Vec::with_capacity(subs.len()); + + let subs: Vec<_> = subs.into_iter().map(|sub| sub.normalized()).collect(); + let trivial_count = subs.iter().filter(|&pol| *pol == Policy::Trivial).count(); + let unsatisfied_count = subs + .iter() + .filter(|&pol| *pol == Policy::Unsatisfiable) + .count(); + + let n = subs.len() - unsatisfied_count - trivial_count; // remove all true/false + let m = k.checked_sub(trivial_count).map_or(0, |x| x); // satisfy all trivial + // m == n denotes `and` and m == 1 denotes `or` + let is_and = m == n; + let is_or = m == 1; + for sub in subs { + match sub { + Policy::Trivial | Policy::Unsatisfiable => {} + Policy::Threshold(k, subs) => { + match (is_and, is_or) { + (true, true) => { + // means m = n = 1, thresh(1,X) type thing. + ret_subs.push(Policy::Threshold(k, subs)); + } + (true, false) if k == subs.len() => ret_subs.extend(subs), // and case + (false, true) if k == 1 => ret_subs.extend(subs), // or case + _ => ret_subs.push(Policy::Threshold(k, subs)), + } + } + x => ret_subs.push(x), + } + } + // Now reason about m of n threshold + if m == 0 { + Policy::Trivial + } else if m > ret_subs.len() { + Policy::Unsatisfiable + } else if ret_subs.len() == 1 { + ret_subs.pop().unwrap() + } else if is_and { + Policy::Threshold(ret_subs.len(), ret_subs) + } else if is_or { + Policy::Threshold(1, ret_subs) + } else { + Policy::Threshold(m, ret_subs) + } + } + x => x, + } + } + + /// Detects a true/trivial policy. + /// + /// Only checks whether the policy is `Policy::Trivial`, to check if the + /// normalized form is trivial, the caller is expected to normalize the + /// policy first. + pub fn is_trivial(&self) -> bool { matches!(*self, Policy::Trivial) } + + /// Detects a false/unsatisfiable policy. + /// + /// Only checks whether the policy is `Policy::Unsatisfiable`, to check if + /// the normalized form is unsatisfiable, the caller is expected to + /// normalize the policy first. + pub fn is_unsatisfiable(&self) -> bool { matches!(*self, Policy::Unsatisfiable) } + + /// Helper function to do the recursion in `timelocks`. + fn real_relative_timelocks(&self) -> Vec { + match *self { + Policy::Unsatisfiable + | Policy::Trivial + | Policy::Key(..) + | Policy::Sha256(..) + | Policy::Hash256(..) + | Policy::Ripemd160(..) + | Policy::Hash160(..) => vec![], + Policy::After(..) => vec![], + Policy::Older(t) => vec![t.to_consensus_u32()], + Policy::Threshold(_, ref subs) => subs.iter().fold(vec![], |mut acc, x| { + acc.extend(x.real_relative_timelocks()); + acc + }), + } + } + + /// Returns a list of all relative timelocks, not including 0, which appear + /// in the policy. + pub fn relative_timelocks(&self) -> Vec { + let mut ret = self.real_relative_timelocks(); + ret.sort_unstable(); + ret.dedup(); + ret + } + + /// Helper function for recursion in `absolute timelocks` + fn real_absolute_timelocks(&self) -> Vec { + match *self { + Policy::Unsatisfiable + | Policy::Trivial + | Policy::Key(..) + | Policy::Sha256(..) + | Policy::Hash256(..) + | Policy::Ripemd160(..) + | Policy::Hash160(..) => vec![], + Policy::Older(..) => vec![], + Policy::After(t) => vec![t.to_u32()], + Policy::Threshold(_, ref subs) => subs.iter().fold(vec![], |mut acc, x| { + acc.extend(x.real_absolute_timelocks()); + acc + }), + } + } + + /// Returns a list of all absolute timelocks, not including 0, which appear + /// in the policy. + pub fn absolute_timelocks(&self) -> Vec { + let mut ret = self.real_absolute_timelocks(); + ret.sort_unstable(); + ret.dedup(); + ret + } + + /// Filters a policy by eliminating relative timelock constraints + /// that are not satisfied at the given `age`. + pub fn at_age(mut self, age: Sequence) -> Policy { + self = match self { + Policy::Older(t) => { + if t.is_height_locked() && age.is_time_locked() + || t.is_time_locked() && age.is_height_locked() + || t.to_consensus_u32() > age.to_consensus_u32() + { + Policy::Unsatisfiable + } else { + Policy::Older(t) + } + } + Policy::Threshold(k, subs) => { + Policy::Threshold(k, subs.into_iter().map(|sub| sub.at_age(age)).collect()) + } + x => x, + }; + self.normalized() + } + + /// Filters a policy by eliminating absolute timelock constraints + /// that are not satisfied at the given `n` (`n OP_CHECKLOCKTIMEVERIFY`). + pub fn at_lock_time(mut self, n: absolute::LockTime) -> Policy { + use absolute::LockTime::*; + + self = match self { + Policy::After(t) => { + let t = absolute::LockTime::from(t); + let is_satisfied_by = match (t, n) { + (Blocks(t), Blocks(n)) => t <= n, + (Seconds(t), Seconds(n)) => t <= n, + _ => false, + }; + if !is_satisfied_by { + Policy::Unsatisfiable + } else { + Policy::After(t.into()) + } + } + Policy::Threshold(k, subs) => { + Policy::Threshold(k, subs.into_iter().map(|sub| sub.at_lock_time(n)).collect()) + } + x => x, + }; + self.normalized() + } + + /// Counts the number of public keys and keyhashes referenced in a policy. + /// Duplicate keys will be double-counted. + pub fn n_keys(&self) -> usize { + match *self { + Policy::Unsatisfiable | Policy::Trivial => 0, + Policy::Key(..) => 1, + Policy::After(..) + | Policy::Older(..) + | Policy::Sha256(..) + | Policy::Hash256(..) + | Policy::Ripemd160(..) + | Policy::Hash160(..) => 0, + Policy::Threshold(_, ref subs) => subs.iter().map(|sub| sub.n_keys()).sum::(), + } + } + + /// Counts the minimum number of public keys for which signatures could be + /// used to satisfy the policy. + /// + /// # Returns + /// + /// Returns `None` if the policy is not satisfiable. + pub fn minimum_n_keys(&self) -> Option { + match *self { + Policy::Unsatisfiable => None, + Policy::Trivial => Some(0), + Policy::Key(..) => Some(1), + Policy::After(..) + | Policy::Older(..) + | Policy::Sha256(..) + | Policy::Hash256(..) + | Policy::Ripemd160(..) + | Policy::Hash160(..) => Some(0), + Policy::Threshold(k, ref subs) => { + let mut sublens: Vec = + subs.iter().filter_map(Policy::minimum_n_keys).collect(); + if sublens.len() < k { + // Not enough branches are satisfiable + None + } else { + sublens.sort_unstable(); + Some(sublens[0..k].iter().cloned().sum::()) + } + } + } + } +} + +impl Policy { + /// "Sorts" a policy to bring it into a canonical form to allow comparisons. + /// + /// Does **not** allow policies to be compared for functional equivalence; + /// in general this appears to require Gröbner basis techniques that are not + /// implemented. + pub fn sorted(self) -> Policy { + match self { + Policy::Threshold(k, subs) => { + let mut new_subs: Vec<_> = subs.into_iter().map(Policy::sorted).collect(); + new_subs.sort(); + Policy::Threshold(k, new_subs) + } + x => x, + } + } +} + +#[cfg(test)] +mod tests { + use core::str::FromStr; + + use bitcoin::PublicKey; + + use super::*; + + type StringPolicy = Policy; + + #[test] + fn parse_policy_err() { + assert!(StringPolicy::from_str("(").is_err()); + assert!(StringPolicy::from_str("(x()").is_err()); + assert!(StringPolicy::from_str("(\u{7f}()3").is_err()); + assert!(StringPolicy::from_str("pk()").is_ok()); + + assert!(StringPolicy::from_str("or(or)").is_err()); + + assert!(Policy::::from_str("pk()").is_err()); + assert!(Policy::::from_str( + "pk(\ + 0200000000000000000000000000000000000002\ + )" + ) + .is_err()); + assert!(Policy::::from_str( + "pk(\ + 02c79ef3ede6d14f72a00d0e49b4becfb152197b64c0707425c4f231df29500ee7\ + )" + ) + .is_ok()); + } + + #[test] + fn semantic_analysis() { + let policy = StringPolicy::from_str("pk()").unwrap(); + assert_eq!(policy, Policy::Key("".to_owned())); + assert_eq!(policy.relative_timelocks(), vec![]); + assert_eq!(policy.absolute_timelocks(), vec![]); + assert_eq!(policy.clone().at_age(Sequence::ZERO), policy); + assert_eq!(policy.clone().at_age(Sequence::from_height(10000)), policy); + assert_eq!(policy.n_keys(), 1); + assert_eq!(policy.minimum_n_keys(), Some(1)); + + let policy = StringPolicy::from_str("older(1000)").unwrap(); + assert_eq!(policy, Policy::Older(Sequence::from_height(1000))); + assert_eq!(policy.absolute_timelocks(), vec![]); + assert_eq!(policy.relative_timelocks(), vec![1000]); + assert_eq!(policy.clone().at_age(Sequence::ZERO), Policy::Unsatisfiable); + assert_eq!(policy.clone().at_age(Sequence::from_height(999)), Policy::Unsatisfiable); + assert_eq!(policy.clone().at_age(Sequence::from_height(1000)), policy); + assert_eq!(policy.clone().at_age(Sequence::from_height(10000)), policy); + assert_eq!(policy.n_keys(), 0); + assert_eq!(policy.minimum_n_keys(), Some(0)); + + let policy = StringPolicy::from_str("or(pk(),older(1000))").unwrap(); + assert_eq!( + policy, + Policy::Threshold( + 1, + vec![ + Policy::Key("".to_owned()), + Policy::Older(Sequence::from_height(1000)), + ] + ) + ); + assert_eq!(policy.relative_timelocks(), vec![1000]); + assert_eq!(policy.absolute_timelocks(), vec![]); + assert_eq!(policy.clone().at_age(Sequence::ZERO), Policy::Key("".to_owned())); + assert_eq!(policy.clone().at_age(Sequence::from_height(999)), Policy::Key("".to_owned())); + assert_eq!(policy.clone().at_age(Sequence::from_height(1000)), policy.clone().normalized()); + assert_eq!( + policy.clone().at_age(Sequence::from_height(10000)), + policy.clone().normalized() + ); + assert_eq!(policy.n_keys(), 1); + assert_eq!(policy.minimum_n_keys(), Some(0)); + + let policy = StringPolicy::from_str("or(pk(),UNSATISFIABLE)").unwrap(); + assert_eq!( + policy, + Policy::Threshold(1, vec![Policy::Key("".to_owned()), Policy::Unsatisfiable,]) + ); + assert_eq!(policy.relative_timelocks(), vec![]); + assert_eq!(policy.absolute_timelocks(), vec![]); + assert_eq!(policy.n_keys(), 1); + assert_eq!(policy.minimum_n_keys(), Some(1)); + + let policy = StringPolicy::from_str("and(pk(),UNSATISFIABLE)").unwrap(); + assert_eq!( + policy, + Policy::Threshold(2, vec![Policy::Key("".to_owned()), Policy::Unsatisfiable,]) + ); + assert_eq!(policy.relative_timelocks(), vec![]); + assert_eq!(policy.absolute_timelocks(), vec![]); + assert_eq!(policy.n_keys(), 1); + assert_eq!(policy.minimum_n_keys(), None); + + let policy = StringPolicy::from_str( + "thresh(\ + 2,older(1000),older(10000),older(1000),older(2000),older(2000)\ + )", + ) + .unwrap(); + assert_eq!( + policy, + Policy::Threshold( + 2, + vec![ + Policy::Older(Sequence::from_height(1000)), + Policy::Older(Sequence::from_height(10000)), + Policy::Older(Sequence::from_height(1000)), + Policy::Older(Sequence::from_height(2000)), + Policy::Older(Sequence::from_height(2000)), + ] + ) + ); + assert_eq!( + policy.relative_timelocks(), + vec![1000, 2000, 10000] //sorted and dedup'd + ); + + let policy = StringPolicy::from_str( + "thresh(\ + 2,older(1000),older(10000),older(1000),UNSATISFIABLE,UNSATISFIABLE\ + )", + ) + .unwrap(); + assert_eq!( + policy, + Policy::Threshold( + 2, + vec![ + Policy::Older(Sequence::from_height(1000)), + Policy::Older(Sequence::from_height(10000)), + Policy::Older(Sequence::from_height(1000)), + Policy::Unsatisfiable, + Policy::Unsatisfiable, + ] + ) + ); + assert_eq!( + policy.relative_timelocks(), + vec![1000, 10000] //sorted and dedup'd + ); + assert_eq!(policy.n_keys(), 0); + assert_eq!(policy.minimum_n_keys(), Some(0)); + + // Block height 1000. + let policy = StringPolicy::from_str("after(1000)").unwrap(); + assert_eq!(policy, Policy::after(1000)); + assert_eq!(policy.absolute_timelocks(), vec![1000]); + assert_eq!(policy.relative_timelocks(), vec![]); + assert_eq!(policy.clone().at_lock_time(absolute::LockTime::ZERO), Policy::Unsatisfiable); + assert_eq!( + policy + .clone() + .at_lock_time(absolute::LockTime::from_height(999).expect("valid block height")), + Policy::Unsatisfiable + ); + assert_eq!( + policy + .clone() + .at_lock_time(absolute::LockTime::from_height(1000).expect("valid block height")), + policy + ); + assert_eq!( + policy + .clone() + .at_lock_time(absolute::LockTime::from_height(10000).expect("valid block height")), + policy + ); + // Pass a UNIX timestamp to at_lock_time while policy uses a block height. + assert_eq!( + policy + .clone() + .at_lock_time(absolute::LockTime::from_time(500_000_001).expect("valid timestamp")), + Policy::Unsatisfiable + ); + assert_eq!(policy.n_keys(), 0); + assert_eq!(policy.minimum_n_keys(), Some(0)); + + // UNIX timestamp of 10 seconds after the epoch. + let policy = StringPolicy::from_str("after(500000010)").unwrap(); + assert_eq!(policy, Policy::after(500_000_010)); + assert_eq!(policy.absolute_timelocks(), vec![500_000_010]); + assert_eq!(policy.relative_timelocks(), vec![]); + // Pass a block height to at_lock_time while policy uses a UNIX timestapm. + assert_eq!(policy.clone().at_lock_time(absolute::LockTime::ZERO), Policy::Unsatisfiable); + assert_eq!( + policy + .clone() + .at_lock_time(absolute::LockTime::from_height(999).expect("valid block height")), + Policy::Unsatisfiable + ); + assert_eq!( + policy + .clone() + .at_lock_time(absolute::LockTime::from_height(1000).expect("valid block height")), + Policy::Unsatisfiable + ); + assert_eq!( + policy + .clone() + .at_lock_time(absolute::LockTime::from_height(10000).expect("valid block height")), + Policy::Unsatisfiable + ); + // And now pass a UNIX timestamp to at_lock_time while policy also uses a timestamp. + assert_eq!( + policy + .clone() + .at_lock_time(absolute::LockTime::from_time(500_000_000).expect("valid timestamp")), + Policy::Unsatisfiable + ); + assert_eq!( + policy + .clone() + .at_lock_time(absolute::LockTime::from_time(500_000_001).expect("valid timestamp")), + Policy::Unsatisfiable + ); + assert_eq!( + policy + .clone() + .at_lock_time(absolute::LockTime::from_time(500_000_010).expect("valid timestamp")), + policy + ); + assert_eq!( + policy + .clone() + .at_lock_time(absolute::LockTime::from_time(500_000_012).expect("valid timestamp")), + policy + ); + assert_eq!(policy.n_keys(), 0); + assert_eq!(policy.minimum_n_keys(), Some(0)); + } + + #[test] + fn entailment_liquid_test() { + //liquid policy + let liquid_pol = StringPolicy::from_str( + "or(and(older(4096),thresh(2,pk(A),pk(B),pk(C))),thresh(11,pk(F1),pk(F2),pk(F3),pk(F4),pk(F5),pk(F6),pk(F7),pk(F8),pk(F9),pk(F10),pk(F11),pk(F12),pk(F13),pk(F14)))").unwrap(); + // Very bad idea to add master key,pk but let's have it have 50M blocks + let master_key = StringPolicy::from_str("and(older(50000000),pk(master))").unwrap(); + let new_liquid_pol = Policy::Threshold(1, vec![liquid_pol.clone(), master_key]); + + assert!(liquid_pol.clone().entails(new_liquid_pol.clone()).unwrap()); + assert!(!new_liquid_pol.entails(liquid_pol.clone()).unwrap()); + + // test liquid backup policy before the emergency timeout + let backup_policy = StringPolicy::from_str("thresh(2,pk(A),pk(B),pk(C))").unwrap(); + assert!(!backup_policy + .entails(liquid_pol.clone().at_age(Sequence::from_height(4095))) + .unwrap()); + + // Finally test both spending paths + let fed_pol = StringPolicy::from_str("thresh(11,pk(F1),pk(F2),pk(F3),pk(F4),pk(F5),pk(F6),pk(F7),pk(F8),pk(F9),pk(F10),pk(F11),pk(F12),pk(F13),pk(F14))").unwrap(); + let backup_policy_after_expiry = + StringPolicy::from_str("and(older(4096),thresh(2,pk(A),pk(B),pk(C)))").unwrap(); + assert!(fed_pol.entails(liquid_pol.clone()).unwrap()); + assert!(backup_policy_after_expiry.entails(liquid_pol).unwrap()); + } + + #[test] + fn entailment_escrow() { + // Escrow contract + let escrow_pol = StringPolicy::from_str("thresh(2,pk(Alice),pk(Bob),pk(Judge))").unwrap(); + // Alice's authorization constraint + // Authorization is a constraint that states the conditions under which one party must + // be able to redeem the funds. + let auth_alice = StringPolicy::from_str("and(pk(Alice),pk(Judge))").unwrap(); + + //Alice's Control constraint + // The control constraint states the conditions that one party requires + // must be met if the funds are spent by anyone + // Either Alice must authorize the funds or both Judge and Bob must control it + let control_alice = StringPolicy::from_str("or(pk(Alice),and(pk(Judge),pk(Bob)))").unwrap(); + + // Entailment rules + // Authorization entails |- policy |- control constraints + assert!(auth_alice.entails(escrow_pol.clone()).unwrap()); + assert!(escrow_pol.entails(control_alice).unwrap()); + + // Entailment HTLC's + // Escrow contract + let h = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + let htlc_pol = StringPolicy::from_str(&format!( + "or(and(pk(Alice),older(100)),and(pk(Bob),sha256({})))", + h + )) + .unwrap(); + // Alice's authorization constraint + // Authorization is a constraint that states the conditions under which one party must + // be able to redeem the funds. In HLTC, alice only cares that she can + // authorize her funds with Pk and CSV 100. + let auth_alice = StringPolicy::from_str("and(pk(Alice),older(100))").unwrap(); + + //Alice's Control constraint + // The control constraint states the conditions that one party requires + // must be met if the funds are spent by anyone + // Either Alice must authorize the funds or sha2 preimage must be revealed. + let control_alice = + StringPolicy::from_str(&format!("or(pk(Alice),sha256({}))", h)).unwrap(); + + // Entailment rules + // Authorization entails |- policy |- control constraints + assert!(auth_alice.entails(htlc_pol.clone()).unwrap()); + assert!(htlc_pol.entails(control_alice).unwrap()); + } + + #[test] + fn for_each_key() { + let liquid_pol = StringPolicy::from_str( + "or(and(older(4096),thresh(2,pk(A),pk(B),pk(C))),thresh(11,pk(F1),pk(F2),pk(F3),pk(F4),pk(F5),pk(F6),pk(F7),pk(F8),pk(F9),pk(F10),pk(F11),pk(F12),pk(F13),pk(F14)))").unwrap(); + let mut count = 0; + assert!(liquid_pol.for_each_key(|_| { + count += 1; + true + })); + assert_eq!(count, 17); + } +} diff --git a/src/descriptor/bare.rs b/src/descriptor/bare.rs index 8afeb1a86..fab792e49 100644 --- a/src/descriptor/bare.rs +++ b/src/descriptor/bare.rs @@ -18,9 +18,8 @@ use crate::expression::{self, FromTree}; use crate::miniscript::context::{ScriptContext, ScriptContextError}; use crate::miniscript::satisfy::{Placeholder, Satisfaction, Witness}; use crate::plan::AssetProvider; -use crate::policy::r#abstract; use crate::prelude::*; -use crate::r#abstract::Liftable; +use crate::r#abstract::{self, Liftable}; use crate::util::{varint_len, witness_to_scriptsig}; use crate::{ BareCtx, Error, ForEachKey, Miniscript, MiniscriptKey, Satisfier, ToPublicKey, TranslateErr, diff --git a/src/descriptor/segwitv0.rs b/src/descriptor/segwitv0.rs index c93561199..128455ac6 100644 --- a/src/descriptor/segwitv0.rs +++ b/src/descriptor/segwitv0.rs @@ -16,9 +16,8 @@ use crate::expression::{self, FromTree}; use crate::miniscript::context::{ScriptContext, ScriptContextError}; use crate::miniscript::satisfy::{Placeholder, Satisfaction, Witness}; use crate::plan::AssetProvider; -use crate::policy::r#abstract; +use crate::r#abstract::{self, Liftable}; use crate::prelude::*; -use crate::r#abstract::Liftable; use crate::util::varint_len; use crate::{ Error, ForEachKey, Miniscript, MiniscriptKey, Satisfier, Segwitv0, ToPublicKey, TranslateErr, diff --git a/src/descriptor/sh.rs b/src/descriptor/sh.rs index c2ffb647e..57829dd0c 100644 --- a/src/descriptor/sh.rs +++ b/src/descriptor/sh.rs @@ -20,9 +20,8 @@ use crate::expression::{self, FromTree}; use crate::miniscript::context::ScriptContext; use crate::miniscript::satisfy::{Placeholder, Satisfaction}; use crate::plan::AssetProvider; -use crate::policy::r#abstract; +use crate::r#abstract::{self, Liftable}; use crate::prelude::*; -use crate::r#abstract::Liftable; use crate::util::{varint_len, witness_to_scriptsig}; use crate::{ push_opcode_size, Error, ForEachKey, Legacy, Miniscript, MiniscriptKey, Satisfier, Segwitv0, diff --git a/src/descriptor/sortedmulti.rs b/src/descriptor/sortedmulti.rs index 6e8854a8a..27ffa4853 100644 --- a/src/descriptor/sortedmulti.rs +++ b/src/descriptor/sortedmulti.rs @@ -16,7 +16,7 @@ use crate::miniscript::decode::Terminal; use crate::miniscript::limits::MAX_PUBKEYS_PER_MULTISIG; use crate::miniscript::satisfy::{Placeholder, Satisfaction}; use crate::plan::AssetProvider; -use crate::policy::r#abstract; +use crate::r#abstract; use crate::prelude::*; use crate::{ errstr, expression, script_num_size, Error, ForEachKey, Miniscript, MiniscriptKey, Satisfier, diff --git a/src/descriptor/tr.rs b/src/descriptor/tr.rs index 6722332ab..ecc2ec2f1 100644 --- a/src/descriptor/tr.rs +++ b/src/descriptor/tr.rs @@ -16,7 +16,7 @@ use crate::expression::{self, FromTree}; use crate::miniscript::satisfy::{Placeholder, Satisfaction, SchnorrSigType, Witness}; use crate::miniscript::Miniscript; use crate::plan::AssetProvider; -use crate::policy::r#abstract::Policy as Abstract; +use crate::r#abstract::Policy as Abstract; use crate::prelude::*; use crate::r#abstract::Liftable; use crate::util::{varint_len, witness_size}; diff --git a/src/policy/concrete.rs b/src/policy/concrete.rs index a67d4eba0..acf24e391 100644 --- a/src/policy/concrete.rs +++ b/src/policy/concrete.rs @@ -12,7 +12,7 @@ use bitcoin::{absolute, Sequence}; use { crate::descriptor::TapTree, crate::miniscript::ScriptContext, crate::policy::compiler, crate::policy::compiler::CompilerError, crate::policy::compiler::OrdF64, - crate::policy::concrete::Policy as Concrete, crate::policy::r#abstract::Policy as Abstract, + crate::policy::concrete::Policy as Concrete, crate::r#abstract::Policy as Abstract, crate::r#abstract::Liftable, crate::Descriptor, crate::Miniscript, crate::Tap, core::cmp::Reverse, }; @@ -553,7 +553,7 @@ impl ForEachKey for Policy { impl Policy { /// Converts a policy using one kind of public key to another type of public key. /// - /// For example usage please see [`crate::policy::abstract::Policy::translate_pk`]. + /// For example usage please see [`crate::abstract::Policy::translate_pk`]. pub fn translate_pk(&self, t: &mut T) -> Result, E> where T: Translator, diff --git a/src/policy/mod.rs b/src/policy/mod.rs index 0ff94fdbf..4583addb6 100644 --- a/src/policy/mod.rs +++ b/src/policy/mod.rs @@ -10,7 +10,6 @@ //! these with BIP32 paths, pay-to-contract instructions, etc. //! -pub mod r#abstract; #[cfg(feature = "compiler")] pub mod compiler; pub mod concrete; From 70b2e5fe8bb56d726b0173a177a236b7e2009476 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Thu, 28 Sep 2023 08:26:31 +1000 Subject: [PATCH 7/8] Remove unused abstract.rs file We do not build this file in the build, remove it. --- src/policy/abstract.rs | 960 ----------------------------------------- 1 file changed, 960 deletions(-) delete mode 100644 src/policy/abstract.rs diff --git a/src/policy/abstract.rs b/src/policy/abstract.rs deleted file mode 100644 index 435eb2ca6..000000000 --- a/src/policy/abstract.rs +++ /dev/null @@ -1,960 +0,0 @@ -// SPDX-License-Identifier: CC0-1.0 - -//! Abstract Policies - -use core::str::FromStr; -use core::{fmt, str}; - -use bitcoin::{absolute, Sequence}; - -use crate::policy::concrete::PolicyError; -use crate::policy::ENTAILMENT_MAX_TERMINALS; -use crate::prelude::*; -use crate::{errstr, expression, AbsLockTime, Error, ForEachKey, MiniscriptKey, Translator}; - -/// Abstract policy which corresponds to the semantics of a miniscript and -/// which allows complex forms of analysis, e.g. filtering and normalization. -/// -/// Abstract policies store only hashes of keys to ensure that objects -/// representing the same policy are lifted to the same abstract `Policy`, -/// regardless of their choice of `pk` or `pk_h` nodes. -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] -pub enum Policy { - /// Unsatisfiable. - Unsatisfiable, - /// Trivially satisfiable. - Trivial, - /// Signature and public key matching a given hash is required. - Key(Pk), - /// An absolute locktime restriction. - After(AbsLockTime), - /// A relative locktime restriction. - Older(Sequence), - /// A SHA256 whose preimage must be provided to satisfy the descriptor. - Sha256(Pk::Sha256), - /// A SHA256d whose preimage must be provided to satisfy the descriptor. - Hash256(Pk::Hash256), - /// A RIPEMD160 whose preimage must be provided to satisfy the descriptor. - Ripemd160(Pk::Ripemd160), - /// A HASH160 whose preimage must be provided to satisfy the descriptor. - Hash160(Pk::Hash160), - /// A set of descriptors, satisfactions must be provided for `k` of them. - Threshold(usize, Vec>), -} - -impl Policy -where - Pk: MiniscriptKey, -{ - /// Constructs a `Policy::After` from `n`. - /// - /// Helper function equivalent to `Policy::After(absolute::LockTime::from_consensus(n))`. - pub fn after(n: u32) -> Policy { - Policy::After(AbsLockTime::from(absolute::LockTime::from_consensus(n))) - } - - /// Construct a `Policy::Older` from `n`. - /// - /// Helper function equivalent to `Policy::Older(Sequence::from_consensus(n))`. - pub fn older(n: u32) -> Policy { Policy::Older(Sequence::from_consensus(n)) } -} - -impl ForEachKey for Policy { - fn for_each_key<'a, F: FnMut(&'a Pk) -> bool>(&'a self, mut pred: F) -> bool { - self.real_for_each_key(&mut pred) - } -} - -impl Policy { - fn real_for_each_key<'a, F: FnMut(&'a Pk) -> bool>(&'a self, pred: &mut F) -> bool { - match *self { - Policy::Unsatisfiable | Policy::Trivial => true, - Policy::Key(ref pk) => pred(pk), - Policy::Sha256(..) - | Policy::Hash256(..) - | Policy::Ripemd160(..) - | Policy::Hash160(..) - | Policy::After(..) - | Policy::Older(..) => true, - Policy::Threshold(_, ref subs) => { - subs.iter().all(|sub| sub.real_for_each_key(&mut *pred)) - } - } - } - - /// Converts a policy using one kind of public key to another type of public key. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::str::FromStr; - /// use miniscript::bitcoin::{hashes::hash160, PublicKey}; - /// use miniscript::{translate_hash_fail, policy::r#abstract::Policy, Translator}; - /// let alice_pk = "02c79ef3ede6d14f72a00d0e49b4becfb152197b64c0707425c4f231df29500ee7"; - /// let bob_pk = "03d008a849fbf474bd17e9d2c1a827077a468150e58221582ec3410ab309f5afe4"; - /// let placeholder_policy = Policy::::from_str("and(pk(alice_pk),pk(bob_pk))").unwrap(); - /// - /// // Information to translate abstract string type keys to concrete `bitcoin::PublicKey`s. - /// // In practice, wallets would map from string key names to BIP32 keys. - /// struct StrPkTranslator { - /// pk_map: HashMap - /// } - /// - /// // If we also wanted to provide mapping of other associated types (sha256, older etc), - /// // we would use the general [`Translator`] trait. - /// impl Translator for StrPkTranslator { - /// fn pk(&mut self, pk: &String) -> Result { - /// self.pk_map.get(pk).copied().ok_or(()) // Dummy Err - /// } - /// - /// // Handy macro for failing if we encounter any other fragment. - /// // See also [`translate_hash_clone!`] for cloning instead of failing. - /// translate_hash_fail!(String, bitcoin::PublicKey, ()); - /// } - /// - /// let mut pk_map = HashMap::new(); - /// pk_map.insert(String::from("alice_pk"), bitcoin::PublicKey::from_str(alice_pk).unwrap()); - /// pk_map.insert(String::from("bob_pk"), bitcoin::PublicKey::from_str(bob_pk).unwrap()); - /// let mut t = StrPkTranslator { pk_map }; - /// - /// let real_policy = placeholder_policy.translate_pk(&mut t).unwrap(); - /// - /// let expected_policy = Policy::from_str(&format!("and(pk({}),pk({}))", alice_pk, bob_pk)).unwrap(); - /// assert_eq!(real_policy, expected_policy); - /// ``` - pub fn translate_pk(&self, t: &mut T) -> Result, E> - where - T: Translator, - Q: MiniscriptKey, - { - self._translate_pk(t) - } - - fn _translate_pk(&self, t: &mut T) -> Result, E> - where - T: Translator, - Q: MiniscriptKey, - { - match *self { - Policy::Unsatisfiable => Ok(Policy::Unsatisfiable), - Policy::Trivial => Ok(Policy::Trivial), - Policy::Key(ref pk) => t.pk(pk).map(Policy::Key), - Policy::Sha256(ref h) => t.sha256(h).map(Policy::Sha256), - Policy::Hash256(ref h) => t.hash256(h).map(Policy::Hash256), - Policy::Ripemd160(ref h) => t.ripemd160(h).map(Policy::Ripemd160), - Policy::Hash160(ref h) => t.hash160(h).map(Policy::Hash160), - Policy::After(n) => Ok(Policy::After(n)), - Policy::Older(n) => Ok(Policy::Older(n)), - Policy::Threshold(k, ref subs) => { - let new_subs: Result>, _> = - subs.iter().map(|sub| sub._translate_pk(t)).collect(); - new_subs.map(|ok| Policy::Threshold(k, ok)) - } - } - } - - /// Computes whether the current policy entails the second one. - /// - /// A |- B means every satisfaction of A is also a satisfaction of B. - /// - /// This implementation will run slowly for larger policies but should be - /// sufficient for most practical policies. - // This algorithm has a naive implementation. It is possible to optimize this - // by memoizing and maintaining a hashmap. - pub fn entails(self, other: Policy) -> Result { - if self.n_terminals() > ENTAILMENT_MAX_TERMINALS { - return Err(PolicyError::EntailmentMaxTerminals); - } - match (self, other) { - (Policy::Unsatisfiable, _) => Ok(true), - (Policy::Trivial, Policy::Trivial) => Ok(true), - (Policy::Trivial, _) => Ok(false), - (_, Policy::Unsatisfiable) => Ok(false), - (a, b) => { - let (a_norm, b_norm) = (a.normalized(), b.normalized()); - let first_constraint = a_norm.first_constraint(); - let (a1, b1) = ( - a_norm.clone().satisfy_constraint(&first_constraint, true), - b_norm.clone().satisfy_constraint(&first_constraint, true), - ); - let (a2, b2) = ( - a_norm.satisfy_constraint(&first_constraint, false), - b_norm.satisfy_constraint(&first_constraint, false), - ); - Ok(Policy::entails(a1, b1)? && Policy::entails(a2, b2)?) - } - } - } - - // Helper function to compute the number of constraints in policy. - fn n_terminals(&self) -> usize { - match self { - &Policy::Threshold(_k, ref subs) => subs.iter().map(|sub| sub.n_terminals()).sum(), - &Policy::Trivial | &Policy::Unsatisfiable => 0, - _leaf => 1, - } - } - - // Helper function to get the first constraint in the policy. - // Returns the first leaf policy. Used in policy entailment. - // Assumes that the current policy is normalized. - fn first_constraint(&self) -> Policy { - debug_assert!(self.clone().normalized() == self.clone()); - match self { - &Policy::Threshold(_k, ref subs) => subs[0].first_constraint(), - first => first.clone(), - } - } - - // Helper function that takes in witness and its availability, changing it - // to true or false and returning the resultant normalized policy. Witness - // is currently encoded as policy. Only accepts leaf fragment and a - // normalized policy - pub(crate) fn satisfy_constraint(self, witness: &Policy, available: bool) -> Policy { - debug_assert!(self.clone().normalized() == self); - if let Policy::Threshold { .. } = *witness { - // We can't debug_assert on Policy::Threshold. - panic!("should be unreachable") - } - - let ret = match self { - Policy::Threshold(k, subs) => { - let mut ret_subs = vec![]; - for sub in subs { - ret_subs.push(sub.satisfy_constraint(witness, available)); - } - Policy::Threshold(k, ret_subs) - } - ref leaf if leaf == witness => { - if available { - Policy::Trivial - } else { - Policy::Unsatisfiable - } - } - x => x, - }; - ret.normalized() - } -} - -impl fmt::Debug for Policy { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Policy::Unsatisfiable => f.write_str("UNSATISFIABLE()"), - Policy::Trivial => f.write_str("TRIVIAL()"), - Policy::Key(ref pkh) => write!(f, "pk({:?})", pkh), - Policy::After(n) => write!(f, "after({})", n), - Policy::Older(n) => write!(f, "older({})", n), - Policy::Sha256(ref h) => write!(f, "sha256({})", h), - Policy::Hash256(ref h) => write!(f, "hash256({})", h), - Policy::Ripemd160(ref h) => write!(f, "ripemd160({})", h), - Policy::Hash160(ref h) => write!(f, "hash160({})", h), - Policy::Threshold(k, ref subs) => { - if k == subs.len() { - write!(f, "and(")?; - } else if k == 1 { - write!(f, "or(")?; - } else { - write!(f, "thresh({},", k)?; - } - for (i, sub) in subs.iter().enumerate() { - if i == 0 { - write!(f, "{}", sub)?; - } else { - write!(f, ",{}", sub)?; - } - } - f.write_str(")") - } - } - } -} - -impl fmt::Display for Policy { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Policy::Unsatisfiable => f.write_str("UNSATISFIABLE"), - Policy::Trivial => f.write_str("TRIVIAL"), - Policy::Key(ref pkh) => write!(f, "pk({})", pkh), - Policy::After(n) => write!(f, "after({})", n), - Policy::Older(n) => write!(f, "older({})", n), - Policy::Sha256(ref h) => write!(f, "sha256({})", h), - Policy::Hash256(ref h) => write!(f, "hash256({})", h), - Policy::Ripemd160(ref h) => write!(f, "ripemd160({})", h), - Policy::Hash160(ref h) => write!(f, "hash160({})", h), - Policy::Threshold(k, ref subs) => { - if k == subs.len() { - write!(f, "and(")?; - } else if k == 1 { - write!(f, "or(")?; - } else { - write!(f, "thresh({},", k)?; - } - for (i, sub) in subs.iter().enumerate() { - if i == 0 { - write!(f, "{}", sub)?; - } else { - write!(f, ",{}", sub)?; - } - } - f.write_str(")") - } - } - } -} - -impl_from_str!( - Policy, - type Err = Error;, - fn from_str(s: &str) -> Result, Error> { - expression::check_valid_chars(s)?; - - let tree = expression::Tree::from_str(s)?; - expression::FromTree::from_tree(&tree) - } -); - -serde_string_impl_pk!(Policy, "a miniscript semantic policy"); - -impl_from_tree!( - Policy, - fn from_tree(top: &expression::Tree) -> Result, Error> { - match (top.name, top.args.len()) { - ("UNSATISFIABLE", 0) => Ok(Policy::Unsatisfiable), - ("TRIVIAL", 0) => Ok(Policy::Trivial), - ("pk", 1) => expression::terminal(&top.args[0], |pk| Pk::from_str(pk).map(Policy::Key)), - ("after", 1) => expression::terminal(&top.args[0], |x| { - expression::parse_num(x).map(|x| Policy::after(x)) - }), - ("older", 1) => expression::terminal(&top.args[0], |x| { - expression::parse_num(x).map(|x| Policy::older(x)) - }), - ("sha256", 1) => { - expression::terminal(&top.args[0], |x| Pk::Sha256::from_str(x).map(Policy::Sha256)) - } - ("hash256", 1) => expression::terminal(&top.args[0], |x| { - Pk::Hash256::from_str(x).map(Policy::Hash256) - }), - ("ripemd160", 1) => expression::terminal(&top.args[0], |x| { - Pk::Ripemd160::from_str(x).map(Policy::Ripemd160) - }), - ("hash160", 1) => expression::terminal(&top.args[0], |x| { - Pk::Hash160::from_str(x).map(Policy::Hash160) - }), - ("and", nsubs) => { - if nsubs < 2 { - return Err(Error::PolicyError(PolicyError::InsufficientArgsforAnd)); - } - let mut subs = Vec::with_capacity(nsubs); - for arg in &top.args { - subs.push(Policy::from_tree(arg)?); - } - Ok(Policy::Threshold(nsubs, subs)) - } - ("or", nsubs) => { - if nsubs < 2 { - return Err(Error::PolicyError(PolicyError::InsufficientArgsforOr)); - } - let mut subs = Vec::with_capacity(nsubs); - for arg in &top.args { - subs.push(Policy::from_tree(arg)?); - } - Ok(Policy::Threshold(1, subs)) - } - ("thresh", nsubs) => { - if nsubs == 0 || nsubs == 1 { - // thresh() and thresh(k) are err - return Err(errstr("thresh without args")); - } - if !top.args[0].args.is_empty() { - return Err(errstr(top.args[0].args[0].name)); - } - - let thresh = expression::parse_num(top.args[0].name)?; - - // thresh(1) and thresh(n) are disallowed in semantic policies - if thresh <= 1 || thresh >= (nsubs as u32 - 1) { - return Err(errstr( - "Abstract Policy thresh cannot have k = 1 or k =n, use `and`/`or` instead", - )); - } - if thresh >= (nsubs as u32) { - return Err(errstr(top.args[0].name)); - } - - let mut subs = Vec::with_capacity(top.args.len() - 1); - for arg in &top.args[1..] { - subs.push(Policy::from_tree(arg)?); - } - Ok(Policy::Threshold(thresh as usize, subs)) - } - _ => Err(errstr(top.name)), - } - } -); - -impl Policy { - /// Flattens out trees of `And`s and `Or`s; eliminate `Trivial` and - /// `Unsatisfiable`s. Does not reorder any branches; use `.sort`. - pub fn normalized(self) -> Policy { - match self { - Policy::Threshold(k, subs) => { - let mut ret_subs = Vec::with_capacity(subs.len()); - - let subs: Vec<_> = subs.into_iter().map(|sub| sub.normalized()).collect(); - let trivial_count = subs.iter().filter(|&pol| *pol == Policy::Trivial).count(); - let unsatisfied_count = subs - .iter() - .filter(|&pol| *pol == Policy::Unsatisfiable) - .count(); - - let n = subs.len() - unsatisfied_count - trivial_count; // remove all true/false - let m = k.checked_sub(trivial_count).map_or(0, |x| x); // satisfy all trivial - // m == n denotes `and` and m == 1 denotes `or` - let is_and = m == n; - let is_or = m == 1; - for sub in subs { - match sub { - Policy::Trivial | Policy::Unsatisfiable => {} - Policy::Threshold(k, subs) => { - match (is_and, is_or) { - (true, true) => { - // means m = n = 1, thresh(1,X) type thing. - ret_subs.push(Policy::Threshold(k, subs)); - } - (true, false) if k == subs.len() => ret_subs.extend(subs), // and case - (false, true) if k == 1 => ret_subs.extend(subs), // or case - _ => ret_subs.push(Policy::Threshold(k, subs)), - } - } - x => ret_subs.push(x), - } - } - // Now reason about m of n threshold - if m == 0 { - Policy::Trivial - } else if m > ret_subs.len() { - Policy::Unsatisfiable - } else if ret_subs.len() == 1 { - ret_subs.pop().unwrap() - } else if is_and { - Policy::Threshold(ret_subs.len(), ret_subs) - } else if is_or { - Policy::Threshold(1, ret_subs) - } else { - Policy::Threshold(m, ret_subs) - } - } - x => x, - } - } - - /// Detects a true/trivial policy. - /// - /// Only checks whether the policy is `Policy::Trivial`, to check if the - /// normalized form is trivial, the caller is expected to normalize the - /// policy first. - pub fn is_trivial(&self) -> bool { matches!(*self, Policy::Trivial) } - - /// Detects a false/unsatisfiable policy. - /// - /// Only checks whether the policy is `Policy::Unsatisfiable`, to check if - /// the normalized form is unsatisfiable, the caller is expected to - /// normalize the policy first. - pub fn is_unsatisfiable(&self) -> bool { matches!(*self, Policy::Unsatisfiable) } - - /// Helper function to do the recursion in `timelocks`. - fn real_relative_timelocks(&self) -> Vec { - match *self { - Policy::Unsatisfiable - | Policy::Trivial - | Policy::Key(..) - | Policy::Sha256(..) - | Policy::Hash256(..) - | Policy::Ripemd160(..) - | Policy::Hash160(..) => vec![], - Policy::After(..) => vec![], - Policy::Older(t) => vec![t.to_consensus_u32()], - Policy::Threshold(_, ref subs) => subs.iter().fold(vec![], |mut acc, x| { - acc.extend(x.real_relative_timelocks()); - acc - }), - } - } - - /// Returns a list of all relative timelocks, not including 0, which appear - /// in the policy. - pub fn relative_timelocks(&self) -> Vec { - let mut ret = self.real_relative_timelocks(); - ret.sort_unstable(); - ret.dedup(); - ret - } - - /// Helper function for recursion in `absolute timelocks` - fn real_absolute_timelocks(&self) -> Vec { - match *self { - Policy::Unsatisfiable - | Policy::Trivial - | Policy::Key(..) - | Policy::Sha256(..) - | Policy::Hash256(..) - | Policy::Ripemd160(..) - | Policy::Hash160(..) => vec![], - Policy::Older(..) => vec![], - Policy::After(t) => vec![t.to_u32()], - Policy::Threshold(_, ref subs) => subs.iter().fold(vec![], |mut acc, x| { - acc.extend(x.real_absolute_timelocks()); - acc - }), - } - } - - /// Returns a list of all absolute timelocks, not including 0, which appear - /// in the policy. - pub fn absolute_timelocks(&self) -> Vec { - let mut ret = self.real_absolute_timelocks(); - ret.sort_unstable(); - ret.dedup(); - ret - } - - /// Filters a policy by eliminating relative timelock constraints - /// that are not satisfied at the given `age`. - pub fn at_age(mut self, age: Sequence) -> Policy { - self = match self { - Policy::Older(t) => { - if t.is_height_locked() && age.is_time_locked() - || t.is_time_locked() && age.is_height_locked() - || t.to_consensus_u32() > age.to_consensus_u32() - { - Policy::Unsatisfiable - } else { - Policy::Older(t) - } - } - Policy::Threshold(k, subs) => { - Policy::Threshold(k, subs.into_iter().map(|sub| sub.at_age(age)).collect()) - } - x => x, - }; - self.normalized() - } - - /// Filters a policy by eliminating absolute timelock constraints - /// that are not satisfied at the given `n` (`n OP_CHECKLOCKTIMEVERIFY`). - pub fn at_lock_time(mut self, n: absolute::LockTime) -> Policy { - use absolute::LockTime::*; - - self = match self { - Policy::After(t) => { - let t = absolute::LockTime::from(t); - let is_satisfied_by = match (t, n) { - (Blocks(t), Blocks(n)) => t <= n, - (Seconds(t), Seconds(n)) => t <= n, - _ => false, - }; - if !is_satisfied_by { - Policy::Unsatisfiable - } else { - Policy::After(t.into()) - } - } - Policy::Threshold(k, subs) => { - Policy::Threshold(k, subs.into_iter().map(|sub| sub.at_lock_time(n)).collect()) - } - x => x, - }; - self.normalized() - } - - /// Counts the number of public keys and keyhashes referenced in a policy. - /// Duplicate keys will be double-counted. - pub fn n_keys(&self) -> usize { - match *self { - Policy::Unsatisfiable | Policy::Trivial => 0, - Policy::Key(..) => 1, - Policy::After(..) - | Policy::Older(..) - | Policy::Sha256(..) - | Policy::Hash256(..) - | Policy::Ripemd160(..) - | Policy::Hash160(..) => 0, - Policy::Threshold(_, ref subs) => subs.iter().map(|sub| sub.n_keys()).sum::(), - } - } - - /// Counts the minimum number of public keys for which signatures could be - /// used to satisfy the policy. - /// - /// # Returns - /// - /// Returns `None` if the policy is not satisfiable. - pub fn minimum_n_keys(&self) -> Option { - match *self { - Policy::Unsatisfiable => None, - Policy::Trivial => Some(0), - Policy::Key(..) => Some(1), - Policy::After(..) - | Policy::Older(..) - | Policy::Sha256(..) - | Policy::Hash256(..) - | Policy::Ripemd160(..) - | Policy::Hash160(..) => Some(0), - Policy::Threshold(k, ref subs) => { - let mut sublens: Vec = - subs.iter().filter_map(Policy::minimum_n_keys).collect(); - if sublens.len() < k { - // Not enough branches are satisfiable - None - } else { - sublens.sort_unstable(); - Some(sublens[0..k].iter().cloned().sum::()) - } - } - } - } -} - -impl Policy { - /// "Sorts" a policy to bring it into a canonical form to allow comparisons. - /// - /// Does **not** allow policies to be compared for functional equivalence; - /// in general this appears to require Gröbner basis techniques that are not - /// implemented. - pub fn sorted(self) -> Policy { - match self { - Policy::Threshold(k, subs) => { - let mut new_subs: Vec<_> = subs.into_iter().map(Policy::sorted).collect(); - new_subs.sort(); - Policy::Threshold(k, new_subs) - } - x => x, - } - } -} - -#[cfg(test)] -mod tests { - use core::str::FromStr; - - use bitcoin::PublicKey; - - use super::*; - - type StringPolicy = Policy; - - #[test] - fn parse_policy_err() { - assert!(StringPolicy::from_str("(").is_err()); - assert!(StringPolicy::from_str("(x()").is_err()); - assert!(StringPolicy::from_str("(\u{7f}()3").is_err()); - assert!(StringPolicy::from_str("pk()").is_ok()); - - assert!(StringPolicy::from_str("or(or)").is_err()); - - assert!(Policy::::from_str("pk()").is_err()); - assert!(Policy::::from_str( - "pk(\ - 0200000000000000000000000000000000000002\ - )" - ) - .is_err()); - assert!(Policy::::from_str( - "pk(\ - 02c79ef3ede6d14f72a00d0e49b4becfb152197b64c0707425c4f231df29500ee7\ - )" - ) - .is_ok()); - } - - #[test] - fn semantic_analysis() { - let policy = StringPolicy::from_str("pk()").unwrap(); - assert_eq!(policy, Policy::Key("".to_owned())); - assert_eq!(policy.relative_timelocks(), vec![]); - assert_eq!(policy.absolute_timelocks(), vec![]); - assert_eq!(policy.clone().at_age(Sequence::ZERO), policy); - assert_eq!(policy.clone().at_age(Sequence::from_height(10000)), policy); - assert_eq!(policy.n_keys(), 1); - assert_eq!(policy.minimum_n_keys(), Some(1)); - - let policy = StringPolicy::from_str("older(1000)").unwrap(); - assert_eq!(policy, Policy::Older(Sequence::from_height(1000))); - assert_eq!(policy.absolute_timelocks(), vec![]); - assert_eq!(policy.relative_timelocks(), vec![1000]); - assert_eq!(policy.clone().at_age(Sequence::ZERO), Policy::Unsatisfiable); - assert_eq!(policy.clone().at_age(Sequence::from_height(999)), Policy::Unsatisfiable); - assert_eq!(policy.clone().at_age(Sequence::from_height(1000)), policy); - assert_eq!(policy.clone().at_age(Sequence::from_height(10000)), policy); - assert_eq!(policy.n_keys(), 0); - assert_eq!(policy.minimum_n_keys(), Some(0)); - - let policy = StringPolicy::from_str("or(pk(),older(1000))").unwrap(); - assert_eq!( - policy, - Policy::Threshold( - 1, - vec![ - Policy::Key("".to_owned()), - Policy::Older(Sequence::from_height(1000)), - ] - ) - ); - assert_eq!(policy.relative_timelocks(), vec![1000]); - assert_eq!(policy.absolute_timelocks(), vec![]); - assert_eq!(policy.clone().at_age(Sequence::ZERO), Policy::Key("".to_owned())); - assert_eq!(policy.clone().at_age(Sequence::from_height(999)), Policy::Key("".to_owned())); - assert_eq!(policy.clone().at_age(Sequence::from_height(1000)), policy.clone().normalized()); - assert_eq!( - policy.clone().at_age(Sequence::from_height(10000)), - policy.clone().normalized() - ); - assert_eq!(policy.n_keys(), 1); - assert_eq!(policy.minimum_n_keys(), Some(0)); - - let policy = StringPolicy::from_str("or(pk(),UNSATISFIABLE)").unwrap(); - assert_eq!( - policy, - Policy::Threshold(1, vec![Policy::Key("".to_owned()), Policy::Unsatisfiable,]) - ); - assert_eq!(policy.relative_timelocks(), vec![]); - assert_eq!(policy.absolute_timelocks(), vec![]); - assert_eq!(policy.n_keys(), 1); - assert_eq!(policy.minimum_n_keys(), Some(1)); - - let policy = StringPolicy::from_str("and(pk(),UNSATISFIABLE)").unwrap(); - assert_eq!( - policy, - Policy::Threshold(2, vec![Policy::Key("".to_owned()), Policy::Unsatisfiable,]) - ); - assert_eq!(policy.relative_timelocks(), vec![]); - assert_eq!(policy.absolute_timelocks(), vec![]); - assert_eq!(policy.n_keys(), 1); - assert_eq!(policy.minimum_n_keys(), None); - - let policy = StringPolicy::from_str( - "thresh(\ - 2,older(1000),older(10000),older(1000),older(2000),older(2000)\ - )", - ) - .unwrap(); - assert_eq!( - policy, - Policy::Threshold( - 2, - vec![ - Policy::Older(Sequence::from_height(1000)), - Policy::Older(Sequence::from_height(10000)), - Policy::Older(Sequence::from_height(1000)), - Policy::Older(Sequence::from_height(2000)), - Policy::Older(Sequence::from_height(2000)), - ] - ) - ); - assert_eq!( - policy.relative_timelocks(), - vec![1000, 2000, 10000] //sorted and dedup'd - ); - - let policy = StringPolicy::from_str( - "thresh(\ - 2,older(1000),older(10000),older(1000),UNSATISFIABLE,UNSATISFIABLE\ - )", - ) - .unwrap(); - assert_eq!( - policy, - Policy::Threshold( - 2, - vec![ - Policy::Older(Sequence::from_height(1000)), - Policy::Older(Sequence::from_height(10000)), - Policy::Older(Sequence::from_height(1000)), - Policy::Unsatisfiable, - Policy::Unsatisfiable, - ] - ) - ); - assert_eq!( - policy.relative_timelocks(), - vec![1000, 10000] //sorted and dedup'd - ); - assert_eq!(policy.n_keys(), 0); - assert_eq!(policy.minimum_n_keys(), Some(0)); - - // Block height 1000. - let policy = StringPolicy::from_str("after(1000)").unwrap(); - assert_eq!(policy, Policy::after(1000)); - assert_eq!(policy.absolute_timelocks(), vec![1000]); - assert_eq!(policy.relative_timelocks(), vec![]); - assert_eq!(policy.clone().at_lock_time(absolute::LockTime::ZERO), Policy::Unsatisfiable); - assert_eq!( - policy - .clone() - .at_lock_time(absolute::LockTime::from_height(999).expect("valid block height")), - Policy::Unsatisfiable - ); - assert_eq!( - policy - .clone() - .at_lock_time(absolute::LockTime::from_height(1000).expect("valid block height")), - policy - ); - assert_eq!( - policy - .clone() - .at_lock_time(absolute::LockTime::from_height(10000).expect("valid block height")), - policy - ); - // Pass a UNIX timestamp to at_lock_time while policy uses a block height. - assert_eq!( - policy - .clone() - .at_lock_time(absolute::LockTime::from_time(500_000_001).expect("valid timestamp")), - Policy::Unsatisfiable - ); - assert_eq!(policy.n_keys(), 0); - assert_eq!(policy.minimum_n_keys(), Some(0)); - - // UNIX timestamp of 10 seconds after the epoch. - let policy = StringPolicy::from_str("after(500000010)").unwrap(); - assert_eq!(policy, Policy::after(500_000_010)); - assert_eq!(policy.absolute_timelocks(), vec![500_000_010]); - assert_eq!(policy.relative_timelocks(), vec![]); - // Pass a block height to at_lock_time while policy uses a UNIX timestapm. - assert_eq!(policy.clone().at_lock_time(absolute::LockTime::ZERO), Policy::Unsatisfiable); - assert_eq!( - policy - .clone() - .at_lock_time(absolute::LockTime::from_height(999).expect("valid block height")), - Policy::Unsatisfiable - ); - assert_eq!( - policy - .clone() - .at_lock_time(absolute::LockTime::from_height(1000).expect("valid block height")), - Policy::Unsatisfiable - ); - assert_eq!( - policy - .clone() - .at_lock_time(absolute::LockTime::from_height(10000).expect("valid block height")), - Policy::Unsatisfiable - ); - // And now pass a UNIX timestamp to at_lock_time while policy also uses a timestamp. - assert_eq!( - policy - .clone() - .at_lock_time(absolute::LockTime::from_time(500_000_000).expect("valid timestamp")), - Policy::Unsatisfiable - ); - assert_eq!( - policy - .clone() - .at_lock_time(absolute::LockTime::from_time(500_000_001).expect("valid timestamp")), - Policy::Unsatisfiable - ); - assert_eq!( - policy - .clone() - .at_lock_time(absolute::LockTime::from_time(500_000_010).expect("valid timestamp")), - policy - ); - assert_eq!( - policy - .clone() - .at_lock_time(absolute::LockTime::from_time(500_000_012).expect("valid timestamp")), - policy - ); - assert_eq!(policy.n_keys(), 0); - assert_eq!(policy.minimum_n_keys(), Some(0)); - } - - #[test] - fn entailment_liquid_test() { - //liquid policy - let liquid_pol = StringPolicy::from_str( - "or(and(older(4096),thresh(2,pk(A),pk(B),pk(C))),thresh(11,pk(F1),pk(F2),pk(F3),pk(F4),pk(F5),pk(F6),pk(F7),pk(F8),pk(F9),pk(F10),pk(F11),pk(F12),pk(F13),pk(F14)))").unwrap(); - // Very bad idea to add master key,pk but let's have it have 50M blocks - let master_key = StringPolicy::from_str("and(older(50000000),pk(master))").unwrap(); - let new_liquid_pol = Policy::Threshold(1, vec![liquid_pol.clone(), master_key]); - - assert!(liquid_pol.clone().entails(new_liquid_pol.clone()).unwrap()); - assert!(!new_liquid_pol.entails(liquid_pol.clone()).unwrap()); - - // test liquid backup policy before the emergency timeout - let backup_policy = StringPolicy::from_str("thresh(2,pk(A),pk(B),pk(C))").unwrap(); - assert!(!backup_policy - .entails(liquid_pol.clone().at_age(Sequence::from_height(4095))) - .unwrap()); - - // Finally test both spending paths - let fed_pol = StringPolicy::from_str("thresh(11,pk(F1),pk(F2),pk(F3),pk(F4),pk(F5),pk(F6),pk(F7),pk(F8),pk(F9),pk(F10),pk(F11),pk(F12),pk(F13),pk(F14))").unwrap(); - let backup_policy_after_expiry = - StringPolicy::from_str("and(older(4096),thresh(2,pk(A),pk(B),pk(C)))").unwrap(); - assert!(fed_pol.entails(liquid_pol.clone()).unwrap()); - assert!(backup_policy_after_expiry.entails(liquid_pol).unwrap()); - } - - #[test] - fn entailment_escrow() { - // Escrow contract - let escrow_pol = StringPolicy::from_str("thresh(2,pk(Alice),pk(Bob),pk(Judge))").unwrap(); - // Alice's authorization constraint - // Authorization is a constraint that states the conditions under which one party must - // be able to redeem the funds. - let auth_alice = StringPolicy::from_str("and(pk(Alice),pk(Judge))").unwrap(); - - //Alice's Control constraint - // The control constraint states the conditions that one party requires - // must be met if the funds are spent by anyone - // Either Alice must authorize the funds or both Judge and Bob must control it - let control_alice = StringPolicy::from_str("or(pk(Alice),and(pk(Judge),pk(Bob)))").unwrap(); - - // Entailment rules - // Authorization entails |- policy |- control constraints - assert!(auth_alice.entails(escrow_pol.clone()).unwrap()); - assert!(escrow_pol.entails(control_alice).unwrap()); - - // Entailment HTLC's - // Escrow contract - let h = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; - let htlc_pol = StringPolicy::from_str(&format!( - "or(and(pk(Alice),older(100)),and(pk(Bob),sha256({})))", - h - )) - .unwrap(); - // Alice's authorization constraint - // Authorization is a constraint that states the conditions under which one party must - // be able to redeem the funds. In HLTC, alice only cares that she can - // authorize her funds with Pk and CSV 100. - let auth_alice = StringPolicy::from_str("and(pk(Alice),older(100))").unwrap(); - - //Alice's Control constraint - // The control constraint states the conditions that one party requires - // must be met if the funds are spent by anyone - // Either Alice must authorize the funds or sha2 preimage must be revealed. - let control_alice = - StringPolicy::from_str(&format!("or(pk(Alice),sha256({}))", h)).unwrap(); - - // Entailment rules - // Authorization entails |- policy |- control constraints - assert!(auth_alice.entails(htlc_pol.clone()).unwrap()); - assert!(htlc_pol.entails(control_alice).unwrap()); - } - - #[test] - fn for_each_key() { - let liquid_pol = StringPolicy::from_str( - "or(and(older(4096),thresh(2,pk(A),pk(B),pk(C))),thresh(11,pk(F1),pk(F2),pk(F3),pk(F4),pk(F5),pk(F6),pk(F7),pk(F8),pk(F9),pk(F10),pk(F11),pk(F12),pk(F13),pk(F14)))").unwrap(); - let mut count = 0; - assert!(liquid_pol.for_each_key(|_| { - count += 1; - true - })); - assert_eq!(count, 17); - } -} From b79be87b5544b2dfcb02bb68b459b775419d0a78 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Thu, 28 Sep 2023 08:38:09 +1000 Subject: [PATCH 8/8] Rename abstract::Policy to Abstract Use the identifier `Abstract` to further disambiguate the abstract policy and the concrete policy. This has the nice advantage of removing a few usages of the kind-of-ugly `r#abstract::Policy`. --- fuzz/fuzz_targets/roundtrip_semantic.rs | 2 +- src/abstract/lift.rs | 4 +- src/abstract/mod.rs | 487 ++++++++++++------------ src/descriptor/bare.rs | 8 +- src/descriptor/segwitv0.rs | 8 +- src/descriptor/sh.rs | 6 +- src/descriptor/sortedmulti.rs | 15 +- src/descriptor/tr.rs | 10 +- src/policy/concrete.rs | 4 +- 9 files changed, 271 insertions(+), 273 deletions(-) diff --git a/fuzz/fuzz_targets/roundtrip_semantic.rs b/fuzz/fuzz_targets/roundtrip_semantic.rs index 106830102..0e309d163 100644 --- a/fuzz/fuzz_targets/roundtrip_semantic.rs +++ b/fuzz/fuzz_targets/roundtrip_semantic.rs @@ -2,7 +2,7 @@ use std::str::FromStr; use honggfuzz::fuzz; -type Policy = miniscript::r#abstract::Policy; +type Policy = miniscript::r#abstract::Abstract; fn do_test(data: &[u8]) { let data_str = String::from_utf8_lossy(data); diff --git a/src/abstract/lift.rs b/src/abstract/lift.rs index 99c2f8481..6c68a5bec 100644 --- a/src/abstract/lift.rs +++ b/src/abstract/lift.rs @@ -15,7 +15,7 @@ use std::error; use crate::descriptor::Descriptor; use crate::miniscript::{Miniscript, ScriptContext}; use crate::policy::concrete::Policy as Concrete; -use crate::r#abstract::Policy as Abstract; +use crate::r#abstract::Abstract; use crate::sync::Arc; use crate::{Error, MiniscriptKey, Terminal}; @@ -227,7 +227,7 @@ mod tests { use crate::{descriptor::TapTree, Descriptor, Tap}; type ConcretePol = crate::policy::concrete::Policy; - type AbstractPol = crate::r#abstract::Policy; + type AbstractPol = crate::r#abstract::Abstract; fn concrete_policy_rtt(s: &str) { let conc = ConcretePol::from_str(s).unwrap(); diff --git a/src/abstract/mod.rs b/src/abstract/mod.rs index 080eb4860..c64e2e3f9 100644 --- a/src/abstract/mod.rs +++ b/src/abstract/mod.rs @@ -24,7 +24,7 @@ use crate::{errstr, expression, AbsLockTime, Error, ForEachKey, MiniscriptKey, T /// representing the same policy are lifted to the same abstract `Policy`, /// regardless of their choice of `pk` or `pk_h` nodes. #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] -pub enum Policy { +pub enum Abstract { /// Unsatisfiable. Unsatisfiable, /// Trivially satisfiable. @@ -44,44 +44,44 @@ pub enum Policy { /// A HASH160 whose preimage must be provided to satisfy the descriptor. Hash160(Pk::Hash160), /// A set of descriptors, satisfactions must be provided for `k` of them. - Threshold(usize, Vec>), + Threshold(usize, Vec>), } -impl Policy +impl Abstract where Pk: MiniscriptKey, { - /// Constructs a `Policy::After` from `n`. + /// Constructs a `Abstract::After` from `n`. /// - /// Helper function equivalent to `Policy::After(absolute::LockTime::from_consensus(n))`. - pub fn after(n: u32) -> Policy { - Policy::After(AbsLockTime::from(absolute::LockTime::from_consensus(n))) + /// Helper function equivalent to `Abstract::After(absolute::LockTime::from_consensus(n))`. + pub fn after(n: u32) -> Abstract { + Abstract::After(AbsLockTime::from(absolute::LockTime::from_consensus(n))) } - /// Construct a `Policy::Older` from `n`. + /// Construct a `Abstract::Older` from `n`. /// - /// Helper function equivalent to `Policy::Older(Sequence::from_consensus(n))`. - pub fn older(n: u32) -> Policy { Policy::Older(Sequence::from_consensus(n)) } + /// Helper function equivalent to `Abstract::Older(Sequence::from_consensus(n))`. + pub fn older(n: u32) -> Abstract { Abstract::Older(Sequence::from_consensus(n)) } } -impl ForEachKey for Policy { +impl ForEachKey for Abstract { fn for_each_key<'a, F: FnMut(&'a Pk) -> bool>(&'a self, mut pred: F) -> bool { self.real_for_each_key(&mut pred) } } -impl Policy { +impl Abstract { fn real_for_each_key<'a, F: FnMut(&'a Pk) -> bool>(&'a self, pred: &mut F) -> bool { match *self { - Policy::Unsatisfiable | Policy::Trivial => true, - Policy::Key(ref pk) => pred(pk), - Policy::Sha256(..) - | Policy::Hash256(..) - | Policy::Ripemd160(..) - | Policy::Hash160(..) - | Policy::After(..) - | Policy::Older(..) => true, - Policy::Threshold(_, ref subs) => { + Abstract::Unsatisfiable | Abstract::Trivial => true, + Abstract::Key(ref pk) => pred(pk), + Abstract::Sha256(..) + | Abstract::Hash256(..) + | Abstract::Ripemd160(..) + | Abstract::Hash160(..) + | Abstract::After(..) + | Abstract::Older(..) => true, + Abstract::Threshold(_, ref subs) => { subs.iter().all(|sub| sub.real_for_each_key(&mut *pred)) } } @@ -95,10 +95,10 @@ impl Policy { /// use std::collections::HashMap; /// use std::str::FromStr; /// use miniscript::bitcoin::{hashes::hash160, PublicKey}; - /// use miniscript::{translate_hash_fail, r#abstract::Policy, Translator}; + /// use miniscript::{translate_hash_fail, r#abstract::Abstract, Translator}; /// let alice_pk = "02c79ef3ede6d14f72a00d0e49b4becfb152197b64c0707425c4f231df29500ee7"; /// let bob_pk = "03d008a849fbf474bd17e9d2c1a827077a468150e58221582ec3410ab309f5afe4"; - /// let placeholder_policy = Policy::::from_str("and(pk(alice_pk),pk(bob_pk))").unwrap(); + /// let placeholder_policy = Abstract::::from_str("and(pk(alice_pk),pk(bob_pk))").unwrap(); /// /// // Information to translate abstract string type keys to concrete `bitcoin::PublicKey`s. /// // In practice, wallets would map from string key names to BIP32 keys. @@ -125,10 +125,10 @@ impl Policy { /// /// let real_policy = placeholder_policy.translate_pk(&mut t).unwrap(); /// - /// let expected_policy = Policy::from_str(&format!("and(pk({}),pk({}))", alice_pk, bob_pk)).unwrap(); + /// let expected_policy = Abstract::from_str(&format!("and(pk({}),pk({}))", alice_pk, bob_pk)).unwrap(); /// assert_eq!(real_policy, expected_policy); /// ``` - pub fn translate_pk(&self, t: &mut T) -> Result, E> + pub fn translate_pk(&self, t: &mut T) -> Result, E> where T: Translator, Q: MiniscriptKey, @@ -136,25 +136,25 @@ impl Policy { self._translate_pk(t) } - fn _translate_pk(&self, t: &mut T) -> Result, E> + fn _translate_pk(&self, t: &mut T) -> Result, E> where T: Translator, Q: MiniscriptKey, { match *self { - Policy::Unsatisfiable => Ok(Policy::Unsatisfiable), - Policy::Trivial => Ok(Policy::Trivial), - Policy::Key(ref pk) => t.pk(pk).map(Policy::Key), - Policy::Sha256(ref h) => t.sha256(h).map(Policy::Sha256), - Policy::Hash256(ref h) => t.hash256(h).map(Policy::Hash256), - Policy::Ripemd160(ref h) => t.ripemd160(h).map(Policy::Ripemd160), - Policy::Hash160(ref h) => t.hash160(h).map(Policy::Hash160), - Policy::After(n) => Ok(Policy::After(n)), - Policy::Older(n) => Ok(Policy::Older(n)), - Policy::Threshold(k, ref subs) => { - let new_subs: Result>, _> = + Abstract::Unsatisfiable => Ok(Abstract::Unsatisfiable), + Abstract::Trivial => Ok(Abstract::Trivial), + Abstract::Key(ref pk) => t.pk(pk).map(Abstract::Key), + Abstract::Sha256(ref h) => t.sha256(h).map(Abstract::Sha256), + Abstract::Hash256(ref h) => t.hash256(h).map(Abstract::Hash256), + Abstract::Ripemd160(ref h) => t.ripemd160(h).map(Abstract::Ripemd160), + Abstract::Hash160(ref h) => t.hash160(h).map(Abstract::Hash160), + Abstract::After(n) => Ok(Abstract::After(n)), + Abstract::Older(n) => Ok(Abstract::Older(n)), + Abstract::Threshold(k, ref subs) => { + let new_subs: Result>, _> = subs.iter().map(|sub| sub._translate_pk(t)).collect(); - new_subs.map(|ok| Policy::Threshold(k, ok)) + new_subs.map(|ok| Abstract::Threshold(k, ok)) } } } @@ -167,15 +167,15 @@ impl Policy { /// sufficient for most practical policies. // This algorithm has a naive implementation. It is possible to optimize this // by memoizing and maintaining a hashmap. - pub fn entails(self, other: Policy) -> Result { + pub fn entails(self, other: Abstract) -> Result { if self.n_terminals() > ENTAILMENT_MAX_TERMINALS { return Err(PolicyError::EntailmentMaxTerminals); } match (self, other) { - (Policy::Unsatisfiable, _) => Ok(true), - (Policy::Trivial, Policy::Trivial) => Ok(true), - (Policy::Trivial, _) => Ok(false), - (_, Policy::Unsatisfiable) => Ok(false), + (Abstract::Unsatisfiable, _) => Ok(true), + (Abstract::Trivial, Abstract::Trivial) => Ok(true), + (Abstract::Trivial, _) => Ok(false), + (_, Abstract::Unsatisfiable) => Ok(false), (a, b) => { let (a_norm, b_norm) = (a.normalized(), b.normalized()); let first_constraint = a_norm.first_constraint(); @@ -187,7 +187,7 @@ impl Policy { a_norm.satisfy_constraint(&first_constraint, false), b_norm.satisfy_constraint(&first_constraint, false), ); - Ok(Policy::entails(a1, b1)? && Policy::entails(a2, b2)?) + Ok(Abstract::entails(a1, b1)? && Abstract::entails(a2, b2)?) } } } @@ -195,8 +195,8 @@ impl Policy { // Helper function to compute the number of constraints in policy. fn n_terminals(&self) -> usize { match self { - &Policy::Threshold(_k, ref subs) => subs.iter().map(|sub| sub.n_terminals()).sum(), - &Policy::Trivial | &Policy::Unsatisfiable => 0, + &Abstract::Threshold(_k, ref subs) => subs.iter().map(|sub| sub.n_terminals()).sum(), + &Abstract::Trivial | &Abstract::Unsatisfiable => 0, _leaf => 1, } } @@ -204,10 +204,10 @@ impl Policy { // Helper function to get the first constraint in the policy. // Returns the first leaf policy. Used in policy entailment. // Assumes that the current policy is normalized. - fn first_constraint(&self) -> Policy { + fn first_constraint(&self) -> Abstract { debug_assert!(self.clone().normalized() == self.clone()); match self { - &Policy::Threshold(_k, ref subs) => subs[0].first_constraint(), + &Abstract::Threshold(_k, ref subs) => subs[0].first_constraint(), first => first.clone(), } } @@ -216,26 +216,30 @@ impl Policy { // to true or false and returning the resultant normalized policy. Witness // is currently encoded as policy. Only accepts leaf fragment and a // normalized policy - pub(crate) fn satisfy_constraint(self, witness: &Policy, available: bool) -> Policy { + pub(crate) fn satisfy_constraint( + self, + witness: &Abstract, + available: bool, + ) -> Abstract { debug_assert!(self.clone().normalized() == self); - if let Policy::Threshold { .. } = *witness { - // We can't debug_assert on Policy::Threshold. + if let Abstract::Threshold { .. } = *witness { + // We can't debug_assert on Abstract::Threshold. panic!("should be unreachable") } let ret = match self { - Policy::Threshold(k, subs) => { + Abstract::Threshold(k, subs) => { let mut ret_subs = vec![]; for sub in subs { ret_subs.push(sub.satisfy_constraint(witness, available)); } - Policy::Threshold(k, ret_subs) + Abstract::Threshold(k, ret_subs) } ref leaf if leaf == witness => { if available { - Policy::Trivial + Abstract::Trivial } else { - Policy::Unsatisfiable + Abstract::Unsatisfiable } } x => x, @@ -244,19 +248,19 @@ impl Policy { } } -impl fmt::Debug for Policy { +impl fmt::Debug for Abstract { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - Policy::Unsatisfiable => f.write_str("UNSATISFIABLE()"), - Policy::Trivial => f.write_str("TRIVIAL()"), - Policy::Key(ref pkh) => write!(f, "pk({:?})", pkh), - Policy::After(n) => write!(f, "after({})", n), - Policy::Older(n) => write!(f, "older({})", n), - Policy::Sha256(ref h) => write!(f, "sha256({})", h), - Policy::Hash256(ref h) => write!(f, "hash256({})", h), - Policy::Ripemd160(ref h) => write!(f, "ripemd160({})", h), - Policy::Hash160(ref h) => write!(f, "hash160({})", h), - Policy::Threshold(k, ref subs) => { + Abstract::Unsatisfiable => f.write_str("UNSATISFIABLE()"), + Abstract::Trivial => f.write_str("TRIVIAL()"), + Abstract::Key(ref pkh) => write!(f, "pk({:?})", pkh), + Abstract::After(n) => write!(f, "after({})", n), + Abstract::Older(n) => write!(f, "older({})", n), + Abstract::Sha256(ref h) => write!(f, "sha256({})", h), + Abstract::Hash256(ref h) => write!(f, "hash256({})", h), + Abstract::Ripemd160(ref h) => write!(f, "ripemd160({})", h), + Abstract::Hash160(ref h) => write!(f, "hash160({})", h), + Abstract::Threshold(k, ref subs) => { if k == subs.len() { write!(f, "and(")?; } else if k == 1 { @@ -277,19 +281,19 @@ impl fmt::Debug for Policy { } } -impl fmt::Display for Policy { +impl fmt::Display for Abstract { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - Policy::Unsatisfiable => f.write_str("UNSATISFIABLE"), - Policy::Trivial => f.write_str("TRIVIAL"), - Policy::Key(ref pkh) => write!(f, "pk({})", pkh), - Policy::After(n) => write!(f, "after({})", n), - Policy::Older(n) => write!(f, "older({})", n), - Policy::Sha256(ref h) => write!(f, "sha256({})", h), - Policy::Hash256(ref h) => write!(f, "hash256({})", h), - Policy::Ripemd160(ref h) => write!(f, "ripemd160({})", h), - Policy::Hash160(ref h) => write!(f, "hash160({})", h), - Policy::Threshold(k, ref subs) => { + Abstract::Unsatisfiable => f.write_str("UNSATISFIABLE"), + Abstract::Trivial => f.write_str("TRIVIAL"), + Abstract::Key(ref pkh) => write!(f, "pk({})", pkh), + Abstract::After(n) => write!(f, "after({})", n), + Abstract::Older(n) => write!(f, "older({})", n), + Abstract::Sha256(ref h) => write!(f, "sha256({})", h), + Abstract::Hash256(ref h) => write!(f, "hash256({})", h), + Abstract::Ripemd160(ref h) => write!(f, "ripemd160({})", h), + Abstract::Hash160(ref h) => write!(f, "hash160({})", h), + Abstract::Threshold(k, ref subs) => { if k == subs.len() { write!(f, "and(")?; } else if k == 1 { @@ -311,9 +315,9 @@ impl fmt::Display for Policy { } impl_from_str!( - Policy, + Abstract, type Err = Error;, - fn from_str(s: &str) -> Result, Error> { + fn from_str(s: &str) -> Result, Error> { expression::check_valid_chars(s)?; let tree = expression::Tree::from_str(s)?; @@ -321,32 +325,34 @@ impl_from_str!( } ); -serde_string_impl_pk!(Policy, "a miniscript semantic policy"); +serde_string_impl_pk!(Abstract, "a miniscript semantic policy"); impl_from_tree!( - Policy, - fn from_tree(top: &expression::Tree) -> Result, Error> { + Abstract, + fn from_tree(top: &expression::Tree) -> Result, Error> { match (top.name, top.args.len()) { - ("UNSATISFIABLE", 0) => Ok(Policy::Unsatisfiable), - ("TRIVIAL", 0) => Ok(Policy::Trivial), - ("pk", 1) => expression::terminal(&top.args[0], |pk| Pk::from_str(pk).map(Policy::Key)), + ("UNSATISFIABLE", 0) => Ok(Abstract::Unsatisfiable), + ("TRIVIAL", 0) => Ok(Abstract::Trivial), + ("pk", 1) => { + expression::terminal(&top.args[0], |pk| Pk::from_str(pk).map(Abstract::Key)) + } ("after", 1) => expression::terminal(&top.args[0], |x| { - expression::parse_num(x).map(|x| Policy::after(x)) + expression::parse_num(x).map(|x| Abstract::after(x)) }), ("older", 1) => expression::terminal(&top.args[0], |x| { - expression::parse_num(x).map(|x| Policy::older(x)) + expression::parse_num(x).map(|x| Abstract::older(x)) + }), + ("sha256", 1) => expression::terminal(&top.args[0], |x| { + Pk::Sha256::from_str(x).map(Abstract::Sha256) }), - ("sha256", 1) => { - expression::terminal(&top.args[0], |x| Pk::Sha256::from_str(x).map(Policy::Sha256)) - } ("hash256", 1) => expression::terminal(&top.args[0], |x| { - Pk::Hash256::from_str(x).map(Policy::Hash256) + Pk::Hash256::from_str(x).map(Abstract::Hash256) }), ("ripemd160", 1) => expression::terminal(&top.args[0], |x| { - Pk::Ripemd160::from_str(x).map(Policy::Ripemd160) + Pk::Ripemd160::from_str(x).map(Abstract::Ripemd160) }), ("hash160", 1) => expression::terminal(&top.args[0], |x| { - Pk::Hash160::from_str(x).map(Policy::Hash160) + Pk::Hash160::from_str(x).map(Abstract::Hash160) }), ("and", nsubs) => { if nsubs < 2 { @@ -354,9 +360,9 @@ impl_from_tree!( } let mut subs = Vec::with_capacity(nsubs); for arg in &top.args { - subs.push(Policy::from_tree(arg)?); + subs.push(Abstract::from_tree(arg)?); } - Ok(Policy::Threshold(nsubs, subs)) + Ok(Abstract::Threshold(nsubs, subs)) } ("or", nsubs) => { if nsubs < 2 { @@ -364,9 +370,9 @@ impl_from_tree!( } let mut subs = Vec::with_capacity(nsubs); for arg in &top.args { - subs.push(Policy::from_tree(arg)?); + subs.push(Abstract::from_tree(arg)?); } - Ok(Policy::Threshold(1, subs)) + Ok(Abstract::Threshold(1, subs)) } ("thresh", nsubs) => { if nsubs == 0 || nsubs == 1 { @@ -382,7 +388,7 @@ impl_from_tree!( // thresh(1) and thresh(n) are disallowed in semantic policies if thresh <= 1 || thresh >= (nsubs as u32 - 1) { return Err(errstr( - "Abstract Policy thresh cannot have k = 1 or k =n, use `and`/`or` instead", + "Abstract Abstract thresh cannot have k = 1 or k =n, use `and`/`or` instead", )); } if thresh >= (nsubs as u32) { @@ -391,28 +397,28 @@ impl_from_tree!( let mut subs = Vec::with_capacity(top.args.len() - 1); for arg in &top.args[1..] { - subs.push(Policy::from_tree(arg)?); + subs.push(Abstract::from_tree(arg)?); } - Ok(Policy::Threshold(thresh as usize, subs)) + Ok(Abstract::Threshold(thresh as usize, subs)) } _ => Err(errstr(top.name)), } } ); -impl Policy { +impl Abstract { /// Flattens out trees of `And`s and `Or`s; eliminate `Trivial` and /// `Unsatisfiable`s. Does not reorder any branches; use `.sort`. - pub fn normalized(self) -> Policy { + pub fn normalized(self) -> Abstract { match self { - Policy::Threshold(k, subs) => { + Abstract::Threshold(k, subs) => { let mut ret_subs = Vec::with_capacity(subs.len()); let subs: Vec<_> = subs.into_iter().map(|sub| sub.normalized()).collect(); - let trivial_count = subs.iter().filter(|&pol| *pol == Policy::Trivial).count(); + let trivial_count = subs.iter().filter(|&pol| *pol == Abstract::Trivial).count(); let unsatisfied_count = subs .iter() - .filter(|&pol| *pol == Policy::Unsatisfiable) + .filter(|&pol| *pol == Abstract::Unsatisfiable) .count(); let n = subs.len() - unsatisfied_count - trivial_count; // remove all true/false @@ -422,16 +428,16 @@ impl Policy { let is_or = m == 1; for sub in subs { match sub { - Policy::Trivial | Policy::Unsatisfiable => {} - Policy::Threshold(k, subs) => { + Abstract::Trivial | Abstract::Unsatisfiable => {} + Abstract::Threshold(k, subs) => { match (is_and, is_or) { (true, true) => { // means m = n = 1, thresh(1,X) type thing. - ret_subs.push(Policy::Threshold(k, subs)); + ret_subs.push(Abstract::Threshold(k, subs)); } (true, false) if k == subs.len() => ret_subs.extend(subs), // and case (false, true) if k == 1 => ret_subs.extend(subs), // or case - _ => ret_subs.push(Policy::Threshold(k, subs)), + _ => ret_subs.push(Abstract::Threshold(k, subs)), } } x => ret_subs.push(x), @@ -439,17 +445,17 @@ impl Policy { } // Now reason about m of n threshold if m == 0 { - Policy::Trivial + Abstract::Trivial } else if m > ret_subs.len() { - Policy::Unsatisfiable + Abstract::Unsatisfiable } else if ret_subs.len() == 1 { ret_subs.pop().unwrap() } else if is_and { - Policy::Threshold(ret_subs.len(), ret_subs) + Abstract::Threshold(ret_subs.len(), ret_subs) } else if is_or { - Policy::Threshold(1, ret_subs) + Abstract::Threshold(1, ret_subs) } else { - Policy::Threshold(m, ret_subs) + Abstract::Threshold(m, ret_subs) } } x => x, @@ -458,31 +464,31 @@ impl Policy { /// Detects a true/trivial policy. /// - /// Only checks whether the policy is `Policy::Trivial`, to check if the + /// Only checks whether the policy is `Abstract::Trivial`, to check if the /// normalized form is trivial, the caller is expected to normalize the /// policy first. - pub fn is_trivial(&self) -> bool { matches!(*self, Policy::Trivial) } + pub fn is_trivial(&self) -> bool { matches!(*self, Abstract::Trivial) } /// Detects a false/unsatisfiable policy. /// - /// Only checks whether the policy is `Policy::Unsatisfiable`, to check if + /// Only checks whether the policy is `Abstract::Unsatisfiable`, to check if /// the normalized form is unsatisfiable, the caller is expected to /// normalize the policy first. - pub fn is_unsatisfiable(&self) -> bool { matches!(*self, Policy::Unsatisfiable) } + pub fn is_unsatisfiable(&self) -> bool { matches!(*self, Abstract::Unsatisfiable) } /// Helper function to do the recursion in `timelocks`. fn real_relative_timelocks(&self) -> Vec { match *self { - Policy::Unsatisfiable - | Policy::Trivial - | Policy::Key(..) - | Policy::Sha256(..) - | Policy::Hash256(..) - | Policy::Ripemd160(..) - | Policy::Hash160(..) => vec![], - Policy::After(..) => vec![], - Policy::Older(t) => vec![t.to_consensus_u32()], - Policy::Threshold(_, ref subs) => subs.iter().fold(vec![], |mut acc, x| { + Abstract::Unsatisfiable + | Abstract::Trivial + | Abstract::Key(..) + | Abstract::Sha256(..) + | Abstract::Hash256(..) + | Abstract::Ripemd160(..) + | Abstract::Hash160(..) => vec![], + Abstract::After(..) => vec![], + Abstract::Older(t) => vec![t.to_consensus_u32()], + Abstract::Threshold(_, ref subs) => subs.iter().fold(vec![], |mut acc, x| { acc.extend(x.real_relative_timelocks()); acc }), @@ -501,16 +507,16 @@ impl Policy { /// Helper function for recursion in `absolute timelocks` fn real_absolute_timelocks(&self) -> Vec { match *self { - Policy::Unsatisfiable - | Policy::Trivial - | Policy::Key(..) - | Policy::Sha256(..) - | Policy::Hash256(..) - | Policy::Ripemd160(..) - | Policy::Hash160(..) => vec![], - Policy::Older(..) => vec![], - Policy::After(t) => vec![t.to_u32()], - Policy::Threshold(_, ref subs) => subs.iter().fold(vec![], |mut acc, x| { + Abstract::Unsatisfiable + | Abstract::Trivial + | Abstract::Key(..) + | Abstract::Sha256(..) + | Abstract::Hash256(..) + | Abstract::Ripemd160(..) + | Abstract::Hash160(..) => vec![], + Abstract::Older(..) => vec![], + Abstract::After(t) => vec![t.to_u32()], + Abstract::Threshold(_, ref subs) => subs.iter().fold(vec![], |mut acc, x| { acc.extend(x.real_absolute_timelocks()); acc }), @@ -528,20 +534,20 @@ impl Policy { /// Filters a policy by eliminating relative timelock constraints /// that are not satisfied at the given `age`. - pub fn at_age(mut self, age: Sequence) -> Policy { + pub fn at_age(mut self, age: Sequence) -> Abstract { self = match self { - Policy::Older(t) => { + Abstract::Older(t) => { if t.is_height_locked() && age.is_time_locked() || t.is_time_locked() && age.is_height_locked() || t.to_consensus_u32() > age.to_consensus_u32() { - Policy::Unsatisfiable + Abstract::Unsatisfiable } else { - Policy::Older(t) + Abstract::Older(t) } } - Policy::Threshold(k, subs) => { - Policy::Threshold(k, subs.into_iter().map(|sub| sub.at_age(age)).collect()) + Abstract::Threshold(k, subs) => { + Abstract::Threshold(k, subs.into_iter().map(|sub| sub.at_age(age)).collect()) } x => x, }; @@ -550,11 +556,11 @@ impl Policy { /// Filters a policy by eliminating absolute timelock constraints /// that are not satisfied at the given `n` (`n OP_CHECKLOCKTIMEVERIFY`). - pub fn at_lock_time(mut self, n: absolute::LockTime) -> Policy { + pub fn at_lock_time(mut self, n: absolute::LockTime) -> Abstract { use absolute::LockTime::*; self = match self { - Policy::After(t) => { + Abstract::After(t) => { let t = absolute::LockTime::from(t); let is_satisfied_by = match (t, n) { (Blocks(t), Blocks(n)) => t <= n, @@ -562,13 +568,13 @@ impl Policy { _ => false, }; if !is_satisfied_by { - Policy::Unsatisfiable + Abstract::Unsatisfiable } else { - Policy::After(t.into()) + Abstract::After(t.into()) } } - Policy::Threshold(k, subs) => { - Policy::Threshold(k, subs.into_iter().map(|sub| sub.at_lock_time(n)).collect()) + Abstract::Threshold(k, subs) => { + Abstract::Threshold(k, subs.into_iter().map(|sub| sub.at_lock_time(n)).collect()) } x => x, }; @@ -579,15 +585,15 @@ impl Policy { /// Duplicate keys will be double-counted. pub fn n_keys(&self) -> usize { match *self { - Policy::Unsatisfiable | Policy::Trivial => 0, - Policy::Key(..) => 1, - Policy::After(..) - | Policy::Older(..) - | Policy::Sha256(..) - | Policy::Hash256(..) - | Policy::Ripemd160(..) - | Policy::Hash160(..) => 0, - Policy::Threshold(_, ref subs) => subs.iter().map(|sub| sub.n_keys()).sum::(), + Abstract::Unsatisfiable | Abstract::Trivial => 0, + Abstract::Key(..) => 1, + Abstract::After(..) + | Abstract::Older(..) + | Abstract::Sha256(..) + | Abstract::Hash256(..) + | Abstract::Ripemd160(..) + | Abstract::Hash160(..) => 0, + Abstract::Threshold(_, ref subs) => subs.iter().map(|sub| sub.n_keys()).sum::(), } } @@ -599,18 +605,18 @@ impl Policy { /// Returns `None` if the policy is not satisfiable. pub fn minimum_n_keys(&self) -> Option { match *self { - Policy::Unsatisfiable => None, - Policy::Trivial => Some(0), - Policy::Key(..) => Some(1), - Policy::After(..) - | Policy::Older(..) - | Policy::Sha256(..) - | Policy::Hash256(..) - | Policy::Ripemd160(..) - | Policy::Hash160(..) => Some(0), - Policy::Threshold(k, ref subs) => { + Abstract::Unsatisfiable => None, + Abstract::Trivial => Some(0), + Abstract::Key(..) => Some(1), + Abstract::After(..) + | Abstract::Older(..) + | Abstract::Sha256(..) + | Abstract::Hash256(..) + | Abstract::Ripemd160(..) + | Abstract::Hash160(..) => Some(0), + Abstract::Threshold(k, ref subs) => { let mut sublens: Vec = - subs.iter().filter_map(Policy::minimum_n_keys).collect(); + subs.iter().filter_map(Abstract::minimum_n_keys).collect(); if sublens.len() < k { // Not enough branches are satisfiable None @@ -623,18 +629,18 @@ impl Policy { } } -impl Policy { +impl Abstract { /// "Sorts" a policy to bring it into a canonical form to allow comparisons. /// /// Does **not** allow policies to be compared for functional equivalence; /// in general this appears to require Gröbner basis techniques that are not /// implemented. - pub fn sorted(self) -> Policy { + pub fn sorted(self) -> Abstract { match self { - Policy::Threshold(k, subs) => { - let mut new_subs: Vec<_> = subs.into_iter().map(Policy::sorted).collect(); + Abstract::Threshold(k, subs) => { + let mut new_subs: Vec<_> = subs.into_iter().map(Abstract::sorted).collect(); new_subs.sort(); - Policy::Threshold(k, new_subs) + Abstract::Threshold(k, new_subs) } x => x, } @@ -649,25 +655,25 @@ mod tests { use super::*; - type StringPolicy = Policy; + type StringAbstract = Abstract; #[test] fn parse_policy_err() { - assert!(StringPolicy::from_str("(").is_err()); - assert!(StringPolicy::from_str("(x()").is_err()); - assert!(StringPolicy::from_str("(\u{7f}()3").is_err()); - assert!(StringPolicy::from_str("pk()").is_ok()); + assert!(StringAbstract::from_str("(").is_err()); + assert!(StringAbstract::from_str("(x()").is_err()); + assert!(StringAbstract::from_str("(\u{7f}()3").is_err()); + assert!(StringAbstract::from_str("pk()").is_ok()); - assert!(StringPolicy::from_str("or(or)").is_err()); + assert!(StringAbstract::from_str("or(or)").is_err()); - assert!(Policy::::from_str("pk()").is_err()); - assert!(Policy::::from_str( + assert!(Abstract::::from_str("pk()").is_err()); + assert!(Abstract::::from_str( "pk(\ 0200000000000000000000000000000000000002\ )" ) .is_err()); - assert!(Policy::::from_str( + assert!(Abstract::::from_str( "pk(\ 02c79ef3ede6d14f72a00d0e49b4becfb152197b64c0707425c4f231df29500ee7\ )" @@ -677,8 +683,8 @@ mod tests { #[test] fn semantic_analysis() { - let policy = StringPolicy::from_str("pk()").unwrap(); - assert_eq!(policy, Policy::Key("".to_owned())); + let policy = StringAbstract::from_str("pk()").unwrap(); + assert_eq!(policy, Abstract::Key("".to_owned())); assert_eq!(policy.relative_timelocks(), vec![]); assert_eq!(policy.absolute_timelocks(), vec![]); assert_eq!(policy.clone().at_age(Sequence::ZERO), policy); @@ -686,32 +692,32 @@ mod tests { assert_eq!(policy.n_keys(), 1); assert_eq!(policy.minimum_n_keys(), Some(1)); - let policy = StringPolicy::from_str("older(1000)").unwrap(); - assert_eq!(policy, Policy::Older(Sequence::from_height(1000))); + let policy = StringAbstract::from_str("older(1000)").unwrap(); + assert_eq!(policy, Abstract::Older(Sequence::from_height(1000))); assert_eq!(policy.absolute_timelocks(), vec![]); assert_eq!(policy.relative_timelocks(), vec![1000]); - assert_eq!(policy.clone().at_age(Sequence::ZERO), Policy::Unsatisfiable); - assert_eq!(policy.clone().at_age(Sequence::from_height(999)), Policy::Unsatisfiable); + assert_eq!(policy.clone().at_age(Sequence::ZERO), Abstract::Unsatisfiable); + assert_eq!(policy.clone().at_age(Sequence::from_height(999)), Abstract::Unsatisfiable); assert_eq!(policy.clone().at_age(Sequence::from_height(1000)), policy); assert_eq!(policy.clone().at_age(Sequence::from_height(10000)), policy); assert_eq!(policy.n_keys(), 0); assert_eq!(policy.minimum_n_keys(), Some(0)); - let policy = StringPolicy::from_str("or(pk(),older(1000))").unwrap(); + let policy = StringAbstract::from_str("or(pk(),older(1000))").unwrap(); assert_eq!( policy, - Policy::Threshold( + Abstract::Threshold( 1, vec![ - Policy::Key("".to_owned()), - Policy::Older(Sequence::from_height(1000)), + Abstract::Key("".to_owned()), + Abstract::Older(Sequence::from_height(1000)), ] ) ); assert_eq!(policy.relative_timelocks(), vec![1000]); assert_eq!(policy.absolute_timelocks(), vec![]); - assert_eq!(policy.clone().at_age(Sequence::ZERO), Policy::Key("".to_owned())); - assert_eq!(policy.clone().at_age(Sequence::from_height(999)), Policy::Key("".to_owned())); + assert_eq!(policy.clone().at_age(Sequence::ZERO), Abstract::Key("".to_owned())); + assert_eq!(policy.clone().at_age(Sequence::from_height(999)), Abstract::Key("".to_owned())); assert_eq!(policy.clone().at_age(Sequence::from_height(1000)), policy.clone().normalized()); assert_eq!( policy.clone().at_age(Sequence::from_height(10000)), @@ -720,27 +726,27 @@ mod tests { assert_eq!(policy.n_keys(), 1); assert_eq!(policy.minimum_n_keys(), Some(0)); - let policy = StringPolicy::from_str("or(pk(),UNSATISFIABLE)").unwrap(); + let policy = StringAbstract::from_str("or(pk(),UNSATISFIABLE)").unwrap(); assert_eq!( policy, - Policy::Threshold(1, vec![Policy::Key("".to_owned()), Policy::Unsatisfiable,]) + Abstract::Threshold(1, vec![Abstract::Key("".to_owned()), Abstract::Unsatisfiable,]) ); assert_eq!(policy.relative_timelocks(), vec![]); assert_eq!(policy.absolute_timelocks(), vec![]); assert_eq!(policy.n_keys(), 1); assert_eq!(policy.minimum_n_keys(), Some(1)); - let policy = StringPolicy::from_str("and(pk(),UNSATISFIABLE)").unwrap(); + let policy = StringAbstract::from_str("and(pk(),UNSATISFIABLE)").unwrap(); assert_eq!( policy, - Policy::Threshold(2, vec![Policy::Key("".to_owned()), Policy::Unsatisfiable,]) + Abstract::Threshold(2, vec![Abstract::Key("".to_owned()), Abstract::Unsatisfiable,]) ); assert_eq!(policy.relative_timelocks(), vec![]); assert_eq!(policy.absolute_timelocks(), vec![]); assert_eq!(policy.n_keys(), 1); assert_eq!(policy.minimum_n_keys(), None); - let policy = StringPolicy::from_str( + let policy = StringAbstract::from_str( "thresh(\ 2,older(1000),older(10000),older(1000),older(2000),older(2000)\ )", @@ -748,14 +754,14 @@ mod tests { .unwrap(); assert_eq!( policy, - Policy::Threshold( + Abstract::Threshold( 2, vec![ - Policy::Older(Sequence::from_height(1000)), - Policy::Older(Sequence::from_height(10000)), - Policy::Older(Sequence::from_height(1000)), - Policy::Older(Sequence::from_height(2000)), - Policy::Older(Sequence::from_height(2000)), + Abstract::Older(Sequence::from_height(1000)), + Abstract::Older(Sequence::from_height(10000)), + Abstract::Older(Sequence::from_height(1000)), + Abstract::Older(Sequence::from_height(2000)), + Abstract::Older(Sequence::from_height(2000)), ] ) ); @@ -764,7 +770,7 @@ mod tests { vec![1000, 2000, 10000] //sorted and dedup'd ); - let policy = StringPolicy::from_str( + let policy = StringAbstract::from_str( "thresh(\ 2,older(1000),older(10000),older(1000),UNSATISFIABLE,UNSATISFIABLE\ )", @@ -772,14 +778,14 @@ mod tests { .unwrap(); assert_eq!( policy, - Policy::Threshold( + Abstract::Threshold( 2, vec![ - Policy::Older(Sequence::from_height(1000)), - Policy::Older(Sequence::from_height(10000)), - Policy::Older(Sequence::from_height(1000)), - Policy::Unsatisfiable, - Policy::Unsatisfiable, + Abstract::Older(Sequence::from_height(1000)), + Abstract::Older(Sequence::from_height(10000)), + Abstract::Older(Sequence::from_height(1000)), + Abstract::Unsatisfiable, + Abstract::Unsatisfiable, ] ) ); @@ -791,16 +797,16 @@ mod tests { assert_eq!(policy.minimum_n_keys(), Some(0)); // Block height 1000. - let policy = StringPolicy::from_str("after(1000)").unwrap(); - assert_eq!(policy, Policy::after(1000)); + let policy = StringAbstract::from_str("after(1000)").unwrap(); + assert_eq!(policy, Abstract::after(1000)); assert_eq!(policy.absolute_timelocks(), vec![1000]); assert_eq!(policy.relative_timelocks(), vec![]); - assert_eq!(policy.clone().at_lock_time(absolute::LockTime::ZERO), Policy::Unsatisfiable); + assert_eq!(policy.clone().at_lock_time(absolute::LockTime::ZERO), Abstract::Unsatisfiable); assert_eq!( policy .clone() .at_lock_time(absolute::LockTime::from_height(999).expect("valid block height")), - Policy::Unsatisfiable + Abstract::Unsatisfiable ); assert_eq!( policy @@ -819,48 +825,48 @@ mod tests { policy .clone() .at_lock_time(absolute::LockTime::from_time(500_000_001).expect("valid timestamp")), - Policy::Unsatisfiable + Abstract::Unsatisfiable ); assert_eq!(policy.n_keys(), 0); assert_eq!(policy.minimum_n_keys(), Some(0)); // UNIX timestamp of 10 seconds after the epoch. - let policy = StringPolicy::from_str("after(500000010)").unwrap(); - assert_eq!(policy, Policy::after(500_000_010)); + let policy = StringAbstract::from_str("after(500000010)").unwrap(); + assert_eq!(policy, Abstract::after(500_000_010)); assert_eq!(policy.absolute_timelocks(), vec![500_000_010]); assert_eq!(policy.relative_timelocks(), vec![]); // Pass a block height to at_lock_time while policy uses a UNIX timestapm. - assert_eq!(policy.clone().at_lock_time(absolute::LockTime::ZERO), Policy::Unsatisfiable); + assert_eq!(policy.clone().at_lock_time(absolute::LockTime::ZERO), Abstract::Unsatisfiable); assert_eq!( policy .clone() .at_lock_time(absolute::LockTime::from_height(999).expect("valid block height")), - Policy::Unsatisfiable + Abstract::Unsatisfiable ); assert_eq!( policy .clone() .at_lock_time(absolute::LockTime::from_height(1000).expect("valid block height")), - Policy::Unsatisfiable + Abstract::Unsatisfiable ); assert_eq!( policy .clone() .at_lock_time(absolute::LockTime::from_height(10000).expect("valid block height")), - Policy::Unsatisfiable + Abstract::Unsatisfiable ); // And now pass a UNIX timestamp to at_lock_time while policy also uses a timestamp. assert_eq!( policy .clone() .at_lock_time(absolute::LockTime::from_time(500_000_000).expect("valid timestamp")), - Policy::Unsatisfiable + Abstract::Unsatisfiable ); assert_eq!( policy .clone() .at_lock_time(absolute::LockTime::from_time(500_000_001).expect("valid timestamp")), - Policy::Unsatisfiable + Abstract::Unsatisfiable ); assert_eq!( policy @@ -881,25 +887,25 @@ mod tests { #[test] fn entailment_liquid_test() { //liquid policy - let liquid_pol = StringPolicy::from_str( + let liquid_pol = StringAbstract::from_str( "or(and(older(4096),thresh(2,pk(A),pk(B),pk(C))),thresh(11,pk(F1),pk(F2),pk(F3),pk(F4),pk(F5),pk(F6),pk(F7),pk(F8),pk(F9),pk(F10),pk(F11),pk(F12),pk(F13),pk(F14)))").unwrap(); // Very bad idea to add master key,pk but let's have it have 50M blocks - let master_key = StringPolicy::from_str("and(older(50000000),pk(master))").unwrap(); - let new_liquid_pol = Policy::Threshold(1, vec![liquid_pol.clone(), master_key]); + let master_key = StringAbstract::from_str("and(older(50000000),pk(master))").unwrap(); + let new_liquid_pol = Abstract::Threshold(1, vec![liquid_pol.clone(), master_key]); assert!(liquid_pol.clone().entails(new_liquid_pol.clone()).unwrap()); assert!(!new_liquid_pol.entails(liquid_pol.clone()).unwrap()); // test liquid backup policy before the emergency timeout - let backup_policy = StringPolicy::from_str("thresh(2,pk(A),pk(B),pk(C))").unwrap(); + let backup_policy = StringAbstract::from_str("thresh(2,pk(A),pk(B),pk(C))").unwrap(); assert!(!backup_policy .entails(liquid_pol.clone().at_age(Sequence::from_height(4095))) .unwrap()); // Finally test both spending paths - let fed_pol = StringPolicy::from_str("thresh(11,pk(F1),pk(F2),pk(F3),pk(F4),pk(F5),pk(F6),pk(F7),pk(F8),pk(F9),pk(F10),pk(F11),pk(F12),pk(F13),pk(F14))").unwrap(); + let fed_pol = StringAbstract::from_str("thresh(11,pk(F1),pk(F2),pk(F3),pk(F4),pk(F5),pk(F6),pk(F7),pk(F8),pk(F9),pk(F10),pk(F11),pk(F12),pk(F13),pk(F14))").unwrap(); let backup_policy_after_expiry = - StringPolicy::from_str("and(older(4096),thresh(2,pk(A),pk(B),pk(C)))").unwrap(); + StringAbstract::from_str("and(older(4096),thresh(2,pk(A),pk(B),pk(C)))").unwrap(); assert!(fed_pol.entails(liquid_pol.clone()).unwrap()); assert!(backup_policy_after_expiry.entails(liquid_pol).unwrap()); } @@ -907,17 +913,18 @@ mod tests { #[test] fn entailment_escrow() { // Escrow contract - let escrow_pol = StringPolicy::from_str("thresh(2,pk(Alice),pk(Bob),pk(Judge))").unwrap(); + let escrow_pol = StringAbstract::from_str("thresh(2,pk(Alice),pk(Bob),pk(Judge))").unwrap(); // Alice's authorization constraint // Authorization is a constraint that states the conditions under which one party must // be able to redeem the funds. - let auth_alice = StringPolicy::from_str("and(pk(Alice),pk(Judge))").unwrap(); + let auth_alice = StringAbstract::from_str("and(pk(Alice),pk(Judge))").unwrap(); //Alice's Control constraint // The control constraint states the conditions that one party requires // must be met if the funds are spent by anyone // Either Alice must authorize the funds or both Judge and Bob must control it - let control_alice = StringPolicy::from_str("or(pk(Alice),and(pk(Judge),pk(Bob)))").unwrap(); + let control_alice = + StringAbstract::from_str("or(pk(Alice),and(pk(Judge),pk(Bob)))").unwrap(); // Entailment rules // Authorization entails |- policy |- control constraints @@ -927,7 +934,7 @@ mod tests { // Entailment HTLC's // Escrow contract let h = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; - let htlc_pol = StringPolicy::from_str(&format!( + let htlc_pol = StringAbstract::from_str(&format!( "or(and(pk(Alice),older(100)),and(pk(Bob),sha256({})))", h )) @@ -936,14 +943,14 @@ mod tests { // Authorization is a constraint that states the conditions under which one party must // be able to redeem the funds. In HLTC, alice only cares that she can // authorize her funds with Pk and CSV 100. - let auth_alice = StringPolicy::from_str("and(pk(Alice),older(100))").unwrap(); + let auth_alice = StringAbstract::from_str("and(pk(Alice),older(100))").unwrap(); //Alice's Control constraint // The control constraint states the conditions that one party requires // must be met if the funds are spent by anyone // Either Alice must authorize the funds or sha2 preimage must be revealed. let control_alice = - StringPolicy::from_str(&format!("or(pk(Alice),sha256({}))", h)).unwrap(); + StringAbstract::from_str(&format!("or(pk(Alice),sha256({}))", h)).unwrap(); // Entailment rules // Authorization entails |- policy |- control constraints @@ -953,7 +960,7 @@ mod tests { #[test] fn for_each_key() { - let liquid_pol = StringPolicy::from_str( + let liquid_pol = StringAbstract::from_str( "or(and(older(4096),thresh(2,pk(A),pk(B),pk(C))),thresh(11,pk(F1),pk(F2),pk(F3),pk(F4),pk(F5),pk(F6),pk(F7),pk(F8),pk(F9),pk(F10),pk(F11),pk(F12),pk(F13),pk(F14)))").unwrap(); let mut count = 0; assert!(liquid_pol.for_each_key(|_| { diff --git a/src/descriptor/bare.rs b/src/descriptor/bare.rs index fab792e49..b20b6205f 100644 --- a/src/descriptor/bare.rs +++ b/src/descriptor/bare.rs @@ -19,7 +19,7 @@ use crate::miniscript::context::{ScriptContext, ScriptContextError}; use crate::miniscript::satisfy::{Placeholder, Satisfaction, Witness}; use crate::plan::AssetProvider; use crate::prelude::*; -use crate::r#abstract::{self, Liftable}; +use crate::r#abstract::{Abstract, Liftable}; use crate::util::{varint_len, witness_to_scriptsig}; use crate::{ BareCtx, Error, ForEachKey, Miniscript, MiniscriptKey, Satisfier, ToPublicKey, TranslateErr, @@ -165,7 +165,7 @@ impl fmt::Display for Bare { } impl Liftable for Bare { - fn lift(&self) -> Result, Error> { self.ms.lift() } + fn lift(&self) -> Result, Error> { self.ms.lift() } } impl_from_tree!( @@ -362,9 +362,7 @@ impl fmt::Display for Pkh { } impl Liftable for Pkh { - fn lift(&self) -> Result, Error> { - Ok(r#abstract::Policy::Key(self.pk.clone())) - } + fn lift(&self) -> Result, Error> { Ok(Abstract::Key(self.pk.clone())) } } impl_from_tree!( diff --git a/src/descriptor/segwitv0.rs b/src/descriptor/segwitv0.rs index 128455ac6..24f99f4c9 100644 --- a/src/descriptor/segwitv0.rs +++ b/src/descriptor/segwitv0.rs @@ -16,8 +16,8 @@ use crate::expression::{self, FromTree}; use crate::miniscript::context::{ScriptContext, ScriptContextError}; use crate::miniscript::satisfy::{Placeholder, Satisfaction, Witness}; use crate::plan::AssetProvider; -use crate::r#abstract::{self, Liftable}; use crate::prelude::*; +use crate::r#abstract::{Abstract, Liftable}; use crate::util::varint_len; use crate::{ Error, ForEachKey, Miniscript, MiniscriptKey, Satisfier, Segwitv0, ToPublicKey, TranslateErr, @@ -220,7 +220,7 @@ pub enum WshInner { } impl Liftable for Wsh { - fn lift(&self) -> Result, Error> { + fn lift(&self) -> Result, Error> { match self.inner { WshInner::SortedMulti(ref smv) => smv.lift(), WshInner::Ms(ref ms) => ms.lift(), @@ -469,9 +469,7 @@ impl fmt::Display for Wpkh { } impl Liftable for Wpkh { - fn lift(&self) -> Result, Error> { - Ok(r#abstract::Policy::Key(self.pk.clone())) - } + fn lift(&self) -> Result, Error> { Ok(Abstract::Key(self.pk.clone())) } } impl_from_tree!( diff --git a/src/descriptor/sh.rs b/src/descriptor/sh.rs index 57829dd0c..6496a96da 100644 --- a/src/descriptor/sh.rs +++ b/src/descriptor/sh.rs @@ -20,8 +20,8 @@ use crate::expression::{self, FromTree}; use crate::miniscript::context::ScriptContext; use crate::miniscript::satisfy::{Placeholder, Satisfaction}; use crate::plan::AssetProvider; -use crate::r#abstract::{self, Liftable}; use crate::prelude::*; +use crate::r#abstract::{Abstract, Liftable}; use crate::util::{varint_len, witness_to_scriptsig}; use crate::{ push_opcode_size, Error, ForEachKey, Legacy, Miniscript, MiniscriptKey, Satisfier, Segwitv0, @@ -49,10 +49,10 @@ pub enum ShInner { } impl Liftable for Sh { - fn lift(&self) -> Result, Error> { + fn lift(&self) -> Result, Error> { match self.inner { ShInner::Wsh(ref wsh) => wsh.lift(), - ShInner::Wpkh(ref pk) => Ok(r#abstract::Policy::Key(pk.as_inner().clone())), + ShInner::Wpkh(ref pk) => Ok(Abstract::Key(pk.as_inner().clone())), ShInner::SortedMulti(ref smv) => smv.lift(), ShInner::Ms(ref ms) => ms.lift(), } diff --git a/src/descriptor/sortedmulti.rs b/src/descriptor/sortedmulti.rs index 27ffa4853..993837ca4 100644 --- a/src/descriptor/sortedmulti.rs +++ b/src/descriptor/sortedmulti.rs @@ -16,8 +16,8 @@ use crate::miniscript::decode::Terminal; use crate::miniscript::limits::MAX_PUBKEYS_PER_MULTISIG; use crate::miniscript::satisfy::{Placeholder, Satisfaction}; use crate::plan::AssetProvider; -use crate::r#abstract; use crate::prelude::*; +use crate::r#abstract::{Abstract, Liftable}; use crate::{ errstr, expression, script_num_size, Error, ForEachKey, Miniscript, MiniscriptKey, Satisfier, ToPublicKey, TranslateErr, Translator, @@ -196,16 +196,11 @@ impl SortedMultiVec { pub fn max_satisfaction_size(&self) -> usize { 1 + 73 * self.k } } -impl crate::r#abstract::Liftable - for SortedMultiVec -{ - fn lift(&self) -> Result, Error> { - let ret = r#abstract::Policy::Threshold( +impl Liftable for SortedMultiVec { + fn lift(&self) -> Result, Error> { + let ret = Abstract::Threshold( self.k, - self.pks - .iter() - .map(|k| r#abstract::Policy::Key(k.clone())) - .collect(), + self.pks.iter().map(|k| Abstract::Key(k.clone())).collect(), ); Ok(ret) } diff --git a/src/descriptor/tr.rs b/src/descriptor/tr.rs index ecc2ec2f1..ed94fdec0 100644 --- a/src/descriptor/tr.rs +++ b/src/descriptor/tr.rs @@ -16,9 +16,8 @@ use crate::expression::{self, FromTree}; use crate::miniscript::satisfy::{Placeholder, Satisfaction, SchnorrSigType, Witness}; use crate::miniscript::Miniscript; use crate::plan::AssetProvider; -use crate::r#abstract::Policy as Abstract; use crate::prelude::*; -use crate::r#abstract::Liftable; +use crate::r#abstract::{Abstract, Liftable}; use crate::util::{varint_len, witness_size}; use crate::{ errstr, Error, ForEachKey, MiniscriptKey, Satisfier, ScriptContext, Tap, ToPublicKey, @@ -635,9 +634,10 @@ impl Liftable for TapTree { impl Liftable for Tr { fn lift(&self) -> Result, Error> { match &self.tree { - Some(root) => { - Ok(Abstract::Threshold(1, vec![Abstract::Key(self.internal_key.clone()), root.lift()?])) - } + Some(root) => Ok(Abstract::Threshold( + 1, + vec![Abstract::Key(self.internal_key.clone()), root.lift()?], + )), None => Ok(Abstract::Key(self.internal_key.clone())), } } diff --git a/src/policy/concrete.rs b/src/policy/concrete.rs index acf24e391..1fa800141 100644 --- a/src/policy/concrete.rs +++ b/src/policy/concrete.rs @@ -12,7 +12,7 @@ use bitcoin::{absolute, Sequence}; use { crate::descriptor::TapTree, crate::miniscript::ScriptContext, crate::policy::compiler, crate::policy::compiler::CompilerError, crate::policy::compiler::OrdF64, - crate::policy::concrete::Policy as Concrete, crate::r#abstract::Policy as Abstract, + crate::policy::concrete::Policy as Concrete, crate::r#abstract::Abstract, crate::r#abstract::Liftable, crate::Descriptor, crate::Miniscript, crate::Tap, core::cmp::Reverse, }; @@ -553,7 +553,7 @@ impl ForEachKey for Policy { impl Policy { /// Converts a policy using one kind of public key to another type of public key. /// - /// For example usage please see [`crate::abstract::Policy::translate_pk`]. + /// For example usage please see [`crate::abstract::Abstract::translate_pk`]. pub fn translate_pk(&self, t: &mut T) -> Result, E> where T: Translator,