From b93e84c53f2bd470871e9a3d219d9d20d7466971 Mon Sep 17 00:00:00 2001 From: pokkst Date: Sat, 12 Aug 2023 22:20:54 -0500 Subject: [PATCH 1/3] Add edge case bugfix for our databse not updating correctly when Alice redeems her BTC --- swap/src/protocol/bob/swap.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/swap/src/protocol/bob/swap.rs b/swap/src/protocol/bob/swap.rs index 66933a87..6c1d2584 100644 --- a/swap/src/protocol/bob/swap.rs +++ b/swap/src/protocol/bob/swap.rs @@ -190,6 +190,11 @@ async fn next_state( // Bob sends Alice his key select! { + state5 = state.watch_for_redeem_btc(bitcoin_wallet) => { + // there seems to be a weird edge case where you can send the encrypted signature, alice can redeem the btc, but your database doesn't update to encsigsent or btcredeemed, so here we will watch for redemption too, so we have a chance to unlock xmr. + // this seemed to have happened when a surge of blocks came in on stagenet within a short time frame + BobState::BtcRedeemed(state5?) + }, result = event_loop_handle.send_encrypted_signature(state.tx_redeem_encsig()) => { match result { Ok(_) => BobState::EncSigSent(state), From 0f69d2fbb82f3a1e7b61e889744a931097705716 Mon Sep 17 00:00:00 2001 From: pokkst Date: Sun, 13 Aug 2023 13:53:48 -0500 Subject: [PATCH 2/3] Add safety checks (cherry picked from commit 1bd9b6abdca6bb1fbb60835cae58c5c5caad2683) --- swap/src/protocol/bob/state.rs | 24 ++++++++++++++++++++++++ swap/src/protocol/bob/swap.rs | 4 +++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/swap/src/protocol/bob/state.rs b/swap/src/protocol/bob/state.rs index 4d0e5a31..e0bad1eb 100644 --- a/swap/src/protocol/bob/state.rs +++ b/swap/src/protocol/bob/state.rs @@ -493,6 +493,30 @@ impl State4 { self.b.encsign(self.S_a_bitcoin, tx_redeem.digest()) } + 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 async fn watch_for_redeem_btc(&self, bitcoin_wallet: &bitcoin::Wallet) -> Result { 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 6c1d2584..6604ac88 100644 --- a/swap/src/protocol/bob/swap.rs +++ b/swap/src/protocol/bob/swap.rs @@ -185,7 +185,9 @@ async fn next_state( BobState::XmrLocked(state) => { let tx_lock_status = bitcoin_wallet.subscribe_to(state.tx_lock.clone()).await; - if let ExpiredTimelocks::None = state.expired_timelock(bitcoin_wallet).await? { + if let Ok(state5) = state.check_for_tx_redeem(bitcoin_wallet).await { + BobState::BtcRedeemed(state5) + } else if let ExpiredTimelocks::None = state.expired_timelock(bitcoin_wallet).await? { // Alice has locked Xmr // Bob sends Alice his key From 335945648201b582bf2289187cc3a223349f5b89 Mon Sep 17 00:00:00 2001 From: pokkst Date: Mon, 14 Aug 2023 00:56:39 -0500 Subject: [PATCH 3/3] Move check to outside of select, in case Alice redeems the BTC and we saw that before we get confirmation that she received the encrypted signature. --- swap/src/protocol/bob/swap.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/swap/src/protocol/bob/swap.rs b/swap/src/protocol/bob/swap.rs index 6604ac88..604eaa1e 100644 --- a/swap/src/protocol/bob/swap.rs +++ b/swap/src/protocol/bob/swap.rs @@ -186,17 +186,13 @@ async fn next_state( let tx_lock_status = bitcoin_wallet.subscribe_to(state.tx_lock.clone()).await; if let Ok(state5) = state.check_for_tx_redeem(bitcoin_wallet).await { + // this is in case we send the encrypted signature to alice, but we don't get confirmation that she received it. alice would be able to redeem the btc, but we would be stuck in xmrlocked, never being able to redeem the xmr. BobState::BtcRedeemed(state5) } else if let ExpiredTimelocks::None = state.expired_timelock(bitcoin_wallet).await? { // Alice has locked Xmr // Bob sends Alice his key select! { - state5 = state.watch_for_redeem_btc(bitcoin_wallet) => { - // there seems to be a weird edge case where you can send the encrypted signature, alice can redeem the btc, but your database doesn't update to encsigsent or btcredeemed, so here we will watch for redemption too, so we have a chance to unlock xmr. - // this seemed to have happened when a surge of blocks came in on stagenet within a short time frame - BobState::BtcRedeemed(state5?) - }, result = event_loop_handle.send_encrypted_signature(state.tx_redeem_encsig()) => { match result { Ok(_) => BobState::EncSigSent(state),