From 355f08498caf2617fa8b3e7da1615c03e9ab04da Mon Sep 17 00:00:00 2001 From: John Boyd Date: Mon, 18 Nov 2019 05:19:00 -0500 Subject: [PATCH] Add option to finalize and post transactions separately (for air-gap wallets) (#255) * Add option to output finalized slate * Add subcommand to post a finalized slate file --- controller/src/command.rs | 60 +++++++++++++++++++++++++++++++-------- src/bin/grin-wallet.yml | 21 ++++++++++++++ src/cmd/wallet_args.rs | 23 +++++++++++++++ 3 files changed, 92 insertions(+), 12 deletions(-) diff --git a/controller/src/command.rs b/controller/src/command.rs index 6e52037f..9c691bdb 100644 --- a/controller/src/command.rs +++ b/controller/src/command.rs @@ -406,6 +406,8 @@ where pub struct FinalizeArgs { pub input: String, pub fluff: bool, + pub nopost: bool, + pub dest: Option, } pub fn finalize<'a, L, C, K>( @@ -462,19 +464,27 @@ where })?; } - controller::owner_single_use(wallet.clone(), keychain_mask, |api, m| { - let result = api.post_tx(m, &slate.tx, args.fluff); - match result { - Ok(_) => { - info!("Transaction sent successfully, check the wallet again for confirmation."); - Ok(()) + if !args.nopost { + controller::owner_single_use(wallet.clone(), keychain_mask, |api, m| { + let result = api.post_tx(m, &slate.tx, args.fluff); + match result { + Ok(_) => { + info!( + "Transaction sent successfully, check the wallet again for confirmation." + ); + Ok(()) + } + Err(e) => { + error!("Tx not sent: {}", e); + Err(e) + } } - Err(e) => { - error!("Tx not sent: {}", e); - Err(e) - } - } - })?; + })?; + } + + if args.dest.is_some() { + PathToSlate((&args.dest.unwrap()).into()).put_tx(&slate)?; + } Ok(()) } @@ -720,6 +730,32 @@ where Ok(()) } +/// Post +pub struct PostArgs { + pub input: String, + pub fluff: bool, +} + +pub fn post<'a, L, C, K>( + wallet: Arc>>>, + keychain_mask: Option<&SecretKey>, + args: PostArgs, +) -> Result<(), Error> +where + L: WalletLCProvider<'a, C, K>, + C: NodeClient + 'a, + K: keychain::Keychain + 'a, +{ + let slate = PathToSlate((&args.input).into()).get_tx()?; + + controller::owner_single_use(wallet.clone(), keychain_mask, |api, m| { + api.post_tx(m, &slate.tx, args.fluff)?; + info!("Posted transaction"); + return Ok(()); + })?; + Ok(()) +} + /// Repost pub struct RepostArgs { pub id: u32, diff --git a/src/bin/grin-wallet.yml b/src/bin/grin-wallet.yml index 1703631f..dcd89b5a 100644 --- a/src/bin/grin-wallet.yml +++ b/src/bin/grin-wallet.yml @@ -176,6 +176,15 @@ subcommands: help: Fluff the transaction (ignore Dandelion relay protocol) short: f long: fluff + - nopost: + help: Do not post the transaction. + short: n + long: nopost + - dest: + help: Specify file to save the finalized slate. + short: d + long: dest + takes_value: true - invoice: about: Initialize an invoice transaction. args: @@ -259,6 +268,18 @@ subcommands: short: t long: txid takes_value: true + - post: + about: Posts a finalized transaction to the chain + args: + - input: + help: File name of the transaction to post + short: i + long: input + takes_value: true + - fluff: + help: Fluff the transaction (ignore Dandelion relay protocol) + short: f + long: fluff - repost: about: Reposts a stored, completed but unconfirmed transaction to the chain, or dumps it to a file args: diff --git a/src/cmd/wallet_args.rs b/src/cmd/wallet_args.rs index 1b9a7473..4b285198 100644 --- a/src/cmd/wallet_args.rs +++ b/src/cmd/wallet_args.rs @@ -559,15 +559,24 @@ pub fn parse_receive_args(receive_args: &ArgMatches) -> Result Result { let fluff = args.is_present("fluff"); + let nopost = args.is_present("nopost"); let tx_file = parse_required(args, "input")?; if !Path::new(&tx_file).is_file() { let msg = format!("File {} not found.", tx_file); return Err(ParseError::ArgumentError(msg)); } + + let dest_file = match args.is_present("dest") { + true => Some(args.value_of("dest").unwrap().to_owned()), + false => None, + }; + Ok(command::FinalizeArgs { input: tx_file.to_owned(), fluff: fluff, + nopost: nopost, + dest: dest_file.to_owned(), }) } @@ -738,6 +747,16 @@ pub fn parse_txs_args(args: &ArgMatches) -> Result }) } +pub fn parse_post_args(args: &ArgMatches) -> Result { + let tx_file = parse_required(args, "input")?; + let fluff = args.is_present("fluff"); + + Ok(command::PostArgs { + input: tx_file.to_owned(), + fluff: fluff, + }) +} + pub fn parse_repost_args(args: &ArgMatches) -> Result { let tx_id = match args.value_of("id") { None => None, @@ -1012,6 +1031,10 @@ where wallet_config.dark_background_color_scheme.unwrap_or(true), ) } + ("post", Some(args)) => { + let a = arg_parse!(parse_post_args(&args)); + command::post(wallet, km, a) + } ("repost", Some(args)) => { let a = arg_parse!(parse_repost_args(&args)); command::repost(wallet, km, a)