Skip to content

Commit

Permalink
Merge #784: expression: implement parsing of policies and miniscripts…
Browse files Browse the repository at this point in the history
… non-recursively

26c6dc3 miniscript: implement from_tree nonrecursively (Andrew Poelstra)
2c422af policy: make Concrete::from_tree non-recursive (Andrew Poelstra)
61a89a2 policy: make Semantic::from_tree non-recursive (Andrew Poelstra)
8a78025 miniscript: add constructors for all the terminals (Andrew Poelstra)

Pull request description:

  This brings us to the end of my cached "rewrite expression module" branch, by making expression parsing non-recursive. The diff here is actually reasonably short but it's pretty invasive. Hopefully the new fuzz test from the last PR is enough to convince us of correctness.

  I have some more work to do in two directions:

  * Breaking up the two `Policy` types into a structs with inner enums (or possibly just a flat tree type), which will allow us to attach metadata to nodes and trees; after this, I will introduce the `ValidationParams` structure and start moving sanity/validation/parsing logic into there as suggested in #723. I have some incomplete commits in this direction but I probably want to redo them with lessons learned from the Taproot work.
  * Replacing the `TapTree` enum with a non-recursive type, which will let us replace the taproot iteration API with a more natural one (as well as getting some performance benefit). I have more-complete work on this, but I'm getting a bit hamstrung right now handling the difference between `MiniscriptKey` and `ToPublicKey` in this specialization-less language.

ACKs for top commit:
  sanket1729:
    ACK 26c6dc3

Tree-SHA512: b576d779ec1bf495176867e8e35602e1ed8e2a6a9df99fb0058ab437f9e43cf680a0b3bd459c27ba4e21ac58f534b35f7cac6566273c66615d7dc47015243c9a
  • Loading branch information
apoelstra committed Dec 20, 2024
2 parents 772d7de + 26c6dc3 commit f3704d1
Show file tree
Hide file tree
Showing 4 changed files with 497 additions and 336 deletions.
138 changes: 1 addition & 137 deletions src/miniscript/astelem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,147 +9,11 @@
use bitcoin::hashes::Hash;
use bitcoin::{absolute, opcodes, script};
use sync::Arc;

use crate::miniscript::context::SigType;
use crate::miniscript::ScriptContext;
use crate::prelude::*;
use crate::util::MsKeyBuilder;
use crate::{expression, Error, FromStrKey, Miniscript, MiniscriptKey, Terminal, ToPublicKey};

impl<Pk: FromStrKey, Ctx: ScriptContext> crate::expression::FromTree for Arc<Terminal<Pk, Ctx>> {
fn from_tree(root: expression::TreeIterItem) -> Result<Arc<Terminal<Pk, Ctx>>, Error> {
Ok(Arc::new(expression::FromTree::from_tree(root)?))
}
}

impl<Pk: FromStrKey, Ctx: ScriptContext> crate::expression::FromTree for Terminal<Pk, Ctx> {
fn from_tree(top: expression::TreeIterItem) -> Result<Terminal<Pk, Ctx>, Error> {
let binary = |node: expression::TreeIterItem,
name,
termfn: fn(_, _) -> Self|
-> Result<Self, Error> {
node.verify_binary(name)
.map_err(From::from)
.map_err(Error::Parse)
.and_then(|(x, y)| {
let x = Arc::<Miniscript<Pk, Ctx>>::from_tree(x)?;
let y = Arc::<Miniscript<Pk, Ctx>>::from_tree(y)?;
Ok(termfn(x, y))
})
};

let (frag_wrap, frag_name) = top
.name_separated(':')
.map_err(From::from)
.map_err(Error::Parse)?;
// "pk" and "pkh" are aliases for "c:pk_k" and "c:pk_h" respectively.
let unwrapped = match frag_name {
"expr_raw_pkh" => top
.verify_terminal_parent("expr_raw_pkh", "public key hash")
.map(Terminal::RawPkH)
.map_err(Error::Parse),
"pk" => top
.verify_terminal_parent("pk", "public key")
.map(Terminal::PkK)
.map_err(Error::Parse)
.and_then(|term| Miniscript::from_ast(term))
.map(|ms| Terminal::Check(Arc::new(ms))),
"pkh" => top
.verify_terminal_parent("pkh", "public key")
.map(Terminal::PkH)
.map_err(Error::Parse)
.and_then(|term| Miniscript::from_ast(term))
.map(|ms| Terminal::Check(Arc::new(ms))),
"pk_k" => top
.verify_terminal_parent("pk_k", "public key")
.map(Terminal::PkK)
.map_err(Error::Parse),
"pk_h" => top
.verify_terminal_parent("pk_h", "public key")
.map(Terminal::PkH)
.map_err(Error::Parse),
"after" => top
.verify_after()
.map_err(Error::Parse)
.map(Terminal::After),
"older" => top
.verify_older()
.map_err(Error::Parse)
.map(Terminal::Older),
"sha256" => top
.verify_terminal_parent("sha256", "hash")
.map(Terminal::Sha256)
.map_err(Error::Parse),
"hash256" => top
.verify_terminal_parent("hash256", "hash")
.map(Terminal::Hash256)
.map_err(Error::Parse),
"ripemd160" => top
.verify_terminal_parent("ripemd160", "hash")
.map(Terminal::Ripemd160)
.map_err(Error::Parse),
"hash160" => top
.verify_terminal_parent("hash160", "hash")
.map(Terminal::Hash160)
.map_err(Error::Parse),
"1" => {
top.verify_n_children("1", 0..=0)
.map_err(From::from)
.map_err(Error::Parse)?;
Ok(Terminal::True)
}
"0" => {
top.verify_n_children("0", 0..=0)
.map_err(From::from)
.map_err(Error::Parse)?;
Ok(Terminal::False)
}
"and_v" => binary(top, "and_v", Terminal::AndV),
"and_b" => binary(top, "and_b", Terminal::AndB),
"and_n" => {
binary(top, "and_n", |x, y| Terminal::AndOr(x, y, Arc::new(Miniscript::FALSE)))
}
"andor" => {
top.verify_n_children("andor", 3..=3)
.map_err(From::from)
.map_err(Error::Parse)?;
let mut child_iter = top
.children()
.map(|x| Arc::<Miniscript<Pk, Ctx>>::from_tree(x));
Ok(Terminal::AndOr(
child_iter.next().unwrap()?,
child_iter.next().unwrap()?,
child_iter.next().unwrap()?,
))
}
"or_b" => binary(top, "or_b", Terminal::OrB),
"or_d" => binary(top, "or_d", Terminal::OrD),
"or_c" => binary(top, "or_c", Terminal::OrC),
"or_i" => binary(top, "or_i", Terminal::OrI),
"thresh" => top
.verify_threshold(|sub| Miniscript::from_tree(sub).map(Arc::new))
.map(Terminal::Thresh),
"multi" => top
.verify_threshold(|sub| sub.verify_terminal("public_key").map_err(Error::Parse))
.map(Terminal::Multi),
"multi_a" => top
.verify_threshold(|sub| sub.verify_terminal("public_key").map_err(Error::Parse))
.map(Terminal::MultiA),
x => Err(Error::Parse(crate::ParseError::Tree(crate::ParseTreeError::UnknownName {
name: x.to_owned(),
}))),
}?;

if frag_wrap == Some("") {
return Err(Error::Parse(crate::ParseError::Tree(
crate::ParseTreeError::UnknownName { name: top.name().to_owned() },
)));
}
let ms = super::wrap_into_miniscript(unwrapped, frag_wrap.unwrap_or(""))?;
Ok(ms.node)
}
}
use crate::{Miniscript, MiniscriptKey, Terminal, ToPublicKey};

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

0 comments on commit f3704d1

Please sign in to comment.