Skip to content

Commit b342833

Browse files
authored
Merge pull request #199 from sanket1729/lift_checks
lift failure bug fixes
2 parents 2ff9f11 + e7eeb6c commit b342833

File tree

8 files changed

+82
-60
lines changed

8 files changed

+82
-60
lines changed

src/descriptor/mod.rs

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -268,15 +268,7 @@ impl fmt::Display for DescriptorKeyParseError {
268268
}
269269
}
270270

271-
impl error::Error for DescriptorKeyParseError {
272-
fn description(&self) -> &str {
273-
""
274-
}
275-
276-
fn cause(&self) -> Option<&error::Error> {
277-
None
278-
}
279-
}
271+
impl error::Error for DescriptorKeyParseError {}
280272

281273
impl fmt::Display for DescriptorPublicKey {
282274
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

src/interpreter/error.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,6 @@ impl From<::Error> for Error {
101101
}
102102

103103
impl error::Error for Error {
104-
fn description(&self) -> &str {
105-
""
106-
}
107104
fn cause(&self) -> Option<&error::Error> {
108105
match *self {
109106
Error::Secp(ref err) => Some(err),

src/lib.rs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -354,15 +354,17 @@ pub enum Error {
354354
CouldNotSatisfy,
355355
/// Typechecking failed
356356
TypeCheck(String),
357-
///General error in creating descriptor
357+
/// General error in creating descriptor
358358
BadDescriptor,
359-
///Forward-secp related errors
359+
/// Forward-secp related errors
360360
Secp(bitcoin::secp256k1::Error),
361361
#[cfg(feature = "compiler")]
362-
///Compiler related errors
362+
/// Compiler related errors
363363
CompilerError(policy::compiler::CompilerError),
364-
///Errors related to policy
364+
/// Errors related to policy
365365
PolicyError(policy::concrete::PolicyError),
366+
/// Errors related to lifting
367+
LiftError(policy::LiftError),
366368
/// Forward script context related errors
367369
ContextError(miniscript::context::ScriptContextError),
368370
/// Recursion depth exceeded when parsing policy/miniscript from string
@@ -387,6 +389,13 @@ where
387389
}
388390
}
389391

392+
#[doc(hidden)]
393+
impl From<policy::LiftError> for Error {
394+
fn from(e: policy::LiftError) -> Error {
395+
Error::LiftError(e)
396+
}
397+
}
398+
390399
#[doc(hidden)]
391400
impl From<miniscript::context::ScriptContextError> for Error {
392401
fn from(e: miniscript::context::ScriptContextError) -> Error {
@@ -419,10 +428,6 @@ impl error::Error for Error {
419428
_ => None,
420429
}
421430
}
422-
423-
fn description(&self) -> &str {
424-
""
425-
}
426431
}
427432

428433
// https://github.com/sipa/miniscript/pull/5 for discussion on this number
@@ -472,6 +477,7 @@ impl fmt::Display for Error {
472477
#[cfg(feature = "compiler")]
473478
Error::CompilerError(ref e) => fmt::Display::fmt(e, f),
474479
Error::PolicyError(ref e) => fmt::Display::fmt(e, f),
480+
Error::LiftError(ref e) => fmt::Display::fmt(e, f),
475481
Error::MaxRecursiveDepthExceeded => write!(
476482
f,
477483
"Recursive depth over {} not permitted",

src/miniscript/analyzable.rs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,7 @@ impl fmt::Display for AnalysisError {
6565
}
6666
}
6767

68-
impl error::Error for AnalysisError {
69-
fn description(&self) -> &str {
70-
""
71-
}
72-
fn cause(&self) -> Option<&error::Error> {
73-
None
74-
}
75-
}
68+
impl error::Error for AnalysisError {}
7669

7770
impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
7871
/// Whether all spend paths of miniscript require a signature

src/policy/compiler.rs

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,15 +62,7 @@ pub enum CompilerError {
6262
PolicyError(policy::concrete::PolicyError),
6363
}
6464

65-
impl error::Error for CompilerError {
66-
fn cause(&self) -> Option<&error::Error> {
67-
None
68-
}
69-
70-
fn description(&self) -> &str {
71-
""
72-
}
73-
}
65+
impl error::Error for CompilerError {}
7466

7567
impl fmt::Display for CompilerError {
7668
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

src/policy/concrete.rs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -92,14 +92,7 @@ pub enum PolicyError {
9292
DuplicatePubKeys,
9393
}
9494

95-
impl error::Error for PolicyError {
96-
fn description(&self) -> &str {
97-
""
98-
}
99-
fn cause(&self) -> Option<&error::Error> {
100-
None
101-
}
102-
}
95+
impl error::Error for PolicyError {}
10396

10497
impl fmt::Display for PolicyError {
10598
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

src/policy/mod.rs

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
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")]
2627
pub 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.
4857
pub 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+
53114
impl<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
}

src/psbt/mod.rs

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -183,15 +183,7 @@ impl From<bitcoin::util::key::Error> for InputError {
183183
}
184184
}
185185

186-
impl error::Error for Error {
187-
fn cause(&self) -> Option<&error::Error> {
188-
None
189-
}
190-
191-
fn description(&self) -> &str {
192-
""
193-
}
194-
}
186+
impl error::Error for Error {}
195187

196188
impl fmt::Display for Error {
197189
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

0 commit comments

Comments
 (0)