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,
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(
encrypted_signature,
&state3.tx_lock,

View File

@ -223,7 +223,8 @@ where
// Bob has locked Btc
// Watch for Alice to Lock Xmr or for t1 to elapse
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?;
// todo: watch until t1, not indefinitely
@ -232,7 +233,11 @@ where
.watch_for_lock_xmr(monero_wallet.as_ref(), msg2)
.await?;
let state = BobState::XmrLocked(state4);
BobState::XmrLocked(state4)
} else {
let state4 = state3.t1_expired();
BobState::T1Expired(state4)
};
let db_state = state.clone().into();
db.insert_latest_state(swap_id, state::Swap::Bob(db_state))
.await?;
@ -249,10 +254,8 @@ where
.await
}
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? {
event_loop_handle.dial().await?;
// Alice has locked Xmr
// Bob sends Alice his key
let tx_redeem_encsig = state.tx_redeem_encsig();

View File

@ -697,18 +697,13 @@ impl State3 {
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),
}
crate::current_epoch(
bitcoin_wallet,
self.refund_timelock,
self.punish_timelock,
self.tx_lock.txid(),
)
.await
}
}

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 {
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)]
@ -761,18 +795,13 @@ impl State4 {
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),
}
crate::current_epoch(
bitcoin_wallet,
self.refund_timelock,
self.punish_timelock,
self.tx_lock.txid(),
)
.await
}
pub async fn refund_btc<W: bitcoin::BroadcastSignedTransaction>(

View File

@ -60,4 +60,26 @@ pub mod monero;
pub mod serde;
pub mod transport;
use crate::bitcoin::{BlockHeight, TransactionBlockHeight, WatchForRawTransaction};
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),
}
}