diff --git a/swap/src/asb/network.rs b/swap/src/asb/network.rs index 181ec9bc..b2a680a4 100644 --- a/swap/src/asb/network.rs +++ b/swap/src/asb/network.rs @@ -30,14 +30,27 @@ use uuid::Uuid; pub mod transport { use super::*; + use crate::network::tor_transport::TorDialOnlyTransport; + use libp2p::core::transport::OptionalTransport; /// Creates the libp2p transport for the ASB. - pub fn new(identity: &identity::Keypair) -> Result> { + pub fn new( + identity: &identity::Keypair, + maybe_tor_socks5_port: Option, + ) -> Result> { let tcp = TokioTcpConfig::new().nodelay(true); let tcp_with_dns = TokioDnsConfig::system(tcp)?; let websocket_with_dns = WsConfig::new(tcp_with_dns.clone()); - let transport = tcp_with_dns.or_transport(websocket_with_dns).boxed(); + let maybe_tor_transport = match maybe_tor_socks5_port { + Some(port) => OptionalTransport::some(TorDialOnlyTransport::new(port)), + None => OptionalTransport::none(), + }; + + let transport = maybe_tor_transport + .or_transport(tcp_with_dns) + .or_transport(websocket_with_dns) + .boxed(); authenticate_and_multiplex(transport, identity) } diff --git a/swap/src/bin/asb.rs b/swap/src/bin/asb.rs index ff3b0e4c..0510191f 100644 --- a/swap/src/bin/asb.rs +++ b/swap/src/bin/asb.rs @@ -100,6 +100,36 @@ async fn main() -> Result<()> { let seed = Seed::from_file_or_generate(&config.data.dir).expect("Could not retrieve/initialize seed"); + let tor_client = + tor::Client::new(config.tor.socks5_port).with_control_port(config.tor.control_port); + let _ac = match tor_client.assert_tor_running().await { + Ok(_) => { + tracing::info!("Setting up Tor hidden service"); + let ac = + register_tor_services(config.network.clone().listen, tor_client, &seed).await?; + Some(ac) + } + Err(_) => { + tracing::warn!("Tor not found. Running on clear net"); + None + } + }; + let tor_port = if _ac.is_some() { + config.tor.socks5_port + } else { + 0u16 + }; + let proxy_string = if tor_port != 0u16 { + format!("127.0.0.1:{}", tor_port) + } else { + "".to_string() + }; + if proxy_string.is_empty() { + tracing::info!(%proxy_string, "Not using SOCKS5 proxy"); + } else { + tracing::info!(%proxy_string, "Using SOCKS5 proxy at"); + } + match cmd { Command::Start { resume_only } => { // check and warn for duplicate rendezvous points @@ -140,29 +170,13 @@ async fn main() -> Result<()> { } } - let bitcoin_wallet = init_bitcoin_wallet(&config, &seed, env_config).await?; + let bitcoin_wallet = + init_bitcoin_wallet(&config, &seed, env_config, proxy_string).await?; let bitcoin_balance = bitcoin_wallet.balance().await?; tracing::info!(%bitcoin_balance, "Bitcoin wallet balance"); let kraken_price_updates = kraken::connect(config.maker.price_ticker_ws_url.clone())?; - // setup Tor hidden services - let tor_client = - tor::Client::new(config.tor.socks5_port).with_control_port(config.tor.control_port); - let _ac = match tor_client.assert_tor_running().await { - Ok(_) => { - tracing::info!("Setting up Tor hidden service"); - let ac = - register_tor_services(config.network.clone().listen, tor_client, &seed) - .await?; - Some(ac) - } - Err(_) => { - tracing::warn!("Tor not found. Running on clear net"); - None - } - }; - let kraken_rate = KrakenRate::new(config.maker.ask_spread, kraken_price_updates); let namespace = XmrBtcNamespace::from_is_testnet(testnet); @@ -175,7 +189,9 @@ async fn main() -> Result<()> { env_config, namespace, &rendezvous_addrs, - )?; + tor_port, + ) + .await?; for listen in config.network.listen.clone() { Swarm::listen_on(&mut swarm, listen.clone()) @@ -241,7 +257,8 @@ async fn main() -> Result<()> { println!("{}", config_json); } Command::WithdrawBtc { amount, address } => { - let bitcoin_wallet = init_bitcoin_wallet(&config, &seed, env_config).await?; + let bitcoin_wallet = + init_bitcoin_wallet(&config, &seed, env_config, proxy_string).await?; let amount = match amount { Some(amount) => amount, @@ -264,20 +281,23 @@ async fn main() -> Result<()> { let monero_balance = monero_wallet.get_balance().await?; tracing::info!(%monero_balance); - let bitcoin_wallet = init_bitcoin_wallet(&config, &seed, env_config).await?; + let bitcoin_wallet = + init_bitcoin_wallet(&config, &seed, env_config, proxy_string).await?; let bitcoin_balance = bitcoin_wallet.balance().await?; tracing::info!(%bitcoin_balance); tracing::info!(%bitcoin_balance, %monero_balance, "Current balance"); } Command::Cancel { swap_id } => { - let bitcoin_wallet = init_bitcoin_wallet(&config, &seed, env_config).await?; + let bitcoin_wallet = + init_bitcoin_wallet(&config, &seed, env_config, proxy_string).await?; let (txid, _) = cancel(swap_id, Arc::new(bitcoin_wallet), db).await?; tracing::info!("Cancel transaction successfully published with id {}", txid); } Command::Refund { swap_id } => { - let bitcoin_wallet = init_bitcoin_wallet(&config, &seed, env_config).await?; + let bitcoin_wallet = + init_bitcoin_wallet(&config, &seed, env_config, proxy_string).await?; let monero_wallet = init_monero_wallet(&config, env_config).await?; refund( @@ -291,7 +311,8 @@ async fn main() -> Result<()> { tracing::info!("Monero successfully refunded"); } Command::Punish { swap_id } => { - let bitcoin_wallet = init_bitcoin_wallet(&config, &seed, env_config).await?; + let bitcoin_wallet = + init_bitcoin_wallet(&config, &seed, env_config, proxy_string).await?; let (txid, _) = punish(swap_id, Arc::new(bitcoin_wallet), db).await?; @@ -306,7 +327,8 @@ async fn main() -> Result<()> { swap_id, do_not_await_finality, } => { - let bitcoin_wallet = init_bitcoin_wallet(&config, &seed, env_config).await?; + let bitcoin_wallet = + init_bitcoin_wallet(&config, &seed, env_config, proxy_string).await?; let (txid, _) = redeem( swap_id, @@ -319,7 +341,8 @@ async fn main() -> Result<()> { tracing::info!("Redeem transaction successfully published with id {}", txid); } Command::ExportBitcoinWallet => { - let bitcoin_wallet = init_bitcoin_wallet(&config, &seed, env_config).await?; + let bitcoin_wallet = + init_bitcoin_wallet(&config, &seed, env_config, proxy_string).await?; let wallet_export = bitcoin_wallet.wallet_export("asb").await?; println!("{}", wallet_export.to_string()) } @@ -332,11 +355,13 @@ async fn init_bitcoin_wallet( config: &Config, seed: &Seed, env_config: swap::env::Config, + proxy_string: String, ) -> Result { tracing::debug!("Opening Bitcoin wallet"); let data_dir = &config.data.dir; let wallet = bitcoin::Wallet::new( config.bitcoin.electrum_rpc_url.clone(), + proxy_string.as_str(), data_dir, seed.derive_extended_private_key(env_config.bitcoin_network)?, env_config, diff --git a/swap/src/bin/swap.rs b/swap/src/bin/swap.rs index 292a4586..65a260e5 100644 --- a/swap/src/bin/swap.rs +++ b/swap/src/bin/swap.rs @@ -505,6 +505,7 @@ async fn init_bitcoin_wallet( let wallet = bitcoin::Wallet::new( electrum_rpc_url.clone(), + "", data_dir, xprivkey, env_config, diff --git a/swap/src/bitcoin/wallet.rs b/swap/src/bitcoin/wallet.rs index 2e5732f8..14f9f33d 100644 --- a/swap/src/bitcoin/wallet.rs +++ b/swap/src/bitcoin/wallet.rs @@ -6,7 +6,7 @@ use ::bitcoin::Txid; use anyhow::{bail, Context, Result}; use bdk::blockchain::{Blockchain, ElectrumBlockchain, GetTx}; use bdk::database::BatchDatabase; -use bdk::electrum_client::{ElectrumApi, GetHistoryRes}; +use bdk::electrum_client::{ElectrumApi, GetHistoryRes, Socks5Config}; use bdk::sled::Tree; use bdk::wallet::export::FullyNodedExport; use bdk::wallet::AddressIndex; @@ -20,6 +20,7 @@ use rust_decimal_macros::dec; use std::collections::{BTreeMap, HashMap}; use std::convert::TryFrom; use std::fmt; +use std::ops::Not; use std::path::Path; use std::sync::Arc; use std::time::{Duration, Instant}; @@ -47,6 +48,7 @@ pub struct Wallet { impl Wallet { pub async fn new( electrum_rpc_url: Url, + electrum_socks5_proxy_string: &str, data_dir: impl AsRef, xprivkey: ExtendedPrivKey, env_config: env::Config, @@ -70,7 +72,11 @@ impl Wallet { err => err?, }; - let client = Client::new(electrum_rpc_url, env_config.bitcoin_sync_interval())?; + let client = Client::new( + electrum_rpc_url, + electrum_socks5_proxy_string, + env_config.bitcoin_sync_interval(), + )?; let network = wallet.network(); @@ -723,20 +729,32 @@ pub struct Client { } impl Client { - fn new(electrum_rpc_url: Url, interval: Duration) -> Result { - let config = bdk::electrum_client::ConfigBuilder::default() - .retry(5) - .build(); - let electrum = bdk::electrum_client::Client::from_config(electrum_rpc_url.as_str(), config) - .context("Failed to initialize Electrum RPC client")?; + fn new( + electrum_rpc_url: Url, + electrum_socks5_proxy_string: &str, + interval: Duration, + ) -> Result { + let mut config_builder = bdk::electrum_client::ConfigBuilder::default().retry(5); + if electrum_socks5_proxy_string.is_empty().not() { + config_builder = config_builder + .socks5(Option::from(Socks5Config::new( + electrum_socks5_proxy_string.to_string(), + ))) + .unwrap() // use Tor with the Electrum client + } + let config = config_builder.build(); + let electrum = + bdk::electrum_client::Client::from_config(electrum_rpc_url.as_str(), config.clone()) + .context("Failed to initialize Electrum RPC client")?; // Initially fetch the latest block for storing the height. // We do not act on this subscription after this call. let latest_block = electrum .block_headers_subscribe() .context("Failed to subscribe to header notifications")?; - let client = bdk::electrum_client::Client::new(electrum_rpc_url.as_str()) - .context("Failed to initialize Electrum RPC client")?; + let client = + bdk::electrum_client::Client::from_config(electrum_rpc_url.as_str(), config.clone()) + .context("Failed to initialize Electrum RPC client")?; let blockchain = ElectrumBlockchain::from(client); let last_sync = Instant::now() .checked_sub(interval) diff --git a/swap/src/network/swarm.rs b/swap/src/network/swarm.rs index 37bb0a5e..48d0595c 100644 --- a/swap/src/network/swarm.rs +++ b/swap/src/network/swarm.rs @@ -9,7 +9,7 @@ use libp2p::{identity, Multiaddr, Swarm}; use std::fmt::Debug; #[allow(clippy::too_many_arguments)] -pub fn asb( +pub async fn asb( seed: &Seed, min_buy: bitcoin::Amount, max_buy: bitcoin::Amount, @@ -18,10 +18,16 @@ pub fn asb( env_config: env::Config, namespace: XmrBtcNamespace, rendezvous_addrs: &[Multiaddr], + tor_socks5_port: u16, ) -> Result>> where LR: LatestRate + Send + 'static + Debug + Clone, { + let maybe_tor_socks5_port = match tor::Client::new(tor_socks5_port).assert_tor_running().await { + Ok(()) => Some(tor_socks5_port), + Err(_) => None, + }; + let identity = seed.derive_libp2p_identity(); let rendezvous_nodes = rendezvous_addrs @@ -45,7 +51,7 @@ where rendezvous_nodes, ); - let transport = asb::transport::new(&identity)?; + let transport = asb::transport::new(&identity, maybe_tor_socks5_port)?; let peer_id = identity.public().into(); let swarm = SwarmBuilder::new(transport, behaviour, peer_id)