diff --git a/src/bin/hal/cmd/miniscript.rs b/src/bin/hal/cmd/miniscript.rs index 094d440..3f843e9 100644 --- a/src/bin/hal/cmd/miniscript.rs +++ b/src/bin/hal/cmd/miniscript.rs @@ -1,4 +1,4 @@ -use bitcoin::hashes::hex::FromHex; +use bitcoin::hashes::hex::{FromHex, ToHex}; use bitcoin::Script; use clap; use hal::miniscript::{ @@ -17,6 +17,7 @@ pub fn subcommand<'a>() -> clap::App<'a, 'a> { .subcommand(cmd_inspect()) .subcommand(cmd_parse()) .subcommand(cmd_policy()) + .subcommand(cmd_compile()) } pub fn execute<'a>(args: &clap::ArgMatches<'a>) { @@ -25,6 +26,7 @@ pub fn execute<'a>(args: &clap::ArgMatches<'a>) { ("inspect", Some(ref m)) => exec_inspect(&m), ("parse", Some(ref m)) => exec_parse(&m), ("policy", Some(ref m)) => exec_policy(&m), + ("compile", Some(ref m)) => exec_compile(&m), (_, _) => unreachable!("clap prints help"), }; } @@ -236,6 +238,59 @@ fn exec_policy<'a>(args: &clap::ArgMatches<'a>) { } } +fn cmd_compile<'a>() -> clap::App<'a, 'a> { + cmd::subcommand("compile", "compile a policy into a script") + .arg(args::arg("policy", "the miniscript policy to compile").required(false)) + .arg( + clap::Arg::with_name("type") + .long("type") + .takes_value(true) + .possible_values(&["bare", "p2sh", "segwitv0", "tapscript"]) + .default_value("tapscript") + .help("script type to compile to"), + ) +} + +fn exec_compile<'a>(args: &clap::ArgMatches<'a>) { + let policy_str = util::arg_or_stdin(args, "policy"); + let script_type = args.value_of("type").unwrap(); + + let result = policy_str + .parse::>() + .map_err(|e| format!("Invalid concrete policy: {}", e)) + .and_then(|concrete| { + match script_type { + "bare" => policy::compiler::best_compilation::<_, BareCtx>(&concrete) + .map(|ms| ms.encode()), + "p2sh" => policy::compiler::best_compilation::<_, Legacy>(&concrete) + .map(|ms| ms.encode()), + "segwitv0" => policy::compiler::best_compilation::<_, Segwitv0>(&concrete) + .map(|ms| ms.encode()), + "tapscript" => policy::compiler::best_compilation::<_, miniscript::Tap>(&concrete) + .map(|ms| ms.encode()), + _ => unreachable!("clap enforces valid values"), + } + .map_err(|e| format!("Compilation error: {}", e)) + }); + + match result { + Ok(script) => { + #[derive(serde::Serialize)] + struct ScriptOutput { + hex: String, + asm: String, + } + let output = ScriptOutput { + hex: script.as_bytes().to_hex(), + asm: script.to_string(), + }; + + args.print_output(&output) + } + Err(e) => exit!("{}", e), + } +} + trait FromScriptContexts: Sized { fn from_bare( ms: Miniscript,