From f5cfe014be19d33de28dbbc684f2e7a1edb89bd0 Mon Sep 17 00:00:00 2001 From: rishflab Date: Fri, 15 Jan 2021 09:45:00 +1100 Subject: [PATCH 01/16] Fix imports --- swap/src/protocol/alice/swap.rs | 5 ++--- swap/src/protocol/bob/swap.rs | 10 ++++++---- swap/tests/testutils/mod.rs | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/swap/src/protocol/alice/swap.rs b/swap/src/protocol/alice/swap.rs index dd16d506..0f8cf54c 100644 --- a/swap/src/protocol/alice/swap.rs +++ b/swap/src/protocol/alice/swap.rs @@ -88,12 +88,11 @@ pub async fn run_until( state: AliceState, is_target_state: fn(&AliceState) -> bool, mut event_loop_handle: EventLoopHandle, - bitcoin_wallet: Arc, - monero_wallet: Arc, + bitcoin_wallet: Arc, + monero_wallet: Arc, config: Config, swap_id: Uuid, db: Database, - // TODO: Remove EventLoopHandle! ) -> Result { info!("Current state:{}", state); if is_target_state(&state) { diff --git a/swap/src/protocol/bob/swap.rs b/swap/src/protocol/bob/swap.rs index 022511d6..038b5523 100644 --- a/swap/src/protocol/bob/swap.rs +++ b/swap/src/protocol/bob/swap.rs @@ -7,7 +7,9 @@ use tracing::info; use uuid::Uuid; use crate::{ + bitcoin, database::{Database, Swap}, + monero, protocol::bob::{self, event_loop::EventLoopHandle, state::*}, ExpiredTimelocks, SwapAmounts, }; @@ -18,8 +20,8 @@ pub async fn swap( state: BobState, event_loop_handle: EventLoopHandle, db: Database, - bitcoin_wallet: Arc, - monero_wallet: Arc, + bitcoin_wallet: Arc, + monero_wallet: Arc, rng: R, swap_id: Uuid, ) -> Result @@ -69,8 +71,8 @@ pub async fn run_until( is_target_state: fn(&BobState) -> bool, mut event_loop_handle: EventLoopHandle, db: Database, - bitcoin_wallet: Arc, - monero_wallet: Arc, + bitcoin_wallet: Arc, + monero_wallet: Arc, mut rng: R, swap_id: Uuid, ) -> Result diff --git a/swap/tests/testutils/mod.rs b/swap/tests/testutils/mod.rs index db5d7ee2..4b1bad00 100644 --- a/swap/tests/testutils/mod.rs +++ b/swap/tests/testutils/mod.rs @@ -32,7 +32,7 @@ pub async fn init_wallets( name: &str, bitcoind: &Bitcoind<'_>, monero: &Monero, - btc_starting_balance: Option<::bitcoin::Amount>, + btc_starting_balance: Option, xmr_starting_balance: Option, config: Config, ) -> (Arc, Arc) { From e8fdf626236e1c3bfd68221950968df16f161e78 Mon Sep 17 00:00:00 2001 From: rishflab Date: Fri, 15 Jan 2021 10:20:18 +1100 Subject: [PATCH 02/16] Re-export swap function --- swap/src/protocol/alice.rs | 4 +++- swap/src/protocol/bob.rs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/swap/src/protocol/alice.rs b/swap/src/protocol/alice.rs index 96571299..9e9bc405 100644 --- a/swap/src/protocol/alice.rs +++ b/swap/src/protocol/alice.rs @@ -19,7 +19,9 @@ use crate::{ SwapAmounts, }; -pub use self::{amounts::*, message0::Message0, message1::Message1, message2::Message2, state::*}; +pub use self::{ + amounts::*, message0::Message0, message1::Message1, message2::Message2, state::*, swap::swap, +}; mod amounts; pub mod event_loop; diff --git a/swap/src/protocol/bob.rs b/swap/src/protocol/bob.rs index a0968ef5..8666be77 100644 --- a/swap/src/protocol/bob.rs +++ b/swap/src/protocol/bob.rs @@ -20,7 +20,7 @@ use crate::{ pub use self::{ amounts::*, message0::Message0, message1::Message1, message2::Message2, message3::Message3, - state::*, + state::*, swap::swap, }; mod amounts; From 6040f2ae63a7f493a4886f2557d1331ae59c5cd5 Mon Sep 17 00:00:00 2001 From: rishflab Date: Fri, 15 Jan 2021 10:31:25 +1100 Subject: [PATCH 03/16] Re-export event loops --- swap/src/protocol/alice.rs | 3 ++- swap/src/protocol/bob.rs | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/swap/src/protocol/alice.rs b/swap/src/protocol/alice.rs index 9e9bc405..d14f8477 100644 --- a/swap/src/protocol/alice.rs +++ b/swap/src/protocol/alice.rs @@ -20,7 +20,8 @@ use crate::{ }; pub use self::{ - amounts::*, message0::Message0, message1::Message1, message2::Message2, state::*, swap::swap, + amounts::*, event_loop::EventLoop, message0::Message0, message1::Message1, message2::Message2, + state::*, swap::swap, }; mod amounts; diff --git a/swap/src/protocol/bob.rs b/swap/src/protocol/bob.rs index 8666be77..d972c20c 100644 --- a/swap/src/protocol/bob.rs +++ b/swap/src/protocol/bob.rs @@ -19,8 +19,8 @@ use crate::{ }; pub use self::{ - amounts::*, message0::Message0, message1::Message1, message2::Message2, message3::Message3, - state::*, swap::swap, + amounts::*, event_loop::EventLoop, message0::Message0, message1::Message1, message2::Message2, + message3::Message3, state::*, swap::swap, }; mod amounts; From 9cbf6e977472f1f2ee153ce3982395a8d02250ca Mon Sep 17 00:00:00 2001 From: rishflab Date: Fri, 15 Jan 2021 10:42:49 +1100 Subject: [PATCH 04/16] Re-export event loop handles --- swap/src/protocol/alice.rs | 9 +++++++-- swap/src/protocol/bob.rs | 10 ++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/swap/src/protocol/alice.rs b/swap/src/protocol/alice.rs index d14f8477..300475a0 100644 --- a/swap/src/protocol/alice.rs +++ b/swap/src/protocol/alice.rs @@ -20,8 +20,13 @@ use crate::{ }; pub use self::{ - amounts::*, event_loop::EventLoop, message0::Message0, message1::Message1, message2::Message2, - state::*, swap::swap, + amounts::*, + event_loop::{EventLoop, EventLoopHandle}, + message0::Message0, + message1::Message1, + message2::Message2, + state::*, + swap::swap, }; mod amounts; diff --git a/swap/src/protocol/bob.rs b/swap/src/protocol/bob.rs index d972c20c..350a0018 100644 --- a/swap/src/protocol/bob.rs +++ b/swap/src/protocol/bob.rs @@ -19,8 +19,14 @@ use crate::{ }; pub use self::{ - amounts::*, event_loop::EventLoop, message0::Message0, message1::Message1, message2::Message2, - message3::Message3, state::*, swap::swap, + amounts::*, + event_loop::{EventLoop, EventLoopHandle}, + message0::Message0, + message1::Message1, + message2::Message2, + message3::Message3, + state::*, + swap::swap, }; mod amounts; From 537d05e01edfb5e1fe7e9168a1e11d15e41da40f Mon Sep 17 00:00:00 2001 From: rishflab Date: Fri, 15 Jan 2021 11:26:32 +1100 Subject: [PATCH 05/16] Add reusable test function We introduce a reusable test function to make it easier to add new tests and make our existing tests more readable. --- swap/tests/happy_path.rs | 149 +++++++++--------------------------- swap/tests/testutils/mod.rs | 133 ++++++++++++++++++++++++++++++++ 2 files changed, 171 insertions(+), 111 deletions(-) diff --git a/swap/tests/happy_path.rs b/swap/tests/happy_path.rs index c1e12e0e..200c1fbe 100644 --- a/swap/tests/happy_path.rs +++ b/swap/tests/happy_path.rs @@ -1,21 +1,9 @@ -use crate::testutils::{init_alice, init_bob}; -use futures::{ - future::{join, select}, - FutureExt, -}; -use get_port::get_port; -use libp2p::Multiaddr; use rand::rngs::OsRng; use swap::{ bitcoin, - config::Config, - monero, - protocol::{alice, bob}, - seed::Seed, + protocol::{alice, alice::AliceState, bob, bob::BobState}, }; -use testcontainers::clients::Cli; -use testutils::init_tracing; -use uuid::Uuid; +use tokio::join; pub mod testutils; @@ -23,108 +11,47 @@ pub mod testutils; #[tokio::test] async fn happy_path() { - let _guard = init_tracing(); + testutils::test(|alice, bob, swap_amounts| async move { + let alice_swap_fut = alice::swap( + alice.state, + alice.event_loop_handle, + alice.bitcoin_wallet.clone(), + alice.monero_wallet.clone(), + alice.config, + alice.swap_id, + alice.db, + ); + let bob_swap_fut = bob::swap( + bob.state, + bob.event_loop_handle, + bob.db, + bob.bitcoin_wallet.clone(), + bob.monero_wallet.clone(), + OsRng, + bob.swap_id, + ); + let (alice_state, bob_state) = join!(alice_swap_fut, bob_swap_fut); - let cli = Cli::default(); - let ( - monero, - testutils::Containers { - bitcoind, - monerods: _monerods, - }, - ) = testutils::init_containers(&cli).await; + let btc_alice_final = alice.bitcoin_wallet.as_ref().balance().await.unwrap(); + let btc_bob_final = bob.bitcoin_wallet.as_ref().balance().await.unwrap(); - let btc_to_swap = bitcoin::Amount::from_sat(1_000_000); - let btc_alice = bitcoin::Amount::ZERO; - let btc_bob = btc_to_swap * 10; + let xmr_alice_final = alice.monero_wallet.as_ref().get_balance().await.unwrap(); - // this xmr value matches the logic of alice::calculate_amounts i.e. btc * - // 10_000 * 100 - let xmr_to_swap = monero::Amount::from_piconero(1_000_000_000_000); - let xmr_alice = xmr_to_swap * 10; - let xmr_bob = monero::Amount::ZERO; + bob.monero_wallet.as_ref().inner.refresh().await.unwrap(); + let xmr_bob_final = bob.monero_wallet.as_ref().get_balance().await.unwrap(); - let port = get_port().expect("Failed to find a free port"); - let alice_multiaddr: Multiaddr = format!("/ip4/127.0.0.1/tcp/{}", port) - .parse() - .expect("failed to parse Alice's address"); + assert!(matches!(alice_state.unwrap(), AliceState::BtcRedeemed)); + assert!(matches!(bob_state.unwrap(), BobState::XmrRedeemed)); - let config = Config::regtest(); + assert_eq!( + btc_alice_final, + alice.btc_starting_balance + swap_amounts.btc + - bitcoin::Amount::from_sat(bitcoin::TX_FEE) + ); + assert!(btc_bob_final <= bob.btc_starting_balance - swap_amounts.btc); - let ( - alice_state, - mut alice_event_loop, - alice_event_loop_handle, - alice_btc_wallet, - alice_xmr_wallet, - alice_db, - ) = init_alice( - &bitcoind, - &monero, - btc_to_swap, - xmr_to_swap, - xmr_alice, - alice_multiaddr.clone(), - config, - Seed::random().unwrap(), - ) + assert!(xmr_alice_final <= alice.xmr_starting_balance - swap_amounts.xmr); + assert_eq!(xmr_bob_final, bob.xmr_starting_balance + swap_amounts.xmr); + }) .await; - - let (bob_state, bob_event_loop, bob_event_loop_handle, bob_btc_wallet, bob_xmr_wallet, bob_db) = - init_bob( - alice_multiaddr.clone(), - alice_event_loop.peer_id(), - &bitcoind, - &monero, - btc_to_swap, - btc_bob, - xmr_to_swap, - config, - ) - .await; - - let alice_swap_fut = alice::swap::swap( - alice_state, - alice_event_loop_handle, - alice_btc_wallet.clone(), - alice_xmr_wallet.clone(), - config, - Uuid::new_v4(), - alice_db, - ) - .boxed(); - - let alice_fut = select(alice_swap_fut, alice_event_loop.run().boxed()); - - let bob_swap_fut = bob::swap::swap( - bob_state, - bob_event_loop_handle, - bob_db, - bob_btc_wallet.clone(), - bob_xmr_wallet.clone(), - OsRng, - Uuid::new_v4(), - ) - .boxed(); - - let bob_fut = select(bob_swap_fut, bob_event_loop.run().boxed()); - - join(alice_fut, bob_fut).await; - - let btc_alice_final = alice_btc_wallet.as_ref().balance().await.unwrap(); - let btc_bob_final = bob_btc_wallet.as_ref().balance().await.unwrap(); - - let xmr_alice_final = alice_xmr_wallet.as_ref().get_balance().await.unwrap(); - - bob_xmr_wallet.as_ref().inner.refresh().await.unwrap(); - let xmr_bob_final = bob_xmr_wallet.as_ref().get_balance().await.unwrap(); - - assert_eq!( - btc_alice_final, - btc_alice + btc_to_swap - bitcoin::Amount::from_sat(bitcoin::TX_FEE) - ); - assert!(btc_bob_final <= btc_bob - btc_to_swap); - - assert!(xmr_alice_final <= xmr_alice - xmr_to_swap); - assert_eq!(xmr_bob_final, xmr_bob + xmr_to_swap); } diff --git a/swap/tests/testutils/mod.rs b/swap/tests/testutils/mod.rs index 4b1bad00..9fb9b20b 100644 --- a/swap/tests/testutils/mod.rs +++ b/swap/tests/testutils/mod.rs @@ -1,4 +1,7 @@ +use crate::testutils; use bitcoin_harness::Bitcoind; +use futures::Future; +use get_port::get_port; use libp2p::{core::Multiaddr, PeerId}; use monero_harness::{image, Monero}; use rand::rngs::OsRng; @@ -17,6 +20,136 @@ use tempfile::tempdir; use testcontainers::{clients::Cli, Container}; use tracing_core::dispatcher::DefaultGuard; use tracing_log::LogTracer; +use uuid::Uuid; + +pub struct Alice { + pub state: AliceState, + pub event_loop_handle: alice::EventLoopHandle, + pub bitcoin_wallet: Arc, + pub monero_wallet: Arc, + pub config: Config, + pub swap_id: Uuid, + pub db: Database, + pub xmr_starting_balance: monero::Amount, + pub btc_starting_balance: bitcoin::Amount, +} + +pub struct Bob { + pub state: BobState, + pub event_loop_handle: bob::EventLoopHandle, + pub db: Database, + pub bitcoin_wallet: Arc, + pub monero_wallet: Arc, + pub swap_id: Uuid, + pub btc_starting_balance: bitcoin::Amount, + pub xmr_starting_balance: monero::Amount, +} + +pub async fn test(testfn: T) +where + T: Fn(Alice, Bob, SwapAmounts) -> F, + F: Future, +{ + let cli = Cli::default(); + + let _guard = init_tracing(); + + let (monero, containers) = testutils::init_containers(&cli).await; + + let swap_amounts = SwapAmounts { + btc: bitcoin::Amount::from_sat(1_000_000), + xmr: monero::Amount::from_piconero(1_000_000_000_000), + }; + + let bob_btc_starting_balance = swap_amounts.btc * 10; + let alice_xmr_starting_balance = swap_amounts.xmr * 10; + + let port = get_port().expect("Failed to find a free port"); + + let alice_multiaddr: Multiaddr = format!("/ip4/127.0.0.1/tcp/{}", port) + .parse() + .expect("failed to parse Alice's address"); + + let config = Config::regtest(); + + let alice_seed = Seed::random().unwrap(); + + let (alice_btc_wallet, alice_xmr_wallet) = init_wallets( + "alice", + &containers.bitcoind, + &monero, + None, + Some(alice_xmr_starting_balance), + config, + ) + .await; + + let alice_state = init_alice_state( + swap_amounts.btc, + swap_amounts.xmr, + alice_btc_wallet.clone(), + config, + ) + .await; + + let (mut alice_event_loop, alice_event_loop_handle) = + init_alice_event_loop(alice_multiaddr.clone(), alice_seed); + + let alice_db_datadir = tempdir().unwrap(); + let alice_db = Database::open(alice_db_datadir.path()).unwrap(); + + let (bob_btc_wallet, bob_xmr_wallet) = init_wallets( + "bob", + &containers.bitcoind, + &monero, + Some(bob_btc_starting_balance), + None, + config, + ) + .await; + + let bob_state = init_bob_state( + swap_amounts.btc, + swap_amounts.xmr, + bob_btc_wallet.clone(), + config, + ) + .await; + + let (bob_event_loop, bob_event_loop_handle) = + init_bob_event_loop(alice_event_loop.peer_id(), alice_multiaddr); + + let bob_db_dir = tempdir().unwrap(); + let bob_db = Database::open(bob_db_dir.path()).unwrap(); + + tokio::spawn(async move { alice_event_loop.run().await }); + tokio::spawn(async move { bob_event_loop.run().await }); + + let alice = Alice { + state: alice_state, + event_loop_handle: alice_event_loop_handle, + bitcoin_wallet: alice_btc_wallet, + monero_wallet: alice_xmr_wallet, + config, + swap_id: Uuid::new_v4(), + db: alice_db, + xmr_starting_balance: alice_xmr_starting_balance, + btc_starting_balance: bitcoin::Amount::ZERO, + }; + + let bob = Bob { + state: bob_state, + event_loop_handle: bob_event_loop_handle, + db: bob_db, + bitcoin_wallet: bob_btc_wallet, + monero_wallet: bob_xmr_wallet, + swap_id: Uuid::new_v4(), + xmr_starting_balance: monero::Amount::ZERO, + btc_starting_balance: bob_btc_starting_balance, + }; + + testfn(alice, bob, swap_amounts).await +} pub async fn init_containers(cli: &Cli) -> (Monero, Containers<'_>) { let bitcoind = Bitcoind::new(&cli, "0.19.1").unwrap(); From b031bc5e427402ddaece2a3f0a376d5e61dd62ea Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Fri, 15 Jan 2021 11:49:47 +1100 Subject: [PATCH 06/16] Re-export run_until --- swap/src/protocol/alice.rs | 2 +- swap/src/protocol/bob.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/swap/src/protocol/alice.rs b/swap/src/protocol/alice.rs index 300475a0..6ce5fd95 100644 --- a/swap/src/protocol/alice.rs +++ b/swap/src/protocol/alice.rs @@ -26,7 +26,7 @@ pub use self::{ message1::Message1, message2::Message2, state::*, - swap::swap, + swap::{run_until, swap}, }; mod amounts; diff --git a/swap/src/protocol/bob.rs b/swap/src/protocol/bob.rs index 350a0018..ab9bbfc6 100644 --- a/swap/src/protocol/bob.rs +++ b/swap/src/protocol/bob.rs @@ -26,7 +26,7 @@ pub use self::{ message2::Message2, message3::Message3, state::*, - swap::swap, + swap::{run_until, swap}, }; mod amounts; From 152c8d7eba597b1c1e2e6378ea343864d7ec5e2f Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Fri, 15 Jan 2021 12:44:15 +1100 Subject: [PATCH 07/16] Refactor Alice restart test by introducing factory for creating Alices The factory keeps all static state of Alice to be able to simulate a restart. --- swap/tests/happy_path.rs | 11 +- swap/tests/happy_path_restart_alice.rs | 200 +++++++---------------- swap/tests/testutils/mod.rs | 211 +++++++++++++++++++------ 3 files changed, 222 insertions(+), 200 deletions(-) diff --git a/swap/tests/happy_path.rs b/swap/tests/happy_path.rs index 200c1fbe..10ca6ec0 100644 --- a/swap/tests/happy_path.rs +++ b/swap/tests/happy_path.rs @@ -11,12 +11,13 @@ pub mod testutils; #[tokio::test] async fn happy_path() { - testutils::test(|alice, bob, swap_amounts| async move { + testutils::test(|alice_factory, bob, swap_amounts| async move { + let alice = alice_factory.new_alice().await; let alice_swap_fut = alice::swap( alice.state, alice.event_loop_handle, - alice.bitcoin_wallet.clone(), - alice.monero_wallet.clone(), + alice.btc_wallet.clone(), + alice.xmr_wallet.clone(), alice.config, alice.swap_id, alice.db, @@ -32,10 +33,10 @@ async fn happy_path() { ); let (alice_state, bob_state) = join!(alice_swap_fut, bob_swap_fut); - let btc_alice_final = alice.bitcoin_wallet.as_ref().balance().await.unwrap(); + let btc_alice_final = alice.btc_wallet.as_ref().balance().await.unwrap(); let btc_bob_final = bob.bitcoin_wallet.as_ref().balance().await.unwrap(); - let xmr_alice_final = alice.monero_wallet.as_ref().get_balance().await.unwrap(); + let xmr_alice_final = alice.xmr_wallet.as_ref().get_balance().await.unwrap(); bob.monero_wallet.as_ref().inner.refresh().await.unwrap(); let xmr_bob_final = bob.monero_wallet.as_ref().get_balance().await.unwrap(); diff --git a/swap/tests/happy_path_restart_alice.rs b/swap/tests/happy_path_restart_alice.rs index 5c615227..fd0cb7fe 100644 --- a/swap/tests/happy_path_restart_alice.rs +++ b/swap/tests/happy_path_restart_alice.rs @@ -1,166 +1,78 @@ -use crate::testutils::{init_alice, init_bob}; -use get_port::get_port; -use libp2p::Multiaddr; use rand::rngs::OsRng; use swap::{ bitcoin, - config::Config, - database::Database, - monero, - protocol::{alice, alice::AliceState, bob}, - seed::Seed, + protocol::{alice, alice::AliceState, bob, bob::BobState}, }; -use tempfile::tempdir; -use testcontainers::clients::Cli; -use testutils::init_tracing; -use uuid::Uuid; pub mod testutils; #[tokio::test] async fn given_alice_restarts_after_encsig_is_learned_resume_swap() { - let _guard = init_tracing(); + testutils::test(|alice_factory, bob, swap_amounts| async move { + let alice = alice_factory.new_alice().await; - let cli = Cli::default(); - let ( - monero, - testutils::Containers { - bitcoind, - monerods: _monerods, - }, - ) = testutils::init_containers(&cli).await; + let bob_swap_fut = bob::swap( + bob.state, + bob.event_loop_handle, + bob.db, + bob.bitcoin_wallet.clone(), + bob.monero_wallet.clone(), + OsRng, + bob.swap_id, + ); + let bob_swap_handle = tokio::spawn(bob_swap_fut); - let btc_to_swap = bitcoin::Amount::from_sat(1_000_000); - let xmr_to_swap = monero::Amount::from_piconero(1_000_000_000_000); - - let bob_btc_starting_balance = btc_to_swap * 10; - let alice_xmr_starting_balance = xmr_to_swap * 10; - - let port = get_port().expect("Failed to find a free port"); - let alice_multiaddr: Multiaddr = format!("/ip4/127.0.0.1/tcp/{}", port) - .parse() - .expect("failed to parse Alice's address"); - - let config = Config::regtest(); - - let alice_seed = Seed::random().unwrap(); - let ( - start_state, - mut alice_event_loop, - alice_event_loop_handle, - alice_btc_wallet, - alice_xmr_wallet, - _, - ) = init_alice( - &bitcoind, - &monero, - btc_to_swap, - xmr_to_swap, - alice_xmr_starting_balance, - alice_multiaddr.clone(), - config, - alice_seed, - ) - .await; - - let alice_peer_id = alice_event_loop.peer_id(); - - let (bob_state, bob_event_loop, bob_event_loop_handle, bob_btc_wallet, bob_xmr_wallet, bob_db) = - init_bob( - alice_multiaddr.clone(), - alice_peer_id.clone(), - &bitcoind, - &monero, - btc_to_swap, - bob_btc_starting_balance, - xmr_to_swap, - config, + let alice_state = alice::run_until( + alice.state, + alice::swap::is_encsig_learned, + alice.event_loop_handle, + alice.btc_wallet.clone(), + alice.xmr_wallet.clone(), + alice.config, + alice.swap_id, + alice.db, ) - .await; + .await + .unwrap(); + assert!(matches!(alice_state, AliceState::EncSigLearned {..})); - // TODO: we are making a clone of Bob's wallets here to keep them in scope after - // Bob's wallets are moved into an async task. - let bob_btc_wallet_clone = bob_btc_wallet.clone(); - let bob_xmr_wallet_clone = bob_xmr_wallet.clone(); + let alice = alice_factory.recover_alice_from_db().await; + assert!(matches!(alice.state, AliceState::EncSigLearned {..})); - let bob_fut = bob::swap::swap( - bob_state, - bob_event_loop_handle, - bob_db, - bob_btc_wallet.clone(), - bob_xmr_wallet.clone(), - OsRng, - Uuid::new_v4(), - ); + let alice_state = alice::swap( + alice.state, + alice.event_loop_handle, + alice.btc_wallet.clone(), + alice.xmr_wallet.clone(), + alice.config, + alice.swap_id, + alice.db, + ) + .await + .unwrap(); - let alice_db_datadir = tempdir().unwrap(); - let alice_db = Database::open(alice_db_datadir.path()).unwrap(); + let bob_state = bob_swap_handle.await.unwrap(); - tokio::spawn(async move { alice_event_loop.run().await }); - let bob_swap_handle = tokio::spawn(bob_fut); - tokio::spawn(bob_event_loop.run()); + let btc_alice_final = alice.btc_wallet.as_ref().balance().await.unwrap(); + let btc_bob_final = bob.bitcoin_wallet.as_ref().balance().await.unwrap(); - let alice_swap_id = Uuid::new_v4(); + let xmr_alice_final = alice.xmr_wallet.as_ref().get_balance().await.unwrap(); - let alice_state = alice::swap::run_until( - start_state, - alice::swap::is_encsig_learned, - alice_event_loop_handle, - alice_btc_wallet.clone(), - alice_xmr_wallet.clone(), - config, - alice_swap_id, - alice_db, - ) - .await - .unwrap(); + bob.monero_wallet.as_ref().inner.refresh().await.unwrap(); + let xmr_bob_final = bob.monero_wallet.as_ref().get_balance().await.unwrap(); - assert!(matches!(alice_state, AliceState::EncSigLearned {..})); + assert!(matches!(alice_state, AliceState::BtcRedeemed)); + assert!(matches!(bob_state.unwrap(), BobState::XmrRedeemed)); - let alice_db = Database::open(alice_db_datadir.path()).unwrap(); + assert_eq!( + btc_alice_final, + alice.btc_starting_balance + swap_amounts.btc + - bitcoin::Amount::from_sat(bitcoin::TX_FEE) + ); + assert!(btc_bob_final <= bob.btc_starting_balance - swap_amounts.btc); - let resume_state = - if let swap::database::Swap::Alice(state) = alice_db.get_state(alice_swap_id).unwrap() { - assert!(matches!(state, swap::database::Alice::EncSigLearned {..})); - state.into() - } else { - unreachable!() - }; - - let (mut event_loop_after_restart, event_loop_handle_after_restart) = - testutils::init_alice_event_loop(alice_multiaddr, alice_seed); - tokio::spawn(async move { event_loop_after_restart.run().await }); - - let alice_state = alice::swap::swap( - resume_state, - event_loop_handle_after_restart, - alice_btc_wallet.clone(), - alice_xmr_wallet.clone(), - config, - alice_swap_id, - alice_db, - ) - .await - .unwrap(); - - // Wait for Bob to finish - bob_swap_handle.await.unwrap().unwrap(); - - assert!(matches!(alice_state, AliceState::BtcRedeemed {..})); - - let btc_alice_final = alice_btc_wallet.as_ref().balance().await.unwrap(); - let btc_bob_final = bob_btc_wallet_clone.as_ref().balance().await.unwrap(); - - assert_eq!( - btc_alice_final, - btc_to_swap - bitcoin::Amount::from_sat(bitcoin::TX_FEE) - ); - assert!(btc_bob_final <= bob_btc_starting_balance - btc_to_swap); - - let xmr_alice_final = alice_xmr_wallet.as_ref().get_balance().await.unwrap(); - bob_xmr_wallet_clone.as_ref().inner.refresh().await.unwrap(); - let xmr_bob_final = bob_xmr_wallet_clone.as_ref().get_balance().await.unwrap(); - - assert!(xmr_alice_final <= alice_xmr_starting_balance - xmr_to_swap); - assert_eq!(xmr_bob_final, xmr_to_swap); + assert!(xmr_alice_final <= alice.xmr_starting_balance - swap_amounts.xmr); + assert_eq!(xmr_bob_final, bob.xmr_starting_balance + swap_amounts.xmr); + }) + .await; } diff --git a/swap/tests/testutils/mod.rs b/swap/tests/testutils/mod.rs index 9fb9b20b..3a5186ad 100644 --- a/swap/tests/testutils/mod.rs +++ b/swap/tests/testutils/mod.rs @@ -5,7 +5,7 @@ use get_port::get_port; use libp2p::{core::Multiaddr, PeerId}; use monero_harness::{image, Monero}; use rand::rngs::OsRng; -use std::sync::Arc; +use std::{path::PathBuf, sync::Arc}; use swap::{ bitcoin, config::Config, @@ -23,15 +23,19 @@ use tracing_log::LogTracer; use uuid::Uuid; pub struct Alice { - pub state: AliceState, pub event_loop_handle: alice::EventLoopHandle, - pub bitcoin_wallet: Arc, - pub monero_wallet: Arc, + pub btc_wallet: Arc, + pub xmr_wallet: Arc, pub config: Config, - pub swap_id: Uuid, pub db: Database, + + pub state: AliceState, + pub xmr_starting_balance: monero::Amount, pub btc_starting_balance: bitcoin::Amount, + + // test context (state we have to keep to simulate restart) + pub swap_id: Uuid, } pub struct Bob { @@ -45,9 +49,149 @@ pub struct Bob { pub xmr_starting_balance: monero::Amount, } +pub struct AliceFactory { + listen_address: Multiaddr, + peer_id: PeerId, + + seed: Seed, + db_path: PathBuf, + swap_id: Uuid, + + // Stuff that should probably not be in here... + swap_amounts: SwapAmounts, + btc_wallet: Arc, + xmr_wallet: Arc, + config: Config, + xmr_starting_balance: monero::Amount, + btc_starting_balance: bitcoin::Amount, +} + +impl AliceFactory { + pub fn peer_id(&self) -> PeerId { + self.peer_id.clone() + } + + pub fn listen_address(&self) -> Multiaddr { + self.listen_address.clone() + } + + pub async fn new( + config: Config, + swap_amounts: SwapAmounts, + swap_id: Uuid, + monero: &Monero, + bitcoind: &Bitcoind<'_>, + xmr_starting_balance: monero::Amount, + btc_starting_balance: bitcoin::Amount, + ) -> Self { + let port = get_port().expect("Failed to find a free port"); + + let listen_address: Multiaddr = format!("/ip4/127.0.0.1/tcp/{}", port) + .parse() + .expect("failed to parse Alice's address"); + + let seed = Seed::random().unwrap(); + + let db_path = tempdir().unwrap().path().to_path_buf(); + + let alice_xmr_starting_balance = swap_amounts.xmr * 10; + let (btc_wallet, xmr_wallet) = init_wallets( + "alice", + bitcoind, + monero, + None, + Some(alice_xmr_starting_balance), + config, + ) + .await; + + // TODO: This should be done by changing the production code + let network_seed = network::Seed::new(seed); + let identity = network_seed.derive_libp2p_identity(); + let peer_id = PeerId::from(identity.public()); + + Self { + seed, + db_path, + listen_address, + peer_id, + swap_id, + swap_amounts, + btc_wallet, + xmr_wallet, + config, + xmr_starting_balance, + btc_starting_balance, + } + } + + pub async fn new_alice(&self) -> Alice { + let initial_state = init_alice_state( + self.swap_amounts.btc, + self.swap_amounts.xmr, + self.btc_wallet.clone(), + self.config, + ) + .await; + + let (mut event_loop, event_loop_handle) = + init_alice_event_loop(self.listen_address.clone(), self.seed); + + tokio::spawn(async move { event_loop.run().await }); + + let db = Database::open(self.db_path.as_path()).unwrap(); + + Alice { + event_loop_handle, + btc_wallet: self.btc_wallet.clone(), + xmr_wallet: self.xmr_wallet.clone(), + config: self.config, + db, + state: initial_state, + xmr_starting_balance: self.xmr_starting_balance, + btc_starting_balance: self.btc_starting_balance, + swap_id: self.swap_id, + } + } + + pub async fn recover_alice_from_db(&self) -> Alice { + // TODO: "simulated restart" issues: + // - create new wallets instead of reusing (hard because of container + // lifetimes) + // - consider aborting the old event loop (currently just keeps running) + + // reopen the existing database + let db = Database::open(self.db_path.clone().as_path()).unwrap(); + + let resume_state = + if let swap::database::Swap::Alice(state) = db.get_state(self.swap_id).unwrap() { + state.into() + } else { + unreachable!() + }; + + let (mut event_loop, event_loop_handle) = + init_alice_event_loop(self.listen_address.clone(), self.seed); + + tokio::spawn(async move { event_loop.run().await }); + + Alice { + state: resume_state, + event_loop_handle, + btc_wallet: self.btc_wallet.clone(), + xmr_wallet: self.xmr_wallet.clone(), + config: self.config, + swap_id: self.swap_id, + db, + xmr_starting_balance: self.xmr_starting_balance, + btc_starting_balance: self.btc_starting_balance, + } + } +} + pub async fn test(testfn: T) where - T: Fn(Alice, Bob, SwapAmounts) -> F, + T: Fn(AliceFactory, Bob, SwapAmounts) -> F, F: Future, { let cli = Cli::default(); @@ -61,42 +205,20 @@ where xmr: monero::Amount::from_piconero(1_000_000_000_000), }; - let bob_btc_starting_balance = swap_amounts.btc * 10; - let alice_xmr_starting_balance = swap_amounts.xmr * 10; - - let port = get_port().expect("Failed to find a free port"); - - let alice_multiaddr: Multiaddr = format!("/ip4/127.0.0.1/tcp/{}", port) - .parse() - .expect("failed to parse Alice's address"); - let config = Config::regtest(); - let alice_seed = Seed::random().unwrap(); - - let (alice_btc_wallet, alice_xmr_wallet) = init_wallets( - "alice", - &containers.bitcoind, + let alice_factory = AliceFactory::new( + config, + swap_amounts, + Uuid::new_v4(), &monero, - None, - Some(alice_xmr_starting_balance), - config, + &containers.bitcoind, + swap_amounts.xmr * 10, + bitcoin::Amount::ZERO, ) .await; - let alice_state = init_alice_state( - swap_amounts.btc, - swap_amounts.xmr, - alice_btc_wallet.clone(), - config, - ) - .await; - - let (mut alice_event_loop, alice_event_loop_handle) = - init_alice_event_loop(alice_multiaddr.clone(), alice_seed); - - let alice_db_datadir = tempdir().unwrap(); - let alice_db = Database::open(alice_db_datadir.path()).unwrap(); + let bob_btc_starting_balance = swap_amounts.btc * 10; let (bob_btc_wallet, bob_xmr_wallet) = init_wallets( "bob", @@ -117,26 +239,13 @@ where .await; let (bob_event_loop, bob_event_loop_handle) = - init_bob_event_loop(alice_event_loop.peer_id(), alice_multiaddr); + init_bob_event_loop(alice_factory.peer_id(), alice_factory.listen_address()); let bob_db_dir = tempdir().unwrap(); let bob_db = Database::open(bob_db_dir.path()).unwrap(); - tokio::spawn(async move { alice_event_loop.run().await }); tokio::spawn(async move { bob_event_loop.run().await }); - let alice = Alice { - state: alice_state, - event_loop_handle: alice_event_loop_handle, - bitcoin_wallet: alice_btc_wallet, - monero_wallet: alice_xmr_wallet, - config, - swap_id: Uuid::new_v4(), - db: alice_db, - xmr_starting_balance: alice_xmr_starting_balance, - btc_starting_balance: bitcoin::Amount::ZERO, - }; - let bob = Bob { state: bob_state, event_loop_handle: bob_event_loop_handle, @@ -148,7 +257,7 @@ where btc_starting_balance: bob_btc_starting_balance, }; - testfn(alice, bob, swap_amounts).await + testfn(alice_factory, bob, swap_amounts).await } pub async fn init_containers(cli: &Cli) -> (Monero, Containers<'_>) { From 87edec0d502937a3f852e77e39b88ee25ff732a0 Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Fri, 15 Jan 2021 18:34:46 +1100 Subject: [PATCH 08/16] Rename Alice's factory to harness and include redeem assertions This makes the redeem assertion reusable for all tests with a redeem scenario. Since the factory was not a clean factory before and is now doing even more it was renamed to harness. --- swap/tests/happy_path.rs | 29 +++------- swap/tests/happy_path_restart_alice.rs | 36 ++++-------- swap/tests/testutils/mod.rs | 76 +++++++++++++++----------- 3 files changed, 61 insertions(+), 80 deletions(-) diff --git a/swap/tests/happy_path.rs b/swap/tests/happy_path.rs index 10ca6ec0..c2a251b1 100644 --- a/swap/tests/happy_path.rs +++ b/swap/tests/happy_path.rs @@ -1,8 +1,5 @@ use rand::rngs::OsRng; -use swap::{ - bitcoin, - protocol::{alice, alice::AliceState, bob, bob::BobState}, -}; +use swap::protocol::{alice, bob, bob::BobState}; use tokio::join; pub mod testutils; @@ -11,13 +8,13 @@ pub mod testutils; #[tokio::test] async fn happy_path() { - testutils::test(|alice_factory, bob, swap_amounts| async move { - let alice = alice_factory.new_alice().await; + testutils::test(|alice_harness, bob, swap_amounts| async move { + let alice = alice_harness.new_alice().await; let alice_swap_fut = alice::swap( alice.state, alice.event_loop_handle, - alice.btc_wallet.clone(), - alice.xmr_wallet.clone(), + alice.bitcoin_wallet.clone(), + alice.monero_wallet.clone(), alice.config, alice.swap_id, alice.db, @@ -33,25 +30,15 @@ async fn happy_path() { ); let (alice_state, bob_state) = join!(alice_swap_fut, bob_swap_fut); - let btc_alice_final = alice.btc_wallet.as_ref().balance().await.unwrap(); + alice_harness.assert_redeemed(alice_state.unwrap()).await; + let btc_bob_final = bob.bitcoin_wallet.as_ref().balance().await.unwrap(); - let xmr_alice_final = alice.xmr_wallet.as_ref().get_balance().await.unwrap(); - bob.monero_wallet.as_ref().inner.refresh().await.unwrap(); + let xmr_bob_final = bob.monero_wallet.as_ref().get_balance().await.unwrap(); - - assert!(matches!(alice_state.unwrap(), AliceState::BtcRedeemed)); assert!(matches!(bob_state.unwrap(), BobState::XmrRedeemed)); - - assert_eq!( - btc_alice_final, - alice.btc_starting_balance + swap_amounts.btc - - bitcoin::Amount::from_sat(bitcoin::TX_FEE) - ); assert!(btc_bob_final <= bob.btc_starting_balance - swap_amounts.btc); - - assert!(xmr_alice_final <= alice.xmr_starting_balance - swap_amounts.xmr); assert_eq!(xmr_bob_final, bob.xmr_starting_balance + swap_amounts.xmr); }) .await; diff --git a/swap/tests/happy_path_restart_alice.rs b/swap/tests/happy_path_restart_alice.rs index fd0cb7fe..136dcd84 100644 --- a/swap/tests/happy_path_restart_alice.rs +++ b/swap/tests/happy_path_restart_alice.rs @@ -1,15 +1,12 @@ use rand::rngs::OsRng; -use swap::{ - bitcoin, - protocol::{alice, alice::AliceState, bob, bob::BobState}, -}; +use swap::protocol::{alice, alice::AliceState, bob, bob::BobState}; pub mod testutils; #[tokio::test] async fn given_alice_restarts_after_encsig_is_learned_resume_swap() { - testutils::test(|alice_factory, bob, swap_amounts| async move { - let alice = alice_factory.new_alice().await; + testutils::test(|alice_harness, bob, swap_amounts| async move { + let alice = alice_harness.new_alice().await; let bob_swap_fut = bob::swap( bob.state, @@ -26,8 +23,8 @@ async fn given_alice_restarts_after_encsig_is_learned_resume_swap() { alice.state, alice::swap::is_encsig_learned, alice.event_loop_handle, - alice.btc_wallet.clone(), - alice.xmr_wallet.clone(), + alice.bitcoin_wallet.clone(), + alice.monero_wallet.clone(), alice.config, alice.swap_id, alice.db, @@ -36,14 +33,14 @@ async fn given_alice_restarts_after_encsig_is_learned_resume_swap() { .unwrap(); assert!(matches!(alice_state, AliceState::EncSigLearned {..})); - let alice = alice_factory.recover_alice_from_db().await; + let alice = alice_harness.recover_alice_from_db().await; assert!(matches!(alice.state, AliceState::EncSigLearned {..})); let alice_state = alice::swap( alice.state, alice.event_loop_handle, - alice.btc_wallet.clone(), - alice.xmr_wallet.clone(), + alice.bitcoin_wallet.clone(), + alice.monero_wallet.clone(), alice.config, alice.swap_id, alice.db, @@ -51,27 +48,14 @@ async fn given_alice_restarts_after_encsig_is_learned_resume_swap() { .await .unwrap(); + alice_harness.assert_redeemed(alice_state).await; + let bob_state = bob_swap_handle.await.unwrap(); - - let btc_alice_final = alice.btc_wallet.as_ref().balance().await.unwrap(); let btc_bob_final = bob.bitcoin_wallet.as_ref().balance().await.unwrap(); - - let xmr_alice_final = alice.xmr_wallet.as_ref().get_balance().await.unwrap(); - bob.monero_wallet.as_ref().inner.refresh().await.unwrap(); let xmr_bob_final = bob.monero_wallet.as_ref().get_balance().await.unwrap(); - - assert!(matches!(alice_state, AliceState::BtcRedeemed)); assert!(matches!(bob_state.unwrap(), BobState::XmrRedeemed)); - - assert_eq!( - btc_alice_final, - alice.btc_starting_balance + swap_amounts.btc - - bitcoin::Amount::from_sat(bitcoin::TX_FEE) - ); assert!(btc_bob_final <= bob.btc_starting_balance - swap_amounts.btc); - - assert!(xmr_alice_final <= alice.xmr_starting_balance - swap_amounts.xmr); assert_eq!(xmr_bob_final, bob.xmr_starting_balance + swap_amounts.xmr); }) .await; diff --git a/swap/tests/testutils/mod.rs b/swap/tests/testutils/mod.rs index 3a5186ad..bbb6aff0 100644 --- a/swap/tests/testutils/mod.rs +++ b/swap/tests/testutils/mod.rs @@ -22,20 +22,19 @@ use tracing_core::dispatcher::DefaultGuard; use tracing_log::LogTracer; use uuid::Uuid; +pub struct StartingBalances { + pub xmr: monero::Amount, + pub btc: bitcoin::Amount, +} + pub struct Alice { - pub event_loop_handle: alice::EventLoopHandle, - pub btc_wallet: Arc, - pub xmr_wallet: Arc, - pub config: Config, - pub db: Database, - pub state: AliceState, - - pub xmr_starting_balance: monero::Amount, - pub btc_starting_balance: bitcoin::Amount, - - // test context (state we have to keep to simulate restart) + pub event_loop_handle: alice::EventLoopHandle, + pub bitcoin_wallet: Arc, + pub monero_wallet: Arc, + pub config: Config, pub swap_id: Uuid, + pub db: Database, } pub struct Bob { @@ -49,7 +48,7 @@ pub struct Bob { pub xmr_starting_balance: monero::Amount, } -pub struct AliceFactory { +pub struct AliceHarness { listen_address: Multiaddr, peer_id: PeerId, @@ -62,11 +61,10 @@ pub struct AliceFactory { btc_wallet: Arc, xmr_wallet: Arc, config: Config, - xmr_starting_balance: monero::Amount, - btc_starting_balance: bitcoin::Amount, + starting_balances: StartingBalances, } -impl AliceFactory { +impl AliceHarness { pub fn peer_id(&self) -> PeerId { self.peer_id.clone() } @@ -75,14 +73,28 @@ impl AliceFactory { self.listen_address.clone() } + pub async fn assert_redeemed(&self, state: AliceState) { + assert!(matches!(state, AliceState::BtcRedeemed)); + + let btc_alice_final = self.btc_wallet.as_ref().balance().await.unwrap(); + + assert_eq!( + btc_alice_final, + self.starting_balances.btc + self.swap_amounts.btc + - bitcoin::Amount::from_sat(bitcoin::TX_FEE) + ); + + let xmr_alice_final = self.xmr_wallet.as_ref().get_balance().await.unwrap(); + assert!(xmr_alice_final <= self.starting_balances.xmr - self.swap_amounts.xmr); + } + pub async fn new( config: Config, swap_amounts: SwapAmounts, swap_id: Uuid, monero: &Monero, bitcoind: &Bitcoind<'_>, - xmr_starting_balance: monero::Amount, - btc_starting_balance: bitcoin::Amount, + starting_balances: StartingBalances, ) -> Self { let port = get_port().expect("Failed to find a free port"); @@ -94,13 +106,13 @@ impl AliceFactory { let db_path = tempdir().unwrap().path().to_path_buf(); - let alice_xmr_starting_balance = swap_amounts.xmr * 10; + let xmr_starting_balance = swap_amounts.xmr * 10; let (btc_wallet, xmr_wallet) = init_wallets( "alice", bitcoind, monero, None, - Some(alice_xmr_starting_balance), + Some(xmr_starting_balance), config, ) .await; @@ -120,8 +132,7 @@ impl AliceFactory { btc_wallet, xmr_wallet, config, - xmr_starting_balance, - btc_starting_balance, + starting_balances, } } @@ -143,13 +154,11 @@ impl AliceFactory { Alice { event_loop_handle, - btc_wallet: self.btc_wallet.clone(), - xmr_wallet: self.xmr_wallet.clone(), + bitcoin_wallet: self.btc_wallet.clone(), + monero_wallet: self.xmr_wallet.clone(), config: self.config, db, state: initial_state, - xmr_starting_balance: self.xmr_starting_balance, - btc_starting_balance: self.btc_starting_balance, swap_id: self.swap_id, } } @@ -178,20 +187,18 @@ impl AliceFactory { Alice { state: resume_state, event_loop_handle, - btc_wallet: self.btc_wallet.clone(), - xmr_wallet: self.xmr_wallet.clone(), + bitcoin_wallet: self.btc_wallet.clone(), + monero_wallet: self.xmr_wallet.clone(), config: self.config, swap_id: self.swap_id, db, - xmr_starting_balance: self.xmr_starting_balance, - btc_starting_balance: self.btc_starting_balance, } } } pub async fn test(testfn: T) where - T: Fn(AliceFactory, Bob, SwapAmounts) -> F, + T: Fn(AliceHarness, Bob, SwapAmounts) -> F, F: Future, { let cli = Cli::default(); @@ -207,14 +214,17 @@ where let config = Config::regtest(); - let alice_factory = AliceFactory::new( + let alice_starting_balances = StartingBalances { + xmr: swap_amounts.xmr * 10, + btc: bitcoin::Amount::ZERO, + }; + let alice_factory = AliceHarness::new( config, swap_amounts, Uuid::new_v4(), &monero, &containers.bitcoind, - swap_amounts.xmr * 10, - bitcoin::Amount::ZERO, + alice_starting_balances, ) .await; From 59f9a1c286207017a36e9f0e94a24d51721b70ab Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Fri, 15 Jan 2021 19:03:11 +1100 Subject: [PATCH 09/16] Fix usage of StartingBalance in Alice and Bob --- swap/tests/testutils/mod.rs | 65 ++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 37 deletions(-) diff --git a/swap/tests/testutils/mod.rs b/swap/tests/testutils/mod.rs index bbb6aff0..eef2654f 100644 --- a/swap/tests/testutils/mod.rs +++ b/swap/tests/testutils/mod.rs @@ -22,6 +22,7 @@ use tracing_core::dispatcher::DefaultGuard; use tracing_log::LogTracer; use uuid::Uuid; +#[derive(Debug, Clone)] pub struct StartingBalances { pub xmr: monero::Amount, pub btc: bitcoin::Amount, @@ -106,16 +107,8 @@ impl AliceHarness { let db_path = tempdir().unwrap().path().to_path_buf(); - let xmr_starting_balance = swap_amounts.xmr * 10; - let (btc_wallet, xmr_wallet) = init_wallets( - "alice", - bitcoind, - monero, - None, - Some(xmr_starting_balance), - config, - ) - .await; + let (btc_wallet, xmr_wallet) = + init_wallets("alice", bitcoind, monero, starting_balances.clone(), config).await; // TODO: This should be done by changing the production code let network_seed = network::Seed::new(seed); @@ -228,14 +221,16 @@ where ) .await; - let bob_btc_starting_balance = swap_amounts.btc * 10; + let bob_starting_balances = StartingBalances { + xmr: monero::Amount::ZERO, + btc: swap_amounts.btc * 10, + }; let (bob_btc_wallet, bob_xmr_wallet) = init_wallets( "bob", &containers.bitcoind, &monero, - Some(bob_btc_starting_balance), - None, + bob_starting_balances.clone(), config, ) .await; @@ -263,8 +258,8 @@ where bitcoin_wallet: bob_btc_wallet, monero_wallet: bob_xmr_wallet, swap_id: Uuid::new_v4(), - xmr_starting_balance: monero::Amount::ZERO, - btc_starting_balance: bob_btc_starting_balance, + xmr_starting_balance: bob_starting_balances.xmr, + btc_starting_balance: bob_starting_balances.btc, }; testfn(alice_factory, bob, swap_amounts).await @@ -284,24 +279,13 @@ pub async fn init_wallets( name: &str, bitcoind: &Bitcoind<'_>, monero: &Monero, - btc_starting_balance: Option, - xmr_starting_balance: Option, + starting_balances: StartingBalances, config: Config, ) -> (Arc, Arc) { - match xmr_starting_balance { - Some(amount) => { - monero - .init(vec![(name, amount.as_piconero())]) - .await - .unwrap(); - } - None => { - monero - .init(vec![(name, monero::Amount::ZERO.as_piconero())]) - .await - .unwrap(); - } - }; + monero + .init(vec![(name, starting_balances.xmr.as_piconero())]) + .await + .unwrap(); let xmr_wallet = Arc::new(swap::monero::Wallet { inner: monero.wallet(name).unwrap().client(), @@ -314,9 +298,12 @@ pub async fn init_wallets( .unwrap(), ); - if let Some(amount) = btc_starting_balance { + if starting_balances.btc != bitcoin::Amount::ZERO { bitcoind - .mint(btc_wallet.inner.new_address().await.unwrap(), amount) + .mint( + btc_wallet.inner.new_address().await.unwrap(), + starting_balances.btc, + ) .await .unwrap(); } @@ -391,8 +378,10 @@ pub async fn init_alice( "alice", bitcoind, monero, - None, - Some(xmr_starting_balance), + StartingBalances { + xmr: xmr_starting_balance, + btc: bitcoin::Amount::ZERO, + }, config, ) .await; @@ -473,8 +462,10 @@ pub async fn init_bob( "bob", bitcoind, monero, - Some(btc_starting_balance), - None, + StartingBalances { + xmr: monero::Amount::ZERO, + btc: btc_starting_balance, + }, config, ) .await; From bede1c13dd6e68d79c8ee64645467bd31b9c848a Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Fri, 15 Jan 2021 19:29:26 +1100 Subject: [PATCH 10/16] Refactor Bob's side (happy path + alice restart) Refactor Bob's test setup in the same way as Alice's. Introduce BobHarness that allows creating and restarting as well as asserting redeemed for Bob. --- swap/tests/happy_path.rs | 16 +-- swap/tests/happy_path_restart_alice.rs | 12 +- swap/tests/testutils/mod.rs | 186 ++++++++++++++++++------- 3 files changed, 148 insertions(+), 66 deletions(-) diff --git a/swap/tests/happy_path.rs b/swap/tests/happy_path.rs index c2a251b1..4f40db1c 100644 --- a/swap/tests/happy_path.rs +++ b/swap/tests/happy_path.rs @@ -1,5 +1,5 @@ use rand::rngs::OsRng; -use swap::protocol::{alice, bob, bob::BobState}; +use swap::protocol::{alice, bob}; use tokio::join; pub mod testutils; @@ -8,7 +8,7 @@ pub mod testutils; #[tokio::test] async fn happy_path() { - testutils::test(|alice_harness, bob, swap_amounts| async move { + testutils::test(|alice_harness, bob_harness| async move { let alice = alice_harness.new_alice().await; let alice_swap_fut = alice::swap( alice.state, @@ -19,6 +19,8 @@ async fn happy_path() { alice.swap_id, alice.db, ); + + let bob = bob_harness.new_bob().await; let bob_swap_fut = bob::swap( bob.state, bob.event_loop_handle, @@ -31,15 +33,7 @@ async fn happy_path() { let (alice_state, bob_state) = join!(alice_swap_fut, bob_swap_fut); alice_harness.assert_redeemed(alice_state.unwrap()).await; - - let btc_bob_final = bob.bitcoin_wallet.as_ref().balance().await.unwrap(); - - bob.monero_wallet.as_ref().inner.refresh().await.unwrap(); - - let xmr_bob_final = bob.monero_wallet.as_ref().get_balance().await.unwrap(); - assert!(matches!(bob_state.unwrap(), BobState::XmrRedeemed)); - assert!(btc_bob_final <= bob.btc_starting_balance - swap_amounts.btc); - assert_eq!(xmr_bob_final, bob.xmr_starting_balance + swap_amounts.xmr); + bob_harness.assert_redeemed(bob_state.unwrap()).await; }) .await; } diff --git a/swap/tests/happy_path_restart_alice.rs b/swap/tests/happy_path_restart_alice.rs index 136dcd84..48612540 100644 --- a/swap/tests/happy_path_restart_alice.rs +++ b/swap/tests/happy_path_restart_alice.rs @@ -1,12 +1,13 @@ use rand::rngs::OsRng; -use swap::protocol::{alice, alice::AliceState, bob, bob::BobState}; +use swap::protocol::{alice, alice::AliceState, bob}; pub mod testutils; #[tokio::test] async fn given_alice_restarts_after_encsig_is_learned_resume_swap() { - testutils::test(|alice_harness, bob, swap_amounts| async move { + testutils::test(|alice_harness, bob_harness| async move { let alice = alice_harness.new_alice().await; + let bob = bob_harness.new_bob().await; let bob_swap_fut = bob::swap( bob.state, @@ -51,12 +52,7 @@ async fn given_alice_restarts_after_encsig_is_learned_resume_swap() { alice_harness.assert_redeemed(alice_state).await; let bob_state = bob_swap_handle.await.unwrap(); - let btc_bob_final = bob.bitcoin_wallet.as_ref().balance().await.unwrap(); - bob.monero_wallet.as_ref().inner.refresh().await.unwrap(); - let xmr_bob_final = bob.monero_wallet.as_ref().get_balance().await.unwrap(); - assert!(matches!(bob_state.unwrap(), BobState::XmrRedeemed)); - assert!(btc_bob_final <= bob.btc_starting_balance - swap_amounts.btc); - assert_eq!(xmr_bob_final, bob.xmr_starting_balance + swap_amounts.xmr); + bob_harness.assert_redeemed(bob_state.unwrap()).await }) .await; } diff --git a/swap/tests/testutils/mod.rs b/swap/tests/testutils/mod.rs index eef2654f..27d39111 100644 --- a/swap/tests/testutils/mod.rs +++ b/swap/tests/testutils/mod.rs @@ -38,17 +38,6 @@ pub struct Alice { pub db: Database, } -pub struct Bob { - pub state: BobState, - pub event_loop_handle: bob::EventLoopHandle, - pub db: Database, - pub bitcoin_wallet: Arc, - pub monero_wallet: Arc, - pub swap_id: Uuid, - pub btc_starting_balance: bitcoin::Amount, - pub xmr_starting_balance: monero::Amount, -} - pub struct AliceHarness { listen_address: Multiaddr, peer_id: PeerId, @@ -57,7 +46,6 @@ pub struct AliceHarness { db_path: PathBuf, swap_id: Uuid, - // Stuff that should probably not be in here... swap_amounts: SwapAmounts, btc_wallet: Arc, xmr_wallet: Arc, @@ -189,9 +177,137 @@ impl AliceHarness { } } +pub struct Bob { + pub state: BobState, + pub event_loop_handle: bob::EventLoopHandle, + pub db: Database, + pub bitcoin_wallet: Arc, + pub monero_wallet: Arc, + pub swap_id: Uuid, +} + +pub struct BobHarness { + db_path: PathBuf, + swap_id: Uuid, + + swap_amounts: SwapAmounts, + btc_wallet: Arc, + xmr_wallet: Arc, + config: Config, + starting_balances: StartingBalances, + + alice_connect_address: Multiaddr, + alice_connect_peer_id: PeerId, +} + +impl BobHarness { + #[allow(clippy::too_many_arguments)] + pub async fn new( + config: Config, + swap_amounts: SwapAmounts, + 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 (btc_wallet, xmr_wallet) = + init_wallets("bob", bitcoind, monero, starting_balances.clone(), config).await; + + Self { + db_path, + swap_id, + swap_amounts, + btc_wallet, + xmr_wallet, + config, + starting_balances, + alice_connect_address, + alice_connect_peer_id, + } + } + + pub async fn new_bob(&self) -> Bob { + let initial_state = init_bob_state( + self.swap_amounts.btc, + self.swap_amounts.xmr, + self.btc_wallet.clone(), + self.config, + ) + .await; + + let (event_loop, event_loop_handle) = init_bob_event_loop( + self.alice_connect_peer_id.clone(), + self.alice_connect_address.clone(), + ); + + tokio::spawn(async move { event_loop.run().await }); + + let db = Database::open(self.db_path.as_path()).unwrap(); + + Bob { + state: initial_state, + event_loop_handle, + db, + bitcoin_wallet: self.btc_wallet.clone(), + monero_wallet: self.xmr_wallet.clone(), + swap_id: self.swap_id, + } + } + + pub async fn recover_bob_from_db(&self) -> Bob { + // TODO: "simulated restart" issues: + // - create new wallets instead of reusing (hard because of container + // lifetimes) + // - consider aborting the old event loop (currently just keeps running) + + // 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.alice_connect_peer_id.clone(), + self.alice_connect_address.clone(), + ); + + tokio::spawn(async move { event_loop.run().await }); + + Bob { + state: resume_state, + event_loop_handle, + db, + bitcoin_wallet: self.btc_wallet.clone(), + monero_wallet: self.xmr_wallet.clone(), + swap_id: self.swap_id, + } + } + + pub async fn assert_redeemed(&self, state: BobState) { + assert!(matches!(state, BobState::XmrRedeemed)); + + let btc_bob_final = self.btc_wallet.as_ref().balance().await.unwrap(); + self.xmr_wallet.as_ref().inner.refresh().await.unwrap(); + let xmr_bob_final = self.xmr_wallet.as_ref().get_balance().await.unwrap(); + assert!(btc_bob_final <= self.starting_balances.btc - self.swap_amounts.btc); + assert_eq!( + xmr_bob_final, + self.starting_balances.xmr + self.swap_amounts.xmr + ); + } +} + pub async fn test(testfn: T) where - T: Fn(AliceHarness, Bob, SwapAmounts) -> F, + T: Fn(AliceHarness, BobHarness) -> F, F: Future, { let cli = Cli::default(); @@ -211,7 +327,7 @@ where xmr: swap_amounts.xmr * 10, btc: bitcoin::Amount::ZERO, }; - let alice_factory = AliceHarness::new( + let alice_harness = AliceHarness::new( config, swap_amounts, Uuid::new_v4(), @@ -226,43 +342,19 @@ where btc: swap_amounts.btc * 10, }; - let (bob_btc_wallet, bob_xmr_wallet) = init_wallets( - "bob", - &containers.bitcoind, + let bob_harness = BobHarness::new( + config, + swap_amounts, + Uuid::new_v4(), &monero, - bob_starting_balances.clone(), - config, + &containers.bitcoind, + bob_starting_balances, + alice_harness.listen_address(), + alice_harness.peer_id(), ) .await; - let bob_state = init_bob_state( - swap_amounts.btc, - swap_amounts.xmr, - bob_btc_wallet.clone(), - config, - ) - .await; - - let (bob_event_loop, bob_event_loop_handle) = - init_bob_event_loop(alice_factory.peer_id(), alice_factory.listen_address()); - - let bob_db_dir = tempdir().unwrap(); - let bob_db = Database::open(bob_db_dir.path()).unwrap(); - - tokio::spawn(async move { bob_event_loop.run().await }); - - let bob = Bob { - state: bob_state, - event_loop_handle: bob_event_loop_handle, - db: bob_db, - bitcoin_wallet: bob_btc_wallet, - monero_wallet: bob_xmr_wallet, - swap_id: Uuid::new_v4(), - xmr_starting_balance: bob_starting_balances.xmr, - btc_starting_balance: bob_starting_balances.btc, - }; - - testfn(alice_factory, bob, swap_amounts).await + testfn(alice_harness, bob_harness).await } pub async fn init_containers(cli: &Cli) -> (Monero, Containers<'_>) { From 8a2eb07928b1352a1912a19f09a1a31b3e4a884a Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Fri, 15 Jan 2021 19:34:51 +1100 Subject: [PATCH 11/16] Harmonize names and structure Simple renames and structure changes, no logical changes. --- swap/tests/happy_path.rs | 9 ++--- swap/tests/happy_path_restart_alice.rs | 4 +-- swap/tests/testutils/mod.rs | 50 +++++++++++++------------- 3 files changed, 32 insertions(+), 31 deletions(-) diff --git a/swap/tests/happy_path.rs b/swap/tests/happy_path.rs index 4f40db1c..18d31198 100644 --- a/swap/tests/happy_path.rs +++ b/swap/tests/happy_path.rs @@ -10,7 +10,9 @@ pub mod testutils; async fn happy_path() { testutils::test(|alice_harness, bob_harness| async move { let alice = alice_harness.new_alice().await; - let alice_swap_fut = alice::swap( + let bob = bob_harness.new_bob().await; + + let alice_swap = alice::swap( alice.state, alice.event_loop_handle, alice.bitcoin_wallet.clone(), @@ -20,8 +22,7 @@ async fn happy_path() { alice.db, ); - let bob = bob_harness.new_bob().await; - let bob_swap_fut = bob::swap( + let bob_swap = bob::swap( bob.state, bob.event_loop_handle, bob.db, @@ -30,7 +31,7 @@ async fn happy_path() { OsRng, bob.swap_id, ); - let (alice_state, bob_state) = join!(alice_swap_fut, bob_swap_fut); + let (alice_state, bob_state) = join!(alice_swap, bob_swap); alice_harness.assert_redeemed(alice_state.unwrap()).await; bob_harness.assert_redeemed(bob_state.unwrap()).await; diff --git a/swap/tests/happy_path_restart_alice.rs b/swap/tests/happy_path_restart_alice.rs index 48612540..1e49fdf3 100644 --- a/swap/tests/happy_path_restart_alice.rs +++ b/swap/tests/happy_path_restart_alice.rs @@ -9,7 +9,7 @@ async fn given_alice_restarts_after_encsig_is_learned_resume_swap() { let alice = alice_harness.new_alice().await; let bob = bob_harness.new_bob().await; - let bob_swap_fut = bob::swap( + let bob_swap = bob::swap( bob.state, bob.event_loop_handle, bob.db, @@ -18,7 +18,7 @@ async fn given_alice_restarts_after_encsig_is_learned_resume_swap() { OsRng, bob.swap_id, ); - let bob_swap_handle = tokio::spawn(bob_swap_fut); + let bob_swap_handle = tokio::spawn(bob_swap); let alice_state = alice::run_until( alice.state, diff --git a/swap/tests/testutils/mod.rs b/swap/tests/testutils/mod.rs index 27d39111..407632c6 100644 --- a/swap/tests/testutils/mod.rs +++ b/swap/tests/testutils/mod.rs @@ -47,8 +47,8 @@ pub struct AliceHarness { swap_id: Uuid, swap_amounts: SwapAmounts, - btc_wallet: Arc, - xmr_wallet: Arc, + bitcoin_wallet: Arc, + monero_wallet: Arc, config: Config, starting_balances: StartingBalances, } @@ -65,7 +65,7 @@ impl AliceHarness { pub async fn assert_redeemed(&self, state: AliceState) { assert!(matches!(state, AliceState::BtcRedeemed)); - let btc_alice_final = self.btc_wallet.as_ref().balance().await.unwrap(); + let btc_alice_final = self.bitcoin_wallet.as_ref().balance().await.unwrap(); assert_eq!( btc_alice_final, @@ -73,7 +73,7 @@ impl AliceHarness { - bitcoin::Amount::from_sat(bitcoin::TX_FEE) ); - let xmr_alice_final = self.xmr_wallet.as_ref().get_balance().await.unwrap(); + let xmr_alice_final = self.monero_wallet.as_ref().get_balance().await.unwrap(); assert!(xmr_alice_final <= self.starting_balances.xmr - self.swap_amounts.xmr); } @@ -95,7 +95,7 @@ impl AliceHarness { let db_path = tempdir().unwrap().path().to_path_buf(); - let (btc_wallet, xmr_wallet) = + let (bitcoin_wallet, monero_wallet) = init_wallets("alice", bitcoind, monero, starting_balances.clone(), config).await; // TODO: This should be done by changing the production code @@ -110,8 +110,8 @@ impl AliceHarness { peer_id, swap_id, swap_amounts, - btc_wallet, - xmr_wallet, + bitcoin_wallet, + monero_wallet, config, starting_balances, } @@ -121,7 +121,7 @@ impl AliceHarness { let initial_state = init_alice_state( self.swap_amounts.btc, self.swap_amounts.xmr, - self.btc_wallet.clone(), + self.bitcoin_wallet.clone(), self.config, ) .await; @@ -135,8 +135,8 @@ impl AliceHarness { Alice { event_loop_handle, - bitcoin_wallet: self.btc_wallet.clone(), - monero_wallet: self.xmr_wallet.clone(), + bitcoin_wallet: self.bitcoin_wallet.clone(), + monero_wallet: self.monero_wallet.clone(), config: self.config, db, state: initial_state, @@ -168,8 +168,8 @@ impl AliceHarness { Alice { state: resume_state, event_loop_handle, - bitcoin_wallet: self.btc_wallet.clone(), - monero_wallet: self.xmr_wallet.clone(), + bitcoin_wallet: self.bitcoin_wallet.clone(), + monero_wallet: self.monero_wallet.clone(), config: self.config, swap_id: self.swap_id, db, @@ -191,8 +191,8 @@ pub struct BobHarness { swap_id: Uuid, swap_amounts: SwapAmounts, - btc_wallet: Arc, - xmr_wallet: Arc, + bitcoin_wallet: Arc, + monero_wallet: Arc, config: Config, starting_balances: StartingBalances, @@ -214,15 +214,15 @@ impl BobHarness { ) -> Self { let db_path = tempdir().unwrap().path().to_path_buf(); - let (btc_wallet, xmr_wallet) = + let (bitcoin_wallet, monero_wallet) = init_wallets("bob", bitcoind, monero, starting_balances.clone(), config).await; Self { db_path, swap_id, swap_amounts, - btc_wallet, - xmr_wallet, + bitcoin_wallet, + monero_wallet, config, starting_balances, alice_connect_address, @@ -234,7 +234,7 @@ impl BobHarness { let initial_state = init_bob_state( self.swap_amounts.btc, self.swap_amounts.xmr, - self.btc_wallet.clone(), + self.bitcoin_wallet.clone(), self.config, ) .await; @@ -252,8 +252,8 @@ impl BobHarness { state: initial_state, event_loop_handle, db, - bitcoin_wallet: self.btc_wallet.clone(), - monero_wallet: self.xmr_wallet.clone(), + bitcoin_wallet: self.bitcoin_wallet.clone(), + monero_wallet: self.monero_wallet.clone(), swap_id: self.swap_id, } } @@ -285,8 +285,8 @@ impl BobHarness { state: resume_state, event_loop_handle, db, - bitcoin_wallet: self.btc_wallet.clone(), - monero_wallet: self.xmr_wallet.clone(), + bitcoin_wallet: self.bitcoin_wallet.clone(), + monero_wallet: self.monero_wallet.clone(), swap_id: self.swap_id, } } @@ -294,9 +294,9 @@ impl BobHarness { pub async fn assert_redeemed(&self, state: BobState) { assert!(matches!(state, BobState::XmrRedeemed)); - let btc_bob_final = self.btc_wallet.as_ref().balance().await.unwrap(); - self.xmr_wallet.as_ref().inner.refresh().await.unwrap(); - let xmr_bob_final = self.xmr_wallet.as_ref().get_balance().await.unwrap(); + let btc_bob_final = self.bitcoin_wallet.as_ref().balance().await.unwrap(); + self.monero_wallet.as_ref().inner.refresh().await.unwrap(); + let xmr_bob_final = self.monero_wallet.as_ref().get_balance().await.unwrap(); assert!(btc_bob_final <= self.starting_balances.btc - self.swap_amounts.btc); assert_eq!( xmr_bob_final, From 73a2841ec5aa3868ce266622ebdc137f8526d451 Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Mon, 18 Jan 2021 10:16:46 +1100 Subject: [PATCH 12/16] Refactor happy path bob restart tests --- .../happy_path_restart_bob_after_comm.rs | 197 ++++-------------- .../happy_path_restart_bob_before_comm.rs | 184 ++++------------ 2 files changed, 86 insertions(+), 295 deletions(-) diff --git a/swap/tests/happy_path_restart_bob_after_comm.rs b/swap/tests/happy_path_restart_bob_after_comm.rs index 3b83d4b9..7c1618b0 100644 --- a/swap/tests/happy_path_restart_bob_after_comm.rs +++ b/swap/tests/happy_path_restart_bob_after_comm.rs @@ -1,168 +1,59 @@ -use crate::testutils::{init_alice, init_bob}; -use get_port::get_port; -use libp2p::Multiaddr; use rand::rngs::OsRng; -use swap::{ - bitcoin, - config::Config, - database::Database, - monero, - protocol::{alice, bob, bob::BobState}, - seed::Seed, -}; -use tempfile::tempdir; -use testcontainers::clients::Cli; -use testutils::init_tracing; -use uuid::Uuid; +use swap::protocol::{alice, bob, bob::BobState}; pub mod testutils; #[tokio::test] async fn given_bob_restarts_after_encsig_is_sent_resume_swap() { - let _guard = init_tracing(); + testutils::test(|alice_harness, bob_harness| async move { + let alice = alice_harness.new_alice().await; + let bob = bob_harness.new_bob().await; - let cli = Cli::default(); - let ( - monero, - testutils::Containers { - bitcoind, - monerods: _monerods, - }, - ) = testutils::init_containers(&cli).await; + let alice_swap = alice::swap( + alice.state, + alice.event_loop_handle, + alice.bitcoin_wallet.clone(), + alice.monero_wallet.clone(), + alice.config, + alice.swap_id, + alice.db, + ); + let alice_swap_handle = tokio::spawn(alice_swap); - let btc_to_swap = bitcoin::Amount::from_sat(1_000_000); - let xmr_to_swap = monero::Amount::from_piconero(1_000_000_000_000); - - let bob_btc_starting_balance = btc_to_swap * 10; - let alice_xmr_starting_balance = xmr_to_swap * 10; - - let port = get_port().expect("Failed to find a free port"); - let alice_multiaddr: Multiaddr = format!("/ip4/127.0.0.1/tcp/{}", port) - .parse() - .expect("failed to parse Alice's address"); - - let config = Config::regtest(); - - let ( - alice_state, - mut alice_event_loop, - alice_event_loop_handle, - alice_btc_wallet, - alice_xmr_wallet, - alice_db, - ) = init_alice( - &bitcoind, - &monero, - btc_to_swap, - xmr_to_swap, - alice_xmr_starting_balance, - alice_multiaddr.clone(), - config, - Seed::random().unwrap(), - ) - .await; - - let alice_peer_id = alice_event_loop.peer_id(); - let (bob_state, bob_event_loop, bob_event_loop_handle, bob_btc_wallet, bob_xmr_wallet, _) = - init_bob( - alice_multiaddr.clone(), - alice_peer_id.clone(), - &bitcoind, - &monero, - btc_to_swap, - bob_btc_starting_balance, - xmr_to_swap, - config, + let bob_state = bob::run_until( + bob.state, + bob::swap::is_encsig_sent, + bob.event_loop_handle, + bob.db, + bob.bitcoin_wallet.clone(), + bob.monero_wallet.clone(), + OsRng, + bob.swap_id, ) - .await; + .await + .unwrap(); - // TODO: we are making a clone of Alices's wallets here to keep them in scope - // after Alices's wallets are moved into an async task. - let alice_btc_wallet_clone = alice_btc_wallet.clone(); - let alice_xmr_wallet_clone = alice_xmr_wallet.clone(); + assert!(matches!(bob_state, BobState::EncSigSent {..})); - // TODO: we are making a clone of Bob's wallets here to keep them in scope after - // Bob's wallets are moved into an async task. - let bob_btc_wallet_clone = bob_btc_wallet.clone(); - let bob_xmr_wallet_clone = bob_xmr_wallet.clone(); + let bob = bob_harness.recover_bob_from_db().await; + assert!(matches!(bob.state, BobState::EncSigSent {..})); - let alice_swap_handle = tokio::spawn(alice::swap::swap( - alice_state, - alice_event_loop_handle, - alice_btc_wallet.clone(), - alice_xmr_wallet.clone(), - config, - Uuid::new_v4(), - alice_db, - )); + let bob_state = bob::swap( + bob.state, + bob.event_loop_handle, + bob.db, + bob.bitcoin_wallet.clone(), + bob.monero_wallet.clone(), + OsRng, + bob.swap_id, + ) + .await + .unwrap(); - tokio::spawn(async move { alice_event_loop.run().await }); + bob_harness.assert_redeemed(bob_state).await; - tokio::spawn(bob_event_loop.run()); - - let bob_swap_id = Uuid::new_v4(); - let bob_db_datadir = tempdir().unwrap(); - let bob_db = Database::open(bob_db_datadir.path()).unwrap(); - - let bob_state = bob::swap::run_until( - bob_state, - bob::swap::is_encsig_sent, - bob_event_loop_handle, - bob_db, - bob_btc_wallet.clone(), - bob_xmr_wallet.clone(), - OsRng, - bob_swap_id, - ) - .await - .unwrap(); - - assert!(matches!(bob_state, BobState::EncSigSent {..})); - - let bob_db = Database::open(bob_db_datadir.path()).unwrap(); - - let resume_state = - if let swap::database::Swap::Bob(state) = bob_db.get_state(bob_swap_id).unwrap() { - assert!(matches!(state, swap::database::Bob::EncSigSent {..})); - state.into() - } else { - unreachable!() - }; - - let (event_loop_after_restart, event_loop_handle_after_restart) = - testutils::init_bob_event_loop(alice_peer_id, alice_multiaddr); - tokio::spawn(event_loop_after_restart.run()); - - let bob_state = bob::swap::swap( - resume_state, - event_loop_handle_after_restart, - bob_db, - bob_btc_wallet, - bob_xmr_wallet, - OsRng, - bob_swap_id, - ) - .await - .unwrap(); - - // Wait for Alice to finish too - alice_swap_handle.await.unwrap().unwrap(); - - assert!(matches!(bob_state, BobState::XmrRedeemed {..})); - - let btc_alice_final = alice_btc_wallet_clone.as_ref().balance().await.unwrap(); - let btc_bob_final = bob_btc_wallet_clone.as_ref().balance().await.unwrap(); - - assert_eq!( - btc_alice_final, - btc_to_swap - bitcoin::Amount::from_sat(bitcoin::TX_FEE) - ); - assert!(btc_bob_final <= bob_btc_starting_balance - btc_to_swap); - - let xmr_alice_final = alice_xmr_wallet_clone.as_ref().get_balance().await.unwrap(); - bob_xmr_wallet_clone.as_ref().inner.refresh().await.unwrap(); - let xmr_bob_final = bob_xmr_wallet_clone.as_ref().get_balance().await.unwrap(); - - assert!(xmr_alice_final <= alice_xmr_starting_balance - xmr_to_swap); - assert_eq!(xmr_bob_final, xmr_to_swap); + let alice_state = alice_swap_handle.await.unwrap(); + alice_harness.assert_redeemed(alice_state.unwrap()).await; + }) + .await; } diff --git a/swap/tests/happy_path_restart_bob_before_comm.rs b/swap/tests/happy_path_restart_bob_before_comm.rs index a3c5216e..c206e2fd 100644 --- a/swap/tests/happy_path_restart_bob_before_comm.rs +++ b/swap/tests/happy_path_restart_bob_before_comm.rs @@ -1,159 +1,59 @@ -use crate::testutils::{init_alice, init_bob}; -use get_port::get_port; -use libp2p::Multiaddr; use rand::rngs::OsRng; -use swap::{ - bitcoin, - config::Config, - database::Database, - monero, - protocol::{alice, alice::AliceState, bob, bob::BobState}, - seed::Seed, -}; -use tempfile::tempdir; -use testcontainers::clients::Cli; -use testutils::init_tracing; -use tokio::select; -use uuid::Uuid; +use swap::protocol::{alice, bob, bob::BobState}; pub mod testutils; #[tokio::test] async fn given_bob_restarts_after_xmr_is_locked_resume_swap() { - let _guard = init_tracing(); + testutils::test(|alice_harness, bob_harness| async move { + let alice = alice_harness.new_alice().await; + let bob = bob_harness.new_bob().await; - let cli = Cli::default(); - let ( - monero, - testutils::Containers { - bitcoind, - monerods: _monerods, - }, - ) = testutils::init_containers(&cli).await; + let alice_swap = alice::swap( + alice.state, + alice.event_loop_handle, + alice.bitcoin_wallet.clone(), + alice.monero_wallet.clone(), + alice.config, + alice.swap_id, + alice.db, + ); + let alice_swap_handle = tokio::spawn(alice_swap); - let btc_to_swap = bitcoin::Amount::from_sat(1_000_000); - let xmr_to_swap = monero::Amount::from_piconero(1_000_000_000_000); - - let bob_btc_starting_balance = btc_to_swap * 10; - let bob_xmr_starting_balance = monero::Amount::from_piconero(0); - - let alice_btc_starting_balance = bitcoin::Amount::ZERO; - let alice_xmr_starting_balance = xmr_to_swap * 10; - - let port = get_port().expect("Failed to find a free port"); - let alice_multiaddr: Multiaddr = format!("/ip4/127.0.0.1/tcp/{}", port) - .parse() - .expect("failed to parse Alice's address"); - - let ( - alice_state, - mut alice_event_loop, - alice_event_loop_handle, - alice_btc_wallet, - alice_xmr_wallet, - alice_db, - ) = init_alice( - &bitcoind, - &monero, - btc_to_swap, - xmr_to_swap, - alice_xmr_starting_balance, - alice_multiaddr.clone(), - Config::regtest(), - Seed::random().unwrap(), - ) - .await; - - let alice_peer_id = alice_event_loop.peer_id(); - let (bob_state, bob_event_loop_1, bob_event_loop_handle_1, bob_btc_wallet, bob_xmr_wallet, _) = - init_bob( - alice_multiaddr.clone(), - alice_peer_id.clone(), - &bitcoind, - &monero, - btc_to_swap, - bob_btc_starting_balance, - xmr_to_swap, - Config::regtest(), - ) - .await; - - let alice_fut = alice::swap::swap( - alice_state, - alice_event_loop_handle, - alice_btc_wallet.clone(), - alice_xmr_wallet.clone(), - Config::regtest(), - Uuid::new_v4(), - alice_db, - ); - - let bob_swap_id = Uuid::new_v4(); - let bob_db_datadir = tempdir().unwrap(); - - let bob_xmr_locked_fut = { - let bob_db = Database::open(bob_db_datadir.path()).unwrap(); - bob::swap::run_until( - bob_state, + let bob_state = bob::run_until( + bob.state, bob::swap::is_xmr_locked, - bob_event_loop_handle_1, - bob_db, - bob_btc_wallet.clone(), - bob_xmr_wallet.clone(), + bob.event_loop_handle, + bob.db, + bob.bitcoin_wallet.clone(), + bob.monero_wallet.clone(), OsRng, - bob_swap_id, + bob.swap_id, ) - }; + .await + .unwrap(); - tokio::spawn(async move { alice_event_loop.run().await }); + assert!(matches!(bob_state, BobState::XmrLocked {..})); - let alice_fut_handle = tokio::spawn(alice_fut); + let bob = bob_harness.recover_bob_from_db().await; + assert!(matches!(bob.state, BobState::XmrLocked {..})); - // We are selecting with bob_event_loop_1 so that we stop polling on it once - // bob reaches `xmr locked` state. - let bob_restart_state = select! { - res = bob_xmr_locked_fut => res.unwrap(), - _ = bob_event_loop_1.run() => panic!("The event loop should never finish") - }; + let bob_state = bob::swap( + bob.state, + bob.event_loop_handle, + bob.db, + bob.bitcoin_wallet.clone(), + bob.monero_wallet.clone(), + OsRng, + bob.swap_id, + ) + .await + .unwrap(); - let (bob_event_loop_2, bob_event_loop_handle_2) = - testutils::init_bob_event_loop(alice_peer_id, alice_multiaddr); + bob_harness.assert_redeemed(bob_state).await; - let bob_fut = bob::swap::swap( - bob_restart_state, - bob_event_loop_handle_2, - Database::open(bob_db_datadir.path()).unwrap(), - bob_btc_wallet.clone(), - bob_xmr_wallet.clone(), - OsRng, - bob_swap_id, - ); - - let bob_final_state = select! { - bob_final_state = bob_fut => bob_final_state.unwrap(), - _ = bob_event_loop_2.run() => panic!("Event loop is not expected to stop") - }; - - assert!(matches!(bob_final_state, BobState::XmrRedeemed)); - - // Wait for Alice to finish too. - let alice_final_state = alice_fut_handle.await.unwrap().unwrap(); - assert!(matches!(alice_final_state, AliceState::BtcRedeemed)); - - let btc_alice_final = alice_btc_wallet.as_ref().balance().await.unwrap(); - let btc_bob_final = bob_btc_wallet.as_ref().balance().await.unwrap(); - - let xmr_alice_final = alice_xmr_wallet.as_ref().get_balance().await.unwrap(); - - bob_xmr_wallet.as_ref().inner.refresh().await.unwrap(); - let xmr_bob_final = bob_xmr_wallet.as_ref().get_balance().await.unwrap(); - - assert_eq!( - btc_alice_final, - alice_btc_starting_balance + btc_to_swap - bitcoin::Amount::from_sat(bitcoin::TX_FEE) - ); - assert!(btc_bob_final <= bob_btc_starting_balance - btc_to_swap); - - assert!(xmr_alice_final <= alice_xmr_starting_balance - xmr_to_swap); - assert_eq!(xmr_bob_final, bob_xmr_starting_balance + xmr_to_swap); + let alice_state = alice_swap_handle.await.unwrap(); + alice_harness.assert_redeemed(alice_state.unwrap()).await; + }) + .await; } From 55024572aee7acbac8dd005c550ea5c294a5777a Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Mon, 18 Jan 2021 10:57:52 +1100 Subject: [PATCH 13/16] Refactor punish test and punish assertions --- swap/tests/punish.rs | 188 +++++++++++------------------------- swap/tests/testutils/mod.rs | 54 +++++++++-- 2 files changed, 99 insertions(+), 143 deletions(-) diff --git a/swap/tests/punish.rs b/swap/tests/punish.rs index 4628d45b..dacc1cc5 100644 --- a/swap/tests/punish.rs +++ b/swap/tests/punish.rs @@ -1,21 +1,5 @@ -use crate::testutils::{init_alice, init_bob}; -use futures::{ - future::{join, select, Either}, - FutureExt, -}; -use get_port::get_port; -use libp2p::Multiaddr; use rand::rngs::OsRng; -use swap::{ - bitcoin, - config::Config, - monero, - protocol::{alice, alice::AliceState, bob, bob::BobState}, - seed::Seed, -}; -use testcontainers::clients::Cli; -use testutils::init_tracing; -use uuid::Uuid; +use swap::protocol::{alice, bob, bob::BobState}; pub mod testutils; @@ -23,128 +7,64 @@ pub mod testutils; /// the encsig and fail to refund or redeem. Alice punishes. #[tokio::test] async fn alice_punishes_if_bob_never_acts_after_fund() { - let _guard = init_tracing(); + testutils::test(|alice_harness, bob_harness| async move { + let alice = alice_harness.new_alice().await; + let bob = bob_harness.new_bob().await; - let cli = Cli::default(); - let ( - monero, - testutils::Containers { - bitcoind, - monerods: _monerods, - }, - ) = testutils::init_containers(&cli).await; + let alice_swap = alice::swap( + alice.state, + alice.event_loop_handle, + alice.bitcoin_wallet.clone(), + alice.monero_wallet.clone(), + alice.config, + alice.swap_id, + alice.db, + ); + let alice_swap_handle = tokio::spawn(alice_swap); - let btc_to_swap = bitcoin::Amount::from_sat(1_000_000); - let xmr_to_swap = monero::Amount::from_piconero(1_000_000_000_000); - - let bob_btc_starting_balance = btc_to_swap * 10; - - let alice_btc_starting_balance = bitcoin::Amount::ZERO; - let alice_xmr_starting_balance = xmr_to_swap * 10; - - let port = get_port().expect("Failed to find a free port"); - let alice_multiaddr: Multiaddr = format!("/ip4/127.0.0.1/tcp/{}", port) - .parse() - .expect("failed to parse Alice's address"); - - let config = Config::regtest(); - - let ( - alice_state, - mut alice_event_loop, - alice_event_loop_handle, - alice_btc_wallet, - alice_xmr_wallet, - alice_db, - ) = init_alice( - &bitcoind, - &monero, - btc_to_swap, - xmr_to_swap, - alice_xmr_starting_balance, - alice_multiaddr.clone(), - config, - Seed::random().unwrap(), - ) - .await; - - let (bob_state, bob_event_loop, bob_event_loop_handle, bob_btc_wallet, bob_xmr_wallet, bob_db) = - init_bob( - alice_multiaddr, - alice_event_loop.peer_id(), - &bitcoind, - &monero, - btc_to_swap, - bob_btc_starting_balance, - xmr_to_swap, - config, + let bob_state = bob::run_until( + bob.state, + bob::swap::is_btc_locked, + bob.event_loop_handle, + bob.db, + bob.bitcoin_wallet.clone(), + bob.monero_wallet.clone(), + OsRng, + bob.swap_id, ) - .await; - - let bob_btc_locked_fut = bob::swap::run_until( - bob_state, - bob::swap::is_btc_locked, - bob_event_loop_handle, - bob_db, - bob_btc_wallet.clone(), - bob_xmr_wallet.clone(), - OsRng, - Uuid::new_v4(), - ) - .boxed(); - - let bob_fut = select(bob_btc_locked_fut, bob_event_loop.run().boxed()); - - let alice_fut = alice::swap::swap( - alice_state, - alice_event_loop_handle, - alice_btc_wallet.clone(), - alice_xmr_wallet.clone(), - Config::regtest(), - Uuid::new_v4(), - alice_db, - ) - .boxed(); - - let alice_fut = select(alice_fut, alice_event_loop.run().boxed()); - - // Wait until alice has locked xmr and bob has locked btc - let (alice_state, bob_state) = join(alice_fut, bob_fut).await; - - let alice_state = match alice_state { - Either::Left((state, _)) => state.unwrap(), - Either::Right(_) => panic!("Alice event loop should not terminate."), - }; - - let bob_state = match bob_state { - Either::Left((state, _)) => state.unwrap(), - Either::Right(_) => panic!("Bob event loop should not terminate."), - }; - - assert!(matches!(alice_state, AliceState::BtcPunished)); - let bob_state3 = if let BobState::BtcLocked(state3, ..) = bob_state { - state3 - } else { - panic!("Bob in unexpected state"); - }; - - let btc_alice_final = alice_btc_wallet.as_ref().balance().await.unwrap(); - let btc_bob_final = bob_btc_wallet.as_ref().balance().await.unwrap(); - - // lock_tx_bitcoin_fee is determined by the wallet, it is not necessarily equal - // to TX_FEE - let lock_tx_bitcoin_fee = bob_btc_wallet - .transaction_fee(bob_state3.tx_lock_id()) .await .unwrap(); - assert_eq!( - btc_alice_final, - alice_btc_starting_balance + btc_to_swap - bitcoin::Amount::from_sat(2 * bitcoin::TX_FEE) - ); + assert!(matches!(bob_state, BobState::BtcLocked {..})); - assert_eq!( - btc_bob_final, - bob_btc_starting_balance - btc_to_swap - lock_tx_bitcoin_fee - ); + let alice_state = alice_swap_handle.await.unwrap(); + alice_harness.assert_punished(alice_state.unwrap()).await; + + // Restart Bob after Alice punished to ensure Bob transitions to + // punished and does not run indefinitely + let bob = bob_harness.recover_bob_from_db().await; + assert!(matches!(bob.state, BobState::BtcLocked {..})); + + // TODO: make lock-tx-id available in final states + let lock_tx_id = if let BobState::BtcLocked(state3) = bob_state { + state3.tx_lock_id() + } else { + panic!("Bob in unexpected state"); + }; + + let bob_state = bob::swap( + bob.state, + bob.event_loop_handle, + bob.db, + bob.bitcoin_wallet.clone(), + bob.monero_wallet.clone(), + OsRng, + bob.swap_id, + ) + .await + .unwrap(); + + bob_harness.assert_punished(bob_state, lock_tx_id).await; + }) + .await; } diff --git a/swap/tests/testutils/mod.rs b/swap/tests/testutils/mod.rs index 407632c6..e9ffa108 100644 --- a/swap/tests/testutils/mod.rs +++ b/swap/tests/testutils/mod.rs @@ -65,16 +65,29 @@ impl AliceHarness { pub async fn assert_redeemed(&self, state: AliceState) { assert!(matches!(state, AliceState::BtcRedeemed)); - let btc_alice_final = self.bitcoin_wallet.as_ref().balance().await.unwrap(); - + let btc_balance_after_swap = self.bitcoin_wallet.as_ref().balance().await.unwrap(); assert_eq!( - btc_alice_final, + btc_balance_after_swap, self.starting_balances.btc + self.swap_amounts.btc - bitcoin::Amount::from_sat(bitcoin::TX_FEE) ); - let xmr_alice_final = self.monero_wallet.as_ref().get_balance().await.unwrap(); - assert!(xmr_alice_final <= self.starting_balances.xmr - self.swap_amounts.xmr); + let xmr_balance_after_swap = self.monero_wallet.as_ref().get_balance().await.unwrap(); + assert!(xmr_balance_after_swap <= self.starting_balances.xmr - self.swap_amounts.xmr); + } + + pub async fn assert_punished(&self, state: AliceState) { + assert!(matches!(state, AliceState::BtcPunished)); + + let btc_balance_after_swap = self.bitcoin_wallet.as_ref().balance().await.unwrap(); + assert_eq!( + btc_balance_after_swap, + self.starting_balances.btc + self.swap_amounts.btc + - bitcoin::Amount::from_sat(2 * bitcoin::TX_FEE) + ); + + let xnr_balance_after_swap = self.monero_wallet.as_ref().get_balance().await.unwrap(); + assert!(xnr_balance_after_swap <= self.starting_balances.xmr - self.swap_amounts.xmr); } pub async fn new( @@ -294,15 +307,38 @@ impl BobHarness { pub async fn assert_redeemed(&self, state: BobState) { assert!(matches!(state, BobState::XmrRedeemed)); - let btc_bob_final = self.bitcoin_wallet.as_ref().balance().await.unwrap(); + let btc_balance_after_swap = self.bitcoin_wallet.as_ref().balance().await.unwrap(); + assert!(btc_balance_after_swap <= self.starting_balances.btc - self.swap_amounts.btc); + + // Ensure that Bob's balance is refreshed as we use a newly created wallet self.monero_wallet.as_ref().inner.refresh().await.unwrap(); - let xmr_bob_final = self.monero_wallet.as_ref().get_balance().await.unwrap(); - assert!(btc_bob_final <= self.starting_balances.btc - self.swap_amounts.btc); + let xmr_balance_after_swap = self.monero_wallet.as_ref().get_balance().await.unwrap(); assert_eq!( - xmr_bob_final, + xmr_balance_after_swap, self.starting_balances.xmr + self.swap_amounts.xmr ); } + + pub async fn assert_punished(&self, state: BobState, lock_tx_id: ::bitcoin::Txid) { + assert!(matches!(state, BobState::BtcPunished)); + + // lock_tx_bitcoin_fee is determined by the wallet, it is not necessarily equal + // to TX_FEE + let lock_tx_bitcoin_fee = self + .bitcoin_wallet + .transaction_fee(lock_tx_id) + .await + .unwrap(); + + let btc_balance_after_swap = self.bitcoin_wallet.as_ref().balance().await.unwrap(); + assert_eq!( + btc_balance_after_swap, + self.starting_balances.btc - self.swap_amounts.btc - lock_tx_bitcoin_fee + ); + + let xmr_balance_after_swap = self.monero_wallet.as_ref().get_balance().await.unwrap(); + assert_eq!(xmr_balance_after_swap, self.starting_balances.xmr); + } } pub async fn test(testfn: T) From 8ef824077165632c81adb305043ba330324f0c2f Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Mon, 18 Jan 2021 12:50:49 +1100 Subject: [PATCH 14/16] Refactor refund test --- swap/tests/refund_restart_alice.rs | 208 +++++++---------------------- swap/tests/testutils/mod.rs | 46 ++++++- 2 files changed, 94 insertions(+), 160 deletions(-) diff --git a/swap/tests/refund_restart_alice.rs b/swap/tests/refund_restart_alice.rs index 0f9a062f..2a1d338c 100644 --- a/swap/tests/refund_restart_alice.rs +++ b/swap/tests/refund_restart_alice.rs @@ -1,174 +1,66 @@ -use crate::testutils::{init_alice, init_bob}; -use futures::future::try_join; -use get_port::get_port; -use libp2p::Multiaddr; use rand::rngs::OsRng; -use swap::{ - bitcoin, - config::Config, - database::Database, - monero, - protocol::{alice, alice::AliceState, bob, bob::BobState}, - seed::Seed, -}; -use tempfile::tempdir; -use testcontainers::clients::Cli; -use testutils::init_tracing; -use tokio::select; -use uuid::Uuid; +use swap::protocol::{alice, alice::AliceState, bob}; pub mod testutils; -// Bob locks btc and Alice locks xmr. Alice fails to act so Bob refunds. Alice -// then also refunds. +/// Bob locks btc and Alice locks xmr. Alice fails to act so Bob refunds. Alice +/// then also refunds. #[tokio::test] async fn given_alice_restarts_after_xmr_is_locked_abort_swap() { - let _guard = init_tracing(); + testutils::test(|alice_harness, bob_harness| async move { + let alice = alice_harness.new_alice().await; + let bob = bob_harness.new_bob().await; - let cli = Cli::default(); - let ( - monero, - testutils::Containers { - bitcoind, - monerods: _monerods, - }, - ) = testutils::init_containers(&cli).await; + let bob_swap = bob::swap( + bob.state, + bob.event_loop_handle, + bob.db, + bob.bitcoin_wallet.clone(), + bob.monero_wallet.clone(), + OsRng, + bob.swap_id, + ); + let bob_swap_handle = tokio::spawn(bob_swap); - let btc_to_swap = bitcoin::Amount::from_sat(1_000_000); - let xmr_to_swap = monero::Amount::from_piconero(1_000_000_000_000); - - let bob_btc_starting_balance = btc_to_swap * 10; - let bob_xmr_starting_balance = monero::Amount::from_piconero(0); - - let alice_btc_starting_balance = bitcoin::Amount::ZERO; - let alice_xmr_starting_balance = xmr_to_swap * 10; - - let port = get_port().expect("Failed to find a free port"); - let alice_multiaddr: Multiaddr = format!("/ip4/127.0.0.1/tcp/{}", port) - .parse() - .expect("failed to parse Alice's address"); - - let alice_seed = Seed::random().unwrap(); - let ( - alice_state, - mut alice_event_loop_1, - alice_event_loop_handle_1, - alice_btc_wallet, - alice_xmr_wallet, - _, - ) = init_alice( - &bitcoind, - &monero, - btc_to_swap, - xmr_to_swap, - alice_xmr_starting_balance, - alice_multiaddr.clone(), - Config::regtest(), - alice_seed, - ) - .await; - - let (bob_state, bob_event_loop, bob_event_loop_handle, bob_btc_wallet, bob_xmr_wallet, bob_db) = - init_bob( - alice_multiaddr.clone(), - alice_event_loop_1.peer_id(), - &bitcoind, - &monero, - btc_to_swap, - bob_btc_starting_balance, - xmr_to_swap, - Config::regtest(), - ) - .await; - - let bob_fut = bob::swap::swap( - bob_state, - bob_event_loop_handle, - bob_db, - bob_btc_wallet.clone(), - bob_xmr_wallet.clone(), - OsRng, - Uuid::new_v4(), - ); - - let alice_swap_id = Uuid::new_v4(); - let alice_db_datadir = tempdir().unwrap(); - - let alice_xmr_locked_fut = { - let alice_db = Database::open(alice_db_datadir.path()).unwrap(); - alice::swap::run_until( - alice_state, + let alice_state = alice::run_until( + alice.state, alice::swap::is_xmr_locked, - alice_event_loop_handle_1, - alice_btc_wallet.clone(), - alice_xmr_wallet.clone(), - Config::regtest(), - alice_swap_id, - alice_db, - ) - }; - - tokio::spawn(bob_event_loop.run()); - - // We are selecting with alice_event_loop_1 so that we stop polling on it once - // the try_join is finished. - let (bob_state, alice_restart_state) = select! { - res = try_join(bob_fut, alice_xmr_locked_fut) => res.unwrap(), - _ = alice_event_loop_1.run() => panic!("The event loop should never finish") - }; - - let tx_lock_id = if let BobState::BtcRefunded(state4) = bob_state { - state4.tx_lock_id() - } else { - panic!("Bob in unexpected state"); - }; - - let (mut alice_event_loop_2, alice_event_loop_handle_2) = - testutils::init_alice_event_loop(alice_multiaddr, alice_seed); - - let alice_final_state = { - let alice_db = Database::open(alice_db_datadir.path()).unwrap(); - alice::swap::swap( - alice_restart_state, - alice_event_loop_handle_2, - alice_btc_wallet.clone(), - alice_xmr_wallet.clone(), - Config::regtest(), - alice_swap_id, - alice_db, + alice.event_loop_handle, + alice.bitcoin_wallet.clone(), + alice.monero_wallet.clone(), + alice.config, + alice.swap_id, + alice.db, ) .await - .unwrap() - }; - tokio::spawn(async move { alice_event_loop_2.run().await }); + .unwrap(); + assert!(matches!(alice_state, AliceState::XmrLocked {..})); - assert!(matches!(alice_final_state, AliceState::XmrRefunded)); + // Alice does not act, Bob refunds + let bob_state = bob_swap_handle.await.unwrap(); - let btc_alice_final = alice_btc_wallet.as_ref().balance().await.unwrap(); - let btc_bob_final = bob_btc_wallet.as_ref().balance().await.unwrap(); + // Once bob has finished Alice is restarted and refunds as well + let alice = alice_harness.recover_alice_from_db().await; + assert!(matches!(alice.state, AliceState::XmrLocked {..})); - let lock_tx_bitcoin_fee = bob_btc_wallet.transaction_fee(tx_lock_id).await.unwrap(); + let alice_state = alice::swap( + alice.state, + alice.event_loop_handle, + alice.bitcoin_wallet.clone(), + alice.monero_wallet.clone(), + alice.config, + alice.swap_id, + alice.db, + ) + .await + .unwrap(); - assert_eq!(btc_alice_final, alice_btc_starting_balance); - - // Alice or Bob could publish TxCancel. This means Bob could pay tx fees for - // TxCancel and TxRefund or only TxRefund - let btc_bob_final_alice_submitted_cancel = btc_bob_final - == bob_btc_starting_balance - - lock_tx_bitcoin_fee - - bitcoin::Amount::from_sat(bitcoin::TX_FEE); - - let btc_bob_final_bob_submitted_cancel = btc_bob_final - == bob_btc_starting_balance - - lock_tx_bitcoin_fee - - bitcoin::Amount::from_sat(2 * bitcoin::TX_FEE); - assert!(btc_bob_final_alice_submitted_cancel || btc_bob_final_bob_submitted_cancel); - - alice_xmr_wallet.as_ref().inner.refresh().await.unwrap(); - let xmr_alice_final = alice_xmr_wallet.as_ref().get_balance().await.unwrap(); - assert_eq!(xmr_alice_final, xmr_to_swap); - - bob_xmr_wallet.as_ref().inner.refresh().await.unwrap(); - let xmr_bob_final = bob_xmr_wallet.as_ref().get_balance().await.unwrap(); - assert_eq!(xmr_bob_final, bob_xmr_starting_balance); + // TODO: The test passes like this, but the assertion should be done after Bob + // refunded, not at the end because this can cause side-effects! + // We have to properly wait for the refund tx's finality inside the assertion, + // which requires storing the refund_tx_id in the the state! + bob_harness.assert_refunded(bob_state.unwrap()).await; + alice_harness.assert_refunded(alice_state).await; + }) + .await; } diff --git a/swap/tests/testutils/mod.rs b/swap/tests/testutils/mod.rs index e9ffa108..569cce46 100644 --- a/swap/tests/testutils/mod.rs +++ b/swap/tests/testutils/mod.rs @@ -76,6 +76,18 @@ impl AliceHarness { assert!(xmr_balance_after_swap <= self.starting_balances.xmr - self.swap_amounts.xmr); } + pub async fn assert_refunded(&self, state: AliceState) { + assert!(matches!(state, AliceState::XmrRefunded)); + + let btc_balance_after_swap = self.bitcoin_wallet.as_ref().balance().await.unwrap(); + assert_eq!(btc_balance_after_swap, self.starting_balances.btc); + + // Ensure that Alice's balance is refreshed as we use a newly created wallet + self.monero_wallet.as_ref().inner.refresh().await.unwrap(); + let xmr_balance_after_swap = self.monero_wallet.as_ref().get_balance().await.unwrap(); + assert_eq!(xmr_balance_after_swap, self.swap_amounts.xmr); + } + pub async fn assert_punished(&self, state: AliceState) { assert!(matches!(state, AliceState::BtcPunished)); @@ -319,11 +331,41 @@ impl BobHarness { ); } + pub async fn assert_refunded(&self, state: BobState) { + let lock_tx_id = if let BobState::BtcRefunded(state4) = state { + state4.tx_lock_id() + } else { + panic!("Bob in unexpected state"); + }; + let lock_tx_bitcoin_fee = self + .bitcoin_wallet + .transaction_fee(lock_tx_id) + .await + .unwrap(); + + let btc_balance_after_swap = self.bitcoin_wallet.as_ref().balance().await.unwrap(); + + let alice_submitted_cancel = btc_balance_after_swap + == self.starting_balances.btc + - lock_tx_bitcoin_fee + - bitcoin::Amount::from_sat(bitcoin::TX_FEE); + + let bob_submitted_cancel = btc_balance_after_swap + == self.starting_balances.btc + - lock_tx_bitcoin_fee + - bitcoin::Amount::from_sat(2 * bitcoin::TX_FEE); + + // The cancel tx can be submitted by both Alice and Bob. + // Since we cannot be sure who submitted it we have to assert accordingly + assert!(alice_submitted_cancel || bob_submitted_cancel); + + let xmr_balance_after_swap = self.monero_wallet.as_ref().get_balance().await.unwrap(); + assert_eq!(xmr_balance_after_swap, self.starting_balances.xmr); + } + pub async fn assert_punished(&self, state: BobState, lock_tx_id: ::bitcoin::Txid) { assert!(matches!(state, BobState::BtcPunished)); - // lock_tx_bitcoin_fee is determined by the wallet, it is not necessarily equal - // to TX_FEE let lock_tx_bitcoin_fee = self .bitcoin_wallet .transaction_fee(lock_tx_id) From 7832ee94f34e14b16d35fa5082cd389b2bc27b0c Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Mon, 18 Jan 2021 12:57:27 +1100 Subject: [PATCH 15/16] Remove unused code and only expose necessary functionality --- swap/tests/testutils/mod.rs | 121 ++++-------------------------------- 1 file changed, 13 insertions(+), 108 deletions(-) diff --git a/swap/tests/testutils/mod.rs b/swap/tests/testutils/mod.rs index 569cce46..1f72b88c 100644 --- a/swap/tests/testutils/mod.rs +++ b/swap/tests/testutils/mod.rs @@ -23,7 +23,7 @@ use tracing_log::LogTracer; use uuid::Uuid; #[derive(Debug, Clone)] -pub struct StartingBalances { +struct StartingBalances { pub xmr: monero::Amount, pub btc: bitcoin::Amount, } @@ -102,7 +102,7 @@ impl AliceHarness { assert!(xnr_balance_after_swap <= self.starting_balances.xmr - self.swap_amounts.xmr); } - pub async fn new( + async fn new( config: Config, swap_amounts: SwapAmounts, swap_id: Uuid, @@ -227,7 +227,7 @@ pub struct BobHarness { impl BobHarness { #[allow(clippy::too_many_arguments)] - pub async fn new( + async fn new( config: Config, swap_amounts: SwapAmounts, swap_id: Uuid, @@ -435,7 +435,7 @@ where testfn(alice_harness, bob_harness).await } -pub async fn init_containers(cli: &Cli) -> (Monero, Containers<'_>) { +async fn init_containers(cli: &Cli) -> (Monero, Containers<'_>) { let bitcoind = Bitcoind::new(&cli, "0.19.1").unwrap(); let _ = bitcoind.init(5).await; let (monero, monerods) = Monero::new(&cli, None, vec!["alice".to_string(), "bob".to_string()]) @@ -445,7 +445,7 @@ pub async fn init_containers(cli: &Cli) -> (Monero, Containers<'_>) { (monero, Containers { bitcoind, monerods }) } -pub async fn init_wallets( +async fn init_wallets( name: &str, bitcoind: &Bitcoind<'_>, monero: &Monero, @@ -481,7 +481,7 @@ pub async fn init_wallets( (btc_wallet, xmr_wallet) } -pub async fn init_alice_state( +async fn init_alice_state( btc_to_swap: bitcoin::Amount, xmr_to_swap: monero::Amount, alice_btc_wallet: Arc, @@ -514,7 +514,7 @@ pub async fn init_alice_state( AliceState::Started { amounts, state0 } } -pub fn init_alice_event_loop( +fn init_alice_event_loop( listen: Multiaddr, seed: Seed, ) -> ( @@ -526,55 +526,7 @@ pub fn init_alice_event_loop( alice::event_loop::EventLoop::new(alice_transport, alice_behaviour, listen).unwrap() } -#[allow(clippy::too_many_arguments)] -pub async fn init_alice( - bitcoind: &Bitcoind<'_>, - monero: &Monero, - btc_to_swap: bitcoin::Amount, - xmr_to_swap: monero::Amount, - xmr_starting_balance: monero::Amount, - listen: Multiaddr, - config: Config, - seed: Seed, -) -> ( - AliceState, - alice::event_loop::EventLoop, - alice::event_loop::EventLoopHandle, - Arc, - Arc, - Database, -) { - let (alice_btc_wallet, alice_xmr_wallet) = init_wallets( - "alice", - bitcoind, - monero, - StartingBalances { - xmr: xmr_starting_balance, - btc: bitcoin::Amount::ZERO, - }, - config, - ) - .await; - - let alice_start_state = - init_alice_state(btc_to_swap, xmr_to_swap, alice_btc_wallet.clone(), config).await; - - let (event_loop, event_loop_handle) = init_alice_event_loop(listen, seed); - - let alice_db_datadir = tempdir().unwrap(); - let alice_db = Database::open(alice_db_datadir.path()).unwrap(); - - ( - alice_start_state, - event_loop, - event_loop_handle, - alice_btc_wallet, - alice_xmr_wallet, - alice_db, - ) -} - -pub async fn init_bob_state( +async fn init_bob_state( btc_to_swap: bitcoin::Amount, xmr_to_swap: monero::Amount, bob_btc_wallet: Arc, @@ -599,7 +551,7 @@ pub async fn init_bob_state( BobState::Started { state0, amounts } } -pub fn init_bob_event_loop( +fn init_bob_event_loop( alice_peer_id: PeerId, alice_addr: Multiaddr, ) -> (bob::event_loop::EventLoop, bob::event_loop::EventLoopHandle) { @@ -610,58 +562,11 @@ pub fn init_bob_event_loop( .unwrap() } -#[allow(clippy::too_many_arguments)] -pub async fn init_bob( - alice_multiaddr: Multiaddr, - alice_peer_id: PeerId, - bitcoind: &Bitcoind<'_>, - monero: &Monero, - btc_to_swap: bitcoin::Amount, - btc_starting_balance: bitcoin::Amount, - xmr_to_swap: monero::Amount, - config: Config, -) -> ( - BobState, - bob::event_loop::EventLoop, - bob::event_loop::EventLoopHandle, - Arc, - Arc, - Database, -) { - let (bob_btc_wallet, bob_xmr_wallet) = init_wallets( - "bob", - bitcoind, - monero, - StartingBalances { - xmr: monero::Amount::ZERO, - btc: btc_starting_balance, - }, - config, - ) - .await; - - let bob_state = init_bob_state(btc_to_swap, xmr_to_swap, bob_btc_wallet.clone(), config).await; - - let (event_loop, event_loop_handle) = init_bob_event_loop(alice_peer_id, alice_multiaddr); - - let bob_db_dir = tempdir().unwrap(); - let bob_db = Database::open(bob_db_dir.path()).unwrap(); - - ( - bob_state, - event_loop, - event_loop_handle, - bob_btc_wallet, - bob_xmr_wallet, - bob_db, - ) -} - // This is just to keep the containers alive #[allow(dead_code)] -pub struct Containers<'a> { - pub bitcoind: Bitcoind<'a>, - pub monerods: Vec>, +struct Containers<'a> { + bitcoind: Bitcoind<'a>, + monerods: Vec>, } /// Utility function to initialize logging in the test environment. @@ -670,7 +575,7 @@ pub struct Containers<'a> { /// ```rust /// let _guard = init_tracing(); /// ``` -pub fn init_tracing() -> DefaultGuard { +fn init_tracing() -> DefaultGuard { // converts all log records into tracing events // Note: Make sure to initialize without unwrapping, otherwise this causes // trouble when running multiple tests. From 317b25130206da9e13e70546bacb5a840cc5da14 Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Mon, 18 Jan 2021 13:03:56 +1100 Subject: [PATCH 16/16] Re-arrange order of structs/functions in testutils move important things to the top and harmonize structure for alice and bob. --- swap/tests/testutils/mod.rs | 204 ++++++++++++++++++------------------ 1 file changed, 102 insertions(+), 102 deletions(-) diff --git a/swap/tests/testutils/mod.rs b/swap/tests/testutils/mod.rs index 1f72b88c..d179e5bb 100644 --- a/swap/tests/testutils/mod.rs +++ b/swap/tests/testutils/mod.rs @@ -22,10 +22,56 @@ use tracing_core::dispatcher::DefaultGuard; use tracing_log::LogTracer; use uuid::Uuid; -#[derive(Debug, Clone)] -struct StartingBalances { - pub xmr: monero::Amount, - pub btc: bitcoin::Amount, +pub async fn test(testfn: T) +where + T: Fn(AliceHarness, BobHarness) -> F, + F: Future, +{ + let cli = Cli::default(); + + let _guard = init_tracing(); + + let (monero, containers) = testutils::init_containers(&cli).await; + + let swap_amounts = SwapAmounts { + btc: bitcoin::Amount::from_sat(1_000_000), + xmr: monero::Amount::from_piconero(1_000_000_000_000), + }; + + let config = Config::regtest(); + + let alice_starting_balances = StartingBalances { + xmr: swap_amounts.xmr * 10, + btc: bitcoin::Amount::ZERO, + }; + let alice_harness = AliceHarness::new( + config, + swap_amounts, + Uuid::new_v4(), + &monero, + &containers.bitcoind, + alice_starting_balances, + ) + .await; + + let bob_starting_balances = StartingBalances { + xmr: monero::Amount::ZERO, + btc: swap_amounts.btc * 10, + }; + + let bob_harness = BobHarness::new( + config, + swap_amounts, + Uuid::new_v4(), + &monero, + &containers.bitcoind, + bob_starting_balances, + alice_harness.listen_address(), + alice_harness.peer_id(), + ) + .await; + + testfn(alice_harness, bob_harness).await } pub struct Alice { @@ -54,54 +100,6 @@ pub struct AliceHarness { } impl AliceHarness { - pub fn peer_id(&self) -> PeerId { - self.peer_id.clone() - } - - pub fn listen_address(&self) -> Multiaddr { - self.listen_address.clone() - } - - pub async fn assert_redeemed(&self, state: AliceState) { - assert!(matches!(state, AliceState::BtcRedeemed)); - - let btc_balance_after_swap = self.bitcoin_wallet.as_ref().balance().await.unwrap(); - assert_eq!( - btc_balance_after_swap, - self.starting_balances.btc + self.swap_amounts.btc - - bitcoin::Amount::from_sat(bitcoin::TX_FEE) - ); - - let xmr_balance_after_swap = self.monero_wallet.as_ref().get_balance().await.unwrap(); - assert!(xmr_balance_after_swap <= self.starting_balances.xmr - self.swap_amounts.xmr); - } - - pub async fn assert_refunded(&self, state: AliceState) { - assert!(matches!(state, AliceState::XmrRefunded)); - - let btc_balance_after_swap = self.bitcoin_wallet.as_ref().balance().await.unwrap(); - assert_eq!(btc_balance_after_swap, self.starting_balances.btc); - - // Ensure that Alice's balance is refreshed as we use a newly created wallet - self.monero_wallet.as_ref().inner.refresh().await.unwrap(); - let xmr_balance_after_swap = self.monero_wallet.as_ref().get_balance().await.unwrap(); - assert_eq!(xmr_balance_after_swap, self.swap_amounts.xmr); - } - - pub async fn assert_punished(&self, state: AliceState) { - assert!(matches!(state, AliceState::BtcPunished)); - - let btc_balance_after_swap = self.bitcoin_wallet.as_ref().balance().await.unwrap(); - assert_eq!( - btc_balance_after_swap, - self.starting_balances.btc + self.swap_amounts.btc - - bitcoin::Amount::from_sat(2 * bitcoin::TX_FEE) - ); - - let xnr_balance_after_swap = self.monero_wallet.as_ref().get_balance().await.unwrap(); - assert!(xnr_balance_after_swap <= self.starting_balances.xmr - self.swap_amounts.xmr); - } - async fn new( config: Config, swap_amounts: SwapAmounts, @@ -200,6 +198,54 @@ impl AliceHarness { db, } } + + pub async fn assert_redeemed(&self, state: AliceState) { + assert!(matches!(state, AliceState::BtcRedeemed)); + + let btc_balance_after_swap = self.bitcoin_wallet.as_ref().balance().await.unwrap(); + assert_eq!( + btc_balance_after_swap, + self.starting_balances.btc + self.swap_amounts.btc + - bitcoin::Amount::from_sat(bitcoin::TX_FEE) + ); + + let xmr_balance_after_swap = self.monero_wallet.as_ref().get_balance().await.unwrap(); + assert!(xmr_balance_after_swap <= self.starting_balances.xmr - self.swap_amounts.xmr); + } + + pub async fn assert_refunded(&self, state: AliceState) { + assert!(matches!(state, AliceState::XmrRefunded)); + + let btc_balance_after_swap = self.bitcoin_wallet.as_ref().balance().await.unwrap(); + assert_eq!(btc_balance_after_swap, self.starting_balances.btc); + + // Ensure that Alice's balance is refreshed as we use a newly created wallet + self.monero_wallet.as_ref().inner.refresh().await.unwrap(); + let xmr_balance_after_swap = self.monero_wallet.as_ref().get_balance().await.unwrap(); + assert_eq!(xmr_balance_after_swap, self.swap_amounts.xmr); + } + + pub async fn assert_punished(&self, state: AliceState) { + assert!(matches!(state, AliceState::BtcPunished)); + + let btc_balance_after_swap = self.bitcoin_wallet.as_ref().balance().await.unwrap(); + assert_eq!( + btc_balance_after_swap, + self.starting_balances.btc + self.swap_amounts.btc + - bitcoin::Amount::from_sat(2 * bitcoin::TX_FEE) + ); + + let xnr_balance_after_swap = self.monero_wallet.as_ref().get_balance().await.unwrap(); + assert!(xnr_balance_after_swap <= self.starting_balances.xmr - self.swap_amounts.xmr); + } + + pub fn peer_id(&self) -> PeerId { + self.peer_id.clone() + } + + pub fn listen_address(&self) -> Multiaddr { + self.listen_address.clone() + } } pub struct Bob { @@ -383,56 +429,10 @@ impl BobHarness { } } -pub async fn test(testfn: T) -where - T: Fn(AliceHarness, BobHarness) -> F, - F: Future, -{ - let cli = Cli::default(); - - let _guard = init_tracing(); - - let (monero, containers) = testutils::init_containers(&cli).await; - - let swap_amounts = SwapAmounts { - btc: bitcoin::Amount::from_sat(1_000_000), - xmr: monero::Amount::from_piconero(1_000_000_000_000), - }; - - let config = Config::regtest(); - - let alice_starting_balances = StartingBalances { - xmr: swap_amounts.xmr * 10, - btc: bitcoin::Amount::ZERO, - }; - let alice_harness = AliceHarness::new( - config, - swap_amounts, - Uuid::new_v4(), - &monero, - &containers.bitcoind, - alice_starting_balances, - ) - .await; - - let bob_starting_balances = StartingBalances { - xmr: monero::Amount::ZERO, - btc: swap_amounts.btc * 10, - }; - - let bob_harness = BobHarness::new( - config, - swap_amounts, - Uuid::new_v4(), - &monero, - &containers.bitcoind, - bob_starting_balances, - alice_harness.listen_address(), - alice_harness.peer_id(), - ) - .await; - - testfn(alice_harness, bob_harness).await +#[derive(Debug, Clone)] +struct StartingBalances { + pub xmr: monero::Amount, + pub btc: bitcoin::Amount, } async fn init_containers(cli: &Cli) -> (Monero, Containers<'_>) {