From 273cf15631735c985bf06b3ff44eec66cad31bdb Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 16 Mar 2021 19:11:14 +1100 Subject: [PATCH] Introduce `Watchable` abstraction for Bitcoin wallet We have a repeated pattern where we construct one of our Tx{Cancel,Redeem,Punish,Refund,Lock} transactions and wait until the status of this transaction changes. We can make this more ergonomic by creating and implementing a `Watchable` trait that gives access to the TxId and relevant script for this transaction. This allows us to remove a parameter from the `watch_until_status` function. Additionally, there is a 2nd pattern: "Completing" one of these transaction and waiting until they are confirmed with the configured number of blocks for finality. We can make this more ergonomic by returning a future from `broadcast` that callers can await in case they want to wait for the broadcasted transaction to reach finality. --- swap/src/bitcoin/cancel.rs | 23 ++++---- swap/src/bitcoin/lock.rs | 11 ++++ swap/src/bitcoin/punish.rs | 18 ++++++- swap/src/bitcoin/redeem.rs | 16 +++++- swap/src/bitcoin/refund.rs | 14 +++++ swap/src/bitcoin/wallet.rs | 90 ++++++++++++++++++++++++-------- swap/src/protocol/alice/state.rs | 16 ++---- swap/src/protocol/alice/steps.rs | 48 ++++++++--------- swap/src/protocol/alice/swap.rs | 67 ++++++++---------------- swap/src/protocol/bob/state.rs | 47 +++++------------ swap/src/protocol/bob/swap.rs | 6 +-- 11 files changed, 204 insertions(+), 152 deletions(-) diff --git a/swap/src/bitcoin/cancel.rs b/swap/src/bitcoin/cancel.rs index 606ac579..e54bc988 100644 --- a/swap/src/bitcoin/cancel.rs +++ b/swap/src/bitcoin/cancel.rs @@ -1,3 +1,4 @@ +use crate::bitcoin::wallet::Watchable; use crate::bitcoin::{ build_shared_output_descriptor, Address, Amount, BlockHeight, PublicKey, Transaction, TxLock, TX_FEE, @@ -81,7 +82,7 @@ impl PartialEq for u32 { } } -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct TxCancel { inner: Transaction, digest: SigHash, @@ -148,16 +149,6 @@ impl TxCancel { OutPoint::new(self.inner.txid(), 0) } - /// Return the relevant script_pubkey of this transaction. - /// - /// Even though a transaction can have multiple outputs, the nature of our - /// protocol is that there is only one relevant output within this one. - /// As such, subscribing or inquiring the status of this script allows us to - /// check the status of the whole transaction. - pub fn script_pubkey(&self) -> Script { - self.output_descriptor.script_pubkey() - } - pub fn add_signatures( self, (A, sig_a): (PublicKey, Signature), @@ -216,3 +207,13 @@ impl TxCancel { } } } + +impl Watchable for TxCancel { + fn id(&self) -> Txid { + self.txid() + } + + fn script(&self) -> Script { + self.output_descriptor.script_pubkey() + } +} diff --git a/swap/src/bitcoin/lock.rs b/swap/src/bitcoin/lock.rs index 220f80da..d2d5e7ca 100644 --- a/swap/src/bitcoin/lock.rs +++ b/swap/src/bitcoin/lock.rs @@ -1,3 +1,4 @@ +use crate::bitcoin::wallet::Watchable; use crate::bitcoin::{ build_shared_output_descriptor, Address, Amount, PublicKey, Transaction, Wallet, TX_FEE, }; @@ -105,3 +106,13 @@ impl From for PartiallySignedTransaction { from.inner } } + +impl Watchable for TxLock { + fn id(&self) -> Txid { + self.txid() + } + + fn script(&self) -> Script { + self.output_descriptor.script_pubkey() + } +} diff --git a/swap/src/bitcoin/punish.rs b/swap/src/bitcoin/punish.rs index 37033316..4845429d 100644 --- a/swap/src/bitcoin/punish.rs +++ b/swap/src/bitcoin/punish.rs @@ -1,15 +1,18 @@ -use crate::bitcoin::{self, Address, PunishTimelock, Transaction, TxCancel}; +use crate::bitcoin::wallet::Watchable; +use crate::bitcoin::{self, Address, PunishTimelock, Transaction, TxCancel, Txid}; use ::bitcoin::util::bip143::SigHashCache; use ::bitcoin::{SigHash, SigHashType}; use anyhow::{Context, Result}; +use bdk::bitcoin::Script; use miniscript::{Descriptor, DescriptorTrait}; use std::collections::HashMap; -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct TxPunish { inner: Transaction, digest: SigHash, cancel_output_descriptor: Descriptor<::bitcoin::PublicKey>, + watch_script: Script, } impl TxPunish { @@ -31,6 +34,7 @@ impl TxPunish { inner: tx_punish, digest, cancel_output_descriptor: tx_cancel.output_descriptor.clone(), + watch_script: punish_address.script_pubkey(), } } @@ -68,3 +72,13 @@ impl TxPunish { Ok(tx_punish) } } + +impl Watchable for TxPunish { + fn id(&self) -> Txid { + self.inner.txid() + } + + fn script(&self) -> Script { + self.watch_script.clone() + } +} diff --git a/swap/src/bitcoin/redeem.rs b/swap/src/bitcoin/redeem.rs index 0a7888c5..6d14c201 100644 --- a/swap/src/bitcoin/redeem.rs +++ b/swap/src/bitcoin/redeem.rs @@ -1,3 +1,4 @@ +use crate::bitcoin::wallet::Watchable; use crate::bitcoin::{ verify_encsig, verify_sig, Address, EmptyWitnessStack, EncryptedSignature, NoInputs, NotThreeWitnesses, PublicKey, SecretKey, TooManyInputs, Transaction, TxLock, @@ -5,6 +6,7 @@ use crate::bitcoin::{ use ::bitcoin::util::bip143::SigHashCache; use ::bitcoin::{SigHash, SigHashType, Txid}; use anyhow::{bail, Context, Result}; +use bitcoin::Script; use ecdsa_fun::adaptor::{Adaptor, HashTranscript}; use ecdsa_fun::fun::Scalar; use ecdsa_fun::nonce::Deterministic; @@ -13,11 +15,12 @@ use miniscript::{Descriptor, DescriptorTrait}; use sha2::Sha256; use std::collections::HashMap; -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct TxRedeem { inner: Transaction, digest: SigHash, lock_output_descriptor: Descriptor<::bitcoin::PublicKey>, + watch_script: Script, } impl TxRedeem { @@ -37,6 +40,7 @@ impl TxRedeem { inner: tx_redeem, digest, lock_output_descriptor: tx_lock.output_descriptor.clone(), + watch_script: redeem_address.script_pubkey(), } } @@ -130,3 +134,13 @@ impl TxRedeem { Ok(sig) } } + +impl Watchable for TxRedeem { + fn id(&self) -> Txid { + self.txid() + } + + fn script(&self) -> Script { + self.watch_script.clone() + } +} diff --git a/swap/src/bitcoin/refund.rs b/swap/src/bitcoin/refund.rs index 5257d859..3d282c99 100644 --- a/swap/src/bitcoin/refund.rs +++ b/swap/src/bitcoin/refund.rs @@ -1,3 +1,4 @@ +use crate::bitcoin::wallet::Watchable; use crate::bitcoin::{ verify_sig, Address, EmptyWitnessStack, NoInputs, NotThreeWitnesses, PublicKey, TooManyInputs, Transaction, TxCancel, @@ -5,6 +6,7 @@ use crate::bitcoin::{ use ::bitcoin::util::bip143::SigHashCache; use ::bitcoin::{SigHash, SigHashType, Txid}; use anyhow::{bail, Context, Result}; +use bitcoin::Script; use ecdsa_fun::Signature; use miniscript::{Descriptor, DescriptorTrait}; use std::collections::HashMap; @@ -14,6 +16,7 @@ pub struct TxRefund { inner: Transaction, digest: SigHash, cancel_output_descriptor: Descriptor<::bitcoin::PublicKey>, + watch_script: Script, } impl TxRefund { @@ -31,6 +34,7 @@ impl TxRefund { inner: tx_punish, digest, cancel_output_descriptor: tx_cancel.output_descriptor.clone(), + watch_script: refund_address.script_pubkey(), } } @@ -110,3 +114,13 @@ impl TxRefund { Ok(sig) } } + +impl Watchable for TxRefund { + fn id(&self) -> Txid { + self.txid() + } + + fn script(&self) -> Script { + self.watch_script.clone() + } +} diff --git a/swap/src/bitcoin/wallet.rs b/swap/src/bitcoin/wallet.rs index f3306d25..480bf68e 100644 --- a/swap/src/bitcoin/wallet.rs +++ b/swap/src/bitcoin/wallet.rs @@ -13,6 +13,7 @@ use reqwest::Url; use std::collections::BTreeMap; use std::convert::TryFrom; use std::fmt; +use std::future::Future; use std::path::Path; use std::sync::Arc; use std::time::{Duration, Instant}; @@ -159,9 +160,22 @@ impl Wallet { /// Broadcast the given transaction to the network and emit a log statement /// if done so successfully. - pub async fn broadcast(&self, transaction: Transaction, kind: &str) -> Result { + /// + /// Returns the transaction ID and a future for when the transaction meets + /// the configured finality confirmations. + pub async fn broadcast( + &self, + transaction: Transaction, + kind: &str, + ) -> Result<(Txid, impl Future> + '_)> { let txid = transaction.txid(); + // to watch for confirmations, watching a single output is enough + let watcher = self.wait_for_transaction_finality( + (txid, transaction.output[0].script_pubkey.clone()), + kind.to_owned(), + ); + self.wallet .lock() .await @@ -172,7 +186,7 @@ impl Wallet { tracing::info!(%txid, "Published Bitcoin {} transaction", kind); - Ok(txid) + Ok((txid, watcher)) } pub async fn sign_and_finalize(&self, psbt: PartiallySignedTransaction) -> Result { @@ -193,20 +207,27 @@ impl Wallet { .ok_or_else(|| anyhow!("Could not get raw tx with id: {}", txid)) } - pub async fn status_of_script(&self, script: &Script, txid: &Txid) -> Result { - self.client.lock().await.status_of_script(script, txid) + pub async fn status_of_script(&self, tx: &T) -> Result + where + T: Watchable, + { + self.client.lock().await.status_of_script(tx) } - pub async fn watch_until_status( + pub async fn watch_until_status( &self, - txid: Txid, - script: Script, + tx: &T, mut status_fn: impl FnMut(ScriptStatus) -> bool, - ) -> Result<()> { + ) -> Result<()> + where + T: Watchable, + { + let txid = tx.id(); + let mut last_status = None; loop { - let new_status = self.client.lock().await.status_of_script(&script, &txid)?; + let new_status = self.client.lock().await.status_of_script(tx)?; if Some(new_status) != last_status { tracing::debug!(%txid, "Transaction is {}", new_status); @@ -223,23 +244,23 @@ impl Wallet { Ok(()) } - pub async fn wait_for_transaction_finality( - &self, - txid: Txid, - script_to_watch: Script, - ) -> Result<()> { + async fn wait_for_transaction_finality(&self, tx: T, kind: String) -> Result<()> + where + T: Watchable, + { let conf_target = self.bitcoin_finality_confirmations; + let txid = tx.id(); - tracing::info!(%txid, "Waiting for {} confirmation{} of Bitcoin transaction", conf_target, if conf_target > 1 { "s" } else { "" }); + tracing::info!(%txid, "Waiting for {} confirmation{} of Bitcoin {} transaction", conf_target, if conf_target > 1 { "s" } else { "" }, kind); let mut seen_confirmations = 0; - self.watch_until_status(txid, script_to_watch, |status| match status { + self.watch_until_status(&tx, |status| match status { ScriptStatus::Confirmed(inner) => { let confirmations = inner.confirmations(); if confirmations > seen_confirmations { - tracing::info!(%txid, "Bitcoin tx has {} out of {} confirmation{}", confirmations, conf_target, if conf_target > 1 { "s" } else { "" }); + tracing::info!(%txid, "Bitcoin {} tx has {} out of {} confirmation{}", kind, confirmations, conf_target, if conf_target > 1 { "s" } else { "" }); seen_confirmations = confirmations; } @@ -260,6 +281,27 @@ impl Wallet { } } +/// Defines a watchable transaction. +/// +/// For a transaction to be watchable, we need to know two things: Its +/// transaction ID and the specific output script that is going to change. +/// A transaction can obviously have multiple outputs but our protocol purposes, +/// we are usually interested in a specific one. +pub trait Watchable { + fn id(&self) -> Txid; + fn script(&self) -> Script; +} + +impl Watchable for (Txid, Script) { + fn id(&self) -> Txid { + self.0 + } + + fn script(&self) -> Script { + self.1.clone() + } +} + struct Client { electrum: bdk::electrum_client::Client, latest_block: BlockHeight, @@ -321,18 +363,24 @@ impl Client { Ok(()) } - fn status_of_script(&mut self, script: &Script, txid: &Txid) -> Result { - if !self.script_history.contains_key(script) { + fn status_of_script(&mut self, tx: &T) -> Result + where + T: Watchable, + { + let txid = tx.id(); + let script = tx.script(); + + if !self.script_history.contains_key(&script) { self.script_history.insert(script.clone(), vec![]); } self.drain_notifications()?; - let history = self.script_history.entry(script.clone()).or_default(); + let history = self.script_history.entry(script).or_default(); let history_of_tx = history .iter() - .filter(|entry| &entry.tx_hash == txid) + .filter(|entry| entry.tx_hash == txid) .collect::>(); match history_of_tx.as_slice() { diff --git a/swap/src/protocol/alice/state.rs b/swap/src/protocol/alice/state.rs index f3b83881..263aee10 100644 --- a/swap/src/protocol/alice/state.rs +++ b/swap/src/protocol/alice/state.rs @@ -321,11 +321,9 @@ impl State3 { bitcoin_wallet: &bitcoin::Wallet, ) -> Result<()> { bitcoin_wallet - .watch_until_status( - self.tx_lock.txid(), - self.tx_lock.script_pubkey(), - |status| status.is_confirmed_with(self.cancel_timelock), - ) + .watch_until_status(&self.tx_lock, |status| { + status.is_confirmed_with(self.cancel_timelock) + }) .await?; Ok(()) @@ -337,12 +335,8 @@ impl State3 { ) -> Result { let tx_cancel = self.tx_cancel(); - let tx_lock_status = bitcoin_wallet - .status_of_script(&self.tx_lock.script_pubkey(), &self.tx_lock.txid()) - .await?; - let tx_cancel_status = bitcoin_wallet - .status_of_script(&tx_cancel.script_pubkey(), &tx_cancel.txid()) - .await?; + let tx_lock_status = bitcoin_wallet.status_of_script(&self.tx_lock).await?; + let tx_cancel_status = bitcoin_wallet.status_of_script(&tx_cancel).await?; Ok(current_epoch( self.cancel_timelock, diff --git a/swap/src/protocol/alice/steps.rs b/swap/src/protocol/alice/steps.rs index d4516a07..3f7b18e8 100644 --- a/swap/src/protocol/alice/steps.rs +++ b/swap/src/protocol/alice/steps.rs @@ -6,7 +6,6 @@ use crate::protocol::alice::event_loop::EventLoopHandle; use crate::protocol::alice::TransferProof; use crate::{bitcoin, monero}; use anyhow::{bail, Context, Result}; -use futures::future::{select, Either}; use futures::pin_mut; use libp2p::PeerId; @@ -61,9 +60,7 @@ pub async fn publish_cancel_transaction( bitcoin_wallet: &bitcoin::Wallet, ) -> Result<()> { bitcoin_wallet - .watch_until_status(tx_lock.txid(), tx_lock.script_pubkey(), |status| { - status.is_confirmed_with(cancel_timelock) - }) + .watch_until_status(&tx_lock, |status| status.is_confirmed_with(cancel_timelock)) .await?; let tx_cancel = bitcoin::TxCancel::new(&tx_lock, cancel_timelock, a.public(), B); @@ -85,7 +82,7 @@ pub async fn publish_cancel_transaction( .expect("sig_{a,b} to be valid signatures for tx_cancel"); // TODO(Franck): Error handling is delicate, why can't we broadcast? - bitcoin_wallet.broadcast(tx_cancel, "cancel").await?; + let (..) = bitcoin_wallet.broadcast(tx_cancel, "cancel").await?; // TODO(Franck): Wait until transaction is mined and returned mined // block height @@ -96,35 +93,36 @@ pub async fn publish_cancel_transaction( pub async fn wait_for_bitcoin_refund( tx_cancel: &TxCancel, + tx_refund: &TxRefund, punish_timelock: PunishTimelock, - refund_address: &bitcoin::Address, bitcoin_wallet: &bitcoin::Wallet, -) -> Result<(bitcoin::TxRefund, Option)> { - let tx_refund = bitcoin::TxRefund::new(tx_cancel, refund_address); +) -> Result> { + let refund_tx_id = tx_refund.txid(); + let seen_refund_tx = + bitcoin_wallet.watch_until_status(tx_refund, |status| status.has_been_seen()); - let seen_refund_tx = bitcoin_wallet.watch_until_status( - tx_refund.txid(), - refund_address.script_pubkey(), - |status| status.has_been_seen(), - ); - - let punish_timelock_expired = - bitcoin_wallet.watch_until_status(tx_cancel.txid(), tx_cancel.script_pubkey(), |status| { - status.is_confirmed_with(punish_timelock) - }); + let punish_timelock_expired = bitcoin_wallet.watch_until_status(tx_cancel, |status| { + status.is_confirmed_with(punish_timelock) + }); pin_mut!(punish_timelock_expired); pin_mut!(seen_refund_tx); - match select(punish_timelock_expired, seen_refund_tx).await { - Either::Left(_) => Ok((tx_refund, None)), - Either::Right((Ok(()), _)) => { - let published_refund_tx = bitcoin_wallet.get_raw_transaction(tx_refund.txid()).await?; + tokio::select! { + seen_refund = seen_refund_tx => { + match seen_refund { + Ok(()) => { + let published_refund_tx = bitcoin_wallet.get_raw_transaction(refund_tx_id).await?; - Ok((tx_refund, Some(published_refund_tx))) + Ok(Some(published_refund_tx)) + } + Err(e) => { + bail!(e.context("Failed to monitor refund transaction")) + } + } } - Either::Right((Err(e), _)) => { - bail!(e.context("Failed to monitor refund transaction")) + _ = punish_timelock_expired => { + Ok(None) } } } diff --git a/swap/src/protocol/alice/swap.rs b/swap/src/protocol/alice/swap.rs index b59a2fd8..2d652416 100644 --- a/swap/src/protocol/alice/swap.rs +++ b/swap/src/protocol/alice/swap.rs @@ -82,20 +82,16 @@ async fn run_until_internal( } => { timeout( execution_params.bob_time_to_act, - bitcoin_wallet.watch_until_status( - state3.tx_lock.txid(), - state3.tx_lock.script_pubkey(), - |status| status.has_been_seen(), - ), + bitcoin_wallet + .watch_until_status(&state3.tx_lock, |status| status.has_been_seen()), ) .await .context("Failed to find lock Bitcoin tx")??; bitcoin_wallet - .wait_for_transaction_finality( - state3.tx_lock.txid(), - state3.tx_lock.script_pubkey(), - ) + .watch_until_status(&state3.tx_lock, |status| { + status.is_confirmed_with(execution_params.bitcoin_finality_confirmations) + }) .await?; let state = AliceState::BtcLocked { @@ -209,30 +205,19 @@ async fn run_until_internal( } => { let state = match state3.expired_timelocks(bitcoin_wallet.as_ref()).await? { ExpiredTimelocks::None => { - let tx_redeem = TxRedeem::new(&state3.tx_lock, &state3.redeem_address); - - match tx_redeem.complete( + match TxRedeem::new(&state3.tx_lock, &state3.redeem_address).complete( *encrypted_signature, state3.a.clone(), state3.s_a.to_secpfun_scalar(), state3.B, ) { Ok(tx) => match bitcoin_wallet.broadcast(tx, "redeem").await { - Ok(txid) => { - let publishded_redeem_tx = bitcoin_wallet - .wait_for_transaction_finality( - txid, - state3.redeem_address.script_pubkey(), - ) - .await; - - match publishded_redeem_tx { - Ok(_) => AliceState::BtcRedeemed, - Err(e) => { - bail!("Waiting for Bitcoin transaction finality failed with {}! The redeem transaction was published, but it is not ensured that the transaction was included! You're screwed.", e) - } + Ok((_, finality)) => match finality.await { + Ok(_) => AliceState::BtcRedeemed, + Err(e) => { + bail!("Waiting for Bitcoin transaction finality failed with {}! The redeem transaction was published, but it is not ensured that the transaction was included! You're screwed.", e) } - } + }, Err(e) => { error!("Publishing the redeem transaction failed with {}, attempting to wait for cancellation now. If you restart the application before the timelock is expired publishing the redeem transaction will be retried.", e); state3 @@ -316,10 +301,10 @@ async fn run_until_internal( state3, monero_wallet_restore_blockheight, } => { - let (tx_refund, published_refund_tx) = wait_for_bitcoin_refund( + let published_refund_tx = wait_for_bitcoin_refund( &state3.tx_cancel(), + &state3.tx_refund(), state3.punish_timelock, - &state3.refund_address, &bitcoin_wallet, ) .await?; @@ -350,7 +335,7 @@ async fn run_until_internal( Some(published_refund_tx) => { let spend_key = extract_monero_private_key( published_refund_tx, - &tx_refund, + &state3.tx_refund(), state3.s_a, state3.a.clone(), state3.S_b_bitcoin, @@ -405,36 +390,30 @@ async fn run_until_internal( state3.B, )?; - let punish_script_pubkey = state3.punish_address.script_pubkey(); - let punish_tx_finalised = async { - let txid = bitcoin_wallet.broadcast(signed_tx_punish, "punish").await?; + let (txid, finality) = + bitcoin_wallet.broadcast(signed_tx_punish, "punish").await?; - bitcoin_wallet - .wait_for_transaction_finality(txid, punish_script_pubkey) - .await?; + finality.await?; Result::<_, anyhow::Error>::Ok(txid) }; - let refund_tx_seen = bitcoin_wallet.watch_until_status( - state3.tx_refund().txid(), - state3.refund_address.script_pubkey(), - |status| status.has_been_seen(), - ); + let tx_refund = state3.tx_refund(); + let refund_tx_seen = + bitcoin_wallet.watch_until_status(&tx_refund, |status| status.has_been_seen()); pin_mut!(punish_tx_finalised); pin_mut!(refund_tx_seen); match select(refund_tx_seen, punish_tx_finalised).await { Either::Left((Ok(()), _)) => { - let published_refund_tx = bitcoin_wallet - .get_raw_transaction(state3.tx_refund().txid()) - .await?; + let published_refund_tx = + bitcoin_wallet.get_raw_transaction(tx_refund.txid()).await?; let spend_key = extract_monero_private_key( published_refund_tx, - &state3.tx_refund(), + &tx_refund, state3.s_a, state3.a.clone(), state3.S_b_bitcoin, diff --git a/swap/src/protocol/bob/state.rs b/swap/src/protocol/bob/state.rs index d723b9c2..7d288a2e 100644 --- a/swap/src/protocol/bob/state.rs +++ b/swap/src/protocol/bob/state.rs @@ -350,11 +350,9 @@ impl State3 { bitcoin_wallet: &bitcoin::Wallet, ) -> Result<()> { bitcoin_wallet - .watch_until_status( - self.tx_lock.txid(), - self.tx_lock.script_pubkey(), - |status| status.is_confirmed_with(self.cancel_timelock), - ) + .watch_until_status(&self.tx_lock, |status| { + status.is_confirmed_with(self.cancel_timelock) + }) .await?; Ok(()) } @@ -389,12 +387,8 @@ impl State3 { ) -> Result { let tx_cancel = TxCancel::new(&self.tx_lock, self.cancel_timelock, self.A, self.b.public()); - let tx_lock_status = bitcoin_wallet - .status_of_script(&self.tx_lock.script_pubkey(), &self.tx_lock.txid()) - .await?; - let tx_cancel_status = bitcoin_wallet - .status_of_script(&tx_cancel.script_pubkey(), &tx_cancel.txid()) - .await?; + let tx_lock_status = bitcoin_wallet.status_of_script(&self.tx_lock).await?; + let tx_cancel_status = bitcoin_wallet.status_of_script(&tx_cancel).await?; Ok(current_epoch( self.cancel_timelock, @@ -454,14 +448,13 @@ impl State4 { let sig_b = self.b.sign(tx_cancel.digest()); let tx_cancel = tx_cancel - .clone() .add_signatures((self.A, sig_a), (self.b.public(), sig_b)) .expect( "sig_{a,b} to be valid signatures for tx_cancel", ); - let tx_id = bitcoin_wallet.broadcast(tx_cancel, "cancel").await?; + let (tx_id, _) = bitcoin_wallet.broadcast(tx_cancel, "cancel").await?; Ok(tx_id) } @@ -471,11 +464,7 @@ impl State4 { let tx_redeem_encsig = self.b.encsign(self.S_a_bitcoin, tx_redeem.digest()); bitcoin_wallet - .watch_until_status( - tx_redeem.txid(), - self.redeem_address.script_pubkey(), - |status| status.has_been_seen(), - ) + .watch_until_status(&tx_redeem, |status| status.has_been_seen()) .await?; let tx_redeem_candidate = bitcoin_wallet.get_raw_transaction(tx_redeem.txid()).await?; @@ -499,11 +488,9 @@ impl State4 { bitcoin_wallet: &bitcoin::Wallet, ) -> Result<()> { bitcoin_wallet - .watch_until_status( - self.tx_lock.txid(), - self.tx_lock.script_pubkey(), - |status| status.is_confirmed_with(self.cancel_timelock), - ) + .watch_until_status(&self.tx_lock, |status| { + status.is_confirmed_with(self.cancel_timelock) + }) .await?; Ok(()) @@ -515,12 +502,8 @@ impl State4 { ) -> Result { let tx_cancel = TxCancel::new(&self.tx_lock, self.cancel_timelock, self.A, self.b.public()); - let tx_lock_status = bitcoin_wallet - .status_of_script(&self.tx_lock.script_pubkey(), &self.tx_lock.txid()) - .await?; - let tx_cancel_status = bitcoin_wallet - .status_of_script(&tx_cancel.script_pubkey(), &tx_cancel.txid()) - .await?; + let tx_lock_status = bitcoin_wallet.status_of_script(&self.tx_lock).await?; + let tx_cancel_status = bitcoin_wallet.status_of_script(&tx_cancel).await?; Ok(current_epoch( self.cancel_timelock, @@ -544,11 +527,9 @@ impl State4 { let signed_tx_refund = tx_refund.add_signatures((self.A, sig_a), (self.b.public(), sig_b))?; - let txid = bitcoin_wallet.broadcast(signed_tx_refund, "refund").await?; + let (_, finality) = bitcoin_wallet.broadcast(signed_tx_refund, "refund").await?; - bitcoin_wallet - .wait_for_transaction_finality(txid, self.refund_address.script_pubkey()) - .await?; + finality.await?; Ok(()) } diff --git a/swap/src/protocol/bob/swap.rs b/swap/src/protocol/bob/swap.rs index 75db537d..197e5fd9 100644 --- a/swap/src/protocol/bob/swap.rs +++ b/swap/src/protocol/bob/swap.rs @@ -104,12 +104,10 @@ async fn run_until_internal( .sign_and_finalize(tx_lock.clone().into()) .await .context("Failed to sign Bitcoin lock transaction")?; - let tx_lock_id = bitcoin_wallet.broadcast(signed_tx, "lock").await?; + let (..) = bitcoin_wallet.broadcast(signed_tx, "lock").await?; bitcoin_wallet - .watch_until_status(tx_lock_id, tx_lock.script_pubkey(), |status| { - status.is_confirmed() - }) + .watch_until_status(&tx_lock, |status| status.is_confirmed()) .await?; let state = BobState::BtcLocked(state3);