From 83ce6f2c85aec80367d414080c78df7616f4a3b4 Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Mon, 21 Dec 2020 17:33:18 +1100 Subject: [PATCH] Ensure that Bob can cancel correctly if T1 expired and Alice did not move Bob has to check for the possibility to cancel in every state after he locked the BTC. Otherwise Bob will try to perform actions that don't have any point. --- swap/src/alice/swap.rs | 4 ++++ swap/src/bob/swap.rs | 25 +++++++++++--------- xmr-btc/src/alice.rs | 19 ++++++--------- xmr-btc/src/bob.rs | 53 ++++++++++++++++++++++++++++++++---------- xmr-btc/src/lib.rs | 22 ++++++++++++++++++ 5 files changed, 88 insertions(+), 35 deletions(-) diff --git a/swap/src/alice/swap.rs b/swap/src/alice/swap.rs index 6690e5c7..4f0b9cf6 100644 --- a/swap/src/alice/swap.rs +++ b/swap/src/alice/swap.rs @@ -429,6 +429,10 @@ pub async fn run_until( state3, encrypted_signature, } => { + // TODO: Evaluate if it is correct for Alice to Redeem no matter what. + // If T1 expired she should potentially not try redeem. (The implementation + // gives her an advantage.) + let signed_tx_redeem = match build_bitcoin_redeem_transaction( encrypted_signature, &state3.tx_lock, diff --git a/swap/src/bob/swap.rs b/swap/src/bob/swap.rs index 19a0bbd0..596603df 100644 --- a/swap/src/bob/swap.rs +++ b/swap/src/bob/swap.rs @@ -223,16 +223,21 @@ where // Bob has locked Btc // Watch for Alice to Lock Xmr or for t1 to elapse BobState::BtcLocked(state3) => { - // TODO(Franck): Refund if cannot connect to Alice. - event_loop_handle.dial().await?; + let state = if let Epoch::T0 = state3.current_epoch(bitcoin_wallet.as_ref()).await? + { + event_loop_handle.dial().await?; - // todo: watch until t1, not indefinitely - let msg2 = event_loop_handle.recv_message2().await?; - let state4 = state3 - .watch_for_lock_xmr(monero_wallet.as_ref(), msg2) - .await?; + // todo: watch until t1, not indefinitely + let msg2 = event_loop_handle.recv_message2().await?; + let state4 = state3 + .watch_for_lock_xmr(monero_wallet.as_ref(), msg2) + .await?; - let state = BobState::XmrLocked(state4); + BobState::XmrLocked(state4) + } else { + let state4 = state3.t1_expired(); + BobState::T1Expired(state4) + }; let db_state = state.clone().into(); db.insert_latest_state(swap_id, state::Swap::Bob(db_state)) .await?; @@ -249,10 +254,8 @@ where .await } BobState::XmrLocked(state) => { - // TODO(Franck): Refund if cannot connect to Alice. - event_loop_handle.dial().await?; - let state = if let Epoch::T0 = state.current_epoch(bitcoin_wallet.as_ref()).await? { + event_loop_handle.dial().await?; // Alice has locked Xmr // Bob sends Alice his key let tx_redeem_encsig = state.tx_redeem_encsig(); diff --git a/xmr-btc/src/alice.rs b/xmr-btc/src/alice.rs index 74557f5d..0598b40c 100644 --- a/xmr-btc/src/alice.rs +++ b/xmr-btc/src/alice.rs @@ -697,18 +697,13 @@ impl State3 { where W: WatchForRawTransaction + TransactionBlockHeight + BlockHeight, { - let current_block_height = bitcoin_wallet.block_height().await; - let t0 = bitcoin_wallet - .transaction_block_height(self.tx_lock.txid()) - .await; - let t1 = t0 + self.refund_timelock; - let t2 = t1 + self.punish_timelock; - - match (current_block_height < t1, current_block_height < t2) { - (true, _) => Ok(Epoch::T0), - (false, true) => Ok(Epoch::T1), - (false, false) => Ok(Epoch::T2), - } + crate::current_epoch( + bitcoin_wallet, + self.refund_timelock, + self.punish_timelock, + self.tx_lock.txid(), + ) + .await } } diff --git a/xmr-btc/src/bob.rs b/xmr-btc/src/bob.rs index 61c072ab..e962d483 100644 --- a/xmr-btc/src/bob.rs +++ b/xmr-btc/src/bob.rs @@ -621,9 +621,43 @@ impl State3 { }) } + pub fn t1_expired(&self) -> State4 { + State4 { + A: self.A, + b: self.b.clone(), + s_b: self.s_b, + S_a_monero: self.S_a_monero, + S_a_bitcoin: self.S_a_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.clone(), + redeem_address: self.redeem_address.clone(), + punish_address: self.punish_address.clone(), + tx_lock: self.tx_lock.clone(), + tx_cancel_sig_a: self.tx_cancel_sig_a.clone(), + tx_refund_encsig: self.tx_refund_encsig.clone(), + } + } + pub fn tx_lock_id(&self) -> bitcoin::Txid { self.tx_lock.txid() } + + pub async fn current_epoch(&self, bitcoin_wallet: &W) -> Result + where + W: WatchForRawTransaction + TransactionBlockHeight + BlockHeight, + { + crate::current_epoch( + bitcoin_wallet, + self.refund_timelock, + self.punish_timelock, + self.tx_lock.txid(), + ) + .await + } } #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] @@ -761,18 +795,13 @@ impl State4 { where W: WatchForRawTransaction + TransactionBlockHeight + BlockHeight, { - let current_block_height = bitcoin_wallet.block_height().await; - let t0 = bitcoin_wallet - .transaction_block_height(self.tx_lock.txid()) - .await; - let t1 = t0 + self.refund_timelock; - let t2 = t1 + self.punish_timelock; - - match (current_block_height < t1, current_block_height < t2) { - (true, _) => Ok(Epoch::T0), - (false, true) => Ok(Epoch::T1), - (false, false) => Ok(Epoch::T2), - } + crate::current_epoch( + bitcoin_wallet, + self.refund_timelock, + self.punish_timelock, + self.tx_lock.txid(), + ) + .await } pub async fn refund_btc( diff --git a/xmr-btc/src/lib.rs b/xmr-btc/src/lib.rs index 0c9e8728..0c024455 100644 --- a/xmr-btc/src/lib.rs +++ b/xmr-btc/src/lib.rs @@ -60,4 +60,26 @@ pub mod monero; pub mod serde; pub mod transport; +use crate::bitcoin::{BlockHeight, TransactionBlockHeight, WatchForRawTransaction}; pub use cross_curve_dleq; + +pub async fn current_epoch( + bitcoin_wallet: &W, + refund_timelock: u32, + punish_timelock: u32, + lock_tx_id: ::bitcoin::Txid, +) -> anyhow::Result +where + W: WatchForRawTransaction + TransactionBlockHeight + BlockHeight, +{ + let current_block_height = bitcoin_wallet.block_height().await; + let t0 = bitcoin_wallet.transaction_block_height(lock_tx_id).await; + let t1 = t0 + refund_timelock; + let t2 = t1 + punish_timelock; + + match (current_block_height < t1, current_block_height < t2) { + (true, _) => Ok(Epoch::T0), + (false, true) => Ok(Epoch::T1), + (false, false) => Ok(Epoch::T2), + } +}