Ensure that Bob can cancel correctly if T1 expired and Alice did not move

Bob has to check for the possibility to cancel in every state after he locked the BTC.
Otherwise Bob will try to perform actions that don't have any point.
This commit is contained in:
Daniel Karzel 2020-12-21 17:33:18 +11:00
parent 8296490764
commit 83ce6f2c85
5 changed files with 88 additions and 35 deletions

View File

@ -429,6 +429,10 @@ pub async fn run_until(
state3, state3,
encrypted_signature, encrypted_signature,
} => { } => {
// TODO: Evaluate if it is correct for Alice to Redeem no matter what.
// If T1 expired she should potentially not try redeem. (The implementation
// gives her an advantage.)
let signed_tx_redeem = match build_bitcoin_redeem_transaction( let signed_tx_redeem = match build_bitcoin_redeem_transaction(
encrypted_signature, encrypted_signature,
&state3.tx_lock, &state3.tx_lock,

View File

@ -223,16 +223,21 @@ where
// Bob has locked Btc // Bob has locked Btc
// Watch for Alice to Lock Xmr or for t1 to elapse // Watch for Alice to Lock Xmr or for t1 to elapse
BobState::BtcLocked(state3) => { BobState::BtcLocked(state3) => {
// TODO(Franck): Refund if cannot connect to Alice. let state = if let Epoch::T0 = state3.current_epoch(bitcoin_wallet.as_ref()).await?
event_loop_handle.dial().await?; {
event_loop_handle.dial().await?;
// todo: watch until t1, not indefinitely // todo: watch until t1, not indefinitely
let msg2 = event_loop_handle.recv_message2().await?; let msg2 = event_loop_handle.recv_message2().await?;
let state4 = state3 let state4 = state3
.watch_for_lock_xmr(monero_wallet.as_ref(), msg2) .watch_for_lock_xmr(monero_wallet.as_ref(), msg2)
.await?; .await?;
let state = BobState::XmrLocked(state4); BobState::XmrLocked(state4)
} else {
let state4 = state3.t1_expired();
BobState::T1Expired(state4)
};
let db_state = state.clone().into(); let db_state = state.clone().into();
db.insert_latest_state(swap_id, state::Swap::Bob(db_state)) db.insert_latest_state(swap_id, state::Swap::Bob(db_state))
.await?; .await?;
@ -249,10 +254,8 @@ where
.await .await
} }
BobState::XmrLocked(state) => { BobState::XmrLocked(state) => {
// TODO(Franck): Refund if cannot connect to Alice.
event_loop_handle.dial().await?;
let state = if let Epoch::T0 = state.current_epoch(bitcoin_wallet.as_ref()).await? { let state = if let Epoch::T0 = state.current_epoch(bitcoin_wallet.as_ref()).await? {
event_loop_handle.dial().await?;
// Alice has locked Xmr // Alice has locked Xmr
// Bob sends Alice his key // Bob sends Alice his key
let tx_redeem_encsig = state.tx_redeem_encsig(); let tx_redeem_encsig = state.tx_redeem_encsig();

View File

@ -697,18 +697,13 @@ impl State3 {
where where
W: WatchForRawTransaction + TransactionBlockHeight + BlockHeight, W: WatchForRawTransaction + TransactionBlockHeight + BlockHeight,
{ {
let current_block_height = bitcoin_wallet.block_height().await; crate::current_epoch(
let t0 = bitcoin_wallet bitcoin_wallet,
.transaction_block_height(self.tx_lock.txid()) self.refund_timelock,
.await; self.punish_timelock,
let t1 = t0 + self.refund_timelock; self.tx_lock.txid(),
let t2 = t1 + self.punish_timelock; )
.await
match (current_block_height < t1, current_block_height < t2) {
(true, _) => Ok(Epoch::T0),
(false, true) => Ok(Epoch::T1),
(false, false) => Ok(Epoch::T2),
}
} }
} }

View File

@ -621,9 +621,43 @@ impl State3 {
}) })
} }
pub fn t1_expired(&self) -> State4 {
State4 {
A: self.A,
b: self.b.clone(),
s_b: self.s_b,
S_a_monero: self.S_a_monero,
S_a_bitcoin: self.S_a_bitcoin,
v: self.v,
btc: self.btc,
xmr: self.xmr,
refund_timelock: self.refund_timelock,
punish_timelock: self.punish_timelock,
refund_address: self.refund_address.clone(),
redeem_address: self.redeem_address.clone(),
punish_address: self.punish_address.clone(),
tx_lock: self.tx_lock.clone(),
tx_cancel_sig_a: self.tx_cancel_sig_a.clone(),
tx_refund_encsig: self.tx_refund_encsig.clone(),
}
}
pub fn tx_lock_id(&self) -> bitcoin::Txid { pub fn tx_lock_id(&self) -> bitcoin::Txid {
self.tx_lock.txid() self.tx_lock.txid()
} }
pub async fn current_epoch<W>(&self, bitcoin_wallet: &W) -> Result<Epoch>
where
W: WatchForRawTransaction + TransactionBlockHeight + BlockHeight,
{
crate::current_epoch(
bitcoin_wallet,
self.refund_timelock,
self.punish_timelock,
self.tx_lock.txid(),
)
.await
}
} }
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
@ -761,18 +795,13 @@ impl State4 {
where where
W: WatchForRawTransaction + TransactionBlockHeight + BlockHeight, W: WatchForRawTransaction + TransactionBlockHeight + BlockHeight,
{ {
let current_block_height = bitcoin_wallet.block_height().await; crate::current_epoch(
let t0 = bitcoin_wallet bitcoin_wallet,
.transaction_block_height(self.tx_lock.txid()) self.refund_timelock,
.await; self.punish_timelock,
let t1 = t0 + self.refund_timelock; self.tx_lock.txid(),
let t2 = t1 + self.punish_timelock; )
.await
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<W: bitcoin::BroadcastSignedTransaction>( pub async fn refund_btc<W: bitcoin::BroadcastSignedTransaction>(

View File

@ -60,4 +60,26 @@ pub mod monero;
pub mod serde; pub mod serde;
pub mod transport; pub mod transport;
use crate::bitcoin::{BlockHeight, TransactionBlockHeight, WatchForRawTransaction};
pub use cross_curve_dleq; pub use cross_curve_dleq;
pub async fn current_epoch<W>(
bitcoin_wallet: &W,
refund_timelock: u32,
punish_timelock: u32,
lock_tx_id: ::bitcoin::Txid,
) -> anyhow::Result<Epoch>
where
W: WatchForRawTransaction + TransactionBlockHeight + BlockHeight,
{
let current_block_height = bitcoin_wallet.block_height().await;
let t0 = bitcoin_wallet.transaction_block_height(lock_tx_id).await;
let t1 = t0 + refund_timelock;
let t2 = t1 + 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),
}
}