Merge pull request #763 from ardocrat/pay_tor_result

Optimize tor sync flow
This commit is contained in:
wiesche
2026-06-23 23:33:19 +02:00
committed by GitHub
7 changed files with 298 additions and 196 deletions
+28 -11
View File
@@ -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),
+111 -85
View File
@@ -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<Slate, Error> {
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<TorConfig>,
tor_sender: Option<TorSlateSender>,
send_to_finalize: bool,
test_mode: bool,
) -> Result<Option<Slate>, Error> {
if let Some(tc) = &tor_config {
if tc.skip_send_attempt == Some(true) {
return Ok(None);
}
}
) -> Result<Slate, Error> {
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<L, C, K>(
api: &Owner<L, C, K>,
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(())
}
+14 -4
View File
@@ -164,18 +164,18 @@ impl From<io::Error> for ConfigError {
pub struct TorConfig {
/// whether to use integrated Tor library
pub use_integrated: Option<bool>,
/// whether to skip any attempts to send via TOR
/// whether to skip any attempts to send via Tor
pub skip_send_attempt: Option<bool>,
/// 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>) -> 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 {
+104 -55
View File
@@ -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<L, C, K>(
@@ -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
+1 -3
View File
@@ -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),
};
}
+1 -37
View File
@@ -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<C, K>(
wallet: &mut WalletBackend<C, K>,
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(())
}
+39 -1
View File
@@ -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<C, K>(
wallet: &mut WalletBackend<C, K>,
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(())
}