From 8615aaed6ecfd5785b60ffb0b5cbe5d63d7ce25c Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Mon, 18 Jan 2021 14:45:47 +1100 Subject: [PATCH] Make lock-tx id available in redeem/punish state to be able to assert exact fees --- swap/src/database.rs | 4 ++-- swap/src/database/bob.rs | 12 ++++++------ swap/src/protocol/bob/state.rs | 31 +++++++++++++++++++++++++++---- swap/src/protocol/bob/swap.rs | 12 ++++++------ swap/tests/punish.rs | 9 +-------- swap/tests/testutils/mod.rs | 25 +++++++++++++++++++++---- 6 files changed, 63 insertions(+), 30 deletions(-) diff --git a/swap/src/database.rs b/swap/src/database.rs index 826d866f..fa7a8b8a 100644 --- a/swap/src/database.rs +++ b/swap/src/database.rs @@ -130,7 +130,7 @@ mod tests { .await .expect("Failed to save second state"); - let state_2 = Swap::Bob(Bob::Done(BobEndState::XmrRedeemed)); + let state_2 = Swap::Bob(Bob::Done(BobEndState::SafelyAborted)); let swap_id_2 = Uuid::new_v4(); db.insert_latest_state(swap_id_2, state_2.clone()) .await @@ -186,7 +186,7 @@ mod tests { .await .expect("Failed to save second state"); - let state_2 = Swap::Bob(Bob::Done(BobEndState::BtcPunished)); + let state_2 = Swap::Bob(Bob::Done(BobEndState::SafelyAborted)); let swap_id_2 = Uuid::new_v4(); db.insert_latest_state(swap_id_2, state_2.clone()) .await diff --git a/swap/src/database/bob.rs b/swap/src/database/bob.rs index 54b4249b..6e396a45 100644 --- a/swap/src/database/bob.rs +++ b/swap/src/database/bob.rs @@ -33,9 +33,9 @@ pub enum Bob { #[derive(Clone, strum::Display, Debug, Deserialize, Serialize, PartialEq)] pub enum BobEndState { SafelyAborted, - XmrRedeemed, + XmrRedeemed(Box), BtcRefunded(Box), - BtcPunished, + BtcPunished(Box), } impl From for Bob { @@ -50,8 +50,8 @@ impl From for Bob { BobState::CancelTimelockExpired(state4) => Bob::CancelTimelockExpired(state4), BobState::BtcCancelled(state4) => Bob::BtcCancelled(state4), BobState::BtcRefunded(state4) => Bob::Done(BobEndState::BtcRefunded(Box::new(state4))), - BobState::XmrRedeemed => Bob::Done(BobEndState::XmrRedeemed), - BobState::BtcPunished => Bob::Done(BobEndState::BtcPunished), + BobState::XmrRedeemed(state6) => Bob::Done(BobEndState::XmrRedeemed(Box::new(state6))), + BobState::BtcPunished(state6) => Bob::Done(BobEndState::BtcPunished(Box::new(state6))), BobState::SafelyAborted => Bob::Done(BobEndState::SafelyAborted), } } @@ -70,9 +70,9 @@ impl From for BobState { Bob::BtcCancelled(state4) => BobState::BtcCancelled(state4), Bob::Done(end_state) => match end_state { BobEndState::SafelyAborted => BobState::SafelyAborted, - BobEndState::XmrRedeemed => BobState::XmrRedeemed, + BobEndState::XmrRedeemed(state6) => BobState::XmrRedeemed(*state6), BobEndState::BtcRefunded(state4) => BobState::BtcRefunded(*state4), - BobEndState::BtcPunished => BobState::BtcPunished, + BobEndState::BtcPunished(state6) => BobState::BtcPunished(*state6), }, } } diff --git a/swap/src/protocol/bob/state.rs b/swap/src/protocol/bob/state.rs index e3ab0cfa..14137da7 100644 --- a/swap/src/protocol/bob/state.rs +++ b/swap/src/protocol/bob/state.rs @@ -35,8 +35,8 @@ pub enum BobState { CancelTimelockExpired(State4), BtcCancelled(State4), BtcRefunded(State4), - XmrRedeemed, - BtcPunished, + XmrRedeemed(State6), + BtcPunished(State6), SafelyAborted, } @@ -52,8 +52,8 @@ impl fmt::Display for BobState { BobState::CancelTimelockExpired(..) => write!(f, "cancel timelock is expired"), BobState::BtcCancelled(..) => write!(f, "btc is cancelled"), BobState::BtcRefunded(..) => write!(f, "btc is refunded"), - BobState::XmrRedeemed => write!(f, "xmr is redeemed"), - BobState::BtcPunished => write!(f, "btc is punished"), + BobState::XmrRedeemed(..) => write!(f, "xmr is redeemed"), + BobState::BtcPunished(..) => write!(f, "btc is punished"), BobState::SafelyAborted => write!(f, "safely aborted"), } } @@ -592,6 +592,12 @@ impl State4 { pub fn tx_lock_id(&self) -> bitcoin::Txid { self.tx_lock.txid() } + + pub fn state6(&self) -> State6 { + State6 { + tx_lock_id: self.tx_lock.txid(), + } + } } #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] @@ -644,4 +650,21 @@ impl State5 { pub fn tx_lock_id(&self) -> bitcoin::Txid { self.tx_lock.txid() } + + pub fn state6(&self) -> State6 { + State6 { + tx_lock_id: self.tx_lock.txid(), + } + } +} + +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] +pub struct State6 { + pub tx_lock_id: Txid, +} + +impl State6 { + pub fn tx_lock_id(&self) -> bitcoin::Txid { + self.tx_lock_id + } } diff --git a/swap/src/protocol/bob/swap.rs b/swap/src/protocol/bob/swap.rs index 038b5523..ee9942cc 100644 --- a/swap/src/protocol/bob/swap.rs +++ b/swap/src/protocol/bob/swap.rs @@ -45,8 +45,8 @@ pub fn is_complete(state: &BobState) -> bool { matches!( state, BobState::BtcRefunded(..) - | BobState::XmrRedeemed - | BobState::BtcPunished + | BobState::XmrRedeemed { .. } + | BobState::BtcPunished { .. } | BobState::SafelyAborted ) } @@ -273,7 +273,7 @@ where // Bob redeems XMR using revealed s_a state.claim_xmr(monero_wallet.as_ref()).await?; - let state = BobState::XmrRedeemed; + let state = BobState::XmrRedeemed(state.state6()); let db_state = state.clone().into(); db.insert_latest_state(swap_id, Swap::Bob(db_state)).await?; run_until( @@ -323,7 +323,7 @@ where state.refund_btc(bitcoin_wallet.as_ref()).await?; BobState::BtcRefunded(state) } - ExpiredTimelocks::Punish => BobState::BtcPunished, + ExpiredTimelocks::Punish => BobState::BtcPunished(state.state6()), }; let db_state = state.clone().into(); @@ -341,9 +341,9 @@ where .await } BobState::BtcRefunded(state4) => Ok(BobState::BtcRefunded(state4)), - BobState::BtcPunished => Ok(BobState::BtcPunished), + BobState::BtcPunished(state6) => Ok(BobState::BtcPunished(state6)), BobState::SafelyAborted => Ok(BobState::SafelyAborted), - BobState::XmrRedeemed => Ok(BobState::XmrRedeemed), + BobState::XmrRedeemed(state6) => Ok(BobState::XmrRedeemed(state6)), } } } diff --git a/swap/tests/punish.rs b/swap/tests/punish.rs index dacc1cc5..e17de0ff 100644 --- a/swap/tests/punish.rs +++ b/swap/tests/punish.rs @@ -45,13 +45,6 @@ async fn alice_punishes_if_bob_never_acts_after_fund() { let bob = bob_harness.recover_bob_from_db().await; assert!(matches!(bob.state, BobState::BtcLocked {..})); - // TODO: make lock-tx-id available in final states - let lock_tx_id = if let BobState::BtcLocked(state3) = bob_state { - state3.tx_lock_id() - } else { - panic!("Bob in unexpected state"); - }; - let bob_state = bob::swap( bob.state, bob.event_loop_handle, @@ -64,7 +57,7 @@ async fn alice_punishes_if_bob_never_acts_after_fund() { .await .unwrap(); - bob_harness.assert_punished(bob_state, lock_tx_id).await; + bob_harness.assert_punished(bob_state).await; }) .await; } diff --git a/swap/tests/testutils/mod.rs b/swap/tests/testutils/mod.rs index d179e5bb..95be1e29 100644 --- a/swap/tests/testutils/mod.rs +++ b/swap/tests/testutils/mod.rs @@ -363,10 +363,23 @@ impl BobHarness { } pub async fn assert_redeemed(&self, state: BobState) { - assert!(matches!(state, BobState::XmrRedeemed)); + let lock_tx_id = if let BobState::XmrRedeemed(state6) = state { + state6.tx_lock_id() + } else { + panic!("Bob in unexpected state"); + }; + + let lock_tx_bitcoin_fee = self + .bitcoin_wallet + .transaction_fee(lock_tx_id) + .await + .unwrap(); let btc_balance_after_swap = self.bitcoin_wallet.as_ref().balance().await.unwrap(); - assert!(btc_balance_after_swap <= self.starting_balances.btc - self.swap_amounts.btc); + assert_eq!( + btc_balance_after_swap, + self.starting_balances.btc - self.swap_amounts.btc - lock_tx_bitcoin_fee + ); // Ensure that Bob's balance is refreshed as we use a newly created wallet self.monero_wallet.as_ref().inner.refresh().await.unwrap(); @@ -409,8 +422,12 @@ impl BobHarness { assert_eq!(xmr_balance_after_swap, self.starting_balances.xmr); } - pub async fn assert_punished(&self, state: BobState, lock_tx_id: ::bitcoin::Txid) { - assert!(matches!(state, BobState::BtcPunished)); + pub async fn assert_punished(&self, state: BobState) { + let lock_tx_id = if let BobState::BtcPunished(state6) = state { + state6.tx_lock_id() + } else { + panic!("Bob in unexpected state"); + }; let lock_tx_bitcoin_fee = self .bitcoin_wallet