From 093cbaa4b43eae088ce965936b3f2cb9fabc898f Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Thu, 17 Dec 2020 15:17:15 +1100 Subject: [PATCH 1/3] Introduce T1Expired state for Bob properly and check expiries in states after both locked --- swap/src/bob/swap.rs | 90 +++++++++++++++++++++++++++++--------------- swap/src/state.rs | 8 ++-- 2 files changed, 65 insertions(+), 33 deletions(-) diff --git a/swap/src/bob/swap.rs b/swap/src/bob/swap.rs index 3a231fbe..c4844f5d 100644 --- a/swap/src/bob/swap.rs +++ b/swap/src/bob/swap.rs @@ -33,6 +33,7 @@ pub enum BobState { XmrLocked(bob::State4, PeerId), EncSigSent(bob::State4, PeerId), BtcRedeemed(bob::State5), + T1Expired(bob::State4), Cancelled(bob::State4), BtcRefunded(bob::State4), XmrRedeemed, @@ -49,6 +50,7 @@ impl fmt::Display for BobState { BobState::XmrLocked(..) => write!(f, "xmr_locked"), BobState::EncSigSent(..) => write!(f, "encsig_sent"), BobState::BtcRedeemed(..) => write!(f, "btc_redeemed"), + BobState::T1Expired(..) => write!(f, "t1_expired"), BobState::Cancelled(..) => write!(f, "cancelled"), BobState::BtcRefunded(..) => write!(f, "btc_refunded"), BobState::XmrRedeemed => write!(f, "xmr_redeemed"), @@ -70,6 +72,7 @@ impl From for state::Bob { BobState::XmrLocked(state4, peer_id) => Bob::XmrLocked { state4, peer_id }, BobState::EncSigSent(state4, peer_id) => Bob::EncSigSent { state4, peer_id }, BobState::BtcRedeemed(state5) => Bob::BtcRedeemed(state5), + BobState::T1Expired(state4) => Bob::T1Expired(state4), BobState::Cancelled(state4) => Bob::BtcCancelled(state4), BobState::BtcRefunded(_) | BobState::XmrRedeemed @@ -90,6 +93,7 @@ impl TryFrom for BobState { Bob::XmrLocked { state4, peer_id } => BobState::XmrLocked(state4, peer_id), Bob::EncSigSent { state4, peer_id } => BobState::EncSigSent(state4, peer_id), Bob::BtcRedeemed(state5) => BobState::BtcRedeemed(state5), + Bob::T1Expired(state4) => BobState::T1Expired(state4), Bob::BtcCancelled(state4) => BobState::Cancelled(state4), Bob::SwapComplete => BobState::SafelyAborted, }; @@ -272,18 +276,27 @@ where .await } BobState::XmrLocked(state, alice_peer_id) => { - // Alice has locked Xmr - // Bob sends Alice his key - let tx_redeem_encsig = state.tx_redeem_encsig(); - // Do we have to wait for a response? - // What if Alice fails to receive this? Should we always resend? - // todo: If we cannot dial Alice we should go to EncSigSent. Maybe dialing - // should happen in this arm? - event_loop_handle - .send_message3(alice_peer_id.clone(), tx_redeem_encsig) - .await?; + let state = if let Epoch::T0 = state.current_epoch(bitcoin_wallet.as_ref()).await? { + // Alice has locked Xmr + // Bob sends Alice his key + let tx_redeem_encsig = state.tx_redeem_encsig(); - let state = BobState::EncSigSent(state, alice_peer_id); + let state4_clone = state.clone(); + let enc_sig_sent_watcher = + event_loop_handle.send_message3(alice_peer_id.clone(), tx_redeem_encsig); + let bitcoin_wallet = bitcoin_wallet.clone(); + let t1_timeout = state4_clone.wait_for_t1(bitcoin_wallet.as_ref()); + + pin_mut!(enc_sig_sent_watcher); + pin_mut!(t1_timeout); + + match select(enc_sig_sent_watcher, t1_timeout).await { + Either::Left((..)) => BobState::EncSigSent(state, alice_peer_id), + Either::Right((..)) => BobState::T1Expired(state), + } + } else { + BobState::T1Expired(state) + }; let db_state = state.clone().into(); db.insert_latest_state(swap_id, state::Swap::Bob(db_state)) .await?; @@ -300,28 +313,20 @@ where .await } BobState::EncSigSent(state, ..) => { - let state_clone = state.clone(); - let redeem_watcher = state_clone.watch_for_redeem_btc(bitcoin_wallet.as_ref()); - let t1_timeout = state_clone.wait_for_t1(bitcoin_wallet.as_ref()); + let state = if let Epoch::T0 = state.current_epoch(bitcoin_wallet.as_ref()).await? { + let state_clone = state.clone(); + let redeem_watcher = state_clone.watch_for_redeem_btc(bitcoin_wallet.as_ref()); + let t1_timeout = state_clone.wait_for_t1(bitcoin_wallet.as_ref()); - pin_mut!(redeem_watcher); - pin_mut!(t1_timeout); + pin_mut!(redeem_watcher); + pin_mut!(t1_timeout); - let state = match select(redeem_watcher, t1_timeout).await { - Either::Left((val, _)) => BobState::BtcRedeemed(val?), - Either::Right((..)) => { - // Check whether TxCancel has been published. - // We should not fail if the transaction is already on the blockchain - if state - .check_for_tx_cancel(bitcoin_wallet.as_ref()) - .await - .is_err() - { - state.submit_tx_cancel(bitcoin_wallet.as_ref()).await?; - } - - BobState::Cancelled(state) + match select(redeem_watcher, t1_timeout).await { + Either::Left((val, _)) => BobState::BtcRedeemed(val?), + Either::Right((..)) => BobState::T1Expired(state), } + } else { + BobState::T1Expired(state) }; let db_state = state.clone().into(); @@ -359,6 +364,31 @@ where ) .await } + BobState::T1Expired(state4) => { + if state4 + .check_for_tx_cancel(bitcoin_wallet.as_ref()) + .await + .is_err() + { + state4.submit_tx_cancel(bitcoin_wallet.as_ref()).await?; + } + + let state = BobState::Cancelled(state4); + db.insert_latest_state(swap_id, state::Swap::Bob(state.clone().into())) + .await?; + + run_until( + state, + is_target_state, + event_loop_handle, + db, + bitcoin_wallet, + monero_wallet, + rng, + swap_id, + ) + .await + } BobState::Cancelled(state) => { // TODO // Bob has cancelled the swap diff --git a/swap/src/state.rs b/swap/src/state.rs index 1ffeac6a..a9dd9877 100644 --- a/swap/src/state.rs +++ b/swap/src/state.rs @@ -60,6 +60,7 @@ pub enum Bob { peer_id: PeerId, }, BtcRedeemed(bob::State5), + T1Expired(bob::State4), BtcCancelled(bob::State4), SwapComplete, } @@ -106,9 +107,10 @@ impl Display for Bob { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Bob::Negotiated { .. } => f.write_str("Handshake complete"), - Bob::BtcLocked { .. } | Bob::XmrLocked { .. } | Bob::BtcCancelled(_) => { - f.write_str("Bitcoin refundable") - } + Bob::BtcLocked { .. } + | Bob::XmrLocked { .. } + | Bob::T1Expired(_) + | Bob::BtcCancelled(_) => f.write_str("Bitcoin refundable"), Bob::BtcRedeemed(_) => f.write_str("Monero redeemable"), Bob::SwapComplete => f.write_str("Swap complete"), Bob::EncSigSent { .. } => f.write_str("Encrypted signature sent"), From e6493784bfc86a7c7b2482c3e945d9d2c2950b57 Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Fri, 18 Dec 2020 11:37:02 +1100 Subject: [PATCH 2/3] Change `pin_mut!` to `select!` --- swap/src/bob/swap.rs | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/swap/src/bob/swap.rs b/swap/src/bob/swap.rs index c4844f5d..40020df0 100644 --- a/swap/src/bob/swap.rs +++ b/swap/src/bob/swap.rs @@ -7,13 +7,10 @@ use crate::{ }; use anyhow::{bail, Result}; use async_recursion::async_recursion; -use futures::{ - future::{select, Either}, - pin_mut, -}; use libp2p::{core::Multiaddr, PeerId}; use rand::{CryptoRng, RngCore}; use std::{convert::TryFrom, fmt, sync::Arc}; +use tokio::select; use tracing::info; use uuid::Uuid; use xmr_btc::{ @@ -287,12 +284,13 @@ where let bitcoin_wallet = bitcoin_wallet.clone(); let t1_timeout = state4_clone.wait_for_t1(bitcoin_wallet.as_ref()); - pin_mut!(enc_sig_sent_watcher); - pin_mut!(t1_timeout); - - match select(enc_sig_sent_watcher, t1_timeout).await { - Either::Left((..)) => BobState::EncSigSent(state, alice_peer_id), - Either::Right((..)) => BobState::T1Expired(state), + select! { + _ = enc_sig_sent_watcher => { + BobState::EncSigSent(state, alice_peer_id) + }, + _ = t1_timeout => { + BobState::T1Expired(state) + } } } else { BobState::T1Expired(state) @@ -318,12 +316,13 @@ where let redeem_watcher = state_clone.watch_for_redeem_btc(bitcoin_wallet.as_ref()); let t1_timeout = state_clone.wait_for_t1(bitcoin_wallet.as_ref()); - pin_mut!(redeem_watcher); - pin_mut!(t1_timeout); - - match select(redeem_watcher, t1_timeout).await { - Either::Left((val, _)) => BobState::BtcRedeemed(val?), - Either::Right((..)) => BobState::T1Expired(state), + select! { + state5 = redeem_watcher => { + BobState::BtcRedeemed(state5?) + }, + _ = t1_timeout => { + BobState::T1Expired(state) + } } } else { BobState::T1Expired(state) From 20893c1bb170a557c8736ff69cb1115f207ead27 Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Fri, 18 Dec 2020 11:39:21 +1100 Subject: [PATCH 3/3] Cleanup Display for database states --- swap/src/state.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/swap/src/state.rs b/swap/src/state.rs index a9dd9877..8fb04539 100644 --- a/swap/src/state.rs +++ b/swap/src/state.rs @@ -93,7 +93,7 @@ impl Display for Alice { Alice::BtcLocked(_) => f.write_str("Bitcoin locked"), Alice::XmrLocked(_) => f.write_str("Monero locked"), Alice::BtcRedeemable { .. } => f.write_str("Bitcoin redeemable"), - Alice::T1Expired(_) => f.write_str("Submitting TxCancel"), + Alice::T1Expired(_) => f.write_str("Timelock T1 expired"), Alice::BtcCancelled(_) => f.write_str("Bitcoin cancel transaction published"), Alice::BtcPunishable(_) => f.write_str("Bitcoin punishable"), Alice::BtcRefunded { .. } => f.write_str("Monero refundable"), @@ -107,10 +107,10 @@ impl Display for Bob { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Bob::Negotiated { .. } => f.write_str("Handshake complete"), - Bob::BtcLocked { .. } - | Bob::XmrLocked { .. } - | Bob::T1Expired(_) - | Bob::BtcCancelled(_) => f.write_str("Bitcoin refundable"), + Bob::BtcLocked { .. } => f.write_str("Bitcoin locked"), + Bob::XmrLocked { .. } => f.write_str("Monero locked"), + Bob::T1Expired(_) => f.write_str("Timelock T1 expired"), + Bob::BtcCancelled(_) => f.write_str("Bitcoin refundable"), Bob::BtcRedeemed(_) => f.write_str("Monero redeemable"), Bob::SwapComplete => f.write_str("Swap complete"), Bob::EncSigSent { .. } => f.write_str("Encrypted signature sent"),