Skip to content

Commit 26c6dc3

Browse files
committed
miniscript: implement from_tree nonrecursively
This also drops the FromTree impls for Terminal and Arc<Terminal>. As argued elsewhere, the Terminal types are not first-class types and it doesn't make sense to allow them to be constructed in elaborate ways. The benchmarks are pretty noisy but appear to show a pretty big speedup. On master: test expression::benches::parse_tree ... bench: 885.43 ns/iter (+/- 412.97) test expression::benches::parse_tree_deep ... bench: 1,951.10 ns/iter (+/- 845.66) test miniscript::benches::parse_segwit0 ... bench: 9,571.35 ns/iter (+/- 565.98) test miniscript::benches::parse_segwit0_deep ... bench: 25,571.58 ns/iter (+/- 468.93) On this commit: test expression::benches::parse_tree ... bench: 1,020.52 ns/iter (+/- 139.58) test expression::benches::parse_tree_deep ... bench: 1,632.75 ns/iter (+/- 176.35) test miniscript::benches::parse_segwit0 ... bench: 10,747.28 ns/iter (+/- 2,338.26) test miniscript::benches::parse_segwit0_deep ... bench: 19,090.65 ns/iter (+/- 1,040.62) So if you take the numbers at face value, that's a 33% speedup on the parse_segwit0_deep benchmark. Nice.
1 parent 2c422af commit 26c6dc3

File tree

2 files changed

+193
-181
lines changed

2 files changed

+193
-181
lines changed

src/miniscript/astelem.rs

Lines changed: 1 addition & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -9,147 +9,11 @@
99
1010
use bitcoin::hashes::Hash;
1111
use bitcoin::{absolute, opcodes, script};
12-
use sync::Arc;
1312

1413
use crate::miniscript::context::SigType;
1514
use crate::miniscript::ScriptContext;
16-
use crate::prelude::*;
1715
use crate::util::MsKeyBuilder;
18-
use crate::{expression, Error, FromStrKey, Miniscript, MiniscriptKey, Terminal, ToPublicKey};
19-
20-
impl<Pk: FromStrKey, Ctx: ScriptContext> crate::expression::FromTree for Arc<Terminal<Pk, Ctx>> {
21-
fn from_tree(root: expression::TreeIterItem) -> Result<Arc<Terminal<Pk, Ctx>>, Error> {
22-
Ok(Arc::new(expression::FromTree::from_tree(root)?))
23-
}
24-
}
25-
26-
impl<Pk: FromStrKey, Ctx: ScriptContext> crate::expression::FromTree for Terminal<Pk, Ctx> {
27-
fn from_tree(top: expression::TreeIterItem) -> Result<Terminal<Pk, Ctx>, Error> {
28-
let binary = |node: expression::TreeIterItem,
29-
name,
30-
termfn: fn(_, _) -> Self|
31-
-> Result<Self, Error> {
32-
node.verify_binary(name)
33-
.map_err(From::from)
34-
.map_err(Error::Parse)
35-
.and_then(|(x, y)| {
36-
let x = Arc::<Miniscript<Pk, Ctx>>::from_tree(x)?;
37-
let y = Arc::<Miniscript<Pk, Ctx>>::from_tree(y)?;
38-
Ok(termfn(x, y))
39-
})
40-
};
41-
42-
let (frag_wrap, frag_name) = top
43-
.name_separated(':')
44-
.map_err(From::from)
45-
.map_err(Error::Parse)?;
46-
// "pk" and "pkh" are aliases for "c:pk_k" and "c:pk_h" respectively.
47-
let unwrapped = match frag_name {
48-
"expr_raw_pkh" => top
49-
.verify_terminal_parent("expr_raw_pkh", "public key hash")
50-
.map(Terminal::RawPkH)
51-
.map_err(Error::Parse),
52-
"pk" => top
53-
.verify_terminal_parent("pk", "public key")
54-
.map(Terminal::PkK)
55-
.map_err(Error::Parse)
56-
.and_then(|term| Miniscript::from_ast(term))
57-
.map(|ms| Terminal::Check(Arc::new(ms))),
58-
"pkh" => top
59-
.verify_terminal_parent("pkh", "public key")
60-
.map(Terminal::PkH)
61-
.map_err(Error::Parse)
62-
.and_then(|term| Miniscript::from_ast(term))
63-
.map(|ms| Terminal::Check(Arc::new(ms))),
64-
"pk_k" => top
65-
.verify_terminal_parent("pk_k", "public key")
66-
.map(Terminal::PkK)
67-
.map_err(Error::Parse),
68-
"pk_h" => top
69-
.verify_terminal_parent("pk_h", "public key")
70-
.map(Terminal::PkH)
71-
.map_err(Error::Parse),
72-
"after" => top
73-
.verify_after()
74-
.map_err(Error::Parse)
75-
.map(Terminal::After),
76-
"older" => top
77-
.verify_older()
78-
.map_err(Error::Parse)
79-
.map(Terminal::Older),
80-
"sha256" => top
81-
.verify_terminal_parent("sha256", "hash")
82-
.map(Terminal::Sha256)
83-
.map_err(Error::Parse),
84-
"hash256" => top
85-
.verify_terminal_parent("hash256", "hash")
86-
.map(Terminal::Hash256)
87-
.map_err(Error::Parse),
88-
"ripemd160" => top
89-
.verify_terminal_parent("ripemd160", "hash")
90-
.map(Terminal::Ripemd160)
91-
.map_err(Error::Parse),
92-
"hash160" => top
93-
.verify_terminal_parent("hash160", "hash")
94-
.map(Terminal::Hash160)
95-
.map_err(Error::Parse),
96-
"1" => {
97-
top.verify_n_children("1", 0..=0)
98-
.map_err(From::from)
99-
.map_err(Error::Parse)?;
100-
Ok(Terminal::True)
101-
}
102-
"0" => {
103-
top.verify_n_children("0", 0..=0)
104-
.map_err(From::from)
105-
.map_err(Error::Parse)?;
106-
Ok(Terminal::False)
107-
}
108-
"and_v" => binary(top, "and_v", Terminal::AndV),
109-
"and_b" => binary(top, "and_b", Terminal::AndB),
110-
"and_n" => {
111-
binary(top, "and_n", |x, y| Terminal::AndOr(x, y, Arc::new(Miniscript::FALSE)))
112-
}
113-
"andor" => {
114-
top.verify_n_children("andor", 3..=3)
115-
.map_err(From::from)
116-
.map_err(Error::Parse)?;
117-
let mut child_iter = top
118-
.children()
119-
.map(|x| Arc::<Miniscript<Pk, Ctx>>::from_tree(x));
120-
Ok(Terminal::AndOr(
121-
child_iter.next().unwrap()?,
122-
child_iter.next().unwrap()?,
123-
child_iter.next().unwrap()?,
124-
))
125-
}
126-
"or_b" => binary(top, "or_b", Terminal::OrB),
127-
"or_d" => binary(top, "or_d", Terminal::OrD),
128-
"or_c" => binary(top, "or_c", Terminal::OrC),
129-
"or_i" => binary(top, "or_i", Terminal::OrI),
130-
"thresh" => top
131-
.verify_threshold(|sub| Miniscript::from_tree(sub).map(Arc::new))
132-
.map(Terminal::Thresh),
133-
"multi" => top
134-
.verify_threshold(|sub| sub.verify_terminal("public_key").map_err(Error::Parse))
135-
.map(Terminal::Multi),
136-
"multi_a" => top
137-
.verify_threshold(|sub| sub.verify_terminal("public_key").map_err(Error::Parse))
138-
.map(Terminal::MultiA),
139-
x => Err(Error::Parse(crate::ParseError::Tree(crate::ParseTreeError::UnknownName {
140-
name: x.to_owned(),
141-
}))),
142-
}?;
143-
144-
if frag_wrap == Some("") {
145-
return Err(Error::Parse(crate::ParseError::Tree(
146-
crate::ParseTreeError::UnknownName { name: top.name().to_owned() },
147-
)));
148-
}
149-
let ms = super::wrap_into_miniscript(unwrapped, frag_wrap.unwrap_or(""))?;
150-
Ok(ms.node)
151-
}
152-
}
16+
use crate::{Miniscript, MiniscriptKey, Terminal, ToPublicKey};
15317

15418
/// Helper trait to add a `push_astelem` method to `script::Builder`
15519
trait PushAstElem<Pk: MiniscriptKey, Ctx: ScriptContext> {

0 commit comments

Comments
 (0)