Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
docs(chain): Tighten CanonicalView documentation
  • Loading branch information
evanlinjin committed Sep 17, 2025
commit 40790d00600c8601f5e15b51ec39fccbc8a539bd
129 changes: 6 additions & 123 deletions crates/chain/src/canonical_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,31 +81,9 @@ impl<A: Anchor> CanonicalView<A> {
/// This constructor analyzes the given [`TxGraph`] and creates a canonical view of all
/// transactions, resolving conflicts and ordering them according to their chain position.
///
/// # Arguments
///
/// * `tx_graph` - The transaction graph containing all known transactions
/// * `chain` - A chain oracle for determining block inclusion
/// * `chain_tip` - The current chain tip to use for canonicalization
/// * `params` - Parameters controlling the canonicalization process
///
/// # Returns
///
/// Returns `Ok(CanonicalView)` on success, or an error if the chain oracle fails.
///
/// # Example
///
/// ```
/// # use bdk_chain::{CanonicalView, TxGraph, CanonicalizationParams, local_chain::LocalChain};
/// # use bdk_core::BlockId;
/// # use bitcoin::hashes::Hash;
/// # let tx_graph = TxGraph::<BlockId>::default();
/// # let chain = LocalChain::from_blocks([(0, bitcoin::BlockHash::all_zeros())].into_iter().collect()).unwrap();
/// let chain_tip = chain.tip().block_id();
/// let params = CanonicalizationParams::default();
///
/// let view = CanonicalView::new(&tx_graph, &chain, chain_tip, params)?;
/// # Ok::<_, Box<dyn std::error::Error>>(())
/// ```
pub fn new<'g, C>(
tx_graph: &'g TxGraph<A>,
chain: &'g C,
Expand Down Expand Up @@ -213,21 +191,6 @@ impl<A: Anchor> CanonicalView<A> {
///
/// Returns `Some(CanonicalViewTx)` if the transaction exists in the canonical view,
/// or `None` if the transaction doesn't exist or was excluded due to conflicts.
///
/// # Example
///
/// ```
/// # use bdk_chain::{CanonicalView, TxGraph, local_chain::LocalChain};
/// # use bdk_core::BlockId;
/// # use bitcoin::hashes::Hash;
/// # let tx_graph = TxGraph::<BlockId>::default();
/// # let chain = LocalChain::from_blocks([(0, bitcoin::BlockHash::all_zeros())].into_iter().collect()).unwrap();
/// # let view = CanonicalView::new(&tx_graph, &chain, chain.tip().block_id(), Default::default()).unwrap();
/// # let txid = bitcoin::Txid::all_zeros();
/// if let Some(canonical_tx) = view.tx(txid) {
/// println!("Found tx {} at position {:?}", canonical_tx.txid, canonical_tx.pos);
/// }
/// ```
pub fn tx(&self, txid: Txid) -> Option<CanonicalTx<A>> {
self.txs
.get(&txid)
Expand All @@ -244,25 +207,6 @@ impl<A: Anchor> CanonicalView<A> {
/// - The transaction doesn't exist in the canonical view
/// - The output index is out of bounds
/// - The transaction was excluded due to conflicts
///
/// # Example
///
/// ```
/// # use bdk_chain::{CanonicalView, TxGraph, local_chain::LocalChain};
/// # use bdk_core::BlockId;
/// # use bitcoin::{OutPoint, hashes::Hash};
/// # let tx_graph = TxGraph::<BlockId>::default();
/// # let chain = LocalChain::from_blocks([(0, bitcoin::BlockHash::all_zeros())].into_iter().collect()).unwrap();
/// # let view = CanonicalView::new(&tx_graph, &chain, chain.tip().block_id(), Default::default()).unwrap();
/// # let outpoint = OutPoint::default();
/// if let Some(txout) = view.txout(outpoint) {
/// if txout.spent_by.is_some() {
/// println!("Output is spent");
/// } else {
/// println!("Output is unspent with value: {}", txout.txout.value);
/// }
/// }
/// ```
pub fn txout(&self, op: OutPoint) -> Option<FullTxOut<A>> {
let (tx, pos) = self.txs.get(&op.txid)?;
let vout: usize = op.vout.try_into().ok()?;
Expand Down Expand Up @@ -315,12 +259,8 @@ impl<A: Anchor> CanonicalView<A> {
/// of `(identifier, full_txout)` pairs for outpoints that exist in the canonical view.
/// Non-existent outpoints are silently filtered out.
///
/// The identifier type `O` can be any cloneable type and is passed through unchanged.
/// This is useful for tracking which outpoints correspond to which addresses or keys.
///
/// # Arguments
///
/// * `outpoints` - An iterator of `(identifier, outpoint)` pairs to look up
/// The identifier type `O` is useful for tracking which outpoints correspond to which addresses
/// or keys.
///
/// # Example
///
Expand All @@ -333,7 +273,7 @@ impl<A: Anchor> CanonicalView<A> {
/// # let view = CanonicalView::new(&tx_graph, &chain, chain.tip().block_id(), Default::default()).unwrap();
/// # let indexer = KeychainTxOutIndex::<&str>::default();
/// // Get all outputs from an indexer
/// for (keychain, txout) in view.filter_outpoints(indexer.outpoints().into_iter().map(|(k, op)| (k.clone(), *op))) {
/// for (keychain, txout) in view.filter_outpoints(indexer.outpoints().clone()) {
/// println!("{}: {} sats", keychain.0, txout.txout.value);
/// }
/// ```
Expand All @@ -351,10 +291,6 @@ impl<A: Anchor> CanonicalView<A> {
/// Similar to [`filter_outpoints`](Self::filter_outpoints), but only returns outputs that
/// have not been spent. This is useful for finding available UTXOs for spending.
///
/// # Arguments
///
/// * `outpoints` - An iterator of `(identifier, outpoint)` pairs to look up
///
/// # Example
///
/// ```
Expand All @@ -366,7 +302,7 @@ impl<A: Anchor> CanonicalView<A> {
/// # let view = CanonicalView::new(&tx_graph, &chain, chain.tip().block_id(), Default::default()).unwrap();
/// # let indexer = KeychainTxOutIndex::<&str>::default();
/// // Get unspent outputs (UTXOs) from an indexer
/// for (keychain, utxo) in view.filter_unspent_outpoints(indexer.outpoints().into_iter().map(|(k, op)| (k.clone(), *op))) {
/// for (keychain, utxo) in view.filter_unspent_outpoints(indexer.outpoints().clone()) {
/// println!("{} UTXO: {} sats", keychain.0, utxo.txout.value);
/// }
/// ```
Expand Down Expand Up @@ -395,25 +331,12 @@ impl<A: Anchor> CanonicalView<A> {
///
/// # Minimum Confirmations
///
/// The `min_confirmations` parameter controls when outputs are considered confirmed:
///
/// - `0` or `1`: Standard behavior - require at least 1 confirmation
/// - `6`: Conservative - require 6 confirmations (often used for high-value transactions)
/// - `100+`: May be used for coinbase outputs which require 100 confirmations
/// The `min_confirmations` parameter controls when outputs are considered confirmed. A
/// `min_confirmations` value of `0` is equivalent to `1` (require at least 1 confirmation).
///
/// Outputs with fewer than `min_confirmations` are categorized as pending (trusted or
/// untrusted based on the trust predicate).
///
/// # Balance Categories
///
/// The returned [`Balance`] contains four categories:
///
/// - `confirmed`: Outputs with ≥ `min_confirmations` and spendable
/// - `trusted_pending`: Unconfirmed or insufficiently confirmed outputs from trusted scripts
/// - `untrusted_pending`: Unconfirmed or insufficiently confirmed outputs from untrusted
/// scripts
/// - `immature`: Coinbase outputs that haven't reached maturity (100 confirmations)
///
/// # Example
///
/// ```
Expand All @@ -430,13 +353,6 @@ impl<A: Anchor> CanonicalView<A> {
/// |_keychain, _script| true, // Trust all outputs
/// 6, // Require 6 confirmations
/// );
///
/// // Or calculate balance trusting no outputs
/// let untrusted_balance = view.balance(
/// indexer.outpoints().into_iter().map(|(k, op)| (k.clone(), *op)),
/// |_keychain, _script| false, // Trust no outputs
/// 1,
/// );
/// ```
pub fn balance<'v, O: Clone + 'v>(
&'v self,
Expand Down Expand Up @@ -498,39 +414,6 @@ impl<A: Anchor> CanonicalView<A> {
/// commonly used with
/// [`SyncRequestBuilder::expected_spk_txids`](bdk_core::spk_client::SyncRequestBuilder::expected_spk_txids)
/// to inform sync operations about known transactions.
///
/// # Arguments
///
/// * `indexer` - A script pubkey indexer (e.g., `KeychainTxOutIndex`) that tracks which scripts
/// are relevant
/// * `spk_index_range` - A range bound to constrain which script indices to include. Use `..`
/// for all indices.
///
/// # Returns
///
/// An iterator of `(script_pubkey, txid)` pairs for all canonical transactions that involve
/// the specified scripts.
///
/// # Example
///
/// ```
/// # use bdk_chain::{CanonicalView, TxGraph, local_chain::LocalChain, spk_txout::SpkTxOutIndex};
/// # use bdk_core::BlockId;
/// # use bitcoin::hashes::Hash;
/// # let tx_graph = TxGraph::<BlockId>::default();
/// # let chain = LocalChain::from_blocks([(0, bitcoin::BlockHash::all_zeros())].into_iter().collect()).unwrap();
/// # let view = CanonicalView::new(&tx_graph, &chain, chain.tip().block_id(), Default::default()).unwrap();
/// # let indexer = SpkTxOutIndex::<u32>::default();
/// // List all expected transactions for script indices 0-100
/// for (script, txid) in view.list_expected_spk_txids(&indexer, 0..100) {
/// println!("Script {:?} appears in transaction {}", script, txid);
/// }
///
/// // List all expected transactions (no range constraint)
/// for (script, txid) in view.list_expected_spk_txids(&indexer, ..) {
/// println!("Found transaction {} for script", txid);
/// }
/// ```
pub fn list_expected_spk_txids<'v, I>(
&'v self,
indexer: &'v impl AsRef<SpkTxOutIndex<I>>,
Expand Down