Skip to content

Commit c317cd7

Browse files
committed
compiler: pull non-binary argument errors from PolicyError into CompilerError
The limitation that we have only binary and()s and or()s is really just a compiler limit; it's neither needed nor enforced when parsing policies, printing them, lifting them, etc etc. Maybe we _want_ to enforce this at the type level, but we don't now, and given that, these error variants are in the wrong place (and will get in the way of the refactors in the next commit).
1 parent 385234c commit c317cd7

File tree

2 files changed

+38
-28
lines changed

2 files changed

+38
-28
lines changed

src/policy/compiler.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ impl Ord for OrdF64 {
4141
/// Detailed error type for compiler.
4242
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
4343
pub enum CompilerError {
44+
/// `And` fragments only support two args.
45+
NonBinaryArgAnd,
46+
/// `Or` fragments only support two args.
47+
NonBinaryArgOr,
4448
/// Compiler has non-safe input policy.
4549
TopLevelNonSafe,
4650
/// Non-Malleable compilation does exists for the given sub-policy.
@@ -67,6 +71,12 @@ pub enum CompilerError {
6771
impl fmt::Display for CompilerError {
6872
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
6973
match *self {
74+
CompilerError::NonBinaryArgAnd => {
75+
f.write_str("And policy fragment must take 2 arguments")
76+
}
77+
CompilerError::NonBinaryArgOr => {
78+
f.write_str("Or policy fragment must take 2 arguments")
79+
}
7080
CompilerError::TopLevelNonSafe => {
7181
f.write_str("Top Level script is not safe on some spendpath")
7282
}
@@ -93,7 +103,9 @@ impl error::Error for CompilerError {
93103
use self::CompilerError::*;
94104

95105
match self {
96-
TopLevelNonSafe
106+
NonBinaryArgAnd
107+
| NonBinaryArgOr
108+
| TopLevelNonSafe
97109
| ImpossibleNonMalleableCompilation
98110
| LimitsExceeded
99111
| NoInternalKey

src/policy/concrete.rs

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,6 @@ pub enum Policy<Pk: MiniscriptKey> {
7272
/// Detailed error type for concrete policies.
7373
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
7474
pub enum PolicyError {
75-
/// `And` fragments only support two args.
76-
NonBinaryArgAnd,
77-
/// `Or` fragments only support two args.
78-
NonBinaryArgOr,
7975
/// Cannot lift policies that have a combination of height and timelocks.
8076
HeightTimelockCombination,
8177
/// Duplicate Public Keys.
@@ -100,10 +96,6 @@ pub enum DescriptorCtx<Pk> {
10096
impl fmt::Display for PolicyError {
10197
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
10298
match *self {
103-
PolicyError::NonBinaryArgAnd => {
104-
f.write_str("And policy fragment must take 2 arguments")
105-
}
106-
PolicyError::NonBinaryArgOr => f.write_str("Or policy fragment must take 2 arguments"),
10799
PolicyError::HeightTimelockCombination => {
108100
f.write_str("Cannot lift policies that have a heightlock and timelock combination")
109101
}
@@ -118,7 +110,7 @@ impl error::Error for PolicyError {
118110
use self::PolicyError::*;
119111

120112
match self {
121-
NonBinaryArgAnd | NonBinaryArgOr | HeightTimelockCombination | DuplicatePubKeys => None,
113+
HeightTimelockCombination | DuplicatePubKeys => None,
122114
}
123115
}
124116
}
@@ -157,6 +149,26 @@ impl<'p, Pk: MiniscriptKey> Iterator for TapleafProbabilityIter<'p, Pk> {
157149
}
158150

159151
impl<Pk: MiniscriptKey> Policy<Pk> {
152+
#[cfg(feature = "compiler")]
153+
fn check_binary_ops(&self) -> Result<(), CompilerError> {
154+
for policy in self.pre_order_iter() {
155+
match *policy {
156+
Policy::And(ref subs) => {
157+
if subs.len() != 2 {
158+
return Err(CompilerError::NonBinaryArgAnd);
159+
}
160+
}
161+
Policy::Or(ref subs) => {
162+
if subs.len() != 2 {
163+
return Err(CompilerError::NonBinaryArgOr);
164+
}
165+
}
166+
_ => {}
167+
}
168+
}
169+
Ok(())
170+
}
171+
160172
/// Flattens the [`Policy`] tree structure into an iterator of tuples `(leaf script, leaf probability)`
161173
/// with leaf probabilities corresponding to odds for each sub-branch in the policy.
162174
/// We calculate the probability of selecting the sub-branch at every level and calculate the
@@ -223,6 +235,7 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
223235
#[cfg(feature = "compiler")]
224236
pub fn compile_tr(&self, unspendable_key: Option<Pk>) -> Result<Descriptor<Pk>, CompilerError> {
225237
self.is_valid().map_err(CompilerError::PolicyError)?;
238+
self.check_binary_ops()?;
226239
match self.is_safe_nonmalleable() {
227240
(false, _) => Err(CompilerError::TopLevelNonSafe),
228241
(_, false) => Err(CompilerError::ImpossibleNonMalleableCompilation),
@@ -286,6 +299,7 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
286299
unspendable_key: Option<Pk>,
287300
) -> Result<Descriptor<Pk>, Error> {
288301
self.is_valid().map_err(Error::ConcretePolicy)?;
302+
self.check_binary_ops()?;
289303
match self.is_safe_nonmalleable() {
290304
(false, _) => Err(Error::from(CompilerError::TopLevelNonSafe)),
291305
(_, false) => Err(Error::from(CompilerError::ImpossibleNonMalleableCompilation)),
@@ -339,6 +353,7 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
339353
desc_ctx: DescriptorCtx<Pk>,
340354
) -> Result<Descriptor<Pk>, Error> {
341355
self.is_valid().map_err(Error::ConcretePolicy)?;
356+
self.check_binary_ops()?;
342357
match self.is_safe_nonmalleable() {
343358
(false, _) => Err(Error::from(CompilerError::TopLevelNonSafe)),
344359
(_, false) => Err(Error::from(CompilerError::ImpossibleNonMalleableCompilation)),
@@ -364,6 +379,7 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
364379
#[cfg(feature = "compiler")]
365380
pub fn compile<Ctx: ScriptContext>(&self) -> Result<Miniscript<Pk, Ctx>, CompilerError> {
366381
self.is_valid()?;
382+
self.check_binary_ops()?;
367383
match self.is_safe_nonmalleable() {
368384
(false, _) => Err(CompilerError::TopLevelNonSafe),
369385
(_, false) => Err(CompilerError::ImpossibleNonMalleableCompilation),
@@ -684,26 +700,8 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
684700
/// Validity condition also checks whether there is a possible satisfaction
685701
/// combination of timelocks and heightlocks
686702
pub fn is_valid(&self) -> Result<(), PolicyError> {
687-
use Policy::*;
688-
689703
self.check_timelocks()?;
690704
self.check_duplicate_keys()?;
691-
692-
for policy in self.pre_order_iter() {
693-
match *policy {
694-
And(ref subs) => {
695-
if subs.len() != 2 {
696-
return Err(PolicyError::NonBinaryArgAnd);
697-
}
698-
}
699-
Or(ref subs) => {
700-
if subs.len() != 2 {
701-
return Err(PolicyError::NonBinaryArgOr);
702-
}
703-
}
704-
_ => {}
705-
}
706-
}
707705
Ok(())
708706
}
709707

0 commit comments

Comments
 (0)