diff --git a/swap/src/bob/swap.rs b/swap/src/bob/swap.rs index 4221b1e9..5eb5dc57 100644 --- a/swap/src/bob/swap.rs +++ b/swap/src/bob/swap.rs @@ -10,7 +10,7 @@ use rand::{CryptoRng, RngCore}; use std::{fmt, sync::Arc}; use tracing::info; use uuid::Uuid; -use xmr_btc::bob::{self}; +use xmr_btc::bob::{self, Epoch}; // The same data structure is used for swap execution and recovery. // This allows for a seamless transition from a failed swap to recovery. @@ -259,17 +259,41 @@ where ) .await } - BobState::Cancelled(_state) => { + BobState::Cancelled(state) => { // Bob has cancelled the swap - // If panic!("Cancelled before t1??? Something is really wrong"), + Epoch::T1 => { + state.refund_btc(bitcoin_wallet.as_ref()).await?; + run_until( + BobState::BtcRefunded, + is_target_state, + swarm, + db, + bitcoin_wallet, + monero_wallet, + rng, + swap_id, + ) + .await + } + Epoch::T2 => { + // todo: If t2 has elapsed should we check whether Alice has punished? If + // not can we still refund? + run_until( + BobState::Punished, + is_target_state, + swarm, + db, + bitcoin_wallet, + monero_wallet, + rng, + swap_id, + ) + .await + } + } } BobState::BtcRefunded => Ok(BobState::BtcRefunded), BobState::Punished => Ok(BobState::Punished), diff --git a/xmr-btc/src/bob.rs b/xmr-btc/src/bob.rs index dbc6b53c..f0a84b14 100644 --- a/xmr-btc/src/bob.rs +++ b/xmr-btc/src/bob.rs @@ -799,11 +799,80 @@ impl State4 { t1_timeout.await; Ok(()) } + + pub async fn current_epoch(&self, bitcoin_wallet: &W) -> Result + 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), + } + } + + pub async fn refund_btc( + &self, + bitcoin_wallet: &W, + ) -> Result<()> { + let tx_cancel = + bitcoin::TxCancel::new(&self.tx_lock, self.refund_timelock, self.A, self.b.public()); + let tx_refund = bitcoin::TxRefund::new(&tx_cancel, &self.refund_address); + + { + let sig_b = self.b.sign(tx_cancel.digest()); + let sig_a = self.tx_cancel_sig_a.clone(); + + let signed_tx_cancel = tx_cancel.clone().add_signatures( + &self.tx_lock, + (self.A, sig_a), + (self.b.public(), sig_b), + )?; + + let _ = bitcoin_wallet + .broadcast_signed_transaction(signed_tx_cancel) + .await?; + } + + { + let adaptor = Adaptor::>::default(); + + let sig_b = self.b.sign(tx_refund.digest()); + let sig_a = adaptor + .decrypt_signature(&self.s_b.into_secp256k1(), self.tx_refund_encsig.clone()); + + let signed_tx_refund = tx_refund.add_signatures( + &tx_cancel.clone(), + (self.A, sig_a), + (self.b.public(), sig_b), + )?; + + let _ = bitcoin_wallet + .broadcast_signed_transaction(signed_tx_refund) + .await?; + } + Ok(()) + } + pub fn tx_lock_id(&self) -> bitcoin::Txid { self.tx_lock.txid() } } +#[derive(Debug, Clone, Copy)] +pub enum Epoch { + T0, + T1, + T2, +} + #[derive(Debug, Clone, Deserialize, Serialize)] pub struct State5 { A: bitcoin::PublicKey,