Skip to content

Add eth_getLogs to the EVM-only Giga RPC - #4308

Merged
masih merged 12 commits into
mainfrom
masih/1790162475-evmonly-get-logs
Sep 24, 2026
Merged

masih merged 12 commits into
mainfrom
masih/1790162475-evmonly-get-logs

Conversation

@masih

@masih masih commented Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator

The EVM-only Giga RPC has no eth_getLogs, so indexers such as sei-street, which poll getContractEvents over 2000-block ranges, cannot follow contract events through Giga even though evmonly already persists logs and blooms and opens the indexed littidx receipt backend whose ReceiptStore.FilterLogs serves range queries.

This adds a filterAPI registered under eth with a GetLogs method that handles both the range form and the blockHash form and passes the full address/topic criteria through to ReceiptStore.FilterLogs. Open and tag bounds resolve to the lower of the backend head and the receipt store's latest version, and the earliest tag to the store's retention floor. Explicit bounds are never clamped: littidx indexes receipts asynchronously and may trail the committed head by a block or two, so a request for [from, eth_blockNumber] answered only up to the indexed head would let an indexer advance its cursor past logs it never saw. Instead an explicit bound above the indexed head fails with a not-yet-indexed error the client can retry, and one below the retention floor fails with a pruned error; an inverted range is rejected, as is a range wider than 2000 blocks, and each query runs under a receipt.LogBudget of 10000 logs / DefaultMaxLogBytes. Because Giga assigns block-wide log indexes at execution time and the receipt store adds the per-transaction firstLogIndex offset again when materialising range-query logs, GetLogs reads each distinct transaction's receipt once and subtracts its first log index so logIndex matches what eth_getTransactionReceipt reports; it also fills blockHash per distinct block from Backend.Block, since stored logs carry no block hash. Rather than another index, this deliberately reuses the existing store path; the stateful filter family and eth_subscribe("logs") are out of scope.

MemoryReceiptStore.FilterLogs previously returned ErrRangeQueryNotSupported and now implements the same query and index conventions as the disk-backed stores, which is what lets the new RPC tests run without an on-disk store. There is no consensus or state impact; the change is confined to the read-only RPC surface. Reviewers should look most closely at the range resolution and the log-index rebase. Validated with new giga/evmonly/rpc tests covering address/topic filters, the blockHash form, bound resolution against the indexed range, range limits, budget enforcement, backend errors, and an end-to-end JSON-RPC round trip, plus the existing giga/evmonly suite.

@devin-ai-integration

Copy link
Copy Markdown
Contributor

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@github-actions

github-actions Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

The latest Buf updates on your PR. Results from workflow Buf / buf (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed✅ passed✅ passed✅ passedSep 24, 2026, 11:23 AM

@codecov

codecov Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.41104% with 14 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.39%. Comparing base (e5f44ae) to head (e084061).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
giga/evmonly/rpc/filter.go 92.78% 7 Missing ⚠️
giga/evmonly/receipt_store.go 92.59% 4 Missing ⚠️
giga/evmonly/rpc/server.go 50.00% 1 Missing ⚠️
sei-db/ledger_db/receipt/log_match.go 87.50% 1 Missing ⚠️
sei-db/ledger_db/receipt/receipt_cache.go 0.00% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #4308      +/-   ##
==========================================
- Coverage   67.49%   66.39%   -1.10%     
==========================================
  Files        2181     2062     -119     
  Lines      167798   155791   -12007     
==========================================
- Hits       113251   103437    -9814     
+ Misses      54537    52344    -2193     
  Partials       10       10              
Flag Coverage Δ
sei-chain-pr 85.63% <91.41%> (?)
sei-db 74.50% <ø> (ø)
sei-db-state-db ?

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
sei-db/ledger_db/receipt/litt_tag_index.go 87.93% <100.00%> (+0.57%) ⬆️
sei-db/ledger_db/receipt/receipt_store.go 73.22% <ø> (+0.54%) ⬆️
giga/evmonly/rpc/server.go 24.44% <50.00%> (+1.18%) ⬆️
sei-db/ledger_db/receipt/log_match.go 87.50% <87.50%> (ø)
sei-db/ledger_db/receipt/receipt_cache.go 13.63% <0.00%> (+1.94%) ⬆️
giga/evmonly/receipt_store.go 85.85% <92.59%> (+7.09%) ⬆️
giga/evmonly/rpc/filter.go 92.78% <92.78%> (ø)

... and 178 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Open and tag bounds resolve to the store's indexed head; explicit bounds
beyond it or below the retention floor now fail instead of returning a
partial result an indexer would mistake for a complete one.
Adds an [evm-only-rpc] section to the node config with
max-blocks-for-logs and max-logs-per-query (defaults 2000 / 10000) and
threads them into the EVM-only RPC server.
// query may cover.
maxBlocksForLogs = 2000
// maxLogsPerQuery is the most logs one eth_getLogs query may return.
maxLogsPerQuery = 10000

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left these as constant for now, given this path might refactor and unclear where we actually want the configuaration for evm-only RPC.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ a const symbol is easier to parameterize than magic values. Sufficient to advance.

@masih
masih marked this pull request as ready for review September 23, 2026 12:01
@cursor

cursor Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

PR Summary

Medium Risk
Read-only RPC surface, but log-index rebasing and strict indexed-range errors affect indexer correctness; wide queries are bounded but still resource-sensitive.

Overview
Adds eth_getLogs to the EVM-only Giga RPC via a new filterAPI registered on the eth namespace. GetLogs resolves block ranges against the receipt store’s indexed head and retention floor (explicit bounds outside that range error instead of returning partial results), enforces a 2000-block window and 10k-log budget, delegates filtering to ReceiptStore.FilterLogs with go-ethereum’s strict topic-position rule, then fills blockHash and rebases logIndex so responses match eth_getTransactionReceipt.

MemoryReceiptStore now implements FilterLogs and IterateReceipts using the same LogsForTx / MatchLogForQuery path as disk backends so RPC tests can run in memory. The receipt package exports MatchLog, LogsForTx, and MatchLogForQuery (optional strict topic count via context); littidx filtering switches to MatchLogForQuery.

Reviewed by Cursor Bugbot for commit e084061. Bugbot is set up for automated code reviews on this repo. Configure here.

@seidroid seidroid Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adds a read-only eth_getLogs to the EVM-only Giga RPC plus a real MemoryReceiptStore.FilterLogs; the range resolution and the log-index rebase are arithmetically correct (the subtraction is exact for Giga's block-wide stored indexes and a no-op for the legacy per-tx convention), and test coverage of the new surface is good. Remaining notes are non-blocking: a pruning race that can still yield a silently partial answer, a doubled receipt read per matching transaction, a duplicated match predicate, and no test pinning the rebase against the real littidx store.

Findings: 0 blocking | 6 non-blocking | 3 posted inline

Blockers

  • None at the file/PR level.

Non-blocking

  • [suggestion] The log-index rebase depends on a convention of the real littidx store (stored Log.Index is block-wide under Giga, so FilterLogs adds firstLogIndex a second time), but every new test runs against MemoryReceiptStore, whose FilterLogs this same PR wrote to mirror that convention — so the tests cannot detect a mismatch with the store actually used in production. The litt store's own tests (TestLittIdxBlockWideLogIndex) write per-transaction-relative indexes, i.e. the opposite convention, which makes the assumption worth pinning: a littidx-backed test that writes Giga-shaped receipts (block-wide Log.Index) and asserts eth_getLogs logIndex equals what eth_getTransactionReceipt reports would cover it.
  • [suggestion] maxBlocksForLogs/maxLogsPerQuery hardcode 2000/10000, which are exactly the defaults of the existing, operator-tunable evm.max_blocks_for_log / evm.max_log_no_block keys (evmrpc/config/config.go). Two copies of the same policy that can drift, and an operator running an indexer against a Giga node has no way to tune them. Sourcing them from the existing config (or at least referencing it) would keep one source of truth.
  • 3 suggestion(s)/nit(s) flagged inline on specific lines.
  • 1 non-blocking pre-existing issue(s) listed below under pre-existing issues.

Pre-existing issues

  • [suggestion] matchLog (sei-db/ledger_db/receipt/receipt_cache.go:271) skips wildcard topic positions without requiring that the log has a topic at that position, so criteria [A, null] match a log whose only topic is A. go-ethereum's filterLogs rejects any log with len(crit.Topics) > len(log.Topics). This is the predicate the littidx store applies, so the new eth_getLogs inherits the divergence on the disk-backed path.

Comment thread giga/evmonly/rpc/filter.go Outdated
Comment thread giga/evmonly/rpc/filter.go
Comment thread giga/evmonly/receipt_store.go Outdated

@shemnon shemnon left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My notes are quasi-optional, if not fixed in this PR then there should be one or more linear issues to address them.

Comment thread giga/evmonly/receipt_store.go Outdated
Comment thread giga/evmonly/receipt_store.go Outdated
Comment thread giga/evmonly/receipt_store.go
Comment thread giga/evmonly/receipt_store.go Outdated
@masih

masih commented Sep 24, 2026

Copy link
Copy Markdown
Collaborator Author

@seidroid review

@seidroid seidroid Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR adds a read-only eth_getLogs to the EVM-only Giga RPC and implements MemoryReceiptStore.FilterLogs. The range resolution, the invalid-params errors and the log-index rebase are correct, and the new surface is well tested. None of the three points from the previous review has been addressed yet, and none of them blocks the merge.

Findings: 0 blocking | 6 non-blocking | 3 posted inline

Blockers

  • None at the file/PR level.

Non-blocking

  • [suggestion] The log-index rebase relies on a convention of the real littidx store: under Giga the stored Log.Index is block-wide, and FilterLogs adds firstLogIndex on top of it. Every new test runs against MemoryReceiptStore, whose FilterLogs this PR wrote to follow that same convention, so the tests cannot catch a mismatch with the store production actually uses. A littidx-backed test would pin it: write Giga-shaped receipts and assert that the logIndex from eth_getLogs matches what eth_getTransactionReceipt reports.
  • [suggestion] maxBlocksForLogs (2000) and maxLogsPerQuery (10000) repeat the defaults of the existing evm.max_blocks_for_log and evm.max_log_no_block keys (evmrpc/config/config.go). The configurable version was reverted in this PR, so the two copies of this policy can drift and operators cannot tune them. If hardcoding is deliberate for now, a comment pointing to the config keys would at least keep them linked.
  • 3 suggestion(s)/nit(s) flagged inline on specific lines.
  • 1 non-blocking pre-existing issue(s) listed below under pre-existing issues.

Pre-existing issues

  • [suggestion] matchLog (sei-db/ledger_db/receipt/receipt_cache.go:271) skips wildcard topic positions without checking that the log has a topic there, so criteria [A, null] match a log whose only topic is A. go-ethereum rejects any log where len(crit.Topics) > len(log.Topics). The littidx path applies this predicate, so the new endpoint inherits the divergence.

Comment thread giga/evmonly/rpc/filter.go Outdated
Comment thread giga/evmonly/rpc/filter.go
Comment thread giga/evmonly/receipt_store.go Outdated

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread giga/evmonly/rpc/filter.go
…memory store

MatchLog and LogsForTx are exported from sei-db/ledger_db/receipt so the
in-memory store filters with the same code as littidx, and MatchLog now
rejects a filter with more topic positions than the log has topics, matching
go-ethereum. Adds a littidx-backed test pinning eth_getLogs logIndex to the
stored receipt index, and a comment tying the hardcoded getLogs caps to the
legacy evm.max_blocks_for_log / evm.max_log_no_block defaults.
@masih

masih commented Sep 24, 2026

Copy link
Copy Markdown
Collaborator Author

@seidroid review

@seidroid seidroid Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR adds a read-only eth_getLogs to the EVM-only Giga RPC. The range resolution, the pruning re-check, the littidx-backed logIndex test and the shared receipt.MatchLog/LogsForTx are correct, and the earlier predicate-duplication thread is resolved. Two non-blocking points remain. Every matching transaction's receipt is still read a second time. The stricter MatchLog also changes filtering in the legacy evmrpc littidx/cache path, and the receipt package has no test for that change.

Findings: 0 blocking | 2 non-blocking | 2 posted inline

Blockers

  • None at the file/PR level.

Non-blocking

  • None at the file/PR level.
  • 2 suggestion(s)/nit(s) flagged inline on specific lines.

// offset again when it materializes range-query logs, so this is the amount to
// subtract from each returned log's Index.
func (api *filterAPI) firstLogIndex(ctx context.Context, txHash common.Hash) (uint, error) {
stored, err := api.store.GetReceipt(receiptContext(ctx), txHash)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[suggestion] Still open from the last two reviews. The rebase gives the right answer, but it costs a second GetReceipt and proto decode for every distinct matching transaction, even though the store already decoded that receipt inside FilterLogs. With a 10000-log budget that is up to 10000 extra point reads, issued one after another. It also leaves logIndex silently dependent on the store adding firstLogIndex. The new littidx test now pins that convention, which helps, but the cost is unchanged. Having the store report the offset, or stop applying it, would remove both the extra reads and the coupling, with the change made in one place.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Acknowledged; deliberately deferred to a follow-up since it needs a ReceiptStore API change (store reports firstLogIndex or stops applying it) rather than an RPC-layer fix. The littidx test in this PR pins the current convention so the follow-up can change it in one place.

Comment thread sei-db/ledger_db/receipt/receipt_cache.go Outdated
…a only

The shared predicate also serves the legacy evmrpc littidx path, whose
block-scan fallback accepts trailing wildcard positions beyond a log's
topics, so the stricter go-ethereum rule now lives in filterAPI.GetLogs
instead. Adds a MatchLog unit test.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0a244e6. Configure here.

Comment thread giga/evmonly/rpc/filter.go Outdated
Comment thread giga/evmonly/rpc/filter.go Outdated
@masih
masih requested review from alrevuelta and shemnon September 24, 2026 11:20
… budgeting

Giga opts in via receipt.WithStrictTopicCount on the request context, so
littidx and memory FilterLogs reject logs with fewer topics than filter
positions before budget.Reserve. Legacy evmrpc callers keep the lenient
MatchLog behavior.
// query may cover.
maxBlocksForLogs = 2000
// maxLogsPerQuery is the most logs one eth_getLogs query may return.
maxLogsPerQuery = 10000

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ a const symbol is easier to parameterize than magic values. Sufficient to advance.

}
if s.latestVersion >= 0 && toBlock > uint64(s.latestVersion) { //nolint:gosec // latestVersion is non-negative.
toBlock = uint64(s.latestVersion) //nolint:gosec // latestVersion is non-negative.
it, err := s.IterateReceipts(fromBlock)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Less data read and less code lines. Two wins in one!

@shemnon

shemnon commented Sep 24, 2026

Copy link
Copy Markdown
Contributor

LGTM, my prior approval is still valid.

@masih
masih added this pull request to the merge queue Sep 24, 2026
Merged via the queue into main with commit 99254f7 Sep 24, 2026
92 checks passed
@masih
masih deleted the masih/1790162475-evmonly-get-logs branch September 24, 2026 21:30
yzang2019 added a commit that referenced this pull request Sep 25, 2026
* main: (21 commits)
  Backport evmonly parse, app-hash and changeset perf fixes from giga-1 (#4345)
  Remove the oracle module behind a v6.8 upgrade (#4319)
  fix(seidb): report only the current migration boundary on the snapshot gauge (#4327)
  fix(flatkv): keep 10 old checkpoints instead of mirroring memIAVL's count (#4322)
  Backport Autobahn execute-loop and produced-tx metrics from giga-1 (#4330)
  Add giga.storage.receipts to toggle the Autobahn receipt store (#4333)
  optimize gather phase (#4326)
  Regenerate the Unreleased changelog as a plain PR list (#4336)
  Bump sei-protocol/go-ethereum to v1.15.7-sei-21 (#4332)
  Fail dynamic-gas precompile out-of-gas as an EVM out-of-gas call (#4318)
  Add dashboard and topology option for Autobahn e2e (#4167)
  Add Giga fetch/serve and BlockDB prune metrics (#4329)
  Add eth_getLogs to the EVM-only Giga RPC (#4308)
  Generate v6.8 precompiles (#4320)
  Add [giga] app.toml section and honor it on the Autobahn node (#4323)
  feat(evmonly): add eth_estimateGas via existing libraries (#4325)
  Use Pebble batch directly in SS (#4300)
  Fix pruning issue in SS causing huge disk spike (#4321)
  Make Autobahn always run the EVM-only executor, disable/remove some integration tests (#4316)
  reduce seal lock contention (#4314)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants