From 3266ff01653752e18e6946fd4c0207194990fc19 Mon Sep 17 00:00:00 2001 From: Camillarhi Date: Thu, 9 Oct 2025 16:51:39 +0100 Subject: [PATCH] Use BDK events in `update_payment_store` instead of scanning all transactions Replace the full transaction list scan in `update_payment_store` with handling of BDK's `WalletEvent` stream during sync. This leverages the new events in BDK 2.2, reduces redundant work, and prepares the foundation for reliable RBF/CPFP tracking via `WalletEvent::TxReplaced`. --- src/wallet/mod.rs | 65 +++++++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/src/wallet/mod.rs b/src/wallet/mod.rs index 6d79fe02f..ea4bc6210 100644 --- a/src/wallet/mod.rs +++ b/src/wallet/mod.rs @@ -11,9 +11,10 @@ use std::str::FromStr; use std::sync::{Arc, Mutex}; use bdk_chain::spk_client::{FullScanRequest, SyncRequest}; +use bdk_chain::Anchor; #[allow(deprecated)] use bdk_wallet::SignOptions; -use bdk_wallet::{Balance, KeychainKind, PersistedWallet, Update}; +use bdk_wallet::{event::WalletEvent, Balance, KeychainKind, PersistedWallet, Update}; use bitcoin::address::NetworkUnchecked; use bitcoin::blockdata::constants::WITNESS_SCALE_FACTOR; use bitcoin::blockdata::locktime::absolute::LockTime; @@ -112,15 +113,15 @@ impl Wallet { pub(crate) fn apply_update(&self, update: impl Into) -> Result<(), Error> { let mut locked_wallet = self.inner.lock().unwrap(); - match locked_wallet.apply_update(update) { - Ok(()) => { + match locked_wallet.apply_update_events(update) { + Ok(events) => { let mut locked_persister = self.persister.lock().unwrap(); locked_wallet.persist(&mut locked_persister).map_err(|e| { log_error!(self.logger, "Failed to persist wallet: {}", e); Error::PersistenceFailed })?; - self.update_payment_store(&mut *locked_wallet).map_err(|e| { + self.update_payment_store(&mut *locked_wallet, &mut *events).map_err(|e| { log_error!(self.logger, "Failed to update payment store: {}", e); Error::PersistenceFailed })?; @@ -152,31 +153,39 @@ impl Wallet { fn update_payment_store<'a>( &self, locked_wallet: &'a mut PersistedWallet, + events: &'a mut Vec, ) -> Result<(), Error> { - for wtx in locked_wallet.transactions() { - let id = PaymentId(wtx.tx_node.txid.to_byte_array()); - let txid = wtx.tx_node.txid; - let (payment_status, confirmation_status) = match wtx.chain_position { - bdk_chain::ChainPosition::Confirmed { anchor, .. } => { - let confirmation_height = anchor.block_id.height; - let cur_height = locked_wallet.latest_checkpoint().height(); - let payment_status = if cur_height >= confirmation_height + ANTI_REORG_DELAY - 1 - { - PaymentStatus::Succeeded - } else { - PaymentStatus::Pending - }; - let confirmation_status = ConfirmationStatus::Confirmed { - block_hash: anchor.block_id.hash, - height: confirmation_height, - timestamp: anchor.confirmation_time, - }; - (payment_status, confirmation_status) + for event in events.iter() { + let (id, txid, tx, payment_status, confirmation_status) = match event { + WalletEvent::TxConfirmed { txid, tx, block_time, old_block_time: None } => ( + PaymentId(txid.to_byte_array()), + *txid, + *tx, + PaymentStatus::Succeeded, + ConfirmationStatus::Confirmed { + block_hash: block_time.block_id.hash, + height: block_time.block_id.height, + timestamp: block_time.confirmation_time, + }, + ), + WalletEvent::TxUnconfirmed { txid, tx, old_block_time: None } => ( + PaymentId(txid.to_byte_array()), + *txid, + *tx, + PaymentStatus::Pending, + ConfirmationStatus::Unconfirmed, + ), + WalletEvent::TxReplaced { txid, tx, conflicts } => { + todo!() }, - bdk_chain::ChainPosition::Unconfirmed { .. } => { - (PaymentStatus::Pending, ConfirmationStatus::Unconfirmed) + WalletEvent::TxDropped { txid, tx } => { + todo!() + }, + _ => { + // unexpected event, do nothing }, }; + // TODO: It would be great to introduce additional variants for // `ChannelFunding` and `ChannelClosing`. For the former, we could just // take a reference to `ChannelManager` here and check against @@ -189,8 +198,8 @@ impl Wallet { // here to determine the `PaymentKind`, but that's not really satisfactory, so // we're punting on it until we can come up with a better solution. let kind = crate::payment::PaymentKind::Onchain { txid, status: confirmation_status }; - let fee = locked_wallet.calculate_fee(&wtx.tx_node.tx).unwrap_or(Amount::ZERO); - let (sent, received) = locked_wallet.sent_and_received(&wtx.tx_node.tx); + let fee = locked_wallet.calculate_fee(&tx).unwrap_or(Amount::ZERO); + let (sent, received) = locked_wallet.sent_and_received(&tx); let (direction, amount_msat) = if sent > received { let direction = PaymentDirection::Outbound; let amount_msat = Some( @@ -723,7 +732,7 @@ impl Listen for Wallet { match locked_wallet.apply_block(block, height) { Ok(()) => { - if let Err(e) = self.update_payment_store(&mut *locked_wallet) { + if let Err(e) = self.update_payment_store(&mut *locked_wallet, &mut *events) { log_error!(self.logger, "Failed to update payment store: {}", e); return; }