Remove test using next_state approach

This commit is contained in:
Franck Royer 2020-12-02 13:32:24 +11:00
parent d4d0bb2737
commit 8e489ab1fe
No known key found for this signature in database
GPG Key ID: A82ED75A8DFC50A4
4 changed files with 2 additions and 410 deletions

View File

@ -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<Message> + ReceiveMessage<bob::Message>,
>(
bitcoin_wallet: &B,
monero_wallet: &M,
transport: &mut T,
state: State,
rng: &mut R,
) -> Result<State> {
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<W>(self, monero_wallet: &W) -> Result<State5>
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<W: bitcoin::BroadcastSignedTransaction>(
&self,
bitcoin_wallet: &W,

View File

@ -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
);
}
}

View File

@ -25,28 +25,6 @@ impl AliceNode {
}
}
pub async fn run_alice_until<R: RngCore + CryptoRng>(
alice: &mut AliceNode,
initial_state: alice::State,
is_state: fn(&alice::State) -> bool,
rng: &mut R,
) -> Result<alice::State> {
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 {

View File

@ -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<EncryptedSignature>;
type BobNetwork = Network<TransferProof>;
#[derive(Debug)]
struct Network<M> {
// 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<M>,
}
#[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()
}
}