Skip to content

Lower bound fixes + gold lower bounds#248

Merged
KirillPamPam merged 4 commits into
mainfrom
lower_bound_fixes
Jul 9, 2026
Merged

Lower bound fixes + gold lower bounds#248
KirillPamPam merged 4 commits into
mainfrom
lower_bound_fixes

Conversation

@KirillPamPam

@KirillPamPam KirillPamPam commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Gold lower bounds + lower-bound detection fixes

Summary

This PR fixes several correctness bugs in EVM lower-bound detection and adds a fast
"gold lower bound" short-circuit that lets us skip the binary search entirely when a
chain ships a known-oldest data point. It also splits the monolithic EVM detector into
per-type files and hardens the retry behaviour of the shared search.

Gold lower bounds

chains.yaml now carries an optional lower-bounds section per chain, parsed into new
ChainLowerBounds / GoldLowerBound types (pkg/chains/chains.go) and threaded through
ConfiguredChain. A gold bound is a known-oldest {block, hash} pair for a chain. This
data already ships in the bundled chains.yaml submodule — it was simply never parsed, so
the change is purely additive (bitcoin and other chains without the section stay nil).

When a chain has a gold hash for tx or receipts, the detector issues a single
eth_getTransactionByHash / eth_getTransactionReceipt probe for that ancient hash:

  • Hit → the upstream serves that oldest datum, so it is fully archival and the bound
    resolves to 1 with no binary search.
  • Miss (null result, no-data error, or any other error) → falls back to the normal
    search path.

This is implemented in the new gold_bound.go and short-circuits at the top of
DetectLowerBound. EVM detectors now receive the full *chains.ConfiguredChain (instead
of just chains.Chain) so they can read the configured bounds; LowerBoundResults was
exported so the short-circuit reuses the same per-supported-type fan-out as the search.

Lower-bound search fixes

The shared LowerBoundSearchCalculator (lower_bound_search.go) was rewritten to fix
real detection bugs:

  • Genesis-hole bug. The old search trusted block 1 whenever it was retained. If a node
    retained genesis but had a hole above it, it reported a bogus bound of 1. The search now
    converges on the real lower boundary of the contiguous retained range regardless of
    whether genesis happens to be present.
  • Offset search for tx/receipts. Historical blocks may legitimately contain no
    transactions, which made a naive "first tx of the middle block" probe give up too early.
    A new offset variant (detectWithOffset / shiftLeftAndSearch) scans downward up to
    maxOffset (20) blocks from a no-data midpoint to tolerate sporadic empty/missing blocks;
    a hole wider than the offset is abandoned and the search moves higher. Tx and receipts
    detectors use this variant; block/state/proof use the plain binary search.
  • No-data errors treated as "no data here". Archival/pruning gaps surface as a wide
    range of upstream error messages. These are now classified (no_data_errors.go) and
    treated as "no data at this height, search higher" rather than aborting the whole
    detection. Only genuinely unexpected failures propagate.
  • Convergence guard. On a non-trivial chain (latest > 10) where nothing is ever
    confirmed, detection now fails cleanly so the processor keeps the previously cached
    bound instead of publishing a bogus 1. Tiny chains still resolve to 1.
  • Cached-bound reuse. A previously found bound is re-confirmed with a single probe
    before any re-search, keeping steady-state cost to one call per cycle.
  • Retry policy. Probes and latest-height fetches now retry with exponential backoff
    (up to 30 attempts, 1s → 1m) instead of the previous fixed 3× / 100ms, so transient
    upstream errors no longer poison a detection cycle. SetSearchRetryPolicy lets tests
    run with negligible backoff.

Refactor: split evm_bounds by bound type

evm_lower_bound.go was ~470 lines mixing construction, five per-type probes, JSON
helpers, and the error-hint list. It is now split into focused files so each bound type
reads as a self-contained unit, with the shared wiring left behind:

  • block_bound.go, state_bound.go, tx_bound.go, receipts_bound.go, proof_bound.go
    — one constructor + probe per data type.
  • evm_util.go — block/tx envelope parsing and hex/JSON helpers.
  • no_data_errors.go — the no-data error classification and hint list.
  • gold_bound.go — the gold short-circuit.
  • evm_lower_bound.go — construction, probe dispatch, fetchLatestHeight, and the
    JSON-RPC call helper.

@KirillPamPam KirillPamPam merged commit 59aa4bc into main Jul 9, 2026
5 checks passed
@KirillPamPam KirillPamPam deleted the lower_bound_fixes branch July 9, 2026 11:50
a10zn8 added a commit that referenced this pull request Jul 10, 2026
main's lower-bound rework (#248) changed LowerBoundDetector to take the
caller's context; thread it through fetchLedgerInfo instead of deriving
from context.Background()

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

2 participants