Bob refunds swap after restart that requires communication

As Bob is dialing Alice, we now ensure that we are connected to Alice
at each step that needs communication.
If we are not connected, we proceed with dialing.

In an attempt to improve libp2p usage, we also add known address of
Alice first and only use peer_id to dial.
This ensures that we use the expected peer id.
This commit is contained in:
Franck Royer 2020-12-18 17:39:04 +11:00
parent d9ea7ab605
commit 1a4bd0e2b4
No known key found for this signature in database
GPG key ID: A82ED75A8DFC50A4
14 changed files with 347 additions and 72 deletions

View file

@ -63,7 +63,8 @@ async fn happy_path() {
let (bob_state, bob_event_loop, bob_event_loop_handle, bob_btc_wallet, bob_xmr_wallet, bob_db) =
init_bob(
alice_multiaddr,
alice_multiaddr.clone(),
alice_event_loop.peer_id(),
&bitcoind,
&monero,
btc_to_swap,
@ -83,6 +84,8 @@ async fn happy_path() {
alice_db,
);
let alice_peer_id = alice_event_loop.peer_id();
let _alice_swarm_fut = tokio::spawn(async move { alice_event_loop.run().await });
let bob_swap_fut = bob::swap::swap(
@ -93,6 +96,8 @@ async fn happy_path() {
bob_xmr_wallet.clone(),
OsRng,
Uuid::new_v4(),
alice_peer_id,
alice_multiaddr,
);
let _bob_swarm_fut = tokio::spawn(async move { bob_event_loop.run().await });

View file

@ -56,9 +56,12 @@ async fn given_alice_restarts_after_encsig_is_learned_resume_swap() {
)
.await;
let alice_peer_id = alice_event_loop.peer_id();
let (bob_state, bob_event_loop, bob_event_loop_handle, bob_btc_wallet, bob_xmr_wallet, bob_db) =
init_bob(
alice_multiaddr.clone(),
alice_peer_id.clone(),
&bitcoind,
&monero,
btc_to_swap,
@ -73,18 +76,17 @@ async fn given_alice_restarts_after_encsig_is_learned_resume_swap() {
let bob_btc_wallet_clone = bob_btc_wallet.clone();
let bob_xmr_wallet_clone = bob_xmr_wallet.clone();
let _ = tokio::spawn(async move {
bob::swap::swap(
bob_state,
bob_event_loop_handle,
bob_db,
bob_btc_wallet.clone(),
bob_xmr_wallet.clone(),
OsRng,
Uuid::new_v4(),
)
.await
});
let _ = tokio::spawn(bob::swap::swap(
bob_state,
bob_event_loop_handle,
bob_db,
bob_btc_wallet.clone(),
bob_xmr_wallet.clone(),
OsRng,
Uuid::new_v4(),
alice_peer_id,
alice_multiaddr.clone(),
));
let _bob_swarm_fut = tokio::spawn(async move { bob_event_loop.run().await });

View file

@ -3,10 +3,11 @@ use get_port::get_port;
use libp2p::Multiaddr;
use rand::rngs::OsRng;
use std::convert::TryFrom;
use swap::{alice, bitcoin, bob, bob::swap::BobState, storage::Database};
use swap::{alice, alice::swap::AliceState, bitcoin, bob, bob::swap::BobState, storage::Database};
use tempfile::tempdir;
use testcontainers::clients::Cli;
use testutils::init_tracing;
use tokio::select;
use uuid::Uuid;
use xmr_btc::config::Config;
@ -56,9 +57,11 @@ async fn given_bob_restarts_after_encsig_is_sent_resume_swap() {
)
.await;
let alice_peer_id = alice_event_loop.peer_id();
let (bob_state, bob_event_loop, bob_event_loop_handle, bob_btc_wallet, bob_xmr_wallet, _) =
init_bob(
alice_multiaddr.clone(),
alice_peer_id.clone(),
&bitcoind,
&monero,
btc_to_swap,
@ -136,6 +139,8 @@ async fn given_bob_restarts_after_encsig_is_sent_resume_swap() {
bob_xmr_wallet,
OsRng,
bob_swap_id,
alice_peer_id,
alice_multiaddr,
)
.await
.unwrap();
@ -158,3 +163,149 @@ async fn given_bob_restarts_after_encsig_is_sent_resume_swap() {
assert!(xmr_alice_final <= alice_xmr_starting_balance - xmr_to_swap);
assert_eq!(xmr_bob_final, xmr_to_swap);
}
#[tokio::test]
async fn given_bob_restarts_after_xmr_is_locked_resume_swap() {
let _guard = init_tracing();
let cli = Cli::default();
let (
monero,
testutils::Containers {
bitcoind,
monerods: _monerods,
},
) = testutils::init_containers(&cli).await;
let btc_to_swap = bitcoin::Amount::from_sat(1_000_000);
let xmr_to_swap = xmr_btc::monero::Amount::from_piconero(1_000_000_000_000);
let bob_btc_starting_balance = btc_to_swap * 10;
let bob_xmr_starting_balance = xmr_btc::monero::Amount::from_piconero(0);
let alice_btc_starting_balance = bitcoin::Amount::ZERO;
let alice_xmr_starting_balance = xmr_to_swap * 10;
let port = get_port().expect("Failed to find a free port");
let alice_multiaddr: Multiaddr = format!("/ip4/127.0.0.1/tcp/{}", port)
.parse()
.expect("failed to parse Alice's address");
let (
alice_state,
mut alice_event_loop,
alice_event_loop_handle,
alice_btc_wallet,
alice_xmr_wallet,
alice_db,
) = init_alice(
&bitcoind,
&monero,
btc_to_swap,
xmr_to_swap,
alice_xmr_starting_balance,
alice_multiaddr.clone(),
Config::regtest(),
)
.await;
let alice_peer_id = alice_event_loop.peer_id();
let (bob_state, bob_event_loop_1, bob_event_loop_handle_1, bob_btc_wallet, bob_xmr_wallet, _) =
init_bob(
alice_multiaddr.clone(),
alice_peer_id.clone(),
&bitcoind,
&monero,
btc_to_swap,
bob_btc_starting_balance,
xmr_to_swap,
Config::regtest(),
)
.await;
let alice_fut = alice::swap::swap(
alice_state,
alice_event_loop_handle,
alice_btc_wallet.clone(),
alice_xmr_wallet.clone(),
Config::regtest(),
Uuid::new_v4(),
alice_db,
);
let bob_swap_id = Uuid::new_v4();
let bob_db_datadir = tempdir().unwrap();
let bob_xmr_locked_fut = {
let bob_db = Database::open(bob_db_datadir.path()).unwrap();
bob::swap::run_until(
bob_state,
bob::swap::is_xmr_locked,
bob_event_loop_handle_1,
bob_db,
bob_btc_wallet.clone(),
bob_xmr_wallet.clone(),
OsRng,
bob_swap_id,
)
};
tokio::spawn(async move { alice_event_loop.run().await });
let alice_fut_handle = tokio::spawn(alice_fut);
// We are selecting with bob_event_loop_1 so that we stop polling on it once
// bob reaches `xmr locked` state.
let bob_restart_state = select! {
res = bob_xmr_locked_fut => res.unwrap(),
_ = bob_event_loop_1.run() => panic!("The event loop should never finish")
};
// let tx_lock_id = if let AliceState::BtcRefunded { state3, .. } = alice_state
// { state3.tx_lock.txid()
// } else {
// panic!(format!("Alice in unexpected state: {}", alice_state));
// };
let (bob_event_loop_2, bob_event_loop_handle_2) = testutils::init_bob_event_loop();
let bob_fut = bob::swap::swap(
bob_restart_state,
bob_event_loop_handle_2,
Database::open(bob_db_datadir.path()).unwrap(),
bob_btc_wallet.clone(),
bob_xmr_wallet.clone(),
OsRng,
bob_swap_id,
alice_peer_id,
alice_multiaddr,
);
let bob_final_state = select! {
bob_final_state = bob_fut => bob_final_state.unwrap(),
_ = bob_event_loop_2.run() => panic!("Event loop is not expected to stop")
};
assert!(matches!(bob_final_state, BobState::XmrRedeemed));
// Wait for Alice to finish too.
let alice_final_state = alice_fut_handle.await.unwrap().unwrap();
assert!(matches!(alice_final_state, AliceState::BtcRedeemed));
let btc_alice_final = alice_btc_wallet.as_ref().balance().await.unwrap();
let btc_bob_final = bob_btc_wallet.as_ref().balance().await.unwrap();
let xmr_alice_final = alice_xmr_wallet.as_ref().get_balance().await.unwrap();
bob_xmr_wallet.as_ref().0.refresh().await.unwrap();
let xmr_bob_final = bob_xmr_wallet.as_ref().get_balance().await.unwrap();
assert_eq!(
btc_alice_final,
alice_btc_starting_balance + btc_to_swap - bitcoin::Amount::from_sat(bitcoin::TX_FEE)
);
assert!(btc_bob_final <= bob_btc_starting_balance - btc_to_swap);
assert!(xmr_alice_final <= alice_xmr_starting_balance - xmr_to_swap);
assert_eq!(xmr_bob_final, bob_xmr_starting_balance + xmr_to_swap);
}

View file

@ -62,6 +62,7 @@ async fn alice_punishes_if_bob_never_acts_after_fund() {
let (bob_state, bob_event_loop, bob_event_loop_handle, bob_btc_wallet, bob_xmr_wallet, bob_db) =
init_bob(
alice_multiaddr,
alice_event_loop.peer_id(),
&bitcoind,
&monero,
btc_to_swap,

View file

@ -63,6 +63,7 @@ async fn given_alice_restarts_after_xmr_is_locked_abort_swap() {
let (bob_state, bob_event_loop, bob_event_loop_handle, bob_btc_wallet, bob_xmr_wallet, bob_db) =
init_bob(
alice_multiaddr.clone(),
alice_event_loop_1.peer_id(),
&bitcoind,
&monero,
btc_to_swap,
@ -80,6 +81,8 @@ async fn given_alice_restarts_after_xmr_is_locked_abort_swap() {
bob_xmr_wallet.clone(),
OsRng,
Uuid::new_v4(),
alice_event_loop_1.peer_id(),
alice_multiaddr.clone(),
);
let alice_swap_id = Uuid::new_v4();

View file

@ -1,5 +1,5 @@
use bitcoin_harness::Bitcoind;
use libp2p::core::Multiaddr;
use libp2p::{core::Multiaddr, PeerId};
use monero_harness::{image, Monero};
use rand::rngs::OsRng;
use std::sync::Arc;
@ -155,7 +155,7 @@ pub async fn init_alice(
}
pub async fn init_bob_state(
alice_multiaddr: Multiaddr,
alice_peer_id: PeerId,
btc_to_swap: bitcoin::Amount,
xmr_to_swap: xmr_btc::monero::Amount,
bob_btc_wallet: Arc<bitcoin::Wallet>,
@ -179,7 +179,7 @@ pub async fn init_bob_state(
BobState::Started {
state0,
amounts,
addr: alice_multiaddr,
alice_peer_id,
}
}
@ -192,6 +192,7 @@ pub fn init_bob_event_loop() -> (bob::event_loop::EventLoop, bob::event_loop::Ev
#[allow(clippy::too_many_arguments)]
pub async fn init_bob(
alice_multiaddr: Multiaddr,
alice_peer_id: PeerId,
bitcoind: &Bitcoind<'_>,
monero: &Monero,
btc_to_swap: bitcoin::Amount,
@ -217,7 +218,7 @@ pub async fn init_bob(
.await;
let bob_state = init_bob_state(
alice_multiaddr,
alice_peer_id.clone(),
btc_to_swap,
xmr_to_swap,
bob_btc_wallet.clone(),
@ -225,7 +226,12 @@ pub async fn init_bob(
)
.await;
let (event_loop, event_loop_handle) = init_bob_event_loop();
let (event_loop, mut event_loop_handle) = init_bob_event_loop();
event_loop_handle
.add_address(alice_peer_id, alice_multiaddr)
.await
.unwrap();
let bob_db_dir = tempdir().unwrap();
let bob_db = Database::open(bob_db_dir.path()).unwrap();