2121//! The format represents EC public keys abstractly to allow wallets to replace
2222//! these with BIP32 paths, pay-to-contract instructions, etc.
2323//!
24+ use { error, fmt} ;
2425
2526#[ cfg( feature = "compiler" ) ]
2627pub mod compiler;
@@ -43,22 +44,78 @@ const ENTAILMENT_MAX_TERMINALS: usize = 20;
4344/// Trait describing script representations which can be lifted into
4445/// an abstract policy, by discarding information.
4546/// After Lifting all policies are converted into `KeyHash(Pk::HasH)` to
46- /// maintain the following invariant:
47+ /// maintain the following invariant(modulo resource limits) :
4748/// `Lift(Concrete) == Concrete -> Miniscript -> Script -> Miniscript -> Semantic`
49+ /// Lifting from [miniscript.Miniscript], [descriptor.Descriptor] can fail
50+ /// if the miniscript contains a timelock combination or if it contains a
51+ /// branch that exceeds resource limits.
52+ /// Lifting from Concrete policies can fail if it contains a timelock
53+ /// combination. It is possible that concrete policy has some branches that
54+ /// exceed resource limits for any compilation, but cannot detect such
55+ /// policies while lifting. Note that our compiler would not succeed for any
56+ /// such policies.
4857pub trait Liftable < Pk : MiniscriptKey > {
4958 /// Convert the object into an abstract policy
5059 fn lift ( & self ) -> Result < Semantic < Pk > , Error > ;
5160}
5261
62+ /// Detailed Error type for Policies
63+ #[ derive( Copy , Clone , PartialEq , Eq , Debug ) ]
64+ pub enum LiftError {
65+ /// Cannot lift policies that have
66+ /// a combination of height and timelocks.
67+ HeightTimeLockCombination ,
68+ /// Duplicate Public Keys
69+ BranchExceedResourceLimits ,
70+ }
71+
72+ impl error:: Error for LiftError {
73+ fn description ( & self ) -> & str {
74+ ""
75+ }
76+ fn cause ( & self ) -> Option < & error:: Error > {
77+ None
78+ }
79+ }
80+
81+ impl fmt:: Display for LiftError {
82+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
83+ match * self {
84+ LiftError :: HeightTimeLockCombination => {
85+ f. write_str ( "Cannot lift policies that have a heightlock and timelock combination" )
86+ }
87+ LiftError :: BranchExceedResourceLimits => f. write_str (
88+ "Cannot lift policies containing one branch that exceeds resource limits" ,
89+ ) ,
90+ }
91+ }
92+ }
93+
94+ impl < Pk : MiniscriptKey , Ctx : ScriptContext > Miniscript < Pk , Ctx > {
95+ /// Lifting corresponds conversion of miniscript into Policy
96+ /// [policy.semantic.Policy] for human readable or machine analysis.
97+ /// However, naively lifting miniscripts can result in incorrect
98+ /// interpretations that don't correspond underlying semantics when
99+ /// we try to spend them on bitcoin network.
100+ /// This can occur if the miniscript contains a
101+ /// 1. Timelock combination
102+ /// 2. Contains a spend that exceeds resource limits
103+ pub fn lift_check ( & self ) -> Result < ( ) , LiftError > {
104+ if !self . within_resource_limits ( ) {
105+ Err ( LiftError :: BranchExceedResourceLimits )
106+ } else if self . has_mixed_timelocks ( ) {
107+ Err ( LiftError :: HeightTimeLockCombination )
108+ } else {
109+ Ok ( ( ) )
110+ }
111+ }
112+ }
113+
53114impl < Pk : MiniscriptKey , Ctx : ScriptContext > Liftable < Pk > for Miniscript < Pk , Ctx > {
54115 fn lift ( & self ) -> Result < Semantic < Pk > , Error > {
55116 // check whether the root miniscript can have a spending path that is
56117 // a combination of heightlock and timelock
57- if self . ext . timelock_info . contains_unspendable_path ( ) {
58- return Err ( Error :: PolicyError (
59- concrete:: PolicyError :: HeightTimeLockCombination ,
60- ) ) ;
61- }
118+ self . lift_check ( ) ?;
62119 self . as_inner ( ) . lift ( )
63120 }
64121}
0 commit comments