diff --git a/api/src/foreign.rs b/api/src/foreign.rs index f5e8276..726cc3c 100644 --- a/api/src/foreign.rs +++ b/api/src/foreign.rs @@ -24,6 +24,7 @@ use crate::libwallet::{ use crate::try_slatepack_sync_workflow; use crate::util::secp::key::SecretKey; use crate::util::Mutex; +use libwallet::api_impl::types::update_tx_slate_state; use std::sync::Arc; /// ForeignAPI Middleware Check callback @@ -370,18 +371,34 @@ where )?; match r_addr { Some(a) => { - let tor_config_lock = self.tor_config.lock(); - let res = try_slatepack_sync_workflow( - &ret_slate, - &a, - tor_config_lock.clone(), - None, - true, - self.doctest_mode, - ); + let tc = self.tor_config.lock(); + let can_send = if let Some(tc) = tc.as_ref() { + tc.send_tor(None) + } else { + false + }; + if self.doctest_mode || !can_send { + return Ok(ret_slate); + } + let res = try_slatepack_sync_workflow(&ret_slate, &a, tc.clone(), None, true); match res { - Ok(s) => Ok(s.unwrap()), - Err(_) => Ok(ret_slate), + Ok(s) => { + let parent_key_id = w.parent_key_id(); + match update_tx_slate_state( + w, + (&self.keychain_mask).as_ref(), + &parent_key_id, + &s, + ) { + Ok(_) => {} + Err(e) => error!("Error on updating slate state: {}", e), + } + Ok(s) + } + Err(e) => { + error!("Error on sending over Tor: {}", e); + Ok(ret_slate) + } } } None => Ok(ret_slate), diff --git a/api/src/owner.rs b/api/src/owner.rs index afecfec..853c7e9 100644 --- a/api/src/owner.rs +++ b/api/src/owner.rs @@ -37,7 +37,10 @@ use crate::util::logger::LoggingConfig; use crate::util::secp::{key::SecretKey, pedersen::Commitment}; use crate::util::{from_hex, static_secp_instance, Mutex, ZeroingString}; use grin_wallet_util::OnionV3Address; +use libwallet::api_impl::types::update_tx_slate_state; use std::convert::TryFrom; +use std::fs::File; +use std::io::Write; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{channel, Sender}; use std::sync::Arc; @@ -673,28 +676,20 @@ where Some(sa) => { let tor_config_lock = self.tor_config.lock(); let tc = tor_config_lock.clone(); - let tc = match tc { - Some(mut c) => { - if let Some(skip_tor) = sa.skip_tor { - c.skip_send_attempt = Some(skip_tor); - } - Some(c) - } - None => None, + let can_send = if let Some(tc) = tc.as_ref() { + tc.send_tor(sa.skip_tor) + } else { + false }; - let res = try_slatepack_sync_workflow( - &slate, - &sa.dest, - tc, - None, - false, - self.doctest_mode, - ); + if self.doctest_mode || !can_send { + return Ok(slate); + } + let res = try_slatepack_sync_workflow(&slate, &sa.dest, tc, None, false); match res { - Ok(Some(s)) => { + Ok(s) => { + self.tx_lock_outputs(keychain_mask, &s)?; + let ret_slate = self.finalize_tx(keychain_mask, &s)?; if sa.post_tx { - self.tx_lock_outputs(keychain_mask, &s)?; - let ret_slate = self.finalize_tx(keychain_mask, &s)?; let result = self.post_tx(keychain_mask, &ret_slate, sa.fluff); match result { Ok(_) => { @@ -707,13 +702,13 @@ where } } } else { - self.tx_lock_outputs(keychain_mask, &s)?; - let ret_slate = self.finalize_tx(keychain_mask, &s)?; Ok(ret_slate) } } - Ok(None) => Ok(slate), - Err(_) => Ok(slate), + Err(e) => { + error!("Error on sending over Tor: {}", e); + Ok(slate) + } } } None => Ok(slate), @@ -832,35 +827,49 @@ where slate: &Slate, args: InitTxArgs, ) -> Result { - let mut w_lock = self.wallet_inst.lock(); - let w = w_lock.lc_provider()?.wallet_inst()?; let send_args = args.send_args.clone(); - let slate = owner::process_invoice_tx(w, keychain_mask, slate, args, self.doctest_mode)?; + let slate = { + let mut w_lock = self.wallet_inst.lock(); + let w = w_lock.lc_provider()?.wallet_inst()?; + owner::process_invoice_tx(w, keychain_mask, slate, args, self.doctest_mode)? + }; // Helper functionality. If send arguments exist, attempt to send match send_args { Some(sa) => { let tor_config_lock = self.tor_config.lock(); let tc = tor_config_lock.clone(); - let tc = match tc { - Some(mut c) => { - if let Some(skip_tor) = sa.skip_tor { - c.skip_send_attempt = Some(skip_tor); - } - Some(c) - } - None => None, + let can_send = if let Some(tc) = tc.as_ref() { + tc.send_tor(sa.skip_tor) + } else { + false }; - let res = try_slatepack_sync_workflow( - &slate, - &sa.dest, - tc, - None, - true, - self.doctest_mode, - ); + if self.doctest_mode || !can_send { + return Ok(slate); + } + let res = try_slatepack_sync_workflow(&slate, &sa.dest, tc, None, true); match res { - Ok(s) => Ok(s.unwrap()), - Err(_) => Ok(slate), + Ok(s) => { + // Update slate state. + { + let mut w_lock = self.wallet_inst.lock(); + let w = w_lock.lc_provider()?.wallet_inst()?; + let parent_key_id = w.parent_key_id(); + match update_tx_slate_state(w, keychain_mask, &parent_key_id, &s) { + Ok(_) => {} + Err(e) => error!("Error on updating slate state: {}", e), + } + } + // Output slatepack message to file. + match output_slatepack_file(&self, keychain_mask, &s, &sa.dest) { + Ok(_) => {} + Err(e) => error!("Error on saving output slatepack message: {}", e), + } + Ok(s) + } + Err(e) => { + error!("Error on sending over Tor: {}", e); + Ok(slate) + } } } None => Ok(slate), @@ -2497,13 +2506,7 @@ pub fn try_slatepack_sync_workflow( tor_config: Option, tor_sender: Option, send_to_finalize: bool, - test_mode: bool, -) -> Result, Error> { - if let Some(tc) = &tor_config { - if tc.skip_send_attempt == Some(true) { - return Ok(None); - } - } +) -> Result { let mut ret_slate = Slate::blank(2, false); let mut send_sync = |mut sender: TorSlateSender, method_str: &str| match sender .send_tx(&slate, send_to_finalize) @@ -2521,55 +2524,43 @@ pub fn try_slatepack_sync_workflow( } }; - // Try parsing Slatepack address + // Try parsing Slatepack address. match SlatepackAddress::try_from(dest) { Ok(address) => { - let tor_addr = OnionV3Address::try_from(&address).unwrap(); - // Try sending to the destination via TOR + let tor_addr = OnionV3Address::try_from(&address).map_err(|_| { + Error::SlatepackAddress(format!( + "Destination {} is not a valid Onion address.", + dest + )) + })?; + // Try sending to the destination via Tor. let sender = match tor_sender { None => { - if test_mode { - None - } else { - if let Some(tc) = tor_config { - match TorSlateSender::new(&tor_addr.to_http_str(), tc) { - Ok(s) => Some(s), - Err(e) => { - debug!("Send (TOR): Cannot create TOR Slate sender {:?}", e); - None - } + if let Some(tc) = tor_config { + match TorSlateSender::new(&tor_addr.to_http_str(), tc) { + Ok(s) => s, + Err(e) => { + debug!("Send (Tor): Cannot create TOR Slate sender {:?}", e); + return Err(e); } - } else { - return Err(Error::TorConfig("Tor config is not set".to_string())); } - } - } - Some(s) => { - if test_mode { - None } else { - Some(s) + return Err(Error::TorConfig("Tor config is not set".to_string())); } } + Some(s) => s, }; - if let Some(s) = sender { - warn!("Attempting to send transaction via TOR"); - match send_sync(s, "TOR") { - Ok(_) => return Ok(Some(ret_slate)), - Err(e) => { - debug!("Unable to send via TOR: {}", e); - warn!("Unable to send transaction via TOR"); - } - } + warn!("Attempting to send transaction via Tor"); + match send_sync(sender, "Tor") { + Ok(_) => Ok(ret_slate), + Err(e) => Err(e), } } Err(e) => { - debug!("Send (TOR): Destination is not SlatepackAddress {:?}", e); - warn!("Destination is not a valid Slatepack address. Will output Slatepack.") + error!("Destination {} is not a valid Slatepack address.", dest); + Err(e) } } - - Ok(None) } #[doc(hidden)] @@ -2637,3 +2628,38 @@ macro_rules! doctest_helper_setup_doc_env { let mut $wallet = Arc::new(Mutex::new(wallet)); }; } + +/// Output slatepack message to file. +fn output_slatepack_file( + api: &Owner, + keychain_mask: Option<&SecretKey>, + slate: &Slate, + dest: &str, +) -> Result<(), Error> +where + L: WalletLCProvider<'static, C, K> + 'static, + C: NodeClient + 'static, + K: Keychain + 'static, +{ + let address = match SlatepackAddress::try_from(dest) { + Ok(a) => Some(a), + Err(_) => None, + }; + // encrypt for recipient by default + let recipients = match address.clone() { + Some(a) => vec![a], + None => vec![], + }; + let message = api.create_slatepack_message(keychain_mask, &slate, Some(0), recipients)?; + let tld = api.get_top_level_directory()?; + + // Create a directory to which files will be output. + let slate_dir = format!("{}/{}", tld, "slatepack"); + let _ = std::fs::create_dir_all(slate_dir.clone()); + let out_file_name = format!("{}/{}.{}.slatepack", slate_dir, slate.id, slate.state); + + let mut output = File::create(out_file_name.clone())?; + output.write_all(&message.as_bytes())?; + output.sync_all()?; + Ok(()) +} diff --git a/config/src/types.rs b/config/src/types.rs index a40a720..31e5ac4 100644 --- a/config/src/types.rs +++ b/config/src/types.rs @@ -164,18 +164,18 @@ impl From for ConfigError { pub struct TorConfig { /// whether to use integrated Tor library pub use_integrated: Option, - /// whether to skip any attempts to send via TOR + /// whether to skip any attempts to send via Tor pub skip_send_attempt: Option, - /// Whether to start tor listener on listener startup (default true) + /// Whether to start Tor listener on listener startup (default true) pub use_tor_listener: bool, /// Just the address of the socks proxy for now pub socks_proxy_addr: String, /// Send configuration directory pub send_config_dir: String, - /// tor bridge config + /// Tor bridge config #[serde(default)] pub bridge: TorBridgeConfig, - /// tor proxy config + /// Tor proxy config #[serde(default)] pub proxy: TorProxyConfig, } @@ -194,6 +194,16 @@ impl Default for TorConfig { } } +impl TorConfig { + /// Check if attempt to send over Tor is needed using provided possible argument at priority. + pub fn send_tor(&self, skip_arg: Option) -> bool { + if let Some(skip_tor) = skip_arg { + return !skip_tor; + } + !self.skip_send_attempt.unwrap_or(false) + } +} + /// Tor Bridge Config #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct TorBridgeConfig { diff --git a/controller/src/command.rs b/controller/src/command.rs index 0731e2b..cb76d26 100644 --- a/controller/src/command.rs +++ b/controller/src/command.rs @@ -40,6 +40,7 @@ use std::sync::Arc; use std::thread; use std::time::Duration; use uuid::Uuid; +use grin_wallet_libwallet::api_impl::types::update_tx_slate_state; fn show_recovery_phrase(phrase: ZeroingString) { println!("Your recovery phrase is:"); @@ -431,18 +432,37 @@ where if let Some(b) = args.bridge.clone() { c.bridge.bridge_line = Some(b); } - if let Some(s) = args.skip_tor { - c.skip_send_attempt = Some(s); - } Some(c) } None => None, }; - let res = try_slatepack_sync_workflow(&slate, &args.dest, tor_config, None, false, test_mode); + let output_sp = || -> Result<(), Error> { + Ok(output_slatepack( + owner_api, + keychain_mask, + &slate, + args.dest.as_str(), + args.outfile, + true, + false, + args.slatepack_qr, + )?) + }; + + let can_send = if let Some(tc) = tor_config.as_ref() { + tc.send_tor(args.skip_tor) + } else { + false + }; + if test_mode || !can_send { + return output_sp(); + } + + let res = try_slatepack_sync_workflow(&slate, &args.dest, tor_config, None, false); match res { - Ok(Some(s)) => { + Ok(s) => { controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| { api.tx_lock_outputs(m, &s)?; let ret_slate = api.finalize_tx(m, &s)?; @@ -459,19 +479,10 @@ where } })?; } - Ok(None) => { - output_slatepack( - owner_api, - keychain_mask, - &slate, - args.dest.as_str(), - args.outfile, - true, - false, - args.slatepack_qr, - )?; + Err(e) => { + error!("Error sending slate sync: {}", e); + output_sp()?; } - Err(e) => return Err(e.into()), } Ok(()) } @@ -668,9 +679,6 @@ where if let Some(b) = args.bridge { c.bridge.bridge_line = Some(b); } - if let Some(s) = args.skip_tor { - c.skip_send_attempt = Some(s); - } Some(c) } None => None, @@ -686,33 +694,55 @@ where None => String::from(""), }; - let res = try_slatepack_sync_workflow(&slate, &dest, tor_config, None, true, test_mode); + let output_sp = || -> Result<(), Error> { + Ok(output_slatepack( + owner_api, + keychain_mask, + &slate, + &dest, + args.outfile, + false, + false, + args.slatepack_qr, + )?) + }; + + let can_send = if let Some(tc) = tor_config.as_ref() { + tc.send_tor(args.skip_tor) + } else { + false + }; + if test_mode || !can_send { + return output_sp(); + } + + let res = try_slatepack_sync_workflow(&slate, &dest, tor_config, None, true); match res { - Ok(Some(_)) => { + Ok(s) => { + // Update slate state. + { + let mut w_lock = owner_api.wallet_inst.lock(); + let w = w_lock.lc_provider()?.wallet_inst()?; + let parent_key_id = w.parent_key_id(); + match update_tx_slate_state(w, keychain_mask, &parent_key_id, &s) { + Ok(_) => {} + Err(e) => error!("Error on updating slate state: {}", e), + } + } println!(); println!( "Transaction received and sent back to sender at {} for finalization.", dest ); println!(); - Ok(()) } - Ok(None) => { - output_slatepack( - owner_api, - keychain_mask, - &slate, - &dest, - args.outfile, - false, - false, - args.slatepack_qr, - )?; - Ok(()) + Err(e) => { + error!("Error sending slate sync: {}", e); + output_sp()?; } - Err(e) => Err(e.into()), } + Ok(()) } pub fn unpack( @@ -1012,41 +1042,60 @@ where if let Some(b) = args.bridge { c.bridge.bridge_line = Some(b); } - if let Some(skip_tor) = args.skip_tor { - c.skip_send_attempt = Some(skip_tor); - } Some(c) } None => None, }; - let res = try_slatepack_sync_workflow(&slate, &dest, tor_config, None, true, test_mode); + let output_sp = || -> Result<(), Error> { + Ok(output_slatepack( + owner_api, + keychain_mask, + &slate, + &dest, + args.outfile, + true, + false, + args.slatepack_qr, + )?) + }; + + let can_send = if let Some(tc) = tor_config.as_ref() { + tc.send_tor(args.skip_tor) + } else { + false + }; + if test_mode || !can_send { + return output_sp(); + } + + let res = try_slatepack_sync_workflow(&slate, &dest, tor_config, None, true); match res { - Ok(Some(_)) => { + Ok(s) => { + // Update slate state. + { + let mut w_lock = owner_api.wallet_inst.lock(); + let w = w_lock.lc_provider()?.wallet_inst()?; + let parent_key_id = w.parent_key_id(); + match update_tx_slate_state(w, keychain_mask, &parent_key_id, &s) { + Ok(_) => {} + Err(e) => error!("Error on updating slate state: {}", e), + } + } println!(); println!( "Transaction paid and sent back to initiator at {} for finalization.", dest ); println!(); - Ok(()) } - Ok(None) => { - output_slatepack( - owner_api, - keychain_mask, - &slate, - &dest, - args.outfile, - true, - false, - args.slatepack_qr, - )?; - Ok(()) + Err(e) => { + error!("Error sending slate sync: {}", e); + output_sp()?; } - Err(e) => Err(e.into()), } + Ok(()) } /// Info command args diff --git a/impls/src/tor/arti.rs b/impls/src/tor/arti.rs index 3b88b2e..e5af8d3 100644 --- a/impls/src/tor/arti.rs +++ b/impls/src/tor/arti.rs @@ -300,9 +300,7 @@ fn init_client( cached_client_config.replace((client.clone(), config.clone())); Ok((client, config)) } - Err(e) => { - Err(e) - }, + Err(e) => Err(e), }; } diff --git a/libwallet/src/api_impl/foreign.rs b/libwallet/src/api_impl/foreign.rs index f3cb611..036027a 100644 --- a/libwallet/src/api_impl/foreign.rs +++ b/libwallet/src/api_impl/foreign.rs @@ -14,11 +14,11 @@ //! Generic implementation of owner API functions -use grin_keychain::Identifier; use strum::IntoEnumIterator; use super::owner::tx_lock_outputs; use crate::api_impl::owner::{check_ttl, post_tx}; +use crate::api_impl::types::update_tx_slate_state; use crate::backend::WalletBackend; use crate::grin_core::core::FeeFields; use crate::grin_keychain::Keychain; @@ -236,39 +236,3 @@ where } Ok(sl) } - -/// Update transaction slate state. -fn update_tx_slate_state( - wallet: &mut WalletBackend, - keychain_mask: Option<&SecretKey>, - parent_key_id: &Identifier, - slate: &Slate, -) -> Result<(), Error> -where - C: NodeClient, - K: Keychain, -{ - let mut bad_records = 0; - let tx = wallet - .tx_log_iter()? - .filter(|tx| { - if tx.is_err() { - bad_records += 1; - } - tx.is_ok() - }) - .map(|tx| tx.unwrap()) - .find(|tx| tx.tx_slate_id == Some(slate.id)); - if let Some(mut tx) = tx { - let mut batch = wallet.batch(keychain_mask)?; - tx.tx_slate_state = Some(slate.state.clone()); - batch.save_tx_log_entry(tx.clone(), parent_key_id)?; - batch.commit()?; - } else { - return Err(Error::Backend(format!( - "Tx log entry with slate id {} not found, there are {} bad tx log records", - slate.id, bad_records - ))); - } - Ok(()) -} diff --git a/libwallet/src/api_impl/types.rs b/libwallet/src/api_impl/types.rs index 10d32f3..8338b7e 100644 --- a/libwallet/src/api_impl/types.rs +++ b/libwallet/src/api_impl/types.rs @@ -21,10 +21,12 @@ use crate::grin_util::secp::pedersen; use crate::slate_versions::ser as dalek_ser; use crate::slate_versions::SlateVersion; use crate::types::OutputData; -use crate::SlatepackAddress; +use crate::{Error, NodeClient, Slate, SlatepackAddress, WalletBackend}; use chrono::prelude::*; use ed25519_dalek::Signature as DalekSignature; +use grin_keychain::Keychain; +use grin_util::secp::SecretKey; pub use crate::mwixnet::{Hop, MixnetReqCreationParams, SwapReq}; @@ -344,3 +346,39 @@ pub struct BuiltOutput { /// Output pub output: Output, } + +/// Update transaction slate state. +pub fn update_tx_slate_state( + wallet: &mut WalletBackend, + keychain_mask: Option<&SecretKey>, + parent_key_id: &Identifier, + slate: &Slate, +) -> Result<(), Error> +where + C: NodeClient, + K: Keychain, +{ + let mut bad_records = 0; + let tx = wallet + .tx_log_iter()? + .filter(|tx| { + if tx.is_err() { + bad_records += 1; + } + tx.is_ok() + }) + .map(|tx| tx.unwrap()) + .find(|tx| tx.tx_slate_id == Some(slate.id)); + if let Some(mut tx) = tx { + let mut batch = wallet.batch(keychain_mask)?; + tx.tx_slate_state = Some(slate.state.clone()); + batch.save_tx_log_entry(tx.clone(), parent_key_id)?; + batch.commit()?; + } else { + return Err(Error::Backend(format!( + "Tx log entry with slate id {} not found, there are {} bad tx log records", + slate.id, bad_records + ))); + } + Ok(()) +}