Implement wait_for_transaction_finality

This commit is contained in:
Franck Royer 2020-12-02 10:00:00 +11:00
parent 65e910e1c1
commit 765482b0aa
No known key found for this signature in database
GPG key ID: A82ED75A8DFC50A4
10 changed files with 186 additions and 33 deletions

View file

@ -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)
}

View file

@ -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
}

View file

@ -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(())
}
}