]> Untitled Git - bdk/commitdiff
fix(chain): Tx assumed to be canonical will not be forced unconfimred
author志宇 <hello@evanlinjin.me>
Wed, 11 Mar 2026 15:36:56 +0000 (15:36 +0000)
committerLeonardo Lima <oleonardolima@users.noreply.github.com>
Wed, 11 Mar 2026 17:17:54 +0000 (14:17 -0300)
Previously there was a bug where txs added in
`CanonicalizationParams::assume_canonical` were forced to always be
unconfirmed (even if it's anchored in the best chain). This commit fixes
this.

`test_assumed_canonical_with_anchor_is_confirmed` is added to ensure
expected behavior.

Co-authored-by: AayushGupta56 <aayushgupt56@gmail.com>
crates/chain/src/tx_graph.rs
crates/chain/tests/test_tx_graph.rs

index 546c4b5939c6f612df55bf61c935a99a00c177c8..36eb7456669d22887b331361824a5e37cc6d9119 100644 (file)
@@ -1028,8 +1028,8 @@ impl<A: Anchor> TxGraph<A> {
                 res.map(|(txid, _, canonical_reason)| {
                     let tx_node = self.get_tx_node(txid).expect("must contain tx");
                     let chain_position = match canonical_reason {
-                        CanonicalReason::Assumed { descendant } => match descendant {
-                            Some(_) => match find_direct_anchor(&tx_node, chain, chain_tip)? {
+                        CanonicalReason::Assumed { .. } => {
+                            match find_direct_anchor(&tx_node, chain, chain_tip)? {
                                 Some(anchor) => ChainPosition::Confirmed {
                                     anchor,
                                     transitively: None,
@@ -1038,12 +1038,8 @@ impl<A: Anchor> TxGraph<A> {
                                     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 find_direct_anchor(&tx_node, chain, chain_tip)? {
                                 Some(anchor) => ChainPosition::Confirmed {
index 0e43d8e787778fa7a71f34e0921e66609d36fb91..4d4d434a6f35921b6504260972caded9ac0eaa7b 100644 (file)
@@ -1545,6 +1545,58 @@ fn test_get_first_seen_of_a_tx() {
     assert_eq!(first_seen, Some(seen_at));
 }
 
+#[test]
+fn test_assumed_canonical_with_anchor_is_confirmed() {
+    use bdk_chain::ChainPosition;
+
+    let chain = LocalChain::from_blocks(
+        [(0, hash!("genesis")), (2, hash!("b2"))]
+            .into_iter()
+            .collect(),
+    )
+    .unwrap();
+
+    // Create an anchored transaction that will be assumed canonical via `CanonicalizationParams`.
+    let tx = Transaction {
+        input: vec![TxIn {
+            previous_output: OutPoint::new(hash!("parent"), 0),
+            ..Default::default()
+        }],
+        output: vec![TxOut {
+            value: Amount::from_sat(50_000),
+            script_pubkey: ScriptBuf::new(),
+        }],
+        ..new_tx(1)
+    };
+    let txid = tx.compute_txid();
+
+    let mut tx_graph = TxGraph::default();
+    let _ = tx_graph.insert_tx(tx);
+    let _ = tx_graph.insert_anchor(
+        txid,
+        ConfirmationBlockTime {
+            block_id: chain.get(2).unwrap().block_id(),
+            confirmation_time: 123456,
+        },
+    );
+
+    let canonical_tx = tx_graph
+        .list_canonical_txs(
+            &chain,
+            chain.tip().block_id(),
+            CanonicalizationParams {
+                assume_canonical: vec![txid],
+            },
+        )
+        .find(|c_tx| c_tx.tx_node.txid == txid)
+        .expect("tx must exist");
+
+    assert!(
+        matches!(canonical_tx.chain_position, ChainPosition::Confirmed { .. }),
+        "tx that is assumed canonical and has a direct anchor should have ChainPosition::Confirmed"
+    );
+}
+
 /// A helper structure to constructs multiple [`TxGraph`] scenarios, used in
 /// `test_list_ordered_canonical_txs`.
 struct Scenario<'a> {