Merge bitcoindevkit/bdk#1202: fix(chain): filter coinbase tx not in best chain

991cb77b6fbeedbf52d1bd9aa6b3d680f8269969 fix(chain): filter coinbase tx not in best chain (Wei Chen)

Pull request description:

  ### Description

  Fixes #1144.
  Coinbase transactions cannot exist in the mempool and be unconfirmed. `TxGraph::try_get_chain_position` should always return `None` for coinbase transactions not anchored in best chain.

  ### Checklists

  #### All Submissions:

  * [x] I've signed all my commits
  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)
  * [x] I ran `cargo fmt` and `cargo clippy` before committing

  #### Bugfixes:

  * [ ] This pull request breaks the existing API
  * [x] I've added tests to reproduce the issue which are now passing
  * [x] I'm linking the issue being fixed by this PR

ACKs for top commit:
  notmandatory:
    ACK 991cb77b6fbeedbf52d1bd9aa6b3d680f8269969
  danielabrozzoni:
    ACK 991cb77b6fbeedbf52d1bd9aa6b3d680f8269969

Tree-SHA512: 9e06d8404708eee050c96807a876a470303f4983666c82c56c17d97c2d4b72784e75271279fd393c53a6a967a352aea1ef2762da71ac4bb58f7a0c2f05354948
This commit is contained in:
Daniela Brozzoni 2023-11-14 14:56:56 +01:00
commit f382fa9230
No known key found for this signature in database
GPG Key ID: 7DE4F1FDCED0AB87
2 changed files with 51 additions and 1 deletions

View File

@ -718,7 +718,14 @@ impl<A: Anchor> TxGraph<A> {
// might be in mempool, or it might have been dropped already. // might be in mempool, or it might have been dropped already.
// Let's check conflicts to find out! // Let's check conflicts to find out!
let tx = match tx_node { let tx = match tx_node {
TxNodeInternal::Whole(tx) => tx, TxNodeInternal::Whole(tx) => {
// A coinbase tx that is not anchored in the best chain cannot be unconfirmed and
// should always be filtered out.
if tx.is_coin_base() {
return Ok(None);
}
tx
}
TxNodeInternal::Partial(_) => { TxNodeInternal::Partial(_) => {
// Partial transactions (outputs only) cannot have conflicts. // Partial transactions (outputs only) cannot have conflicts.
return Ok(None); return Ok(None);

View File

@ -45,6 +45,49 @@ fn test_tx_conflict_handling() {
.unwrap_or_default(); .unwrap_or_default();
let scenarios = [ let scenarios = [
Scenario {
name: "coinbase tx cannot be in mempool and be unconfirmed",
tx_templates: &[
TxTemplate {
tx_name: "unconfirmed_coinbase",
inputs: &[TxInTemplate::Coinbase],
outputs: &[TxOutTemplate::new(5000, Some(0))],
..Default::default()
},
TxTemplate {
tx_name: "confirmed_genesis",
inputs: &[TxInTemplate::Bogus],
outputs: &[TxOutTemplate::new(10000, Some(1))],
anchors: &[block_id!(1, "B")],
last_seen: None,
},
TxTemplate {
tx_name: "unconfirmed_conflict",
inputs: &[
TxInTemplate::PrevTx("confirmed_genesis", 0),
TxInTemplate::PrevTx("unconfirmed_coinbase", 0)
],
outputs: &[TxOutTemplate::new(20000, Some(2))],
..Default::default()
},
TxTemplate {
tx_name: "confirmed_conflict",
inputs: &[TxInTemplate::PrevTx("confirmed_genesis", 0)],
outputs: &[TxOutTemplate::new(20000, Some(3))],
anchors: &[block_id!(4, "E")],
..Default::default()
},
],
exp_chain_txs: HashSet::from(["confirmed_genesis", "confirmed_conflict"]),
exp_chain_txouts: HashSet::from([("confirmed_genesis", 0), ("confirmed_conflict", 0)]),
exp_unspents: HashSet::from([("confirmed_conflict", 0)]),
exp_balance: Balance {
immature: 0,
trusted_pending: 0,
untrusted_pending: 0,
confirmed: 20000,
},
},
Scenario { Scenario {
name: "2 unconfirmed txs with same last_seens conflict", name: "2 unconfirmed txs with same last_seens conflict",
tx_templates: &[ tx_templates: &[