]> Untitled Git - bdk/commitdiff
refactor(chain)!: split canonicalization into two tasks with generic `Canonical<A...
author志宇 <hello@evanlinjin.me>
Fri, 13 Feb 2026 12:57:39 +0000 (12:57 +0000)
committer志宇 <hello@evanlinjin.me>
Sat, 13 Jun 2026 20:24:01 +0000 (20:24 +0000)
Separate concerns by splitting `CanonicalizationTask` into two phases:

1. `CanonicalTask` determines which transactions are canonical and why
   (`CanonicalReason`), outputting `CanonicalTxs<A>`.
2. `CanonicalViewTask` resolves reasons into `ChainPosition`s (confirmed
   vs unconfirmed), outputting `CanonicalView<A>`.

Make `Canonical<A, P>`, `CanonicalTx<P>`, and `FullTxOut<P>` generic over
the position type so the same structs serve both phases. Add
`LocalChain::canonical_view()` convenience method for the common two-step
pipeline.

Renames:

- `CanonicalizationTask` -> `CanonicalTask`
- `CanonicalizationParams` -> `CanonicalParams`
- `canonicalization_task()` -> `canonical_task()`
- `FullTxOut::chain_position` -> `FullTxOut::pos`

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
crates/chain/src/canonical_task.rs
crates/chain/src/canonical_view.rs
crates/chain/src/chain_data.rs
crates/chain/src/indexed_tx_graph.rs
crates/chain/src/local_chain.rs
crates/chain/src/tx_graph.rs
crates/chain/tests/common/tx_template.rs
crates/chain/tests/test_indexed_tx_graph.rs

index 4a2347c3677ef231e262905066b4ed8ddfad90c4..8f4ce48f3d6e4ded32bb6d5529b4d043e402ef00 100644 (file)
@@ -1,6 +1,6 @@
 use crate::collections::{HashMap, HashSet, VecDeque};
 use crate::tx_graph::{TxAncestors, TxDescendants};
-use crate::{Anchor, CanonicalView, ChainPosition, TxGraph};
+use crate::{Anchor, CanonicalTxs, CanonicalView, ChainPosition, TxGraph};
 use alloc::boxed::Box;
 use alloc::collections::BTreeSet;
 use alloc::sync::Arc;
@@ -23,8 +23,6 @@ enum CanonicalStage {
     SeenTxs,
     /// Processing leftover transactions.
     LeftOverTxs,
-    /// Processing transitively anchored transactions.
-    TransitivelyAnchoredTxs,
     /// All processing is complete.
     Finished,
 }
@@ -35,8 +33,7 @@ impl CanonicalStage {
             CanonicalStage::AssumedTxs => Self::AnchoredTxs,
             CanonicalStage::AnchoredTxs => Self::SeenTxs,
             CanonicalStage::SeenTxs => Self::LeftOverTxs,
-            CanonicalStage::LeftOverTxs => Self::TransitivelyAnchoredTxs,
-            CanonicalStage::TransitivelyAnchoredTxs => Self::Finished,
+            CanonicalStage::LeftOverTxs => Self::Finished,
             CanonicalStage::Finished => Self::Finished,
         };
     }
@@ -44,7 +41,7 @@ impl CanonicalStage {
 
 /// Modifies the canonicalization algorithm.
 #[derive(Debug, Default, Clone)]
-pub struct CanonicalizationParams {
+pub struct CanonicalParams {
     /// Transactions that will supersede all other transactions.
     ///
     /// In case of conflicting transactions within `assume_canonical`, transactions that appear
@@ -52,8 +49,14 @@ pub struct CanonicalizationParams {
     pub assume_canonical: Vec<Txid>,
 }
 
-/// Manages the canonicalization process without direct I/O operations.
-pub struct CanonicalizationTask<'g, A> {
+/// Determines which transactions are canonical without resolving chain positions.
+///
+/// This task implements the first phase of canonicalization: it walks the transaction
+/// graph and determines which transactions are canonical (non-conflicting) and why
+/// (via [`CanonicalReason`]). The output is a [`CanonicalTxs`] which can then be
+/// further processed by [`CanonicalViewTask`] to resolve reasons into
+/// [`ChainPosition`]s.
+pub struct CanonicalTask<'g, A> {
     tx_graph: &'g TxGraph<A>,
     chain_tip: BlockId,
 
@@ -61,7 +64,6 @@ pub struct CanonicalizationTask<'g, A> {
     unprocessed_anchored_txs: VecDeque<(Txid, Arc<Transaction>, &'g BTreeSet<A>)>,
     unprocessed_seen_txs: Box<dyn Iterator<Item = (Txid, Arc<Transaction>, u64)> + 'g>,
     unprocessed_leftover_txs: VecDeque<(Txid, Arc<Transaction>, u32)>,
-    unprocessed_transitively_anchored_txs: VecDeque<(Txid, Arc<Transaction>, &'g BTreeSet<A>)>,
 
     canonical: CanonicalMap<A>,
     not_canonical: NotCanonicalSet,
@@ -69,15 +71,12 @@ pub struct CanonicalizationTask<'g, A> {
     // Store canonical transactions in order
     canonical_order: Vec<Txid>,
 
-    // Track which transactions have direct anchors (not transitive)
-    direct_anchors: HashMap<Txid, A>,
-
     // Track the current stage of processing
     current_stage: CanonicalStage,
 }
 
-impl<'g, A: Anchor> ChainQuery for CanonicalizationTask<'g, A> {
-    type Output = CanonicalView<A>;
+impl<'g, A: Anchor> ChainQuery for CanonicalTask<'g, A> {
+    type Output = CanonicalTxs<A>;
 
     fn tip(&self) -> BlockId {
         self.chain_tip
@@ -131,15 +130,6 @@ impl<'g, A: Anchor> ChainQuery for CanonicalizationTask<'g, A> {
                         continue;
                     }
                 }
-                CanonicalStage::TransitivelyAnchoredTxs => {
-                    if let Some((_txid, _, anchors)) =
-                        self.unprocessed_transitively_anchored_txs.front()
-                    {
-                        let block_ids =
-                            anchors.iter().map(|anchor| anchor.anchor_block()).collect();
-                        return Some(block_ids);
-                    }
-                }
                 CanonicalStage::Finished => return None,
             }
 
@@ -148,9 +138,6 @@ impl<'g, A: Anchor> ChainQuery for CanonicalizationTask<'g, A> {
     }
 
     fn resolve_query(&mut self, response: ChainResponse) {
-        // Only AnchoredTxs and TransitivelyAnchoredTxs stages should receive query
-        // responses Other stages don't generate queries and thus shouldn't call
-        // resolve_query
         match self.current_stage {
             CanonicalStage::AnchoredTxs => {
                 // Process directly anchored transaction response
@@ -166,7 +153,6 @@ impl<'g, A: Anchor> ChainQuery for CanonicalizationTask<'g, A> {
                     match best_anchor {
                         Some(best_anchor) => {
                             // Transaction has a confirmed anchor
-                            self.direct_anchors.insert(txid, best_anchor.clone());
                             if !self.is_canonicalized(txid) {
                                 self.mark_canonical(
                                     txid,
@@ -193,28 +179,6 @@ impl<'g, A: Anchor> ChainQuery for CanonicalizationTask<'g, A> {
                     }
                 }
             }
-            CanonicalStage::TransitivelyAnchoredTxs => {
-                // Process transitively anchored transaction response
-                if let Some((txid, _tx, anchors)) =
-                    self.unprocessed_transitively_anchored_txs.pop_front()
-                {
-                    // Find the anchor that matches the confirmed BlockId
-                    let best_anchor = response.and_then(|block_id| {
-                        anchors
-                            .iter()
-                            .find(|anchor| anchor.anchor_block() == block_id)
-                            .cloned()
-                    });
-
-                    if let Some(best_anchor) = best_anchor {
-                        // Found a confirmed anchor for this transitively anchored transaction
-                        self.direct_anchors.insert(txid, best_anchor.clone());
-                        // Note: We don't re-mark as canonical since it's already marked
-                        // from being transitively anchored by its descendant
-                    }
-                    // If no confirmed anchor, we keep the transitive canonicalization status
-                }
-            }
             CanonicalStage::AssumedTxs
             | CanonicalStage::SeenTxs
             | CanonicalStage::LeftOverTxs
@@ -230,7 +194,6 @@ impl<'g, A: Anchor> ChainQuery for CanonicalizationTask<'g, A> {
     }
 
     fn finish(self) -> Self::Output {
-        // Build the canonical view
         let mut view_order = Vec::new();
         let mut view_txs = HashMap::new();
         let mut view_spends = HashMap::new();
@@ -246,76 +209,17 @@ impl<'g, A: Anchor> ChainQuery for CanonicalizationTask<'g, A> {
                     }
                 }
 
-                // Get transaction node for first_seen/last_seen info
-                let tx_node = match self.tx_graph.get_tx_node(*txid) {
-                    Some(tx_node) => tx_node,
-                    None => {
-                        debug_assert!(false, "tx node must exist!");
-                        continue;
-                    }
-                };
-
-                // Determine chain position based on reason
-                let chain_position = match reason {
-                    CanonicalReason::Assumed { descendant } => match descendant {
-                        Some(_) => match self.direct_anchors.get(txid) {
-                            Some(anchor) => ChainPosition::Confirmed {
-                                anchor,
-                                transitively: None,
-                            },
-                            None => ChainPosition::Unconfirmed {
-                                first_seen: tx_node.first_seen,
-                                last_seen: tx_node.last_seen,
-                            },
-                        },
-                        None => ChainPosition::Unconfirmed {
-                            first_seen: tx_node.first_seen,
-                            last_seen: tx_node.last_seen,
-                        },
-                    },
-                    CanonicalReason::Anchor { anchor, descendant } => match descendant {
-                        Some(_) => match self.direct_anchors.get(txid) {
-                            Some(anchor) => ChainPosition::Confirmed {
-                                anchor,
-                                transitively: None,
-                            },
-                            None => ChainPosition::Confirmed {
-                                anchor,
-                                transitively: *descendant,
-                            },
-                        },
-                        None => ChainPosition::Confirmed {
-                            anchor,
-                            transitively: None,
-                        },
-                    },
-                    CanonicalReason::ObservedIn { observed_in, .. } => match observed_in {
-                        ObservedIn::Mempool(last_seen) => ChainPosition::Unconfirmed {
-                            first_seen: tx_node.first_seen,
-                            last_seen: Some(*last_seen),
-                        },
-                        ObservedIn::Block(_) => ChainPosition::Unconfirmed {
-                            first_seen: tx_node.first_seen,
-                            last_seen: None,
-                        },
-                    },
-                };
-
-                view_txs.insert(*txid, (tx.clone(), chain_position.cloned()));
+                view_txs.insert(*txid, (tx.clone(), reason.clone()));
             }
         }
 
-        CanonicalView::new(self.chain_tip, view_order, view_txs, view_spends)
+        CanonicalTxs::new(self.chain_tip, view_order, view_txs, view_spends)
     }
 }
 
-impl<'g, A: Anchor> CanonicalizationTask<'g, A> {
+impl<'g, A: Anchor> CanonicalTask<'g, A> {
     /// Creates a new canonicalization task.
-    pub fn new(
-        tx_graph: &'g TxGraph<A>,
-        chain_tip: BlockId,
-        params: CanonicalizationParams,
-    ) -> Self {
+    pub fn new(tx_graph: &'g TxGraph<A>, chain_tip: BlockId, params: CanonicalParams) -> Self {
         let anchors = tx_graph.all_anchors();
         let unprocessed_assumed_txs = Box::new(
             params
@@ -342,13 +246,11 @@ impl<'g, A: Anchor> CanonicalizationTask<'g, A> {
             unprocessed_anchored_txs,
             unprocessed_seen_txs,
             unprocessed_leftover_txs: VecDeque::new(),
-            unprocessed_transitively_anchored_txs: VecDeque::new(),
 
             canonical: HashMap::new(),
             not_canonical: HashSet::new(),
 
             canonical_order: Vec::new(),
-            direct_anchors: HashMap::new(),
             current_stage: CanonicalStage::default(),
         }
     }
@@ -378,8 +280,6 @@ impl<'g, A: Anchor> CanonicalizationTask<'g, A> {
                     reason.clone()
                 } else {
                     // This is an ancestor being marked transitively
-                    // Check if it has its own anchor that needs to be verified later
-                    // We'll check anchors after marking it canonical
                     reason.to_transitive(starting_txid)
                 };
 
@@ -435,27 +335,199 @@ impl<'g, A: Anchor> CanonicalizationTask<'g, A> {
         }
 
         // Add to canonical order
-        for (txid, tx, reason) in &staged_canonical {
+        for (txid, _, _) in &staged_canonical {
             self.canonical_order.push(*txid);
+        }
+    }
+}
+
+/// Represents the current stage of view task processing.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
+enum ViewStage {
+    /// Processing transactions to resolve their chain positions.
+    #[default]
+    ResolvingPositions,
+    /// All processing is complete.
+    Finished,
+}
+
+/// Resolves [`CanonicalReason`]s into [`ChainPosition`]s.
+///
+/// This task implements the second phase of canonicalization: given a set of canonical
+/// transactions with their reasons (from [`CanonicalTask`]), it resolves each reason
+/// into a concrete [`ChainPosition`] (confirmed or unconfirmed). For transitively
+/// anchored transactions, it queries the chain to check if they have their own direct
+/// anchors.
+pub struct CanonicalViewTask<'g, A> {
+    tx_graph: &'g TxGraph<A>,
+    tip: BlockId,
+
+    /// Transactions in canonical order with their reasons.
+    canonical_order: Vec<Txid>,
+    canonical_txs: HashMap<Txid, (Arc<Transaction>, CanonicalReason<A>)>,
+    spends: HashMap<bitcoin::OutPoint, Txid>,
 
-            // ObservedIn transactions don't need anchor verification
-            if matches!(reason, CanonicalReason::ObservedIn { .. }) {
-                continue;
+    /// Transactions that need anchor verification (transitively anchored).
+    unprocessed_anchor_checks: VecDeque<(Txid, &'g BTreeSet<A>)>,
+
+    /// Resolved direct anchors for transitively anchored transactions.
+    direct_anchors: HashMap<Txid, A>,
+
+    current_stage: ViewStage,
+}
+
+impl<'g, A: Anchor> ChainQuery for CanonicalViewTask<'g, A> {
+    type Output = CanonicalView<A>;
+
+    fn tip(&self) -> BlockId {
+        self.tip
+    }
+
+    fn next_query(&mut self) -> Option<ChainRequest> {
+        loop {
+            match self.current_stage {
+                ViewStage::ResolvingPositions => {
+                    if let Some((_txid, anchors)) = self.unprocessed_anchor_checks.front() {
+                        let block_ids =
+                            anchors.iter().map(|anchor| anchor.anchor_block()).collect();
+                        return Some(block_ids);
+                    }
+                }
+                ViewStage::Finished => return None,
             }
 
-            // Check if this transaction was marked transitively and needs its own anchors verified
-            if reason.is_transitive() {
-                if let Some(anchors) = self.tx_graph.all_anchors().get(txid) {
-                    // only check anchors we haven't already confirmed
-                    if !self.direct_anchors.contains_key(txid) {
-                        self.unprocessed_transitively_anchored_txs.push_back((
-                            *txid,
-                            tx.clone(),
-                            anchors,
-                        ));
+            self.current_stage = ViewStage::Finished;
+        }
+    }
+
+    fn resolve_query(&mut self, response: ChainResponse) {
+        match self.current_stage {
+            ViewStage::ResolvingPositions => {
+                if let Some((txid, anchors)) = self.unprocessed_anchor_checks.pop_front() {
+                    let best_anchor = response.and_then(|block_id| {
+                        anchors
+                            .iter()
+                            .find(|anchor| anchor.anchor_block() == block_id)
+                            .cloned()
+                    });
+
+                    if let Some(best_anchor) = best_anchor {
+                        self.direct_anchors.insert(txid, best_anchor);
                     }
                 }
             }
+            ViewStage::Finished => {
+                debug_assert!(false, "resolve_query called in Finished stage");
+            }
+        }
+    }
+
+    fn finish(self) -> Self::Output {
+        let mut view_order = Vec::new();
+        let mut view_txs = HashMap::new();
+
+        for txid in &self.canonical_order {
+            if let Some((tx, reason)) = self.canonical_txs.get(txid) {
+                view_order.push(*txid);
+
+                // Get transaction node for first_seen/last_seen info
+                let tx_node = match self.tx_graph.get_tx_node(*txid) {
+                    Some(tx_node) => tx_node,
+                    None => {
+                        debug_assert!(false, "tx node must exist!");
+                        continue;
+                    }
+                };
+
+                // Determine chain position based on reason
+                let chain_position = match reason {
+                    CanonicalReason::Assumed { descendant } => match descendant {
+                        Some(_) => match self.direct_anchors.get(txid) {
+                            Some(anchor) => ChainPosition::Confirmed {
+                                anchor,
+                                transitively: None,
+                            },
+                            None => ChainPosition::Unconfirmed {
+                                first_seen: tx_node.first_seen,
+                                last_seen: tx_node.last_seen,
+                            },
+                        },
+                        None => ChainPosition::Unconfirmed {
+                            first_seen: tx_node.first_seen,
+                            last_seen: tx_node.last_seen,
+                        },
+                    },
+                    CanonicalReason::Anchor { anchor, descendant } => match descendant {
+                        Some(_) => match self.direct_anchors.get(txid) {
+                            Some(anchor) => ChainPosition::Confirmed {
+                                anchor,
+                                transitively: None,
+                            },
+                            None => ChainPosition::Confirmed {
+                                anchor,
+                                transitively: *descendant,
+                            },
+                        },
+                        None => ChainPosition::Confirmed {
+                            anchor,
+                            transitively: None,
+                        },
+                    },
+                    CanonicalReason::ObservedIn { observed_in, .. } => match observed_in {
+                        ObservedIn::Mempool(last_seen) => ChainPosition::Unconfirmed {
+                            first_seen: tx_node.first_seen,
+                            last_seen: Some(*last_seen),
+                        },
+                        ObservedIn::Block(_) => ChainPosition::Unconfirmed {
+                            first_seen: tx_node.first_seen,
+                            last_seen: None,
+                        },
+                    },
+                };
+
+                view_txs.insert(*txid, (tx.clone(), chain_position.cloned()));
+            }
+        }
+
+        CanonicalView::new(self.tip, view_order, view_txs, self.spends)
+    }
+}
+
+impl<A: Anchor> CanonicalTxs<A> {
+    /// Creates a [`CanonicalViewTask`] that resolves [`CanonicalReason`]s into [`ChainPosition`]s.
+    ///
+    /// This is the second phase of the canonicalization pipeline. The resulting task
+    /// queries the chain to verify anchors for transitively anchored transactions and
+    /// produces a [`CanonicalView`] with resolved chain positions.
+    pub fn view_task<'g>(self, tx_graph: &'g TxGraph<A>) -> CanonicalViewTask<'g, A> {
+        let all_anchors = tx_graph.all_anchors();
+
+        // Find transactions that need anchor verification
+        let mut unprocessed_anchor_checks = VecDeque::new();
+        for txid in &self.order {
+            if let Some((_, reason)) = self.txs.get(txid) {
+                // Skip ObservedIn transactions - they don't have anchors to verify
+                if matches!(reason, CanonicalReason::ObservedIn { .. }) {
+                    continue;
+                }
+                // Transitively anchored transactions need their own anchor checked
+                if reason.is_transitive() {
+                    if let Some(anchors) = all_anchors.get(txid) {
+                        unprocessed_anchor_checks.push_back((*txid, anchors));
+                    }
+                }
+            }
+        }
+
+        CanonicalViewTask {
+            tx_graph,
+            tip: self.tip,
+            canonical_order: self.order,
+            canonical_txs: self.txs,
+            spends: self.spends,
+            unprocessed_anchor_checks,
+            direct_anchors: HashMap::new(),
+            current_stage: ViewStage::default(),
         }
     }
 }
@@ -596,10 +668,12 @@ mod tests {
         };
         let _ = tx_graph.insert_anchor(txid, anchor);
 
-        // Create canonicalization task and canonicalize using the chain
-        let params = CanonicalizationParams::default();
-        let task = CanonicalizationTask::new(&tx_graph, chain_tip, params);
-        let canonical_view = chain.canonicalize(task);
+        // Create canonicalization task and canonicalize using the two-step pipeline
+        let params = CanonicalParams::default();
+        let task = CanonicalTask::new(&tx_graph, chain_tip, params);
+        let canonical_txs = chain.canonicalize(task);
+        let view_task = canonical_txs.view_task(&tx_graph);
+        let canonical_view = chain.canonicalize(view_task);
 
         // Should have one canonical transaction
         assert_eq!(canonical_view.txs().len(), 1);
index b89e9efe1dd57c894a066bc0cdc506be19ff9813..b1d9fda66c4a4b1ba0cee92bb05a3382c0f32a51 100644 (file)
@@ -6,14 +6,14 @@
 //! ## Example
 //!
 //! ```
-//! # use bdk_chain::{TxGraph, CanonicalizationParams, CanonicalizationTask, local_chain::LocalChain};
+//! # use bdk_chain::{TxGraph, CanonicalParams, CanonicalTask, 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 task = CanonicalizationTask::new(&tx_graph, chain_tip, params);
+//! let params = CanonicalParams::default();
+//! let task = CanonicalTask::new(&tx_graph, chain_tip, params);
 //! let view = chain.canonicalize(task);
 //!
 //! // Iterate over canonical transactions
@@ -33,25 +33,27 @@ use bitcoin::{Amount, OutPoint, ScriptBuf, Transaction, Txid};
 
 use crate::{spk_txout::SpkTxOutIndex, Anchor, Balance, ChainPosition, FullTxOut};
 
-/// A single canonical transaction with its chain position.
+/// A single canonical transaction with its position.
 ///
 /// This struct represents a transaction that has been determined to be canonical (not
-/// conflicted). It includes the transaction itself along with its position in the chain (confirmed
-/// or unconfirmed).
+/// conflicted). It includes the transaction itself along with its position information.
+/// The position type `P` is generic - it can be [`ChainPosition`] for resolved views,
+/// or [`CanonicalReason`](crate::canonical_task::CanonicalReason) for unresolved canonicalization
+/// results.
 #[derive(Clone, Debug, PartialEq, Eq)]
-pub struct CanonicalTx<A> {
-    /// The position of this transaction in the chain.
+pub struct CanonicalTx<P> {
+    /// The position of this transaction.
     ///
-    /// This indicates whether the transaction is confirmed (and at what height) or
-    /// unconfirmed (most likely pending in the mempool).
-    pub pos: ChainPosition<A>,
+    /// When `P` is [`ChainPosition`], this indicates whether the transaction is confirmed
+    /// (and at what height) or unconfirmed (most likely pending in the mempool).
+    pub pos: P,
     /// The transaction ID (hash) of this transaction.
     pub txid: Txid,
     /// The full transaction.
     pub tx: Arc<Transaction>,
 }
 
-impl<A: Ord> Ord for CanonicalTx<A> {
+impl<P: Ord> Ord for CanonicalTx<P> {
     fn cmp(&self, other: &Self) -> core::cmp::Ordering {
         self.pos
             .cmp(&other.pos)
@@ -60,45 +62,61 @@ impl<A: Ord> Ord for CanonicalTx<A> {
     }
 }
 
-impl<A: Ord> PartialOrd for CanonicalTx<A> {
+impl<P: Ord> PartialOrd for CanonicalTx<P> {
     fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
         Some(self.cmp(other))
     }
 }
 
-/// A view of canonical transactions from a [`TxGraph`].
+/// Canonical set of transactions from a [`TxGraph`].
 ///
-/// `CanonicalView` provides an ordered, conflict-resolved view of transactions. It determines
+/// `Canonical` provides a conflict-resolved list of transactions. It determines
 /// which transactions are canonical (non-conflicted) based on the current chain state and
 /// provides methods to query transaction data, unspent outputs, and balances.
 ///
+/// The position type `P` is generic:
+/// - [`ChainPosition<A>`] for resolved views (aka [`CanonicalView`])
+/// - [`CanonicalReason<A>`](crate::canonical_task::CanonicalReason) for unresolved results (aka
+///   [`CanonicalTxs`])
+///
 /// The view maintains:
-/// - An ordered list of canonical transactions in topological-spending order
+/// - A list of canonical transactions
 /// - A mapping of outpoints to the transactions that spend them
 /// - The chain tip used for canonicalization
+///
+/// [`TxGraph`]: crate::TxGraph
 #[derive(Debug)]
-pub struct CanonicalView<A> {
-    /// Ordered list of transaction IDs in in topological-spending order.
-    order: Vec<Txid>,
-    /// Map of transaction IDs to their transaction data and chain position.
-    txs: HashMap<Txid, (Arc<Transaction>, ChainPosition<A>)>,
+pub struct Canonical<A, P> {
+    /// List of canonical transaction IDs.
+    pub(crate) order: Vec<Txid>,
+    /// Map of transaction IDs to their transaction data and position.
+    pub(crate) txs: HashMap<Txid, (Arc<Transaction>, P)>,
     /// Map of outpoints to the transaction ID that spends them.
-    spends: HashMap<OutPoint, Txid>,
+    pub(crate) spends: HashMap<OutPoint, Txid>,
     /// The chain tip at the time this view was created.
-    tip: BlockId,
+    pub(crate) tip: BlockId,
+    /// Marker for the anchor type.
+    pub(crate) _anchor: core::marker::PhantomData<A>,
 }
 
-impl<A: Anchor> CanonicalView<A> {
-    /// Creates a [`CanonicalView`] from its constituent parts.
+/// Type alias for canonical transactions with resolved [`ChainPosition`]s.
+pub type CanonicalView<A> = Canonical<A, ChainPosition<A>>;
+
+/// Type alias for canonical transactions with unresolved
+/// [`CanonicalReason`](crate::canonical_task::CanonicalReason)s.
+pub type CanonicalTxs<A> = Canonical<A, crate::canonical_task::CanonicalReason<A>>;
+
+impl<A, P: Clone> Canonical<A, P> {
+    /// Creates a [`Canonical`] from its constituent parts.
     ///
-    /// This internal constructor is used by [`CanonicalizationTask`] to build the view
+    /// This internal constructor is used by [`CanonicalTask`] to build the canonical set
     /// after completing the canonicalization process. It takes the processed transaction
-    /// data including the canonical ordering, transaction map with chain positions, and
+    /// data including the canonical ordering, transaction map with positions, and
     /// spend information.
     pub(crate) fn new(
         tip: BlockId,
         order: Vec<Txid>,
-        txs: HashMap<Txid, (Arc<Transaction>, ChainPosition<A>)>,
+        txs: HashMap<Txid, (Arc<Transaction>, P)>,
         spends: HashMap<OutPoint, Txid>,
     ) -> Self {
         Self {
@@ -106,14 +124,20 @@ impl<A: Anchor> CanonicalView<A> {
             order,
             txs,
             spends,
+            _anchor: core::marker::PhantomData,
         }
     }
 
+    /// Get the chain tip used to construct this canonical set.
+    pub fn tip(&self) -> BlockId {
+        self.tip
+    }
+
     /// Get a single canonical transaction by its transaction ID.
     ///
-    /// Returns `Some(CanonicalViewTx)` if the transaction exists in the canonical view,
+    /// Returns `Some(CanonicalTx)` if the transaction exists in the canonical set,
     /// or `None` if the transaction doesn't exist or was excluded due to conflicts.
-    pub fn tx(&self, txid: Txid) -> Option<CanonicalTx<A>> {
+    pub fn tx(&self, txid: Txid) -> Option<CanonicalTx<P>> {
         self.txs
             .get(&txid)
             .cloned()
@@ -126,10 +150,10 @@ impl<A: Anchor> CanonicalView<A> {
     /// spent and by which transaction.
     ///
     /// Returns `None` if:
-    /// - The transaction doesn't exist in the canonical view
+    /// - The transaction doesn't exist in the canonical set
     /// - The output index is out of bounds
     /// - The transaction was excluded due to conflicts
-    pub fn txout(&self, op: OutPoint) -> Option<FullTxOut<A>> {
+    pub fn txout(&self, op: OutPoint) -> Option<FullTxOut<P>> {
         let (tx, pos) = self.txs.get(&op.txid)?;
         let vout: usize = op.vout.try_into().ok()?;
         let txout = tx.output.get(vout)?;
@@ -138,7 +162,7 @@ impl<A: Anchor> CanonicalView<A> {
             (spent_by_pos.clone(), *spent_by_txid)
         });
         Some(FullTxOut {
-            chain_position: pos.clone(),
+            pos: pos.clone(),
             outpoint: op,
             txout: txout.clone(),
             spent_by,
@@ -154,13 +178,13 @@ impl<A: Anchor> CanonicalView<A> {
     /// # Example
     ///
     /// ```
-    /// # use bdk_chain::{TxGraph, CanonicalizationTask, local_chain::LocalChain};
+    /// # use bdk_chain::{TxGraph, CanonicalTask, 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 task = CanonicalizationTask::new(&tx_graph, chain_tip, Default::default());
+    /// # let task = CanonicalTask::new(&tx_graph, chain_tip, Default::default());
     /// # let view = chain.canonicalize(task);
     /// // Iterate over all canonical transactions
     /// for tx in view.txs() {
@@ -170,7 +194,7 @@ impl<A: Anchor> CanonicalView<A> {
     /// // Get the total number of canonical transactions
     /// println!("Total canonical transactions: {}", view.txs().len());
     /// ```
-    pub fn txs(&self) -> impl ExactSizeIterator<Item = CanonicalTx<A>> + DoubleEndedIterator + '_ {
+    pub fn txs(&self) -> impl ExactSizeIterator<Item = CanonicalTx<P>> + DoubleEndedIterator + '_ {
         self.order.iter().map(|&txid| {
             let (tx, pos) = self.txs[&txid].clone();
             CanonicalTx { pos, txid, tx }
@@ -180,7 +204,7 @@ impl<A: Anchor> CanonicalView<A> {
     /// Get a filtered list of outputs from the given outpoints.
     ///
     /// This method takes an iterator of `(identifier, outpoint)` pairs and returns an iterator
-    /// of `(identifier, full_txout)` pairs for outpoints that exist in the canonical view.
+    /// of `(identifier, full_txout)` pairs for outpoints that exist in the canonical set.
     /// Non-existent outpoints are silently filtered out.
     ///
     /// The identifier type `O` is useful for tracking which outpoints correspond to which addresses
@@ -189,13 +213,13 @@ impl<A: Anchor> CanonicalView<A> {
     /// # Example
     ///
     /// ```
-    /// # use bdk_chain::{TxGraph, CanonicalizationTask, local_chain::LocalChain, keychain_txout::KeychainTxOutIndex};
+    /// # use bdk_chain::{TxGraph, CanonicalTask, local_chain::LocalChain, keychain_txout::KeychainTxOutIndex};
     /// # 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 task = CanonicalizationTask::new(&tx_graph, chain_tip, Default::default());
+    /// # let task = CanonicalTask::new(&tx_graph, chain_tip, Default::default());
     /// # let view = chain.canonicalize(task);
     /// # let indexer = KeychainTxOutIndex::<&str>::default();
     /// // Get all outputs from an indexer
@@ -206,7 +230,7 @@ impl<A: Anchor> CanonicalView<A> {
     pub fn filter_outpoints<'v, O: Clone + 'v>(
         &'v self,
         outpoints: impl IntoIterator<Item = (O, OutPoint)> + 'v,
-    ) -> impl Iterator<Item = (O, FullTxOut<A>)> + 'v {
+    ) -> impl Iterator<Item = (O, FullTxOut<P>)> + 'v {
         outpoints
             .into_iter()
             .filter_map(|(op_i, op)| Some((op_i, self.txout(op)?)))
@@ -220,13 +244,13 @@ impl<A: Anchor> CanonicalView<A> {
     /// # Example
     ///
     /// ```
-    /// # use bdk_chain::{TxGraph, CanonicalizationTask, local_chain::LocalChain, keychain_txout::KeychainTxOutIndex};
+    /// # use bdk_chain::{TxGraph, CanonicalTask, local_chain::LocalChain, keychain_txout::KeychainTxOutIndex};
     /// # 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 task = CanonicalizationTask::new(&tx_graph, chain_tip, Default::default());
+    /// # let task = CanonicalTask::new(&tx_graph, chain_tip, Default::default());
     /// # let view = chain.canonicalize(task);
     /// # let indexer = KeychainTxOutIndex::<&str>::default();
     /// // Get unspent outputs (UTXOs) from an indexer
@@ -237,11 +261,40 @@ impl<A: Anchor> CanonicalView<A> {
     pub fn filter_unspent_outpoints<'v, O: Clone + 'v>(
         &'v self,
         outpoints: impl IntoIterator<Item = (O, OutPoint)> + 'v,
-    ) -> impl Iterator<Item = (O, FullTxOut<A>)> + 'v {
+    ) -> impl Iterator<Item = (O, FullTxOut<P>)> + 'v {
         self.filter_outpoints(outpoints)
             .filter(|(_, txo)| txo.spent_by.is_none())
     }
 
+    /// List transaction IDs that are expected to exist for the given script pubkeys.
+    ///
+    /// This method is primarily used for synchronization with external sources, helping to
+    /// identify which transactions are expected to exist for a set of script pubkeys. It's
+    /// commonly used with
+    /// [`SyncRequestBuilder::expected_spk_txids`](bdk_core::spk_client::SyncRequestBuilder::expected_spk_txids)
+    /// to inform sync operations about known transactions.
+    pub fn list_expected_spk_txids<'v, I>(
+        &'v self,
+        indexer: &'v impl AsRef<SpkTxOutIndex<I>>,
+        spk_index_range: impl RangeBounds<I> + 'v,
+    ) -> impl Iterator<Item = (ScriptBuf, Txid)> + 'v
+    where
+        I: fmt::Debug + Clone + Ord + 'v,
+    {
+        let indexer = indexer.as_ref();
+        self.txs().flat_map(move |c_tx| -> Vec<_> {
+            let range = &spk_index_range;
+            let relevant_spks = indexer.relevant_spks_of_tx(&c_tx.tx);
+            relevant_spks
+                .into_iter()
+                .filter(|(i, _)| range.contains(i))
+                .map(|(_, spk)| (spk, c_tx.txid))
+                .collect()
+        })
+    }
+}
+
+impl<A: Anchor> CanonicalView<A> {
     /// Calculate the total balance of the given outpoints.
     ///
     /// This method computes a detailed balance breakdown for a set of outpoints, categorizing
@@ -268,14 +321,13 @@ impl<A: Anchor> CanonicalView<A> {
     /// # Example
     ///
     /// ```
-    /// # use bdk_chain::{CanonicalizationTask, TxGraph, local_chain::LocalChain, keychain_txout::KeychainTxOutIndex};
+    /// # use bdk_chain::{CanonicalParams, TxGraph, local_chain::LocalChain, keychain_txout::KeychainTxOutIndex};
     /// # 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 task = CanonicalizationTask::new(&tx_graph, chain_tip, Default::default());
-    /// # let view = chain.canonicalize(task);
+    /// # let view = chain.canonical_view(&tx_graph, chain_tip, CanonicalParams::default());
     /// # let indexer = KeychainTxOutIndex::<&str>::default();
     /// // Calculate balance with 6 confirmations, trusting all outputs
     /// let balance = view.balance(
@@ -287,7 +339,7 @@ impl<A: Anchor> CanonicalView<A> {
     pub fn balance<'v, O: Clone + 'v>(
         &'v self,
         outpoints: impl IntoIterator<Item = (O, OutPoint)> + 'v,
-        mut trust_predicate: impl FnMut(&O, &FullTxOut<A>) -> bool,
+        mut trust_predicate: impl FnMut(&O, &FullTxOut<ChainPosition<A>>) -> bool,
         min_confirmations: u32,
     ) -> Balance {
         let mut immature = Amount::ZERO;
@@ -296,7 +348,7 @@ impl<A: Anchor> CanonicalView<A> {
         let mut confirmed = Amount::ZERO;
 
         for (spk_i, txout) in self.filter_unspent_outpoints(outpoints) {
-            match &txout.chain_position {
+            match &txout.pos {
                 ChainPosition::Confirmed { anchor, .. } => {
                     let confirmation_height = anchor.confirmation_height_upper_bound();
                     let confirmations = self
@@ -336,31 +388,4 @@ impl<A: Anchor> CanonicalView<A> {
             confirmed,
         }
     }
-
-    /// List transaction IDs that are expected to exist for the given script pubkeys.
-    ///
-    /// This method is primarily used for synchronization with external sources, helping to
-    /// identify which transactions are expected to exist for a set of script pubkeys. It's
-    /// commonly used with
-    /// [`SyncRequestBuilder::expected_spk_txids`](bdk_core::spk_client::SyncRequestBuilder::expected_spk_txids)
-    /// to inform sync operations about known transactions.
-    pub fn list_expected_spk_txids<'v, I>(
-        &'v self,
-        indexer: &'v impl AsRef<SpkTxOutIndex<I>>,
-        spk_index_range: impl RangeBounds<I> + 'v,
-    ) -> impl Iterator<Item = (ScriptBuf, Txid)> + 'v
-    where
-        I: fmt::Debug + Clone + Ord + 'v,
-    {
-        let indexer = indexer.as_ref();
-        self.txs().flat_map(move |c_tx| -> Vec<_> {
-            let range = &spk_index_range;
-            let relevant_spks = indexer.relevant_spks_of_tx(&c_tx.tx);
-            relevant_spks
-                .into_iter()
-                .filter(|(i, _)| range.contains(i))
-                .map(|(_, spk)| (spk, c_tx.txid))
-                .collect()
-        })
-    }
 }
index 43d41e2ed4c060e51b9d7615f5dfb0bf8d08570b..9bbf3a85c7c655b9593a1ad795dff4aff69889b8 100644 (file)
@@ -161,38 +161,42 @@ impl<A: Ord> PartialOrd for ChainPosition<A> {
     }
 }
 
-/// A `TxOut` with as much data as we can retrieve about it
+/// A `TxOut` with as much data as we can retrieve about it.
+///
+/// The position type `P` is generic — it can be [`ChainPosition`] for resolved views,
+/// or [`CanonicalReason`](crate::canonical_task::CanonicalReason) for unresolved canonicalization
+/// results.
 #[derive(Debug, Clone, PartialEq, Eq)]
-pub struct FullTxOut<A> {
+pub struct FullTxOut<P> {
     /// The position of the transaction in `outpoint` in the overall chain.
-    pub chain_position: ChainPosition<A>,
+    pub pos: P,
     /// The location of the `TxOut`.
     pub outpoint: OutPoint,
     /// The `TxOut`.
     pub txout: TxOut,
-    /// The txid and chain position of the transaction (if any) that has spent this output.
-    pub spent_by: Option<(ChainPosition<A>, Txid)>,
+    /// The txid and position of the transaction (if any) that has spent this output.
+    pub spent_by: Option<(P, Txid)>,
     /// Whether this output is on a coinbase transaction.
     pub is_on_coinbase: bool,
 }
 
-impl<A: Ord> Ord for FullTxOut<A> {
+impl<P: Ord> Ord for FullTxOut<P> {
     fn cmp(&self, other: &Self) -> core::cmp::Ordering {
-        self.chain_position
-            .cmp(&other.chain_position)
+        self.pos
+            .cmp(&other.pos)
             // Tie-break with `outpoint` and `spent_by`.
             .then_with(|| self.outpoint.cmp(&other.outpoint))
             .then_with(|| self.spent_by.cmp(&other.spent_by))
     }
 }
 
-impl<A: Ord> PartialOrd for FullTxOut<A> {
+impl<P: Ord> PartialOrd for FullTxOut<P> {
     fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
         Some(self.cmp(other))
     }
 }
 
-impl<A: Anchor> FullTxOut<A> {
+impl<A: Anchor> FullTxOut<ChainPosition<A>> {
     /// Whether the `txout` is considered mature.
     ///
     /// Depending on the implementation of [`confirmation_height_upper_bound`] in [`Anchor`], this
@@ -202,7 +206,7 @@ impl<A: Anchor> FullTxOut<A> {
     /// [`confirmation_height_upper_bound`]: Anchor::confirmation_height_upper_bound
     pub fn is_mature(&self, tip: u32) -> bool {
         if self.is_on_coinbase {
-            let conf_height = match self.chain_position.confirmation_height_upper_bound() {
+            let conf_height = match self.pos.confirmation_height_upper_bound() {
                 Some(height) => height,
                 None => {
                     debug_assert!(false, "coinbase tx can never be unconfirmed");
@@ -232,7 +236,7 @@ impl<A: Anchor> FullTxOut<A> {
             return false;
         }
 
-        let conf_height = match self.chain_position.confirmation_height_upper_bound() {
+        let conf_height = match self.pos.confirmation_height_upper_bound() {
             Some(height) => height,
             None => return false,
         };
index 5c18080778d77e335595ed8562de9f981b5a5cab..c56f7d58de60391f05c4270902036dfaf0336bc0 100644 (file)
@@ -7,7 +7,7 @@ use bitcoin::{Block, OutPoint, Transaction, TxOut, Txid};
 
 use crate::{
     tx_graph::{self, TxGraph},
-    Anchor, BlockId, CanonicalizationParams, CanonicalizationTask, Indexer, Merge, TxPosInBlock,
+    Anchor, BlockId, CanonicalParams, CanonicalTask, Indexer, Merge, TxPosInBlock,
 };
 
 /// A [`TxGraph<A>`] paired with an indexer `I`, enforcing that every insertion into the graph is
@@ -436,18 +436,20 @@ impl<A, X> IndexedTxGraph<A, X>
 where
     A: Anchor,
 {
-    /// Creates a [`CanonicalizationTask`] to determine the [`CanonicalView`] of transactions.
+    /// Creates a [`CanonicalTask`] to determine the [`CanonicalView`] of transactions.
     ///
-    /// This method delegates to the underlying [`TxGraph`] to create a [`CanonicalizationTask`]
+    /// This method delegates to the underlying [`TxGraph`] to create a [`CanonicalTask`]
     /// that can be used to determine which transactions are canonical based on the provided
     /// parameters. The task handles the stateless canonicalization logic and can be polled
     /// for anchor verification requests.
-    pub fn canonicalization_task(
+    ///
+    /// [`CanonicalView`]: crate::CanonicalView
+    pub fn canonical_task(
         &'_ self,
         chain_tip: BlockId,
-        params: CanonicalizationParams,
-    ) -> CanonicalizationTask<'_, A> {
-        self.graph.canonicalization_task(chain_tip, params)
+        params: CanonicalParams,
+    ) -> CanonicalTask<'_, A> {
+        self.graph.canonical_task(chain_tip, params)
     }
 }
 
index 96a671065ab0c4698797df2792148497042464e7..38fc30c464ff5c13b692c5ad37c5a0e39df2b1a2 100644 (file)
@@ -5,7 +5,7 @@ use core::fmt;
 use core::ops::RangeBounds;
 
 use crate::collections::BTreeMap;
-use crate::{Anchor, BlockId, CanonicalView, CanonicalizationParams, ChainOracle, Merge, TxGraph};
+use crate::{Anchor, BlockId, CanonicalParams, CanonicalView, ChainOracle, Merge, TxGraph};
 use bdk_core::{ChainQuery, CheckPointEntry, ToBlockHash};
 pub use bdk_core::{CheckPoint, CheckPointIter};
 use bitcoin::block::Header;
@@ -108,13 +108,13 @@ impl LocalChain<BlockHash> {
     /// # Example
     ///
     /// ```
-    /// # use bdk_chain::{CanonicalizationTask, CanonicalizationParams, TxGraph, local_chain::LocalChain};
+    /// # use bdk_chain::{CanonicalTask, CanonicalParams, TxGraph, local_chain::LocalChain};
     /// # use bdk_core::BlockId;
     /// # use bitcoin::hashes::Hash;
     /// # let tx_graph: TxGraph<BlockId> = TxGraph::default();
     /// # let chain = LocalChain::from_blocks([(0, bitcoin::BlockHash::all_zeros())].into_iter().collect()).unwrap();
     /// let chain_tip = chain.tip().block_id();
-    /// let task = CanonicalizationTask::new(&tx_graph, chain_tip, CanonicalizationParams::default());
+    /// let task = CanonicalTask::new(&tx_graph, chain_tip, CanonicalParams::default());
     /// let view = chain.canonicalize(task);
     /// ```
     pub fn canonicalize<Q>(&self, mut task: Q) -> Q::Output
@@ -139,24 +139,21 @@ impl LocalChain<BlockHash> {
         task.finish()
     }
 
-    /// A convenience method that creates [`CanonicalizationTask`] task, canonicalize it and returns
-    /// a [`CanonicalView`].
+    /// Convenience method that runs both canonicalization phases and returns a [`CanonicalView`].
     ///
     /// This is equivalent to:
     /// ```ignore
-    /// let task = graph.canonicalization_task(chain_tip, Default::default());
-    /// let canonical_view = chain.canonicalize(task);
+    /// let canonical_txs = chain.canonicalize(tx_graph.canonical_task(tip, params));
+    /// let view = chain.canonicalize(canonical_txs.view_task(tx_graph));
     /// ```
-    ///
-    /// [`CanonicalizationTask`]: crate::CanonicalizationTask
     pub fn canonical_view<A: Anchor>(
         &self,
         tx_graph: &TxGraph<A>,
         tip: BlockId,
-        params: CanonicalizationParams,
+        params: CanonicalParams,
     ) -> CanonicalView<A> {
-        let task = tx_graph.canonicalization_task(tip, params);
-        self.canonicalize(task)
+        let canonical_txs = self.canonicalize(tx_graph.canonical_task(tip, params));
+        self.canonicalize(canonical_txs.view_task(tx_graph))
     }
 
     /// Update the chain with a given [`Header`] at `height` which you claim is connected to a
index 8ec7ab75a16b43d4189f3c4f1f1a844248da885b..560217b3b65bbd64d75e30c13ce16126f2591fed 100644 (file)
 //!
 //! The canonicalization process uses a two-step, sans-IO approach:
 //!
-//! 1. **Create a canonicalization task** using
-//!    [`canonicalization_task`](TxGraph::canonicalization_task): ```ignore let task =
-//!    tx_graph.canonicalization_task(params); ``` This creates a [`CanonicalizationTask`] that
-//!    encapsulates the canonicalization logic without performing any I/O operations.
+//! 1. **Create a canonicalization task** using [`canonical_task`]: ```ignore let task =
+//!    tx_graph.canonical_task(params);```This creates a [`CanonicalTask`] that encapsulates the
+//!    canonicalization logic without performing any I/O operations.
 //!
 //! 2. **Execute the task** with a chain oracle to obtain a [`CanonicalView`]: ```ignore let view =
 //!    chain.canonicalize(task); ``` The chain oracle (such as
 //! let changeset = graph.apply_update(update);
 //! assert!(changeset.is_empty());
 //! ```
+//!
 //! [`insert_txout`]: TxGraph::insert_txout
+//! [`CanonicalView`]: crate::CanonicalView
+//! [`canonical_task`]: TxGraph::canonical_task
 
 use crate::collections::*;
-use crate::CanonicalizationParams;
-use crate::CanonicalizationTask;
+use crate::CanonicalParams;
+use crate::CanonicalTask;
 use crate::{Anchor, BlockId, Merge};
 use alloc::collections::vec_deque::VecDeque;
 use alloc::sync::Arc;
@@ -976,18 +978,20 @@ impl<A: Anchor> TxGraph<A> {
         }
     }
 
-    /// Creates a [`CanonicalizationTask`] to determine the [`CanonicalView`] of transactions.
+    /// Creates a [`CanonicalTask`] to determine the [`CanonicalView`] of transactions.
     ///
-    /// This method delegates to the underlying [`TxGraph`] to create a [`CanonicalizationTask`]
+    /// This method delegates to the underlying [`TxGraph`] to create a [`CanonicalTask`]
     /// that can be used to determine which transactions are canonical based on the provided
     /// parameters. The task handles the stateless canonicalization logic and can be polled
     /// for anchor verification requests.
-    pub fn canonicalization_task(
+    ///
+    /// [`CanonicalView`]: crate::CanonicalView
+    pub fn canonical_task(
         &'_ self,
         chain_tip: BlockId,
-        params: CanonicalizationParams,
-    ) -> CanonicalizationTask<'_, A> {
-        CanonicalizationTask::new(self, chain_tip, params)
+        params: CanonicalParams,
+    ) -> CanonicalTask<'_, A> {
+        CanonicalTask::new(self, chain_tip, params)
     }
 }
 
index 29f36169ad05a19728d26bb2cc31ef6c5043fea0..7bdac78a50b220305881bced368c19804e857d04 100644 (file)
@@ -4,7 +4,7 @@ use bdk_testenv::utils::DESCRIPTORS;
 use rand::distributions::{Alphanumeric, DistString};
 use std::collections::HashMap;
 
-use bdk_chain::{spk_txout::SpkTxOutIndex, tx_graph::TxGraph, Anchor, CanonicalizationParams};
+use bdk_chain::{spk_txout::SpkTxOutIndex, tx_graph::TxGraph, Anchor, CanonicalParams};
 use bitcoin::{
     locktime::absolute::LockTime, secp256k1::Secp256k1, transaction, Amount, OutPoint, ScriptBuf,
     Sequence, Transaction, TxIn, TxOut, Txid, Witness,
@@ -57,7 +57,7 @@ pub struct TxTemplateEnv<'a, A> {
     pub tx_graph: TxGraph<A>,
     pub indexer: SpkTxOutIndex<u32>,
     pub txid_to_name: HashMap<&'a str, Txid>,
-    pub canonicalization_params: CanonicalizationParams,
+    pub canonicalization_params: CanonicalParams,
 }
 
 #[allow(dead_code)]
@@ -79,7 +79,7 @@ pub fn init_graph<'a, A: Anchor + Clone + 'a>(
     });
     let mut txid_to_name = HashMap::<&'a str, Txid>::new();
 
-    let mut canonicalization_params = CanonicalizationParams::default();
+    let mut canonicalization_params = CanonicalParams::default();
     for (bogus_txin_vout, tx_tmp) in tx_templates.into_iter().enumerate() {
         let tx = Transaction {
             version: transaction::Version::non_standard(0),
index ad65bab808190c6f881f23f04c2d2a99ecc52adc..d6fabbab3b1de9dfef49b05338d2a676e128abdc 100644 (file)
@@ -489,7 +489,7 @@ fn test_list_owned_txouts() {
             let confirmed_txouts_txid = txouts
                 .iter()
                 .filter_map(|(_, full_txout)| {
-                    if full_txout.chain_position.is_confirmed() {
+                    if full_txout.pos.is_confirmed() {
                         Some(full_txout.outpoint.txid)
                     } else {
                         None
@@ -500,7 +500,7 @@ fn test_list_owned_txouts() {
             let unconfirmed_txouts_txid = txouts
                 .iter()
                 .filter_map(|(_, full_txout)| {
-                    if !full_txout.chain_position.is_confirmed() {
+                    if !full_txout.pos.is_confirmed() {
                         Some(full_txout.outpoint.txid)
                     } else {
                         None
@@ -511,7 +511,7 @@ fn test_list_owned_txouts() {
             let confirmed_utxos_txid = utxos
                 .iter()
                 .filter_map(|(_, full_txout)| {
-                    if full_txout.chain_position.is_confirmed() {
+                    if full_txout.pos.is_confirmed() {
                         Some(full_txout.outpoint.txid)
                     } else {
                         None
@@ -522,7 +522,7 @@ fn test_list_owned_txouts() {
             let unconfirmed_utxos_txid = utxos
                 .iter()
                 .filter_map(|(_, full_txout)| {
-                    if !full_txout.chain_position.is_confirmed() {
+                    if !full_txout.pos.is_confirmed() {
                         Some(full_txout.outpoint.txid)
                     } else {
                         None