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<String, Segwitv0>; -type Policy = policy::Concrete<String>; +type Policy = policy::concrete::Policy<String>; 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<String>; +type Policy = policy::concrete::Policy<String>; 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..0e309d163 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::Semantic<String>; +type Policy = miniscript::r#abstract::Abstract<String>; 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..6c68a5bec --- /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::r#abstract::Abstract; +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 -> Abstract` +/// +/// 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<Pk: MiniscriptKey> { + /// Converts this object into an abstract policy. + fn lift(&self) -> Result<Abstract<Pk>, 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<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> { + /// 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 + /// 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<Pk: MiniscriptKey, Ctx: ScriptContext> Liftable<Pk> for Miniscript<Pk, Ctx> { + fn lift(&self) -> Result<Abstract<Pk>, 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<Pk: MiniscriptKey, Ctx: ScriptContext> Liftable<Pk> for Terminal<Pk, Ctx> { + fn lift(&self) -> Result<Abstract<Pk>, Error> { + let ret = match *self { + 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) => 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) + | 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) => { + Abstract::Threshold(2, vec![left.node.lift()?, right.node.lift()?]) + } + Terminal::AndOr(ref a, ref b, ref c) => Abstract::Threshold( + 1, + vec![ + Abstract::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) => { + 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(); + Abstract::Threshold(k, semantic_subs?) + } + Terminal::Multi(k, ref keys) | Terminal::MultiA(k, ref keys) => { + Abstract::Threshold(k, keys.iter().map(|k| Abstract::Key(k.clone())).collect()) + } + } + .normalized(); + Ok(ret) + } +} + +impl<Pk: MiniscriptKey> Liftable<Pk> for Descriptor<Pk> { + fn lift(&self) -> Result<Abstract<Pk>, 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<Pk: MiniscriptKey> Liftable<Pk> for Abstract<Pk> { + fn lift(&self) -> Result<Abstract<Pk>, Error> { Ok(self.clone()) } +} + +impl<Pk: MiniscriptKey> Liftable<Pk> for Concrete<Pk> { + fn lift(&self) -> Result<Abstract<Pk>, 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 => 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(); + Abstract::Threshold(2, semantic_subs?) + } + Concrete::Or(ref subs) => { + let semantic_subs: Result<_, Error> = + subs.iter().map(|(_p, sub)| sub.lift()).collect(); + Abstract::Threshold(1, semantic_subs?) + } + Concrete::Threshold(k, ref subs) => { + let semantic_subs: Result<_, Error> = subs.iter().map(Liftable::lift).collect(); + Abstract::Threshold(k, semantic_subs?) + } + } + .normalized(); + Ok(ret) + } +} +impl<Pk: MiniscriptKey> Liftable<Pk> for Arc<Concrete<Pk>> { + fn lift(&self) -> Result<Abstract<Pk>, 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<String>; + type AbstractPol = crate::r#abstract::Abstract<String>; + + 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 = AbstractPol::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!(AbstractPol::from_str("thresh(0)").is_err()); + assert!(AbstractPol::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<k<=n" + ); + assert_eq!( + ConcretePol::from_str("thresh(2,pk(),thresh(0,pk()))") + .unwrap_err() + .to_string(), + "Threshold k must be greater than 0 and less than or equal to n 0<k<=n" + ); + assert_eq!( + ConcretePol::from_str("and(pk())").unwrap_err().to_string(), + "And policy fragment must take 2 arguments" + ); + assert_eq!( + ConcretePol::from_str("or(pk())").unwrap_err().to_string(), + "Or policy fragment must take 2 arguments" + ); + assert_eq!( + ConcretePol::from_str("thresh(3,after(0),pk(),pk())") + .unwrap_err() + .to_string(), + "Time must be greater than 0; n > 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<bitcoin::PublicKey, Segwitv0> = + format!("andor(multi(1,{}),older(42),c:pk_k({}))", key_a.inner, key_b.inner) + .parse() + .unwrap(); + assert_eq!( + Abstract::Threshold( + 1, + vec![ + Abstract::Threshold( + 2, + vec![ + Abstract::Key(key_a), + Abstract::Older(Sequence::from_height(42)) + ] + ), + Abstract::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<String> = 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<String, Tap> = ms_str!("multi_a(2,A,B,C,D)"); + let tree: TapTree<String> = 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<String> = 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<Miniscript<String, Tap>> = + Arc::new(ms_str!("and_v(v:pk(C),pk(D))")); + let right_ms_compilation: Arc<Miniscript<String, Tap>> = + 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<String> = 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<String> = 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::<Vec<_>>(); + 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::<Vec<_>>(); + + // Generate TapTree leaves compilations from the given sub-policies + let node_compilations = sorted_policies + .into_iter() + .map(|x| { + let leaf_policy: Concrete<String> = policy_str!("{}", x); + TapTree::Leaf(Arc::from(leaf_policy.compile::<Tap>().unwrap())) + }) + .collect::<Vec<_>>(); + + // 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::<String>::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::<String>::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::<String>::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::<String>::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/policy/semantic.rs b/src/abstract/mod.rs similarity index 63% rename from src/policy/semantic.rs rename to src/abstract/mod.rs index 9636c0d2a..c64e2e3f9 100644 --- a/src/policy/semantic.rs +++ b/src/abstract/mod.rs @@ -1,28 +1,30 @@ // SPDX-License-Identifier: CC0-1.0 //! Abstract Policies -//! -//! We use the terms "semantic" and "abstract" interchangeably because -//! "abstract" is a reserved keyword in Rust. + +mod lift; + +#[rustfmt::skip] // Keep public re-exports separate. +pub use self::lift::{LiftError, Liftable}; use core::str::FromStr; 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}; /// 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)] -pub enum Policy<Pk: MiniscriptKey> { +pub enum Abstract<Pk: MiniscriptKey> { /// Unsatisfiable. Unsatisfiable, /// Trivially satisfiable. @@ -42,44 +44,44 @@ pub enum Policy<Pk: MiniscriptKey> { /// 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<Policy<Pk>>), + Threshold(usize, Vec<Abstract<Pk>>), } -impl<Pk> Policy<Pk> +impl<Pk> Abstract<Pk> 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<Pk> { - 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<Pk> { + 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<Pk> { Policy::Older(Sequence::from_consensus(n)) } + /// Helper function equivalent to `Abstract::Older(Sequence::from_consensus(n))`. + pub fn older(n: u32) -> Abstract<Pk> { Abstract::Older(Sequence::from_consensus(n)) } } -impl<Pk: MiniscriptKey> ForEachKey<Pk> for Policy<Pk> { +impl<Pk: MiniscriptKey> ForEachKey<Pk> for Abstract<Pk> { fn for_each_key<'a, F: FnMut(&'a Pk) -> bool>(&'a self, mut pred: F) -> bool { self.real_for_each_key(&mut pred) } } -impl<Pk: MiniscriptKey> Policy<Pk> { +impl<Pk: MiniscriptKey> Abstract<Pk> { 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)) } } @@ -93,10 +95,10 @@ impl<Pk: MiniscriptKey> Policy<Pk> { /// 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, r#abstract::Abstract, Translator}; /// let alice_pk = "02c79ef3ede6d14f72a00d0e49b4becfb152197b64c0707425c4f231df29500ee7"; /// let bob_pk = "03d008a849fbf474bd17e9d2c1a827077a468150e58221582ec3410ab309f5afe4"; - /// let placeholder_policy = Policy::<String>::from_str("and(pk(alice_pk),pk(bob_pk))").unwrap(); + /// let placeholder_policy = Abstract::<String>::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. @@ -123,10 +125,10 @@ impl<Pk: MiniscriptKey> Policy<Pk> { /// /// 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<Q, E, T>(&self, t: &mut T) -> Result<Policy<Q>, E> + pub fn translate_pk<Q, E, T>(&self, t: &mut T) -> Result<Abstract<Q>, E> where T: Translator<Pk, Q, E>, Q: MiniscriptKey, @@ -134,25 +136,25 @@ impl<Pk: MiniscriptKey> Policy<Pk> { self._translate_pk(t) } - fn _translate_pk<Q, E, T>(&self, t: &mut T) -> Result<Policy<Q>, E> + fn _translate_pk<Q, E, T>(&self, t: &mut T) -> Result<Abstract<Q>, E> where T: Translator<Pk, Q, E>, 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<Vec<Policy<Q>>, _> = + 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<Vec<Abstract<Q>>, _> = 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)) } } } @@ -165,15 +167,15 @@ impl<Pk: MiniscriptKey> Policy<Pk> { /// 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<Pk>) -> Result<bool, PolicyError> { + pub fn entails(self, other: Abstract<Pk>) -> Result<bool, PolicyError> { 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(); @@ -185,7 +187,7 @@ impl<Pk: MiniscriptKey> Policy<Pk> { 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)?) } } } @@ -193,8 +195,8 @@ impl<Pk: MiniscriptKey> Policy<Pk> { // 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, } } @@ -202,10 +204,10 @@ impl<Pk: MiniscriptKey> Policy<Pk> { // 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<Pk> { + fn first_constraint(&self) -> Abstract<Pk> { 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(), } } @@ -214,26 +216,30 @@ impl<Pk: MiniscriptKey> Policy<Pk> { // 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<Pk>, available: bool) -> Policy<Pk> { + pub(crate) fn satisfy_constraint( + self, + witness: &Abstract<Pk>, + available: bool, + ) -> Abstract<Pk> { 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, @@ -242,19 +248,19 @@ impl<Pk: MiniscriptKey> Policy<Pk> { } } -impl<Pk: MiniscriptKey> fmt::Debug for Policy<Pk> { +impl<Pk: MiniscriptKey> fmt::Debug for Abstract<Pk> { 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 { @@ -275,19 +281,19 @@ impl<Pk: MiniscriptKey> fmt::Debug for Policy<Pk> { } } -impl<Pk: MiniscriptKey> fmt::Display for Policy<Pk> { +impl<Pk: MiniscriptKey> fmt::Display for Abstract<Pk> { 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 { @@ -309,9 +315,9 @@ impl<Pk: MiniscriptKey> fmt::Display for Policy<Pk> { } impl_from_str!( - Policy<Pk>, + Abstract<Pk>, type Err = Error;, - fn from_str(s: &str) -> Result<Policy<Pk>, Error> { + fn from_str(s: &str) -> Result<Abstract<Pk>, Error> { expression::check_valid_chars(s)?; let tree = expression::Tree::from_str(s)?; @@ -319,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<Pk>, - fn from_tree(top: &expression::Tree) -> Result<Policy<Pk>, Error> { + Abstract<Pk>, + fn from_tree(top: &expression::Tree) -> Result<Abstract<Pk>, 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 { @@ -352,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 { @@ -362,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 { @@ -380,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( - "Semantic 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) { @@ -389,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<Pk: MiniscriptKey> Policy<Pk> { +impl<Pk: MiniscriptKey> Abstract<Pk> { /// 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<Pk> { + pub fn normalized(self) -> Abstract<Pk> { 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 @@ -420,16 +428,16 @@ impl<Pk: MiniscriptKey> Policy<Pk> { 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), @@ -437,17 +445,17 @@ impl<Pk: MiniscriptKey> Policy<Pk> { } // 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, @@ -456,31 +464,31 @@ impl<Pk: MiniscriptKey> Policy<Pk> { /// 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<u32> { 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 }), @@ -499,16 +507,16 @@ impl<Pk: MiniscriptKey> Policy<Pk> { /// Helper function for recursion in `absolute timelocks` fn real_absolute_timelocks(&self) -> Vec<u32> { 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 }), @@ -526,20 +534,20 @@ impl<Pk: MiniscriptKey> Policy<Pk> { /// 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<Pk> { + pub fn at_age(mut self, age: Sequence) -> Abstract<Pk> { 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, }; @@ -548,11 +556,11 @@ impl<Pk: MiniscriptKey> Policy<Pk> { /// 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<Pk> { + pub fn at_lock_time(mut self, n: absolute::LockTime) -> Abstract<Pk> { 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, @@ -560,13 +568,13 @@ impl<Pk: MiniscriptKey> Policy<Pk> { _ => 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, }; @@ -577,15 +585,15 @@ impl<Pk: MiniscriptKey> Policy<Pk> { /// 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::<usize>(), + 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::<usize>(), } } @@ -597,18 +605,18 @@ impl<Pk: MiniscriptKey> Policy<Pk> { /// Returns `None` if the policy is not satisfiable. pub fn minimum_n_keys(&self) -> Option<usize> { 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<usize> = - 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 @@ -621,18 +629,18 @@ impl<Pk: MiniscriptKey> Policy<Pk> { } } -impl<Pk: MiniscriptKey> Policy<Pk> { +impl<Pk: MiniscriptKey> Abstract<Pk> { /// "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<Pk> { + pub fn sorted(self) -> Abstract<Pk> { 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, } @@ -647,25 +655,25 @@ mod tests { use super::*; - type StringPolicy = Policy<String>; + type StringAbstract = Abstract<String>; #[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::<PublicKey>::from_str("pk()").is_err()); - assert!(Policy::<PublicKey>::from_str( + assert!(Abstract::<PublicKey>::from_str("pk()").is_err()); + assert!(Abstract::<PublicKey>::from_str( "pk(\ 0200000000000000000000000000000000000002\ )" ) .is_err()); - assert!(Policy::<PublicKey>::from_str( + assert!(Abstract::<PublicKey>::from_str( "pk(\ 02c79ef3ede6d14f72a00d0e49b4becfb152197b64c0707425c4f231df29500ee7\ )" @@ -675,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); @@ -684,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)), @@ -718,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)\ )", @@ -746,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)), ] ) ); @@ -762,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\ )", @@ -770,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, ] ) ); @@ -789,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 @@ -817,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 @@ -879,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()); } @@ -905,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 @@ -925,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 )) @@ -934,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 @@ -951,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 c4ca14b14..b20b6205f 100644 --- a/src/descriptor/bare.rs +++ b/src/descriptor/bare.rs @@ -18,8 +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::{semantic, Liftable}; use crate::prelude::*; +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<Pk: MiniscriptKey> fmt::Display for Bare<Pk> { } impl<Pk: MiniscriptKey> Liftable<Pk> for Bare<Pk> { - fn lift(&self) -> Result<semantic::Policy<Pk>, Error> { self.ms.lift() } + fn lift(&self) -> Result<Abstract<Pk>, Error> { self.ms.lift() } } impl_from_tree!( @@ -362,9 +362,7 @@ impl<Pk: MiniscriptKey> fmt::Display for Pkh<Pk> { } impl<Pk: MiniscriptKey> Liftable<Pk> for Pkh<Pk> { - fn lift(&self) -> Result<semantic::Policy<Pk>, Error> { - Ok(semantic::Policy::Key(self.pk.clone())) - } + fn lift(&self) -> Result<Abstract<Pk>, Error> { Ok(Abstract::Key(self.pk.clone())) } } impl_from_tree!( 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/descriptor/segwitv0.rs b/src/descriptor/segwitv0.rs index 0cc665468..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::policy::{semantic, 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<Pk: MiniscriptKey> { } impl<Pk: MiniscriptKey> Liftable<Pk> for Wsh<Pk> { - fn lift(&self) -> Result<semantic::Policy<Pk>, Error> { + fn lift(&self) -> Result<Abstract<Pk>, Error> { match self.inner { WshInner::SortedMulti(ref smv) => smv.lift(), WshInner::Ms(ref ms) => ms.lift(), @@ -469,9 +469,7 @@ impl<Pk: MiniscriptKey> fmt::Display for Wpkh<Pk> { } impl<Pk: MiniscriptKey> Liftable<Pk> for Wpkh<Pk> { - fn lift(&self) -> Result<semantic::Policy<Pk>, Error> { - Ok(semantic::Policy::Key(self.pk.clone())) - } + fn lift(&self) -> Result<Abstract<Pk>, Error> { Ok(Abstract::Key(self.pk.clone())) } } impl_from_tree!( diff --git a/src/descriptor/sh.rs b/src/descriptor/sh.rs index 53b4107ce..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::policy::{semantic, 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<Pk: MiniscriptKey> { } impl<Pk: MiniscriptKey> Liftable<Pk> for Sh<Pk> { - fn lift(&self) -> Result<semantic::Policy<Pk>, Error> { + fn lift(&self) -> Result<Abstract<Pk>, 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(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 3bd5fa8e0..993837ca4 100644 --- a/src/descriptor/sortedmulti.rs +++ b/src/descriptor/sortedmulti.rs @@ -17,9 +17,10 @@ use crate::miniscript::limits::MAX_PUBKEYS_PER_MULTISIG; use crate::miniscript::satisfy::{Placeholder, Satisfaction}; use crate::plan::AssetProvider; use crate::prelude::*; +use crate::r#abstract::{Abstract, Liftable}; 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 @@ -195,14 +196,11 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiVec<Pk, Ctx> { pub fn max_satisfaction_size(&self) -> usize { 1 + 73 * self.k } } -impl<Pk: MiniscriptKey, Ctx: ScriptContext> policy::Liftable<Pk> for SortedMultiVec<Pk, Ctx> { - fn lift(&self) -> Result<policy::semantic::Policy<Pk>, Error> { - let ret = policy::semantic::Policy::Threshold( +impl<Pk: MiniscriptKey, Ctx: ScriptContext> Liftable<Pk> for SortedMultiVec<Pk, Ctx> { + fn lift(&self) -> Result<Abstract<Pk>, Error> { + let ret = Abstract::Threshold( self.k, - self.pks - .iter() - .map(|k| policy::semantic::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 48deb63e0..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::policy::semantic::Policy; -use crate::policy::Liftable; use crate::prelude::*; +use crate::r#abstract::{Abstract, Liftable}; use crate::util::{varint_len, witness_size}; use crate::{ errstr, Error, ForEachKey, MiniscriptKey, Satisfier, ScriptContext, Tap, ToPublicKey, @@ -617,11 +616,11 @@ fn split_once(inp: &str, delim: char) -> Option<(&str, &str)> { } impl<Pk: MiniscriptKey> Liftable<Pk> for TapTree<Pk> { - fn lift(&self) -> Result<Policy<Pk>, Error> { - fn lift_helper<Pk: MiniscriptKey>(s: &TapTree<Pk>) -> Result<Policy<Pk>, Error> { + fn lift(&self) -> Result<Abstract<Pk>, Error> { + fn lift_helper<Pk: MiniscriptKey>(s: &TapTree<Pk>) -> Result<Abstract<Pk>, 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 +632,13 @@ impl<Pk: MiniscriptKey> Liftable<Pk> for TapTree<Pk> { } impl<Pk: MiniscriptKey> Liftable<Pk> for Tr<Pk> { - fn lift(&self) -> Result<Policy<Pk>, Error> { + fn lift(&self) -> Result<Abstract<Pk>, Error> { match &self.tree { - Some(root) => { - Ok(Policy::Threshold(1, vec![Policy::Key(self.internal_key.clone()), root.lift()?])) - } - None => Ok(Policy::Key(self.internal_key.clone())), + 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/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/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<Pk: MiniscriptKey, Ctx: ScriptContext> TreeLike for Arc<Miniscript<Pk, Ctx> } } -impl<'a, Pk: MiniscriptKey> TreeLike for &'a policy::Concrete<Pk> { +impl<'a, Pk: MiniscriptKey> TreeLike for &'a policy::concrete::Policy<Pk> { fn as_node(&self) -> Tree<Self> { - 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<Pk> { } } -impl<'a, Pk: MiniscriptKey> TreeLike for Arc<policy::Concrete<Pk>> { +impl<'a, Pk: MiniscriptKey> TreeLike for Arc<policy::concrete::Policy<Pk>> { fn as_node(&self) -> Tree<Self> { - 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<policy::LiftError> for Error { - fn from(e: policy::LiftError) -> Error { Error::LiftError(e) } +impl From<crate::r#abstract::LiftError> 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 28349e427..8ed29f6c7 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::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<String>; diff --git a/src/policy/concrete.rs b/src/policy/concrete.rs index 23305087d..1fa800141 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::r#abstract::Abstract, + crate::r#abstract::Liftable, crate::Descriptor, crate::Miniscript, crate::Tap, core::cmp::Reverse, }; @@ -98,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, @@ -140,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) @@ -238,8 +234,8 @@ impl<Pk: MiniscriptKey> Policy<Pk> { 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) => { @@ -557,7 +553,7 @@ impl<Pk: MiniscriptKey> ForEachKey<Pk> for Policy<Pk> { impl<Pk: MiniscriptKey> Policy<Pk> { /// 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::abstract::Abstract::translate_pk`]. pub fn translate_pk<Q, E, T>(&self, t: &mut T) -> Result<Policy<Q>, E> where T: Translator<Pk, Q, E>, diff --git a/src/policy/mod.rs b/src/policy/mod.rs index 138ec45b7..4583addb6 100644 --- a/src/policy/mod.rs +++ b/src/policy/mod.rs @@ -9,553 +9,10 @@ //! 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; #[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; -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; - -/// 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<Pk: MiniscriptKey> { - /// Converts this object into an abstract policy. - fn lift(&self) -> Result<Semantic<Pk>, 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<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> { - /// 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<Pk: MiniscriptKey, Ctx: ScriptContext> Liftable<Pk> for Miniscript<Pk, Ctx> { - fn lift(&self) -> Result<Semantic<Pk>, 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<Pk: MiniscriptKey, Ctx: ScriptContext> Liftable<Pk> for Terminal<Pk, Ctx> { - fn lift(&self) -> Result<Semantic<Pk>, 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<Pk: MiniscriptKey> Liftable<Pk> for Descriptor<Pk> { - fn lift(&self) -> Result<Semantic<Pk>, 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<Pk: MiniscriptKey> Liftable<Pk> for Semantic<Pk> { - fn lift(&self) -> Result<Semantic<Pk>, Error> { Ok(self.clone()) } -} - -impl<Pk: MiniscriptKey> Liftable<Pk> for Concrete<Pk> { - fn lift(&self) -> Result<Semantic<Pk>, 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<Pk: MiniscriptKey> Liftable<Pk> for Arc<Concrete<Pk>> { - fn lift(&self) -> Result<Semantic<Pk>, Error> { self.as_ref().lift() } -} - -#[cfg(test)] -mod tests { - use core::str::FromStr; - - use bitcoin::Sequence; - #[cfg(feature = "compiler")] - use sync::Arc; - - use super::super::miniscript::context::Segwitv0; - use super::super::miniscript::Miniscript; - use super::{Concrete, Liftable, Semantic}; - #[cfg(feature = "compiler")] - use crate::descriptor::Tr; - use crate::prelude::*; - #[cfg(feature = "compiler")] - use crate::{descriptor::TapTree, Descriptor, Tap}; - - type ConcretePol = Concrete<String>; - type SemanticPol = Semantic<String>; - - 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<k<=n" - ); - assert_eq!( - ConcretePol::from_str("thresh(2,pk(),thresh(0,pk()))") - .unwrap_err() - .to_string(), - "Threshold k must be greater than 0 and less than or equal to n 0<k<=n" - ); - assert_eq!( - ConcretePol::from_str("and(pk())").unwrap_err().to_string(), - "And policy fragment must take 2 arguments" - ); - assert_eq!( - ConcretePol::from_str("or(pk())").unwrap_err().to_string(), - "Or policy fragment must take 2 arguments" - ); - assert_eq!( - ConcretePol::from_str("thresh(3,after(0),pk(),pk())") - .unwrap_err() - .to_string(), - "Time must be greater than 0; n > 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<bitcoin::PublicKey, Segwitv0> = - 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<String> = 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<String, Tap> = ms_str!("multi_a(2,A,B,C,D)"); - let tree: TapTree<String> = 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<String> = 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<Miniscript<String, Tap>> = - Arc::new(ms_str!("and_v(v:pk(C),pk(D))")); - let right_ms_compilation: Arc<Miniscript<String, Tap>> = - 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<String> = 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<String> = 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::<Vec<_>>(); - 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::<Vec<_>>(); - - // Generate TapTree leaves compilations from the given sub-policies - let node_compilations = sorted_policies - .into_iter() - .map(|x| { - let leaf_policy: Concrete<String> = policy_str!("{}", x); - TapTree::Leaf(Arc::from(leaf_policy.compile::<Tap>().unwrap())) - }) - .collect::<Vec<_>>(); - - // 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::<String>::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::<String>::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::<String>::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::<String>::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<Descriptor<String>, Error>; - - #[bench] - pub fn compile_large_tap(bh: &mut Bencher) { - let pol = Concrete::<String>::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(); - }); - } -} +pub(crate) const ENTAILMENT_MAX_TERMINALS: usize = 20;