mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-04-22 00:39:12 -04:00
Remove test using next_state
approach
This commit is contained in:
parent
d4d0bb2737
commit
8e489ab1fe
@ -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,
|
||||
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user