diff --git a/.gitignore b/.gitignore index 2d4ee4efc8..d3f4ccb98f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ release/ .DS_Store build/ cache/ +*.iml # Local .terraform directories **/.terraform diff --git a/app/app.go b/app/app.go index 653298491c..0f89d819e7 100644 --- a/app/app.go +++ b/app/app.go @@ -29,9 +29,11 @@ import ( "github.com/cosmos/cosmos-sdk/server/config" servertypes "github.com/cosmos/cosmos-sdk/server/types" storetypes "github.com/cosmos/cosmos-sdk/store/types" + "github.com/cosmos/cosmos-sdk/tasks" sdk "github.com/cosmos/cosmos-sdk/types" genesistypes "github.com/cosmos/cosmos-sdk/types/genesis" "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/types/occ" "github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth/ante" @@ -85,8 +87,12 @@ import ( upgradeclient "github.com/cosmos/cosmos-sdk/x/upgrade/client" upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + "github.com/ethereum/go-ethereum/core" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/ethclient" ethrpc "github.com/ethereum/go-ethereum/rpc" + "github.com/gorilla/mux" "github.com/rakyll/statik/fs" "github.com/sei-protocol/sei-chain/app/antedecorators" @@ -96,6 +102,8 @@ import ( v0upgrade "github.com/sei-protocol/sei-chain/app/upgrades/v0" "github.com/sei-protocol/sei-chain/evmrpc" evmrpcconfig "github.com/sei-protocol/sei-chain/evmrpc/config" + gigaconfig "github.com/sei-protocol/sei-chain/giga/executor/config" + gigaevmc "github.com/sei-protocol/sei-chain/giga/executor/vm/evmc" "github.com/sei-protocol/sei-chain/precompiles" putils "github.com/sei-protocol/sei-chain/precompiles/utils" "github.com/sei-protocol/sei-chain/sei-db/ss" @@ -124,6 +132,7 @@ import ( evmkeeper "github.com/sei-protocol/sei-chain/x/evm/keeper" "github.com/sei-protocol/sei-chain/x/evm/querier" "github.com/sei-protocol/sei-chain/x/evm/replay" + evmstate "github.com/sei-protocol/sei-chain/x/evm/state" evmtypes "github.com/sei-protocol/sei-chain/x/evm/types" "github.com/sei-protocol/sei-chain/x/mint" mintclient "github.com/sei-protocol/sei-chain/x/mint/client/cli" @@ -660,6 +669,24 @@ func New( } app.EvmKeeper.EthClient = ethclient.NewClient(rpcclient) } + + // Read Giga Executor config + gigaExecutorConfig, err := gigaconfig.ReadConfig(appOpts) + if err != nil { + panic(fmt.Sprintf("error reading giga executor config due to %s", err)) + } + app.EvmKeeper.GigaExecutorEnabled = gigaExecutorConfig.Enabled + app.EvmKeeper.GigaOCCEnabled = gigaExecutorConfig.OCCEnabled + if gigaExecutorConfig.Enabled { + if gigaExecutorConfig.OCCEnabled { + logger.Info("benchmark: Giga Executor with OCC is ENABLED - using new EVM execution path with parallel execution") + } else { + logger.Info("benchmark: Giga Executor (evmone-based) is ENABLED - using new EVM execution path (sequential)") + } + } else { + logger.Info("benchmark: Giga Executor is DISABLED - using default GETH interpreter") + } + lightInvarianceConfig, err := ReadLightInvarianceConfig(appOpts) if err != nil { panic(fmt.Sprintf("error reading light invariance config due to %s", err)) @@ -1433,6 +1460,15 @@ func (app *App) ProcessBlock(ctx sdk.Context, txs [][]byte, req BlockProcessRequ app.wsServerStartSignal <- struct{}{} } }() + + // Route to Giga Executor when enabled - bypasses Cosmos SDK transaction processing + if app.EvmKeeper.GigaExecutorEnabled { + if app.EvmKeeper.GigaOCCEnabled { + return app.ProcessBlockWithGigaExecutorOCC(ctx, txs, req, lastCommit, simulate) + } + return app.ProcessBlockWithGigaExecutor(ctx, txs, req, lastCommit, simulate) + } + ctx = ctx.WithIsOCCEnabled(app.OccEnabled()) blockSpanCtx, blockSpan := app.GetBaseApp().TracingInfo.Start("Block") @@ -1490,6 +1526,355 @@ func (app *App) ProcessBlock(ctx sdk.Context, txs [][]byte, req BlockProcessRequ return events, txResults, endBlockResp, nil } +// ProcessBlockWithGigaExecutor executes block transactions using the Giga executor, +// bypassing the standard Cosmos SDK transaction processing flow. +// This is an experimental path for improved EVM throughput. +func (app *App) ProcessBlockWithGigaExecutor(ctx sdk.Context, txs [][]byte, req BlockProcessRequest, lastCommit abci.CommitInfo, simulate bool) (events []abci.Event, txResults []*abci.ExecTxResult, endBlockResp abci.ResponseEndBlock, err error) { + // Panic recovery like original ProcessBlock + defer func() { + if r := recover(); r != nil { + stack := string(debug.Stack()) + ctx.Logger().Error("benchmark panic in ProcessBlockWithGigaExecutor", "panic", r, "stack", stack) + err = fmt.Errorf("ProcessBlockWithGigaExecutor panic: %v", r) + events = nil + txResults = nil + endBlockResp = abci.ResponseEndBlock{} + } + }() + + // Setup context like original ProcessBlock + ctx = ctx.WithIsOCCEnabled(false) // Disable OCC for giga executor path + + blockSpanCtx, blockSpan := app.GetBaseApp().TracingInfo.Start("GigaBlock") + defer blockSpan.End() + blockSpan.SetAttributes(attribute.Int64("height", req.GetHeight())) + ctx = ctx.WithTraceSpanContext(blockSpanCtx) + + events = []abci.Event{} + + // BeginBlock - still needed for validator updates, etc. + beginBlockResp := app.BeginBlock(ctx, req.GetHeight(), lastCommit.Votes, req.GetByzantineValidators(), true) + events = append(events, beginBlockResp.Events...) + + // Initialize results array + txResults = make([]*abci.ExecTxResult, len(txs)) + evmTxs := make([]*evmtypes.MsgEVMTransaction, len(txs)) + + // TODO: This is where the giga executor will process transactions directly + // For now, decode and execute each transaction through the giga executor + evmTotalGasUsed := int64(0) + + for i, txBytes := range txs { + // Decode as Cosmos SDK tx first to extract EVM message + // TODO: In full implementation, decode directly as Ethereum tx + decodedTx, decodeErr := app.txDecoder(txBytes) + if decodeErr != nil { + txResults[i] = &abci.ExecTxResult{ + Code: 1, + Log: fmt.Sprintf("failed to decode transaction: %v", decodeErr), + } + continue + } + + // Check if this is an EVM transaction + evmMsg := app.GetEVMMsg(decodedTx) + if evmMsg == nil { + // Non-EVM transaction - for now, fall back to standard processing + // TODO: Handle or reject non-EVM txs in giga mode + txResults[i] = &abci.ExecTxResult{ + Code: 1, + Log: "non-EVM transactions not supported in giga executor mode", + } + continue + } + + evmTxs[i] = evmMsg + + // Execute EVM transaction through giga executor + result, execErr := app.executeEVMTxWithGigaExecutor(ctx, i, evmMsg) + if execErr != nil { + txResults[i] = &abci.ExecTxResult{ + Code: 1, + Log: fmt.Sprintf("giga executor error: %v", execErr), + } + continue + } + + txResults[i] = result + if result.EvmTxInfo != nil { + evmTotalGasUsed += result.GasUsed + } + } + + app.EvmKeeper.SetTxResults(txResults) + app.EvmKeeper.SetMsgs(evmTxs) + + // Finalize bank transfers + lazyWriteEvents := app.BankKeeper.WriteDeferredBalances(ctx) + events = append(events, lazyWriteEvents...) + + // EndBlock + endBlockResp = app.EndBlock(ctx, req.GetHeight(), evmTotalGasUsed) + events = append(events, endBlockResp.Events...) + + return events, txResults, endBlockResp, nil +} + +// executeEVMTxWithGigaExecutor executes a single EVM transaction using the giga executor. +// The sender address is recovered directly from the transaction signature - no Cosmos SDK ante handlers needed. +func (app *App) executeEVMTxWithGigaExecutor(ctx sdk.Context, txIndex int, msg *evmtypes.MsgEVMTransaction) (*abci.ExecTxResult, error) { + // Get the Ethereum transaction from the message + ethTx, txData := msg.AsTransaction() + if ethTx == nil || txData == nil { + return nil, fmt.Errorf("failed to convert to eth transaction") + } + + // Recover sender address directly from the transaction signature + // This bypasses all Cosmos SDK ante handler processing + chainID := app.EvmKeeper.ChainID(ctx) + signer := ethtypes.LatestSignerForChainID(chainID) + sender, sigErr := ethtypes.Sender(signer, ethTx) + if sigErr != nil { + return &abci.ExecTxResult{ + Code: 1, + Log: fmt.Sprintf("failed to recover sender from signature: %v", sigErr), + }, nil + } + + // Prepare context for EVM transaction (set infinite gas meter like original flow) + ctx = ctx.WithGasMeter(sdk.NewInfiniteGasMeterWithMultiplier(ctx)) + ctx = ctx.WithTxIndex(txIndex) + + // Create state DB for this transaction + stateDB := evmstate.NewDBImpl(ctx, &app.EvmKeeper, false) + defer stateDB.Cleanup() + + // Get EVM message from the transaction using recovered sender + evmMsg := app.EvmKeeper.GetEVMMessage(ctx, ethTx, sender) + + // Get gas pool + gp := app.EvmKeeper.GetGasPool() + + // Get block context + blockCtx, blockCtxErr := app.EvmKeeper.GetVMBlockContext(ctx, gp) + if blockCtxErr != nil { + return &abci.ExecTxResult{ + Code: 1, + Log: fmt.Sprintf("failed to get block context: %v", blockCtxErr), + }, nil + } + + // Get chain config + sstore := app.EvmKeeper.GetParams(ctx).SeiSstoreSetGasEip2200 + cfg := evmtypes.DefaultChainConfig().EthereumConfigWithSstore(app.EvmKeeper.ChainID(ctx), &sstore) + txCtx := core.NewEVMTxContext(evmMsg) + + // Create Giga executor VM (wraps evmone) + gigaVM := gigaevmc.NewVM(*blockCtx, stateDB, cfg, vm.Config{}, app.EvmKeeper.CustomPrecompiles(ctx)) + gigaVM.SetTxContext(txCtx) + + // Execute the transaction through giga VM + execResult, execErr := gigaVM.ApplyMessage(evmMsg, &gp) + if execErr != nil { + return &abci.ExecTxResult{ + Code: 1, + Log: fmt.Sprintf("giga executor apply message error: %v", execErr), + }, nil + } + + // Finalize state changes + _, ferr := stateDB.Finalize() + if ferr != nil { + return &abci.ExecTxResult{ + Code: 1, + Log: fmt.Sprintf("failed to finalize state: %v", ferr), + }, nil + } + + // Write receipt + vmError := "" + if execResult.Err != nil { + vmError = execResult.Err.Error() + } + receipt, rerr := app.EvmKeeper.WriteReceipt(ctx, stateDB, evmMsg, uint32(ethTx.Type()), ethTx.Hash(), execResult.UsedGas, vmError) + if rerr != nil { + return &abci.ExecTxResult{ + Code: 1, + Log: fmt.Sprintf("failed to write receipt: %v", rerr), + }, nil + } + + // Append deferred info for EndBlock processing + // Calculate surplus (gas fee paid minus gas used * effective gas price) + // For giga executor, we set surplus to zero since we're not charging gas fees through the normal flow + surplus := sdk.ZeroInt() + bloom := ethtypes.Bloom{} + bloom.SetBytes(receipt.LogsBloom) + app.EvmKeeper.AppendToEvmTxDeferredInfo(ctx, bloom, ethTx.Hash(), surplus) + + // Determine result code based on VM error + code := uint32(0) + if execResult.Err != nil { + code = 1 + } + + //nolint:gosec // G115: safe, UsedGas won't exceed int64 max + return &abci.ExecTxResult{ + Code: code, + GasUsed: int64(execResult.UsedGas), + Log: vmError, + EvmTxInfo: &abci.EvmTxInfo{ + TxHash: ethTx.Hash().Hex(), + VmError: vmError, + Nonce: ethTx.Nonce(), + }, + }, nil +} + +// ProcessBlockWithGigaExecutorOCC executes block transactions using the Giga executor with OCC. +// This combines the lean giga executor path with parallel execution and conflict detection. +func (app *App) ProcessBlockWithGigaExecutorOCC(ctx sdk.Context, txs [][]byte, req BlockProcessRequest, lastCommit abci.CommitInfo, simulate bool) (events []abci.Event, txResults []*abci.ExecTxResult, endBlockResp abci.ResponseEndBlock, err error) { + // Panic recovery + defer func() { + if r := recover(); r != nil { + stack := string(debug.Stack()) + ctx.Logger().Error("benchmark panic in ProcessBlockWithGigaExecutorOCC", "panic", r, "height", req.GetHeight(), "txCount", len(txs), "stack", stack) + err = fmt.Errorf("ProcessBlockWithGigaExecutorOCC panic: %v", r) + events = nil + txResults = nil + endBlockResp = abci.ResponseEndBlock{} + } + }() + + // Setup context - OCC is enabled for this path + ctx = ctx.WithIsOCCEnabled(true) + + blockSpanCtx, blockSpan := app.GetBaseApp().TracingInfo.Start("GigaBlockOCC") + defer blockSpan.End() + blockSpan.SetAttributes(attribute.Int64("height", req.GetHeight())) + ctx = ctx.WithTraceSpanContext(blockSpanCtx) + + events = []abci.Event{} + + // BeginBlock + beginBlockResp := app.BeginBlock(ctx, req.GetHeight(), lastCommit.Votes, req.GetByzantineValidators(), true) + events = append(events, beginBlockResp.Events...) + + // Build DeliverTxEntry list for OCC scheduler + txEntries := make([]*sdk.DeliverTxEntry, 0, len(txs)) + evmTxs := make([]*evmtypes.MsgEVMTransaction, len(txs)) + + for i, txBytes := range txs { + decodedTx, decodeErr := app.txDecoder(txBytes) + if decodeErr != nil { + continue + } + evmMsg := app.GetEVMMsg(decodedTx) + if evmMsg == nil { + continue + } + evmTxs[i] = evmMsg + checksum := sha256.Sum256(txBytes) + txEntries = append(txEntries, &sdk.DeliverTxEntry{ + Request: abci.RequestDeliverTxV2{Tx: txBytes}, + SdkTx: decodedTx, + Checksum: checksum, + AbsoluteIndex: i, + }) + } + + // Create OCC scheduler with giga executor deliverTx + scheduler := tasks.NewScheduler( + app.ConcurrencyWorkers(), + app.TracingInfo, + app.gigaDeliverTx, + ) + + responses, schedErr := scheduler.ProcessAll(ctx, txEntries) + if schedErr != nil { + ctx.Logger().Error("benchmark OCC scheduler error", "error", schedErr, "height", req.GetHeight(), "txCount", len(txEntries)) + return nil, nil, abci.ResponseEndBlock{}, schedErr + } + + // Convert responses to ExecTxResult + txResults = make([]*abci.ExecTxResult, len(txs)) + evmTotalGasUsed := int64(0) + for i, resp := range responses { + idx := txEntries[i].AbsoluteIndex + txResults[idx] = &abci.ExecTxResult{ + Code: resp.Code, + Data: resp.Data, + Log: resp.Log, + Info: resp.Info, + GasWanted: resp.GasWanted, + GasUsed: resp.GasUsed, + Events: resp.Events, + Codespace: resp.Codespace, + EvmTxInfo: resp.EvmTxInfo, + } + evmTotalGasUsed += resp.GasUsed + } + + // Fill in nil results for non-EVM or failed decode txs + for i := range txResults { + if txResults[i] == nil { + txResults[i] = &abci.ExecTxResult{ + Code: 1, + Log: "transaction not processed by giga executor OCC", + } + } + } + + app.EvmKeeper.SetTxResults(txResults) + app.EvmKeeper.SetMsgs(evmTxs) + + // Finalize bank transfers + lazyWriteEvents := app.BankKeeper.WriteDeferredBalances(ctx) + events = append(events, lazyWriteEvents...) + + // EndBlock + endBlockResp = app.EndBlock(ctx, req.GetHeight(), evmTotalGasUsed) + events = append(events, endBlockResp.Events...) + + return events, txResults, endBlockResp, nil +} + +// gigaDeliverTx is the OCC-compatible deliverTx function for the giga executor. +// The ctx.MultiStore() is already wrapped with VersionIndexedStore by the scheduler. +func (app *App) gigaDeliverTx(ctx sdk.Context, req abci.RequestDeliverTxV2, tx sdk.Tx, checksum [32]byte) abci.ResponseDeliverTx { + defer func() { + if r := recover(); r != nil { + // OCC abort panics are expected - the scheduler uses them to detect conflicts + // and reschedule transactions. Don't log these as errors. + if _, isOCCAbort := r.(occ.Abort); !isOCCAbort { + ctx.Logger().Error("benchmark panic in gigaDeliverTx", "panic", r, "stack", string(debug.Stack())) + } + } + }() + + evmMsg := app.GetEVMMsg(tx) + if evmMsg == nil { + return abci.ResponseDeliverTx{Code: 1, Log: "not an EVM transaction"} + } + + result, err := app.executeEVMTxWithGigaExecutor(ctx, ctx.TxIndex(), evmMsg) + if err != nil { + return abci.ResponseDeliverTx{Code: 1, Log: fmt.Sprintf("giga executor error: %v", err)} + } + + return abci.ResponseDeliverTx{ + Code: result.Code, + Data: result.Data, + Log: result.Log, + Info: result.Info, + GasWanted: result.GasWanted, + GasUsed: result.GasUsed, + Events: result.Events, + Codespace: result.Codespace, + EvmTxInfo: result.EvmTxInfo, + } +} + func (app *App) GetEVMMsg(tx sdk.Tx) (res *evmtypes.MsgEVMTransaction) { defer func() { if err := recover(); err != nil { diff --git a/app/benchmark.go b/app/benchmark.go index 6a43ed73aa..12ad2f336f 100644 --- a/app/benchmark.go +++ b/app/benchmark.go @@ -25,7 +25,6 @@ type benchmarkLogger struct { maxBlockTime time.Duration // Maximum time difference between consecutive blocks totalBlockTime time.Duration // Sum of all block time differences in the window blockTimeCount int64 // Number of block time differences calculated - peakTps float64 // Highest TPS seen across entire execution (persists across flushes) prevBlockTime time.Time // Previous block time for calculating differences lastFlushTime time.Time // When we last flushed (for TPS calculation) logger log.Logger @@ -83,7 +82,6 @@ type flushStats struct { maxBlockTimeMs int64 avgBlockTimeMs int64 tps float64 - peakTps float64 } // getAndResetStats atomically reads current stats and resets counters for next window @@ -102,7 +100,7 @@ func (l *benchmarkLogger) getAndResetStats(now time.Time) (flushStats, time.Time totalBlockTime := l.totalBlockTime blockTimeCount := l.blockTimeCount - // Reset counters for next window (but keep prevBlockTime and peakTps for continuity) + // Reset counters for next window (but keep prevBlockTime for continuity) l.txCount = 0 l.blockCount = 0 l.latestHeight = 0 @@ -120,12 +118,6 @@ func (l *benchmarkLogger) getAndResetStats(now time.Time) (flushStats, time.Time // Calculate average block time stats.avgBlockTimeMs = calculateAvgBlockTime(totalBlockTime, blockTimeCount) - // Update peak TPS if current TPS is higher - if stats.tps > l.peakTps { - l.peakTps = stats.tps - } - stats.peakTps = l.peakTps - return stats, prevTime } @@ -140,7 +132,6 @@ func (l *benchmarkLogger) FlushLog() { "blockTimeMax", stats.maxBlockTimeMs, "blockTimeAvg", stats.avgBlockTimeMs, "tps", stats.tps, - "peakTps", stats.peakTps, ) } diff --git a/app/benchmark_test.go b/app/benchmark_test.go index c683f7b936..24c06c3c5e 100644 --- a/app/benchmark_test.go +++ b/app/benchmark_test.go @@ -168,7 +168,6 @@ func TestBenchmarkLogger_GetAndResetStats(t *testing.T) { bl.maxBlockTime = 2 * time.Second bl.totalBlockTime = 10 * time.Second bl.blockTimeCount = 9 // 9 intervals between 10 blocks - bl.peakTps = 500.0 // Wait a bit to ensure duration > 0 time.Sleep(10 * time.Millisecond) @@ -189,13 +188,6 @@ func TestBenchmarkLogger_GetAndResetStats(t *testing.T) { expectedTPS := calculateTPS(1000, duration) require.InDelta(t, expectedTPS, stats.tps, 0.01) - // Check peak TPS (should be max of current TPS and previous peak) - if stats.tps > 500.0 { - require.Equal(t, stats.tps, stats.peakTps) - } else { - require.Equal(t, 500.0, stats.peakTps) - } - // Check counters were reset require.Equal(t, int64(0), bl.txCount) require.Equal(t, int64(0), bl.blockCount) @@ -204,49 +196,6 @@ func TestBenchmarkLogger_GetAndResetStats(t *testing.T) { require.Equal(t, time.Duration(0), bl.totalBlockTime) require.Equal(t, int64(0), bl.blockTimeCount) require.Equal(t, now, bl.lastFlushTime) - - // Check peakTps persists (prevBlockTime may be zero if no Increment was called) - require.NotZero(t, bl.peakTps) -} - -func TestBenchmarkLogger_PeakTPS(t *testing.T) { - logger := log.NewNopLogger() - bl := &benchmarkLogger{ - logger: logger, - } - - baseTime := time.Now() - bl.lastFlushTime = baseTime - - // First flush with high TPS - bl.txCount = 10000 - bl.blockCount = 10 - bl.totalBlockTime = 1 * time.Second - bl.blockTimeCount = 9 - time.Sleep(10 * time.Millisecond) - stats1, _ := bl.getAndResetStats(time.Now()) - require.Greater(t, stats1.tps, 0.0) - require.Equal(t, stats1.tps, stats1.peakTps) - - // Second flush with lower TPS - bl.txCount = 1000 - bl.blockCount = 10 - bl.totalBlockTime = 1 * time.Second - bl.blockTimeCount = 9 - time.Sleep(10 * time.Millisecond) - stats2, _ := bl.getAndResetStats(time.Now()) - require.Less(t, stats2.tps, stats1.tps) - require.Equal(t, stats1.tps, stats2.peakTps) // Peak should persist - - // Third flush with even higher TPS - bl.txCount = 20000 - bl.blockCount = 10 - bl.totalBlockTime = 1 * time.Second - bl.blockTimeCount = 9 - time.Sleep(10 * time.Millisecond) - stats3, _ := bl.getAndResetStats(time.Now()) - require.Greater(t, stats3.tps, stats1.tps) - require.Equal(t, stats3.tps, stats3.peakTps) // New peak } func TestBenchmarkLogger_StartStop(t *testing.T) { @@ -510,7 +459,6 @@ func TestFlushLog(t *testing.T) { bl.maxBlockTime = 2 * time.Second bl.totalBlockTime = 10 * time.Second bl.blockTimeCount = 9 - bl.peakTps = 0.0 // Wait a bit to ensure duration > 0 time.Sleep(10 * time.Millisecond) @@ -531,16 +479,15 @@ func TestFlushLog(t *testing.T) { bl.FlushLog() require.Equal(t, int64(0), bl.txCount) - // Test FlushLog with peak TPS set + // Test FlushLog with some TPS bl.txCount = 1000 bl.blockCount = 5 bl.lastFlushTime = time.Now().Add(-2 * time.Second) - bl.peakTps = 500.0 time.Sleep(10 * time.Millisecond) bl.FlushLog() - // Peak TPS should persist - require.Greater(t, bl.peakTps, 0.0, "Peak TPS should persist across flushes") + // Stats should be reset after flush + require.Equal(t, int64(0), bl.txCount) } func TestNewGeneratorCh_ContextCancellation(t *testing.T) { diff --git a/cmd/seid/cmd/app_config.go b/cmd/seid/cmd/app_config.go index 8787a84564..6eb2deff47 100644 --- a/cmd/seid/cmd/app_config.go +++ b/cmd/seid/cmd/app_config.go @@ -4,6 +4,7 @@ import ( srvconfig "github.com/cosmos/cosmos-sdk/server/config" seiapp "github.com/sei-protocol/sei-chain/app" evmrpcconfig "github.com/sei-protocol/sei-chain/evmrpc/config" + gigaconfig "github.com/sei-protocol/sei-chain/giga/executor/config" seidbconfig "github.com/sei-protocol/sei-chain/sei-db/config" "github.com/sei-protocol/sei-chain/x/evm/blocktest" "github.com/sei-protocol/sei-chain/x/evm/querier" @@ -25,6 +26,7 @@ type CustomAppConfig struct { StateStore seidbconfig.StateStoreConfig `mapstructure:"state-store"` WASM WASMConfig `mapstructure:"wasm"` EVM evmrpcconfig.Config `mapstructure:"evm"` + GigaExecutor gigaconfig.Config `mapstructure:"giga_executor"` ETHReplay replay.Config `mapstructure:"eth_replay"` ETHBlockTest blocktest.Config `mapstructure:"eth_block_test"` EvmQuery querier.Config `mapstructure:"evm_query"` @@ -42,6 +44,7 @@ func NewCustomAppConfig(baseConfig *srvconfig.Config, evmConfig evmrpcconfig.Con LruSize: 1, }, EVM: evmConfig, + GigaExecutor: gigaconfig.DefaultConfig, ETHReplay: replay.DefaultConfig, ETHBlockTest: blocktest.DefaultConfig, EvmQuery: querier.DefaultConfig, diff --git a/cmd/seid/cmd/root.go b/cmd/seid/cmd/root.go index ccd3a1e1bb..2ead64ad60 100644 --- a/cmd/seid/cmd/root.go +++ b/cmd/seid/cmd/root.go @@ -34,6 +34,7 @@ import ( "github.com/sei-protocol/sei-chain/app" "github.com/sei-protocol/sei-chain/app/params" evmrpcconfig "github.com/sei-protocol/sei-chain/evmrpc/config" + gigaconfig "github.com/sei-protocol/sei-chain/giga/executor/config" seidbconfig "github.com/sei-protocol/sei-chain/sei-db/config" "github.com/sei-protocol/sei-chain/sei-wasmd/x/wasm" wasmkeeper "github.com/sei-protocol/sei-chain/sei-wasmd/x/wasm/keeper" @@ -439,6 +440,7 @@ func initAppConfig() (string, interface{}) { seidbconfig.StateCommitConfigTemplate + seidbconfig.StateStoreConfigTemplate + evmrpcconfig.ConfigTemplate + + gigaconfig.ConfigTemplate + serverconfig.AutoManagedConfigTemplate + ` ############################################################################### ### WASM Configuration (Auto-managed) ### diff --git a/giga/executor/config/config.go b/giga/executor/config/config.go new file mode 100644 index 0000000000..d3f270c628 --- /dev/null +++ b/giga/executor/config/config.go @@ -0,0 +1,58 @@ +package config + +import ( + servertypes "github.com/cosmos/cosmos-sdk/server/types" + "github.com/spf13/cast" +) + +// Config defines configuration for the Giga Executor +type Config struct { + // Enabled controls whether to use the Giga executor (evmone-based) instead of geth's interpreter + Enabled bool `mapstructure:"enabled"` + // OCCEnabled controls whether to use OCC (Optimistic Concurrency Control) with the Giga executor + OCCEnabled bool `mapstructure:"occ_enabled"` +} + +var DefaultConfig = Config{ + Enabled: false, // disabled by default, opt-in + OCCEnabled: false, // OCC disabled by default +} + +const ( + flagEnabled = "giga_executor.enabled" + flagOCCEnabled = "giga_executor.occ_enabled" +) + +func ReadConfig(opts servertypes.AppOptions) (Config, error) { + cfg := DefaultConfig // copy + var err error + if v := opts.Get(flagEnabled); v != nil { + if cfg.Enabled, err = cast.ToBoolE(v); err != nil { + return cfg, err + } + } + if v := opts.Get(flagOCCEnabled); v != nil { + if cfg.OCCEnabled, err = cast.ToBoolE(v); err != nil { + return cfg, err + } + } + return cfg, nil +} + +// ConfigTemplate defines the TOML configuration template for Giga Executor +const ConfigTemplate = ` +############################################################################### +### Giga Executor Configuration ### +############################################################################### + +[giga_executor] +# enabled controls whether to use the Giga executor (evmone-based) instead of geth's interpreter. +# This is an experimental feature for improved EVM throughput. +# Default: false +enabled = {{ .GigaExecutor.Enabled }} + +# occ_enabled controls whether to use OCC (Optimistic Concurrency Control) with the Giga executor. +# When true, transactions are executed in parallel with conflict detection and retry. +# Default: false +occ_enabled = {{ .GigaExecutor.OCCEnabled }} +` diff --git a/giga/executor/vm/evmc/host_context.go b/giga/executor/vm/evmc/host_context.go index 7840f61d64..3f7f20afe6 100644 --- a/giga/executor/vm/evmc/host_context.go +++ b/giga/executor/vm/evmc/host_context.go @@ -6,6 +6,7 @@ import ( "github.com/ethereum/go-ethereum/core/tracing" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/holiman/uint256" ) var _ evmc.HostContext = (*HostContext)(nil) @@ -13,10 +14,19 @@ var _ evmc.HostContext = (*HostContext)(nil) type HostContext struct { vm *evmc.VM evm *vm.EVM + // delegateToGeth controls whether Call should delegate to geth's EVM implementation + // This is set to true when entering from the interpreter (top-level calls) + // and should delegate child calls back to geth's implementation + delegateToGeth bool } func NewHostContext(vm *evmc.VM, evm *vm.EVM) *HostContext { - return &HostContext{vm: vm, evm: evm} + return &HostContext{vm: vm, evm: evm, delegateToGeth: true} +} + +// SetDelegateToGeth controls whether Call should route to geth's EVM implementation +func (h *HostContext) SetDelegateToGeth(delegate bool) { + h.delegateToGeth = delegate } func (h *HostContext) AccountExists(addr evmc.Address) bool { @@ -97,7 +107,7 @@ func (h *HostContext) Selfdestruct(addr evmc.Address, beneficiary evmc.Address) func (h *HostContext) GetTxContext() evmc.TxContext { var gasPrice evmc.Hash - h.evm.TxContext.GasPrice.FillBytes(gasPrice[:]) + h.evm.GasPrice.FillBytes(gasPrice[:]) var prevRandao evmc.Hash if h.evm.Context.Random != nil { @@ -113,9 +123,10 @@ func (h *HostContext) GetTxContext() evmc.TxContext { var blobBaseFee evmc.Hash h.evm.Context.BlobBaseFee.FillBytes(blobBaseFee[:]) + //nolint:gosec // G115: safe integer conversions for Time and GasLimit return evmc.TxContext{ GasPrice: gasPrice, - Origin: evmc.Address(h.evm.TxContext.Origin), + Origin: evmc.Address(h.evm.Origin), Coinbase: evmc.Address(h.evm.Context.Coinbase), Number: h.evm.Context.BlockNumber.Int64(), Timestamp: int64(h.evm.Context.Time), @@ -128,6 +139,7 @@ func (h *HostContext) GetTxContext() evmc.TxContext { } func (h *HostContext) GetBlockHash(number int64) evmc.Hash { + //nolint:gosec // G115: safe, block numbers are always positive return evmc.Hash(h.evm.Context.GetHash(uint64(number))) } @@ -139,46 +151,135 @@ func (h *HostContext) EmitLog(addr evmc.Address, topics []evmc.Hash, data []byte h.evm.StateDB.AddLog(ðtypes.Log{Address: common.Address(addr), Topics: gethTopics, Data: data}) } -// todo(pdrobnjak): figure out how to populate - evmRevision, delegated, code - probably can be passed down from interpreter -// this will sometimes be called throught interpreter.Run (top level) and sometimes from evmc_execute (child calls) -// which means that sometimes it should delegate to the interpreter and sometimes it should call evm.Call/evm.DelegateCall/... -// we are getting a Frankestein of geth + evmc + evmone -// can this be routed through depth? noup, but we can set an internal flag in HostContext when calling through interpreter.Run -// host HostContext needs to contain the EVM +// Call routes EVM calls either to geth's implementation or to evmone via evmc. +// When delegateToGeth is true (default), calls are routed to geth's EVM which handles +// all the complexity of call types. When false, calls go through evmc to evmone. +// +// The call flow is: +// - Top-level: interpreter.Run -> HostContext.Call (delegateToGeth=true) -> evm.Call/DelegateCall/etc +// - evmone path: evmc.Execute -> HostContext.Call (delegateToGeth=false) -> h.vm.Execute func (h *HostContext) Call( kind evmc.CallKind, recipient evmc.Address, sender evmc.Address, value evmc.Hash, input []byte, gas int64, depth int, static bool, salt evmc.Hash, codeAddress evmc.Address, ) ([]byte, int64, int64, evmc.Address, error) { - // evmc -> opdelegatecall -> HostContext.Call (here we should route ) -> evm.DelegateCall -> intepreter.Run -> HostContext.Call - flag := true - if flag { + // Convert evmc types to geth types + recipientAddr := common.Address(recipient) + senderAddr := common.Address(sender) + valueUint256 := new(uint256.Int).SetBytes(value[:]) + + // When delegateToGeth is true, route calls through geth's EVM implementation + if h.delegateToGeth { + var ret []byte + var leftoverGas uint64 + var err error + var createAddr common.Address + + //nolint:gosec // G115: safe integer conversions for gas values switch kind { case evmc.Call: - ret, leftoverGas, err := h.evm.Call(caller, addr, input, gas, value) + if static { + ret, leftoverGas, err = h.evm.StaticCall(senderAddr, recipientAddr, input, uint64(gas)) + } else { + ret, leftoverGas, err = h.evm.Call(senderAddr, recipientAddr, input, uint64(gas), valueUint256) + } case evmc.DelegateCall: - ret, leftoverGas, err := h.evm.DelegateCall(originCaller, caller, addr, input, gas, value) + // DelegateCall signature: (originCaller, caller, addr, input, gas, value) + // In delegate call, the sender is the origin, recipient is the target + ret, leftoverGas, err = h.evm.DelegateCall(h.evm.Origin, senderAddr, recipientAddr, input, uint64(gas), valueUint256) case evmc.CallCode: - ret, leftoverGas, err := h.evm.CallCode(caller, addr, input, gas, value) + ret, leftoverGas, err = h.evm.CallCode(senderAddr, recipientAddr, input, uint64(gas), valueUint256) case evmc.Create: - ret, createAddr, leftoverGas, err := h.evm.Create(caller, code, gas, value) + ret, createAddr, leftoverGas, err = h.evm.Create(senderAddr, input, uint64(gas), valueUint256) + return ret, int64(leftoverGas), 0, evmc.Address(createAddr), err case evmc.Create2: - ret, createAddr, leftoverGas, err := h.evm.Create2(caller, code, gas, endowment, salt) + saltUint256 := new(uint256.Int).SetBytes(salt[:]) + ret, createAddr, leftoverGas, err = h.evm.Create2(senderAddr, input, uint64(gas), valueUint256, saltUint256) + return ret, int64(leftoverGas), 0, evmc.Address(createAddr), err + default: + // StaticCall and EofCreate are handled here + ret, leftoverGas, err = h.evm.StaticCall(senderAddr, recipientAddr, input, uint64(gas)) } + + //nolint:gosec // G115: safe, leftoverGas won't exceed int64 max + return ret, int64(leftoverGas), 0, evmc.Address{}, err } - // ELSE - evmRevision := evmc.Frontier - delegated := false - var code []byte + + // When not delegating to geth, use evmc/evmone for execution + // Determine EVM revision based on chain config and block number + evmRevision := h.getEVMRevision() + delegated := kind == evmc.DelegateCall || kind == evmc.CallCode + code := h.evm.StateDB.GetCode(recipientAddr) + executionResult, err := h.vm.Execute( h, evmRevision, kind, static, delegated, depth, gas, recipient, sender, input, value, code, ) if err != nil { - return nil, 0, 0, [20]byte{}, err + return nil, 0, 0, evmc.Address{}, err } - //todo(pdrobnjak): figure out how to populate createAddr - return executionResult.Output, executionResult.GasLeft, executionResult.GasRefund, evmc.Address{}, nil + // For Create/Create2, calculate the created address + var createAddr evmc.Address + if kind == evmc.Create || kind == evmc.Create2 { + // The created address should be set in the execution result + // For now, return empty - this needs to be populated from evmone's result + createAddr = evmc.Address{} + } + + return executionResult.Output, executionResult.GasLeft, executionResult.GasRefund, createAddr, nil +} + +// getEVMRevision determines the EVM revision based on the current chain configuration +func (h *HostContext) getEVMRevision() evmc.Revision { + chainConfig := h.evm.ChainConfig() + blockNumber := h.evm.Context.BlockNumber + time := h.evm.Context.Time + isMerge := h.evm.Context.Random != nil + + // Get the rules for the current block + rules := chainConfig.Rules(blockNumber, isMerge, time) + + // Check from newest to oldest using rules + if rules.IsPrague { + return evmc.Prague + } + if rules.IsCancun { + return evmc.Cancun + } + if rules.IsShanghai { + return evmc.Shanghai + } + if rules.IsMerge { + return evmc.Paris + } + if rules.IsLondon { + return evmc.London + } + if rules.IsBerlin { + return evmc.Berlin + } + if rules.IsIstanbul { + return evmc.Istanbul + } + if rules.IsPetersburg { + return evmc.Petersburg + } + if rules.IsConstantinople { + return evmc.Constantinople + } + if rules.IsByzantium { + return evmc.Byzantium + } + if rules.IsEIP158 { + return evmc.SpuriousDragon + } + if rules.IsEIP150 { + return evmc.TangerineWhistle + } + if rules.IsHomestead { + return evmc.Homestead + } + return evmc.Frontier } func (h *HostContext) AccessAccount(addr evmc.Address) evmc.AccessStatus { diff --git a/giga/executor/vm/evmc/interpreter.go b/giga/executor/vm/evmc/interpreter.go index b29a6b9e24..d507c88c0c 100644 --- a/giga/executor/vm/evmc/interpreter.go +++ b/giga/executor/vm/evmc/interpreter.go @@ -5,8 +5,7 @@ import ( "github.com/ethereum/go-ethereum/core/vm" ) -var _ vm.IEVMInterpreter = (*EVMInterpreter)(nil) - +// EVMInterpreter is a custom interpreter that delegates execution to evmone via EVMC. type EVMInterpreter struct { hostContext evmc.HostContext evm *vm.EVM @@ -17,12 +16,12 @@ func NewEVMInterpreter(hostContext evmc.HostContext, evm *vm.EVM) *EVMInterprete return &EVMInterpreter{hostContext: hostContext, evm: evm} } +// Run executes the contract code via evmone. func (e *EVMInterpreter) Run(contract *vm.Contract, input []byte, readOnly bool) ([]byte, error) { - // todo(pdrobnjak): figure out if there is a way to avoid this, probably not, I'll have to replicate every interpreter side effect - // PASTED FROM GETH // Increment the call depth which is restricted to 1024 - e.evm.depth++ - defer func() { e.evm.depth-- }() + e.evm.Depth++ + defer func() { e.evm.Depth-- }() + depth := e.evm.Depth // Make sure the readOnly is only set if we aren't in readOnly yet. // This also makes sure that the readOnly flag isn't removed for child calls. @@ -30,7 +29,6 @@ func (e *EVMInterpreter) Run(contract *vm.Contract, input []byte, readOnly bool) e.readOnly = true defer func() { e.readOnly = false }() } - // PASTED FROM GETH // todo(pdrobnjak): figure out how to access these values and how to validate if they are populated correctly callKind := evmc.Call @@ -40,8 +38,9 @@ func (e *EVMInterpreter) Run(contract *vm.Contract, input []byte, readOnly bool) // irrelevant as it is only used for CREATE2 - geth is handling our CREATE2 logic salt := evmc.Hash{} codeAddress := evmc.Address{} + //nolint:dogsled,gosec // dogsled: Call returns 5 values, we only need output and err; gosec: safe gas conversion output, _, _, _, err := e.hostContext.Call(callKind, recipient, sender, contract.Value().Bytes32(), input, - int64(contract.Gas), e.evm.GetDepth(), static, salt, codeAddress) + int64(contract.Gas), depth, static, salt, codeAddress) if err != nil { return nil, err } diff --git a/giga/executor/vm/evmc/vm.go b/giga/executor/vm/evmc/vm.go index c0c6ec5743..e75ad00b96 100644 --- a/giga/executor/vm/evmc/vm.go +++ b/giga/executor/vm/evmc/vm.go @@ -11,10 +11,10 @@ type VMImpl struct { evm *vm.EVM } -// this should bootstrap evmone - receive a configuration or something similar, we can do it the same way we did in v3 +// NewVM creates a new giga executor VM wrapper. +// TODO(pdrobnjak): populate evmc.VM and integrate evmone for direct bytecode execution func NewVM(blockCtx vm.BlockContext, stateDB vm.StateDB, chainConfig *params.ChainConfig, config vm.Config, customPrecompiles map[common.Address]vm.PrecompiledContract) *VMImpl { evm := vm.NewEVM(blockCtx, stateDB, chainConfig, config, customPrecompiles) - // todo(pdrobnjak): populate evmc.VM hostContext := NewHostContext(nil, evm) evm.EVMInterpreter = NewEVMInterpreter(hostContext, evm) return &VMImpl{ @@ -22,6 +22,11 @@ func NewVM(blockCtx vm.BlockContext, stateDB vm.StateDB, chainConfig *params.Cha } } +// SetTxContext sets the transaction context for the EVM +func (v *VMImpl) SetTxContext(txCtx vm.TxContext) { + v.evm.SetTxContext(txCtx) +} + func (v *VMImpl) ApplyMessage(msg *core.Message, gp *core.GasPool) (*core.ExecutionResult, error) { executionResult, err := core.ApplyMessage(v.evm, msg, gp) return executionResult, err diff --git a/giga/tests/giga_test.go b/giga/tests/giga_test.go new file mode 100644 index 0000000000..978f36a8ac --- /dev/null +++ b/giga/tests/giga_test.go @@ -0,0 +1,472 @@ +package giga_test + +import ( + "math/big" + "testing" + "time" + + "github.com/cosmos/cosmos-sdk/baseapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/sei-protocol/sei-chain/app" + "github.com/sei-protocol/sei-chain/occ_tests/utils" + "github.com/sei-protocol/sei-chain/x/evm/config" + "github.com/sei-protocol/sei-chain/x/evm/types" + "github.com/sei-protocol/sei-chain/x/evm/types/ethtx" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" +) + +// ExecutorMode defines which executor path to use +type ExecutorMode int + +const ( + ModeGethSequential ExecutorMode = iota // Default geth interpreter, no OCC + ModeGigaSequential // Giga executor, no OCC + ModeGigaOCC // Giga executor with OCC +) + +func (m ExecutorMode) String() string { + switch m { + case ModeGethSequential: + return "GethSequential" + case ModeGigaSequential: + return "GigaSequential" + case ModeGigaOCC: + return "GigaOCC" + default: + return "Unknown" + } +} + +// GigaTestContext wraps the test context with executor mode +type GigaTestContext struct { + Ctx sdk.Context + TestApp *app.App + TestAccounts []utils.TestAcct + Mode ExecutorMode +} + +// NewGigaTestContext creates a test context configured for a specific executor mode +func NewGigaTestContext(t *testing.T, testAccts []utils.TestAcct, blockTime time.Time, workers int, mode ExecutorMode) *GigaTestContext { + occEnabled := mode == ModeGigaOCC + gigaEnabled := mode == ModeGigaSequential || mode == ModeGigaOCC + gigaOCCEnabled := mode == ModeGigaOCC + + wrapper := app.NewTestWrapper(t, blockTime, testAccts[0].PublicKey, true, func(ba *baseapp.BaseApp) { + ba.SetOccEnabled(occEnabled) + ba.SetConcurrencyWorkers(workers) + }) + testApp := wrapper.App + ctx := wrapper.Ctx + ctx = ctx.WithBlockHeader(tmproto.Header{ + Height: ctx.BlockHeader().Height, + ChainID: ctx.BlockHeader().ChainID, + Time: blockTime, + }) + + // Configure giga executor + testApp.EvmKeeper.GigaExecutorEnabled = gigaEnabled + testApp.EvmKeeper.GigaOCCEnabled = gigaOCCEnabled + + // Fund test accounts + amounts := sdk.NewCoins( + sdk.NewCoin("usei", sdk.NewInt(1000000000000000000)), + sdk.NewCoin("uusdc", sdk.NewInt(1000000000000000)), + ) + for _, ta := range testAccts { + err := testApp.BankKeeper.MintCoins(ctx, "mint", amounts) + if err != nil { + t.Fatalf("failed to mint coins: %v", err) + } + err = testApp.BankKeeper.SendCoinsFromModuleToAccount(ctx, "mint", ta.AccountAddress, amounts) + if err != nil { + t.Fatalf("failed to send coins: %v", err) + } + } + + return &GigaTestContext{ + Ctx: ctx, + TestApp: testApp, + TestAccounts: testAccts, + Mode: mode, + } +} + +// EVMTransfer represents an EVM transfer transaction for testing +type EVMTransfer struct { + Signer utils.TestAcct + To common.Address + Value *big.Int + Nonce uint64 +} + +// CreateEVMTransferTxs creates signed EVM transfer transactions and funds the signers +func CreateEVMTransferTxs(t *testing.T, tCtx *GigaTestContext, transfers []EVMTransfer) [][]byte { + txs := make([][]byte, 0, len(transfers)) + tc := app.MakeEncodingConfig().TxConfig + + for _, transfer := range transfers { + // Associate the Cosmos address with the EVM address + // This is required for the Giga executor path which bypasses ante handlers + tCtx.TestApp.EvmKeeper.SetAddressMapping(tCtx.Ctx, transfer.Signer.AccountAddress, transfer.Signer.EvmAddress) + + // Fund the signer account before creating the transaction + amounts := sdk.NewCoins( + sdk.NewCoin("usei", sdk.NewInt(1000000000000000000)), + sdk.NewCoin("uusdc", sdk.NewInt(1000000000000000)), + ) + err := tCtx.TestApp.BankKeeper.MintCoins(tCtx.Ctx, "mint", amounts) + require.NoError(t, err) + err = tCtx.TestApp.BankKeeper.SendCoinsFromModuleToAccount(tCtx.Ctx, "mint", transfer.Signer.AccountAddress, amounts) + require.NoError(t, err) + + signedTx, err := ethtypes.SignTx(ethtypes.NewTx(ðtypes.DynamicFeeTx{ + GasFeeCap: new(big.Int).SetUint64(100000000000), + GasTipCap: new(big.Int).SetUint64(100000000000), + Gas: 21000, + ChainID: big.NewInt(config.DefaultChainID), + To: &transfer.To, + Value: transfer.Value, + Nonce: transfer.Nonce, + }), transfer.Signer.EvmSigner, transfer.Signer.EvmPrivateKey) + require.NoError(t, err) + + txData, err := ethtx.NewTxDataFromTx(signedTx) + require.NoError(t, err) + + msg, err := types.NewMsgEVMTransaction(txData) + require.NoError(t, err) + + // Build the Cosmos tx wrapper + txBuilder := tc.NewTxBuilder() + err = txBuilder.SetMsgs(msg) + require.NoError(t, err) + txBuilder.SetGasLimit(10000000000) + txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin("usei", sdk.NewInt(10000000000)))) + + txBytes, err := tc.TxEncoder()(txBuilder.GetTx()) + require.NoError(t, err) + + txs = append(txs, txBytes) + } + + return txs +} + +// GenerateNonConflictingTransfers creates transfers where each sender is unique +func GenerateNonConflictingTransfers(count int) []EVMTransfer { + transfers := make([]EVMTransfer, count) + for i := 0; i < count; i++ { + signer := utils.NewSigner() + transfers[i] = EVMTransfer{ + Signer: signer, + To: signer.EvmAddress, // Send to self + Value: big.NewInt(1), + Nonce: 0, + } + } + return transfers +} + +// GenerateConflictingTransfers creates transfers where all send to the same recipient +func GenerateConflictingTransfers(count int, recipient common.Address) []EVMTransfer { + transfers := make([]EVMTransfer, count) + for i := 0; i < count; i++ { + signer := utils.NewSigner() + transfers[i] = EVMTransfer{ + Signer: signer, + To: recipient, // All send to same address + Value: big.NewInt(1), + Nonce: 0, + } + } + return transfers +} + +// RunBlock executes a block of transactions and returns results +func RunBlock(t *testing.T, tCtx *GigaTestContext, txs [][]byte) ([]abci.Event, []*abci.ExecTxResult, error) { + // Set global OCC flag based on mode + app.EnableOCC = tCtx.Mode == ModeGigaOCC + + req := &abci.RequestFinalizeBlock{ + Txs: txs, + Height: tCtx.Ctx.BlockHeader().Height, + } + + events, results, _, err := tCtx.TestApp.ProcessBlock(tCtx.Ctx, txs, req, req.DecidedLastCommit, false) + return events, results, err +} + +// CompareResults compares execution results between two runs +func CompareResults(t *testing.T, testName string, expected, actual []*abci.ExecTxResult) { + require.Equal(t, len(expected), len(actual), "%s: result count mismatch", testName) + + for i := range expected { + if expected[i].Code != actual[i].Code { + t.Logf("%s: tx[%d] expected code=%d log=%q", testName, i, expected[i].Code, expected[i].Log) + t.Logf("%s: tx[%d] actual code=%d log=%q", testName, i, actual[i].Code, actual[i].Log) + } + require.Equal(t, expected[i].Code, actual[i].Code, + "%s: tx[%d] code mismatch (expected %d, got %d)", testName, i, expected[i].Code, actual[i].Code) + + // For successful txs, compare gas used (allow small variance for different execution paths) + if expected[i].Code == 0 && actual[i].Code == 0 { + require.Equal(t, expected[i].GasUsed, actual[i].GasUsed, + "%s: tx[%d] gas used mismatch", testName, i) + } + + // Compare EvmTxInfo if present + if expected[i].EvmTxInfo != nil { + require.NotNil(t, actual[i].EvmTxInfo, "%s: tx[%d] missing EvmTxInfo", testName, i) + require.Equal(t, expected[i].EvmTxInfo.TxHash, actual[i].EvmTxInfo.TxHash, + "%s: tx[%d] tx hash mismatch", testName, i) + require.Equal(t, expected[i].EvmTxInfo.Nonce, actual[i].EvmTxInfo.Nonce, + "%s: tx[%d] nonce mismatch", testName, i) + } + } +} + +// TestGigaVsGeth_NonConflicting compares Giga executor vs Geth for non-conflicting EVM transfers +func TestGigaVsGeth_NonConflicting(t *testing.T) { + blockTime := time.Now() + accts := utils.NewTestAccounts(5) + txCount := 10 + + // Generate the same transfers for both runs + transfers := GenerateNonConflictingTransfers(txCount) + + // Run with Geth (baseline) + gethCtx := NewGigaTestContext(t, accts, blockTime, 1, ModeGethSequential) + gethTxs := CreateEVMTransferTxs(t, gethCtx, transfers) + _, gethResults, gethErr := RunBlock(t, gethCtx, gethTxs) + require.NoError(t, gethErr, "Geth execution failed") + require.Len(t, gethResults, txCount) + + // Run with Giga Sequential + gigaCtx := NewGigaTestContext(t, accts, blockTime, 1, ModeGigaSequential) + gigaTxs := CreateEVMTransferTxs(t, gigaCtx, transfers) + _, gigaResults, gigaErr := RunBlock(t, gigaCtx, gigaTxs) + require.NoError(t, gigaErr, "Giga execution failed") + require.Len(t, gigaResults, txCount) + + // Compare results + CompareResults(t, "GigaVsGeth_NonConflicting", gethResults, gigaResults) + + // Verify all transactions succeeded + for i, result := range gethResults { + require.Equal(t, uint32(0), result.Code, "Geth tx[%d] failed: %s", i, result.Log) + } + for i, result := range gigaResults { + require.Equal(t, uint32(0), result.Code, "Giga tx[%d] failed: %s", i, result.Log) + } +} + +// TestGigaVsGeth_Conflicting compares Giga executor vs Geth for conflicting EVM transfers +func TestGigaVsGeth_Conflicting(t *testing.T) { + blockTime := time.Now() + accts := utils.NewTestAccounts(5) + txCount := 10 + + // All transfers go to the same recipient (conflicting) + recipient := accts[0].EvmAddress + transfers := GenerateConflictingTransfers(txCount, recipient) + + // Run with Geth (baseline) + gethCtx := NewGigaTestContext(t, accts, blockTime, 1, ModeGethSequential) + gethTxs := CreateEVMTransferTxs(t, gethCtx, transfers) + _, gethResults, gethErr := RunBlock(t, gethCtx, gethTxs) + require.NoError(t, gethErr, "Geth execution failed") + require.Len(t, gethResults, txCount) + + // Run with Giga Sequential + gigaCtx := NewGigaTestContext(t, accts, blockTime, 1, ModeGigaSequential) + gigaTxs := CreateEVMTransferTxs(t, gigaCtx, transfers) + _, gigaResults, gigaErr := RunBlock(t, gigaCtx, gigaTxs) + require.NoError(t, gigaErr, "Giga execution failed") + require.Len(t, gigaResults, txCount) + + // Compare results + CompareResults(t, "GigaVsGeth_Conflicting", gethResults, gigaResults) +} + +// TestGigaOCCVsGigaSequential_NonConflicting compares Giga+OCC vs Giga sequential for non-conflicting transfers +func TestGigaOCCVsGigaSequential_NonConflicting(t *testing.T) { + blockTime := time.Now() + accts := utils.NewTestAccounts(5) + txCount := 20 + workers := 4 + + transfers := GenerateNonConflictingTransfers(txCount) + + // Run with Giga Sequential (baseline) + seqCtx := NewGigaTestContext(t, accts, blockTime, 1, ModeGigaSequential) + seqTxs := CreateEVMTransferTxs(t, seqCtx, transfers) + _, seqResults, seqErr := RunBlock(t, seqCtx, seqTxs) + require.NoError(t, seqErr, "Giga sequential execution failed") + require.Len(t, seqResults, txCount) + + // Run with Giga OCC (multiple times to catch race conditions) + for run := 0; run < 3; run++ { + occCtx := NewGigaTestContext(t, accts, blockTime, workers, ModeGigaOCC) + occTxs := CreateEVMTransferTxs(t, occCtx, transfers) + _, occResults, occErr := RunBlock(t, occCtx, occTxs) + require.NoError(t, occErr, "Giga OCC execution failed (run %d)", run) + require.Len(t, occResults, txCount) + + // Compare results + CompareResults(t, "GigaOCCVsSequential_NonConflicting", seqResults, occResults) + } +} + +// TestGigaOCCVsGigaSequential_Conflicting compares Giga+OCC vs Giga sequential for conflicting transfers +func TestGigaOCCVsGigaSequential_Conflicting(t *testing.T) { + blockTime := time.Now() + accts := utils.NewTestAccounts(5) + txCount := 20 + workers := 4 + + recipient := accts[0].EvmAddress + transfers := GenerateConflictingTransfers(txCount, recipient) + + // Run with Giga Sequential (baseline) + seqCtx := NewGigaTestContext(t, accts, blockTime, 1, ModeGigaSequential) + seqTxs := CreateEVMTransferTxs(t, seqCtx, transfers) + _, seqResults, seqErr := RunBlock(t, seqCtx, seqTxs) + require.NoError(t, seqErr, "Giga sequential execution failed") + require.Len(t, seqResults, txCount) + + // Run with Giga OCC (multiple times to catch race conditions) + for run := 0; run < 3; run++ { + occCtx := NewGigaTestContext(t, accts, blockTime, workers, ModeGigaOCC) + occTxs := CreateEVMTransferTxs(t, occCtx, transfers) + _, occResults, occErr := RunBlock(t, occCtx, occTxs) + require.NoError(t, occErr, "Giga OCC execution failed (run %d)", run) + require.Len(t, occResults, txCount) + + // Compare results + CompareResults(t, "GigaOCCVsSequential_Conflicting", seqResults, occResults) + } +} + +// TestGigaOCCVsGigaSequential_Mixed compares with a mix of conflicting and non-conflicting +func TestGigaOCCVsGigaSequential_Mixed(t *testing.T) { + blockTime := time.Now() + accts := utils.NewTestAccounts(5) + conflictingCount := 10 + nonConflictingCount := 10 + workers := 4 + + recipient := accts[0].EvmAddress + conflicting := GenerateConflictingTransfers(conflictingCount, recipient) + nonConflicting := GenerateNonConflictingTransfers(nonConflictingCount) + + // Interleave conflicting and non-conflicting + transfers := make([]EVMTransfer, 0, conflictingCount+nonConflictingCount) + for i := 0; i < max(conflictingCount, nonConflictingCount); i++ { + if i < conflictingCount { + transfers = append(transfers, conflicting[i]) + } + if i < nonConflictingCount { + transfers = append(transfers, nonConflicting[i]) + } + } + + // Run with Giga Sequential (baseline) + seqCtx := NewGigaTestContext(t, accts, blockTime, 1, ModeGigaSequential) + seqTxs := CreateEVMTransferTxs(t, seqCtx, transfers) + _, seqResults, seqErr := RunBlock(t, seqCtx, seqTxs) + require.NoError(t, seqErr, "Giga sequential execution failed") + + // Run with Giga OCC + for run := 0; run < 3; run++ { + occCtx := NewGigaTestContext(t, accts, blockTime, workers, ModeGigaOCC) + occTxs := CreateEVMTransferTxs(t, occCtx, transfers) + _, occResults, occErr := RunBlock(t, occCtx, occTxs) + require.NoError(t, occErr, "Giga OCC execution failed (run %d)", run) + + CompareResults(t, "GigaOCCVsSequential_Mixed", seqResults, occResults) + } +} + +// TestAllModes_NonConflicting runs the same transactions through all three modes and compares +func TestAllModes_NonConflicting(t *testing.T) { + blockTime := time.Now() + accts := utils.NewTestAccounts(5) + txCount := 15 + workers := 4 + + transfers := GenerateNonConflictingTransfers(txCount) + + // Geth Sequential + gethCtx := NewGigaTestContext(t, accts, blockTime, 1, ModeGethSequential) + gethTxs := CreateEVMTransferTxs(t, gethCtx, transfers) + _, gethResults, gethErr := RunBlock(t, gethCtx, gethTxs) + require.NoError(t, gethErr) + + // Giga Sequential + gigaSeqCtx := NewGigaTestContext(t, accts, blockTime, 1, ModeGigaSequential) + gigaSeqTxs := CreateEVMTransferTxs(t, gigaSeqCtx, transfers) + _, gigaSeqResults, gigaSeqErr := RunBlock(t, gigaSeqCtx, gigaSeqTxs) + require.NoError(t, gigaSeqErr) + + // Giga OCC + gigaOCCCtx := NewGigaTestContext(t, accts, blockTime, workers, ModeGigaOCC) + gigaOCCTxs := CreateEVMTransferTxs(t, gigaOCCCtx, transfers) + _, gigaOCCResults, gigaOCCErr := RunBlock(t, gigaOCCCtx, gigaOCCTxs) + require.NoError(t, gigaOCCErr) + + // Compare: Geth vs Giga Sequential + CompareResults(t, "AllModes_GethVsGigaSeq", gethResults, gigaSeqResults) + + // Compare: Giga Sequential vs Giga OCC + CompareResults(t, "AllModes_GigaSeqVsOCC", gigaSeqResults, gigaOCCResults) + + t.Logf("All %d transactions produced identical results across all three executor modes", txCount) +} + +// TestAllModes_Conflicting runs conflicting transactions through all three modes +func TestAllModes_Conflicting(t *testing.T) { + blockTime := time.Now() + accts := utils.NewTestAccounts(5) + txCount := 15 + workers := 4 + + recipient := accts[0].EvmAddress + transfers := GenerateConflictingTransfers(txCount, recipient) + + // Geth Sequential + gethCtx := NewGigaTestContext(t, accts, blockTime, 1, ModeGethSequential) + gethTxs := CreateEVMTransferTxs(t, gethCtx, transfers) + _, gethResults, gethErr := RunBlock(t, gethCtx, gethTxs) + require.NoError(t, gethErr) + + // Giga Sequential + gigaSeqCtx := NewGigaTestContext(t, accts, blockTime, 1, ModeGigaSequential) + gigaSeqTxs := CreateEVMTransferTxs(t, gigaSeqCtx, transfers) + _, gigaSeqResults, gigaSeqErr := RunBlock(t, gigaSeqCtx, gigaSeqTxs) + require.NoError(t, gigaSeqErr) + + // Giga OCC + gigaOCCCtx := NewGigaTestContext(t, accts, blockTime, workers, ModeGigaOCC) + gigaOCCTxs := CreateEVMTransferTxs(t, gigaOCCCtx, transfers) + _, gigaOCCResults, gigaOCCErr := RunBlock(t, gigaOCCCtx, gigaOCCTxs) + require.NoError(t, gigaOCCErr) + + // Compare: Geth vs Giga Sequential + CompareResults(t, "AllModes_Conflicting_GethVsGigaSeq", gethResults, gigaSeqResults) + + // Compare: Giga Sequential vs Giga OCC + CompareResults(t, "AllModes_Conflicting_GigaSeqVsOCC", gigaSeqResults, gigaOCCResults) + + t.Logf("All %d conflicting transactions produced identical results across all three executor modes", txCount) +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} diff --git a/go.mod b/go.mod index 8a91834fd1..01d909f0fb 100644 --- a/go.mod +++ b/go.mod @@ -352,7 +352,7 @@ replace ( github.com/btcsuite/btcd => github.com/btcsuite/btcd v0.23.2 github.com/confio/ics23/go => github.com/cosmos/cosmos-sdk/ics23/go v0.8.0 github.com/cosmos/cosmos-sdk => ./sei-cosmos - github.com/ethereum/go-ethereum => ../go-ethereum + github.com/ethereum/go-ethereum => github.com/sei-protocol/go-ethereum v1.15.7-sei-9.0.20260108183608-65223515ac2c github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 // Latest goleveldb is broken, we have to stick to this version github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 diff --git a/go.sum b/go.sum index 6b381d5020..cc17aa68ff 100644 --- a/go.sum +++ b/go.sum @@ -1921,6 +1921,8 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg github.com/securego/gosec/v2 v2.13.1 h1:7mU32qn2dyC81MH9L2kefnQyRMUarfDER3iQyMHcjYM= github.com/securego/gosec/v2 v2.13.1/go.mod h1:EO1sImBMBWFjOTFzMWfTRrZW6M15gm60ljzrmy/wtHo= github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY= +github.com/sei-protocol/go-ethereum v1.15.7-sei-9.0.20260108183608-65223515ac2c h1:0IythS4dIrevsGmKVt7G5dpqfJXOyjHzcD+TiQGsJbs= +github.com/sei-protocol/go-ethereum v1.15.7-sei-9.0.20260108183608-65223515ac2c/go.mod h1:+S9k+jFzlyVTNcYGvqFhzN/SFhI6vA+aOY4T5tLSPL0= github.com/sei-protocol/goutils v0.0.2 h1:Bfa7Sv+4CVLNM20QcpvGb81B8C5HkQC/kW1CQpIbXDA= github.com/sei-protocol/goutils v0.0.2/go.mod h1:iYE2DuJfEnM+APPehr2gOUXfuLuPsVxorcDO+Tzq9q8= github.com/sei-protocol/sei-load v0.0.0-20251007135253-78fbdc141082 h1:f2sY8OcN60UL1/6POx+HDMZ4w04FTZtSScnrFSnGZHg= diff --git a/go.work b/go.work index 4f2104417c..a362a82b43 100644 --- a/go.work +++ b/go.work @@ -5,5 +5,3 @@ use ( ./sei-cosmos ./sei-tendermint ) - -replace github.com/ethereum/go-ethereum => ../go-ethereum diff --git a/scripts/benchmark.sh b/scripts/benchmark.sh index 32d417da37..e72c48eefd 100755 --- a/scripts/benchmark.sh +++ b/scripts/benchmark.sh @@ -4,6 +4,8 @@ set -e # Parse command line arguments MOCK_BALANCES=${MOCK_BALANCES:-true} +GIGA_EXECUTOR=${GIGA_EXECUTOR:-false} +GIGA_OCC=${GIGA_OCC:-false} # Use python3 as default, but fall back to python if python3 doesn't exist PYTHON_CMD=python3 @@ -15,6 +17,13 @@ fi # set key name keyname=admin +# Display configuration +echo "=== Benchmark Configuration ===" +echo " MOCK_BALANCES: $MOCK_BALANCES" +echo " GIGA_EXECUTOR: $GIGA_EXECUTOR" +echo " GIGA_OCC: $GIGA_OCC" +echo "================================" + # clean up old sei directory rm -rf ~/.sei echo "Building..." @@ -48,9 +57,9 @@ cat ~/.sei/config/genesis.json | jq '.app_state["oracle"]["params"]["vote_period cat ~/.sei/config/genesis.json | jq '.app_state["evm"]["params"]["target_gas_used_per_block"]="1000000000000"' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json cat ~/.sei/config/genesis.json | jq '.app_state["oracle"]["params"]["whitelist"]=[{"name": "ueth"},{"name": "ubtc"},{"name": "uusdc"},{"name": "uusdt"},{"name": "uosmo"},{"name": "uatom"},{"name": "usei"}]' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json cat ~/.sei/config/genesis.json | jq '.app_state["distribution"]["params"]["community_tax"]="0.000000000000000000"' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json -cat ~/.sei/config/genesis.json | jq '.consensus_params["block"]["max_gas"]="35000000"' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json +cat ~/.sei/config/genesis.json | jq '.consensus_params["block"]["max_gas"]="100000000"' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json cat ~/.sei/config/genesis.json | jq '.consensus_params["block"]["min_txs_in_block"]="2"' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json -cat ~/.sei/config/genesis.json | jq '.consensus_params["block"]["max_gas_wanted"]="50000000"' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json +cat ~/.sei/config/genesis.json | jq '.consensus_params["block"]["max_gas_wanted"]="150000000"' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json cat ~/.sei/config/genesis.json | jq '.app_state["staking"]["params"]["max_voting_power_ratio"]="1.000000000000000000"' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json cat ~/.sei/config/genesis.json | jq '.app_state["bank"]["denom_metadata"]=[{"denom_units":[{"denom":"usei","exponent":0,"aliases":["USEI"]}],"base":"usei","display":"usei","name":"USEI","symbol":"USEI"}]' > ~/.sei/config/tmp_genesis.json && mv ~/.sei/config/tmp_genesis.json ~/.sei/config/genesis.json @@ -73,6 +82,42 @@ sed -i.bak -e 's/occ-enabled = .*/occ-enabled = true/' $APP_TOML_PATH sed -i.bak -e 's/sc-enable = .*/sc-enable = true/' $APP_TOML_PATH sed -i.bak -e 's/ss-enable = .*/ss-enable = true/' $APP_TOML_PATH +# Enable Giga Executor (evmone-based) if requested +if [ "$GIGA_EXECUTOR" = true ]; then + echo "Enabling Giga Executor (evmone-based EVM)..." + if grep -q "\[giga_executor\]" $APP_TOML_PATH; then + # If the section exists, update enabled to true + if [[ "$OSTYPE" == "darwin"* ]]; then + sed -i '' '/\[giga_executor\]/,/^\[/ s/enabled = false/enabled = true/' $APP_TOML_PATH + else + sed -i '/\[giga_executor\]/,/^\[/ s/enabled = false/enabled = true/' $APP_TOML_PATH + fi + else + # If section doesn't exist, append it + echo "" >> $APP_TOML_PATH + echo "[giga_executor]" >> $APP_TOML_PATH + echo "enabled = true" >> $APP_TOML_PATH + echo "occ_enabled = false" >> $APP_TOML_PATH + fi + + # Set OCC based on GIGA_OCC flag + if [ "$GIGA_OCC" = true ]; then + echo "Enabling OCC for Giga Executor..." + if [[ "$OSTYPE" == "darwin"* ]]; then + sed -i '' 's/occ_enabled = false/occ_enabled = true/' $APP_TOML_PATH + else + sed -i 's/occ_enabled = false/occ_enabled = true/' $APP_TOML_PATH + fi + else + echo "Disabling OCC for Giga Executor (sequential mode)..." + if [[ "$OSTYPE" == "darwin"* ]]; then + sed -i '' 's/occ_enabled = true/occ_enabled = false/' $APP_TOML_PATH + else + sed -i 's/occ_enabled = true/occ_enabled = false/' $APP_TOML_PATH + fi + fi +fi + # set block time to 2s if [ ! -z "$1" ]; then CONFIG_PATH="$1" diff --git a/scripts/initialize_local_chain.sh b/scripts/initialize_local_chain.sh index 2ed7e3418e..29fb6365a3 100755 --- a/scripts/initialize_local_chain.sh +++ b/scripts/initialize_local_chain.sh @@ -4,6 +4,7 @@ set -e # Parse command line arguments MOCK_BALANCES=${MOCK_BALANCES:-false} +GIGA_EXECUTOR=${GIGA_EXECUTOR:-false} # Use python3 as default, but fall back to python if python3 doesn't exist PYTHON_CMD=python3 @@ -29,6 +30,12 @@ keyname=admin # -p 14269:14269 \ # -p 9411:9411 \ # jaegertracing/all-in-one:1.33 +# Display configuration +echo "=== Local Chain Configuration ===" +echo " MOCK_BALANCES: $MOCK_BALANCES" +echo " GIGA_EXECUTOR: $GIGA_EXECUTOR" +echo "=================================" + # clean up old sei directory rm -rf ~/.sei echo "Building..." @@ -91,6 +98,24 @@ sed -i.bak -e 's/occ-enabled = .*/occ-enabled = true/' $APP_TOML_PATH sed -i.bak -e 's/sc-enable = .*/sc-enable = true/' $APP_TOML_PATH sed -i.bak -e 's/ss-enable = .*/ss-enable = true/' $APP_TOML_PATH +# Enable Giga Executor (evmone-based) if requested +if [ "$GIGA_EXECUTOR" = true ]; then + echo "Enabling Giga Executor (evmone-based EVM)..." + if grep -q "\[giga_executor\]" $APP_TOML_PATH; then + # If the section exists, update it (only within giga_executor section) + if [[ "$OSTYPE" == "darwin"* ]]; then + sed -i '' '/\[giga_executor\]/,/^\[/ s/enabled = false/enabled = true/' $APP_TOML_PATH + else + sed -i '/\[giga_executor\]/,/^\[/ s/enabled = false/enabled = true/' $APP_TOML_PATH + fi + else + # If section doesn't exist, append it + echo "" >> $APP_TOML_PATH + echo "[giga_executor]" >> $APP_TOML_PATH + echo "enabled = true" >> $APP_TOML_PATH + fi +fi + # set block time to 2s if [ ! -z "$1" ]; then CONFIG_PATH="$1" diff --git a/sei-cosmos/go.mod b/sei-cosmos/go.mod index cb253ede39..43e3bda8cb 100644 --- a/sei-cosmos/go.mod +++ b/sei-cosmos/go.mod @@ -174,7 +174,7 @@ replace ( github.com/99designs/keyring => github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76 github.com/btcsuite/btcd => github.com/btcsuite/btcd v0.23.2 github.com/confio/ics23/go => github.com/cosmos/cosmos-sdk/ics23/go v0.8.0 - github.com/ethereum/go-ethereum => github.com/sei-protocol/go-ethereum v1.15.7-sei-7.0.20250929182230-93350978bb7c + github.com/ethereum/go-ethereum => github.com/sei-protocol/go-ethereum v1.15.7-sei-9.0.20260108183608-65223515ac2c // Fix upstream GHSA-h395-qcrw-5vmq vulnerability. // TODO Remove it: https://github.com/cosmos/cosmos-sdk/issues/10409 github.com/gin-gonic/gin => github.com/gin-gonic/gin v1.7.0 diff --git a/sei-cosmos/tasks/scheduler.go b/sei-cosmos/tasks/scheduler.go index 0cf92afc25..7b1d61b29c 100644 --- a/sei-cosmos/tasks/scheduler.go +++ b/sei-cosmos/tasks/scheduler.go @@ -457,12 +457,13 @@ func (s *scheduler) executeAll(ctx sdk.Context, tasks []*deliverTxTask) error { } func (s *scheduler) prepareAndRunTask(wg *sync.WaitGroup, ctx sdk.Context, task *deliverTxTask) { + defer wg.Done() // Must be deferred to prevent deadlock on panic + eCtx, eSpan := s.traceSpan(ctx, "SchedulerExecute", task) defer eSpan.End() task.Ctx = eCtx s.executeTask(task) - wg.Done() } func (s *scheduler) traceSpan(ctx sdk.Context, name string, task *deliverTxTask) (sdk.Context, trace.Span) { diff --git a/x/evm/keeper/keeper.go b/x/evm/keeper/keeper.go index 5cccbd91b0..52d723a359 100644 --- a/x/evm/keeper/keeper.go +++ b/x/evm/keeper/keeper.go @@ -81,6 +81,12 @@ type Keeper struct { EthBlockTestConfig blocktest.Config BlockTest *tests.BlockTest + // GigaExecutorEnabled controls whether to use the Giga executor (evmone-based) + // instead of geth's interpreter for EVM execution. Experimental feature. + GigaExecutorEnabled bool + // GigaOCCEnabled controls whether to use OCC with the Giga executor + GigaOCCEnabled bool + // used for both ETH replay and block tests. Not used in chain critical path. Trie ethstate.Trie DB ethstate.Database diff --git a/x/evm/keeper/msg_server.go b/x/evm/keeper/msg_server.go index 4dcd6695f2..4d27fbcf3c 100644 --- a/x/evm/keeper/msg_server.go +++ b/x/evm/keeper/msg_server.go @@ -245,6 +245,7 @@ func (k Keeper) applyEVMMessage(ctx sdk.Context, msg *core.Message, stateDB *sta sstore := k.GetParams(ctx).SeiSstoreSetGasEip2200 cfg := types.DefaultChainConfig().EthereumConfigWithSstore(k.ChainID(ctx), &sstore) txCtx := core.NewEVMTxContext(msg) + evmInstance := vm.NewEVM(*blockCtx, stateDB, cfg, vm.Config{}, k.CustomPrecompiles(ctx)) evmInstance.SetTxContext(txCtx) st := core.NewStateTransition(evmInstance, msg, &gp, true, shouldIncrementNonce) // fee already charged in ante handler