Skip to content

Commit 733bedd

Browse files
committed
Merge #778: cleanups: Eliminate errstr and (nearly) eliminate Unexpected
33a60e2 expression: pull MultiColon error into parsing logic, drop AtOutsideOr (Andrew Poelstra) f7cb701 expression: add "illegal and/or" for thresholds; drop errstr (Andrew Poelstra) b3d1b17 descriptor: eliminate several instances of Unexpected (Andrew Poelstra) 1467453 compiler: refactor out a call to errstr (Andrew Poelstra) dd19874 expression: introduce "unknown name" error variant (Andrew Poelstra) 4ae5079 expression: replace methods for parsing numbers and locktimes (Andrew Poelstra) ee15056 policy: remove now-unused semantic::PolicyError type (Andrew Poelstra) ce3d3e8 expression: replace most uses of `terminal` (Andrew Poelstra) 8423557 expression: replace poorly-typed `binary` function with new one (Andrew Poelstra) Pull request description: This PR is a series of commits which cleans up the expression parsing module. After the last couple PRs, which substantially rewrote the parser and introduce a new parsing-error module, we can get rid of many uses of the `Error::Unexpected` variant and its constructor, the `errstr` function. This PR should have no visible effects, and does not even change any algorithms. The next one will return to the process of rewriting the expression parser, by replacing the recursive `Tree` type with a non-recursive one. Will post benchmarks once they are done. ACKs for top commit: sanket1729: ACK 33a60e2 Tree-SHA512: 09927b83f84baa3593af22e95b84aa38f65851e6e005fb53f02cbb23edc7e2275345727399d70a252ccd1f8462707666b91fc043f613d2998fa8a51204525a9e
2 parents d22b558 + 33a60e2 commit 733bedd

File tree

14 files changed

+522
-437
lines changed

14 files changed

+522
-437
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/mod.rs

+20-22
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ use crate::miniscript::{satisfy, Legacy, Miniscript, Segwitv0};
2727
use crate::plan::{AssetProvider, Plan};
2828
use crate::prelude::*;
2929
use crate::{
30-
expression, hash256, BareCtx, Error, ForEachKey, FromStrKey, MiniscriptKey, Satisfier,
31-
ToPublicKey, TranslateErr, Translator,
30+
expression, hash256, BareCtx, Error, ForEachKey, FromStrKey, MiniscriptKey, ParseError,
31+
Satisfier, ToPublicKey, TranslateErr, Translator,
3232
};
3333

3434
mod bare;
@@ -715,8 +715,9 @@ impl Descriptor<DescriptorPublicKey> {
715715
Some(sk),
716716
),
717717
Err(_) => (
718-
DescriptorPublicKey::from_str(s)
719-
.map_err(|e| Error::Unexpected(e.to_string()))?,
718+
// try to parse as a public key if parsing as a secret key failed
719+
s.parse()
720+
.map_err(|e| Error::Parse(ParseError::box_from_str(e)))?,
720721
None,
721722
),
722723
};
@@ -741,37 +742,34 @@ impl Descriptor<DescriptorPublicKey> {
741742
}
742743

743744
fn sha256(&mut self, sha256: &String) -> Result<sha256::Hash, Error> {
744-
let hash =
745-
sha256::Hash::from_str(sha256).map_err(|e| Error::Unexpected(e.to_string()))?;
746-
Ok(hash)
745+
sha256
746+
.parse()
747+
.map_err(|e| Error::Parse(ParseError::box_from_str(e)))
747748
}
748749

749750
fn hash256(&mut self, hash256: &String) -> Result<hash256::Hash, Error> {
750-
let hash = hash256::Hash::from_str(hash256)
751-
.map_err(|e| Error::Unexpected(e.to_string()))?;
752-
Ok(hash)
751+
hash256
752+
.parse()
753+
.map_err(|e| Error::Parse(ParseError::box_from_str(e)))
753754
}
754755

755756
fn ripemd160(&mut self, ripemd160: &String) -> Result<ripemd160::Hash, Error> {
756-
let hash = ripemd160::Hash::from_str(ripemd160)
757-
.map_err(|e| Error::Unexpected(e.to_string()))?;
758-
Ok(hash)
757+
ripemd160
758+
.parse()
759+
.map_err(|e| Error::Parse(ParseError::box_from_str(e)))
759760
}
760761

761762
fn hash160(&mut self, hash160: &String) -> Result<hash160::Hash, Error> {
762-
let hash = hash160::Hash::from_str(hash160)
763-
.map_err(|e| Error::Unexpected(e.to_string()))?;
764-
Ok(hash)
763+
hash160
764+
.parse()
765+
.map_err(|e| Error::Parse(ParseError::box_from_str(e)))
765766
}
766767
}
767768

768769
let descriptor = Descriptor::<String>::from_str(s)?;
769-
let descriptor = descriptor.translate_pk(&mut keymap_pk).map_err(|e| {
770-
Error::Unexpected(
771-
e.expect_translator_err("No Outer context errors")
772-
.to_string(),
773-
)
774-
})?;
770+
let descriptor = descriptor
771+
.translate_pk(&mut keymap_pk)
772+
.map_err(|e| e.expect_translator_err("No Outer context errors"))?;
775773

776774
Ok((descriptor, keymap_pk.0))
777775
}

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/error.rs

+19
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,25 @@ use core::fmt;
88
use std::error;
99

1010
use crate::blanket_traits::StaticDebugAndDisplay;
11+
use crate::primitives::absolute_locktime::AbsLockTimeError;
12+
use crate::primitives::relative_locktime::RelLockTimeError;
1113
use crate::Box;
14+
1215
/// An error parsing a Miniscript object (policy, descriptor or miniscript)
1316
/// from a string.
1417
#[derive(Debug)]
1518
pub enum ParseError {
19+
/// Invalid absolute locktime
20+
AbsoluteLockTime(AbsLockTimeError),
21+
/// Invalid absolute locktime
22+
RelativeLockTime(RelLockTimeError),
1623
/// Failed to parse a public key or hash.
1724
///
1825
/// Note that the error information is lost for nostd compatibility reasons. See
1926
/// <https://users.rust-lang.org/t/how-to-box-an-error-type-retaining-std-error-only-when-std-is-enabled/>.
2027
FromStr(Box<dyn StaticDebugAndDisplay>),
28+
/// Failed to parse a number.
29+
Num(crate::ParseNumError),
2130
/// Error parsing a string into an expression tree.
2231
Tree(crate::ParseTreeError),
2332
}
@@ -29,14 +38,21 @@ impl ParseError {
2938
}
3039
}
3140

41+
impl From<crate::ParseNumError> for ParseError {
42+
fn from(e: crate::ParseNumError) -> Self { Self::Num(e) }
43+
}
44+
3245
impl From<crate::ParseTreeError> for ParseError {
3346
fn from(e: crate::ParseTreeError) -> Self { Self::Tree(e) }
3447
}
3548

3649
impl fmt::Display for ParseError {
3750
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3851
match self {
52+
ParseError::AbsoluteLockTime(ref e) => e.fmt(f),
53+
ParseError::RelativeLockTime(ref e) => e.fmt(f),
3954
ParseError::FromStr(ref e) => e.fmt(f),
55+
ParseError::Num(ref e) => e.fmt(f),
4056
ParseError::Tree(ref e) => e.fmt(f),
4157
}
4258
}
@@ -46,7 +62,10 @@ impl fmt::Display for ParseError {
4662
impl error::Error for ParseError {
4763
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
4864
match self {
65+
ParseError::AbsoluteLockTime(ref e) => Some(e),
66+
ParseError::RelativeLockTime(ref e) => Some(e),
4967
ParseError::FromStr(..) => None,
68+
ParseError::Num(ref e) => Some(e),
5069
ParseError::Tree(ref e) => Some(e),
5170
}
5271
}

src/expression/error.rs

+68-9
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
//! Expression-related errors
44
5-
use core::fmt;
5+
use core::{fmt, num};
66

77
use crate::descriptor::checksum;
88
use crate::prelude::*;
@@ -76,13 +76,25 @@ pub enum ParseTreeError {
7676
/// The position of the opening curly brace.
7777
pos: usize,
7878
},
79+
/// Multiple separators (':' or '@') appeared in a node name.
80+
MultipleSeparators {
81+
/// The separator in question.
82+
separator: char,
83+
/// The position of the second separator.
84+
pos: usize,
85+
},
7986
/// Data occurred after the final ).
8087
TrailingCharacter {
8188
/// The first trailing character.
8289
ch: char,
8390
/// Its byte-index into the string.
8491
pos: usize,
8592
},
93+
/// A node's name was not recognized.
94+
UnknownName {
95+
/// The name that was not recognized.
96+
name: String,
97+
},
8698
}
8799

88100
impl From<checksum::Error> for ParseTreeError {
@@ -144,9 +156,17 @@ impl fmt::Display for ParseTreeError {
144156
}?;
145157
write!(f, ", but found {}", n_children)
146158
}
159+
ParseTreeError::MultipleSeparators { separator, pos } => {
160+
write!(
161+
f,
162+
"separator '{}' occured multiple times (second time at position {})",
163+
separator, pos
164+
)
165+
}
147166
ParseTreeError::TrailingCharacter { ch, pos } => {
148167
write!(f, "trailing data `{}...` (position {})", ch, pos)
149168
}
169+
ParseTreeError::UnknownName { name } => write!(f, "unrecognized name '{}'", name),
150170
}
151171
}
152172
}
@@ -163,7 +183,39 @@ impl std::error::Error for ParseTreeError {
163183
| ParseTreeError::IllegalCurlyBrace { .. }
164184
| ParseTreeError::IncorrectName { .. }
165185
| ParseTreeError::IncorrectNumberOfChildren { .. }
166-
| ParseTreeError::TrailingCharacter { .. } => None,
186+
| ParseTreeError::MultipleSeparators { .. }
187+
| ParseTreeError::TrailingCharacter { .. }
188+
| ParseTreeError::UnknownName { .. } => None,
189+
}
190+
}
191+
}
192+
193+
/// Error parsing a number.
194+
#[derive(Clone, Debug, PartialEq, Eq)]
195+
pub enum ParseNumError {
196+
/// Failed to parse the number at all.
197+
StdParse(num::ParseIntError),
198+
/// Number had a leading zero, + or -.
199+
InvalidLeadingDigit(char),
200+
}
201+
202+
impl fmt::Display for ParseNumError {
203+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
204+
match self {
205+
ParseNumError::StdParse(ref e) => e.fmt(f),
206+
ParseNumError::InvalidLeadingDigit(ch) => {
207+
write!(f, "numbers must start with 1-9, not {}", ch)
208+
}
209+
}
210+
}
211+
}
212+
213+
#[cfg(feature = "std")]
214+
impl std::error::Error for ParseNumError {
215+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
216+
match self {
217+
ParseNumError::StdParse(ref e) => Some(e),
218+
ParseNumError::InvalidLeadingDigit(..) => None,
167219
}
168220
}
169221
}
@@ -175,10 +227,12 @@ pub enum ParseThresholdError {
175227
NoChildren,
176228
/// The threshold value appeared to be a sub-expression rather than a number.
177229
KNotTerminal,
230+
/// A 1-of-n threshold was used in a context it was not allowed.
231+
IllegalOr,
232+
/// A n-of-n threshold was used in a context it was not allowed.
233+
IllegalAnd,
178234
/// Failed to parse the threshold value.
179-
// FIXME this should be a more specific type. Will be handled in a later PR
180-
// that rewrites the expression parsing logic.
181-
ParseK(String),
235+
ParseK(ParseNumError),
182236
/// Threshold parameters were invalid.
183237
Threshold(ThresholdError),
184238
}
@@ -190,7 +244,13 @@ impl fmt::Display for ParseThresholdError {
190244
match *self {
191245
NoChildren => f.write_str("expected threshold, found terminal"),
192246
KNotTerminal => f.write_str("expected positive integer, found expression"),
193-
ParseK(ref x) => write!(f, "failed to parse threshold value {}", x),
247+
IllegalOr => f.write_str(
248+
"1-of-n thresholds not allowed here; please use an 'or' fragment instead",
249+
),
250+
IllegalAnd => f.write_str(
251+
"n-of-n thresholds not allowed here; please use an 'and' fragment instead",
252+
),
253+
ParseK(ref x) => write!(f, "failed to parse threshold value: {}", x),
194254
Threshold(ref e) => e.fmt(f),
195255
}
196256
}
@@ -202,9 +262,8 @@ impl std::error::Error for ParseThresholdError {
202262
use ParseThresholdError::*;
203263

204264
match *self {
205-
NoChildren => None,
206-
KNotTerminal => None,
207-
ParseK(..) => None,
265+
NoChildren | KNotTerminal | IllegalOr | IllegalAnd => None,
266+
ParseK(ref e) => Some(e),
208267
Threshold(ref e) => Some(e),
209268
}
210269
}

0 commit comments

Comments
 (0)