Skip to content

Commit 5f276a6

Browse files
committed
refactor: improve wallet event docs and extract helper method
- Enhanced WalletEvent documentation with clearer explanations - Expanded apply_update() docs with examples and persistence warnings - Extracted collect_wallet_txs() to reduce code duplication - Fixed minor typos and improved code clarity 🤖 LLM-assisted changes
1 parent 66cedb4 commit 5f276a6

File tree

2 files changed

+135
-117
lines changed

2 files changed

+135
-117
lines changed

src/wallet/event.rs

Lines changed: 45 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,76 +8,82 @@ use alloc::vec::Vec;
88
use bitcoin::{Transaction, Txid};
99
use chain::{BlockId, ChainPosition, ConfirmationBlockTime};
1010

11-
/// Events representing changes to wallet transactions.
11+
/// Events representing changes to the wallet state.
1212
///
13-
/// Returned after calling
14-
/// [`Wallet::apply_update_events`](crate::wallet::Wallet::apply_update_events).
13+
/// Returned by [`Wallet::apply_update`], [`Wallet::apply_block`], and
14+
/// [`Wallet::apply_block_connected_to`] to track transaction status changes and chain
15+
/// tip changes due to new blocks or reorganizations.
1516
#[derive(Debug, Clone, PartialEq, Eq)]
1617
#[non_exhaustive]
1718
pub enum WalletEvent {
18-
/// The latest chain tip known to the wallet changed.
19+
/// The blockchain tip known to the wallet has changed.
20+
///
21+
/// Emitted when the blockchain is extended or a chain reorganization occurs.
1922
ChainTipChanged {
20-
/// Previous chain tip.
23+
/// The previous blockchain tip.
2124
old_tip: BlockId,
22-
/// New chain tip.
25+
/// The new blockchain tip.
2326
new_tip: BlockId,
2427
},
25-
/// A transaction is now confirmed.
26-
///
27-
/// If the transaction was previously unconfirmed `old_block_time` will be `None`.
28+
29+
/// A transaction has been confirmed in a block.
2830
///
29-
/// If a confirmed transaction is now re-confirmed in a new block `old_block_time` will contain
30-
/// the block id and the time it was previously confirmed. This can happen after a chain
31-
/// reorg.
31+
/// Emitted when a transaction is first confirmed or re-confirmed in a different block after
32+
/// a chain reorganization. When `old_block_time` is `Some`, the transaction was previously
33+
/// confirmed in a different block.
3234
TxConfirmed {
33-
/// Transaction id.
35+
/// The transaction id.
3436
txid: Txid,
35-
/// Transaction.
37+
/// The full transaction.
3638
tx: Arc<Transaction>,
37-
/// Confirmation block time.
39+
/// The block and timestamp where the transaction is confirmed.
3840
block_time: ConfirmationBlockTime,
39-
/// Old confirmation block and time if previously confirmed in a different block.
41+
/// Previous confirmation details if re-confirmed after a reorg, `None` for first
42+
/// confirmation.
4043
old_block_time: Option<ConfirmationBlockTime>,
4144
},
42-
/// A transaction is now unconfirmed.
43-
///
44-
/// If the transaction is first seen in the mempool `old_block_time` will be `None`.
45+
46+
/// A transaction is now unconfirmed (in the mempool).
4547
///
46-
/// If a previously confirmed transaction is now seen in the mempool `old_block_time` will
47-
/// contain the block id and the time it was previously confirmed. This can happen after a
48-
/// chain reorg.
48+
/// Emitted when a transaction first appears in the mempool or when a confirmed transaction
49+
/// becomes unconfirmed due to a chain reorganization. When `old_block_time` is `Some`, the
50+
/// transaction was previously confirmed but is now unconfirmed due to a reorg.
4951
TxUnconfirmed {
50-
/// Transaction id.
52+
/// The transaction id.
5153
txid: Txid,
52-
/// Transaction.
54+
/// The full transaction.
5355
tx: Arc<Transaction>,
54-
/// Old confirmation block and time, if previously confirmed.
56+
/// Previous confirmation details if unconfirmed due to reorg, `None` if first seen.
5557
old_block_time: Option<ConfirmationBlockTime>,
5658
},
57-
/// An unconfirmed transaction was replaced.
59+
60+
/// One or more unconfirmed transactions were replaced.
5861
///
59-
/// This can happen after an RBF is broadcast or if a third party double spends an input of
60-
/// a received payment transaction before it is confirmed.
62+
/// Occurs when a transaction's inputs are spent by the replacement transaction, typically due
63+
/// to Replace-By-Fee (RBF) or a double-spend attempt.
6164
///
62-
/// The 'conflicts' field contains the txid and vin (in which it conflicts) of the conflicting
63-
/// transactions.
65+
/// The `conflicts` field contains `(input_index, conflicting_txid)` pairs indicating which
66+
/// inputs conflict. Only conflicting transactions known to the wallet are reported.
67+
/// Conflicting transactions are usually added during a sync because they spend a UTXO tracked
68+
/// by the wallet.
6469
TxReplaced {
65-
/// Transaction id.
70+
/// The replacement transaction id.
6671
txid: Txid,
67-
/// Transaction.
72+
/// The full replacement transaction.
6873
tx: Arc<Transaction>,
69-
/// Conflicting transaction ids.
74+
/// List of `(input_index, conflicting_txid)` pairs showing which inputs were double-spent.
7075
conflicts: Vec<(usize, Txid)>,
7176
},
72-
/// Unconfirmed transaction dropped.
77+
78+
/// An unconfirmed transaction was dropped from the mempool.
7379
///
74-
/// The transaction was dropped from the local mempool. This is generally due to the fee rate
75-
/// being too low. The transaction can still reappear in the mempool in the future resulting in
76-
/// a [`WalletEvent::TxUnconfirmed`] event.
80+
/// This typically occurs when a transaction's fee rate is too low and/or the mempool is full.
81+
/// The transaction may reappear later if conditions change, which will emit a
82+
/// [`WalletEvent::TxUnconfirmed`] event.
7783
TxDropped {
78-
/// Transaction id.
84+
/// The dropped transaction id.
7985
txid: Txid,
80-
/// Transaction.
86+
/// The full dropped transaction.
8187
tx: Arc<Transaction>,
8288
},
8389
}

src/wallet/mod.rs

Lines changed: 90 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -2341,101 +2341,118 @@ impl Wallet {
23412341
.to_string()
23422342
}
23432343

2344-
/// Applies an update to the wallet, stages the changes, and returns events.
2344+
/// Applies an update to the wallet, stages changes, and returns events describing what changed.
23452345
///
2346-
/// Usually you create an `update` by interacting with some blockchain data source and inserting
2347-
/// transactions related to your wallet into it. Staged changes are NOT persisted.
2346+
/// This is the primary method for updating wallet state with new blockchain data. It accepts
2347+
/// an [`Update`]
23482348
///
2349-
/// After applying updates, you should process the events in your app before persisting the
2350-
/// staged wallet changes. For an example of how to persist staged wallet changes, see
2351-
/// [`Wallet::reveal_next_address`].
2349+
/// **IMPORTANT**: Changes are staged but **NOT automatically persisted**. You must:
2350+
///
2351+
/// 1. Process the returned events in your application
2352+
/// 2. Call [`take_staged`] to retrieve the [`ChangeSet`]
2353+
/// 3. Persist the changeset to your database/storage
2354+
///
2355+
/// Failing to persist changes means they will be lost when the wallet is reloaded.
2356+
///
2357+
/// # Example
2358+
///
2359+
/// ## Basic usage with event handling
23522360
///
23532361
/// ```rust,no_run
23542362
/// # use bitcoin::*;
23552363
/// # use bdk_wallet::*;
23562364
/// use bdk_wallet::event::WalletEvent;
23572365
/// # let wallet_update = Update::default();
23582366
/// # let mut wallet = doctest_wallet!();
2367+
///
2368+
/// // Apply the update and get events
23592369
/// let events = wallet.apply_update(wallet_update)?;
2360-
/// // Handle wallet relevant events from this update.
2361-
/// events.iter().for_each(|event| {
2370+
///
2371+
/// // Handle each event
2372+
/// for event in events {
23622373
/// match event {
2363-
/// // The chain tip changed.
23642374
/// WalletEvent::ChainTipChanged { old_tip, new_tip } => {
2365-
/// todo!() // handle event
2375+
/// println!(
2376+
/// "Chain advanced from {} to {}",
2377+
/// old_tip.height, new_tip.height
2378+
/// );
23662379
/// }
2367-
/// // An unconfirmed tx is now confirmed in a block.
23682380
/// WalletEvent::TxConfirmed {
23692381
/// txid,
2370-
/// tx,
23712382
/// block_time,
23722383
/// old_block_time: None,
2384+
/// ..
23732385
/// } => {
2374-
/// todo!() // handle event
2386+
/// println!(
2387+
/// "Transaction {} confirmed at height {}",
2388+
/// txid, block_time.block_id.height
2389+
/// );
23752390
/// }
2376-
/// // A confirmed tx is now confirmed in a new block (reorg).
23772391
/// WalletEvent::TxConfirmed {
23782392
/// txid,
2379-
/// tx,
23802393
/// block_time,
2381-
/// old_block_time: Some(old_block_time),
2394+
/// old_block_time: Some(old),
2395+
/// ..
23822396
/// } => {
2383-
/// todo!() // handle event
2397+
/// println!(
2398+
/// "Transaction {} re-confirmed due to reorg: {} -> {}",
2399+
/// txid, old.block_id.height, block_time.block_id.height
2400+
/// );
23842401
/// }
2385-
/// // A new unconfirmed tx was seen in the mempool.
23862402
/// WalletEvent::TxUnconfirmed {
23872403
/// txid,
2388-
/// tx,
23892404
/// old_block_time: None,
2405+
/// ..
23902406
/// } => {
2391-
/// todo!() // handle event
2407+
/// println!("New mempool transaction: {}", txid);
23922408
/// }
2393-
/// // A previously confirmed tx in now unconfirmed in the mempool (reorg).
23942409
/// WalletEvent::TxUnconfirmed {
23952410
/// txid,
2396-
/// tx,
2397-
/// old_block_time: Some(old_block_time),
2411+
/// old_block_time: Some(old),
2412+
/// ..
23982413
/// } => {
2399-
/// todo!() // handle event
2414+
/// println!(
2415+
/// "Transaction {} unconfirmed due to reorg from height {}",
2416+
/// txid, old.block_id.height
2417+
/// );
24002418
/// }
2401-
/// // An unconfirmed tx was replaced in the mempool (RBF or double spent input).
24022419
/// WalletEvent::TxReplaced {
2403-
/// txid,
2404-
/// tx,
2405-
/// conflicts,
2420+
/// txid, conflicts, ..
24062421
/// } => {
2407-
/// todo!() // handle event
2422+
/// println!("Transaction {} replaced: {:?}", txid, conflicts);
24082423
/// }
2409-
/// // An unconfirmed tx was dropped from the mempool (fee too low).
2410-
/// WalletEvent::TxDropped { txid, tx } => {
2411-
/// todo!() // handle event
2412-
/// }
2413-
/// _ => {
2414-
/// // unexpected event, do nothing
2424+
/// WalletEvent::TxDropped { txid, .. } => {
2425+
/// println!("Transaction {} dropped from mempool", txid);
24152426
/// }
24162427
/// }
2417-
/// // take staged wallet changes
2418-
/// let staged = wallet.take_staged();
2419-
/// // persist staged changes
2420-
/// });
2428+
/// }
2429+
///
2430+
/// // IMPORTANT: Persist the changes
2431+
/// if let Some(changeset) = wallet.take_staged() {
2432+
/// // Save changeset to your database
2433+
/// // e.g., db.persist(&changeset)?;
2434+
/// }
24212435
/// # Ok::<(), anyhow::Error>(())
24222436
/// ```
2423-
/// [`TxBuilder`]: crate::TxBuilder
2437+
///
2438+
/// # See Also
2439+
///
2440+
/// - [`apply_block`] - Apply a single block to the wallet
2441+
/// - [`apply_block_connected_to`] - Apply a block with explicit connection point
2442+
/// - [`take_staged`] - Retrieve staged changes for persistence
2443+
/// - [`WalletEvent`] - Documentation for all event types
2444+
/// - [`Update`] - The update structure accepted by this method
2445+
///
2446+
/// [`apply_block`]: Wallet::apply_block
2447+
/// [`apply_block_connected_to`]: Wallet::apply_block_connected_to
2448+
/// [`take_staged`]: Wallet::take_staged
24242449
pub fn apply_update(
24252450
&mut self,
24262451
update: impl Into<Update>,
24272452
) -> Result<Vec<WalletEvent>, CannotConnectError> {
24282453
// snapshot of chain tip and transactions before update
24292454
let chain_tip1 = self.chain.tip().block_id();
2430-
let wallet_txs1 = self
2431-
.transactions()
2432-
.map(|wtx| {
2433-
(
2434-
wtx.tx_node.txid,
2435-
(wtx.tx_node.tx.clone(), wtx.chain_position),
2436-
)
2437-
})
2438-
.collect::<BTreeMap<Txid, (Arc<Transaction>, ChainPosition<ConfirmationBlockTime>)>>();
2455+
let wallet_txs1 = self.collect_wallet_txs();
24392456

24402457
// apply update
24412458
let update = update.into();
@@ -2454,15 +2471,7 @@ impl Wallet {
24542471

24552472
// chain tip and transactions after update
24562473
let chain_tip2 = self.chain.tip().block_id();
2457-
let wallet_txs2 = self
2458-
.transactions()
2459-
.map(|wtx| {
2460-
(
2461-
wtx.tx_node.txid,
2462-
(wtx.tx_node.tx.clone(), wtx.chain_position),
2463-
)
2464-
})
2465-
.collect::<BTreeMap<Txid, (Arc<Transaction>, ChainPosition<ConfirmationBlockTime>)>>();
2474+
let wallet_txs2 = self.collect_wallet_txs();
24662475

24672476
Ok(wallet_events(
24682477
self,
@@ -2557,7 +2566,9 @@ impl Wallet {
25572566
/// if you need the inserted block data to be reloaded after closing the wallet.
25582567
/// See [`Wallet::reveal_next_address`].
25592568
///
2560-
/// See [`apply_update_events`] for more information on the returned [`WalletEvent`]s.
2569+
/// See [`apply_update`] for more information on the returned [`WalletEvent`]s.
2570+
///
2571+
/// [`apply_update`]: Wallet::apply_update
25612572
pub fn apply_block_connected_to(
25622573
&mut self,
25632574
block: &Block,
@@ -2566,15 +2577,7 @@ impl Wallet {
25662577
) -> Result<Vec<WalletEvent>, ApplyHeaderError> {
25672578
// snapshot of chain tip and transactions before update
25682579
let chain_tip1 = self.chain.tip().block_id();
2569-
let wallet_txs1 = self
2570-
.transactions()
2571-
.map(|wtx| {
2572-
(
2573-
wtx.tx_node.txid,
2574-
(wtx.tx_node.tx.clone(), wtx.chain_position),
2575-
)
2576-
})
2577-
.collect::<BTreeMap<Txid, (Arc<Transaction>, ChainPosition<ConfirmationBlockTime>)>>();
2580+
let wallet_txs1 = self.collect_wallet_txs();
25782581

25792582
// apply block to wallet
25802583
let mut changeset = ChangeSet::default();
@@ -2592,15 +2595,7 @@ impl Wallet {
25922595

25932596
// chain tip and transactions after update
25942597
let chain_tip2 = self.chain.tip().block_id();
2595-
let wallet_txs2 = self
2596-
.transactions()
2597-
.map(|wtx| {
2598-
(
2599-
wtx.tx_node.txid,
2600-
(wtx.tx_node.tx.clone(), wtx.chain_position),
2601-
)
2602-
})
2603-
.collect::<BTreeMap<Txid, (Arc<Transaction>, ChainPosition<ConfirmationBlockTime>)>>();
2598+
let wallet_txs2 = self.collect_wallet_txs();
26042599

26052600
Ok(wallet_events(
26062601
self,
@@ -2611,13 +2606,30 @@ impl Wallet {
26112606
))
26122607
}
26132608

2609+
/// Collects all canonical wallet transactions into a map.
2610+
///
2611+
/// This method is primarily used internally to create before/after snapshots when applying
2612+
/// updates or blocks to the wallet.
2613+
fn collect_wallet_txs(
2614+
&self,
2615+
) -> BTreeMap<Txid, (Arc<Transaction>, ChainPosition<ConfirmationBlockTime>)> {
2616+
self.transactions()
2617+
.map(|wtx| {
2618+
(
2619+
wtx.tx_node.txid,
2620+
(wtx.tx_node.tx.clone(), wtx.chain_position),
2621+
)
2622+
})
2623+
.collect()
2624+
}
2625+
26142626
/// Apply relevant unconfirmed transactions to the wallet.
26152627
///
26162628
/// Transactions that are not relevant are filtered out.
26172629
///
26182630
/// This method takes in an iterator of `(tx, last_seen)` where `last_seen` is the timestamp of
26192631
/// when the transaction was last seen in the mempool. This is used for conflict resolution
2620-
/// when there is conflicting unconfirmed transactions. The transaction with the later
2632+
/// when there are conflicting unconfirmed transactions. The transaction with the later
26212633
/// `last_seen` is prioritized.
26222634
///
26232635
/// **WARNING**: You must persist the changes resulting from one or more calls to this method

0 commit comments

Comments
 (0)