385 lines
13 KiB
Rust
Raw Normal View History

#![warn(
unused_extern_crates,
missing_debug_implementations,
missing_copy_implementations,
rust_2018_idioms,
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::fallible_impl_from,
clippy::cast_precision_loss,
clippy::cast_possible_wrap,
clippy::dbg_macro
)]
#![cfg_attr(not(test), warn(clippy::unwrap_used))]
#![forbid(unsafe_code)]
#![allow(non_snake_case)]
pub mod alice;
pub mod bitcoin;
pub mod bob;
pub mod monero;
#[cfg(test)]
mod tests {
use crate::{
alice, bitcoin,
bitcoin::{Amount, TX_FEE},
bob, monero,
};
use bitcoin_harness::Bitcoind;
use monero_harness::Monero;
use rand::rngs::OsRng;
use testcontainers::clients::Cli;
const TEN_XMR: u64 = 10_000_000_000_000;
pub async fn init_bitcoind(tc_client: &Cli) -> Bitcoind<'_> {
let bitcoind = Bitcoind::new(tc_client, "0.19.1").expect("failed to create bitcoind");
let _ = bitcoind.init(5).await;
bitcoind
}
#[tokio::test]
async fn happy_path() {
let cli = Cli::default();
let monero = Monero::new(&cli);
let bitcoind = init_bitcoind(&cli).await;
// must be bigger than our hardcoded fee of 10_000
let btc_amount = bitcoin::Amount::from_sat(10_000_000);
let xmr_amount = monero::Amount::from_piconero(1_000_000_000_000);
let fund_alice = TEN_XMR;
let fund_bob = 0;
monero.init(fund_alice, fund_bob).await.unwrap();
let alice_monero_wallet = monero::AliceWallet(&monero);
let bob_monero_wallet = monero::BobWallet(&monero);
let alice_btc_wallet = bitcoin::Wallet::new("alice", &bitcoind.node_url)
.await
.unwrap();
let bob_btc_wallet = bitcoin::make_wallet("bob", &bitcoind, btc_amount)
.await
.unwrap();
let alice_initial_btc_balance = alice_btc_wallet.balance().await.unwrap();
let bob_initial_btc_balance = bob_btc_wallet.balance().await.unwrap();
let alice_initial_xmr_balance = alice_monero_wallet.0.get_balance_alice().await.unwrap();
let bob_initial_xmr_balance = bob_monero_wallet.0.get_balance_bob().await.unwrap();
let redeem_address = alice_btc_wallet.new_address().await.unwrap();
let punish_address = redeem_address.clone();
let refund_address = bob_btc_wallet.new_address().await.unwrap();
let refund_timelock = 1;
let punish_timelock = 1;
let alice_state0 = alice::State0::new(
&mut OsRng,
btc_amount,
xmr_amount,
refund_timelock,
punish_timelock,
redeem_address,
punish_address,
);
let bob_state0 = bob::State0::new(
&mut OsRng,
btc_amount,
xmr_amount,
refund_timelock,
punish_timelock,
refund_address.clone(),
);
let alice_message0 = alice_state0.next_message(&mut OsRng);
let bob_message0 = bob_state0.next_message(&mut OsRng);
let alice_state1 = alice_state0.receive(bob_message0).unwrap();
let bob_state1 = bob_state0
.receive(&bob_btc_wallet, alice_message0)
.await
.unwrap();
let bob_message1 = bob_state1.next_message();
let alice_state2 = alice_state1.receive(bob_message1);
let alice_message1 = alice_state2.next_message();
let bob_state2 = bob_state1.receive(alice_message1).unwrap();
let bob_message2 = bob_state2.next_message();
let alice_state3 = alice_state2.receive(bob_message2).unwrap();
let bob_state2b = bob_state2.lock_btc(&bob_btc_wallet).await.unwrap();
let lock_txid = bob_state2b.tx_lock_id();
let alice_state4 = alice_state3
.watch_for_lock_btc(&alice_btc_wallet)
.await
.unwrap();
let (alice_state4b, lock_tx_monero_fee) =
alice_state4.lock_xmr(&alice_monero_wallet).await.unwrap();
let alice_message2 = alice_state4b.next_message();
let bob_state3 = bob_state2b
.watch_for_lock_xmr(&bob_monero_wallet, alice_message2)
.await
.unwrap();
let bob_message3 = bob_state3.next_message();
let alice_state5 = alice_state4b.receive(bob_message3);
alice_state5.redeem_btc(&alice_btc_wallet).await.unwrap();
let bob_state4 = bob_state3
.watch_for_redeem_btc(&bob_btc_wallet)
.await
.unwrap();
bob_state4.claim_xmr(&bob_monero_wallet).await.unwrap();
let alice_final_btc_balance = alice_btc_wallet.balance().await.unwrap();
let bob_final_btc_balance = bob_btc_wallet.balance().await.unwrap();
let lock_tx_bitcoin_fee = bob_btc_wallet.transaction_fee(lock_txid).await.unwrap();
assert_eq!(
alice_final_btc_balance,
alice_initial_btc_balance + btc_amount - bitcoin::Amount::from_sat(bitcoin::TX_FEE)
);
assert_eq!(
bob_final_btc_balance,
bob_initial_btc_balance - btc_amount - lock_tx_bitcoin_fee
);
let alice_final_xmr_balance = alice_monero_wallet.0.get_balance_alice().await.unwrap();
bob_monero_wallet
.0
.wait_for_bob_wallet_block_height()
.await
.unwrap();
let bob_final_xmr_balance = bob_monero_wallet.0.get_balance_bob().await.unwrap();
assert_eq!(
alice_final_xmr_balance,
alice_initial_xmr_balance - u64::from(xmr_amount) - u64::from(lock_tx_monero_fee)
);
assert_eq!(
bob_final_xmr_balance,
bob_initial_xmr_balance + u64::from(xmr_amount)
);
}
#[tokio::test]
async fn both_refund() {
let cli = Cli::default();
let monero = Monero::new(&cli);
let bitcoind = init_bitcoind(&cli).await;
// must be bigger than our hardcoded fee of 10_000
let btc_amount = bitcoin::Amount::from_sat(10_000_000);
let xmr_amount = monero::Amount::from_piconero(1_000_000_000_000);
let alice_btc_wallet = bitcoin::Wallet::new("alice", &bitcoind.node_url)
.await
.unwrap();
let bob_btc_wallet = bitcoin::make_wallet("bob", &bitcoind, btc_amount)
.await
.unwrap();
let fund_alice = TEN_XMR;
let fund_bob = 0;
monero.init(fund_alice, fund_bob).await.unwrap();
let alice_monero_wallet = monero::AliceWallet(&monero);
let bob_monero_wallet = monero::BobWallet(&monero);
let alice_initial_btc_balance = alice_btc_wallet.balance().await.unwrap();
let bob_initial_btc_balance = bob_btc_wallet.balance().await.unwrap();
let bob_initial_xmr_balance = bob_monero_wallet.0.get_balance_bob().await.unwrap();
let redeem_address = alice_btc_wallet.new_address().await.unwrap();
let punish_address = redeem_address.clone();
let refund_address = bob_btc_wallet.new_address().await.unwrap();
let refund_timelock = 1;
let punish_timelock = 1;
let alice_state0 = alice::State0::new(
&mut OsRng,
btc_amount,
xmr_amount,
refund_timelock,
punish_timelock,
redeem_address,
punish_address,
);
let bob_state0 = bob::State0::new(
&mut OsRng,
btc_amount,
xmr_amount,
refund_timelock,
punish_timelock,
refund_address.clone(),
);
let alice_message0 = alice_state0.next_message(&mut OsRng);
let bob_message0 = bob_state0.next_message(&mut OsRng);
let alice_state1 = alice_state0.receive(bob_message0).unwrap();
let bob_state1 = bob_state0
.receive(&bob_btc_wallet, alice_message0)
.await
.unwrap();
let bob_message1 = bob_state1.next_message();
let alice_state2 = alice_state1.receive(bob_message1);
let alice_message1 = alice_state2.next_message();
let bob_state2 = bob_state1.receive(alice_message1).unwrap();
let bob_message2 = bob_state2.next_message();
let alice_state3 = alice_state2.receive(bob_message2).unwrap();
let bob_state2b = bob_state2.lock_btc(&bob_btc_wallet).await.unwrap();
let alice_state4 = alice_state3
.watch_for_lock_btc(&alice_btc_wallet)
.await
.unwrap();
let (alice_state4b, _lock_tx_monero_fee) =
alice_state4.lock_xmr(&alice_monero_wallet).await.unwrap();
bob_state2b.refund_btc(&bob_btc_wallet).await.unwrap();
alice_state4b
.refund_xmr(&alice_btc_wallet, &alice_monero_wallet)
.await
.unwrap();
let alice_final_btc_balance = alice_btc_wallet.balance().await.unwrap();
let bob_final_btc_balance = bob_btc_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_btc_wallet
.transaction_fee(bob_state2b.tx_lock_id())
.await
.unwrap();
assert_eq!(alice_final_btc_balance, alice_initial_btc_balance);
assert_eq!(
bob_final_btc_balance,
// The 2 * TX_FEE corresponds to tx_refund and tx_cancel.
bob_initial_btc_balance - Amount::from_sat(2 * TX_FEE) - lock_tx_bitcoin_fee
);
alice_monero_wallet
.0
.wait_for_alice_wallet_block_height()
.await
.unwrap();
let alice_final_xmr_balance = alice_monero_wallet.0.get_balance_alice().await.unwrap();
let bob_final_xmr_balance = bob_monero_wallet.0.get_balance_bob().await.unwrap();
// 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, u64::from(xmr_amount));
assert_eq!(bob_final_xmr_balance, bob_initial_xmr_balance);
}
#[tokio::test]
async fn alice_punishes() {
let cli = Cli::default();
let bitcoind = init_bitcoind(&cli).await;
// must be bigger than our hardcoded fee of 10_000
let btc_amount = bitcoin::Amount::from_sat(10_000_000);
let xmr_amount = monero::Amount::from_piconero(1_000_000_000_000);
let alice_btc_wallet = bitcoin::Wallet::new("alice", &bitcoind.node_url)
.await
.unwrap();
let bob_btc_wallet = bitcoin::make_wallet("bob", &bitcoind, btc_amount)
.await
.unwrap();
let alice_initial_btc_balance = alice_btc_wallet.balance().await.unwrap();
let bob_initial_btc_balance = bob_btc_wallet.balance().await.unwrap();
let redeem_address = alice_btc_wallet.new_address().await.unwrap();
let punish_address = redeem_address.clone();
let refund_address = bob_btc_wallet.new_address().await.unwrap();
let refund_timelock = 1;
let punish_timelock = 1;
let alice_state0 = alice::State0::new(
&mut OsRng,
btc_amount,
xmr_amount,
refund_timelock,
punish_timelock,
redeem_address,
punish_address,
);
let bob_state0 = bob::State0::new(
&mut OsRng,
btc_amount,
xmr_amount,
refund_timelock,
punish_timelock,
refund_address.clone(),
);
let alice_message0 = alice_state0.next_message(&mut OsRng);
let bob_message0 = bob_state0.next_message(&mut OsRng);
let alice_state1 = alice_state0.receive(bob_message0).unwrap();
let bob_state1 = bob_state0
.receive(&bob_btc_wallet, alice_message0)
.await
.unwrap();
let bob_message1 = bob_state1.next_message();
let alice_state2 = alice_state1.receive(bob_message1);
let alice_message1 = alice_state2.next_message();
let bob_state2 = bob_state1.receive(alice_message1).unwrap();
let bob_message2 = bob_state2.next_message();
let alice_state3 = alice_state2.receive(bob_message2).unwrap();
let bob_state2b = bob_state2.lock_btc(&bob_btc_wallet).await.unwrap();
let alice_state4 = alice_state3
.watch_for_lock_btc(&alice_btc_wallet)
.await
.unwrap();
alice_state4.punish(&alice_btc_wallet).await.unwrap();
let alice_final_btc_balance = alice_btc_wallet.balance().await.unwrap();
let bob_final_btc_balance = bob_btc_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_btc_wallet
.transaction_fee(bob_state2b.tx_lock_id())
.await
.unwrap();
assert_eq!(
alice_final_btc_balance,
alice_initial_btc_balance + btc_amount - Amount::from_sat(2 * TX_FEE)
);
assert_eq!(
bob_final_btc_balance,
bob_initial_btc_balance - btc_amount - lock_tx_bitcoin_fee
);
}
}