Electrum: `batch_fetch_anchors` keeps returning the pre-reorg anchor once header and anchor caches are populated

Offen
#2,310 1 Kommentar 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

Bewertung

Schwierigkeit
3/5
Geschätzter Aufwand
1-2 Tage
Anfängerfreundlichkeit
76/100
Issue-Typ
Bug
Klarheit
Klar beschrieben
Aktivitätsstatus
Aktiv
Tech-Stack
rust
Bereich
blockchain

Rechercherichtung

Beginne in crates/electrum/src/bdk_electrum_client.rs bei batch_fetch_anchors und dem nahegelegenen test_batch_fetch_anchors_reorg_uses_new_hash. Führe cargo test -p bdk_electrum --lib batch_fetch_anchors_after_reorg_with_populated_caches mit der bereitgestellten Reproduktion aus. Fertig ist es, wenn der Reorg-Test mit befüllten Caches erfolgreich ist und der zurückgegebene anchor den Block-Hash verwendet, der sich derzeit auf dieser Höhe befindet.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Beschreibung

bug

Describe the bug

batch_fetch_anchors (crates/electrum/src/bdk_electrum_client.rs) derives each height's block hash from block_header_cache and, on an anchor_cache hit for (txid, hash), returns the cached anchor without fetching a header or validating a merkle proof. The header cache is only refreshed when a proof fails to validate, which never happens on the cached path. So once both caches hold the pre-reorg state for a (txid, height), a reorg that re-includes the transaction in a different block at the same height goes unnoticed: every subsequent call returns the anchor with the replaced block's hash, for the lifetime of the BdkElectrumClient.

fetch_tip_and_latest_blocks does pick up the new hash for the LocalChain, so the sync result has a chain with the new hash at that height and an anchor pointing at the old one. Canonicalization then treats the transaction as unconfirmed even though it is confirmed, and later syncs with the same client do not correct it. The existing test_batch_fetch_anchors_reorg_uses_new_hash only covers the case where the anchor cache is empty.

This issue was found by AI.

To Reproduce

Add the following to the test module in crates/electrum/src/bdk_electrum_client.rs (next to test_batch_fetch_anchors_reorg_uses_new_hash) and run cargo test -p bdk_electrum --lib batch_fetch_anchors_after_reorg_with_populated_caches:

#[cfg(feature = "default")]
#[test]
fn batch_fetch_anchors_after_reorg_with_populated_caches() -> anyhow::Result<()> {
    let env = TestEnv::new()?;
    let client = electrum_client::Client::new(env.electrsd.electrum_url.as_str()).unwrap();
    let electrum_client = BdkElectrumClient::new(client);

    env.mine_blocks(101, None)?;
    let addr = env
        .rpc_client()
        .get_new_address(None, None)?
        .address()?
        .assume_checked();
    let txid = env.send(&addr, Amount::from_sat(50_000))?;
    env.mine_blocks(1, None)?;
    env.wait_until_electrum_sees_block(Duration::from_secs(6))?;
    let height = env.rpc_client().get_block_count()?.into_model().0 as usize;

    // First call populates both the header cache and the anchor cache.
    let pre_reorg = electrum_client.batch_fetch_anchors(&[(txid, height)])?;
    let stale_hash = pre_reorg[0].1.block_id.hash;

    // Replace the confirming block; the tx is re-included at the same height.
    env.reorg(1)?;
    env.wait_until_electrum_sees_block(Duration::from_secs(6))?;
    let new_hash = electrum_client.inner.block_header(height)?.block_hash();
    assert_ne!(new_hash, stale_hash);

    let anchors = electrum_client.batch_fetch_anchors(&[(txid, height)])?;
    assert_eq!(anchors[0].1.block_id.hash, new_hash);
    Ok(())
}

Current output:

assertion `left == right` failed
  left: 5cb52f60b4f1ef75606e44e9dc8356fd6ca043eb887a613d6c498f1095930652
 right: 34b9db58142dc754829f9094e68c48a5b1c911f5d1ea84a531758fa906789605

Expected behavior

After a reorg, anchors returned by batch_fetch_anchors should reflect the block currently at that height rather than a previously cached one.

Vorherrschende Sprache
Rust
Sterne
1.1k
Forks
491
Ø Merge
1 T. 5 Std.
Gemergte PRs (30 T.)
1

Beitragsleitfaden

Beitragsleitfaden öffnen

Erste Schritte

  1. Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
  3. Forken Sie das Repository und arbeiten Sie in einem Branch.
  4. Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.

Mehr aus bitcoindevkit/bdk

Alle Issues in bitcoindevkit/bdk

Ähnliche Issues

Weitere Issues zu Rust

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.