From 0ca511bf8a256c8592e10ffdba4e562b690377a4 Mon Sep 17 00:00:00 2001 From: rishflab Date: Thu, 29 Oct 2020 14:45:45 +1100 Subject: [PATCH 1/8] Remove hardcoded configuration The hardcoded configuration was replaced with CLI configuration options. CLI based config was chosen over a config file as it does not access and clutter the user's file system. By CLI options depend on whether the program is run in Alice or Bob mode. --- swap/src/cli.rs | 53 +++++++++++++++------ swap/src/main.rs | 120 ++++++++++++++++++++++------------------------- 2 files changed, 96 insertions(+), 77 deletions(-) diff --git a/swap/src/cli.rs b/swap/src/cli.rs index 5504d87b..1fdcdfbf 100644 --- a/swap/src/cli.rs +++ b/swap/src/cli.rs @@ -1,19 +1,44 @@ +use libp2p::core::Multiaddr; +use url::Url; + #[derive(structopt::StructOpt, Debug)] -pub struct Options { - /// Run the swap as Alice. - #[structopt(long = "as-alice")] - pub as_alice: bool, +pub enum Options { + Alice { + /// Run the swap as Bob and try to swap this many BTC (in satoshi). + #[structopt(long = "sats")] + satoshis: u64, - /// Run the swap as Bob and try to swap this many XMR (in piconero). - #[structopt(long = "picos")] - pub piconeros: Option, + // /// Run the swap as Bob and try to swap this many XMR (in piconero). + // #[structopt(long = "picos", conflicts_with = "sats"))] + // pub piconeros: u64, + #[structopt(default_value = "http://127.0.0.1:8332", long = "bitcoind")] + bitcoind_url: Url, - /// Run the swap as Bob and try to swap this many BTC (in satoshi). - #[structopt(long = "sats")] - pub satoshis: Option, + #[structopt(default_value = "127.0.0.1", long = "listen_addr")] + listen_addr: String, - /// Alice's onion multitaddr (only required for Bob, Alice will autogenerate - /// one) - #[structopt(long)] - pub alice_address: Option, + #[structopt(default_value = 9876, long = "list_port")] + listen_port: u16, + }, + Bob { + /// Alice's multitaddr (only required for Bob, Alice will autogenerate + /// one) + #[structopt(long = "alice_addr")] + alice_addr: Multiaddr, + + /// Run the swap as Bob and try to swap this many BTC (in satoshi). + #[structopt(long = "sats")] + satoshis: u64, + + // /// Run the swap as Bob and try to swap this many XMR (in piconero). + // #[structopt(long = "picos", conflicts_with = "sats"))] + // pub piconeros: u64, + #[structopt(default_value = "http://127.0.0.1:8332", long = "bitcoind")] + bitcoind_url: Url, + // #[structopt(default_value = "/ip4/127.0.0.1/tcp/9876", long = "dial")] + // alice_addr: String, + #[cfg(feature = "tor")] + #[structopt(long = "tor")] + tor_port: u16, + }, } diff --git a/swap/src/main.rs b/swap/src/main.rs index c2b0b4b6..4f70249c 100644 --- a/swap/src/main.rs +++ b/swap/src/main.rs @@ -14,7 +14,7 @@ use anyhow::{bail, Context, Result}; use futures::{channel::mpsc, StreamExt}; -use libp2p::Multiaddr; +use libp2p::core::Multiaddr; use log::LevelFilter; use std::{io, io::Write, process, sync::Arc}; use structopt::StructOpt; @@ -29,80 +29,74 @@ use swap::{alice, bitcoin, bob, monero, Cmd, Rsp, SwapAmounts}; // TODO: Add root seed file instead of generating new seed each run. -// TODO: Add a config file with these in it. -// Alice's address and port until we have a config file. -pub const PORT: u16 = 9876; // Arbitrarily chosen. -pub const ADDR: &str = "127.0.0.1"; -pub const BITCOIND_JSON_RPC_URL: &str = "http://127.0.0.1:8332"; -pub const MONERO_WALLET_RPC_PORT: u16 = 18083; - -#[cfg(feature = "tor")] -pub const TOR_PORT: u16 = PORT + 1; +// // TODO: Add a config file with these in it. +// // Alice's address and port until we have a config file. +// pub const LISTEN_PORT: u16 = 9876; // Arbitrarily chosen. +// pub const LISTEN_ADDR: &str = "127.0.0.1"; +// pub const BITCOIND_JSON_RPC_URL: &str = "http://127.0.0.1:8332"; +// +// #[cfg(feature = "tor")] +// pub const TOR_PORT: u16 = LISTEN_PORT + 1; #[tokio::main] async fn main() -> Result<()> { - let opt = Options::from_args(); + let opt: Options = Options::from_args(); trace::init_tracing(LevelFilter::Debug)?; - #[cfg(feature = "tor")] - let (addr, _ac) = { - let tor_secret_key = torut::onion::TorSecretKeyV3::generate(); - let onion_address = tor_secret_key - .public() - .get_onion_address() - .get_address_without_dot_onion(); - ( - format!("/onion3/{}:{}", onion_address, TOR_PORT), - create_tor_service(tor_secret_key).await?, - ) - }; - #[cfg(not(feature = "tor"))] - let addr = format!("/ip4/{}/tcp/{}", ADDR, PORT); + match opt { + Options::Alice { + bitcoind_url: url, + listen_addr, + listen_port, + .. + } => { + info!("running swap node as Alice ..."); - let alice: Multiaddr = addr.parse().expect("failed to parse Alice's address"); + #[cfg(feature = "tor")] + let (alice, _ac) = { + let tor_secret_key = torut::onion::TorSecretKeyV3::generate(); + let onion_address = tor_secret_key + .public() + .get_onion_address() + .get_address_without_dot_onion(); + let onion_address_string = format!("/onion3/{}:{}", onion_address, TOR_PORT); + let addr: Multiaddr = onion_address_string.parse()?; - if opt.as_alice { - info!("running swap node as Alice ..."); + (addr, create_tor_service(tor_secret_key).await?) + }; - if opt.piconeros.is_some() || opt.satoshis.is_some() { - bail!("Alice cannot set the amount to swap via the cli"); + #[cfg(not(feature = "tor"))] + let alice = { + let alice: Multiaddr = listen_addr + .parse() + .expect("failed to parse Alice's address"); + }; + + let bitcoin_wallet = bitcoin::Wallet::new("alice", &url) + .await + .expect("failed to create bitcoin wallet"); + let bitcoin_wallet = Arc::new(bitcoin_wallet); + + let monero_wallet = Arc::new(monero::Wallet::localhost(MONERO_WALLET_RPC_PORT)); + + swap_as_alice(bitcoin_wallet, monero_wallet, alice.clone()).await?; } + Options::Bob { + alice_addr: alice_address, + satoshis, + bitcoind_url: url, + } => { + info!("running swap node as Bob ..."); - let url = Url::parse(BITCOIND_JSON_RPC_URL).expect("failed to parse url"); - let bitcoin_wallet = bitcoin::Wallet::new("alice", &url) - .await - .expect("failed to create bitcoin wallet"); - let bitcoin_wallet = Arc::new(bitcoin_wallet); + let bitcoin_wallet = Wallet::new("bob", &url) + .await + .expect("failed to create bitcoin wallet"); - let monero_wallet = Arc::new(monero::Wallet::localhost(MONERO_WALLET_RPC_PORT)); + let monero_wallet = Arc::new(monero::Wallet::localhost(MONERO_WALLET_RPC_PORT)); - swap_as_alice(bitcoin_wallet, monero_wallet, alice.clone()).await?; - } else { - info!("running swap node as Bob ..."); - - let alice = match opt.alice_address { - Some(addr) => addr, - None => bail!("Address required to dial"), - }; - let alice = multiaddr(&alice)?; - - let url = Url::parse(BITCOIND_JSON_RPC_URL).expect("failed to parse url"); - let bitcoin_wallet = bitcoin::Wallet::new("bob", &url) - .await - .expect("failed to create bitcoin wallet"); - let bitcoin_wallet = Arc::new(bitcoin_wallet); - - let monero_wallet = Arc::new(monero::Wallet::localhost(MONERO_WALLET_RPC_PORT)); - - match (opt.piconeros, opt.satoshis) { - (Some(_), Some(_)) => bail!("Please supply only a single amount to swap"), - (None, None) => bail!("Please supply an amount to swap"), - (Some(_picos), _) => todo!("support starting with picos"), - (None, Some(sats)) => { - swap_as_bob(bitcoin_wallet, monero_wallet, sats, alice).await?; - } - }; + swap_as_bob(satoshis, alice_address, refund, bitcoin_wallet).await?; + } } Ok(()) @@ -137,7 +131,7 @@ async fn swap_as_alice( } #[cfg(feature = "tor")] { - alice::swap(bitcoin_wallet, monero_wallet, addr, Some(PORT)).await + alice::swap(bitcoin_wallet, monero_wallet, addr, Some(LISTEN_PORT)).await } } From 796d0b34395e97b63a2960a72bee40c507bac19d Mon Sep 17 00:00:00 2001 From: rishflab Date: Thu, 29 Oct 2020 22:18:59 +1100 Subject: [PATCH 2/8] Extract transport creation and remove tor conditional compile The numerous tor conditional compile flags were removed by extracting transport creation to the main statement. A tor transport is created if Alice specifies a tor port using the CLI. --- swap/Cargo.toml | 9 +-- swap/src/alice.rs | 32 +++------ swap/src/bob.rs | 24 ++----- swap/src/cli.rs | 27 +++----- swap/src/lib.rs | 2 +- swap/src/main.rs | 118 ++++++++++++++++++---------------- swap/src/network/transport.rs | 17 ++--- 7 files changed, 101 insertions(+), 128 deletions(-) diff --git a/swap/Cargo.toml b/swap/Cargo.toml index fa283dd7..5b9c18de 100644 --- a/swap/Cargo.toml +++ b/swap/Cargo.toml @@ -32,7 +32,8 @@ structopt = "0.3" tempfile = "3" time = "0.2" tokio = { version = "0.2", features = ["rt-threaded", "time", "macros", "sync"] } -torut = { version = "0.1", optional = true } +#torut = { version = "0.1", optional = true } +torut = { version = "0.1"} tracing = { version = "0.1", features = ["attributes"] } tracing-core = "0.1" tracing-futures = { version = "0.2", features = ["std-future", "futures-03"] } @@ -49,6 +50,6 @@ spectral = "0.6" tempfile = "3" testcontainers = "0.10" -[features] -default = [] -tor = ["torut"] +#[features] +#default = [] +#tor = ["torut"] diff --git a/swap/src/alice.rs b/swap/src/alice.rs index 4e57c135..e9845021 100644 --- a/swap/src/alice.rs +++ b/swap/src/alice.rs @@ -28,7 +28,9 @@ use crate::{ network::{ peer_tracker::{self, PeerTracker}, request_response::AliceToBob, - transport, TokioExecutor, + transport, + transport::SwapTransport, + TokioExecutor, }, SwapAmounts, PUNISH_TIMELOCK, REFUND_TIMELOCK, }; @@ -43,7 +45,10 @@ pub async fn swap( bitcoin_wallet: Arc, monero_wallet: Arc, listen: Multiaddr, - local_port: Option, + redeem_address: ::bitcoin::Address, + punish_address: ::bitcoin::Address, + transport: SwapTransport, + behaviour: Alice, ) -> Result<()> { struct Network { swarm: Arc>, @@ -96,7 +101,7 @@ pub async fn swap( } } - let mut swarm = new_swarm(listen, local_port)?; + let mut swarm = new_swarm(listen, transport, behaviour)?; let message0: bob::Message0; let mut state0: Option = None; let mut last_amounts: Option = None; @@ -230,30 +235,11 @@ pub async fn swap( pub type Swarm = libp2p::Swarm; -fn new_swarm(listen: Multiaddr, port: Option) -> Result { +fn new_swarm(listen: Multiaddr, transport: SwapTransport, behaviour: Alice) -> Result { use anyhow::Context as _; - let behaviour = Alice::default(); - - let local_key_pair = behaviour.identity(); let local_peer_id = behaviour.peer_id(); - let transport; - #[cfg(feature = "tor")] - { - transport = match port { - Some(port) => transport::build(local_key_pair, Some((listen.clone(), port)))?, - None => anyhow::bail!("Must supply local port"), - }; - } - #[cfg(not(feature = "tor"))] - { - transport = match port { - None => transport::build(local_key_pair)?, - Some(_) => anyhow::bail!("local port should not be provided for non-tor usage"), - }; - } - let mut swarm = libp2p::swarm::SwarmBuilder::new(transport, behaviour, local_peer_id.clone()) .executor(Box::new(TokioExecutor { handle: tokio::runtime::Handle::current(), diff --git a/swap/src/bob.rs b/swap/src/bob.rs index 451fc200..05d293c3 100644 --- a/swap/src/bob.rs +++ b/swap/src/bob.rs @@ -27,7 +27,9 @@ use crate::{ monero, network::{ peer_tracker::{self, PeerTracker}, - transport, TokioExecutor, + transport, + transport::SwapTransport, + TokioExecutor, }, Cmd, Rsp, SwapAmounts, PUNISH_TIMELOCK, REFUND_TIMELOCK, }; @@ -45,6 +47,8 @@ pub async fn swap( addr: Multiaddr, mut cmd_tx: Sender, mut rsp_rx: Receiver, + transport: SwapTransport, + behaviour: Bob, ) -> Result<()> { struct Network(Swarm); @@ -80,7 +84,7 @@ pub async fn swap( } } - let mut swarm = new_swarm()?; + let mut swarm = new_swarm(transport, behaviour)?; libp2p::Swarm::dial_addr(&mut swarm, addr)?; let alice = match swarm.next().await { @@ -202,23 +206,9 @@ pub async fn swap( pub type Swarm = libp2p::Swarm; -fn new_swarm() -> Result { - let behaviour = Bob::default(); - - let local_key_pair = behaviour.identity(); +fn new_swarm(transport: SwapTransport, behaviour: Bob) -> Result { let local_peer_id = behaviour.peer_id(); - let transport = { - #[cfg(feature = "tor")] - { - transport::build(local_key_pair, None)? - } - #[cfg(not(feature = "tor"))] - { - transport::build(local_key_pair)? - } - }; - let swarm = libp2p::swarm::SwarmBuilder::new(transport, behaviour, local_peer_id.clone()) .executor(Box::new(TokioExecutor { handle: tokio::runtime::Handle::current(), diff --git a/swap/src/cli.rs b/swap/src/cli.rs index 1fdcdfbf..f8189452 100644 --- a/swap/src/cli.rs +++ b/swap/src/cli.rs @@ -4,41 +4,32 @@ use url::Url; #[derive(structopt::StructOpt, Debug)] pub enum Options { Alice { - /// Run the swap as Bob and try to swap this many BTC (in satoshi). - #[structopt(long = "sats")] - satoshis: u64, - // /// Run the swap as Bob and try to swap this many XMR (in piconero). // #[structopt(long = "picos", conflicts_with = "sats"))] // pub piconeros: u64, #[structopt(default_value = "http://127.0.0.1:8332", long = "bitcoind")] bitcoind_url: Url, - #[structopt(default_value = "127.0.0.1", long = "listen_addr")] - listen_addr: String, + #[structopt(default_value = "/ip4/127.0.0.1/tcp/9876", long = "listen-addr")] + listen_addr: Multiaddr, - #[structopt(default_value = 9876, long = "list_port")] - listen_port: u16, + /// Run the swap as Bob and try to swap this many BTC (in satoshi). + // #[cfg(feature = "tor")] + #[structopt(long = "tor-port")] + tor_port: Option, }, Bob { - /// Alice's multitaddr (only required for Bob, Alice will autogenerate - /// one) - #[structopt(long = "alice_addr")] - alice_addr: Multiaddr, - /// Run the swap as Bob and try to swap this many BTC (in satoshi). #[structopt(long = "sats")] satoshis: u64, + #[structopt(long = "alice-addr")] + alice_addr: Multiaddr, + // /// Run the swap as Bob and try to swap this many XMR (in piconero). // #[structopt(long = "picos", conflicts_with = "sats"))] // pub piconeros: u64, #[structopt(default_value = "http://127.0.0.1:8332", long = "bitcoind")] bitcoind_url: Url, - // #[structopt(default_value = "/ip4/127.0.0.1/tcp/9876", long = "dial")] - // alice_addr: String, - #[cfg(feature = "tor")] - #[structopt(long = "tor")] - tor_port: u16, }, } diff --git a/swap/src/lib.rs b/swap/src/lib.rs index 0d682e77..8d91342f 100644 --- a/swap/src/lib.rs +++ b/swap/src/lib.rs @@ -7,7 +7,7 @@ pub mod bob; pub mod monero; pub mod network; pub mod storage; -#[cfg(feature = "tor")] +//#[cfg(feature = "tor")] pub mod tor; const REFUND_TIMELOCK: u32 = 10; // Relative timelock, this is number of blocks. TODO: What should it be? diff --git a/swap/src/main.rs b/swap/src/main.rs index 4f70249c..d4cbfebf 100644 --- a/swap/src/main.rs +++ b/swap/src/main.rs @@ -12,14 +12,24 @@ )] #![forbid(unsafe_code)] -use anyhow::{bail, Context, Result}; +use anyhow::Result; use futures::{channel::mpsc, StreamExt}; use libp2p::core::Multiaddr; use log::LevelFilter; use std::{io, io::Write, process, sync::Arc}; use structopt::StructOpt; +use swap::{ + alice, + alice::Alice, + bitcoin::Wallet, + bob, + bob::Bob, + network::transport::{build, build_tor, SwapTransport}, + Cmd, Rsp, SwapAmounts, +}; use tracing::info; use url::Url; +use xmr_btc::bitcoin::{BroadcastSignedTransaction, BuildTxLockPsbt, SignTxLock}; mod cli; mod trace; @@ -29,15 +39,6 @@ use swap::{alice, bitcoin, bob, monero, Cmd, Rsp, SwapAmounts}; // TODO: Add root seed file instead of generating new seed each run. -// // TODO: Add a config file with these in it. -// // Alice's address and port until we have a config file. -// pub const LISTEN_PORT: u16 = 9876; // Arbitrarily chosen. -// pub const LISTEN_ADDR: &str = "127.0.0.1"; -// pub const BITCOIND_JSON_RPC_URL: &str = "http://127.0.0.1:8332"; -// -// #[cfg(feature = "tor")] -// pub const TOR_PORT: u16 = LISTEN_PORT + 1; - #[tokio::main] async fn main() -> Result<()> { let opt: Options = Options::from_args(); @@ -48,29 +49,31 @@ async fn main() -> Result<()> { Options::Alice { bitcoind_url: url, listen_addr, - listen_port, + tor_port, .. } => { info!("running swap node as Alice ..."); - #[cfg(feature = "tor")] - let (alice, _ac) = { - let tor_secret_key = torut::onion::TorSecretKeyV3::generate(); - let onion_address = tor_secret_key - .public() - .get_onion_address() - .get_address_without_dot_onion(); - let onion_address_string = format!("/onion3/{}:{}", onion_address, TOR_PORT); - let addr: Multiaddr = onion_address_string.parse()?; + let behaviour = Alice::default(); + let local_key_pair = behaviour.identity(); - (addr, create_tor_service(tor_secret_key).await?) - }; - - #[cfg(not(feature = "tor"))] - let alice = { - let alice: Multiaddr = listen_addr - .parse() - .expect("failed to parse Alice's address"); + let (listen_addr, ac, transport) = match tor_port { + Some(tor_port) => { + let tor_secret_key = torut::onion::TorSecretKeyV3::generate(); + let onion_address = tor_secret_key + .public() + .get_onion_address() + .get_address_without_dot_onion(); + let onion_address_string = format!("/onion3/{}:{}", onion_address, tor_port); + let addr: Multiaddr = onion_address_string.parse()?; + let ac = create_tor_service(tor_secret_key, tor_port).await?; + let transport = build_tor(local_key_pair, addr.clone(), tor_port)?; + (addr, Some(ac), transport) + } + None => { + let transport = build(local_key_pair)?; + (listen_addr, None, transport) + } }; let bitcoin_wallet = bitcoin::Wallet::new("alice", &url) @@ -80,31 +83,44 @@ async fn main() -> Result<()> { let monero_wallet = Arc::new(monero::Wallet::localhost(MONERO_WALLET_RPC_PORT)); - swap_as_alice(bitcoin_wallet, monero_wallet, alice.clone()).await?; + swap_as_alice(listen_addr, redeem, punish, transport, behaviour).await?; } Options::Bob { - alice_addr: alice_address, + alice_addr, satoshis, bitcoind_url: url, } => { info!("running swap node as Bob ..."); + let behaviour = Bob::default(); + let local_key_pair = behaviour.identity(); + + let transport = build(local_key_pair)?; + let bitcoin_wallet = Wallet::new("bob", &url) .await .expect("failed to create bitcoin wallet"); let monero_wallet = Arc::new(monero::Wallet::localhost(MONERO_WALLET_RPC_PORT)); - swap_as_bob(satoshis, alice_address, refund, bitcoin_wallet).await?; + swap_as_bob( + satoshis, + alice_addr, + refund, + bitcoin_wallet, + transport, + behaviour, + ) + .await?; } } Ok(()) } -#[cfg(feature = "tor")] async fn create_tor_service( tor_secret_key: torut::onion::TorSecretKeyV3, + tor_port: u16, ) -> Result { // TODO use configurable ports for tor connection let mut authenticated_connection = swap::tor::UnauthenticatedConnection::default() @@ -113,7 +129,7 @@ async fn create_tor_service( tracing::info!("Tor authenticated."); authenticated_connection - .add_service(TOR_PORT, &tor_secret_key) + .add_service(tor_port, &tor_secret_key) .await?; tracing::info!("Tor service added."); @@ -124,15 +140,12 @@ async fn swap_as_alice( bitcoin_wallet: Arc, monero_wallet: Arc, addr: Multiaddr, + redeem: bitcoin::Address, + punish: bitcoin::Address, + transport: SwapTransport, + behaviour: Alice, ) -> Result<()> { - #[cfg(not(feature = "tor"))] - { - alice::swap(bitcoin_wallet, monero_wallet, addr, None).await - } - #[cfg(feature = "tor")] - { - alice::swap(bitcoin_wallet, monero_wallet, addr, Some(LISTEN_PORT)).await - } + alice::swap(addr, redeem, punish, transport, behaviour).await } async fn swap_as_bob( @@ -140,16 +153,18 @@ async fn swap_as_bob( monero_wallet: Arc, sats: u64, alice: Multiaddr, -) -> Result<()> { + refund: bitcoin::Address, + wallet: W, + transport: SwapTransport, + behaviour: Bob, +) -> Result<()> +where + W: BuildTxLockPsbt + SignTxLock + BroadcastSignedTransaction + Send + Sync + 'static, +{ let (cmd_tx, mut cmd_rx) = mpsc::channel(1); let (mut rsp_tx, rsp_rx) = mpsc::channel(1); tokio::spawn(bob::swap( - bitcoin_wallet, - monero_wallet, - sats, - alice, - cmd_tx, - rsp_rx, + sats, alice, cmd_tx, rsp_rx, refund, wallet, transport, behaviour, )); loop { @@ -201,10 +216,3 @@ fn verify(amounts: SwapAmounts) -> Rsp { fn is_yes(s: &str) -> bool { matches!(s, "y" | "Y" | "yes" | "YES" | "Yes") } - -fn multiaddr(s: &str) -> Result { - let addr = s - .parse() - .with_context(|| format!("failed to parse multiaddr: {}", s))?; - Ok(addr) -} diff --git a/swap/src/network/transport.rs b/swap/src/network/transport.rs index 19ce5021..84500ca8 100644 --- a/swap/src/network/transport.rs +++ b/swap/src/network/transport.rs @@ -5,7 +5,7 @@ use libp2p::{ muxing::StreamMuxerBox, transport::Boxed, upgrade::{SelectUpgrade, Version}, - Transport, + Multiaddr, Transport, }, dns::DnsConfig, mplex::MplexConfig, @@ -18,7 +18,7 @@ use libp2p::{ /// - DNS name resolution /// - authentication via noise /// - multiplexing via yamux or mplex -#[cfg(not(feature = "tor"))] +/// #[cfg(not(feature = "tor"))] pub fn build(id_keys: identity::Keypair) -> Result { use libp2p::tcp::TokioTcpConfig; @@ -40,24 +40,21 @@ pub fn build(id_keys: identity::Keypair) -> Result { Ok(transport) } - /// Builds a libp2p transport with Tor and with the following features: /// - TCP connection over the Tor network /// - DNS name resolution /// - authentication via noise /// - multiplexing via yamux or mplex -#[cfg(feature = "tor")] -pub fn build( +// #[cfg(feature = "tor")] +pub fn build_tor( id_keys: identity::Keypair, - address_port_pair: Option<(libp2p::core::Multiaddr, u16)>, + addr: libp2p::core::Multiaddr, + port: u16, ) -> Result { use libp2p_tokio_socks5::Socks5TokioTcpConfig; use std::collections::HashMap; - let mut map = HashMap::new(); - if let Some((addr, port)) = address_port_pair { - map.insert(addr, port); - } + let map: HashMap = [(addr, port)].iter().cloned().collect(); let dh_keys = noise::Keypair::::new().into_authentic(&id_keys)?; let noise = NoiseConfig::xx(dh_keys).into_authenticated(); From 606c48c75264e5051291a5bfc53485133d066ce9 Mon Sep 17 00:00:00 2001 From: rishflab Date: Tue, 3 Nov 2020 10:55:54 +1100 Subject: [PATCH 3/8] Format swap crate Cargo.toml --- .github/workflows/ci.yml | 1 + swap/Cargo.toml | 7 +------ 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cd8a6c30..4920d97f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,6 +36,7 @@ jobs: cargo tomlfmt -d -p Cargo.toml cargo tomlfmt -d -p xmr-btc/Cargo.toml cargo tomlfmt -d -p monero-harness/Cargo.toml + cargo tomlfmt -d -p swap/Cargo.toml - name: Check code formatting run: cargo fmt --all -- --check diff --git a/swap/Cargo.toml b/swap/Cargo.toml index 5b9c18de..a92615f4 100644 --- a/swap/Cargo.toml +++ b/swap/Cargo.toml @@ -32,8 +32,7 @@ structopt = "0.3" tempfile = "3" time = "0.2" tokio = { version = "0.2", features = ["rt-threaded", "time", "macros", "sync"] } -#torut = { version = "0.1", optional = true } -torut = { version = "0.1"} +torut = { version = "0.1" } tracing = { version = "0.1", features = ["attributes"] } tracing-core = "0.1" tracing-futures = { version = "0.2", features = ["std-future", "futures-03"] } @@ -49,7 +48,3 @@ port_check = "0.1" spectral = "0.6" tempfile = "3" testcontainers = "0.10" - -#[features] -#default = [] -#tor = ["torut"] From 5971ef1f28a9cd77e71927e2226f140d7e00218f Mon Sep 17 00:00:00 2001 From: rishflab Date: Tue, 3 Nov 2020 11:00:39 +1100 Subject: [PATCH 4/8] Fix clippy warnings --- swap/src/alice.rs | 1 - swap/src/bob.rs | 1 - swap/src/main.rs | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/swap/src/alice.rs b/swap/src/alice.rs index e9845021..63638dfd 100644 --- a/swap/src/alice.rs +++ b/swap/src/alice.rs @@ -28,7 +28,6 @@ use crate::{ network::{ peer_tracker::{self, PeerTracker}, request_response::AliceToBob, - transport, transport::SwapTransport, TokioExecutor, }, diff --git a/swap/src/bob.rs b/swap/src/bob.rs index 05d293c3..39b96afe 100644 --- a/swap/src/bob.rs +++ b/swap/src/bob.rs @@ -27,7 +27,6 @@ use crate::{ monero, network::{ peer_tracker::{self, PeerTracker}, - transport, transport::SwapTransport, TokioExecutor, }, diff --git a/swap/src/main.rs b/swap/src/main.rs index d4cbfebf..5fa574e9 100644 --- a/swap/src/main.rs +++ b/swap/src/main.rs @@ -57,7 +57,7 @@ async fn main() -> Result<()> { let behaviour = Alice::default(); let local_key_pair = behaviour.identity(); - let (listen_addr, ac, transport) = match tor_port { + let (listen_addr, _ac, transport) = match tor_port { Some(tor_port) => { let tor_secret_key = torut::onion::TorSecretKeyV3::generate(); let onion_address = tor_secret_key From 249f273d1b7fc873abd1fc1ca20929708708508a Mon Sep 17 00:00:00 2001 From: rishflab Date: Tue, 3 Nov 2020 11:05:39 +1100 Subject: [PATCH 5/8] Remove stale comments --- swap/src/lib.rs | 1 - swap/src/main.rs | 2 +- swap/src/network/transport.rs | 2 -- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/swap/src/lib.rs b/swap/src/lib.rs index 8d91342f..32743dd3 100644 --- a/swap/src/lib.rs +++ b/swap/src/lib.rs @@ -7,7 +7,6 @@ pub mod bob; pub mod monero; pub mod network; pub mod storage; -//#[cfg(feature = "tor")] pub mod tor; const REFUND_TIMELOCK: u32 = 10; // Relative timelock, this is number of blocks. TODO: What should it be? diff --git a/swap/src/main.rs b/swap/src/main.rs index 5fa574e9..4ca74cc0 100644 --- a/swap/src/main.rs +++ b/swap/src/main.rs @@ -14,7 +14,7 @@ use anyhow::Result; use futures::{channel::mpsc, StreamExt}; -use libp2p::core::Multiaddr; +use libp2p::Multiaddr; use log::LevelFilter; use std::{io, io::Write, process, sync::Arc}; use structopt::StructOpt; diff --git a/swap/src/network/transport.rs b/swap/src/network/transport.rs index 84500ca8..b937a516 100644 --- a/swap/src/network/transport.rs +++ b/swap/src/network/transport.rs @@ -18,7 +18,6 @@ use libp2p::{ /// - DNS name resolution /// - authentication via noise /// - multiplexing via yamux or mplex -/// #[cfg(not(feature = "tor"))] pub fn build(id_keys: identity::Keypair) -> Result { use libp2p::tcp::TokioTcpConfig; @@ -45,7 +44,6 @@ pub fn build(id_keys: identity::Keypair) -> Result { /// - DNS name resolution /// - authentication via noise /// - multiplexing via yamux or mplex -// #[cfg(feature = "tor")] pub fn build_tor( id_keys: identity::Keypair, addr: libp2p::core::Multiaddr, From 3b008c38a69b424ec761779934654d6aced0cdb0 Mon Sep 17 00:00:00 2001 From: rishflab Date: Tue, 3 Nov 2020 14:33:48 +1100 Subject: [PATCH 6/8] Remove redundant code and stale comments --- swap/src/cli.rs | 9 --------- swap/src/main.rs | 3 +-- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/swap/src/cli.rs b/swap/src/cli.rs index f8189452..832b10c3 100644 --- a/swap/src/cli.rs +++ b/swap/src/cli.rs @@ -4,31 +4,22 @@ use url::Url; #[derive(structopt::StructOpt, Debug)] pub enum Options { Alice { - // /// Run the swap as Bob and try to swap this many XMR (in piconero). - // #[structopt(long = "picos", conflicts_with = "sats"))] - // pub piconeros: u64, #[structopt(default_value = "http://127.0.0.1:8332", long = "bitcoind")] bitcoind_url: Url, #[structopt(default_value = "/ip4/127.0.0.1/tcp/9876", long = "listen-addr")] listen_addr: Multiaddr, - /// Run the swap as Bob and try to swap this many BTC (in satoshi). - // #[cfg(feature = "tor")] #[structopt(long = "tor-port")] tor_port: Option, }, Bob { - /// Run the swap as Bob and try to swap this many BTC (in satoshi). #[structopt(long = "sats")] satoshis: u64, #[structopt(long = "alice-addr")] alice_addr: Multiaddr, - // /// Run the swap as Bob and try to swap this many XMR (in piconero). - // #[structopt(long = "picos", conflicts_with = "sats"))] - // pub piconeros: u64, #[structopt(default_value = "http://127.0.0.1:8332", long = "bitcoind")] bitcoind_url: Url, }, diff --git a/swap/src/main.rs b/swap/src/main.rs index 4ca74cc0..e52f9bc5 100644 --- a/swap/src/main.rs +++ b/swap/src/main.rs @@ -41,7 +41,7 @@ use swap::{alice, bitcoin, bob, monero, Cmd, Rsp, SwapAmounts}; #[tokio::main] async fn main() -> Result<()> { - let opt: Options = Options::from_args(); + let opt = Options::from_args(); trace::init_tracing(LevelFilter::Debug)?; @@ -50,7 +50,6 @@ async fn main() -> Result<()> { bitcoind_url: url, listen_addr, tor_port, - .. } => { info!("running swap node as Alice ..."); From 7afd316210feb2614e14abf376838f894d7174c5 Mon Sep 17 00:00:00 2001 From: rishflab Date: Tue, 3 Nov 2020 14:54:36 +1100 Subject: [PATCH 7/8] Construct tor transport for Bob --- swap/src/cli.rs | 4 ++++ swap/src/main.rs | 8 ++++++-- swap/src/network/transport.rs | 10 ++++++---- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/swap/src/cli.rs b/swap/src/cli.rs index 832b10c3..24e0041d 100644 --- a/swap/src/cli.rs +++ b/swap/src/cli.rs @@ -2,6 +2,7 @@ use libp2p::core::Multiaddr; use url::Url; #[derive(structopt::StructOpt, Debug)] +#[structopt(name = "xmr-btc-swap", about = "Trustless XMR BTC swaps")] pub enum Options { Alice { #[structopt(default_value = "http://127.0.0.1:8332", long = "bitcoind")] @@ -22,5 +23,8 @@ pub enum Options { #[structopt(default_value = "http://127.0.0.1:8332", long = "bitcoind")] bitcoind_url: Url, + + #[structopt(long = "tor")] + tor: bool, }, } diff --git a/swap/src/main.rs b/swap/src/main.rs index e52f9bc5..d55ce097 100644 --- a/swap/src/main.rs +++ b/swap/src/main.rs @@ -66,7 +66,7 @@ async fn main() -> Result<()> { let onion_address_string = format!("/onion3/{}:{}", onion_address, tor_port); let addr: Multiaddr = onion_address_string.parse()?; let ac = create_tor_service(tor_secret_key, tor_port).await?; - let transport = build_tor(local_key_pair, addr.clone(), tor_port)?; + let transport = build_tor(local_key_pair, Some((addr.clone(), tor_port)))?; (addr, Some(ac), transport) } None => { @@ -88,13 +88,17 @@ async fn main() -> Result<()> { alice_addr, satoshis, bitcoind_url: url, + tor, } => { info!("running swap node as Bob ..."); let behaviour = Bob::default(); let local_key_pair = behaviour.identity(); - let transport = build(local_key_pair)?; + let transport = match tor { + true => build_tor(local_key_pair, None)?, + false => build(local_key_pair)?, + }; let bitcoin_wallet = Wallet::new("bob", &url) .await diff --git a/swap/src/network/transport.rs b/swap/src/network/transport.rs index b937a516..5fa715fa 100644 --- a/swap/src/network/transport.rs +++ b/swap/src/network/transport.rs @@ -5,7 +5,7 @@ use libp2p::{ muxing::StreamMuxerBox, transport::Boxed, upgrade::{SelectUpgrade, Version}, - Multiaddr, Transport, + Transport, }, dns::DnsConfig, mplex::MplexConfig, @@ -46,13 +46,15 @@ pub fn build(id_keys: identity::Keypair) -> Result { /// - multiplexing via yamux or mplex pub fn build_tor( id_keys: identity::Keypair, - addr: libp2p::core::Multiaddr, - port: u16, + address_port_pair: Option<(libp2p::core::Multiaddr, u16)>, ) -> Result { use libp2p_tokio_socks5::Socks5TokioTcpConfig; use std::collections::HashMap; - let map: HashMap = [(addr, port)].iter().cloned().collect(); + let mut map = HashMap::new(); + if let Some((addr, port)) = address_port_pair { + map.insert(addr, port); + } let dh_keys = noise::Keypair::::new().into_authentic(&id_keys)?; let noise = NoiseConfig::xx(dh_keys).into_authenticated(); From a44303f839a4d93248a57d55d9fc6485cbc53db6 Mon Sep 17 00:00:00 2001 From: rishflab Date: Tue, 3 Nov 2020 16:08:46 +1100 Subject: [PATCH 8/8] Add monerod to config Fixed rebase issues --- swap/src/alice.rs | 3 --- swap/src/bitcoin.rs | 22 +++--------------- swap/src/bob.rs | 1 + swap/src/cli.rs | 6 +++++ swap/src/main.rs | 55 +++++++++++++++++++++++++-------------------- swap/src/monero.rs | 5 +++-- swap/tests/e2e.rs | 15 +++++++++---- 7 files changed, 55 insertions(+), 52 deletions(-) diff --git a/swap/src/alice.rs b/swap/src/alice.rs index 63638dfd..036e4dc8 100644 --- a/swap/src/alice.rs +++ b/swap/src/alice.rs @@ -44,8 +44,6 @@ pub async fn swap( bitcoin_wallet: Arc, monero_wallet: Arc, listen: Multiaddr, - redeem_address: ::bitcoin::Address, - punish_address: ::bitcoin::Address, transport: SwapTransport, behaviour: Alice, ) -> Result<()> { @@ -71,7 +69,6 @@ pub async fn swap( // TODO: For retry, use `backoff::ExponentialBackoff` in production as opposed // to `ConstantBackoff`. - #[async_trait] impl ReceiveBitcoinRedeemEncsig for Network { async fn receive_bitcoin_redeem_encsig(&mut self) -> xmr_btc::bitcoin::EncryptedSignature { diff --git a/swap/src/bitcoin.rs b/swap/src/bitcoin.rs index f9e1493d..2d88ae2c 100644 --- a/swap/src/bitcoin.rs +++ b/swap/src/bitcoin.rs @@ -4,7 +4,7 @@ use anyhow::Result; use async_trait::async_trait; use backoff::{backoff::Constant as ConstantBackoff, future::FutureOperation as _}; use bitcoin::{util::psbt::PartiallySignedTransaction, Address, Transaction}; -use bitcoin_harness::{bitcoind_rpc::PsbtBase64, Bitcoind}; +use bitcoin_harness::bitcoind_rpc::PsbtBase64; use reqwest::Url; use tokio::time; use xmr_btc::bitcoin::{ @@ -20,8 +20,8 @@ pub const TX_LOCK_MINE_TIMEOUT: u64 = 3600; pub struct Wallet(pub bitcoin_harness::Wallet); impl Wallet { - pub async fn new(name: &str, url: &Url) -> Result { - let wallet = bitcoin_harness::Wallet::new(name, url.clone()).await?; + pub async fn new(name: &str, url: Url) -> Result { + let wallet = bitcoin_harness::Wallet::new(name, url).await?; Ok(Self(wallet)) } @@ -46,22 +46,6 @@ impl Wallet { } } -pub async fn make_wallet( - name: &str, - bitcoind: &Bitcoind<'_>, - fund_amount: Amount, -) -> Result { - let wallet = Wallet::new(name, &bitcoind.node_url).await?; - let buffer = Amount::from_btc(1.0).unwrap(); - let amount = fund_amount + buffer; - - let address = wallet.0.new_address().await.unwrap(); - - bitcoind.mint(address, amount).await.unwrap(); - - Ok(wallet) -} - #[async_trait] impl BuildTxLockPsbt for Wallet { async fn build_tx_lock_psbt( diff --git a/swap/src/bob.rs b/swap/src/bob.rs index 39b96afe..2f973b99 100644 --- a/swap/src/bob.rs +++ b/swap/src/bob.rs @@ -39,6 +39,7 @@ use xmr_btc::{ monero::CreateWalletForOutput, }; +#[allow(clippy::too_many_arguments)] pub async fn swap( bitcoin_wallet: Arc, monero_wallet: Arc, diff --git a/swap/src/cli.rs b/swap/src/cli.rs index 24e0041d..aff4d67c 100644 --- a/swap/src/cli.rs +++ b/swap/src/cli.rs @@ -8,6 +8,9 @@ pub enum Options { #[structopt(default_value = "http://127.0.0.1:8332", long = "bitcoind")] bitcoind_url: Url, + #[structopt(default_value = "http://127.0.0.1:18083", long = "monerod")] + monerod_url: Url, + #[structopt(default_value = "/ip4/127.0.0.1/tcp/9876", long = "listen-addr")] listen_addr: Multiaddr, @@ -24,6 +27,9 @@ pub enum Options { #[structopt(default_value = "http://127.0.0.1:8332", long = "bitcoind")] bitcoind_url: Url, + #[structopt(default_value = "http://127.0.0.1:18083", long = "monerod")] + monerod_url: Url, + #[structopt(long = "tor")] tor: bool, }, diff --git a/swap/src/main.rs b/swap/src/main.rs index d55ce097..2f8fc74c 100644 --- a/swap/src/main.rs +++ b/swap/src/main.rs @@ -21,21 +21,18 @@ use structopt::StructOpt; use swap::{ alice, alice::Alice, - bitcoin::Wallet, - bob, + bitcoin, bob, bob::Bob, + monero, network::transport::{build, build_tor, SwapTransport}, Cmd, Rsp, SwapAmounts, }; use tracing::info; -use url::Url; -use xmr_btc::bitcoin::{BroadcastSignedTransaction, BuildTxLockPsbt, SignTxLock}; mod cli; mod trace; use cli::Options; -use swap::{alice, bitcoin, bob, monero, Cmd, Rsp, SwapAmounts}; // TODO: Add root seed file instead of generating new seed each run. @@ -47,7 +44,8 @@ async fn main() -> Result<()> { match opt { Options::Alice { - bitcoind_url: url, + bitcoind_url, + monerod_url, listen_addr, tor_port, } => { @@ -75,19 +73,27 @@ async fn main() -> Result<()> { } }; - let bitcoin_wallet = bitcoin::Wallet::new("alice", &url) + let bitcoin_wallet = bitcoin::Wallet::new("alice", bitcoind_url) .await .expect("failed to create bitcoin wallet"); let bitcoin_wallet = Arc::new(bitcoin_wallet); - let monero_wallet = Arc::new(monero::Wallet::localhost(MONERO_WALLET_RPC_PORT)); + let monero_wallet = Arc::new(monero::Wallet::new(monerod_url)); - swap_as_alice(listen_addr, redeem, punish, transport, behaviour).await?; + swap_as_alice( + bitcoin_wallet, + monero_wallet, + listen_addr, + transport, + behaviour, + ) + .await?; } Options::Bob { alice_addr, satoshis, - bitcoind_url: url, + bitcoind_url, + monerod_url, tor, } => { info!("running swap node as Bob ..."); @@ -100,17 +106,18 @@ async fn main() -> Result<()> { false => build(local_key_pair)?, }; - let bitcoin_wallet = Wallet::new("bob", &url) + let bitcoin_wallet = bitcoin::Wallet::new("bob", bitcoind_url) .await .expect("failed to create bitcoin wallet"); + let bitcoin_wallet = Arc::new(bitcoin_wallet); - let monero_wallet = Arc::new(monero::Wallet::localhost(MONERO_WALLET_RPC_PORT)); + let monero_wallet = Arc::new(monero::Wallet::new(monerod_url)); swap_as_bob( + bitcoin_wallet, + monero_wallet, satoshis, alice_addr, - refund, - bitcoin_wallet, transport, behaviour, ) @@ -143,12 +150,10 @@ async fn swap_as_alice( bitcoin_wallet: Arc, monero_wallet: Arc, addr: Multiaddr, - redeem: bitcoin::Address, - punish: bitcoin::Address, transport: SwapTransport, behaviour: Alice, ) -> Result<()> { - alice::swap(addr, redeem, punish, transport, behaviour).await + alice::swap(bitcoin_wallet, monero_wallet, addr, transport, behaviour).await } async fn swap_as_bob( @@ -156,18 +161,20 @@ async fn swap_as_bob( monero_wallet: Arc, sats: u64, alice: Multiaddr, - refund: bitcoin::Address, - wallet: W, transport: SwapTransport, behaviour: Bob, -) -> Result<()> -where - W: BuildTxLockPsbt + SignTxLock + BroadcastSignedTransaction + Send + Sync + 'static, -{ +) -> Result<()> { let (cmd_tx, mut cmd_rx) = mpsc::channel(1); let (mut rsp_tx, rsp_rx) = mpsc::channel(1); tokio::spawn(bob::swap( - sats, alice, cmd_tx, rsp_rx, refund, wallet, transport, behaviour, + bitcoin_wallet, + monero_wallet, + sats, + alice, + cmd_tx, + rsp_rx, + transport, + behaviour, )); loop { diff --git a/swap/src/monero.rs b/swap/src/monero.rs index 0d215738..eb9f9bbc 100644 --- a/swap/src/monero.rs +++ b/swap/src/monero.rs @@ -5,6 +5,7 @@ use monero::{Address, Network, PrivateKey}; use monero_harness::rpc::wallet; use std::{str::FromStr, time::Duration}; +use url::Url; pub use xmr_btc::monero::{ Amount, CreateWalletForOutput, InsufficientFunds, PrivateViewKey, PublicKey, PublicViewKey, Transfer, TransferProof, TxHash, WatchForTransfer, *, @@ -13,8 +14,8 @@ pub use xmr_btc::monero::{ pub struct Wallet(pub wallet::Client); impl Wallet { - pub fn localhost(port: u16) -> Self { - Self(wallet::Client::localhost(port)) + pub fn new(url: Url) -> Self { + Self(wallet::Client::new(url)) } /// Get the balance of the primary account. diff --git a/swap/tests/e2e.rs b/swap/tests/e2e.rs index 09ba8fb0..cb49ef05 100644 --- a/swap/tests/e2e.rs +++ b/swap/tests/e2e.rs @@ -5,7 +5,7 @@ mod e2e_test { use libp2p::Multiaddr; use monero_harness::Monero; use std::sync::Arc; - use swap::{alice, bob}; + use swap::{alice, bob, network::transport::build}; use testcontainers::clients::Cli; use tracing_subscriber::util::SubscriberInitExt; @@ -37,12 +37,12 @@ mod e2e_test { let xmr_bob = 0; let alice_btc_wallet = Arc::new( - swap::bitcoin::Wallet::new("alice", &bitcoind.node_url) + swap::bitcoin::Wallet::new("alice", bitcoind.node_url.clone()) .await .unwrap(), ); let bob_btc_wallet = Arc::new( - swap::bitcoin::Wallet::new("bob", &bitcoind.node_url) + swap::bitcoin::Wallet::new("bob", bitcoind.node_url.clone()) .await .unwrap(), ); @@ -57,15 +57,20 @@ mod e2e_test { let alice_xmr_wallet = Arc::new(swap::monero::Wallet(monero.alice_wallet_rpc_client())); let bob_xmr_wallet = Arc::new(swap::monero::Wallet(monero.bob_wallet_rpc_client())); + let alice_behaviour = alice::Alice::default(); + let alice_transport = build(alice_behaviour.identity()).unwrap(); let alice_swap = alice::swap( alice_btc_wallet.clone(), alice_xmr_wallet.clone(), alice_multiaddr.clone(), - None, + alice_transport, + alice_behaviour, ); let (cmd_tx, mut _cmd_rx) = mpsc::channel(1); let (mut rsp_tx, rsp_rx) = mpsc::channel(1); + let bob_behaviour = bob::Bob::default(); + let bob_transport = build(bob_behaviour.identity()).unwrap(); let bob_swap = bob::swap( bob_btc_wallet.clone(), bob_xmr_wallet.clone(), @@ -73,6 +78,8 @@ mod e2e_test { alice_multiaddr, cmd_tx, rsp_rx, + bob_transport, + bob_behaviour, ); // automate the verification step by accepting any amounts sent over by Alice