Skip to content

Integrate Autobahn with in-memory EVM-only executor - #4028

Open
codchen wants to merge 1 commit into
codex/integrate-evmonly-giga-storefrom
codex/autobahn-evmonly-integration
Open

Integrate Autobahn with in-memory EVM-only executor#4028
codchen wants to merge 1 commit into
codex/integrate-evmonly-giga-storefrom
codex/autobahn-evmonly-integration

Conversation

@codchen

@codchen codchen commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

Describe your changes and provide context

This PR is stacked on #3864.

  • Add a test-only evm_only_in_memory Autobahn configuration that replaces the Cosmos ABCI application with the EVM-only executor backed by MemoryStore.
  • Accept protected raw Ethereum transactions on chain ID 1337, expose the EVM metadata Autobahn mempool routing needs, and execute finalized blocks with a deterministic app hash.
  • Resolve Autobahn block RPC heights from the GigaRouter execution head so the validator RPC surface works without advancing the Cosmos application.
  • Add Docker configuration and make autobahn-evmonly-integration-test, which distributes 4,000 signed transfers across four validator RPC endpoints and waits for all validators to execute the final height.
  • Keep the runtime deliberately ephemeral and load-test-only. Persistence, restart recovery, state sync, receipts/EVM JSON-RPC, and staking or other custom precompiles are out of scope.

Testing performed to validate your change

  • make autobahn-evmonly-integration-test (4,000 raw transfers across four validators; 7,129 tx/s observed locally)
  • go test -count=1 ./giga/evmonly/... ./sei-tendermint/internal/p2p ./sei-tendermint/node ./sei-tendermint/internal/rpc/core ./sei-tendermint/config
  • GOWORK=off go test -tags autobahn_integration --count=0 ./integration_test/autobahn/...
  • gofmt and goimports on every touched Go file
  • git diff --check

make fmtcheck was attempted but golangci-lint v2.8.0 exits before scanning files because its binary reports Go 1.24.5 while this repository targets Go 1.25.6.

@github-actions

Copy link
Copy Markdown

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

BuildFormatLintBreakingUpdated (UTC)
✅ passed✅ passed✅ passed✅ passedAug 27, 2026, 2:13 AM

@codecov

codecov Bot commented Aug 27, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 68.78613% with 54 lines in your changes missing coverage. Please review.
✅ Project coverage is 57.99%. Comparing base (819a564) to head (a81e71a).

Files with missing lines Patch % Lines
...ei-tendermint/internal/p2p/evmonly_inmemory_app.go 73.00% 27 Missing and 17 partials ⚠️
sei-tendermint/node/setup.go 0.00% 6 Missing ⚠️
sei-tendermint/internal/rpc/core/blocks.go 0.00% 4 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@                          Coverage Diff                           @@
##           codex/integrate-evmonly-giga-store    #4028      +/-   ##
======================================================================
- Coverage                               58.03%   57.99%   -0.05%     
======================================================================
  Files                                    2208     2207       -1     
  Lines                                  185680   185358     -322     
======================================================================
- Hits                                   107761   107490     -271     
+ Misses                                  68086    68053      -33     
+ Partials                                 9833     9815      -18     
Flag Coverage Δ
sei-chain-pr 62.84% <68.78%> (-16.65%) ⬇️
sei-db 69.80% <ø> (ø)

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

Files with missing lines Coverage Δ
sei-tendermint/config/autobahn.go 50.00% <ø> (ø)
sei-tendermint/internal/rpc/core/blocks.go 66.09% <0.00%> (-1.16%) ⬇️
sei-tendermint/node/setup.go 57.33% <0.00%> (-0.78%) ⬇️
...ei-tendermint/internal/p2p/evmonly_inmemory_app.go 73.00% <73.00%> (ø)

... and 13 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 August 27, 2026 09:22
@cursor

cursor Bot commented Aug 27, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Replaces the ABCI application and changes Autobahn RPC height semantics on the execution path; impact is limited to explicit test config but misconfiguration on a persistent network would be serious.

Overview
Adds a test-only Autobahn path that swaps the Cosmos ABCI app for an ephemeral EVM-only executor (evm_only_in_memory in autobahn.json, chain ID 1337), backed by evmonly + MemoryStore. Raw protected Ethereum txs are accepted in CheckTx with EVM mempool metadata, blocks run through FinalizeBlock/Commit with a deterministic app hash, and funded accounts are synthetic for load testing.

Docker / CI: AUTOBAHN_EVMONLY_IN_MEMORY flows through compose and step4_config_override.sh (sets evm_only_in_memory, drops persistent_state_dir). New make autobahn-evmonly-integration-test runs a focused integration test that broadcasts 4,000 signed transfers across four validator RPCs and waits for all nodes to execute the final height (skips the fullnode sidecar in TestMain).

RPC: Autobahn block height validation for /block and related endpoints now uses the GigaRouter execution head (LastCommittedBlockNumber) instead of the Cosmos App.Info() height, so validator RPC works when the Cosmos app is not advancing.

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

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

return nil, nil, err
}
logger.Warn("Autobahn EVM-only in-memory execution enabled; state is ephemeral and unsafe for persistent networks")
app = p2p.NewEVMOnlyInMemoryProxy(config.AutobahnEVMOnlyInMemoryChainID, maxGas, genDoc.InitialHeight)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Status height still uses Cosmos app

Medium Severity

EVM-only mode replaces the app only inside buildGigaRouter, so env.App stays the Cosmos application that never executes Autobahn blocks. /block now reads giga.LastCommittedBlockNumber(), but /status and /abci_info still take LatestBlockHeight and LastBlockAppHash from env.App.Info(), which remains at genesis. Standard height polling therefore reports a halted chain while validators are producing blocks.

Fix in Cursor Fix in Web

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

A well-scoped, test-only Autobahn EVM-only in-memory executor: the app-hash derivation is deterministic (the executor's changeset is sorted before encoding), the RPC head switch to the GigaRouter is behaviour-preserving for the existing Cosmos-app path, and the docker port/env plumbing checks out. No blockers; the notes below concern failure modes of the new runtime (a tx-level execution error halts the whole cluster), an unenforced config constraint, and a few structural/robustness cleanups.

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

Blockers

  • None at the file/PR level.

Non-blocking

  • [suggestion] Layering: evmonly_inmemory_app.go lands in sei-tendermint/internal/p2p, and sei-tendermint/config imports that package — so the config package now transitively depends on giga/evmonly and the whole go-ethereum EVM. It compiles today only because nothing under giga/evmonly reaches back to sei-tendermint/config; a future import there becomes a cycle. The app has no p2p concern (it is only constructed from node/setup.go); its own package would keep the dependency out of config's import graph.
  • [suggestion] The block's changeset is encoded twice per block: once inside ExecutePreparedBlock (via the WithStore(store, store.EncodeChangeSet) encoder) and again in hashEVMOnlyInMemoryResult (evmonly.EncodeMemoryStoreChangeSet(result.ChangeSet)). On the 4,000-tx blocks this harness exists to measure, that doubles changeset allocation on the hot path. result.ChangeSet is already deterministically sorted, so hashing it directly avoids the second encode.
  • 4 suggestion(s)/nit(s) flagged inline on specific lines.

panic("unreachable")
}

func (a *evmOnlyInMemoryApplication) CheckTx(_ context.Context, req *abci.RequestCheckTxV2) *abci.ResponseCheckTxV2 {

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] CheckTx validates signature, chain ID, tx type and gas price, but not the constraints that make a tx executable, while FinalizeBlock turns any tx-level execution failure into a whole-block error. executeBlockSequential returns fmt.Errorf("execute tx %d ...") on intrinsic-gas-too-low, nonce mismatch or insufficient funds, which propagates out of FinalizeBlockgigaRouterCommon.executeBlock → the execute loop, halting the node — and because the failure is deterministic, every validator halts on the same block and the ephemeral state makes it unrecoverable.

The producer mempool already closes part of this (it rejects EVMNonce != app.EvmNonce and gas beyond the per-block budgets), so the residual gap is a well-signed, correctly-nonced tx with Gas below intrinsic cost (e.g. 21,000 for a transfer, more with calldata), and nonce races when the same sender is broadcast to two validators' lanes. A cheap fix is an intrinsic-gas check here; the more robust one is to report a failed tx as a non-zero Code in evmOnlyABCIResults (which today hardcodes CodeTypeOK even for reverted txs) rather than failing the block, matching how the Cosmos app behaves. Scoped to the load-test mode, hence a suggestion rather than a blocker.

// EVMOnlyInMemory replaces the Cosmos application used by Autobahn with an
// ephemeral EVM-only executor. It exists only for Docker load testing and
// must not be enabled on a persistent network.
EVMOnlyInMemory bool `json:"evm_only_in_memory,omitzero"`

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 doc comment says this "must not be enabled on a persistent network", but nothing enforces it: AutobahnFileConfig.Validate accepts evm_only_in_memory: true together with persistent_state_dir, and only the docker script happens to del(.persistent_state_dir). A hand-written config with both set gets persisted consensus blocks over an EVM state that resets to the funded base on every restart. Validate() is the choke point every config load passes through — rejecting the combination there makes it an invariant instead of a convention the next config author has to remember.

return env.getHeight(env.App.Info().LastBlockHeight, heightPtr)
giga, ok := env.gigaRouter().Get()
if !ok {
panic("autobahnCheckAndGetHeight called without GigaRouter")

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] Both callers (Block at line 100, BlockResults at line 261) already hold the router from their own env.gigaRouter().Get() check, so this second lookup can never fail and the panic is unreachable by construction — but it is a panic on the RPC surface if that ever stops holding. Taking giga p2p.GigaRouter as a parameter removes both the redundant lookup and the panic.

seid tendermint gen-autobahn-config $NODE_DIRS --output "$AUTOBAHN_CONFIG"
if [ "$AUTOBAHN_EVMONLY_IN_MEMORY" = "true" ]; then
AUTOBAHN_CONFIG_TMP="$AUTOBAHN_CONFIG.tmp"
jq '.evm_only_in_memory = true | del(.persistent_state_dir)' "$AUTOBAHN_CONFIG" > "$AUTOBAHN_CONFIG_TMP"

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] This drops the && guard the repo's other jq rewrites use (override_genesis in step2_genesis.sh:13 is jq ... > tmp && mv tmp target). The script has no set -e, so if jq fails the mv on the next line still runs and replaces autobahn.json with a truncated file, and the node fails later with an unrelated-looking config error. Chaining > "$AUTOBAHN_CONFIG_TMP" && mv ... restores the existing idiom.

}
}

func evmOnlyInMemoryEnabled() bool {

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.

I acknowledge that this is for integration testing, but are there facilities to auto-map env variables to the json config? I'm only seeing JSON for AutobahnFileConfig.

seid tendermint gen-autobahn-config $NODE_DIRS --output "$AUTOBAHN_CONFIG"
if [ "$AUTOBAHN_EVMONLY_IN_MEMORY" = "true" ]; then
AUTOBAHN_CONFIG_TMP="$AUTOBAHN_CONFIG.tmp"
jq '.evm_only_in_memory = true | del(.persistent_state_dir)' "$AUTOBAHN_CONFIG" > "$AUTOBAHN_CONFIG_TMP"

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.

You can pass empty string to persistent_state_dir, which I think will disable persistence.

return nil, nil, err
}
logger.Warn("Autobahn EVM-only in-memory execution enabled; state is ephemeral and unsafe for persistent networks")
app = p2p.NewEVMOnlyInMemoryProxy(config.AutobahnEVMOnlyInMemoryChainID, maxGas, genDoc.InitialHeight)

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.

I think this only updates buildGigaRouter's local app. Maybe try to do it like MockApp, do it earlier in node.New? #3905

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