diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index aab9f2cd..549dddbe 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -1,5 +1,5 @@ use crate::bitcoin::{parse_rpc_error_code, RpcErrorCode, Wallet}; -use crate::protocol::bob::{BobState, BtcCancelledByAlice, BtcPunishedWhileRefundError}; +use crate::protocol::bob::BobState; use crate::protocol::Database; use anyhow::{bail, Result}; use bitcoin::Txid; @@ -16,11 +16,12 @@ pub async fn cancel_and_refund( }; let state = match refund(swap_id, bitcoin_wallet, db).await { - Ok(s) => s, + Ok(s) => { + tracing::info!("Refund transaction submitted"); + s + } Err(e) => bail!(e), }; - - tracing::info!("Refund transaction submitted"); Ok(state) } @@ -64,21 +65,24 @@ pub async fn cancel( Err(err) => { if let Ok(error_code) = parse_rpc_error_code(&err) { if error_code == i64::from(RpcErrorCode::RpcVerifyError) { - tracing::debug!(%error_code, "parse rpc error"); - tracing::info!("General error trying to submit cancel transaction"); - } else if error_code == i64::from(RpcErrorCode::RpcVerifyAlreadyInChain) { - tracing::info!("Cancel transaction has already been confirmed on chain"); + if err + .to_string() + .contains("Failed to broadcast Bitcoin refund transaction") + { + let txid = state6 + .construct_tx_cancel() + .expect("Error when constructing tx_cancel") + .txid(); + let state = BobState::BtcCancelled(state6); + db.insert_latest_state(swap_id, state.clone().into()) + .await?; + tracing::info!("Cancel transaction has already been confirmed on chain. The swap has therefore already been cancelled by Alice."); + return Ok((txid, state)); + } else { + tracing::debug!(%error_code, "parse rpc error"); + tracing::info!("General error trying to submit cancel transaction"); + } } - } else if let Some(error) = err.downcast_ref::() { - let txid = state6 - .construct_tx_cancel() - .expect("Error when constructing tx_cancel") - .txid(); - let state = BobState::BtcCancelled(state6); - db.insert_latest_state(swap_id, state.clone().into()) - .await?; - tracing::info!(%error); - return Ok((txid, state)); } bail!(err); } @@ -112,24 +116,29 @@ pub async fn refund( }; tracing::info!(%swap_id, "Manually refunding swap"); + match state6.publish_refund_btc(bitcoin_wallet.as_ref()).await { - Ok(()) => { - let state = BobState::BtcRefunded(state6); - db.insert_latest_state(swap_id, state.clone().into()) - .await?; - Ok(state) - } - Err(error) => { - if let Some(error) = error.downcast_ref::() { - tracing::info!(%error); + Ok(_) => (), + Err(refund_error) => { + if refund_error + .to_string() + .contains("Failed to broadcast Bitcoin refund transaction") + { let state = BobState::BtcPunished { tx_lock_id: state6.tx_lock_id(), }; db.insert_latest_state(swap_id, state.clone().into()) .await?; + + tracing::info!("Cannot refund because BTC is punished by Alice."); return Ok(state); } - bail!(error); + bail!(refund_error); } } + let state = BobState::BtcRefunded(state6); + db.insert_latest_state(swap_id, state.clone().into()) + .await?; + + Ok(state) } diff --git a/swap/src/protocol/bob/state.rs b/swap/src/protocol/bob/state.rs index 942d9de6..a1fd750a 100644 --- a/swap/src/protocol/bob/state.rs +++ b/swap/src/protocol/bob/state.rs @@ -20,20 +20,6 @@ use sha2::Sha256; use sigma_fun::ext::dl_secp256k1_ed25519_eq::CrossCurveDLEQProof; use std::fmt; use uuid::Uuid; -#[derive(Debug)] -pub struct BtcCancelledByAlice; -impl std::fmt::Display for BtcCancelledByAlice { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Cancel transaction has already been confirmed on chain. The swap has therefore already been cancelled by Alice.") - } -} -#[derive(Debug)] -pub struct BtcPunishedWhileRefundError; -impl std::fmt::Display for BtcPunishedWhileRefundError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Cannot refund because BTC is punished.") - } -} #[derive(Debug, Clone, PartialEq, Serialize)] pub enum BobState { @@ -677,7 +663,6 @@ impl State6 { Ok(tx) } - pub fn construct_tx_cancel(&self) -> Result { bitcoin::TxCancel::new( &self.tx_lock, @@ -689,51 +674,24 @@ impl State6 { .complete_as_bob(self.A, self.b.clone(), self.tx_cancel_sig_a.clone()) .context("Failed to complete Bitcoin cancel transaction") } - pub fn print_type_of(&self, _: &T) { - tracing::debug!("{}", std::any::type_name::()) - } pub async fn submit_tx_cancel( &self, bitcoin_wallet: &bitcoin::Wallet, ) -> Result<(Txid, Subscription)> { let transaction = self.construct_tx_cancel()?; - match bitcoin_wallet.broadcast(transaction, "cancel").await { - Ok((txid, subscription)) => Ok((txid, subscription)), - Err(error) => { - match error.downcast_ref::() { - Some(bdk::Error::Electrum(bdk::electrum_client::Error::Protocol(serde_json::Value::String(ref protocol_error)))) - // UTXO is already spent, swap timeout. - if protocol_error.contains("bad-txns-inputs-missingorspent") => - { - return Err(anyhow!(BtcCancelledByAlice)); - } - _ => { - Err(error) - } - } - } - } + + let (tx_id, subscription) = bitcoin_wallet.broadcast(transaction, "cancel").await?; + + Ok((tx_id, subscription)) } pub async fn publish_refund_btc(&self, bitcoin_wallet: &bitcoin::Wallet) -> Result<()> { let signed_tx_refund = self.signed_refund_transaction()?; + bitcoin_wallet.broadcast(signed_tx_refund, "refund").await?; - match bitcoin_wallet.broadcast(signed_tx_refund, "refund").await { - Ok((_, _)) => Ok(()), - Err(error) => { - match error.downcast_ref::() { - Some(bdk::Error::Electrum(bdk::electrum_client::Error::Protocol(serde_json::Value::String(ref protocol_error)))) - if protocol_error.contains("bad-txns-inputs-missingorspent") => - { - return Err(anyhow!(BtcPunishedWhileRefundError)); - } - _ => { - Err(error) - } - } - } - } + Ok(()) } + pub fn signed_refund_transaction(&self) -> Result { let tx_cancel = bitcoin::TxCancel::new( &self.tx_lock, diff --git a/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs b/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs index d880b60e..5eacefd1 100644 --- a/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs +++ b/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs @@ -76,7 +76,7 @@ async fn alice_manually_punishes_after_bob_dead_and_bob_cancels() { let state = cli::cancel_and_refund(bob_swap_id, bob_swap.bitcoin_wallet, bob_swap.db).await?; - assert!(matches!(state, BobState::BtcPunished { .. })); + assert!(matches!(state, BobState::BtcPunished { .. })); Ok(()) }) .await;