mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-12-10 14:25:40 -05:00
Implement wait_for_transaction_finality
This commit is contained in:
parent
65e910e1c1
commit
765482b0aa
10 changed files with 186 additions and 33 deletions
|
|
@ -15,6 +15,7 @@ use libp2p::request_response::ResponseChannel;
|
|||
use sha2::Sha256;
|
||||
use std::{sync::Arc, time::Duration};
|
||||
use tokio::time::timeout;
|
||||
use tracing::trace;
|
||||
use xmr_btc::{
|
||||
alice,
|
||||
alice::{State0, State3},
|
||||
|
|
@ -23,13 +24,11 @@ use xmr_btc::{
|
|||
EncryptedSignature, GetRawTransaction, TransactionBlockHeight, TxCancel, TxLock, TxRefund,
|
||||
WaitForTransactionFinality, WatchForRawTransaction,
|
||||
},
|
||||
config::Config,
|
||||
cross_curve_dleq,
|
||||
monero::Transfer,
|
||||
};
|
||||
|
||||
// For each step, we are giving Bob 10 minutes to act.
|
||||
static BOB_TIME_TO_ACT: Lazy<Duration> = Lazy::new(|| Duration::from_secs(10 * 60));
|
||||
|
||||
// The maximum we assume we need to wait from the moment the monero transaction
|
||||
// is mined to the moment it reaches finality. We set 15 confirmations for now
|
||||
// (based on Kraken). 1.5 multiplier in case the blockchain is slower than
|
||||
|
|
@ -44,8 +43,10 @@ pub async fn negotiate(
|
|||
v_a: monero::PrivateViewKey,
|
||||
swarm: &mut Swarm,
|
||||
bitcoin_wallet: Arc<bitcoin::Wallet>,
|
||||
config: Config,
|
||||
) -> Result<(ResponseChannel<AliceToBob>, State3)> {
|
||||
let event = timeout(*BOB_TIME_TO_ACT, swarm.next())
|
||||
trace!("Starting negotiate");
|
||||
let event = timeout(config.bob_time_to_act, swarm.next())
|
||||
.await
|
||||
.context("Failed to receive dial connection from Bob")?;
|
||||
match event {
|
||||
|
|
@ -53,7 +54,7 @@ pub async fn negotiate(
|
|||
other => bail!("Unexpected event received: {:?}", other),
|
||||
}
|
||||
|
||||
let event = timeout(*BOB_TIME_TO_ACT, swarm.next())
|
||||
let event = timeout(config.bob_time_to_act, swarm.next())
|
||||
.await
|
||||
.context("Failed to receive amounts from Bob")?;
|
||||
let (btc, channel) = match event {
|
||||
|
|
@ -89,7 +90,7 @@ pub async fn negotiate(
|
|||
// TODO(Franck): Understand why this is needed.
|
||||
swarm.set_state0(state0.clone());
|
||||
|
||||
let event = timeout(*BOB_TIME_TO_ACT, swarm.next())
|
||||
let event = timeout(config.bob_time_to_act, swarm.next())
|
||||
.await
|
||||
.context("Failed to receive message 0 from Bob")?;
|
||||
let message0 = match event {
|
||||
|
|
@ -99,7 +100,7 @@ pub async fn negotiate(
|
|||
|
||||
let state1 = state0.receive(message0)?;
|
||||
|
||||
let event = timeout(*BOB_TIME_TO_ACT, swarm.next())
|
||||
let event = timeout(config.bob_time_to_act, swarm.next())
|
||||
.await
|
||||
.context("Failed to receive message 1 from Bob")?;
|
||||
let (msg, channel) = match event {
|
||||
|
|
@ -112,7 +113,7 @@ pub async fn negotiate(
|
|||
let message1 = state2.next_message();
|
||||
swarm.send_message1(channel, message1);
|
||||
|
||||
let event = timeout(*BOB_TIME_TO_ACT, swarm.next())
|
||||
let event = timeout(config.bob_time_to_act, swarm.next())
|
||||
.await
|
||||
.context("Failed to receive message 2 from Bob")?;
|
||||
let (msg, channel) = match event {
|
||||
|
|
@ -128,13 +129,14 @@ pub async fn negotiate(
|
|||
pub async fn wait_for_locked_bitcoin<W>(
|
||||
lock_bitcoin_txid: bitcoin::Txid,
|
||||
bitcoin_wallet: Arc<W>,
|
||||
config: Config,
|
||||
) -> Result<()>
|
||||
where
|
||||
W: WatchForRawTransaction + WaitForTransactionFinality,
|
||||
{
|
||||
// We assume we will see Bob's transaction in the mempool first.
|
||||
timeout(
|
||||
*BOB_TIME_TO_ACT,
|
||||
config.bob_time_to_act,
|
||||
bitcoin_wallet.watch_for_raw_transaction(lock_bitcoin_txid),
|
||||
)
|
||||
.await
|
||||
|
|
@ -142,7 +144,7 @@ where
|
|||
|
||||
// // We saw the transaction in the mempool, waiting for it to be confirmed.
|
||||
// bitcoin_wallet
|
||||
// .wait_for_transaction_finality(lock_bitcoin_txid)
|
||||
// .wait_for_transaction_finality(lock_bitcoin_txid, config)
|
||||
// .await;
|
||||
|
||||
Ok(())
|
||||
|
|
@ -225,17 +227,18 @@ pub fn build_bitcoin_redeem_transaction(
|
|||
pub async fn publish_bitcoin_redeem_transaction<W>(
|
||||
redeem_tx: bitcoin::Transaction,
|
||||
bitcoin_wallet: Arc<W>,
|
||||
config: Config,
|
||||
) -> Result<()>
|
||||
where
|
||||
W: BroadcastSignedTransaction + WaitForTransactionFinality,
|
||||
{
|
||||
let _tx_id = bitcoin_wallet
|
||||
let tx_id = bitcoin_wallet
|
||||
.broadcast_signed_transaction(redeem_tx)
|
||||
.await?;
|
||||
|
||||
// // TODO(Franck): Not sure if we wait for finality here or just mined
|
||||
// bitcoin_wallet.wait_for_transaction_finality(tx_id).await;
|
||||
Ok(())
|
||||
bitcoin_wallet
|
||||
.wait_for_transaction_finality(tx_id, config)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn publish_cancel_transaction<W>(
|
||||
|
|
@ -364,6 +367,7 @@ pub fn build_bitcoin_punish_transaction(
|
|||
pub async fn publish_bitcoin_punish_transaction<W>(
|
||||
punish_tx: bitcoin::Transaction,
|
||||
bitcoin_wallet: Arc<W>,
|
||||
config: Config,
|
||||
) -> Result<bitcoin::Txid>
|
||||
where
|
||||
W: BroadcastSignedTransaction + WaitForTransactionFinality,
|
||||
|
|
@ -372,8 +376,9 @@ where
|
|||
.broadcast_signed_transaction(punish_tx)
|
||||
.await?;
|
||||
|
||||
// todo: enable this once trait is implemented
|
||||
// bitcoin_wallet.wait_for_transaction_finality(txid).await;
|
||||
bitcoin_wallet
|
||||
.wait_for_transaction_finality(txid, config)
|
||||
.await?;
|
||||
|
||||
Ok(txid)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ use std::sync::Arc;
|
|||
use xmr_btc::{
|
||||
alice::State3,
|
||||
bitcoin::{TransactionBlockHeight, TxCancel, TxRefund, WatchForRawTransaction},
|
||||
config::Config,
|
||||
cross_curve_dleq,
|
||||
monero::CreateWalletForOutput,
|
||||
};
|
||||
|
|
@ -92,6 +93,7 @@ pub async fn swap(
|
|||
mut swarm: Swarm,
|
||||
bitcoin_wallet: Arc<crate::bitcoin::Wallet>,
|
||||
monero_wallet: Arc<crate::monero::Wallet>,
|
||||
config: Config,
|
||||
) -> Result<AliceState> {
|
||||
match state {
|
||||
AliceState::Started {
|
||||
|
|
@ -100,8 +102,16 @@ pub async fn swap(
|
|||
s_a,
|
||||
v_a,
|
||||
} => {
|
||||
let (channel, state3) =
|
||||
negotiate(amounts, a, s_a, v_a, &mut swarm, bitcoin_wallet.clone()).await?;
|
||||
let (channel, state3) = negotiate(
|
||||
amounts,
|
||||
a,
|
||||
s_a,
|
||||
v_a,
|
||||
&mut swarm,
|
||||
bitcoin_wallet.clone(),
|
||||
config,
|
||||
)
|
||||
.await?;
|
||||
|
||||
swap(
|
||||
AliceState::Negotiated {
|
||||
|
|
@ -112,6 +122,7 @@ pub async fn swap(
|
|||
swarm,
|
||||
bitcoin_wallet,
|
||||
monero_wallet,
|
||||
config,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
|
@ -120,7 +131,8 @@ pub async fn swap(
|
|||
channel,
|
||||
amounts,
|
||||
} => {
|
||||
let _ = wait_for_locked_bitcoin(state3.tx_lock.txid(), bitcoin_wallet.clone()).await?;
|
||||
let _ = wait_for_locked_bitcoin(state3.tx_lock.txid(), bitcoin_wallet.clone(), config)
|
||||
.await?;
|
||||
|
||||
swap(
|
||||
AliceState::BtcLocked {
|
||||
|
|
@ -131,6 +143,7 @@ pub async fn swap(
|
|||
swarm,
|
||||
bitcoin_wallet,
|
||||
monero_wallet,
|
||||
config,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
|
@ -153,6 +166,7 @@ pub async fn swap(
|
|||
swarm,
|
||||
bitcoin_wallet,
|
||||
monero_wallet,
|
||||
config,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
|
@ -169,6 +183,7 @@ pub async fn swap(
|
|||
swarm,
|
||||
bitcoin_wallet,
|
||||
monero_wallet,
|
||||
config,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
|
@ -178,6 +193,7 @@ pub async fn swap(
|
|||
swarm,
|
||||
bitcoin_wallet,
|
||||
monero_wallet,
|
||||
config,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
|
@ -202,6 +218,7 @@ pub async fn swap(
|
|||
swarm,
|
||||
bitcoin_wallet,
|
||||
monero_wallet,
|
||||
config,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
|
@ -210,13 +227,15 @@ pub async fn swap(
|
|||
// TODO(Franck): Error handling is delicate here.
|
||||
// If Bob sees this transaction he can redeem Monero
|
||||
// e.g. If the Bitcoin node is down then the user needs to take action.
|
||||
publish_bitcoin_redeem_transaction(signed_tx_redeem, bitcoin_wallet.clone()).await?;
|
||||
publish_bitcoin_redeem_transaction(signed_tx_redeem, bitcoin_wallet.clone(), config)
|
||||
.await?;
|
||||
|
||||
swap(
|
||||
AliceState::BtcRedeemed,
|
||||
swarm,
|
||||
bitcoin_wallet,
|
||||
monero_wallet,
|
||||
config,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
|
@ -236,6 +255,7 @@ pub async fn swap(
|
|||
swarm,
|
||||
bitcoin_wallet,
|
||||
monero_wallet,
|
||||
config,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
|
@ -261,6 +281,7 @@ pub async fn swap(
|
|||
swarm,
|
||||
bitcoin_wallet.clone(),
|
||||
monero_wallet,
|
||||
config,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
|
@ -274,6 +295,7 @@ pub async fn swap(
|
|||
swarm,
|
||||
bitcoin_wallet.clone(),
|
||||
monero_wallet,
|
||||
config,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
|
@ -310,8 +332,11 @@ pub async fn swap(
|
|||
state3.B.clone(),
|
||||
)?;
|
||||
|
||||
let punish_tx_finalised =
|
||||
publish_bitcoin_punish_transaction(signed_tx_punish, bitcoin_wallet.clone());
|
||||
let punish_tx_finalised = publish_bitcoin_punish_transaction(
|
||||
signed_tx_punish,
|
||||
bitcoin_wallet.clone(),
|
||||
config,
|
||||
);
|
||||
|
||||
let refund_tx_seen = bitcoin_wallet.watch_for_raw_transaction(tx_refund.txid());
|
||||
|
||||
|
|
@ -325,6 +350,7 @@ pub async fn swap(
|
|||
swarm,
|
||||
bitcoin_wallet.clone(),
|
||||
monero_wallet,
|
||||
config,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
|
@ -338,6 +364,7 @@ pub async fn swap(
|
|||
swarm,
|
||||
bitcoin_wallet.clone(),
|
||||
monero_wallet,
|
||||
config,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,13 @@ use backoff::{backoff::Constant as ConstantBackoff, future::FutureOperation as _
|
|||
use bitcoin::util::psbt::PartiallySignedTransaction;
|
||||
use bitcoin_harness::bitcoind_rpc::PsbtBase64;
|
||||
use reqwest::Url;
|
||||
use xmr_btc::bitcoin::{
|
||||
BlockHeight, BroadcastSignedTransaction, BuildTxLockPsbt, SignTxLock, TransactionBlockHeight,
|
||||
WatchForRawTransaction,
|
||||
use tokio::time::interval;
|
||||
use xmr_btc::{
|
||||
bitcoin::{
|
||||
BlockHeight, BroadcastSignedTransaction, BuildTxLockPsbt, SignTxLock,
|
||||
TransactionBlockHeight, WatchForRawTransaction,
|
||||
},
|
||||
config::Config,
|
||||
};
|
||||
|
||||
pub use ::bitcoin::{Address, Transaction};
|
||||
|
|
@ -148,7 +152,23 @@ impl TransactionBlockHeight for Wallet {
|
|||
|
||||
#[async_trait]
|
||||
impl WaitForTransactionFinality for Wallet {
|
||||
async fn wait_for_transaction_finality(&self, _txid: Txid) {
|
||||
todo!()
|
||||
async fn wait_for_transaction_finality(&self, txid: Txid, config: Config) -> Result<()> {
|
||||
// TODO(Franck): This assumes that bitcoind runs with txindex=1
|
||||
|
||||
// Divide by 4 to not check too often yet still be aware of the new block early
|
||||
// on.
|
||||
let mut interval = interval(config.bitcoin_avg_block_time / 4);
|
||||
|
||||
loop {
|
||||
let tx = self.0.client.get_raw_transaction_verbose(txid).await?;
|
||||
if let Some(confirmations) = tx.confirmations {
|
||||
if confirmations >= config.bitcoin_finality_confirmations {
|
||||
break;
|
||||
}
|
||||
}
|
||||
interval.tick().await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue