diff --git a/Cargo.toml b/Cargo.toml index 681da7f07..1b073c9ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -109,6 +109,7 @@ env_logger = "0.7" electrsd = "0.21" # Move back to importing from rust-bitcoin once https://github.com/rust-bitcoin/rust-bitcoin/pull/1342 is released base64 = "^0.13" +libtor = "47.8.0+0.4.7.x" [[example]] name = "compact_filters_balance" diff --git a/examples/compact_filters_balance.rs b/examples/compact_filters_balance.rs index ce875b4d5..071f13a3d 100644 --- a/examples/compact_filters_balance.rs +++ b/examples/compact_filters_balance.rs @@ -18,16 +18,40 @@ use blockchain::compact_filters::CompactFiltersError; use log::info; use std::sync::Arc; +pub mod utils; +use crate::utils::tor::{start_tor, use_tor}; + /// This will return wallet balance using compact filters /// Requires a synced local bitcoin node 0.21 running on testnet with blockfilterindex=1 and peerblockfilters=1 fn main() -> Result<(), CompactFiltersError> { env_logger::init(); info!("start"); + let tor_addrs = if use_tor() { + Some(start_tor(Some(18333))) + } else { + None + }; + let num_threads = 4; let mempool = Arc::new(Mempool::default()); let peers = (0..num_threads) - .map(|_| Peer::connect("localhost:18333", Arc::clone(&mempool), Network::Testnet)) + .map(|_| { + if use_tor() { + let addr = &tor_addrs.as_ref().unwrap().hidden_service.as_ref().unwrap(); + let proxy = &tor_addrs.as_ref().unwrap().socks; + info!("conecting to {} via {}", &addr, &proxy); + Peer::connect_proxy( + addr.as_str(), + &proxy, + None, + Arc::clone(&mempool), + Network::Testnet, + ) + } else { + Peer::connect("localhost:18333", Arc::clone(&mempool), Network::Testnet) + } + }) .collect::>()?; let blockchain = CompactFiltersBlockchain::new(peers, "./wallet-filters", Some(500_000))?; info!("done {:?}", blockchain); diff --git a/examples/electrum_backend.rs b/examples/electrum_backend.rs index 5259865f3..d7fec0e4d 100644 --- a/examples/electrum_backend.rs +++ b/examples/electrum_backend.rs @@ -8,12 +8,13 @@ use bdk::template::Bip84; use bdk::wallet::export::FullyNodedExport; use bdk::{KeychainKind, SyncOptions, Wallet}; -use bdk::electrum_client::Client; +use bdk::electrum_client::{Client, ConfigBuilder, Socks5Config}; use bdk::wallet::AddressIndex; use bitcoin::util::bip32; pub mod utils; +use crate::utils::tor::{start_tor, use_tor}; use crate::utils::tx::build_signed_tx; /// This will create a wallet from an xpriv and get the balance by connecting to an Electrum server. @@ -44,8 +45,23 @@ fn create_wallet(network: &Network, xpriv: &ExtendedPrivKey) -> Wallet Wallet Wallet, + } + + pub fn use_tor() -> bool { + match env::var("TOR") { + Ok(val) => val == "1" || val == "true", + _ => false, + } + } + + pub fn start_tor(hidden_service_port: Option) -> TorAddresses { + let socks_port = 19050; + + let data_dir = format!("{}/{}", env::temp_dir().display(), "bdk-tor-example"); + let log_file_name = format!("{}/{}", &data_dir, "log"); + let hidden_service_dir = + hidden_service_port.map(|port| format!("{}/{}-{}", &data_dir, "hs-dir", port)); + + println!("Staring Tor in {}", &data_dir); + + truncate_log(&log_file_name); + + if let Some(hs_port) = hidden_service_port { + Tor::new() + .flag(TorFlag::DataDirectory(data_dir.into())) + .flag(TorFlag::LogTo( + LogLevel::Notice, + LogDestination::File(log_file_name.as_str().into()), + )) + .flag(TorFlag::SocksPort(socks_port)) + .flag(TorFlag::Custom("ExitPolicy reject *:*".into())) + .flag(TorFlag::Custom("BridgeRelay 0".into())) + .flag(TorFlag::HiddenServiceDir( + hidden_service_dir.as_ref().unwrap().into(), + )) + .flag(TorFlag::HiddenServiceVersion(HiddenServiceVersion::V3)) + .flag(TorFlag::HiddenServicePort( + TorAddress::Port(hs_port), + None.into(), + )) + .start_background() + } else { + Tor::new() + .flag(TorFlag::DataDirectory(data_dir.into())) + .flag(TorFlag::LogTo( + LogLevel::Notice, + LogDestination::File(log_file_name.as_str().into()), + )) + .flag(TorFlag::SocksPort(socks_port)) + .flag(TorFlag::Custom("ExitPolicy reject *:*".into())) + .flag(TorFlag::Custom("BridgeRelay 0".into())) + .start_background() + }; + + let mut started = false; + let mut tries = 0; + while !started { + tries += 1; + if tries > 120 { + panic!( + "It took too long to start Tor. See {} for details", + &log_file_name + ); + } + + thread::sleep(Duration::from_millis(1000)); + started = find_string_in_log(&log_file_name, &"Bootstrapped 100%".into()); + } + + println!("Tor started"); + + TorAddresses { + socks: format!("127.0.0.1:{}", socks_port), + hidden_service: hidden_service_port.map(|port| { + format!( + "{}:{}", + get_onion_address(&hidden_service_dir.unwrap()), + port + ) + }), + } + } + + fn truncate_log(filename: &String) { + let path = std::path::Path::new(filename); + if path.exists() { + let file = File::options() + .write(true) + .open(path) + .expect("no such file"); + file.set_len(0).expect("cannot set size to 0"); + } + } + + fn find_string_in_log(filename: &String, str: &String) -> bool { + let path = std::path::Path::new(filename); + if path.exists() { + let mut file = File::open(path).expect("cannot open log file"); + let mut buf = String::new(); + file.read_to_string(&mut buf).expect("cannot read log file"); + buf.contains(str) + } else { + false + } + } + + fn get_onion_address(hidden_service_dir: &String) -> String { + let filename = format!("{}/{}", hidden_service_dir, "hostname"); + let path = std::path::Path::new(&filename); + let mut file = File::open(path).expect("cannot open hostname file"); + let mut buf = String::new(); + file.read_to_string(&mut buf).expect("cannot read log file"); + buf.replace("\n", "") + } +}