diff --git a/src/miniscript/astelem.rs b/src/miniscript/astelem.rs index c990f31fa..19611b083 100644 --- a/src/miniscript/astelem.rs +++ b/src/miniscript/astelem.rs @@ -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 crate::expression::FromTree for Arc> { - fn from_tree(root: expression::TreeIterItem) -> Result>, Error> { - Ok(Arc::new(expression::FromTree::from_tree(root)?)) - } -} - -impl crate::expression::FromTree for Terminal { - fn from_tree(top: expression::TreeIterItem) -> Result, Error> { - let binary = |node: expression::TreeIterItem, - name, - termfn: fn(_, _) -> Self| - -> Result { - node.verify_binary(name) - .map_err(From::from) - .map_err(Error::Parse) - .and_then(|(x, y)| { - let x = Arc::>::from_tree(x)?; - let y = Arc::>::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::>::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 { diff --git a/src/miniscript/mod.rs b/src/miniscript/mod.rs index 65c2da9f6..6508ffa25 100644 --- a/src/miniscript/mod.rs +++ b/src/miniscript/mod.rs @@ -40,6 +40,7 @@ use core::cmp; use sync::Arc; use self::lex::{lex, TokenIter}; +use crate::expression::{FromTree, TreeIterItem}; pub use crate::miniscript::context::ScriptContext; use crate::miniscript::decode::Terminal; use crate::{ @@ -56,7 +57,7 @@ mod private { pub use crate::miniscript::context::ScriptContext; use crate::miniscript::types; use crate::prelude::sync::Arc; - use crate::{Error, MiniscriptKey, Terminal, MAX_RECURSION_DEPTH}; + use crate::{AbsLockTime, Error, MiniscriptKey, RelLockTime, Terminal, MAX_RECURSION_DEPTH}; /// The top-level miniscript abstract syntax tree (AST). pub struct Miniscript { @@ -157,6 +158,118 @@ mod private { phantom: PhantomData, }; + /// The `pk` combinator, which is an alias for `c:pk_k`. + pub fn pk(pk: Pk) -> Self { + let inner = Arc::new(Self::pk_k(pk)); + Self { + ty: types::Type::cast_check(inner.ty).unwrap(), + ext: types::extra_props::ExtData::cast_check(inner.ext), + node: Terminal::Check(inner), + phantom: PhantomData, + } + } + + /// The `pkh` combinator, which is an alias for `c:pk_h`. + pub fn pkh(pk: Pk) -> Self { + let inner = Arc::new(Self::pk_h(pk)); + Self { + ty: types::Type::cast_check(inner.ty).unwrap(), + ext: types::extra_props::ExtData::cast_check(inner.ext), + node: Terminal::Check(inner), + phantom: PhantomData, + } + } + + /// The `pk_k` combinator. + pub fn pk_k(pk: Pk) -> Self { + Self { + node: Terminal::PkK(pk), + ty: types::Type::pk_k(), + ext: types::extra_props::ExtData::pk_k::(), + phantom: PhantomData, + } + } + + /// The `pk_h` combinator. + pub fn pk_h(pk: Pk) -> Self { + Self { + node: Terminal::PkH(pk), + ty: types::Type::pk_h(), + ext: types::extra_props::ExtData::pk_h::(), + phantom: PhantomData, + } + } + + /// The `expr_raw_pkh` combinator. + pub fn expr_raw_pkh(hash: bitcoin::hashes::hash160::Hash) -> Self { + Self { + node: Terminal::RawPkH(hash), + ty: types::Type::pk_h(), + ext: types::extra_props::ExtData::pk_h::(), + phantom: PhantomData, + } + } + + /// The `after` combinator. + pub fn after(time: AbsLockTime) -> Self { + Self { + node: Terminal::After(time), + ty: types::Type::time(), + ext: types::extra_props::ExtData::after(time), + phantom: PhantomData, + } + } + + /// The `older` combinator. + pub fn older(time: RelLockTime) -> Self { + Self { + node: Terminal::Older(time), + ty: types::Type::time(), + ext: types::extra_props::ExtData::older(time), + phantom: PhantomData, + } + } + + /// The `sha256` combinator. + pub const fn sha256(hash: Pk::Sha256) -> Self { + Self { + node: Terminal::Sha256(hash), + ty: types::Type::hash(), + ext: types::extra_props::ExtData::sha256(), + phantom: PhantomData, + } + } + + /// The `hash256` combinator. + pub const fn hash256(hash: Pk::Hash256) -> Self { + Self { + node: Terminal::Hash256(hash), + ty: types::Type::hash(), + ext: types::extra_props::ExtData::hash256(), + phantom: PhantomData, + } + } + + /// The `ripemd160` combinator. + pub const fn ripemd160(hash: Pk::Ripemd160) -> Self { + Self { + node: Terminal::Ripemd160(hash), + ty: types::Type::hash(), + ext: types::extra_props::ExtData::ripemd160(), + phantom: PhantomData, + } + } + + /// The `hash160` combinator. + pub const fn hash160(hash: Pk::Hash160) -> Self { + Self { + node: Terminal::Hash160(hash), + ty: types::Type::hash(), + ext: types::extra_props::ExtData::hash160(), + phantom: PhantomData, + } + } + /// Add type information(Type and Extdata) to Miniscript based on /// `AstElem` fragment. Dependent on display and clone because of Error /// Display code of type_check. @@ -670,41 +783,6 @@ impl Miniscript { } } -/// Utility function used when parsing a script from an expression tree. -/// -/// Once a Miniscript fragment has been parsed into a terminal, apply any -/// wrappers that were included in its name. -fn wrap_into_miniscript( - term: Terminal, - frag_wrap: &str, -) -> Result, Error> -where - Pk: MiniscriptKey, - Ctx: ScriptContext, -{ - let mut unwrapped = term; - for ch in frag_wrap.chars().rev() { - // Check whether the wrapper is valid under the current context - let ms = Miniscript::from_ast(unwrapped)?; - match ch { - 'a' => unwrapped = Terminal::Alt(Arc::new(ms)), - 's' => unwrapped = Terminal::Swap(Arc::new(ms)), - 'c' => unwrapped = Terminal::Check(Arc::new(ms)), - 'd' => unwrapped = Terminal::DupIf(Arc::new(ms)), - 'v' => unwrapped = Terminal::Verify(Arc::new(ms)), - 'j' => unwrapped = Terminal::NonZero(Arc::new(ms)), - 'n' => unwrapped = Terminal::ZeroNotEqual(Arc::new(ms)), - 't' => unwrapped = Terminal::AndV(Arc::new(ms), Arc::new(Miniscript::TRUE)), - 'u' => unwrapped = Terminal::OrI(Arc::new(ms), Arc::new(Miniscript::FALSE)), - 'l' => unwrapped = Terminal::OrI(Arc::new(Miniscript::FALSE), Arc::new(ms)), - x => return Err(Error::UnknownWrapper(x)), - } - } - // Check whether the unwrapped miniscript is valid under the current context - let ms = Miniscript::from_ast(unwrapped)?; - Ok(ms) -} - impl Miniscript { /// Attempt to parse an insane(scripts don't clear sanity checks) /// from string into a Miniscript representation. @@ -736,21 +814,203 @@ impl Miniscript { } } -impl crate::expression::FromTree for Arc> { - fn from_tree(root: expression::TreeIterItem) -> Result>, Error> { - Ok(Arc::new(expression::FromTree::from_tree(root)?)) +impl FromTree for Arc> { + fn from_tree(root: TreeIterItem) -> Result { + Miniscript::from_tree(root).map(Arc::new) } } -impl crate::expression::FromTree for Miniscript { - /// Parse an expression tree into a Miniscript. As a general rule, this - /// should not be called directly; rather go through the descriptor API. - fn from_tree(root: expression::TreeIterItem) -> Result, Error> { +impl FromTree for Miniscript { + fn from_tree(root: TreeIterItem) -> Result { + #[allow(clippy::type_complexity)] + fn binary( + node: expression::TreeIterItem, + stack: &mut Vec>>, + name: &'static str, + termfn: fn(Arc>, Arc>) -> Terminal, + ) -> Result, Error> { + node.verify_n_children(name, 2..=2) + .map_err(From::from) + .map_err(Error::Parse)?; + Miniscript::from_ast(termfn(stack.pop().unwrap(), stack.pop().unwrap())) + } root.verify_no_curly_braces() .map_err(From::from) .map_err(Error::Parse)?; - let inner: Terminal = expression::FromTree::from_tree(root)?; - Miniscript::from_ast(inner) + + let mut stack = Vec::with_capacity(128); + for (n, node) in root.pre_order_iter().enumerate().rev() { + // Before doing anything else, check if this is the inner value of a terminal. + // In that case, just skip the node. Conveniently, there are no combinators + // in Miniscript that have a single child that these might be confused with. + // (Well, there are, but they're all serialized as wrappers.) + // + // We also skip all the children of multi/multi_a and the first child of thresh + // (which will be the k value, not a real child). + // + // We do not do this check on the root node, because its parent might be wsh or + // sh or something, and actually these ARE single-child combinators, but we don't + // want to skip their children. + if n > 0 && node.n_children() == 0 { + let parent = node.parent().unwrap(); + if parent.n_children() == 1 { + continue; + } + + let (_, parent_name) = parent + .name_separated(':') + .map_err(From::from) + .map_err(Error::Parse)?; + + if parent_name == "multi" || parent_name == "multi_a" { + continue; + } + if parent_name == "thresh" && node.is_first_child() { + continue; + } + } + + let (frag_wrap, frag_name) = node + .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 new = match frag_name { + "expr_raw_pkh" => node + .verify_terminal_parent("expr_raw_pkh", "public key hash") + .map(Miniscript::expr_raw_pkh) + .map_err(Error::Parse), + "pk" => node + .verify_terminal_parent("pk", "public key") + .map(Miniscript::pk) + .map_err(Error::Parse), + "pkh" => node + .verify_terminal_parent("pkh", "public key") + .map(Miniscript::pkh) + .map_err(Error::Parse), + "pk_k" => node + .verify_terminal_parent("pk_k", "public key") + .map(Miniscript::pk_k) + .map_err(Error::Parse), + "pk_h" => node + .verify_terminal_parent("pk_h", "public key") + .map(Miniscript::pk_h) + .map_err(Error::Parse), + "after" => node + .verify_after() + .map(Miniscript::after) + .map_err(Error::Parse), + "older" => node + .verify_older() + .map(Miniscript::older) + .map_err(Error::Parse), + "sha256" => node + .verify_terminal_parent("sha256", "hash") + .map(Miniscript::sha256) + .map_err(Error::Parse), + "hash256" => node + .verify_terminal_parent("hash256", "hash") + .map(Miniscript::hash256) + .map_err(Error::Parse), + "ripemd160" => node + .verify_terminal_parent("ripemd160", "hash") + .map(Miniscript::ripemd160) + .map_err(Error::Parse), + "hash160" => node + .verify_terminal_parent("hash160", "hash") + .map(Miniscript::hash160) + .map_err(Error::Parse), + "1" => { + node.verify_n_children("1", 0..=0) + .map_err(From::from) + .map_err(Error::Parse)?; + Ok(Miniscript::TRUE) + } + "0" => { + node.verify_n_children("0", 0..=0) + .map_err(From::from) + .map_err(Error::Parse)?; + Ok(Miniscript::FALSE) + } + "and_v" => binary(node, &mut stack, "and_v", Terminal::AndV), + "and_b" => binary(node, &mut stack, "and_b", Terminal::AndB), + "and_n" => binary(node, &mut stack, "and_n", |x, y| { + Terminal::AndOr(x, y, Arc::new(Miniscript::FALSE)) + }), + "andor" => { + node.verify_n_children("andor", 3..=3) + .map_err(From::from) + .map_err(Error::Parse)?; + Miniscript::from_ast(Terminal::AndOr( + stack.pop().unwrap(), + stack.pop().unwrap(), + stack.pop().unwrap(), + )) + } + "or_b" => binary(node, &mut stack, "or_b", Terminal::OrB), + "or_d" => binary(node, &mut stack, "or_d", Terminal::OrD), + "or_c" => binary(node, &mut stack, "or_c", Terminal::OrC), + "or_i" => binary(node, &mut stack, "or_i", Terminal::OrI), + "thresh" => node + .verify_threshold(|_| Ok(stack.pop().unwrap())) + .map(Terminal::Thresh) + .and_then(Miniscript::from_ast), + "multi" => node + .verify_threshold(|sub| sub.verify_terminal("public_key").map_err(Error::Parse)) + .map(Terminal::Multi) + .and_then(Miniscript::from_ast), + "multi_a" => node + .verify_threshold(|sub| sub.verify_terminal("public_key").map_err(Error::Parse)) + .map(Terminal::MultiA) + .and_then(Miniscript::from_ast), + x => { + Err(Error::Parse(crate::ParseError::Tree(crate::ParseTreeError::UnknownName { + name: x.to_owned(), + }))) + } + }?; + + let mut new = Arc::new(new); + if let Some(frag_wrap) = frag_wrap { + // ":node()" is not valid syntax + if frag_wrap.is_empty() { + return Err(Error::Parse(crate::ParseError::Tree( + crate::ParseTreeError::UnknownName { name: node.name().to_owned() }, + ))); + } + + for ch in frag_wrap.bytes().rev() { + let term = match ch { + b'a' => Terminal::Alt(new), + b's' => Terminal::Swap(new), + b'c' => Terminal::Check(new), + b'd' => Terminal::DupIf(new), + b'v' => Terminal::Verify(new), + b'j' => Terminal::NonZero(new), + b'n' => Terminal::ZeroNotEqual(new), + b't' => Terminal::AndV(new, Arc::new(Miniscript::TRUE)), + b'u' => Terminal::OrI(new, Arc::new(Miniscript::FALSE)), + b'l' => Terminal::OrI(Arc::new(Miniscript::FALSE), new), + x => return Err(Error::UnknownWrapper(x.into())), + }; + new = Arc::new(Miniscript::from_ast(term)?); + } + } + + stack.push(new); + } + + assert_eq!(stack.len(), 1); + let ret = stack.pop().unwrap(); + // Iterate through every node to check global validity. It is definitely not sufficient + // to check only the root, since this will fail to notice illegal xonly keys at the + // leaves. But probably checking every single node is overkill. This may be worth + // optimizing. + for node in ret.pre_order_iter() { + Ctx::check_global_validity(node)?; + } + Ok(Arc::try_unwrap(ret).unwrap()) } } diff --git a/src/policy/concrete.rs b/src/policy/concrete.rs index 8826b3cf7..2fe0748b8 100644 --- a/src/policy/concrete.rs +++ b/src/policy/concrete.rs @@ -843,100 +843,112 @@ impl str::FromStr for Policy { serde_string_impl_pk!(Policy, "a miniscript concrete policy"); -impl Policy { - /// Helper function for `from_tree` to parse subexpressions with - /// names of the form x@y - fn from_tree_prob( - top: expression::TreeIterItem, - allow_prob: bool, - ) -> Result<(usize, Policy), Error> { - // When 'allow_prob' is true we parse '@' signs out of node names. - let (frag_prob, frag_name) = if allow_prob { - top.name_separated('@') - .map_err(From::from) - .map_err(Error::Parse)? - } else { - (None, top.name()) - }; - - let frag_prob = match frag_prob { - None => 1, - Some(s) => expression::parse_num(s) - .map_err(From::from) - .map_err(Error::Parse)? as usize, - }; - - match frag_name { - "UNSATISFIABLE" => { - top.verify_n_children("UNSATISFIABLE", 0..=0) - .map_err(From::from) - .map_err(Error::Parse)?; - Ok(Policy::Unsatisfiable) - } - "TRIVIAL" => { - top.verify_n_children("TRIVIAL", 0..=0) +impl expression::FromTree for Policy { + fn from_tree(root: expression::TreeIterItem) -> Result, Error> { + root.verify_no_curly_braces() + .map_err(From::from) + .map_err(Error::Parse)?; + + let mut stack = Vec::<(usize, _)>::with_capacity(128); + for node in root.pre_order_iter().rev() { + let allow_prob; + // Before doing anything else, check if this is the inner value of a terminal. + // In that case, just skip the node. Conveniently, there are no combinators + // in policy that have a single child that these might be confused with (we + // require and, or and thresholds to all have >1 child). + if let Some(parent) = node.parent() { + if parent.n_children() == 1 { + continue; + } + let (_, parent_name) = parent + .name_separated('@') .map_err(From::from) .map_err(Error::Parse)?; - Ok(Policy::Trivial) + if node.is_first_child() && parent_name == "thresh" { + continue; + } + allow_prob = parent_name == "or"; + } else { + allow_prob = false; } - "pk" => top - .verify_terminal_parent("pk", "public key") - .map(Policy::Key) - .map_err(Error::Parse), - "after" => top.verify_after().map_err(Error::Parse).map(Policy::After), - "older" => top.verify_older().map_err(Error::Parse).map(Policy::Older), - "sha256" => top - .verify_terminal_parent("sha256", "hash") - .map(Policy::Sha256) - .map_err(Error::Parse), - "hash256" => top - .verify_terminal_parent("hash256", "hash") - .map(Policy::Hash256) - .map_err(Error::Parse), - "ripemd160" => top - .verify_terminal_parent("ripemd160", "hash") - .map(Policy::Ripemd160) - .map_err(Error::Parse), - "hash160" => top - .verify_terminal_parent("hash160", "hash") - .map(Policy::Hash160) - .map_err(Error::Parse), - "and" => { - top.verify_n_children("and", 2..=2) + + // When 'allow_prob' is true we parse '@' signs out of node names. + let (frag_prob, frag_name) = if allow_prob { + node.name_separated('@') .map_err(From::from) - .map_err(Error::Parse)?; - let subs = top - .children() - .map(|arg| Self::from_tree(arg).map(Arc::new)) - .collect::>()?; - Ok(Policy::And(subs)) - } - "or" => { - top.verify_n_children("or", 2..=2) + .map_err(Error::Parse)? + } else { + (None, node.name()) + }; + + let frag_prob = match frag_prob { + None => 1, + Some(s) => expression::parse_num(s) .map_err(From::from) - .map_err(Error::Parse)?; - let subs = top - .children() - .map(|arg| { - Self::from_tree_prob(arg, true).map(|(prob, sub)| (prob, Arc::new(sub))) - }) - .collect::>()?; - Ok(Policy::Or(subs)) - } - "thresh" => top - .verify_threshold(|sub| Self::from_tree(sub).map(Arc::new)) - .map(Self::Thresh), - x => Err(Error::Parse(crate::ParseError::Tree(crate::ParseTreeError::UnknownName { - name: x.to_owned(), - }))), + .map_err(Error::Parse)? as usize, + }; + + let new = + match frag_name { + "UNSATISFIABLE" => { + node.verify_n_children("UNSATISFIABLE", 0..=0) + .map_err(From::from) + .map_err(Error::Parse)?; + Ok(Policy::Unsatisfiable) + } + "TRIVIAL" => { + node.verify_n_children("TRIVIAL", 0..=0) + .map_err(From::from) + .map_err(Error::Parse)?; + Ok(Policy::Trivial) + } + "pk" => node + .verify_terminal_parent("pk", "public key") + .map(Policy::Key) + .map_err(Error::Parse), + "after" => node.verify_after().map_err(Error::Parse).map(Policy::After), + "older" => node.verify_older().map_err(Error::Parse).map(Policy::Older), + "sha256" => node + .verify_terminal_parent("sha256", "hash") + .map(Policy::Sha256) + .map_err(Error::Parse), + "hash256" => node + .verify_terminal_parent("hash256", "hash") + .map(Policy::Hash256) + .map_err(Error::Parse), + "ripemd160" => node + .verify_terminal_parent("ripemd160", "hash") + .map(Policy::Ripemd160) + .map_err(Error::Parse), + "hash160" => node + .verify_terminal_parent("hash160", "hash") + .map(Policy::Hash160) + .map_err(Error::Parse), + "and" => { + node.verify_n_children("and", 2..=2) + .map_err(From::from) + .map_err(Error::Parse)?; + Ok(Policy::And(vec![stack.pop().unwrap().1, stack.pop().unwrap().1])) + } + "or" => { + node.verify_n_children("or", 2..=2) + .map_err(From::from) + .map_err(Error::Parse)?; + Ok(Policy::Or(vec![stack.pop().unwrap(), stack.pop().unwrap()])) + } + "thresh" => node + .verify_threshold(|_| Ok(stack.pop().unwrap().1)) + .map(Self::Thresh), + x => Err(Error::Parse(crate::ParseError::Tree( + crate::ParseTreeError::UnknownName { name: x.to_owned() }, + ))), + }?; + + stack.push((frag_prob, Arc::new(new))); } - .map(|res| (frag_prob, res)) - } -} -impl expression::FromTree for Policy { - fn from_tree(root: expression::TreeIterItem) -> Result, Error> { - Policy::from_tree_prob(root, false).map(|(_, result)| result) + assert_eq!(stack.len(), 1); + Ok(Arc::try_unwrap(stack.pop().unwrap().1).unwrap()) } } diff --git a/src/policy/semantic.rs b/src/policy/semantic.rs index 0c97c59c7..c6a640a07 100644 --- a/src/policy/semantic.rs +++ b/src/policy/semantic.rs @@ -284,79 +284,104 @@ impl str::FromStr for Policy { serde_string_impl_pk!(Policy, "a miniscript semantic policy"); impl expression::FromTree for Policy { - fn from_tree(top: expression::TreeIterItem) -> Result, Error> { - match top.name() { - "UNSATISFIABLE" => { - top.verify_n_children("UNSATISFIABLE", 0..=0) - .map_err(From::from) - .map_err(Error::Parse)?; - Ok(Policy::Unsatisfiable) - } - "TRIVIAL" => { - top.verify_n_children("TRIVIAL", 0..=0) - .map_err(From::from) - .map_err(Error::Parse)?; - Ok(Policy::Trivial) - } - "pk" => top - .verify_terminal_parent("pk", "public key") - .map(Policy::Key) - .map_err(Error::Parse), - "after" => top.verify_after().map_err(Error::Parse).map(Policy::After), - "older" => top.verify_older().map_err(Error::Parse).map(Policy::Older), - "sha256" => top - .verify_terminal_parent("sha256", "hash") - .map(Policy::Sha256) - .map_err(Error::Parse), - "hash256" => top - .verify_terminal_parent("hash256", "hash") - .map(Policy::Hash256) - .map_err(Error::Parse), - "ripemd160" => top - .verify_terminal_parent("ripemd160", "hash") - .map(Policy::Ripemd160) - .map_err(Error::Parse), - "hash160" => top - .verify_terminal_parent("hash160", "hash") - .map(Policy::Hash160) - .map_err(Error::Parse), - "and" => { - top.verify_n_children("and", 2..) - .map_err(From::from) - .map_err(Error::Parse)?; - let subs = top - .children() - .map(|arg| Self::from_tree(arg).map(Arc::new)) - .collect::, Error>>()?; - Ok(Policy::Thresh(Threshold::new(subs.len(), subs).map_err(Error::Threshold)?)) - } - "or" => { - top.verify_n_children("or", 2..) - .map_err(From::from) - .map_err(Error::Parse)?; - let subs = top - .children() - .map(|arg| Self::from_tree(arg).map(Arc::new)) - .collect::, Error>>()?; - Ok(Policy::Thresh(Threshold::new(1, subs).map_err(Error::Threshold)?)) + fn from_tree(root: expression::TreeIterItem) -> Result, Error> { + root.verify_no_curly_braces() + .map_err(From::from) + .map_err(Error::Parse)?; + + let mut stack = Vec::with_capacity(128); + for node in root.pre_order_iter().rev() { + // Before doing anything else, check if this is the inner value of a terminal. + // In that case, just skip the node. Conveniently, there are no combinators + // in policy that have a single child that these might be confused with (we + // require and, or and thresholds to all have >1 child). + if let Some(parent) = node.parent() { + if parent.n_children() == 1 { + continue; + } + if node.is_first_child() && parent.name() == "thresh" { + continue; + } } - "thresh" => { - let thresh = top.verify_threshold(|sub| Self::from_tree(sub).map(Arc::new))?; - // thresh(1) and thresh(n) are disallowed in semantic policies - if thresh.is_or() { - return Err(Error::ParseThreshold(crate::ParseThresholdError::IllegalOr)); + let new = match node.name() { + "UNSATISFIABLE" => { + node.verify_n_children("UNSATISFIABLE", 0..=0) + .map_err(From::from) + .map_err(Error::Parse)?; + Ok(Policy::Unsatisfiable) + } + "TRIVIAL" => { + node.verify_n_children("TRIVIAL", 0..=0) + .map_err(From::from) + .map_err(Error::Parse)?; + Ok(Policy::Trivial) } - if thresh.is_and() { - return Err(Error::ParseThreshold(crate::ParseThresholdError::IllegalAnd)); + "pk" => node + .verify_terminal_parent("pk", "public key") + .map(Policy::Key) + .map_err(Error::Parse), + "after" => node.verify_after().map_err(Error::Parse).map(Policy::After), + "older" => node.verify_older().map_err(Error::Parse).map(Policy::Older), + "sha256" => node + .verify_terminal_parent("sha256", "hash") + .map(Policy::Sha256) + .map_err(Error::Parse), + "hash256" => node + .verify_terminal_parent("hash256", "hash") + .map(Policy::Hash256) + .map_err(Error::Parse), + "ripemd160" => node + .verify_terminal_parent("ripemd160", "hash") + .map(Policy::Ripemd160) + .map_err(Error::Parse), + "hash160" => node + .verify_terminal_parent("hash160", "hash") + .map(Policy::Hash160) + .map_err(Error::Parse), + "and" => { + node.verify_n_children("and", 2..) + .map_err(From::from) + .map_err(Error::Parse)?; + + let child_iter = (0..node.n_children()).map(|_| stack.pop().unwrap()); + let thresh = Threshold::from_iter(node.n_children(), child_iter) + .map_err(Error::Threshold)?; + Ok(Policy::Thresh(thresh)) } + "or" => { + node.verify_n_children("or", 2..) + .map_err(From::from) + .map_err(Error::Parse)?; + let child_iter = (0..node.n_children()).map(|_| stack.pop().unwrap()); + let thresh = Threshold::from_iter(1, child_iter).map_err(Error::Threshold)?; + Ok(Policy::Thresh(thresh)) + } + "thresh" => { + let thresh = node.verify_threshold(|_| Ok::<_, Error>(stack.pop().unwrap()))?; - Ok(Policy::Thresh(thresh)) - } - x => Err(Error::Parse(crate::ParseError::Tree(crate::ParseTreeError::UnknownName { - name: x.to_owned(), - }))), + // thresh(1) and thresh(n) are disallowed in semantic policies + if thresh.is_or() { + return Err(Error::ParseThreshold(crate::ParseThresholdError::IllegalOr)); + } + if thresh.is_and() { + return Err(Error::ParseThreshold(crate::ParseThresholdError::IllegalAnd)); + } + + Ok(Policy::Thresh(thresh)) + } + x => { + Err(Error::Parse(crate::ParseError::Tree(crate::ParseTreeError::UnknownName { + name: x.to_owned(), + }))) + } + }?; + + stack.push(Arc::new(new)); } + + assert_eq!(stack.len(), 1); + Ok(Arc::try_unwrap(stack.pop().unwrap()).unwrap()) } }