Skip to content

feat(evmonly): run load tests on Giga storage - #4088

Open
codchen wants to merge 10 commits into
mainfrom
codex/evmonly-receipt-store
Open

feat(evmonly): run load tests on Giga storage#4088
codchen wants to merge 10 commits into
mainfrom
codex/evmonly-receipt-store

Conversation

@codchen

@codchen codchen commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • persist EVM-only execution receipts through the shared receipt.ReceiptStore owned by the existing bootstrap.GigaStorageManager
  • run standalone and Autobahn EVM-only load tests against disk-backed FlatKV state and littidx receipts instead of the in-memory test stores
  • use the same manager-owned littblock store for Autobahn EVM-only consensus blocks
  • encode nonce, code, and storage changes into FlatKV changesets, including storage-prefix clears
  • keep balances in an EVM-only process-local placeholder until the storage team adds FlatKV balance access; apply placeholder changes only after a successful state commit
  • seed standalone non-balance genesis state at height 1 and execute measured blocks from height 2
  • retain MemoryStore and MemoryReceiptStore as documented unit-test implementations only
  • rename the load-test mode from evm-only-in-memory to evm-only

Storage boundary

This PR has no net changes under sei-db. It consumes the existing manager APIs: StateDB(), ReceiptDB(), and BlockStore(). The standalone harness opens the full production storage manager, including the block store, but has no consensus layer to populate blocks.

Testing

  • scripts/ramtest.sh ./giga/evmonly/... ./sei-tendermint/internal/evmonlyapp/... ./sei-tendermint/config/... ./cmd/autobahn-e2e/... -count=1
  • scripts/ramtest.sh ./sei-tendermint/node/... -run 'TestSelectAutobahnBlockStoreOwnership|TestPrepareApplicationEVMOnly|TestValidateNodeSetupConfig|TestBuildGigaConfig|TestPreparePersistentStateDir' -count=1
  • scripts/ramtest.sh ./giga/evmonly/... ./sei-tendermint/internal/evmonlyapp/... -race -count=1
  • go test -tags autobahn_integration -c ./integration_test/autobahn
  • golangci-lint run --timeout 10m — 0 issues
  • golangci-lint fmt --diff
  • gofmt and goimports on every touched Go file
  • bash -n docker/localnode/scripts/step4_config_override.sh docker/localnode/scripts/step5_start_sei.sh
  • git diff --quiet origin/main...HEAD -- sei-db

@github-actions

github-actions Bot commented Sep 3, 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 7, 2026, 6:19 AM

@codecov

codecov Bot commented Sep 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 66.14786% with 174 lines in your changes missing coverage. Please review.
✅ Project coverage is 60.32%. Comparing base (abdcc45) to head (b930957).

Files with missing lines Patch % Lines
giga/evmonly/receipt_store.go 61.94% 38 Missing and 13 partials ⚠️
sei-tendermint/node/setup.go 30.23% 25 Missing and 5 partials ⚠️
giga/evmonly/cmd/evmonly-loadtest/state.go 39.47% 14 Missing and 9 partials ⚠️
giga/evmonly/flatkv_changeset.go 73.97% 9 Missing and 10 partials ⚠️
sei-tendermint/internal/evmonlyapp/app.go 72.54% 12 Missing and 2 partials ⚠️
sei-tendermint/node/node.go 33.33% 12 Missing and 2 partials ⚠️
giga/evmonly/cmd/evmonly-loadtest/pipeline.go 60.00% 5 Missing and 5 partials ⚠️
sei-tendermint/node/public.go 83.78% 4 Missing and 2 partials ⚠️
giga/evmonly/balance_store.go 90.00% 1 Missing and 1 partial ⚠️
giga/evmonly/giga_store.go 88.88% 1 Missing and 1 partial ⚠️
... and 2 more
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #4088      +/-   ##
==========================================
- Coverage   61.21%   60.32%   -0.89%     
==========================================
  Files        2196     2085     -111     
  Lines      193395   179810   -13585     
==========================================
- Hits       118386   108475    -9911     
+ Misses      63793    61179    -2614     
+ Partials    11216    10156    -1060     
Flag Coverage Δ
sei-chain-pr 69.91% <66.14%> (?)
sei-db 69.80% <ø> (ø)
sei-db-state-db ?

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

Files with missing lines Coverage Δ
cmd/autobahn-e2e/aws.go 53.01% <100.00%> (ø)
giga/evmonly/executor.go 86.89% <100.00%> (-0.05%) ⬇️
giga/evmonly/memory_store.go 80.45% <ø> (ø)
giga/evmonly/storage_manager.go 100.00% <100.00%> (ø)
sei-tendermint/config/autobahn.go 50.00% <ø> (ø)
sei-tendermint/config/config.go 83.67% <ø> (ø)
sei-tendermint/config/toml.go 57.62% <ø> (ø)
sei-tendermint/node/seed.go 52.13% <ø> (ø)
cmd/autobahn-e2e/deploy.go 4.34% <0.00%> (ø)
giga/evmonly/balance_store.go 90.00% <90.00%> (ø)
... and 10 more

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

@codchen
codchen marked this pull request as ready for review September 3, 2026 13:06
@cursor

cursor Bot commented Sep 3, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Touches consensus-adjacent EVM-only commit ordering (receipts then state), persistent storage wiring, and Autobahn cluster defaults; balances remain non-durable in the placeholder until FlatKV support lands.

Overview
Renames the Autobahn/load-test mode from evm-only-in-memory to evm-only and switches EVM-only execution from ephemeral in-memory state to the production bootstrap.GigaStorageManager (FlatKV state, littidx receipts, manager-owned blocks).

The evmonly executor now requires both a state store and a receipt.ReceiptStore: receipts are written before the height-advancing state commit, with fail-closed ordering (receipt errors skip state commit; state errors can leave receipts that retries overwrite). WithStorageManager, WithReceiptStore, and WithBalanceStore replace the old single WithStore path for runtimes; NewFlatKVChangeSetEncoder maps nonce/code/storage (including prefix clears) into FlatKV while balances stay in PlaceholderBalanceStore until FlatKV balance I/O exists.

evmonly-loadtest and evmonlyapp (moved out of p2p) open real storage under Autobahn’s persistent state dir or a temp dir, seed non-balance genesis at height 1, and run measured blocks from height 2. Docker/Makefile/e2e deploy paths set AUTOBAHN_EVMONLY=true and drop the in-memory autobahn config flag. Tendermint prepareApplication owns manager lifecycle, shares the block store with Autobahn when enabled, and rejects evm-only in seed mode.

Reviewed by Cursor Bugbot for commit b930957. 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.

Solid, well-tested refactor that routes EVM-only executor receipts through the shared receipt.ReceiptStore and moves Autobahn block-store ownership into a GigaStorageManager that the node closes after giga.Run. No blockers; the notable items are a README reference to a type that doesn't exist, an unrecoverable state/receipt ordering on the persistence path, and the executor now depending on the concrete bootstrap manager.

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

Blockers

  • None at the file/PR level.

Non-blocking

  • [suggestion] newReceiptContext builds an sdk.Context with a nil MultiStore. That is safe for both current ReceiptStore backends (the write path never touches the store, and legacyReceiptFromKVStore guards on a nil storeKey), but nothing records the assumption. A one-line comment on newReceiptContext stating that the returned context carries no store and is only valid for backends constructed without a legacy storeKey would keep the next caller from hitting a nil deref via ctx.KVStore.
  • 5 suggestion(s)/nit(s) flagged inline on specific lines.

Comment thread giga/evmonly/README.md Outdated
Comment thread giga/evmonly/giga_store.go
Comment thread giga/evmonly/storage_manager.go
Comment thread giga/evmonly/receipt_store.go Outdated
Comment thread sei-tendermint/node/public.go
@codchen

codchen commented Sep 4, 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.

The three fixes from the last round landed correctly: receipts are now written before the height-advancing state commit (with tests covering both failure directions), seed mode rejects EVMOnlyInMemory at config validation, and the README no longer references a nonexistent type. Storage ownership through NewmakeNodecloseGigaStorageManager is leak-free on every path I traced; the remaining items are the two unresolved design/retention suggestions plus two new ones on the injected-manager contract.

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

Blockers

  • None at the file/PR level.

Non-blocking

  • [suggestion] newReceiptContext (giga/evmonly/receipt.go) still builds an sdk.Context with a nil MultiStore and carries no comment recording that. It is safe for both current backends (receiptStore.SetReceipts never touches the multistore, and MemoryReceiptStore ignores it), but the executor now accepts any receipt.ReceiptStore via WithStorageManager, so a one-line note that the returned context has no store would keep the next backend from hitting a nil deref through ctx.KVStore.
  • 4 suggestion(s)/nit(s) flagged inline on specific lines.

Comment thread giga/evmonly/receipt_store.go Outdated
Comment thread sei-db/bootstrap/storage_manager.go Outdated
Comment thread giga/evmonly/storage_manager.go
Comment thread sei-tendermint/internal/evmonlyapp/app.go Outdated
@codchen
codchen marked this pull request as draft September 7, 2026 04:46
@codchen codchen changed the title feat(evmonly): persist executor receipts feat(evmonly): run load tests on Giga storage Sep 7, 2026

@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.

Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 93687db. Configure here.

Comment thread sei-tendermint/internal/evmonlyapp/app.go
@codchen
codchen marked this pull request as ready for review September 7, 2026 07:21
@codchen

codchen commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator Author

@seidroid review

4 similar comments
@codchen

codchen commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator Author

@seidroid review

@masih

masih commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

@seidroid review

@alexander-sei

Copy link
Copy Markdown
Contributor

@seidroid review

@masih

masih commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

@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.

Solid follow-up: the EVM-only executor now persists non-balance state through FlatKV and receipts through the manager-owned littidx store, MemoryStore/MemoryReceiptStore are demoted to unit-test doubles, and the NewGigaStorageManagerWithStores test-shaped constructor is gone (the PR has no net sei-db diff). No blockers; the remaining items are restart semantics of the now-persistent EVM-only app, a fail-closed gap when a FlatKV state store is used without a balance store, and two convention/dependency points.

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

Blockers

  • None at the file/PR level.

Non-blocking

  • [suggestion] newReceiptContext (giga/evmonly/receipt.go) still builds an sdk.Context with a nil MultiStore and carries no comment saying so. It is safe for the two backends in play — littReceiptStore.SetReceipts never touches the multistore and its GetReceipt fallback guards on a nil storeKey — but the executor accepts any receipt.ReceiptStore, so a one-line note that the returned context has no store would keep the next backend from hitting a nil deref through ctx.KVStore.
  • [suggestion] giga/evmonly/cmd/evmonly-loadtest/pipeline.go commits generated genesis at height 1, but state.changeSet() emits only nonce/code/storage pairs, so a pure-transfer run with all-zero nonces produces nil changesets and the genesis commit is an empty block. That works (FlatKV accepts an empty contiguous block), but neither flatkv_changeset_test.go nor the loadtest tests cover the empty-changeset encode → commit path, which is the default workload's shape.
  • 4 suggestion(s)/nit(s) flagged inline on specific lines.

func evmOnlyInMemoryGasLimit(req *abci.RequestInitChain) (uint64, error) {
func (a *evmOnlyApplication) seedInitialStateVersion(initialHeight int64) error {
stateStore := a.storage.SC()
if stateStore == nil || initialHeight == 1 {

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] The initialHeight == 1 short-circuit skips the guard in exactly the case where it is needed. State, receipts and blocks now live in data/autobahn (step4 no longer passes --persistent-state-dir=), so that directory survives a container/process restart, while evmOnlyStatecommittedHeight, appHash, parentHash — and the whole PlaceholderBalanceStore are rebuilt empty by NewEVMOnlyApplication. On a restart of a genesis-height-1 chain, Info() reports height 0, InitChain runs again, this function returns early without noticing that SC().GetLatestVersion() is already N, and the first FinalizeBlock fails at CommitStateChanges(1, ...) because FlatKV requires contiguous versions (store_apply.go:45). Codex flags the same path.

It fails closed rather than diverging, but the diagnostic lands two layers away from the cause. Dropping the initialHeight == 1 clause makes the existing "EVM-only state is already at height %d before InitChain" error fire for the common case too (a fresh store returns 0, so height 1 still passes), and SetInitialVersion is already equivalent to a no-op there. A close/reopen test would pin whichever behaviour you choose — refuse to start on a populated directory, or recover committedHeight/appHash from storage.

}

func (r gigaSnapshotStateReader) GetBalance(addr common.Address) *big.Int {
if r.balances != nil {

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] The balance store is optional here, but for the FlatKV-backed state store it is mandatory: flatKVStateView.GetBalance panics (sei-db/state_db/sc/flatkv/state_view.go:107), and encodeFlatKVChangeSet silently drops changes.Balances. So WithStorageManager(manager, NewFlatKVChangeSetEncoder(...)) without a matching WithBalanceStore panics inside the state view on the first balance read, rather than failing with an executor-level error like the sibling errMissingStateStore / errMissingReceiptStore checks.

Both production call sites pass WithBalanceStore today, so this is a latent misconfiguration rather than a live bug — but it is a guard that belongs at the choke point. Either have executePreparedBlockWithStore reject a nil balanceStore when the state view cannot serve balances, or bundle the encoder and the placeholder store into one option so they cannot be supplied apart.

return giga, ownedBlockStore, nil
}

func selectAutobahnBlockStore(

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] The second return value uses a bare nil as "not owned by the caller", which buildGigaRouter propagates and createRouter then tests with if blockStore != nil, plus a closeAutobahnBlockStore helper that exists only to nil-check. sei-tendermint/AGENTS.md asks for utils.Option for optional values and says not to add defensive nil-checks in internal logic — and this file already uses utils.Option[atypes.BlockStore] for exactly this value on the way in (managed) and on the way out of createRouter.

Returning utils.Option[atypes.BlockStore] for the owned store would make ownership explicit at every hop and let closeAutobahnBlockStore collapse into if store, ok := owned.Get(); ok.


// WithStorageManager selects the stores used for state and receipt persistence.
// The encoder converts executor-native state changes into the state store's format.
func WithStorageManager(manager *bootstrap.GigaStorageManager, encoder NamedChangeSetEncoder) Option {

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] Repeating from the last round, with the case now narrower: WithStore and WithReceiptStore are exported here, so WithStorageManager is a two-line convenience wrapper — and it is the only reason giga/evmonly imports sei-db/bootstrap, pulling littblock, flatkv, controller, the checkpoint scheduler and autobahn/blockstore into the package (and into the standalone evmonly-loadtest binary).

Both production call sites already have the manager in hand and could call WithStore(manager.StateDB(), encoder) plus WithReceiptStore(manager.ReceiptDB()) directly, or pass a locally-declared two-method interface. That drops the import and removes the if manager != nil branch, which currently swallows a nil manager into a much later errMissingStateStore.

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.

3 participants