From 75f89f3b2544cec95d66c585c9d280619bd2a93b Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Mon, 18 Jan 2021 21:57:17 +1100 Subject: [PATCH] Use Bob swap factory in production --- swap/src/main.rs | 134 +++++++++----------------- swap/src/protocol/bob.rs | 165 +++++++++++++++++++++++++++++++- swap/tests/testutils/mod.rs | 186 ++++-------------------------------- 3 files changed, 225 insertions(+), 260 deletions(-) diff --git a/swap/src/main.rs b/swap/src/main.rs index 4a7cc7ef..891c7954 100644 --- a/swap/src/main.rs +++ b/swap/src/main.rs @@ -14,20 +14,16 @@ #![allow(non_snake_case)] use crate::cli::{Command, Options, Resume}; -use anyhow::{bail, Context, Result}; -use libp2p::{core::Multiaddr, PeerId}; +use anyhow::{Context, Result}; use prettytable::{row, Table}; -use rand::rngs::OsRng; use std::sync::Arc; use structopt::StructOpt; use swap::{ bitcoin, config::Config, - database::{Database, Swap}, - monero, network, - network::transport::build, - protocol::{alice, bob, bob::BobState}, - seed::Seed, + database::Database, + monero, + protocol::{alice, bob, bob::BobSwapFactory}, trace::init_tracing, StartingBalances, SwapAmounts, }; @@ -53,8 +49,6 @@ async fn main() -> Result<()> { let data_dir = std::path::Path::new(opt.data_dir.as_str()).to_path_buf(); let db_path = data_dir.join("database"); - let db = Database::open(db_path.as_path()).context("Could not open database")?; - let seed = swap::config::seed::Seed::from_file_or_generate(&data_dir) .expect("Could not retrieve/initialize seed") .into(); @@ -83,6 +77,11 @@ async fn main() -> Result<()> { let swap_id = Uuid::new_v4(); + info!( + "Swap sending {} and receiving {} starting with ID {}", + send_monero, receive_bitcoin, swap_id + ); + let alice_factory = alice::AliceSwapFactory::new( seed, config, @@ -108,7 +107,12 @@ async fn main() -> Result<()> { send_bitcoin, receive_monero, } => { - let (bitcoin_wallet, monero_wallet, _) = setup_wallets( + let swap_amounts = SwapAmounts { + btc: send_bitcoin, + xmr: receive_monero, + }; + + let (bitcoin_wallet, monero_wallet, starting_balances) = setup_wallets( bitcoind_url, bitcoin_wallet_name.as_str(), monero_wallet_rpc_url, @@ -116,47 +120,36 @@ async fn main() -> Result<()> { ) .await?; - let refund_address = bitcoin_wallet.new_address().await?; - let state0 = bob::state::State0::new( - &mut OsRng, - send_bitcoin, - receive_monero, - config.bitcoin_cancel_timelock, - config.bitcoin_punish_timelock, - refund_address, - config.monero_finality_confirmations, - ); - - let amounts = SwapAmounts { - btc: send_bitcoin, - xmr: receive_monero, - }; - - let bob_state = BobState::Started { state0, amounts }; - let swap_id = Uuid::new_v4(); + info!( - "Swap sending {} and receiving {} started with ID {}", + "Swap sending {} and receiving {} starting with ID {}", send_bitcoin, receive_monero, swap_id ); - bob_swap( + let bob_factory = BobSwapFactory::new( + seed, + db_path, swap_id, - bob_state, bitcoin_wallet, monero_wallet, - db, - alice_peer_id, + config, + starting_balances, alice_addr, - seed, - ) - .await?; + alice_peer_id, + ); + let (swap, event_loop) = bob_factory.new_swap_as_bob(swap_amounts).await?; + + tokio::spawn(async move { event_loop.run().await }); + bob::run(swap).await?; } Command::History => { let mut table = Table::new(); table.add_row(row!["SWAP ID", "STATE"]); + let db = Database::open(db_path.as_path()).context("Could not open database")?; + for (swap_id, state) in db.all()? { table.add_row(row![swap_id, state]); } @@ -203,30 +196,29 @@ async fn main() -> Result<()> { alice_peer_id, alice_addr, }) => { - let db_state = if let Swap::Bob(db_state) = db.get_state(swap_id)? { - db_state - } else { - bail!("Swap {} is not buy xmr.", swap_id) - }; - - let (bitcoin_wallet, monero_wallet, _) = setup_wallets( + let (bitcoin_wallet, monero_wallet, starting_balances) = setup_wallets( bitcoind_url, bitcoin_wallet_name.as_str(), monero_wallet_rpc_url, config, ) .await?; - bob_swap( + + let bob_factory = BobSwapFactory::new( + seed, + db_path, swap_id, - db_state.into(), bitcoin_wallet, monero_wallet, - db, - alice_peer_id, + config, + starting_balances, alice_addr, - seed, - ) - .await?; + alice_peer_id, + ); + let (swap, event_loop) = bob_factory.recover_bob_from_db().await?; + + tokio::spawn(async move { event_loop.run().await }); + bob::run(swap).await?; } }; @@ -268,43 +260,3 @@ async fn setup_wallets( Ok((bitcoin_wallet, monero_wallet, starting_balances)) } - -#[allow(clippy::too_many_arguments)] -async fn bob_swap( - swap_id: Uuid, - state: BobState, - bitcoin_wallet: Arc, - monero_wallet: Arc, - db: Database, - alice_peer_id: PeerId, - alice_addr: Multiaddr, - seed: Seed, -) -> Result { - let identity = network::Seed::new(seed).derive_libp2p_identity(); - let peer_id = identity.public().into_peer_id(); - - let bob_behaviour = bob::Behaviour::default(); - let bob_transport = build(identity)?; - - let (event_loop, handle) = bob::event_loop::EventLoop::new( - bob_transport, - bob_behaviour, - peer_id, - alice_peer_id, - alice_addr, - )?; - - let swap = bob::Swap { - state, - event_loop_handle: handle, - db, - bitcoin_wallet, - monero_wallet, - swap_id, - }; - - let swap = bob::swap::run(swap); - - tokio::spawn(event_loop.run()); - swap.await -} diff --git a/swap/src/protocol/bob.rs b/swap/src/protocol/bob.rs index fb2f09aa..25b35f79 100644 --- a/swap/src/protocol/bob.rs +++ b/swap/src/protocol/bob.rs @@ -1,15 +1,16 @@ //! Run an XMR/BTC swap in the role of Bob. //! Bob holds BTC and wishes receive XMR. +use anyhow::Result; use libp2p::{core::Multiaddr, NetworkBehaviour, PeerId}; use tracing::{debug, info}; use crate::{ bitcoin, bitcoin::EncryptedSignature, - monero, + database, monero, network, network::peer_tracker::{self, PeerTracker}, protocol::{alice, bob}, - SwapAmounts, + StartingBalances, SwapAmounts, }; pub use self::{ @@ -22,8 +23,10 @@ pub use self::{ state::*, swap::{run, run_until}, }; -use crate::database::Database; -use std::sync::Arc; +use crate::{config::Config, database::Database, network::transport::build, seed::Seed}; +use libp2p::identity::Keypair; +use rand::rngs::OsRng; +use std::{path::PathBuf, sync::Arc}; use uuid::Uuid; mod amounts; @@ -44,6 +47,160 @@ pub struct Swap { pub swap_id: Uuid, } +pub struct BobSwapFactory { + identity: Keypair, + peer_id: PeerId, + + db_path: PathBuf, + swap_id: Uuid, + + pub bitcoin_wallet: Arc, + pub monero_wallet: Arc, + config: Config, + pub starting_balances: StartingBalances, + + alice_connect_address: Multiaddr, + alice_connect_peer_id: PeerId, +} + +impl BobSwapFactory { + #[allow(clippy::too_many_arguments)] + pub fn new( + seed: Seed, + db_path: PathBuf, + swap_id: Uuid, + bitcoin_wallet: Arc, + monero_wallet: Arc, + config: Config, + starting_balances: StartingBalances, + alice_connect_address: Multiaddr, + alice_connect_peer_id: PeerId, + ) -> Self { + let identity = network::Seed::new(seed).derive_libp2p_identity(); + let peer_id = identity.public().into_peer_id(); + + Self { + identity, + peer_id, + db_path, + swap_id, + bitcoin_wallet, + monero_wallet, + config, + starting_balances, + alice_connect_address, + alice_connect_peer_id, + } + } + + pub async fn new_swap_as_bob( + &self, + swap_amounts: SwapAmounts, + ) -> Result<(bob::Swap, bob::EventLoop)> { + let initial_state = init_bob_state( + swap_amounts.btc, + swap_amounts.xmr, + self.bitcoin_wallet.clone(), + self.config, + ) + .await?; + + let (event_loop, event_loop_handle) = init_bob_event_loop( + self.identity.clone(), + self.peer_id.clone(), + self.alice_connect_peer_id.clone(), + self.alice_connect_address.clone(), + )?; + + let db = Database::open(self.db_path.as_path())?; + + Ok(( + Swap { + state: initial_state, + event_loop_handle, + db, + bitcoin_wallet: self.bitcoin_wallet.clone(), + monero_wallet: self.monero_wallet.clone(), + swap_id: self.swap_id, + }, + event_loop, + )) + } + + pub async fn recover_bob_from_db(&self) -> Result<(bob::Swap, bob::EventLoop)> { + // reopen the existing database + let db = Database::open(self.db_path.clone().as_path())?; + + let resume_state = if let database::Swap::Bob(state) = db.get_state(self.swap_id)? { + state.into() + } else { + unreachable!() + }; + + let (event_loop, event_loop_handle) = init_bob_event_loop( + self.identity.clone(), + self.peer_id.clone(), + self.alice_connect_peer_id.clone(), + self.alice_connect_address.clone(), + )?; + + Ok(( + Swap { + state: resume_state, + event_loop_handle, + db, + bitcoin_wallet: self.bitcoin_wallet.clone(), + monero_wallet: self.monero_wallet.clone(), + swap_id: self.swap_id, + }, + event_loop, + )) + } +} + +async fn init_bob_state( + btc_to_swap: bitcoin::Amount, + xmr_to_swap: monero::Amount, + bob_btc_wallet: Arc, + config: Config, +) -> Result { + let amounts = SwapAmounts { + btc: btc_to_swap, + xmr: xmr_to_swap, + }; + + let refund_address = bob_btc_wallet.new_address().await?; + let state0 = bob::State0::new( + &mut OsRng, + btc_to_swap, + xmr_to_swap, + config.bitcoin_cancel_timelock, + config.bitcoin_punish_timelock, + refund_address, + config.monero_finality_confirmations, + ); + + Ok(BobState::Started { state0, amounts }) +} + +fn init_bob_event_loop( + identity: Keypair, + peer_id: PeerId, + alice_peer_id: PeerId, + alice_addr: Multiaddr, +) -> Result<(bob::event_loop::EventLoop, bob::event_loop::EventLoopHandle)> { + let bob_behaviour = bob::Behaviour::default(); + let bob_transport = build(identity)?; + + bob::event_loop::EventLoop::new( + bob_transport, + bob_behaviour, + peer_id, + alice_peer_id, + alice_addr, + ) +} + #[derive(Debug, Clone)] pub enum OutEvent { ConnectionEstablished(PeerId), diff --git a/swap/tests/testutils/mod.rs b/swap/tests/testutils/mod.rs index 7bb05f8a..b0938705 100644 --- a/swap/tests/testutils/mod.rs +++ b/swap/tests/testutils/mod.rs @@ -2,21 +2,18 @@ use crate::testutils; use bitcoin_harness::Bitcoind; use futures::Future; use get_port::get_port; -use libp2p::{core::Multiaddr, PeerId}; +use libp2p::core::Multiaddr; use monero_harness::{image, Monero}; -use rand::rngs::OsRng; -use std::{path::PathBuf, sync::Arc}; +use std::sync::Arc; use swap::{ bitcoin, config::Config, - database::Database, - monero, network, - network::transport::build, + monero, protocol::{ alice, alice::{AliceState, AliceSwapFactory}, bob, - bob::BobState, + bob::{BobState, BobSwapFactory}, }, seed::Seed, StartingBalances, SwapAmounts, @@ -51,7 +48,8 @@ impl Test { let (swap, event_loop) = self .bob_swap_factory .new_swap_as_bob(self.swap_amounts) - .await; + .await + .unwrap(); tokio::spawn(async move { event_loop.run().await }); @@ -71,7 +69,7 @@ impl Test { } pub async fn recover_bob_from_db(&self) -> bob::Swap { - let (swap, event_loop) = self.bob_swap_factory.recover_bob_from_db().await; + let (swap, event_loop) = self.bob_swap_factory.recover_bob_from_db().await.unwrap(); tokio::spawn(async move { event_loop.run().await }); @@ -363,17 +361,26 @@ where btc: swap_amounts.btc * 10, }; + let (bob_bitcoin_wallet, bob_monero_wallet) = init_wallets( + "bob", + &containers.bitcoind, + &monero, + bob_starting_balances.clone(), + config, + ) + .await; + let bob_swap_factory = BobSwapFactory::new( Seed::random().unwrap(), - config, + tempdir().unwrap().path().to_path_buf(), Uuid::new_v4(), - &monero, - &containers.bitcoind, + bob_bitcoin_wallet, + bob_monero_wallet, + config, bob_starting_balances, alice_swap_factory.listen_address(), alice_swap_factory.peer_id(), - ) - .await; + ); let test = Test { swap_amounts, @@ -384,112 +391,6 @@ where testfn(test).await } -pub struct BobSwapFactory { - seed: Seed, - - db_path: PathBuf, - swap_id: Uuid, - - bitcoin_wallet: Arc, - monero_wallet: Arc, - config: Config, - starting_balances: StartingBalances, - - alice_connect_address: Multiaddr, - alice_connect_peer_id: PeerId, -} - -impl BobSwapFactory { - #[allow(clippy::too_many_arguments)] - async fn new( - seed: Seed, - config: Config, - swap_id: Uuid, - monero: &Monero, - bitcoind: &Bitcoind<'_>, - starting_balances: StartingBalances, - alice_connect_address: Multiaddr, - alice_connect_peer_id: PeerId, - ) -> Self { - let db_path = tempdir().unwrap().path().to_path_buf(); - - let (bitcoin_wallet, monero_wallet) = - init_wallets("bob", bitcoind, monero, starting_balances.clone(), config).await; - - Self { - seed, - db_path, - swap_id, - bitcoin_wallet, - monero_wallet, - config, - starting_balances, - alice_connect_address, - alice_connect_peer_id, - } - } - - pub async fn new_swap_as_bob(&self, swap_amounts: SwapAmounts) -> (bob::Swap, bob::EventLoop) { - let initial_state = init_bob_state( - swap_amounts.btc, - swap_amounts.xmr, - self.bitcoin_wallet.clone(), - self.config, - ) - .await; - - let (event_loop, event_loop_handle) = init_bob_event_loop( - self.seed, - self.alice_connect_peer_id.clone(), - self.alice_connect_address.clone(), - ); - - let db = Database::open(self.db_path.as_path()).unwrap(); - - ( - bob::Swap { - state: initial_state, - event_loop_handle, - db, - bitcoin_wallet: self.bitcoin_wallet.clone(), - monero_wallet: self.monero_wallet.clone(), - swap_id: self.swap_id, - }, - event_loop, - ) - } - - pub async fn recover_bob_from_db(&self) -> (bob::Swap, bob::EventLoop) { - // reopen the existing database - let db = Database::open(self.db_path.clone().as_path()).unwrap(); - - let resume_state = - if let swap::database::Swap::Bob(state) = db.get_state(self.swap_id).unwrap() { - state.into() - } else { - unreachable!() - }; - - let (event_loop, event_loop_handle) = init_bob_event_loop( - self.seed, - self.alice_connect_peer_id.clone(), - self.alice_connect_address.clone(), - ); - - ( - bob::Swap { - state: resume_state, - event_loop_handle, - db, - bitcoin_wallet: self.bitcoin_wallet.clone(), - monero_wallet: self.monero_wallet.clone(), - swap_id: self.swap_id, - }, - event_loop, - ) - } -} - async fn init_containers(cli: &Cli) -> (Monero, Containers<'_>) { let bitcoind = Bitcoind::new(&cli, "0.19.1").unwrap(); let _ = bitcoind.init(5).await; @@ -536,51 +437,6 @@ async fn init_wallets( (btc_wallet, xmr_wallet) } -async fn init_bob_state( - btc_to_swap: bitcoin::Amount, - xmr_to_swap: monero::Amount, - bob_btc_wallet: Arc, - config: Config, -) -> BobState { - let amounts = SwapAmounts { - btc: btc_to_swap, - xmr: xmr_to_swap, - }; - - let refund_address = bob_btc_wallet.new_address().await.unwrap(); - let state0 = bob::State0::new( - &mut OsRng, - btc_to_swap, - xmr_to_swap, - config.bitcoin_cancel_timelock, - config.bitcoin_punish_timelock, - refund_address, - config.monero_finality_confirmations, - ); - - BobState::Started { state0, amounts } -} - -fn init_bob_event_loop( - seed: Seed, - alice_peer_id: PeerId, - alice_addr: Multiaddr, -) -> (bob::event_loop::EventLoop, bob::event_loop::EventLoopHandle) { - let identity = network::Seed::new(seed).derive_libp2p_identity(); - let peer_id = identity.public().into_peer_id(); - - let bob_behaviour = bob::Behaviour::default(); - let bob_transport = build(identity).unwrap(); - bob::event_loop::EventLoop::new( - bob_transport, - bob_behaviour, - peer_id, - alice_peer_id, - alice_addr, - ) - .unwrap() -} - // This is just to keep the containers alive #[allow(dead_code)] struct Containers<'a> {