diff --git a/src/bin/cmd/wallet.rs b/src/bin/cmd/wallet.rs index 553b3bb3..f7bedad2 100644 --- a/src/bin/cmd/wallet.rs +++ b/src/bin/cmd/wallet.rs @@ -603,22 +603,49 @@ pub fn wallet_command(wallet_args: &ArgMatches, config: GlobalWalletConfig) -> i } } ("cancel", Some(tx_args)) => { - let tx_id = tx_args - .value_of("id") - .ok_or_else(|| { - ErrorKind::GenericError("'id' argument (-i) is required.".to_string()) - }).and_then(|v| { - v.parse().map_err(|e| { - ErrorKind::GenericError(format!( + let mut tx_id_string = ""; + let tx_id = match tx_args.value_of("id") { + None => None, + Some(tx) => match tx.parse() { + Ok(t) => { + tx_id_string = tx; + Some(t) + } + Err(e) => { + return Err(ErrorKind::GenericError(format!( "Could not parse id parameter. e={:?}", - e - )) - }) - })?; - let result = api.cancel_tx(tx_id); + e, + )).into()); + } + }, + }; + let tx_slate_id = match tx_args.value_of("txid") { + None => None, + Some(tx) => match tx.parse() { + Ok(t) => { + tx_id_string = tx; + Some(t) + } + Err(e) => { + return Err(ErrorKind::GenericError(format!( + "Could not parse txid parameter. e={:?}", + e, + )).into()); + } + }, + }; + if (tx_id.is_none() && tx_slate_id.is_none()) + || (tx_id.is_some() && tx_slate_id.is_some()) + { + return Err(ErrorKind::GenericError(format!( + "'id' (-i) or 'txid' (-t) argument is required." + )).into()); + } + + let result = api.cancel_tx(tx_id, tx_slate_id); match result { Ok(_) => { - info!("Transaction {} Cancelled", tx_id); + info!("Transaction {} Cancelled", tx_id_string); Ok(()) } Err(e) => { diff --git a/src/bin/grin.rs b/src/bin/grin.rs index 8679f7f0..704fda17 100644 --- a/src/bin/grin.rs +++ b/src/bin/grin.rs @@ -321,6 +321,11 @@ fn real_main() -> i32 { .help("The ID of the transaction to cancel") .short("i") .long("id") + .takes_value(true)) + .arg(Arg::with_name("txid") + .help("The TxID of the transaction to cancel") + .short("t") + .long("txid") .takes_value(true))) .subcommand(SubCommand::with_name("info") diff --git a/wallet/src/libwallet/api.rs b/wallet/src/libwallet/api.rs index 920559fb..1635e740 100644 --- a/wallet/src/libwallet/api.rs +++ b/wallet/src/libwallet/api.rs @@ -322,7 +322,11 @@ where /// output if you're recipient), and unlock all locked outputs associated /// with the transaction used when a transaction is created but never /// posted - pub fn cancel_tx(&mut self, tx_id: u32) -> Result<(), Error> { + pub fn cancel_tx( + &mut self, + tx_id: Option, + tx_slate_id: Option, + ) -> Result<(), Error> { let mut w = self.wallet.lock(); w.open_with_credentials()?; let parent_key_id = w.parent_key_id(); @@ -331,7 +335,7 @@ where "Can't contact running Grin node. Not Cancelling.", ))?; } - tx::cancel_tx(&mut *w, &parent_key_id, tx_id)?; + tx::cancel_tx(&mut *w, &parent_key_id, tx_id, tx_slate_id)?; w.close()?; Ok(()) } diff --git a/wallet/src/libwallet/controller.rs b/wallet/src/libwallet/controller.rs index d35f25f5..75bdc170 100644 --- a/wallet/src/libwallet/controller.rs +++ b/wallet/src/libwallet/controller.rs @@ -342,7 +342,7 @@ where let params = parse_params(&req); if let Some(id_string) = params.get("id") { Box::new(match id_string[0].parse() { - Ok(id) => match api.cancel_tx(id) { + Ok(id) => match api.cancel_tx(Some(id), None) { Ok(_) => ok(()), Err(e) => { error!("cancel_tx: failed with error: {}", e); @@ -356,9 +356,25 @@ where ).into()) } }) + } else if let Some(tx_id_string) = params.get("tx_id") { + Box::new(match tx_id_string[0].parse() { + Ok(tx_id) => match api.cancel_tx(None, Some(tx_id)) { + Ok(_) => ok(()), + Err(e) => { + error!("cancel_tx: failed with error: {}", e); + err(e) + } + }, + Err(e) => { + error!("cancel_tx: could not parse tx_id: {}", e); + err(ErrorKind::TransactionCancellationError( + "cancel_tx: cannot cancel transaction. Could not parse tx_id in request.", + ).into()) + } + }) } else { Box::new(err(ErrorKind::TransactionCancellationError( - "cancel_tx: Cannot cancel transaction. Missing id param in request.", + "cancel_tx: Cannot cancel transaction. Missing id or tx_id param in request.", ).into())) } } diff --git a/wallet/src/libwallet/error.rs b/wallet/src/libwallet/error.rs index d7dc888e..7a84a2a5 100644 --- a/wallet/src/libwallet/error.rs +++ b/wallet/src/libwallet/error.rs @@ -150,11 +150,11 @@ pub enum ErrorKind { /// Transaction doesn't exist #[fail(display = "Transaction {} doesn't exist", _0)] - TransactionDoesntExist(u32), + TransactionDoesntExist(String), /// Transaction already rolled back #[fail(display = "Transaction {} cannot be cancelled", _0)] - TransactionNotCancellable(u32), + TransactionNotCancellable(String), /// Cancellation error #[fail(display = "Cancellation Error: {}", _0)] diff --git a/wallet/src/libwallet/internal/tx.rs b/wallet/src/libwallet/internal/tx.rs index 3aa5a3fe..dd3a885a 100644 --- a/wallet/src/libwallet/internal/tx.rs +++ b/wallet/src/libwallet/internal/tx.rs @@ -16,6 +16,7 @@ use std::sync::Arc; use util::RwLock; +use uuid::Uuid; use core::core::verifier_cache::LruVerifierCache; use core::core::Transaction; @@ -153,26 +154,33 @@ where pub fn cancel_tx( wallet: &mut T, parent_key_id: &Identifier, - tx_id: u32, + tx_id: Option, + tx_slate_id: Option, ) -> Result<(), Error> where T: WalletBackend, C: WalletClient, K: Keychain, { - let tx_vec = updater::retrieve_txs(wallet, Some(tx_id), None, &parent_key_id)?; + let mut tx_id_string = String::new(); + if let Some(tx_id) = tx_id { + tx_id_string = tx_id.to_string(); + } else if let Some(tx_slate_id) = tx_slate_id { + tx_id_string = tx_slate_id.to_string(); + } + let tx_vec = updater::retrieve_txs(wallet, tx_id, tx_slate_id, &parent_key_id)?; if tx_vec.len() != 1 { - return Err(ErrorKind::TransactionDoesntExist(tx_id))?; + return Err(ErrorKind::TransactionDoesntExist(tx_id_string))?; } let tx = tx_vec[0].clone(); if tx.tx_type != TxLogEntryType::TxSent && tx.tx_type != TxLogEntryType::TxReceived { - return Err(ErrorKind::TransactionNotCancellable(tx_id))?; + return Err(ErrorKind::TransactionNotCancellable(tx_id_string))?; } if tx.confirmed == true { - return Err(ErrorKind::TransactionNotCancellable(tx_id))?; + return Err(ErrorKind::TransactionNotCancellable(tx_id_string))?; } // get outputs associated with tx - let res = updater::retrieve_outputs(wallet, false, Some(tx_id), &parent_key_id)?; + let res = updater::retrieve_outputs(wallet, false, Some(tx.id), &parent_key_id)?; let outputs = res.iter().map(|(out, _)| out).cloned().collect(); updater::cancel_tx_and_outputs(wallet, tx, outputs, parent_key_id)?; Ok(()) @@ -192,7 +200,7 @@ where { let tx_vec = updater::retrieve_txs(wallet, Some(tx_id), None, parent_key_id)?; if tx_vec.len() != 1 { - return Err(ErrorKind::TransactionDoesntExist(tx_id))?; + return Err(ErrorKind::TransactionDoesntExist(tx_id.to_string()))?; } let tx = tx_vec[0].clone(); Ok((tx.confirmed, tx.tx_hex))