]> Untitled Git - bdk/commitdiff
chore(workspace): use new `LocalChain::canonical_view` API
authorLeonardo Lima <oleonardolima@users.noreply.github.com>
Tue, 19 May 2026 18:56:26 +0000 (15:56 -0300)
committer志宇 <hello@evanlinjin.me>
Sat, 13 Jun 2026 20:23:39 +0000 (20:23 +0000)
Updates the codebase to use the new convenience
`LocalChain::canonical_view` method in order to generate the
`CanonicalView`. Internally the convenience method follows the `sans-IO` approach,
separating the canonicalization algorithm from i/o operations, and it's
used as follows:

1. Create a new `CanonicalizationTask` with a `TxGraph`, by calling:
   `graph.canonicalization_task(params)`
2. Execute the canonicalization process with a chain oracle (e.g
   `LocalChain`, which implements `ChainOracle` trait), by calling:
   `chain.canonicalize(task, chain_tip)`

crates/bitcoind_rpc/examples/filter_iter.rs
crates/bitcoind_rpc/tests/test_emitter.rs
crates/chain/benches/canonicalization.rs
crates/chain/benches/indexer.rs
crates/chain/tests/test_canonical_view.rs
crates/chain/tests/test_indexed_tx_graph.rs
crates/chain/tests/test_tx_graph.rs
crates/chain/tests/test_tx_graph_conflicts.rs
crates/electrum/tests/test_electrum.rs
crates/esplora/tests/async_ext.rs
crates/esplora/tests/blocking_ext.rs

index e79bde67202fa5c0bfcd8ffa142e29329ef3d802..a346a2db0ece87ec3e2821f2860e6d6ef103b9c0 100644 (file)
@@ -69,7 +69,8 @@ fn main() -> anyhow::Result<()> {
     println!("\ntook: {}s", start.elapsed().as_secs());
     println!("Local tip: {}", chain.tip().height());
 
-    let canonical_view = graph.canonical_view(&chain, chain.tip().block_id(), Default::default());
+    let canonical_view =
+        chain.canonical_view(graph.graph(), chain.tip().block_id(), Default::default());
 
     let unspent: Vec<_> = canonical_view
         .filter_unspent_outpoints(graph.index.outpoints().clone())
index b75d474caaa2ec5bee53b7a1fad610c065cb0858..2aeede628308bc6e2846a1468afc811877ccbe78 100644 (file)
@@ -5,7 +5,7 @@ use bdk_chain::{
     bitcoin::{Address, Amount, Txid},
     local_chain::{CheckPoint, LocalChain},
     spk_txout::SpkTxOutIndex,
-    Balance, BlockId, CanonicalizationParams, IndexedTxGraph, Merge,
+    Balance, BlockId, IndexedTxGraph, Merge,
 };
 use bdk_testenv::{
     anyhow,
@@ -318,11 +318,14 @@ fn get_balance(
     recv_chain: &LocalChain,
     recv_graph: &IndexedTxGraph<BlockId, SpkTxOutIndex<()>>,
 ) -> anyhow::Result<Balance> {
-    let chain_tip = recv_chain.tip().block_id();
     let outpoints = recv_graph.index.outpoints().clone();
-    let balance = recv_graph
-        .canonical_view(recv_chain, chain_tip, CanonicalizationParams::default())
-        .balance(outpoints, |_, _| true, 1);
+    let balance = recv_chain
+        .canonical_view(
+            recv_graph.graph(),
+            recv_chain.tip().block_id(),
+            Default::default(),
+        )
+        .balance(outpoints, |_, _| true, 0);
     Ok(balance)
 }
 
@@ -631,8 +634,8 @@ fn test_expect_tx_evicted() -> anyhow::Result<()> {
     let _txid_2 = core.send_raw_transaction(&tx1b)?;
 
     // Retrieve the expected unconfirmed txids and spks from the graph.
-    let exp_spk_txids = graph
-        .canonical_view(&chain, chain_tip, Default::default())
+    let exp_spk_txids = chain
+        .canonical_view(graph.graph(), chain_tip, Default::default())
         .list_expected_spk_txids(&graph.index, ..)
         .collect::<Vec<_>>();
     assert_eq!(exp_spk_txids, vec![(spk, txid_1)]);
@@ -647,8 +650,8 @@ fn test_expect_tx_evicted() -> anyhow::Result<()> {
     // Update graph with evicted tx.
     let _ = graph.batch_insert_relevant_evicted_at(mempool_event.evicted);
 
-    let canonical_txids = graph
-        .canonical_view(&chain, chain_tip, CanonicalizationParams::default())
+    let canonical_txids = chain
+        .canonical_view(graph.graph(), chain_tip, Default::default())
         .txs()
         .map(|tx| tx.txid)
         .collect::<Vec<_>>();
index 074e38cc429fa00bb1f48ae98a58ac60578cd268..74e6c05fd9cf033c7247f7b670c4e2a82ea02421 100644 (file)
@@ -1,4 +1,3 @@
-use bdk_chain::CanonicalizationParams;
 use bdk_chain::{keychain_txout::KeychainTxOutIndex, local_chain::LocalChain, IndexedTxGraph};
 use bdk_core::{BlockId, CheckPoint};
 use bdk_core::{ConfirmationBlockTime, TxUpdate};
@@ -95,31 +94,19 @@ fn setup<F: Fn(&mut KeychainTxGraph, &LocalChain)>(f: F) -> (KeychainTxGraph, Lo
 }
 
 fn run_list_canonical_txs(tx_graph: &KeychainTxGraph, chain: &LocalChain, exp_txs: usize) {
-    let view = tx_graph.canonical_view(
-        chain,
-        chain.tip().block_id(),
-        CanonicalizationParams::default(),
-    );
+    let view = chain.canonical_view(tx_graph.graph(), chain.tip().block_id(), Default::default());
     let txs = view.txs();
     assert_eq!(txs.count(), exp_txs);
 }
 
 fn run_filter_chain_txouts(tx_graph: &KeychainTxGraph, chain: &LocalChain, exp_txos: usize) {
-    let view = tx_graph.canonical_view(
-        chain,
-        chain.tip().block_id(),
-        CanonicalizationParams::default(),
-    );
+    let view = chain.canonical_view(tx_graph.graph(), chain.tip().block_id(), Default::default());
     let utxos = view.filter_outpoints(tx_graph.index.outpoints().clone());
     assert_eq!(utxos.count(), exp_txos);
 }
 
 fn run_filter_chain_unspents(tx_graph: &KeychainTxGraph, chain: &LocalChain, exp_utxos: usize) {
-    let view = tx_graph.canonical_view(
-        chain,
-        chain.tip().block_id(),
-        CanonicalizationParams::default(),
-    );
+    let view = chain.canonical_view(tx_graph.graph(), chain.tip().block_id(), Default::default());
     let utxos = view.filter_unspent_outpoints(tx_graph.index.outpoints().clone());
     assert_eq!(utxos.count(), exp_utxos);
 }
index 5907c76a0037aa1ead043df354671d15b4277776..97917ce35b2edce2ea1586af778b91d4d2d0bfea 100644 (file)
@@ -1,7 +1,7 @@
 use bdk_chain::{
     keychain_txout::{InsertDescriptorError, KeychainTxOutIndex},
     local_chain::LocalChain,
-    CanonicalizationParams, IndexedTxGraph,
+    IndexedTxGraph,
 };
 use bdk_core::{BlockId, CheckPoint, ConfirmationBlockTime, TxUpdate};
 use bitcoin::{
@@ -82,10 +82,9 @@ fn do_bench(indexed_tx_graph: &KeychainTxGraph, chain: &LocalChain) {
         .unwrap();
 
     // Check balance
-    let chain_tip = chain.tip().block_id();
     let op = graph.index.outpoints().clone();
-    let bal = graph
-        .canonical_view(chain, chain_tip, CanonicalizationParams::default())
+    let bal = chain
+        .canonical_view(graph.graph(), chain.tip().block_id(), Default::default())
         .balance(op, |_, _| false, 1);
     assert_eq!(bal.total(), AMOUNT * TX_CT as u64);
 }
index 3c0d54381c263a81079b48d568da9d7b6c156292..2cbe021033a34dfd90d5845a0ae595f3f41be238 100644 (file)
@@ -2,7 +2,7 @@
 
 use std::collections::BTreeMap;
 
-use bdk_chain::{local_chain::LocalChain, CanonicalizationParams, ConfirmationBlockTime, TxGraph};
+use bdk_chain::{local_chain::LocalChain, ConfirmationBlockTime, TxGraph};
 use bdk_testenv::{hash, utils::new_tx};
 use bitcoin::{Amount, BlockHash, OutPoint, ScriptBuf, Transaction, TxIn, TxOut};
 
@@ -53,9 +53,8 @@ fn test_min_confirmations_parameter() {
     };
     let _ = tx_graph.insert_anchor(txid, anchor_height_5);
 
-    let chain_tip = chain.tip().block_id();
     let canonical_view =
-        tx_graph.canonical_view(&chain, chain_tip, CanonicalizationParams::default());
+        chain.canonical_view(&tx_graph, chain.tip().block_id(), Default::default());
 
     // Test min_confirmations = 1: Should be confirmed (has 6 confirmations)
     let balance_1_conf = canonical_view.balance(
@@ -142,11 +141,8 @@ fn test_min_confirmations_with_untrusted_tx() {
     };
     let _ = tx_graph.insert_anchor(txid, anchor);
 
-    let canonical_view = tx_graph.canonical_view(
-        &chain,
-        chain.tip().block_id(),
-        CanonicalizationParams::default(),
-    );
+    let canonical_view =
+        chain.canonical_view(&tx_graph, chain.tip().block_id(), Default::default());
 
     // Test with min_confirmations = 5 and untrusted predicate
     let balance = canonical_view.balance(
@@ -263,11 +259,8 @@ fn test_min_confirmations_multiple_transactions() {
     );
     outpoints.push(((), outpoint2));
 
-    let canonical_view = tx_graph.canonical_view(
-        &chain,
-        chain.tip().block_id(),
-        CanonicalizationParams::default(),
-    );
+    let canonical_view =
+        chain.canonical_view(&tx_graph, chain.tip().block_id(), Default::default());
 
     // Test with min_confirmations = 5
     // tx0: 11 confirmations -> confirmed
index 2a33f3b1c35bd5a38ddea8b55f0c43b367d7bf7c..ad65bab808190c6f881f23f04c2d2a99ecc52adc 100644 (file)
@@ -10,8 +10,7 @@ use bdk_chain::{
     indexer::keychain_txout::KeychainTxOutIndex,
     local_chain::LocalChain,
     spk_txout::SpkTxOutIndex,
-    tx_graph, Balance, CanonicalizationParams, ChainPosition, ConfirmationBlockTime, DescriptorExt,
-    SpkIterator,
+    tx_graph, Balance, ChainPosition, ConfirmationBlockTime, DescriptorExt, SpkIterator,
 };
 use bdk_testenv::{
     anyhow::{self},
@@ -469,23 +468,23 @@ fn test_list_owned_txouts() {
                 .get(height)
                 .map(|cp| cp.block_id())
                 .unwrap_or_else(|| panic!("block must exist at {height}"));
-            let txouts = graph
-                .canonical_view(&local_chain, chain_tip, CanonicalizationParams::default())
+
+            let canonical_view =
+                local_chain.canonical_view(graph.graph(), chain_tip, Default::default());
+
+            let txouts = canonical_view
                 .filter_outpoints(graph.index.outpoints().iter().cloned())
                 .collect::<Vec<_>>();
 
-            let utxos = graph
-                .canonical_view(&local_chain, chain_tip, CanonicalizationParams::default())
+            let utxos = canonical_view
                 .filter_unspent_outpoints(graph.index.outpoints().iter().cloned())
                 .collect::<Vec<_>>();
 
-            let balance = graph
-                .canonical_view(&local_chain, chain_tip, CanonicalizationParams::default())
-                .balance(
-                    graph.index.outpoints().iter().cloned(),
-                    |_, txout| trusted_spks.contains(&txout.txout.script_pubkey),
-                    1,
-                );
+            let balance = canonical_view.balance(
+                graph.index.outpoints().iter().cloned(),
+                |_, txout| trusted_spks.contains(&txout.txout.script_pubkey),
+                0,
+            );
 
             let confirmed_txouts_txid = txouts
                 .iter()
@@ -788,12 +787,8 @@ fn test_get_chain_position() {
         }
 
         // check chain position
-        let chain_pos = graph
-            .canonical_view(
-                chain,
-                chain.tip().block_id(),
-                CanonicalizationParams::default(),
-            )
+        let chain_pos = chain
+            .canonical_view(graph.graph(), chain.tip().block_id(), Default::default())
             .txs()
             .find_map(|canon_tx| {
                 if canon_tx.txid == txid {
index b2a3596085e904cd70ecc6d23c72970cc622099b..3cbfe3414f1822cce6736ffdcd8e3ad9395d7b8b 100644 (file)
@@ -2,7 +2,7 @@
 
 #[macro_use]
 mod common;
-use bdk_chain::{collections::*, BlockId, CanonicalizationParams, ConfirmationBlockTime};
+use bdk_chain::{collections::*, BlockId, ConfirmationBlockTime};
 use bdk_chain::{
     local_chain::LocalChain,
     tx_graph::{self, CalculateFeeError},
@@ -1014,8 +1014,8 @@ fn test_chain_spends() {
 
     let build_canonical_spends =
         |chain: &LocalChain, tx_graph: &TxGraph<ConfirmationBlockTime>| -> HashMap<OutPoint, _> {
-            tx_graph
-                .canonical_view(chain, tip.block_id(), CanonicalizationParams::default())
+            chain
+                .canonical_view(tx_graph, tip.block_id(), Default::default())
                 .filter_outpoints(tx_graph.all_txouts().map(|(op, _)| ((), op)))
                 .filter_map(|(_, full_txo)| Some((full_txo.outpoint, full_txo.spent_by?)))
                 .collect()
@@ -1023,8 +1023,8 @@ fn test_chain_spends() {
     let build_canonical_positions = |chain: &LocalChain,
                                      tx_graph: &TxGraph<ConfirmationBlockTime>|
      -> HashMap<Txid, ChainPosition<ConfirmationBlockTime>> {
-        tx_graph
-            .canonical_view(chain, tip.block_id(), CanonicalizationParams::default())
+        chain
+            .canonical_view(tx_graph, tip.block_id(), Default::default())
             .txs()
             .map(|canon_tx| (canon_tx.txid, canon_tx.pos))
             .collect()
@@ -1197,35 +1197,23 @@ fn transactions_inserted_into_tx_graph_are_not_canonical_until_they_have_an_anch
         .into_iter()
         .collect();
     let chain = LocalChain::from_blocks(blocks).unwrap();
-    let canonical_txs: Vec<_> = graph
-        .canonical_view(
-            &chain,
-            chain.tip().block_id(),
-            CanonicalizationParams::default(),
-        )
+    let canonical_txs: Vec<_> = chain
+        .canonical_view(&graph, chain.tip().block_id(), Default::default())
         .txs()
         .collect();
     assert!(canonical_txs.is_empty());
 
     // tx0 with seen_at should be returned by canonical txs
     let _ = graph.insert_seen_at(txids[0], 2);
-    let canonical_view = graph.canonical_view(
-        &chain,
-        chain.tip().block_id(),
-        CanonicalizationParams::default(),
-    );
-    let mut canonical_txs = canonical_view.txs();
+    let view = chain.canonical_view(&graph, chain.tip().block_id(), Default::default());
+    let mut canonical_txs = view.txs();
     assert_eq!(canonical_txs.next().map(|tx| tx.txid).unwrap(), txids[0]);
     drop(canonical_txs);
 
     // tx1 with anchor is also canonical
     let _ = graph.insert_anchor(txids[1], block_id!(2, "B"));
-    let canonical_txids: Vec<_> = graph
-        .canonical_view(
-            &chain,
-            chain.tip().block_id(),
-            CanonicalizationParams::default(),
-        )
+    let canonical_txids: Vec<_> = chain
+        .canonical_view(&graph, chain.tip().block_id(), Default::default())
         .txs()
         .map(|tx| tx.txid)
         .collect();
index 38f21365c3cc739d1dee3030b8bc46be1ca7b9df..f1f1669844e7d02503635ac57d54e6e069e258c6 100644 (file)
@@ -970,9 +970,13 @@ fn test_tx_conflict_handling() {
     for scenario in scenarios {
         let env = init_graph(scenario.tx_templates.iter());
 
-        let txs = env
-            .tx_graph
-            .canonical_view(&local_chain, chain_tip, env.canonicalization_params.clone())
+        let canonical_view = local_chain.canonical_view(
+            &env.tx_graph,
+            chain_tip,
+            env.canonicalization_params.clone(),
+        );
+
+        let txs = canonical_view
             .txs()
             .map(|tx| tx.txid)
             .collect::<BTreeSet<_>>();
@@ -987,9 +991,7 @@ fn test_tx_conflict_handling() {
             scenario.name
         );
 
-        let txouts = env
-            .tx_graph
-            .canonical_view(&local_chain, chain_tip, env.canonicalization_params.clone())
+        let txouts = canonical_view
             .filter_outpoints(env.indexer.outpoints().iter().cloned())
             .map(|(_, full_txout)| full_txout.outpoint)
             .collect::<BTreeSet<_>>();
@@ -1007,9 +1009,7 @@ fn test_tx_conflict_handling() {
             scenario.name
         );
 
-        let utxos = env
-            .tx_graph
-            .canonical_view(&local_chain, chain_tip, env.canonicalization_params.clone())
+        let utxos = canonical_view
             .filter_unspent_outpoints(env.indexer.outpoints().iter().cloned())
             .map(|(_, full_txout)| full_txout.outpoint)
             .collect::<BTreeSet<_>>();
@@ -1027,18 +1027,15 @@ fn test_tx_conflict_handling() {
             scenario.name
         );
 
-        let balance = env
-            .tx_graph
-            .canonical_view(&local_chain, chain_tip, env.canonicalization_params.clone())
-            .balance(
-                env.indexer.outpoints().iter().cloned(),
-                |_, txout| {
-                    env.indexer
-                        .index_of_spk(txout.txout.script_pubkey.as_script())
-                        .is_some()
-                },
-                0,
-            );
+        let balance = canonical_view.balance(
+            env.indexer.outpoints().iter().cloned(),
+            |_, txout| {
+                env.indexer
+                    .index_of_spk(txout.txout.script_pubkey.as_script())
+                    .is_some()
+            },
+            0,
+        );
         assert_eq!(
             balance, scenario.exp_balance,
             "\n[{}] 'balance' failed",
index ef94c62f3e6019db6588d5dff921ea5604a9c866..c569b065dafdceab1366317eaa5e87489eecae30 100644 (file)
@@ -3,8 +3,7 @@ use bdk_chain::{
     local_chain::LocalChain,
     spk_client::{FullScanRequest, SyncRequest, SyncResponse},
     spk_txout::SpkTxOutIndex,
-    Balance, CanonicalizationParams, ConfirmationBlockTime, IndexedTxGraph, Indexer, Merge,
-    TxGraph,
+    Balance, ConfirmationBlockTime, IndexedTxGraph, Indexer, Merge, TxGraph,
 };
 use bdk_core::bitcoin::{
     key::{Secp256k1, UntweakedPublicKey},
@@ -56,11 +55,14 @@ fn get_balance(
     recv_chain: &LocalChain,
     recv_graph: &IndexedTxGraph<ConfirmationBlockTime, SpkTxOutIndex<()>>,
 ) -> anyhow::Result<Balance> {
-    let chain_tip = recv_chain.tip().block_id();
     let outpoints = recv_graph.index.outpoints().clone();
-    let balance = recv_graph
-        .canonical_view(recv_chain, chain_tip, CanonicalizationParams::default())
-        .balance(outpoints, |_, _| true, 1);
+    let balance = recv_chain
+        .canonical_view(
+            recv_graph.graph(),
+            recv_chain.tip().block_id(),
+            Default::default(),
+        )
+        .balance(outpoints, |_, _| true, 0);
     Ok(balance)
 }
 
@@ -174,8 +176,7 @@ pub fn detect_receive_tx_cancel() -> anyhow::Result<()> {
         .chain_tip(chain.tip())
         .spks_with_indexes(graph.index.all_spks().clone())
         .expected_spk_txids(
-            graph
-                .canonical_view(&chain, chain.tip().block_id(), Default::default())
+            { chain.canonical_view(graph.graph(), chain.tip().block_id(), Default::default()) }
                 .list_expected_spk_txids(&graph.index, ..),
         );
     let sync_response = client.sync(sync_request, BATCH_SIZE, true)?;
@@ -204,8 +205,7 @@ pub fn detect_receive_tx_cancel() -> anyhow::Result<()> {
         .chain_tip(chain.tip())
         .spks_with_indexes(graph.index.all_spks().clone())
         .expected_spk_txids(
-            graph
-                .canonical_view(&chain, chain.tip().block_id(), Default::default())
+            { chain.canonical_view(graph.graph(), chain.tip().block_id(), Default::default()) }
                 .list_expected_spk_txids(&graph.index, ..),
         );
     let sync_response = client.sync(sync_request, BATCH_SIZE, true)?;
index e6903591c7a9e78ffc84c833ed461ef9bd39339b..2941e2a6691bfed36a1ad4eb57b877f9598f1b4f 100644 (file)
@@ -100,8 +100,7 @@ pub async fn detect_receive_tx_cancel() -> anyhow::Result<()> {
         .chain_tip(chain.tip())
         .spks_with_indexes(graph.index.all_spks().clone())
         .expected_spk_txids(
-            graph
-                .canonical_view(&chain, chain.tip().block_id(), Default::default())
+            { chain.canonical_view(graph.graph(), chain.tip().block_id(), Default::default()) }
                 .list_expected_spk_txids(&graph.index, ..),
         );
     let sync_response = client.sync(sync_request, 1).await?;
@@ -130,8 +129,7 @@ pub async fn detect_receive_tx_cancel() -> anyhow::Result<()> {
         .chain_tip(chain.tip())
         .spks_with_indexes(graph.index.all_spks().clone())
         .expected_spk_txids(
-            graph
-                .canonical_view(&chain, chain.tip().block_id(), Default::default())
+            { chain.canonical_view(graph.graph(), chain.tip().block_id(), Default::default()) }
                 .list_expected_spk_txids(&graph.index, ..),
         );
     let sync_response = client.sync(sync_request, 1).await?;
index 4619901cc862dde7b567dd5449256b05ba8313f8..8db59947fabd0514acc27342e4a6a1c66502a69c 100644 (file)
@@ -97,8 +97,7 @@ pub fn detect_receive_tx_cancel() -> anyhow::Result<()> {
         .chain_tip(chain.tip())
         .spks_with_indexes(graph.index.all_spks().clone())
         .expected_spk_txids(
-            graph
-                .canonical_view(&chain, chain.tip().block_id(), Default::default())
+            { chain.canonical_view(graph.graph(), chain.tip().block_id(), Default::default()) }
                 .list_expected_spk_txids(&graph.index, ..),
         );
     let sync_response = client.sync(sync_request, 1)?;
@@ -127,8 +126,7 @@ pub fn detect_receive_tx_cancel() -> anyhow::Result<()> {
         .chain_tip(chain.tip())
         .spks_with_indexes(graph.index.all_spks().clone())
         .expected_spk_txids(
-            graph
-                .canonical_view(&chain, chain.tip().block_id(), Default::default())
+            { chain.canonical_view(graph.graph(), chain.tip().block_id(), Default::default()) }
                 .list_expected_spk_txids(&graph.index, ..),
         );
     let sync_response = client.sync(sync_request, 1)?;