diff --git a/swap/src/bob/swap.rs b/swap/src/bob/swap.rs index 3a231fbe..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::{ @@ -33,6 +30,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 +47,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 +69,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 +90,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 +273,28 @@ 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()); + + select! { + _ = enc_sig_sent_watcher => { + BobState::EncSigSent(state, alice_peer_id) + }, + _ = t1_timeout => { + 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 +311,21 @@ 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); - - 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?; + select! { + state5 = redeem_watcher => { + BobState::BtcRedeemed(state5?) + }, + _ = t1_timeout => { + BobState::T1Expired(state) } - - BobState::Cancelled(state) } + } else { + BobState::T1Expired(state) }; let db_state = state.clone().into(); @@ -359,6 +363,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..8fb04539 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, } @@ -92,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"), @@ -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 { .. } => 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"),