Skip to content

perf: bound and batch ancestor fetching - #27

Merged
LORDBABUINO merged 2 commits into
mainfrom
perf/bounded-batched-ancestors
Sep 1, 2026
Merged

perf: bound and batch ancestor fetching#27
LORDBABUINO merged 2 commits into
mainfrom
perf/bounded-batched-ancestors

Conversation

@LORDBABUINO

Copy link
Copy Markdown
Collaborator

Descriptor scans on mainnet were pathologically slow for two stacked reasons. The wallet-history path walked transaction ancestry with no depth limit, tracing every wallet transaction back toward genesis until it hit coinbases. And every transaction in that walk cost one HTTP round trip to bitcoind.

Changes:

  1. The wallet-history ancestor walk is now bounded by max_ancestor_depth (default 2), matching what the UTXO path already did. A regression test builds a linear 5-transaction chain on regtest and asserts only depth 0 to 2 gets fetched; against the old code it fails with the whole chain (6 transactions) in the graph.
  2. Each BFS frontier is fetched with JSON-RPC batch requests, 100 getrawtransaction calls per POST, instead of one call each. Wallet-level transactions still fail the scan on error; missing ancestors are skipped.
  3. The gateway trait gains get_transactions with a sequential default implementation, so alternative gateways and mocks keep working unchanged.
  4. Drive-by: the last two production unwraps (lookback-depth detector) became a let-else.

Measured on a real mainnet node (height 953137, txindex): the same descriptor scan with a birth timestamp that previously ran 40+ minutes without finishing now completes in 2.5 seconds, wallet unloaded cleanly, findings intact. Full suite 38/38 with clippy -D warnings clean.

The descriptor path walked transaction ancestry with no depth limit,
tracing every wallet tx back toward genesis. On mainnet this meant
scans that ran for 40+ minutes without finishing. The UTXO path was
bounded but fetched one transaction per HTTP round trip.

- bound the wallet-history ancestor walk by max_ancestor_depth,
  matching the UTXO path (regression test proves the old code
  fetched the whole chain)
- fetch each BFS frontier with JSON-RPC batch requests (100 per
  POST) instead of one call per transaction
- add get_transactions to the gateway trait with a sequential
  default so mocks and tests keep working

Same mainnet descriptor scan that never finished now completes in
2.5 seconds end to end, findings intact.
Production rule going forward: no unwrap. The two here were guarded
by the len() >= 2 check, but let-else states that directly.
@LORDBABUINO
LORDBABUINO merged commit 12cb23c into main Sep 1, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant