From 6d03d1bbffda9a0446044039ee9fe13510e2b1fe Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Tue, 15 Dec 2020 21:26:02 +1100 Subject: [PATCH] Resume command and global database cl-parameter --- swap/src/bin/swap.rs | 265 +++++++++++++++++++++++++++---------------- swap/src/cli.rs | 38 ++++++- swap/src/storage.rs | 2 +- 3 files changed, 203 insertions(+), 102 deletions(-) diff --git a/swap/src/bin/swap.rs b/swap/src/bin/swap.rs index f684f8c6..7ce428ff 100644 --- a/swap/src/bin/swap.rs +++ b/swap/src/bin/swap.rs @@ -13,13 +13,22 @@ #![forbid(unsafe_code)] use anyhow::Result; +use libp2p::core::Multiaddr; use prettytable::{row, Table}; use rand::rngs::OsRng; -use std::sync::Arc; +use std::{convert::TryFrom, sync::Arc}; use structopt::StructOpt; use swap::{ - alice, alice::swap::AliceState, bitcoin, bob, bob::swap::BobState, cli::Options, monero, - network::transport::build, storage::Database, trace::init_tracing, SwapAmounts, + alice, + alice::swap::AliceState, + bitcoin, bob, + bob::swap::BobState, + cli::{Command, Options}, + monero, + network::transport::build, + storage::Database, + trace::init_tracing, + SwapAmounts, }; use tracing::{info, log::LevelFilter}; use uuid::Uuid; @@ -34,12 +43,13 @@ async fn main() -> Result<()> { let opt = Options::from_args(); - // This currently creates the directory if it's not there in the first place - let db = Database::open(std::path::Path::new("./.swap-db/")).unwrap(); let config = Config::mainnet(); - match opt { - Options::SellXmr { + info!("Database: {}", opt.db_path); + let db = Database::open(std::path::Path::new(opt.db_path.as_str())).unwrap(); + + match opt.cmd { + Command::SellXmr { bitcoind_url, bitcoin_wallet_name, monero_wallet_rpc_url, @@ -47,37 +57,22 @@ async fn main() -> Result<()> { send_monero, receive_bitcoin, } => { - info!("running swap node as Alice ..."); + info!("Running swap node as Alice ..."); - let bitcoin_wallet = bitcoin::Wallet::new( - bitcoin_wallet_name.as_str(), + let (bitcoin_wallet, monero_wallet) = setup_wallets( bitcoind_url, - config.bitcoin_network, + bitcoin_wallet_name.as_str(), + monero_wallet_rpc_url, + config, ) - .await - .expect("failed to create bitcoin wallet"); - - let bitcoin_balance = bitcoin_wallet.balance().await?; - info!( - "Connection to Bitcoin wallet succeeded, balance: {}", - bitcoin_balance - ); - let bitcoin_wallet = Arc::new(bitcoin_wallet); - - let monero_wallet = monero::Wallet::new(monero_wallet_rpc_url); - let monero_balance = monero_wallet.get_balance().await?; - info!( - "Connection to Monero wallet succeeded, balance: {}", - monero_balance - ); - let monero_wallet = Arc::new(monero_wallet); + .await?; let amounts = SwapAmounts { btc: receive_bitcoin, xmr: send_monero, }; - let (alice_state, alice_behaviour) = { + let alice_state = { let rng = &mut OsRng; let a = bitcoin::SecretKey::new_random(rng); let s_a = cross_curve_dleq::Scalar::random(rng); @@ -96,43 +91,27 @@ async fn main() -> Result<()> { punish_address, ); - ( - AliceState::Started { amounts, state0 }, - alice::Behaviour::default(), - ) + AliceState::Started { amounts, state0 } }; - let alice_peer_id = alice_behaviour.peer_id(); - info!( - "Alice Peer ID (to be used by Bob to dial her): {}", - alice_peer_id - ); - - let alice_transport = build(alice_behaviour.identity())?; - - let (mut event_loop, handle) = - alice::event_loop::EventLoop::new(alice_transport, alice_behaviour, listen_addr)?; - let swap_id = Uuid::new_v4(); info!( "Swap sending {} and receiving {} started with ID {}", send_monero, receive_bitcoin, swap_id ); - let swap = alice::swap::swap( - alice_state, - handle, - bitcoin_wallet.clone(), - monero_wallet.clone(), - config, + alice_swap( swap_id, + alice_state, + listen_addr, + bitcoin_wallet, + monero_wallet, + config, db, - ); - - let _event_loop = tokio::spawn(async move { event_loop.run().await }); - swap.await?; + ) + .await?; } - Options::BuyXmr { + Command::BuyXmr { alice_addr, bitcoind_url, bitcoin_wallet_name, @@ -140,32 +119,15 @@ async fn main() -> Result<()> { send_bitcoin, receive_monero, } => { - info!("running swap node as Bob ..."); + info!("Running swap node as Bob ..."); - let bob_behaviour = bob::Behaviour::default(); - let bob_transport = build(bob_behaviour.identity())?; - - let bitcoin_wallet = bitcoin::Wallet::new( - bitcoin_wallet_name.as_str(), + let (bitcoin_wallet, monero_wallet) = setup_wallets( bitcoind_url, - config.bitcoin_network, + bitcoin_wallet_name.as_str(), + monero_wallet_rpc_url, + config, ) - .await - .expect("failed to create bitcoin wallet"); - let bitcoin_balance = bitcoin_wallet.balance().await?; - info!( - "Connection to Bitcoin wallet succeeded, balance: {}", - bitcoin_balance - ); - let bitcoin_wallet = Arc::new(bitcoin_wallet); - - let monero_wallet = monero::Wallet::new(monero_wallet_rpc_url); - let monero_balance = monero_wallet.get_balance().await?; - info!( - "Connection to Monero wallet succeeded, balance: {}", - monero_balance - ); - let monero_wallet = Arc::new(monero_wallet); + .await?; let refund_address = bitcoin_wallet.new_address().await.unwrap(); let state0 = xmr_btc::bob::State0::new( @@ -188,29 +150,15 @@ async fn main() -> Result<()> { addr: alice_addr, }; - let (event_loop, handle) = - bob::event_loop::EventLoop::new(bob_transport, bob_behaviour).unwrap(); - let swap_id = Uuid::new_v4(); info!( "Swap sending {} and receiving {} started with ID {}", send_bitcoin, receive_monero, swap_id ); - let swap = bob::swap::swap( - bob_state, - handle, - db, - bitcoin_wallet.clone(), - monero_wallet.clone(), - OsRng, - swap_id, - ); - - let _event_loop = tokio::spawn(async move { event_loop.run().await }); - swap.await?; + bob_swap(swap_id, bob_state, bitcoin_wallet, monero_wallet, db).await?; } - Options::History => { + Command::History => { let mut table = Table::new(); table.add_row(row!["SWAP ID", "STATE"]); @@ -222,8 +170,135 @@ async fn main() -> Result<()> { // Print the table to stdout table.printstd(); } - Options::Resume { .. } => todo!("implement this"), + Command::Resume { + swap_id, + bitcoind_url, + bitcoin_wallet_name, + monero_wallet_rpc_url, + listen_addr, + } => { + let db_swap = db.get_state(swap_id)?; + + if let Ok(alice_state) = AliceState::try_from(db_swap.clone()) { + let (bitcoin_wallet, monero_wallet) = setup_wallets( + bitcoind_url, + bitcoin_wallet_name.as_str(), + monero_wallet_rpc_url, + config, + ) + .await?; + alice_swap( + swap_id, + alice_state, + listen_addr, + bitcoin_wallet, + monero_wallet, + config, + db, + ) + .await?; + } else if let Ok(bob_state) = BobState::try_from(db_swap) { + let (bitcoin_wallet, monero_wallet) = setup_wallets( + bitcoind_url, + bitcoin_wallet_name.as_str(), + monero_wallet_rpc_url, + config, + ) + .await?; + bob_swap(swap_id, bob_state, bitcoin_wallet, monero_wallet, db).await?; + } else { + anyhow::bail!("Unable to construct swap state for swap with id {}") + } + } } Ok(()) } + +async fn setup_wallets( + bitcoind_url: url::Url, + bitcoin_wallet_name: &str, + monero_wallet_rpc_url: url::Url, + config: Config, +) -> Result<(Arc, Arc)> { + let bitcoin_wallet = + bitcoin::Wallet::new(bitcoin_wallet_name, bitcoind_url, config.bitcoin_network) + .await + .expect("failed to create bitcoin wallet"); + let bitcoin_balance = bitcoin_wallet.balance().await?; + info!( + "Connection to Bitcoin wallet succeeded, balance: {}", + bitcoin_balance + ); + let bitcoin_wallet = Arc::new(bitcoin_wallet); + + let monero_wallet = monero::Wallet::new(monero_wallet_rpc_url); + let monero_balance = monero_wallet.get_balance().await?; + info!( + "Connection to Monero wallet succeeded, balance: {}", + monero_balance + ); + let monero_wallet = Arc::new(monero_wallet); + + Ok((bitcoin_wallet, monero_wallet)) +} + +async fn alice_swap( + swap_id: Uuid, + state: AliceState, + listen_addr: Multiaddr, + bitcoin_wallet: Arc, + monero_wallet: Arc, + config: Config, + db: Database, +) -> Result { + let alice_behaviour = alice::Behaviour::default(); + + let alice_peer_id = alice_behaviour.peer_id(); + info!("Own Peer-ID: {}", alice_peer_id); + + let alice_transport = build(alice_behaviour.identity())?; + + let (mut event_loop, handle) = + alice::event_loop::EventLoop::new(alice_transport, alice_behaviour, listen_addr)?; + + let swap = alice::swap::swap( + state, + handle, + bitcoin_wallet.clone(), + monero_wallet.clone(), + config, + swap_id, + db, + ); + + let _event_loop = tokio::spawn(async move { event_loop.run().await }); + Ok(swap.await?) +} + +async fn bob_swap( + swap_id: Uuid, + state: BobState, + bitcoin_wallet: Arc, + monero_wallet: Arc, + db: Database, +) -> Result { + let bob_behaviour = bob::Behaviour::default(); + let bob_transport = build(bob_behaviour.identity())?; + + let (event_loop, handle) = + bob::event_loop::EventLoop::new(bob_transport, bob_behaviour).unwrap(); + + let swap = bob::swap::swap( + state, + handle, + db, + bitcoin_wallet.clone(), + monero_wallet.clone(), + OsRng, + swap_id, + ); + + let _event_loop = tokio::spawn(async move { event_loop.run().await }); + Ok(swap.await?) +} diff --git a/swap/src/cli.rs b/swap/src/cli.rs index 4b90611f..461d9771 100644 --- a/swap/src/cli.rs +++ b/swap/src/cli.rs @@ -2,9 +2,18 @@ use libp2p::core::Multiaddr; use url::Url; use uuid::Uuid; +#[derive(structopt::StructOpt, Debug)] +pub struct Options { + #[structopt(short = "db", long = "database", default_value = "./.swap-db/")] + pub db_path: String, + + #[structopt(subcommand)] + pub cmd: Command, +} + #[derive(structopt::StructOpt, Debug)] #[structopt(name = "xmr-btc-swap", about = "Trustless XMR BTC swaps")] -pub enum Options { +pub enum Command { SellXmr { #[structopt( short = "b", @@ -65,17 +74,34 @@ pub enum Options { }, History, Resume { - #[structopt(required = true)] + #[structopt(short = "id", long = "swap-id")] swap_id: Uuid, - #[structopt(default_value = "http://127.0.0.1:8332", long = "bitcoind")] + #[structopt( + short = "b", + long = "bitcoind", + default_value = "http://127.0.0.1:8332" + )] bitcoind_url: Url, - #[structopt(default_value = "http://127.0.0.1:18083/json_rpc", long = "monerod")] - monerod_url: Url, - #[structopt(short = "n", long = "bitcoin-wallet-name")] bitcoin_wallet_name: String, + + #[structopt( + short = "m", + long = "monero-wallet-rpc", + default_value = "http://127.0.0.1:18083/json_rpc" + )] + monero_wallet_rpc_url: Url, + + // TODO: The listen address is only relevant for Alice, but should be role independent + // see: https://github.com/comit-network/xmr-btc-swap/issues/77 + #[structopt( + short = "a", + long = "listen-addr", + default_value = "/ip4/127.0.0.1/tcp/9876" + )] + listen_addr: Multiaddr, }, } diff --git a/swap/src/storage.rs b/swap/src/storage.rs index 47c6ce11..f26330fe 100644 --- a/swap/src/storage.rs +++ b/swap/src/storage.rs @@ -39,7 +39,7 @@ impl Database { let encoded = self .0 .get(&key)? - .ok_or_else(|| anyhow!("State does not exist {:?}", key))?; + .ok_or_else(|| anyhow!("Swap with id {} not found in database", swap_id))?; let state = deserialize(&encoded).context("Could not deserialize state")?; Ok(state)