Skip to content

perf(evmonly): read nonce and balance through the pending overlay - #4458

Open
bdchatham wants to merge 2 commits into
mainfrom
brandon2/backport-pending-overlay-to-main
Open

bdchatham wants to merge 2 commits into
mainfrom
brandon2/backport-pending-overlay-to-main

Conversation

@bdchatham

Copy link
Copy Markdown
Contributor

This is a backport to main of #4271 (original giga-1 PR #4258) from the giga-1 PR stack #4270–#4273. That stack targeted stacked giga-1 branches and never reached main, except #4270, which landed as #4366. @codchen approved #4271 at 313568b. Since that commit, the port uses main's baseAccountReader and moves app.go's package constants to the top. It also makes the closed executor's synchronous commit advance the commit generation, and makes the mempool's refresh() advance the accounts epoch; a straight port would have shipped both bugs, and each fix has a test that fails without it. Tracked in PLT-1378.

EvmNonce and EvmBalance on the EVM-only app no longer wait for the in-flight block commit. Executor.ReadLatestAccount lays the pending block's changes over a store view and starts over when a commit starts under it. After a failed commit, reads fall back to the settled store. The mempool fetches a first-seen account outside its lock and installs it only if the accounts epoch is unchanged. The autobahn producer's nonce reads use the same path, including the one pruneMempool makes under the producer lock.

This is not app-hash-breaking. Execution reads the same overlay, and a read returns the value the settled store returns once the commit lands; TestEVMOnlyApplicationLatestAccountMatchesTheSettledStore checks this across blocks. In a local run with 2,000-transfer blocks, a nonce read issued right after FinalizeBlock dropped from p50 0.6 ms (max 11 ms) to under 10 µs (max 27 µs). FinalizeBlock time did not change, and main-loop execution cost does not drop. Review the ReadLatestAccount retry loop, which is bounded in practice by the block rate, and note that a steady-state read now loads the full account row (about +10–25 µs). It merges cleanly with #4452 and the #4272 backport in any order.

Executor.ReadLatestAccount lays the in-flight block's pending changes over a
store view, so EvmNonce and EvmBalance no longer wait for the block's commit
to land. A commit generation restarts a read that a commit overtook. The
mempool fetches a first-seen account outside the store lock and installs it
only if the accounts epoch is unchanged.

Backport of #4271 (giga-1 #4258). Adapted to main: the closed executor's
synchronous commit and the mempool's refresh also advance their counters,
the account read uses baseAccountReader, and the app's package
constants move to the top of the file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@bdchatham

Copy link
Copy Markdown
Contributor Author

@seidroid review

@cursor

cursor Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

PR Summary

Medium Risk
Changes concurrent read paths for account state (executor generation retries, mempool epoch) alongside mempool admission; extensive tests cover races, but correctness under commit failure and overlay ordering is subtle.

Overview
Nonce and balance reads no longer block on the in-flight block commit. The executor adds ReadLatestAccount, which layers the pending pipeline changes over a store view and retries when pipelineGeneration advances so stale overlays are not applied on top of newer committed state. Failed commits surface as errors instead of partial state.

The EVM-only app routes EvmNonce and EvmBalance through that path (via latestAccount), so mempool and producers see the block FinalizeBlock just executed without calling AwaitCommits first.

Pending overlay plumbing is refactored: indexed state lives in pendingChanges with overlay(), shared by block execution and latest-account reads.

Mempool inserts prefetch first-seen sender balance/nonce outside the store mutex; an accountsEpoch invalidates prefetches taken before Update, refresh, or Clear, so inserts are not validated against stale nonces while other mempool ops stay unblocked.

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

@codecov

codecov Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 87.34177% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 56.81%. Comparing base (0b93e66) to head (300a701).

Files with missing lines Patch % Lines
giga/evmonly/giga_store.go 89.74% 4 Missing ⚠️
sei-tendermint/internal/evmonlyapp/app.go 66.66% 4 Missing ⚠️
sei-tendermint/internal/mempool/tx.go 90.90% 2 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##             main    #4458   +/-   ##
=======================================
  Coverage   56.81%   56.81%           
=======================================
  Files        2127     2127           
  Lines      167022   167089   +67     
=======================================
+ Hits        94888    94936   +48     
- Misses      72129    72148   +19     
  Partials        5        5           
Flag Coverage Δ
sei-chain 55.00% <87.34%> (+<0.01%) ⬆️
sei-db 75.17% <ø> (ø)
sei-db-state-db 78.83% <ø> (ø)

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

Files with missing lines Coverage Δ
giga/evmonly/executor.go 88.88% <ø> (ø)
giga/evmonly/pipeline_overlay.go 94.04% <100.00%> (+0.22%) ⬆️
sei-tendermint/internal/mempool/tx.go 88.94% <90.90%> (+0.11%) ⬆️
giga/evmonly/giga_store.go 87.77% <89.74%> (+0.36%) ⬆️
sei-tendermint/internal/evmonlyapp/app.go 87.00% <66.66%> (-0.64%) ⬇️

... and 24 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.

@bdchatham

Copy link
Copy Markdown
Contributor Author

@seidroid review

@github-actions

github-actions Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

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

BuildFormatLintBreakingUpdated (UTC)
✅ passed✅ passed✅ passed✅ passedOct 5, 2026, 11:58 PM

@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 change serves EvmNonce and EvmBalance on the EVM-only app through the executor's pending overlay, using a commit-generation retry instead of waiting for the in-flight commit, and moves the mempool's first-seen account fetch outside the store lock behind an accounts epoch. Against the store's point-in-time view contract, the generation check, the synchronous-commit bump, the failed-commit fallback and the epoch bumps on every accounts reset all hold, so nothing blocks; codex's one finding was dropped as pre-existing, because insert's account() already adds the sender before the old-nonce, duplicate-hash and same-nonce rejections and accounts are rebuilt on every Update, while the one newly reachable path (a byHash duplicate) already has its sender tracked, so prefetch does nothing there. Go was unavailable in the sandbox, so the tests were not run.

seidroid review · decision approve · session 7c3bfb9c2afd4b079a0e9d3ff0779f27 · turn resp_claude_00363251295d048fe117e02f75c43f39 · item 9419d2491ecf5af78c6af8b839bdb57e

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

@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 300a701. Configure here.

func (e *Executor) readLatestAccount(snapshot gigatypes.EVMStateView, pending *pendingChanges, generation uint64, addr common.Address) (LatestAccount, bool) {
e.pipelineMu.Lock()
moved := e.pipelineGeneration != generation
e.pipelineMu.Unlock()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Failed commit can still succeed a read

Medium Severity

ReadLatestAccount checks pipelineFailureLocked only before OpenView, and readLatestAccount rechecks generation but not failure. A commit that fails during that window leaves generation unchanged, so the read can still overlay the failed block and return success. latestAccount then keeps that never-committed nonce and balance instead of falling back to the settled store.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 300a701. Configure here.

This branch has not been deployed

No deployments
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.

1 participant