From 1f40ee739d1d4bb0457817ac5a924ac2e4edddbe Mon Sep 17 00:00:00 2001 From: pokkst Date: Sat, 12 Aug 2023 22:20:54 -0500 Subject: [PATCH] fix (Bob): Check if Bitcoin redeem transaction was published before transitioning to CancelTimelockExpired --- swap/src/protocol/bob/state.rs | 24 ++++++++++++++++++++++++ swap/src/protocol/bob/swap.rs | 14 ++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/swap/src/protocol/bob/state.rs b/swap/src/protocol/bob/state.rs index aa0045ea..4e042a20 100644 --- a/swap/src/protocol/bob/state.rs +++ b/swap/src/protocol/bob/state.rs @@ -489,6 +489,30 @@ pub struct State4 { } impl State4 { + pub async fn check_for_tx_redeem( + &self, + bitcoin_wallet: &bitcoin::Wallet, + ) -> Result { + let tx_redeem = + bitcoin::TxRedeem::new(&self.tx_lock, &self.redeem_address, self.tx_redeem_fee); + let tx_redeem_encsig = self.b.encsign(self.S_a_bitcoin, tx_redeem.digest()); + + let tx_redeem_candidate = bitcoin_wallet.get_raw_transaction(tx_redeem.txid()).await?; + + let tx_redeem_sig = + tx_redeem.extract_signature_by_key(tx_redeem_candidate, self.b.public())?; + let s_a = bitcoin::recover(self.S_a_bitcoin, tx_redeem_sig, tx_redeem_encsig)?; + let s_a = monero::private_key_from_secp256k1_scalar(s_a.into()); + + Ok(State5 { + s_a, + s_b: self.s_b, + v: self.v, + tx_lock: self.tx_lock.clone(), + monero_wallet_restore_blockheight: self.monero_wallet_restore_blockheight, + }) + } + pub fn tx_redeem_encsig(&self) -> bitcoin::EncryptedSignature { let tx_redeem = bitcoin::TxRedeem::new(&self.tx_lock, &self.redeem_address, self.tx_redeem_fee); diff --git a/swap/src/protocol/bob/swap.rs b/swap/src/protocol/bob/swap.rs index 37a6b65a..8eaa1489 100644 --- a/swap/src/protocol/bob/swap.rs +++ b/swap/src/protocol/bob/swap.rs @@ -183,6 +183,13 @@ async fn next_state( } } BobState::XmrLocked(state) => { + // In case we send the encrypted signature to Alice, but she doesn't give us a confirmation + // We need to check if she still published the Bitcoin redeem transaction + // Otherwise we risk staying stuck in "XmrLocked" + if let Ok(state5) = state.check_for_tx_redeem(bitcoin_wallet).await { + return Ok(BobState::BtcRedeemed(state5)); + } + let tx_lock_status = bitcoin_wallet.subscribe_to(state.tx_lock.clone()).await; if let ExpiredTimelocks::None { .. } = state.expired_timelock(bitcoin_wallet).await? { @@ -207,6 +214,13 @@ async fn next_state( } } BobState::EncSigSent(state) => { + // We need to make sure that Alice did not publish the redeem transaction while we were offline + // Even if the cancel timelock expired, if Alice published the redeem transaction while we were away we cannot miss it + // If we do we cannot refund and will never be able to leave the "CancelTimelockExpired" state + if let Ok(state5) = state.check_for_tx_redeem(bitcoin_wallet).await { + return Ok(BobState::BtcRedeemed(state5)); + } + let tx_lock_status = bitcoin_wallet.subscribe_to(state.tx_lock.clone()).await; if let ExpiredTimelocks::None { .. } = state.expired_timelock(bitcoin_wallet).await? {