Skip to content

Commit ce3d3e8

Browse files
committed
expression: replace most uses of terminal
This is a big diff but it's almost entirely mechanical. Replaces the old expression::terminal method with two new `verify_terminal_parent` and `verify_terminal` methods, which do the appropriate checks and return a strongly-typed error. Does not directly eliminate any instances of errstr or Unexpected (though the next commit will), but it does eliminate a large class of them: now when you try to parse an expression with a bad number of children, e.g. and_v with 3 children, you will get an error message that says this rather than an opaque "Unexected(<<and_v>>)" or whatever you get now. It does reduce the semantic::PolicyError type to a single variant, which has no information and is only used to indicate that the entailment calculation failed. This can be replaced by an Option (and will be, in the next commit). It also eliminates some uses of concrete::PolicyError, but the variants are still used by the absurd Policy::is_valid method, so we have to keep them for now. Also, you may notice that this commit and others have a ton of calls to .map_err. I apologize for this. But when we change error types so that parsing returns a string-parsing-specific error rather than the giant Error enum, these should mostly go away.
1 parent 8423557 commit ce3d3e8

File tree

10 files changed

+248
-175
lines changed

10 files changed

+248
-175
lines changed

src/descriptor/bare.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -370,11 +370,10 @@ impl<Pk: MiniscriptKey> Liftable<Pk> for Pkh<Pk> {
370370

371371
impl<Pk: FromStrKey> FromTree for Pkh<Pk> {
372372
fn from_tree(top: &expression::Tree) -> Result<Self, Error> {
373-
let top = top
374-
.verify_toplevel("pkh", 1..=1)
375-
.map_err(From::from)
373+
let pk = top
374+
.verify_terminal_parent("pkh", "public key")
376375
.map_err(Error::Parse)?;
377-
Ok(Pkh::new(expression::terminal(top, |pk| Pk::from_str(pk))?)?)
376+
Pkh::new(pk).map_err(Error::ContextError)
378377
}
379378
}
380379

src/descriptor/segwitv0.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -484,11 +484,10 @@ impl<Pk: MiniscriptKey> Liftable<Pk> for Wpkh<Pk> {
484484

485485
impl<Pk: FromStrKey> crate::expression::FromTree for Wpkh<Pk> {
486486
fn from_tree(top: &expression::Tree) -> Result<Self, Error> {
487-
let top = top
488-
.verify_toplevel("wpkh", 1..=1)
489-
.map_err(From::from)
487+
let pk = top
488+
.verify_terminal_parent("wpkh", "public key")
490489
.map_err(Error::Parse)?;
491-
Ok(Wpkh::new(expression::terminal(top, |pk| Pk::from_str(pk))?)?)
490+
Wpkh::new(pk).map_err(Error::ContextError)
492491
}
493492
}
494493

src/descriptor/sortedmulti.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
88
use core::fmt;
99
use core::marker::PhantomData;
10-
use core::str::FromStr;
1110

1211
use bitcoin::script;
1312

13+
use crate::blanket_traits::FromStrKey;
1414
use crate::miniscript::context::ScriptContext;
1515
use crate::miniscript::decode::Terminal;
1616
use crate::miniscript::limits::MAX_PUBKEYS_PER_MULTISIG;
@@ -61,8 +61,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiVec<Pk, Ctx> {
6161
/// Parse an expression tree into a SortedMultiVec
6262
pub fn from_tree(tree: &expression::Tree) -> Result<Self, Error>
6363
where
64-
Pk: FromStr,
65-
<Pk as FromStr>::Err: fmt::Display,
64+
Pk: FromStrKey,
6665
{
6766
tree.verify_toplevel("sortedmulti", 1..)
6867
.map_err(From::from)
@@ -72,7 +71,8 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiVec<Pk, Ctx> {
7271
inner: tree
7372
.to_null_threshold()
7473
.map_err(Error::ParseThreshold)?
75-
.translate_by_index(|i| expression::terminal(&tree.args[i + 1], Pk::from_str))?,
74+
.translate_by_index(|i| tree.args[i + 1].verify_terminal("public key"))
75+
.map_err(Error::Parse)?,
7676
phantom: PhantomData,
7777
};
7878
ret.constructor_check()
@@ -230,6 +230,8 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> fmt::Display for SortedMultiVec<Pk,
230230

231231
#[cfg(test)]
232232
mod tests {
233+
use core::str::FromStr as _;
234+
233235
use bitcoin::secp256k1::PublicKey;
234236

235237
use super::*;

src/descriptor/tr.rs

+5-15
Original file line numberDiff line numberDiff line change
@@ -524,21 +524,11 @@ impl<Pk: FromStrKey> crate::expression::FromTree for Tr<Pk> {
524524
}
525525
} else if item.index == 1 {
526526
// First child of tr, which must be the internal key
527-
if !item.node.args.is_empty() {
528-
return Err(Error::Parse(ParseError::Tree(
529-
ParseTreeError::IncorrectNumberOfChildren {
530-
description: "internal key",
531-
n_children: item.node.args.len(),
532-
minimum: Some(0),
533-
maximum: Some(0),
534-
},
535-
)));
536-
}
537-
internal_key = Some(
538-
Pk::from_str(item.node.name)
539-
.map_err(ParseError::box_from_str)
540-
.map_err(Error::Parse)?,
541-
);
527+
internal_key = item
528+
.node
529+
.verify_terminal("internal key")
530+
.map_err(Error::Parse)
531+
.map(Some)?;
542532
} else {
543533
// From here on we are into the taptree.
544534
if item.n_children_yielded == 0 {

src/expression/mod.rs

+38-1
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@ use core::str::FromStr;
99
use core::{fmt, ops};
1010

1111
pub use self::error::{ParseThresholdError, ParseTreeError};
12+
use crate::blanket_traits::StaticDebugAndDisplay;
1213
use crate::descriptor::checksum::verify_checksum;
1314
use crate::iter::{self, TreeLike};
1415
use crate::prelude::*;
15-
use crate::{errstr, Error, Threshold, MAX_RECURSION_DEPTH};
16+
use crate::{errstr, Error, ParseError, Threshold, MAX_RECURSION_DEPTH};
1617

1718
/// Allowed characters are descriptor strings.
1819
pub const INPUT_CHARSET: &str = "0123456789()[],'/*abcdefgh@:$%{}IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~ijklmnopqrstuvwxyzABCDEFGH`#\"\\ ";
@@ -137,6 +138,42 @@ impl<'a> Tree<'a> {
137138
}
138139
}
139140

141+
/// Check that a tree node is a terminal (has no children).
142+
///
143+
/// If so, parse the terminal from a string and return it.
144+
///
145+
/// The `description` and `inner_description` arguments are only used to
146+
/// populate the error return, and is not validated in any way.
147+
pub fn verify_terminal<T>(&self, description: &'static str) -> Result<T, ParseError>
148+
where
149+
T: FromStr,
150+
T::Err: StaticDebugAndDisplay,
151+
{
152+
self.verify_n_children(description, 0..=0)
153+
.map_err(ParseError::Tree)?;
154+
T::from_str(self.name).map_err(ParseError::box_from_str)
155+
}
156+
157+
/// Check that a tree node has exactly one child, which is a terminal.
158+
///
159+
/// If so, parse the terminal child from a string and return it.
160+
///
161+
/// The `description` and `inner_description` arguments are only used to
162+
/// populate the error return, and is not validated in any way.
163+
pub fn verify_terminal_parent<T>(
164+
&self,
165+
description: &'static str,
166+
inner_description: &'static str,
167+
) -> Result<T, ParseError>
168+
where
169+
T: FromStr,
170+
T::Err: StaticDebugAndDisplay,
171+
{
172+
self.verify_n_children(description, 1..=1)
173+
.map_err(ParseError::Tree)?;
174+
self.args[0].verify_terminal(inner_description)
175+
}
176+
140177
/// Check that a tree node has exactly two children.
141178
///
142179
/// If so, return them.

src/lib.rs

-4
Original file line numberDiff line numberDiff line change
@@ -449,8 +449,6 @@ pub enum Error {
449449
/// Compiler related errors
450450
CompilerError(crate::policy::compiler::CompilerError),
451451
/// Errors related to policy
452-
SemanticPolicy(policy::semantic::PolicyError),
453-
/// Errors related to policy
454452
ConcretePolicy(policy::concrete::PolicyError),
455453
/// Errors related to lifting
456454
LiftError(policy::LiftError),
@@ -514,7 +512,6 @@ impl fmt::Display for Error {
514512
Error::ContextError(ref e) => fmt::Display::fmt(e, f),
515513
#[cfg(feature = "compiler")]
516514
Error::CompilerError(ref e) => fmt::Display::fmt(e, f),
517-
Error::SemanticPolicy(ref e) => fmt::Display::fmt(e, f),
518515
Error::ConcretePolicy(ref e) => fmt::Display::fmt(e, f),
519516
Error::LiftError(ref e) => fmt::Display::fmt(e, f),
520517
Error::MaxRecursiveDepthExceeded => write!(
@@ -577,7 +574,6 @@ impl std::error::Error for Error {
577574
#[cfg(feature = "compiler")]
578575
CompilerError(e) => Some(e),
579576
ConcretePolicy(e) => Some(e),
580-
SemanticPolicy(e) => Some(e),
581577
LiftError(e) => Some(e),
582578
ContextError(e) => Some(e),
583579
AnalysisError(e) => Some(e),

src/miniscript/astelem.rs

+76-51
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@
77
//! encoding in Bitcoin script, as well as a datatype. Full details
88
//! are given on the Miniscript website.
99
10-
use core::str::FromStr;
11-
12-
use bitcoin::hashes::{hash160, Hash};
10+
use bitcoin::hashes::Hash;
1311
use bitcoin::{absolute, opcodes, script};
1412
use sync::Arc;
1513

@@ -33,7 +31,7 @@ impl<Pk: FromStrKey, Ctx: ScriptContext> crate::expression::FromTree for Termina
3331
let binary =
3432
|node: &expression::Tree, name, termfn: fn(_, _) -> Self| -> Result<Self, Error> {
3533
node.verify_binary(name)
36-
.map_err(crate::ParseError::Tree)
34+
.map_err(From::from)
3735
.map_err(Error::Parse)
3836
.and_then(|(x, y)| {
3937
let x = Arc::<Miniscript<Pk, Ctx>>::from_tree(x)?;
@@ -43,70 +41,97 @@ impl<Pk: FromStrKey, Ctx: ScriptContext> crate::expression::FromTree for Termina
4341
};
4442

4543
let (frag_name, frag_wrap) = super::split_expression_name(top.name)?;
46-
let unwrapped = match (frag_name, top.args.len()) {
47-
("expr_raw_pkh", 1) => expression::terminal(&top.args[0], |x| {
48-
hash160::Hash::from_str(x).map(Terminal::RawPkH)
49-
}),
50-
("pk_k", 1) => {
51-
expression::terminal(&top.args[0], |x| Pk::from_str(x).map(Terminal::PkK))
52-
}
53-
("pk_h", 1) => {
54-
expression::terminal(&top.args[0], |x| Pk::from_str(x).map(Terminal::PkH))
55-
}
56-
("after", 1) => expression::terminal(&top.args[0], |x| {
44+
let unwrapped = match frag_name {
45+
"expr_raw_pkh" => top
46+
.verify_terminal_parent("expr_raw_pkh", "public key hash")
47+
.map(Terminal::RawPkH)
48+
.map_err(Error::Parse),
49+
"pk_k" => top
50+
.verify_terminal_parent("pk_k", "public key")
51+
.map(Terminal::PkK)
52+
.map_err(Error::Parse),
53+
"pk_h" => top
54+
.verify_terminal_parent("pk_h", "public key")
55+
.map(Terminal::PkH)
56+
.map_err(Error::Parse),
57+
"after" => expression::terminal(&top.args[0], |x| {
5758
expression::parse_num(x)
5859
.and_then(|x| AbsLockTime::from_consensus(x).map_err(Error::AbsoluteLockTime))
5960
.map(Terminal::After)
6061
}),
61-
("older", 1) => expression::terminal(&top.args[0], |x| {
62+
"older" => expression::terminal(&top.args[0], |x| {
6263
expression::parse_num(x)
6364
.and_then(|x| RelLockTime::from_consensus(x).map_err(Error::RelativeLockTime))
6465
.map(Terminal::Older)
6566
}),
66-
("sha256", 1) => expression::terminal(&top.args[0], |x| {
67-
Pk::Sha256::from_str(x).map(Terminal::Sha256)
68-
}),
69-
("hash256", 1) => expression::terminal(&top.args[0], |x| {
70-
Pk::Hash256::from_str(x).map(Terminal::Hash256)
71-
}),
72-
("ripemd160", 1) => expression::terminal(&top.args[0], |x| {
73-
Pk::Ripemd160::from_str(x).map(Terminal::Ripemd160)
74-
}),
75-
("hash160", 1) => expression::terminal(&top.args[0], |x| {
76-
Pk::Hash160::from_str(x).map(Terminal::Hash160)
77-
}),
78-
("1", 0) => Ok(Terminal::True),
79-
("0", 0) => Ok(Terminal::False),
80-
("and_v", _) => binary(top, "and_v", Terminal::AndV),
81-
("and_b", _) => binary(top, "and_b", Terminal::AndB),
82-
("and_n", 2) => Ok(Terminal::AndOr(
83-
expression::FromTree::from_tree(&top.args[0])?,
84-
expression::FromTree::from_tree(&top.args[1])?,
85-
Arc::new(Miniscript::FALSE),
86-
)),
87-
("andor", 3) => Ok(Terminal::AndOr(
88-
expression::FromTree::from_tree(&top.args[0])?,
89-
expression::FromTree::from_tree(&top.args[1])?,
90-
expression::FromTree::from_tree(&top.args[2])?,
91-
)),
92-
("or_b", _) => binary(top, "or_b", Terminal::OrB),
93-
("or_d", _) => binary(top, "or_d", Terminal::OrD),
94-
("or_c", _) => binary(top, "or_c", Terminal::OrC),
95-
("or_i", _) => binary(top, "or_i", Terminal::OrI),
96-
("thresh", _) => top
67+
"sha256" => top
68+
.verify_terminal_parent("sha256", "hash")
69+
.map(Terminal::Sha256)
70+
.map_err(Error::Parse),
71+
"hash256" => top
72+
.verify_terminal_parent("hash256", "hash")
73+
.map(Terminal::Hash256)
74+
.map_err(Error::Parse),
75+
"ripemd160" => top
76+
.verify_terminal_parent("ripemd160", "hash")
77+
.map(Terminal::Ripemd160)
78+
.map_err(Error::Parse),
79+
"hash160" => top
80+
.verify_terminal_parent("hash160", "hash")
81+
.map(Terminal::Hash160)
82+
.map_err(Error::Parse),
83+
"1" => {
84+
top.verify_n_children("1", 0..=0)
85+
.map_err(From::from)
86+
.map_err(Error::Parse)?;
87+
Ok(Terminal::True)
88+
}
89+
"0" => {
90+
top.verify_n_children("0", 0..=0)
91+
.map_err(From::from)
92+
.map_err(Error::Parse)?;
93+
Ok(Terminal::False)
94+
}
95+
"and_v" => binary(top, "and_v", Terminal::AndV),
96+
"and_b" => binary(top, "and_b", Terminal::AndB),
97+
"and_n" => {
98+
binary(top, "and_n", |x, y| Terminal::AndOr(x, y, Arc::new(Miniscript::FALSE)))
99+
}
100+
"andor" => {
101+
top.verify_n_children("andor", 3..=3)
102+
.map_err(From::from)
103+
.map_err(Error::Parse)?;
104+
let x = Arc::<Miniscript<Pk, Ctx>>::from_tree(&top.args[0])?;
105+
let y = Arc::<Miniscript<Pk, Ctx>>::from_tree(&top.args[1])?;
106+
let z = Arc::<Miniscript<Pk, Ctx>>::from_tree(&top.args[2])?;
107+
Ok(Terminal::AndOr(x, y, z))
108+
}
109+
"or_b" => binary(top, "or_b", Terminal::OrB),
110+
"or_d" => binary(top, "or_d", Terminal::OrD),
111+
"or_c" => binary(top, "or_c", Terminal::OrC),
112+
"or_i" => binary(top, "or_i", Terminal::OrI),
113+
"thresh" => top
97114
.to_null_threshold()
98115
.map_err(Error::ParseThreshold)?
99116
.translate_by_index(|i| Miniscript::from_tree(&top.args[1 + i]).map(Arc::new))
100117
.map(Terminal::Thresh),
101-
("multi", _) => top
118+
"multi" => top
102119
.to_null_threshold()
103120
.map_err(Error::ParseThreshold)?
104-
.translate_by_index(|i| expression::terminal(&top.args[1 + i], Pk::from_str))
121+
.translate_by_index(|i| {
122+
top.args[1 + i]
123+
.verify_terminal("public key")
124+
.map_err(Error::Parse)
125+
})
105126
.map(Terminal::Multi),
106-
("multi_a", _) => top
127+
"multi_a" => top
107128
.to_null_threshold()
108129
.map_err(Error::ParseThreshold)?
109-
.translate_by_index(|i| expression::terminal(&top.args[1 + i], Pk::from_str))
130+
.translate_by_index(|i| {
131+
top.args[1 + i]
132+
.verify_terminal("public key")
133+
.map_err(Error::Parse)
134+
})
110135
.map(Terminal::MultiA),
111136
_ => Err(Error::Unexpected(format!(
112137
"{}({} args) while parsing Miniscript",

0 commit comments

Comments
 (0)