From 2541fad40f57301d1d307e25befb22daf21cb675 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Wed, 9 Dec 2020 17:14:51 +1100 Subject: [PATCH 1/3] Report times in CI --- .github/workflows/ci.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e73e99a4..c7118f59 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -77,6 +77,7 @@ jobs: - name: Install Rust toolchain uses: actions-rs/toolchain@v1 with: + toolchain: nightly profile: minimal override: true @@ -94,14 +95,14 @@ jobs: key: rust-${{ matrix.target }}-${{ matrix.rust_toolchain }}-cargo-registry-directory-${{ hashFiles('Cargo.lock') }}-v1 - name: Cargo check release code with default features - run: cargo check --workspace + run: cargo +nightly check --workspace - name: Cargo check all features - run: cargo check --workspace --all-targets --all-features + run: cargo +nightly check --workspace --all-targets --all-features - name: Cargo test if: (!matrix.skip_tests) - run: cargo test --workspace --all-features + run: cargo +nightly test --workspace --all-features -- -Z unstable-options --report-time env: MONERO_ADDITIONAL_SLEEP_PERIOD: 60000 RUST_MIN_STACK: 10000000 From 989d9d08d3468e49d7ab982a281cbd6698aca426 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Thu, 10 Dec 2020 10:10:23 +1100 Subject: [PATCH 2/3] Reduce verbosity of monero block generate trace --- monero-harness/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monero-harness/src/lib.rs b/monero-harness/src/lib.rs index 7d42a586..067b69da 100644 --- a/monero-harness/src/lib.rs +++ b/monero-harness/src/lib.rs @@ -118,8 +118,8 @@ impl<'c> Monero { // generate the first 70 as bulk let monerod = &self.monerod; - let block = monerod.client().generate_blocks(70, &miner_address).await?; - tracing::info!("Generated {:?} blocks", block); + let res = monerod.client().generate_blocks(70, &miner_address).await?; + tracing::info!("Generated {:?} blocks", res.blocks.len()); miner_wallet.refresh().await?; for (wallet, amount) in wallet_amount.iter() { From 43ffccff7867a4101974e0bbb95af5c07cd3878f Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Thu, 10 Dec 2020 10:31:56 +1100 Subject: [PATCH 3/3] Remove duplicate happy and punish tests --- xmr-btc/tests/e2e.rs | 144 +---------- xmr-btc/tests/on_chain.rs | 521 -------------------------------------- 2 files changed, 1 insertion(+), 664 deletions(-) delete mode 100644 xmr-btc/tests/on_chain.rs diff --git a/xmr-btc/tests/e2e.rs b/xmr-btc/tests/e2e.rs index 373c759e..6ca819d0 100644 --- a/xmr-btc/tests/e2e.rs +++ b/xmr-btc/tests/e2e.rs @@ -14,86 +14,11 @@ mod tests { use std::convert::TryInto; use testcontainers::clients::Cli; use xmr_btc::{ - alice, bitcoin, + alice, bitcoin::{Amount, TX_FEE}, bob, }; - #[tokio::test] - async fn happy_path() { - let cli = Cli::default(); - let (monero, _container) = Monero::new(&cli, Some("hp".to_string()), vec![ - "alice".to_string(), - "bob".to_string(), - ]) - .await - .unwrap(); - let bitcoind = init_bitcoind(&cli).await; - - let ( - alice_state0, - bob_state0, - mut alice_node, - mut bob_node, - initial_balances, - swap_amounts, - ) = init_test(&monero, &bitcoind, None, None).await; - - let (alice_state, bob_state) = future::try_join( - run_alice_until( - &mut alice_node, - alice_state0.into(), - harness::alice::is_state6, - &mut OsRng, - ), - run_bob_until( - &mut bob_node, - bob_state0.into(), - harness::bob::is_state5, - &mut OsRng, - ), - ) - .await - .unwrap(); - - let alice_state6: alice::State6 = alice_state.try_into().unwrap(); - let bob_state5: bob::State5 = bob_state.try_into().unwrap(); - - let alice_final_btc_balance = alice_node.bitcoin_wallet.balance().await.unwrap(); - let bob_final_btc_balance = bob_node.bitcoin_wallet.balance().await.unwrap(); - - let lock_tx_bitcoin_fee = bob_node - .bitcoin_wallet - .transaction_fee(bob_state5.tx_lock_id()) - .await - .unwrap(); - - let alice_final_xmr_balance = alice_node.monero_wallet.get_balance().await.unwrap(); - - monero.wallet("bob").unwrap().refresh().await.unwrap(); - - let bob_final_xmr_balance = bob_node.monero_wallet.get_balance().await.unwrap(); - - assert_eq!( - alice_final_btc_balance, - initial_balances.alice_btc + swap_amounts.btc - - bitcoin::Amount::from_sat(bitcoin::TX_FEE) - ); - assert_eq!( - bob_final_btc_balance, - initial_balances.bob_btc - swap_amounts.btc - lock_tx_bitcoin_fee - ); - - assert_eq!( - alice_final_xmr_balance, - initial_balances.alice_xmr - swap_amounts.xmr - alice_state6.lock_xmr_fee() - ); - assert_eq!( - bob_final_xmr_balance, - initial_balances.bob_xmr + swap_amounts.xmr - ); - } - #[tokio::test] async fn both_refund() { let cli = Cli::default(); @@ -170,71 +95,4 @@ mod tests { assert_eq!(alice_final_xmr_balance, swap_amounts.xmr); assert_eq!(bob_final_xmr_balance, initial_balances.bob_xmr); } - - #[tokio::test] - async fn alice_punishes() { - let cli = Cli::default(); - let (monero, _containers) = Monero::new(&cli, Some("ap".to_string()), vec![ - "alice".to_string(), - "bob".to_string(), - ]) - .await - .unwrap(); - - let bitcoind = init_bitcoind(&cli).await; - - let ( - alice_state0, - bob_state0, - mut alice_node, - mut bob_node, - initial_balances, - swap_amounts, - ) = init_test(&monero, &bitcoind, None, None).await; - - let (alice_state, bob_state) = future::try_join( - run_alice_until( - &mut alice_node, - alice_state0.into(), - harness::alice::is_state4, - &mut OsRng, - ), - run_bob_until( - &mut bob_node, - bob_state0.into(), - harness::bob::is_state3, - &mut OsRng, - ), - ) - .await - .unwrap(); - - let alice_state4: alice::State4 = alice_state.try_into().unwrap(); - let bob_state3: bob::State3 = bob_state.try_into().unwrap(); - - alice_state4 - .punish(&alice_node.bitcoin_wallet) - .await - .unwrap(); - - let alice_final_btc_balance = alice_node.bitcoin_wallet.balance().await.unwrap(); - let bob_final_btc_balance = bob_node.bitcoin_wallet.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_node - .bitcoin_wallet - .transaction_fee(bob_state3.tx_lock_id()) - .await - .unwrap(); - - assert_eq!( - alice_final_btc_balance, - initial_balances.alice_btc + swap_amounts.btc - Amount::from_sat(2 * TX_FEE) - ); - assert_eq!( - bob_final_btc_balance, - initial_balances.bob_btc - swap_amounts.btc - lock_tx_bitcoin_fee - ); - } } diff --git a/xmr-btc/tests/on_chain.rs b/xmr-btc/tests/on_chain.rs deleted file mode 100644 index 459b368e..00000000 --- a/xmr-btc/tests/on_chain.rs +++ /dev/null @@ -1,521 +0,0 @@ -pub mod harness; - -use anyhow::Result; -use async_trait::async_trait; -use futures::{ - channel::mpsc::{channel, Receiver, Sender}, - future::{select, try_join}, - pin_mut, SinkExt, StreamExt, -}; -use genawaiter::GeneratorState; -use harness::{ - init_bitcoind, init_test, - node::{run_alice_until, run_bob_until}, -}; -use monero_harness::Monero; -use rand::rngs::OsRng; -use std::{convert::TryInto, sync::Arc}; -use testcontainers::clients::Cli; -use tokio::sync::Mutex; -use tracing::info; -use xmr_btc::{ - alice::{self, ReceiveBitcoinRedeemEncsig}, - bitcoin::{self, BroadcastSignedTransaction, EncryptedSignature, SignTxLock}, - bob::{self, ReceiveTransferProof}, - monero::{CreateWalletForOutput, Transfer, TransferProof}, -}; - -/// Time given to Bob to get the Bitcoin lock transaction included in a block. -const BITCOIN_TX_LOCK_TIMEOUT: u64 = 5; - -type AliceNetwork = Network; -type BobNetwork = Network; - -#[derive(Debug)] -struct Network { - // TODO: It is weird to use mpsc's in a situation where only one message is expected, but the - // ownership rules of Rust are making this painful - pub receiver: Receiver, -} - -impl Network { - pub fn new() -> (Network, Sender) { - let (sender, receiver) = channel(1); - - (Self { receiver }, sender) - } -} - -#[async_trait] -impl ReceiveTransferProof for BobNetwork { - async fn receive_transfer_proof(&mut self) -> TransferProof { - self.receiver.next().await.unwrap() - } -} - -#[async_trait] -impl ReceiveBitcoinRedeemEncsig for AliceNetwork { - async fn receive_bitcoin_redeem_encsig(&mut self) -> EncryptedSignature { - self.receiver.next().await.unwrap() - } -} - -struct AliceBehaviour { - lock_xmr: bool, - redeem_btc: bool, - cancel_btc: bool, - punish_btc: bool, - create_monero_wallet_for_output: bool, -} - -impl Default for AliceBehaviour { - fn default() -> Self { - Self { - lock_xmr: true, - redeem_btc: true, - cancel_btc: true, - punish_btc: true, - create_monero_wallet_for_output: true, - } - } -} - -struct BobBehaviour { - lock_btc: bool, - send_btc_redeem_encsig: bool, - create_monero_wallet_for_output: bool, - cancel_btc: bool, - refund_btc: bool, -} - -impl Default for BobBehaviour { - fn default() -> Self { - Self { - lock_btc: true, - send_btc_redeem_encsig: true, - create_monero_wallet_for_output: true, - cancel_btc: true, - refund_btc: true, - } - } -} - -async fn swap_as_alice( - network: Arc>, - // FIXME: It would be more intuitive to have a single network/transport struct instead of - // splitting into two, but Rust ownership rules make this tedious - mut sender: Sender, - monero_wallet: Arc, - bitcoin_wallet: Arc, - behaviour: AliceBehaviour, - state: alice::State3, -) -> Result<()> { - let mut action_generator = alice::action_generator( - network, - bitcoin_wallet.clone(), - state, - BITCOIN_TX_LOCK_TIMEOUT, - ); - - loop { - let state = action_generator.async_resume().await; - - info!("resumed execution of alice generator, got: {:?}", state); - - match state { - GeneratorState::Yielded(alice::Action::LockXmr { - amount, - public_spend_key, - public_view_key, - }) => { - if behaviour.lock_xmr { - let (transfer_proof, _) = monero_wallet - .transfer(public_spend_key, public_view_key, amount) - .await?; - - sender.send(transfer_proof).await?; - } - } - GeneratorState::Yielded(alice::Action::RedeemBtc(tx)) => { - if behaviour.redeem_btc { - let _ = bitcoin_wallet.broadcast_signed_transaction(tx).await?; - } - } - GeneratorState::Yielded(alice::Action::CancelBtc(tx)) => { - if behaviour.cancel_btc { - let _ = bitcoin_wallet.broadcast_signed_transaction(tx).await?; - } - } - GeneratorState::Yielded(alice::Action::PunishBtc(tx)) => { - if behaviour.punish_btc { - let _ = bitcoin_wallet.broadcast_signed_transaction(tx).await?; - } - } - GeneratorState::Yielded(alice::Action::CreateMoneroWalletForOutput { - spend_key, - view_key, - }) => { - if behaviour.create_monero_wallet_for_output { - monero_wallet - .create_and_load_wallet_for_output(spend_key, view_key) - .await?; - } - } - GeneratorState::Complete(()) => return Ok(()), - } - } -} - -async fn swap_as_bob( - network: Arc>, - mut sender: Sender, - monero_wallet: Arc, - bitcoin_wallet: Arc, - behaviour: BobBehaviour, - state: bob::State2, -) -> Result<()> { - let mut action_generator = bob::action_generator( - network, - monero_wallet.clone(), - bitcoin_wallet.clone(), - state, - BITCOIN_TX_LOCK_TIMEOUT, - ); - - loop { - let state = action_generator.async_resume().await; - - info!("resumed execution of bob generator, got: {:?}", state); - - match state { - GeneratorState::Yielded(bob::Action::LockBtc(tx_lock)) => { - if behaviour.lock_btc { - let signed_tx_lock = bitcoin_wallet.sign_tx_lock(tx_lock).await?; - let _ = bitcoin_wallet - .broadcast_signed_transaction(signed_tx_lock) - .await?; - } - } - GeneratorState::Yielded(bob::Action::SendBtcRedeemEncsig(tx_redeem_encsig)) => { - if behaviour.send_btc_redeem_encsig { - sender.send(tx_redeem_encsig).await.unwrap(); - } - } - GeneratorState::Yielded(bob::Action::CreateXmrWalletForOutput { - spend_key, - view_key, - }) => { - if behaviour.create_monero_wallet_for_output { - monero_wallet - .create_and_load_wallet_for_output(spend_key, view_key) - .await?; - } - } - GeneratorState::Yielded(bob::Action::CancelBtc(tx_cancel)) => { - if behaviour.cancel_btc { - let _ = bitcoin_wallet - .broadcast_signed_transaction(tx_cancel) - .await?; - } - } - GeneratorState::Yielded(bob::Action::RefundBtc(tx_refund)) => { - if behaviour.refund_btc { - let _ = bitcoin_wallet - .broadcast_signed_transaction(tx_refund) - .await?; - } - } - GeneratorState::Complete(()) => return Ok(()), - } - } -} - -#[tokio::test] -async fn on_chain_happy_path() { - let cli = Cli::default(); - let (monero, _container) = Monero::new(&cli, Some("ochp".to_string()), vec![ - "alice".to_string(), - "bob".to_string(), - ]) - .await - .unwrap(); - let bitcoind = init_bitcoind(&cli).await; - - let (alice_state0, bob_state0, mut alice_node, mut bob_node, initial_balances, swap_amounts) = - init_test(&monero, &bitcoind, Some(100), Some(100)).await; - - // run the handshake as part of the setup - let (alice_state, bob_state) = try_join( - run_alice_until( - &mut alice_node, - alice_state0.into(), - harness::alice::is_state3, - &mut OsRng, - ), - run_bob_until( - &mut bob_node, - bob_state0.into(), - harness::bob::is_state2, - &mut OsRng, - ), - ) - .await - .unwrap(); - let alice: alice::State3 = alice_state.try_into().unwrap(); - let bob: bob::State2 = bob_state.try_into().unwrap(); - let tx_lock_txid = bob.tx_lock.txid(); - - let alice_bitcoin_wallet = Arc::new(alice_node.bitcoin_wallet); - let bob_bitcoin_wallet = Arc::new(bob_node.bitcoin_wallet); - let alice_monero_wallet = Arc::new(alice_node.monero_wallet); - let bob_monero_wallet = Arc::new(bob_node.monero_wallet); - - let (alice_network, bob_sender) = Network::::new(); - let (bob_network, alice_sender) = Network::::new(); - - try_join( - swap_as_alice( - Arc::new(Mutex::new(alice_network)), - alice_sender, - alice_monero_wallet.clone(), - alice_bitcoin_wallet.clone(), - AliceBehaviour::default(), - alice, - ), - swap_as_bob( - Arc::new(Mutex::new(bob_network)), - bob_sender, - bob_monero_wallet.clone(), - bob_bitcoin_wallet.clone(), - BobBehaviour::default(), - bob, - ), - ) - .await - .unwrap(); - - let alice_final_btc_balance = alice_bitcoin_wallet.balance().await.unwrap(); - let bob_final_btc_balance = bob_bitcoin_wallet.balance().await.unwrap(); - - let lock_tx_bitcoin_fee = bob_bitcoin_wallet - .transaction_fee(tx_lock_txid) - .await - .unwrap(); - - let alice_final_xmr_balance = alice_monero_wallet.get_balance().await.unwrap(); - - monero.wallet("bob").unwrap().refresh().await.unwrap(); - let bob_final_xmr_balance = bob_monero_wallet.get_balance().await.unwrap(); - - assert_eq!( - alice_final_btc_balance, - initial_balances.alice_btc + swap_amounts.btc - bitcoin::Amount::from_sat(bitcoin::TX_FEE) - ); - assert_eq!( - bob_final_btc_balance, - initial_balances.bob_btc - swap_amounts.btc - lock_tx_bitcoin_fee - ); - - // Getting the Monero LockTx fee is tricky in a clean way, I think checking this - // condition is sufficient - assert!(alice_final_xmr_balance <= initial_balances.alice_xmr - swap_amounts.xmr); - assert_eq!( - bob_final_xmr_balance, - initial_balances.bob_xmr + swap_amounts.xmr - ); -} - -#[tokio::test] -async fn on_chain_both_refund_if_alice_never_redeems() { - let cli = Cli::default(); - let (monero, _container) = Monero::new(&cli, Some("ocbr".to_string()), vec![ - "alice".to_string(), - "bob".to_string(), - ]) - .await - .unwrap(); - let bitcoind = init_bitcoind(&cli).await; - - let (alice_state0, bob_state0, mut alice_node, mut bob_node, initial_balances, swap_amounts) = - init_test(&monero, &bitcoind, Some(10), Some(10)).await; - - // run the handshake as part of the setup - let (alice_state, bob_state) = try_join( - run_alice_until( - &mut alice_node, - alice_state0.into(), - harness::alice::is_state3, - &mut OsRng, - ), - run_bob_until( - &mut bob_node, - bob_state0.into(), - harness::bob::is_state2, - &mut OsRng, - ), - ) - .await - .unwrap(); - let alice: alice::State3 = alice_state.try_into().unwrap(); - let bob: bob::State2 = bob_state.try_into().unwrap(); - let tx_lock_txid = bob.tx_lock.txid(); - - let alice_bitcoin_wallet = Arc::new(alice_node.bitcoin_wallet); - let bob_bitcoin_wallet = Arc::new(bob_node.bitcoin_wallet); - let alice_monero_wallet = Arc::new(alice_node.monero_wallet); - let bob_monero_wallet = Arc::new(bob_node.monero_wallet); - - let (alice_network, bob_sender) = Network::::new(); - let (bob_network, alice_sender) = Network::::new(); - - try_join( - swap_as_alice( - Arc::new(Mutex::new(alice_network)), - alice_sender, - alice_monero_wallet.clone(), - alice_bitcoin_wallet.clone(), - AliceBehaviour { - redeem_btc: false, - ..Default::default() - }, - alice, - ), - swap_as_bob( - Arc::new(Mutex::new(bob_network)), - bob_sender, - bob_monero_wallet.clone(), - bob_bitcoin_wallet.clone(), - BobBehaviour::default(), - bob, - ), - ) - .await - .unwrap(); - - let alice_final_btc_balance = alice_bitcoin_wallet.balance().await.unwrap(); - let bob_final_btc_balance = bob_bitcoin_wallet.balance().await.unwrap(); - - let lock_tx_bitcoin_fee = bob_bitcoin_wallet - .transaction_fee(tx_lock_txid) - .await - .unwrap(); - - monero.wallet("alice").unwrap().refresh().await.unwrap(); - let alice_final_xmr_balance = alice_monero_wallet.get_balance().await.unwrap(); - - let bob_final_xmr_balance = bob_monero_wallet.get_balance().await.unwrap(); - - assert_eq!(alice_final_btc_balance, initial_balances.alice_btc); - assert_eq!( - bob_final_btc_balance, - // The 2 * TX_FEE corresponds to tx_refund and tx_cancel. - initial_balances.bob_btc - - bitcoin::Amount::from_sat(2 * bitcoin::TX_FEE) - - lock_tx_bitcoin_fee - ); - - // Because we create a new wallet when claiming Monero, we can only assert on - // this new wallet owning all of `xmr_amount` after refund - assert_eq!(alice_final_xmr_balance, swap_amounts.xmr); - assert_eq!(bob_final_xmr_balance, initial_balances.bob_xmr); -} - -#[tokio::test] -async fn on_chain_alice_punishes_if_bob_never_acts_after_fund() { - let cli = Cli::default(); - let (monero, _container) = Monero::new(&cli, Some("ocap".to_string()), vec![ - "alice".to_string(), - "bob".to_string(), - ]) - .await - .unwrap(); - let bitcoind = init_bitcoind(&cli).await; - - let (alice_state0, bob_state0, mut alice_node, mut bob_node, initial_balances, swap_amounts) = - init_test(&monero, &bitcoind, Some(10), Some(10)).await; - - // run the handshake as part of the setup - let (alice_state, bob_state) = try_join( - run_alice_until( - &mut alice_node, - alice_state0.into(), - harness::alice::is_state3, - &mut OsRng, - ), - run_bob_until( - &mut bob_node, - bob_state0.into(), - harness::bob::is_state2, - &mut OsRng, - ), - ) - .await - .unwrap(); - let alice: alice::State3 = alice_state.try_into().unwrap(); - let bob: bob::State2 = bob_state.try_into().unwrap(); - let tx_lock_txid = bob.tx_lock.txid(); - - let alice_bitcoin_wallet = Arc::new(alice_node.bitcoin_wallet); - let bob_bitcoin_wallet = Arc::new(bob_node.bitcoin_wallet); - let alice_monero_wallet = Arc::new(alice_node.monero_wallet); - let bob_monero_wallet = Arc::new(bob_node.monero_wallet); - - let (alice_network, bob_sender) = Network::::new(); - let (bob_network, alice_sender) = Network::::new(); - - let alice_swap = swap_as_alice( - Arc::new(Mutex::new(alice_network)), - alice_sender, - alice_monero_wallet.clone(), - alice_bitcoin_wallet.clone(), - AliceBehaviour::default(), - alice, - ); - let bob_swap = swap_as_bob( - Arc::new(Mutex::new(bob_network)), - bob_sender, - bob_monero_wallet.clone(), - bob_bitcoin_wallet.clone(), - BobBehaviour { - send_btc_redeem_encsig: false, - create_monero_wallet_for_output: false, - cancel_btc: false, - refund_btc: false, - ..Default::default() - }, - bob, - ); - - pin_mut!(alice_swap); - pin_mut!(bob_swap); - - // since we model Bob as inactive after locking bitcoin, his future does not - // resolve, so we wait for one of the two (Alice's) to resolve via select - select(alice_swap, bob_swap).await; - - let alice_final_btc_balance = alice_bitcoin_wallet.balance().await.unwrap(); - let bob_final_btc_balance = bob_bitcoin_wallet.balance().await.unwrap(); - - let lock_tx_bitcoin_fee = bob_bitcoin_wallet - .transaction_fee(tx_lock_txid) - .await - .unwrap(); - - let alice_final_xmr_balance = alice_monero_wallet.get_balance().await.unwrap(); - let bob_final_xmr_balance = bob_monero_wallet.get_balance().await.unwrap(); - - assert_eq!( - alice_final_btc_balance, - initial_balances.alice_btc + swap_amounts.btc - - bitcoin::Amount::from_sat(2 * bitcoin::TX_FEE) - ); - assert_eq!( - bob_final_btc_balance, - initial_balances.bob_btc - swap_amounts.btc - lock_tx_bitcoin_fee - ); - - // Getting the Monero LockTx fee is tricky in a clean way, I think checking this - // condition is sufficient - assert!(alice_final_xmr_balance <= initial_balances.alice_xmr - swap_amounts.xmr,); - assert_eq!(bob_final_xmr_balance, initial_balances.bob_xmr); -}