diff --git a/xmr-btc/src/alice.rs b/xmr-btc/src/alice.rs index d8dbaafe..c33882fb 100644 --- a/xmr-btc/src/alice.rs +++ b/xmr-btc/src/alice.rs @@ -1,10 +1,4 @@ -use crate::{ - bitcoin, - bitcoin::{BroadcastSignedTransaction, WatchForRawTransaction}, - bob, monero, - monero::{CreateWalletForOutput, Transfer}, - transport::{ReceiveMessage, SendMessage}, -}; +use crate::{bitcoin, bitcoin::WatchForRawTransaction, bob, monero, monero::CreateWalletForOutput}; use anyhow::{anyhow, Result}; use async_trait::async_trait; use ecdsa_fun::{ @@ -14,7 +8,7 @@ use ecdsa_fun::{ use rand::{CryptoRng, RngCore}; use serde::{Deserialize, Serialize}; use sha2::Sha256; -use std::convert::{TryFrom, TryInto}; +use std::convert::TryFrom; use tracing::info; pub mod message; @@ -44,68 +38,6 @@ pub trait ReceiveBitcoinRedeemEncsig { async fn receive_bitcoin_redeem_encsig(&mut self) -> bitcoin::EncryptedSignature; } -// There are no guarantees that send_message and receive_massage do not block -// the flow of execution. Therefore they must be paired between Alice/Bob, one -// send to one receive in the correct order. -pub async fn next_state< - R: RngCore + CryptoRng, - B: WatchForRawTransaction + BroadcastSignedTransaction, - M: CreateWalletForOutput + Transfer, - T: SendMessage + ReceiveMessage, ->( - bitcoin_wallet: &B, - monero_wallet: &M, - transport: &mut T, - state: State, - rng: &mut R, -) -> Result { - match state { - State::State0(state0) => { - let alice_message0 = state0.next_message(rng).into(); - - let bob_message0 = transport.receive_message().await?.try_into()?; - transport.send_message(alice_message0).await?; - - let state1 = state0.receive(bob_message0)?; - Ok(state1.into()) - } - State::State1(state1) => { - let bob_message1 = transport.receive_message().await?.try_into()?; - let state2 = state1.receive(bob_message1); - let alice_message1 = state2.next_message(); - transport.send_message(alice_message1.into()).await?; - Ok(state2.into()) - } - State::State2(state2) => { - let bob_message2 = transport.receive_message().await?.try_into()?; - let state3 = state2.receive(bob_message2)?; - Ok(state3.into()) - } - State::State3(state3) => { - tracing::info!("alice is watching for locked btc"); - let state4 = state3.watch_for_lock_btc(bitcoin_wallet).await?; - Ok(state4.into()) - } - State::State4(state4) => { - let state5 = state4.lock_xmr(monero_wallet).await?; - tracing::info!("alice has locked xmr"); - Ok(state5.into()) - } - State::State5(state5) => { - transport.send_message(state5.next_message().into()).await?; - // todo: pass in state4b as a parameter somewhere in this call to prevent the - // user from waiting for a message that wont be sent - let message3 = transport.receive_message().await?.try_into()?; - let state6 = state5.receive(message3); - tracing::info!("alice has received bob message 3"); - tracing::info!("alice is redeeming btc"); - state6.redeem_btc(bitcoin_wallet).await?; - Ok(state6.into()) - } - State::State6(state6) => Ok(state6.into()), - } -} - #[allow(clippy::large_enum_variant)] #[derive(Debug, Deserialize, Serialize)] pub enum State { @@ -349,9 +281,6 @@ impl State2 { S_b_monero: self.S_b_monero, S_b_bitcoin: self.S_b_bitcoin, v: self.v, - // TODO(Franck): Review if these amounts are actually needed - btc: self.btc, - xmr: self.xmr, refund_timelock: self.refund_timelock, punish_timelock: self.punish_timelock, refund_address: self.refund_address, @@ -372,9 +301,6 @@ pub struct State3 { pub S_b_monero: monero::PublicKey, pub S_b_bitcoin: bitcoin::PublicKey, pub v: monero::PrivateViewKey, - #[serde(with = "::bitcoin::util::amount::serde::as_sat")] - pub btc: bitcoin::Amount, - pub xmr: monero::Amount, pub refund_timelock: u32, pub punish_timelock: u32, pub refund_address: bitcoin::Address, @@ -404,8 +330,6 @@ impl State3 { S_b_monero: self.S_b_monero, S_b_bitcoin: self.S_b_bitcoin, v: self.v, - btc: self.btc, - xmr: self.xmr, refund_timelock: self.refund_timelock, punish_timelock: self.punish_timelock, refund_address: self.refund_address, @@ -426,9 +350,6 @@ pub struct State4 { S_b_monero: monero::PublicKey, S_b_bitcoin: bitcoin::PublicKey, v: monero::PrivateViewKey, - #[serde(with = "::bitcoin::util::amount::serde::as_sat")] - btc: bitcoin::Amount, - xmr: monero::Amount, refund_timelock: u32, punish_timelock: u32, refund_address: bitcoin::Address, @@ -440,41 +361,6 @@ pub struct State4 { } impl State4 { - pub async fn lock_xmr(self, monero_wallet: &W) -> Result - where - W: monero::Transfer, - { - let S_a = monero::PublicKey::from_private_key(&monero::PrivateKey { - scalar: self.s_a.into_ed25519(), - }); - let S_b = self.S_b_monero; - - let (tx_lock_proof, fee) = monero_wallet - .transfer(S_a + S_b, self.v.public(), self.xmr) - .await?; - - Ok(State5 { - a: self.a, - B: self.B, - s_a: self.s_a, - S_b_monero: self.S_b_monero, - S_b_bitcoin: self.S_b_bitcoin, - v: self.v, - btc: self.btc, - xmr: self.xmr, - refund_timelock: self.refund_timelock, - punish_timelock: self.punish_timelock, - refund_address: self.refund_address, - redeem_address: self.redeem_address, - punish_address: self.punish_address, - tx_lock: self.tx_lock, - tx_lock_proof, - tx_punish_sig_bob: self.tx_punish_sig_bob, - tx_cancel_sig_bob: self.tx_cancel_sig_bob, - lock_xmr_fee: fee, - }) - } - pub async fn punish( &self, bitcoin_wallet: &W, diff --git a/xmr-btc/tests/e2e.rs b/xmr-btc/tests/e2e.rs deleted file mode 100644 index 373c759e..00000000 --- a/xmr-btc/tests/e2e.rs +++ /dev/null @@ -1,240 +0,0 @@ -pub mod harness; - -mod tests { - use crate::{ - harness, - harness::{ - init_bitcoind, init_test, - node::{run_alice_until, run_bob_until}, - }, - }; - use futures::future; - use monero_harness::Monero; - use rand::rngs::OsRng; - use std::convert::TryInto; - use testcontainers::clients::Cli; - use xmr_btc::{ - alice, bitcoin, - 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(); - let (monero, _container) = Monero::new(&cli, Some("br".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_state5, - &mut OsRng, - ), - run_bob_until( - &mut bob_node, - bob_state0.into(), - harness::bob::is_state3, - &mut OsRng, - ), - ) - .await - .unwrap(); - - let alice_state5: alice::State5 = alice_state.try_into().unwrap(); - let bob_state3: bob::State3 = bob_state.try_into().unwrap(); - - bob_state3 - .refund_btc(&bob_node.bitcoin_wallet) - .await - .unwrap(); - alice_state5 - .refund_xmr(&alice_node.bitcoin_wallet, &alice_node.monero_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(); - - monero.wallet("alice").unwrap().refresh().await.unwrap(); - let alice_final_xmr_balance = alice_node.monero_wallet.get_balance().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); - assert_eq!( - bob_final_btc_balance, - // The 2 * TX_FEE corresponds to tx_refund and tx_cancel. - initial_balances.bob_btc - Amount::from_sat(2 * 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 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/harness/node.rs b/xmr-btc/tests/harness/node.rs index 7b4bf207..93d5f16f 100644 --- a/xmr-btc/tests/harness/node.rs +++ b/xmr-btc/tests/harness/node.rs @@ -25,28 +25,6 @@ impl AliceNode { } } -pub async fn run_alice_until( - alice: &mut AliceNode, - initial_state: alice::State, - is_state: fn(&alice::State) -> bool, - rng: &mut R, -) -> Result { - let mut result = initial_state; - loop { - result = alice::next_state( - &alice.bitcoin_wallet, - &alice.monero_wallet, - &mut alice.transport, - result, - rng, - ) - .await?; - if is_state(&result) { - return Ok(result); - } - } -} - // TODO: merge this with alice node // This struct is responsible for I/O pub struct BobNode { diff --git a/xmr-btc/tests/on_chain.rs b/xmr-btc/tests/on_chain.rs deleted file mode 100644 index db454545..00000000 --- a/xmr-btc/tests/on_chain.rs +++ /dev/null @@ -1,32 +0,0 @@ -use async_trait::async_trait; -use futures::{channel::mpsc::Receiver, StreamExt}; -use xmr_btc::{ - alice::ReceiveBitcoinRedeemEncsig, bitcoin::EncryptedSignature, bob::ReceiveTransferProof, - monero::TransferProof, -}; - -pub mod harness; - -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, -} - -#[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() - } -}