Skip to content
Open
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
204a411
scaffold evm-only giga executor path
codchen Jun 15, 2026
7314cc2
port native evm-only giga executor
codchen Jun 15, 2026
93e75ff
clean up evm-only executor scaffold leftovers
codchen Jun 15, 2026
7338faf
flatten evm-only executor package
codchen Jun 15, 2026
5de0066
fix evmonly lint after rebase
codchen Jun 15, 2026
4a56125
address evmonly cursor review comments
codchen Jun 16, 2026
f0d8315
fix evmonly state snapshot finalization
codchen Jun 16, 2026
298afdd
let custom precompile placeholders fail in receipts
codchen Jun 17, 2026
0a6110a
expand evmonly executor test coverage
codchen Jun 24, 2026
23fc6d3
add evmonly validation edge case tests
codchen Jun 24, 2026
9c28df7
fix evmonly state finalization edge cases
codchen Jun 24, 2026
5e36ab5
Merge branch 'main' into codex/sei-v3-evm-only-scaffold
masih Jun 26, 2026
10c3108
add evmonly executor OCC
codchen Jun 29, 2026
f1fa418
optimize evmonly OCC scheduling
codchen Jun 30, 2026
dd519f7
add evmonly executor result sink
codchen Jul 2, 2026
da985fc
pipeline evmonly sender recovery
codchen Jul 2, 2026
06e18a9
reuse evmonly OCC workers
codchen Jul 2, 2026
12e1d83
remove evmonly worker pinning
codchen Jul 2, 2026
1bccbe4
pool evmonly execution outputs
codchen Jul 2, 2026
a38d56e
preserve pooled result leases
codchen Jul 2, 2026
7ba41c4
avoid evmonly statedb scratch reuse
codchen Jul 2, 2026
9ae1cfb
add evmonly OCC fallback stats
codchen Jul 2, 2026
145f089
fix evmonly scaffold review findings
codchen Jul 2, 2026
29047e5
address evmonly scaffold review threads
codchen Jul 3, 2026
904bd9a
fix evmonly occ review edge cases
codchen Jul 3, 2026
cdf544a
track codeless getcodehash account reads
codchen Jul 3, 2026
ec0ff1a
address evmonly nonblocking review issues
codchen Jul 3, 2026
d16e133
reject evmonly blob txs until accounting is wired
codchen Jul 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 150 additions & 0 deletions giga/evmonly/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# EVM-only giga execution

This package contains the EVM-only execution boundary for the final-form giga
path. It is intentionally separate from the current Cosmos-backed giga wiring in
`app/app.go`.

The target execution model is based on the `sei-v3` executor:

- raw transaction bytes are Ethereum RLP transactions, not Cosmos SDK txs
- state layout is EVM-native: balance, storage, code, and nonce are keyed by
EVM addresses and hashes
- block execution can overlap with parsing or persistence work for nearby blocks
- hot execution should not allocate `sdk.Context`, `sdk.Tx`,
`MsgEVMTransaction`, Cosmos messages, or Cosmos coins

The current implementation executes raw RLP transactions with go-ethereum
against an EVM-native state backend, then returns a changeset plus Ethereum
receipts. Custom precompiles are still placeholders. The open work is to port
them behind an EVM-native context that is visible to the executor's conflict
tracking without reintroducing Cosmos keeper dependencies.

## Current implementation

The `evmonly` package currently provides:

- sequential execution of the ordered block transaction list
- RLP decoding and sender recovery through go-ethereum signers
- go-ethereum `core.ApplyMessage` execution against an SDK-free `vm.StateDB`

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.

does "SDK" mean Cosmos?

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.

yep

- key-addressable state reads for balance, nonce, code, and storage
- deterministic post-block `StateChangeSet` construction
- optional executor-internal OCC for non-conflicting transaction sets
- Ethereum receipt construction with logs, bloom, gas, tx hash, block metadata,
contract address, and effective gas price
- a map-backed `MemoryState` for tests and early integration
- fail-closed custom precompile placeholders

The executor accepts config for nonce checks, gas-price checks, minimum gas
price, chain config, and the custom precompile registry.

## Executor interface

The boundary is:

```go
ExecuteBlock(context.Context, BlockRequest) (*BlockResult, error)
```

For callers that can pipeline stateless work across blocks, the concrete
executor also supports:

```go
PrepareBlock(context.Context, BlockRequest) (PreparedBlock, error)
ExecutePreparedBlock(context.Context, PreparedBlock) (*BlockResult, error)
```

`PrepareBlock` decodes transaction RLP and recovers senders. This work does not
touch state, so block N+1 can be prepared while block N is still executing.
`ExecuteBlock` remains the convenience path and performs prepare then execute in
one call.

The executor should be commit-neutral. It executes an ordered EVM block and
returns the state writes and receipts produced by that block. The caller owns
durable persistence, state commitment, block indexing, and receipt publication.
The concrete `Executor` accepts a `StateReader` backend through `WithState(...)`;
callers can persist the returned `ChangeSet` with a matching `StateWriter`.

A non-nil `error` means block validation failed and the caller must not commit a
partial output. EVM call failures inside an otherwise valid transaction are
represented in `Receipts` and `Txs` with failed status.

## Input block format

`BlockRequest` is the expected input:

- `Context` contains block-constant EVM fields:
- block number
- timestamp
- block gas limit
- chain ID
- base fee
- blob base fee, when enabled
- coinbase
- parent hash
- current block hash
- prevRandao
- `Txs` is the canonical, already-ordered transaction list for the block.
- Each tx is raw Ethereum transaction RLP bytes.
- There is no Cosmos SDK tx envelope, `MsgEVMTransaction`, ante wrapper, memo,
fee object, account address mapping object, or Cosmos gas meter in the input.

The runtime or consensus layer is responsible for choosing the block order and
providing the block context. The executor is responsible for parsing tx RLP,
recovering senders, validating EVM nonce/fee/intrinsic-gas rules, executing EVM
state transitions, and producing deterministic outputs.

`BlockHash` is used for receipts and log metadata. The current `BLOCKHASH`
opcode callback only exposes `ParentHash`; older historical hashes require a
runtime-provided hash source in a later integration step.

## Output format

`BlockResult` contains two primary outputs:

- `ChangeSet`: the EVM-native state writes produced by the block.
- `Receipts`: Ethereum receipts for the executed transactions.

`ChangeSet` is expressed as post-block values, not deltas:

- `Balances`: final balance for each changed EVM address
- `Nonces`: final nonce for each changed EVM address
- `Code`: final bytecode updates or deletions
- `StorageClears`: addresses whose persisted storage must be fully cleared
- `Storage`: final storage slot updates or deletions

The changeset should be deterministic and serializable by the runtime layer.
The executor should not require `sdk.Context` or Cosmos stores to build it.
State writers should apply `StorageClears` before per-slot `Storage` updates.

`Receipts` are emitted in transaction order and should contain the Ethereum
receipt fields needed by RPC and indexing: status, cumulative gas used, bloom,
logs, tx hash, contract address, and effective gas price metadata where needed.

`Txs` carries per-transaction execution metadata used to build or enrich
receipts and RPC responses. `GasUsed` is the total EVM gas consumed by the block.

## Open precompile work

Native custom precompiles still need a separate design. If they introduce state
outside balance, nonce, code, and storage, that state must either become part of
the EVM-native changeset or be represented through an explicit extension that is
visible to the OCC conflict tracker.

The intended direction is to treat each custom precompile's migrated module
state as contract storage owned by that precompile address. With no range reads
and no side state, precompile reads and writes can then flow through ordinary
`(address, slot)` storage tracking.

Until that design is implemented, the `evmonly` executor accepts a custom
precompile registry only as a fail-closed placeholder. Calls to registered
custom precompile addresses return `ErrCustomPrecompilesOpen`.

## Current limitations

- The current port is sequential. The EVM-native state boundary and changeset
shape are intended to be replaceable with the sei-v3 OCC scheduler/store.
- State input is key-addressable only. The executor lazily reads storage slots
by `(address, slot)` and does not require or expose range iteration.
- The map-backed `MemoryState` is for tests and early integration; production
should provide a durable native state backend.
- Historical `BLOCKHASH` lookups beyond the parent block are not wired yet.
38 changes: 38 additions & 0 deletions giga/evmonly/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package evmonly

import (
"math/big"

"github.com/ethereum/go-ethereum/params"

"github.com/sei-protocol/sei-chain/giga/evmonly/precompiles"
)

// Config captures the sei-v3 executor knobs needed by the EVM-only path.
type Config struct {
DisableNonceCheck bool
DisableGasPriceCheck bool
MinGasPrice *big.Int
ChainConfig *params.ChainConfig
CustomPrecompiles precompiles.Registry
OCCWorkers int
// BlockResultPoolSize enables a bounded reusable output pool. Callers that
// enable it must call BlockResult.Release when they are done with returned
// results. Result sinks receive a retained result and must release it after
// they finish async persistence.
BlockResultPoolSize int
}

func DefaultConfig() Config {
return Config{
MinGasPrice: big.NewInt(1_000_000_000),
}
}

func (c Config) WithDefaults() Config {
defaults := DefaultConfig()
if c.MinGasPrice == nil {
c.MinGasPrice = new(big.Int).Set(defaults.MinGasPrice)
}
return c
}
Loading
Loading