Skip to content

bdk_esplora: no transaction cache, re-fetches full tx body on every sync #2250

Description

@GuTS805

Describe the enhancement
bdk_esplora re-downloads the full transaction body for every tracked txid on every sync, even when the transaction was already fetched in a previous sync and only its confirmation status may have changed. bdk_electrum does not have this problem.

BdkElectrumClient (crates/electrum/src/bdk_electrum_client.rs:27) maintains a persistent tx_cache: Mutex<HashMap<Txid, Arc<Transaction>>>, along with a populate_tx_cache() method to seed it from an existing TxGraph. Already-known transactions are never re-fetched.

bdk_esplora has no equivalent. Both crates/esplora/src/async_ext.rs and crates/esplora/src/blocking_ext.rs only build a throwaway HashSet<Txid> (inserted_txs) local to each sync call — it's discarded once the function returns. There is no persistent cache at the client level, and no wrapper struct in lib.rs to hold one.

Every txid passed via request.iter_txids() / iter_outpoints() triggers a call to client.get_tx_info(&txid) in fetch_txs_with_txids, which downloads the entire transaction body, even though esplora_client already exposes a lightweight get_tx_status() (GET /tx/{txid}/status) that returns only the confirmation status. I confirmed get_tx_status is never called anywhere in bdk_esplora.

There is a TODO already acknowledging this gap:

  • async_ext.rs:495
  • blocking_ext.rs:454

// TODO: We should maintain a tx cache (like we do with Electrum).

Proposed fix

  1. Add a persistent transaction cache to bdk_esplora, mirroring BdkElectrumClient's pattern — including a populate_tx_cache() method so users can seed it from an existing wallet/TxGraph on startup.
  2. For txids already present in the cache, use the lightweight get_tx_status() call instead of get_tx_info() to check for status changes, rather than re-downloading the full transaction.
  3. Only call get_tx_info() (full fetch) for genuinely new/unseen txids.

Use case
Wallets that sync on an interval (e.g. mobile wallets polling every few seconds/minutes) end up re-downloading the full transaction body for every tracked unconfirmed txid on every single sync, purely to check whether its status changed — wasted bandwidth and slower syncs, especially with many tracked/unconfirmed transactions.

Impact

  • Blocking production usage
  • Nice-to-have / UX improvement
  • Developer experience / maintainability

Are you using BDK in a production project?

  • Yes
  • No
  • Not yet, but planning to

Which backend(s) are relevant (if any)?

  • Electrum
  • Esplora
  • Bitcoin Core RPC
  • None / not backend-related (e.g. bdk_chain, bdk_core)
  • Other (please specify): ____

Project or organization (optional)

Additional context
Confirmed via direct code inspection on master (commit 337e9d68). No cryptography involved, scope is contained to the bdk_esplora crate only.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions