bitcoindevkit / bitcoindevkit/bdk

`CanonicalReason::Assumed` should map to `ChainPosition::Confirmed` if a direct anchor exists

Open
#2,088 3 comments 0 reactions 1 assignee Claimed by @Ishaan-2312 View on GitHub
bug
Dominant language
Rust
Stars
1.1k
Forks
483
Avg merge
20d 3h
Merged PRs (30d)
3

Description

**Describe the bug**

I tried to pass a `Txid` to `CanonicalizationParams::assume_canonical`, which I expected to be confirmed by a direct anchor, but in the set of canonical txs that is returned the chain position of the assumed tx appeared as `ChainPosition::Unconfirmed`. This later caused unexpected results at the time of doing coin selection.

Currently when a txid is assumed to be canonical, returning a chain position of `ChainPosition::Confirmed` is only possible if the `CanonicalReason`'s `descendant` has some value. https://github.com/bitcoindevkit/bdk/blob/09ddfb2853bb312fd832c6c15330dcaad43af6ac/crates/chain/src/canonical_view.rs#L156-L161

**Expected behavior**

`CanonicalReason::Assumed` should map to `ChainPosition::Confirmed` if a direct anchor exists.

- If the reason carries a `descendant` txid that is confirmed by a direct anchor, I would expect a chain position that is transitively confirmed by the descendant.
- If the assumed tx is confirmed by a direct anchor and is not accompanied by a `descendant`, I would expect a chain position that is confirmed by the direct anchor and a `transitively` of `None`.
- If neither the assumed tx nor a descendant are confirmed by a direct anchor, I would expect `ChainPosition::Unconfirmed`.

**To Reproduce**

I added this test case to demonstrate what I think is the expected behavior. Notice `B` is both assumed and confirmed.

> `test_tx_graph_conflicts.rs`

```rust
Scenario {
name: "B and C are confirmed, C1 spends C, all are assume-canonical",
tx_templates: &[
TxTemplate {
tx_name: "B",
inputs: &[TxInTemplate::Bogus],
outputs: &[TxOutTemplate::new(10_000, Some(0))],
anchors: &[block_id!(1, "B")],
last_seen: None,
assume_canonical: true,
},
TxTemplate {
tx_name: "C",
inputs: &[TxInTemplate::Bogus],
outputs: &[TxOutTemplate::new(8_000, Some(0))],
anchors: &[block_id!(2, "C")],
last_seen: None,
assume_canonical: true,
},
TxTemplate {
tx_name: "C1",
inputs: &[TxInTemplate::PrevTx("C", 0)],
outputs: &[TxOutTemplate::new(6_000, Some(0))],
assume_canonical: true,
..Default::default()
}
],
exp_chain_txs: HashSet::from(["B", "C", "C1"]),
exp_chain_txouts: HashSet::from([("B", 0), ("C", 0), ("C1", 0)]),
exp_unspents: HashSet::from([("B", 0), ("C1", 0)]),
exp_balance: Balance {
immature: Amount::ZERO,
trusted_pending: Amount::from_sat(6_000),
untrusted_pending: Amount::ZERO,
confirmed: Amount::from_sat(10_000),
}
}
```

**Build environment**
- BDK tag/commit: 09ddfb2853bb312fd832c6c15330dcaad43af6ac
- Rust/Cargo version: 1.88.0

**Is this blocking production use?**
- [ ] Yes
- [x] No

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.