Skip to content

Commit 30d2d11

Browse files
committed
miniscript: factor out a couple parts of Terminal::from_tree
In a later commit we will rewrite Terminal::from_tree entirely. But the beginning and end (which parse out and then apply "wrappers", the modifiers added to miniscript fragments using `:`) will remain the same. Pull this logic out into a pair of helper functions. This also reduces the total amount of indendentation, reduces function size, and reduces the total number of variables in scope at once. So this is a useful refactor independent of any future work.
1 parent f22dc3a commit 30d2d11

File tree

2 files changed

+96
-66
lines changed

2 files changed

+96
-66
lines changed

src/miniscript/astelem.rs

Lines changed: 3 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -250,51 +250,8 @@ impl<Pk: crate::FromStrKey, Ctx: ScriptContext> crate::expression::FromTree
250250

251251
impl<Pk: crate::FromStrKey, Ctx: ScriptContext> crate::expression::FromTree for Terminal<Pk, Ctx> {
252252
fn from_tree(top: &expression::Tree) -> Result<Terminal<Pk, Ctx>, Error> {
253-
let mut aliased_wrap;
254-
let frag_name;
255-
let frag_wrap;
256-
let mut name_split = top.name.split(':');
257-
match (name_split.next(), name_split.next(), name_split.next()) {
258-
(None, _, _) => {
259-
frag_name = "";
260-
frag_wrap = "";
261-
}
262-
(Some(name), None, _) => {
263-
if name == "pk" {
264-
frag_name = "pk_k";
265-
frag_wrap = "c";
266-
} else if name == "pkh" {
267-
frag_name = "pk_h";
268-
frag_wrap = "c";
269-
} else {
270-
frag_name = name;
271-
frag_wrap = "";
272-
}
273-
}
274-
(Some(wrap), Some(name), None) => {
275-
if wrap.is_empty() {
276-
return Err(Error::Unexpected(top.name.to_owned()));
277-
}
278-
if name == "pk" {
279-
frag_name = "pk_k";
280-
aliased_wrap = wrap.to_owned();
281-
aliased_wrap.push('c');
282-
frag_wrap = &aliased_wrap;
283-
} else if name == "pkh" {
284-
frag_name = "pk_h";
285-
aliased_wrap = wrap.to_owned();
286-
aliased_wrap.push('c');
287-
frag_wrap = &aliased_wrap;
288-
} else {
289-
frag_name = name;
290-
frag_wrap = wrap;
291-
}
292-
}
293-
(Some(_), Some(_), Some(_)) => {
294-
return Err(Error::MultiColon(top.name.to_owned()));
295-
}
296-
}
297-
let mut unwrapped = match (frag_name, top.args.len()) {
253+
let (frag_name, frag_wrap) = super::split_expression_name(top.name)?;
254+
let unwrapped = match (frag_name, top.args.len()) {
298255
("expr_raw_pkh", 1) => expression::terminal(&top.args[0], |x| {
299256
hash160::Hash::from_str(x).map(Terminal::RawPkH)
300257
}),
@@ -388,27 +345,7 @@ impl<Pk: crate::FromStrKey, Ctx: ScriptContext> crate::expression::FromTree for
388345
top.args.len(),
389346
))),
390347
}?;
391-
for ch in frag_wrap.chars().rev() {
392-
// Check whether the wrapper is valid under the current context
393-
let ms = Miniscript::from_ast(unwrapped)?;
394-
Ctx::check_global_validity(&ms)?;
395-
match ch {
396-
'a' => unwrapped = Terminal::Alt(Arc::new(ms)),
397-
's' => unwrapped = Terminal::Swap(Arc::new(ms)),
398-
'c' => unwrapped = Terminal::Check(Arc::new(ms)),
399-
'd' => unwrapped = Terminal::DupIf(Arc::new(ms)),
400-
'v' => unwrapped = Terminal::Verify(Arc::new(ms)),
401-
'j' => unwrapped = Terminal::NonZero(Arc::new(ms)),
402-
'n' => unwrapped = Terminal::ZeroNotEqual(Arc::new(ms)),
403-
't' => unwrapped = Terminal::AndV(Arc::new(ms), Arc::new(Miniscript::TRUE)),
404-
'u' => unwrapped = Terminal::OrI(Arc::new(ms), Arc::new(Miniscript::FALSE)),
405-
'l' => unwrapped = Terminal::OrI(Arc::new(Miniscript::FALSE), Arc::new(ms)),
406-
x => return Err(Error::UnknownWrapper(x)),
407-
}
408-
}
409-
// Check whether the unwrapped miniscript is valid under the current context
410-
let ms = Miniscript::from_ast(unwrapped)?;
411-
Ctx::check_global_validity(&ms)?;
348+
let ms = super::wrap_into_miniscript(unwrapped, frag_wrap)?;
412349
Ok(ms.node)
413350
}
414351
}

src/miniscript/mod.rs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,99 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
524524
}
525525
}
526526

527+
/// Utility function used when parsing a script from an expression tree.
528+
///
529+
/// Checks that the name of each fragment has at most one `:`, splits
530+
/// the name at the `:`, and implements aliases for the old `pk`/`pk_h`
531+
/// fragments.
532+
///
533+
/// Returns the fragment name (right of the `:`) and a list of wrappers
534+
/// (left of the `:`).
535+
fn split_expression_name(name: &str) -> Result<(&str, Cow<str>), Error> {
536+
let mut aliased_wrap;
537+
let frag_name;
538+
let frag_wrap;
539+
let mut name_split = name.split(':');
540+
match (name_split.next(), name_split.next(), name_split.next()) {
541+
(None, _, _) => {
542+
frag_name = "";
543+
frag_wrap = "".into();
544+
}
545+
(Some(name), None, _) => {
546+
if name == "pk" {
547+
frag_name = "pk_k";
548+
frag_wrap = "c".into();
549+
} else if name == "pkh" {
550+
frag_name = "pk_h";
551+
frag_wrap = "c".into();
552+
} else {
553+
frag_name = name;
554+
frag_wrap = "".into();
555+
}
556+
}
557+
(Some(wrap), Some(name), None) => {
558+
if wrap.is_empty() {
559+
return Err(Error::Unexpected(name.to_owned()));
560+
}
561+
if name == "pk" {
562+
frag_name = "pk_k";
563+
aliased_wrap = wrap.to_owned();
564+
aliased_wrap.push('c');
565+
frag_wrap = aliased_wrap.into();
566+
} else if name == "pkh" {
567+
frag_name = "pk_h";
568+
aliased_wrap = wrap.to_owned();
569+
aliased_wrap.push('c');
570+
frag_wrap = aliased_wrap.into();
571+
} else {
572+
frag_name = name;
573+
frag_wrap = wrap.into();
574+
}
575+
}
576+
(Some(_), Some(_), Some(_)) => {
577+
return Err(Error::MultiColon(name.to_owned()));
578+
}
579+
}
580+
Ok((frag_name, frag_wrap))
581+
}
582+
583+
/// Utility function used when parsing a script from an expression tree.
584+
///
585+
/// Once a Miniscript fragment has been parsed into a terminal, apply any
586+
/// wrappers that were included in its name.
587+
fn wrap_into_miniscript<Pk, Ctx>(
588+
term: Terminal<Pk, Ctx>,
589+
frag_wrap: Cow<str>,
590+
) -> Result<Miniscript<Pk, Ctx>, Error>
591+
where
592+
Pk: MiniscriptKey,
593+
Ctx: ScriptContext,
594+
{
595+
let mut unwrapped = term;
596+
for ch in frag_wrap.chars().rev() {
597+
// Check whether the wrapper is valid under the current context
598+
let ms = Miniscript::from_ast(unwrapped)?;
599+
Ctx::check_global_validity(&ms)?;
600+
match ch {
601+
'a' => unwrapped = Terminal::Alt(Arc::new(ms)),
602+
's' => unwrapped = Terminal::Swap(Arc::new(ms)),
603+
'c' => unwrapped = Terminal::Check(Arc::new(ms)),
604+
'd' => unwrapped = Terminal::DupIf(Arc::new(ms)),
605+
'v' => unwrapped = Terminal::Verify(Arc::new(ms)),
606+
'j' => unwrapped = Terminal::NonZero(Arc::new(ms)),
607+
'n' => unwrapped = Terminal::ZeroNotEqual(Arc::new(ms)),
608+
't' => unwrapped = Terminal::AndV(Arc::new(ms), Arc::new(Miniscript::TRUE)),
609+
'u' => unwrapped = Terminal::OrI(Arc::new(ms), Arc::new(Miniscript::FALSE)),
610+
'l' => unwrapped = Terminal::OrI(Arc::new(Miniscript::FALSE), Arc::new(ms)),
611+
x => return Err(Error::UnknownWrapper(x)),
612+
}
613+
}
614+
// Check whether the unwrapped miniscript is valid under the current context
615+
let ms = Miniscript::from_ast(unwrapped)?;
616+
Ctx::check_global_validity(&ms)?;
617+
Ok(ms)
618+
}
619+
527620
impl<Pk: crate::FromStrKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
528621
/// Attempt to parse an insane(scripts don't clear sanity checks)
529622
/// from string into a Miniscript representation.

0 commit comments

Comments
 (0)