mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2024-12-25 07:29:32 -05:00
Merge #107
107: Ensure that Bob can cancel correctly if T1 expired and Alice did not … r=da-kami a=da-kami 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 and it might be impossible to use the `resume` command because it will always fail in trying to go on with Alice even though that might not be possible. Co-authored-by: Daniel Karzel <daniel@comit.network>
This commit is contained in:
commit
9c83ca52ad
@ -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,
|
||||
|
@ -223,16 +223,40 @@ 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.
|
||||
event_loop_handle.dial().await?;
|
||||
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
|
||||
let msg2 = event_loop_handle.recv_message2().await?;
|
||||
let state4 = state3
|
||||
.watch_for_lock_xmr(monero_wallet.as_ref(), msg2)
|
||||
.await?;
|
||||
let msg2_watcher = event_loop_handle.recv_message2();
|
||||
let t1_timeout = state3.wait_for_t1(bitcoin_wallet.as_ref());
|
||||
|
||||
let state = BobState::XmrLocked(state4);
|
||||
select! {
|
||||
msg2 = msg2_watcher => {
|
||||
|
||||
let xmr_lock_watcher = state3.clone()
|
||||
.watch_for_lock_xmr(monero_wallet.as_ref(), msg2?);
|
||||
let t1_timeout = state3.wait_for_t1(bitcoin_wallet.as_ref());
|
||||
|
||||
select! {
|
||||
state4 = xmr_lock_watcher => {
|
||||
BobState::XmrLocked(state4?)
|
||||
},
|
||||
_ = t1_timeout => {
|
||||
let state4 = state3.t1_expired();
|
||||
BobState::T1Expired(state4)
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
_ = t1_timeout => {
|
||||
let state4 = state3.t1_expired();
|
||||
BobState::T1Expired(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 +273,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();
|
||||
|
@ -28,7 +28,7 @@ use std::{
|
||||
use tokio::{sync::Mutex, time::timeout};
|
||||
use tracing::{error, info};
|
||||
pub mod message;
|
||||
use crate::bitcoin::{BlockHeight, TransactionBlockHeight};
|
||||
use crate::bitcoin::{current_epoch, wait_for_t1, BlockHeight, TransactionBlockHeight};
|
||||
pub use message::{Message, Message0, Message1, Message2};
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -684,31 +684,20 @@ impl State3 {
|
||||
where
|
||||
W: WatchForRawTransaction + TransactionBlockHeight + BlockHeight,
|
||||
{
|
||||
let tx_id = self.tx_lock.txid();
|
||||
let tx_lock_height = bitcoin_wallet.transaction_block_height(tx_id).await;
|
||||
|
||||
let t1_timeout =
|
||||
poll_until_block_height_is_gte(bitcoin_wallet, tx_lock_height + self.refund_timelock);
|
||||
t1_timeout.await;
|
||||
Ok(())
|
||||
wait_for_t1(bitcoin_wallet, self.refund_timelock, self.tx_lock.txid()).await
|
||||
}
|
||||
|
||||
pub async fn current_epoch<W>(&self, bitcoin_wallet: &W) -> Result<Epoch>
|
||||
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),
|
||||
}
|
||||
current_epoch(
|
||||
bitcoin_wallet,
|
||||
self.refund_timelock,
|
||||
self.punish_timelock,
|
||||
self.tx_lock.txid(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@ use serde::{Deserialize, Serialize};
|
||||
use sha2::Sha256;
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::Epoch;
|
||||
pub use bitcoin::{util::psbt::PartiallySignedTransaction, *};
|
||||
pub use ecdsa_fun::{adaptor::EncryptedSignature, fun::Scalar, Signature};
|
||||
pub use transactions::{TxCancel, TxLock, TxPunish, TxRedeem, TxRefund};
|
||||
@ -243,3 +244,40 @@ where
|
||||
tokio::time::delay_for(std::time::Duration::from_secs(1)).await;
|
||||
}
|
||||
}
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn wait_for_t1<W>(
|
||||
bitcoin_wallet: &W,
|
||||
refund_timelock: u32,
|
||||
lock_tx_id: ::bitcoin::Txid,
|
||||
) -> Result<()>
|
||||
where
|
||||
W: WatchForRawTransaction + TransactionBlockHeight + BlockHeight,
|
||||
{
|
||||
let tx_lock_height = bitcoin_wallet.transaction_block_height(lock_tx_id).await;
|
||||
|
||||
let t1_timeout =
|
||||
poll_until_block_height_is_gte(bitcoin_wallet, tx_lock_height + refund_timelock);
|
||||
t1_timeout.await;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -34,7 +34,9 @@ use tracing::error;
|
||||
|
||||
pub mod message;
|
||||
use crate::{
|
||||
bitcoin::{BlockHeight, GetRawTransaction, Network, TransactionBlockHeight},
|
||||
bitcoin::{
|
||||
current_epoch, wait_for_t1, BlockHeight, GetRawTransaction, Network, TransactionBlockHeight,
|
||||
},
|
||||
monero::{CreateWalletForOutput, WatchForTransfer},
|
||||
};
|
||||
use ::bitcoin::{Transaction, Txid};
|
||||
@ -621,9 +623,50 @@ impl State3 {
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn wait_for_t1<W>(&self, bitcoin_wallet: &W) -> Result<()>
|
||||
where
|
||||
W: WatchForRawTransaction + TransactionBlockHeight + BlockHeight,
|
||||
{
|
||||
wait_for_t1(bitcoin_wallet, self.refund_timelock, self.tx_lock.txid()).await
|
||||
}
|
||||
|
||||
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,
|
||||
{
|
||||
current_epoch(
|
||||
bitcoin_wallet,
|
||||
self.refund_timelock,
|
||||
self.punish_timelock,
|
||||
self.tx_lock.txid(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
|
||||
@ -748,31 +791,20 @@ impl State4 {
|
||||
where
|
||||
W: WatchForRawTransaction + TransactionBlockHeight + BlockHeight,
|
||||
{
|
||||
let tx_id = self.tx_lock.txid();
|
||||
let tx_lock_height = bitcoin_wallet.transaction_block_height(tx_id).await;
|
||||
|
||||
let t1_timeout =
|
||||
poll_until_block_height_is_gte(bitcoin_wallet, tx_lock_height + self.refund_timelock);
|
||||
t1_timeout.await;
|
||||
Ok(())
|
||||
wait_for_t1(bitcoin_wallet, self.refund_timelock, self.tx_lock.txid()).await
|
||||
}
|
||||
|
||||
pub async fn current_epoch<W>(&self, bitcoin_wallet: &W) -> Result<Epoch>
|
||||
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),
|
||||
}
|
||||
current_epoch(
|
||||
bitcoin_wallet,
|
||||
self.refund_timelock,
|
||||
self.punish_timelock,
|
||||
self.tx_lock.txid(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn refund_btc<W: bitcoin::BroadcastSignedTransaction>(
|
||||
|
Loading…
Reference in New Issue
Block a user