Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
200 changes: 158 additions & 42 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@
gigaexecutor "github.com/sei-protocol/sei-chain/giga/executor"
gigaconfig "github.com/sei-protocol/sei-chain/giga/executor/config"
gigalib "github.com/sei-protocol/sei-chain/giga/executor/lib"
gigaprecompiles "github.com/sei-protocol/sei-chain/giga/executor/precompiles"
gigautils "github.com/sei-protocol/sei-chain/giga/executor/utils"
"github.com/sei-protocol/sei-chain/precompiles"
putils "github.com/sei-protocol/sei-chain/precompiles/utils"
"github.com/sei-protocol/sei-chain/sei-db/state_db/ss"
Expand Down Expand Up @@ -1314,7 +1316,7 @@
}
}

func (app *App) ProcessBlockSynchronous(ctx sdk.Context, txs [][]byte, typedTxs []sdk.Tx, absoluteTxIndices []int) []*abci.ExecTxResult {
func (app *App) ProcessTxsSynchronousV2(ctx sdk.Context, txs [][]byte, typedTxs []sdk.Tx, absoluteTxIndices []int) []*abci.ExecTxResult {
defer metrics.BlockProcessLatency(time.Now(), metrics.SYNCHRONOUS)

txResults := []*abci.ExecTxResult{}
Expand All @@ -1327,6 +1329,44 @@
return txResults
}

func (app *App) ProcessTxsSynchronousGiga(ctx sdk.Context, txs [][]byte, typedTxs []sdk.Tx, absoluteTxIndices []int) []*abci.ExecTxResult {
defer metrics.BlockProcessLatency(time.Now(), metrics.SYNCHRONOUS)

Check warning

Code scanning / CodeQL

Calling the system time Warning

Calling the system time may be a possible source of non-determinism

txResults := make([]*abci.ExecTxResult, len(txs))
for i, tx := range txs {
fmt.Println("GIGA WORKING 17:34 tx", absoluteTxIndices[i])
ctx = ctx.WithTxIndex(absoluteTxIndices[i])
evmMsg := app.GetEVMMsg(typedTxs[i])
// If not an EVM tx, fall back to v2 processing
if evmMsg == nil {
result := app.DeliverTxWithResult(ctx, tx, typedTxs[i])
txResults[i] = result
continue
}

// Execute EVM transaction through giga executor
result, execErr := app.executeEVMTxWithGigaExecutor(ctx, i, evmMsg)
if execErr != nil {
// Check if this is a fail-fast error (Cosmos precompile interop detected)
if gigautils.ShouldExecutionAbort(execErr) {
res := app.DeliverTxWithResult(ctx, tx, typedTxs[i])
txResults[i] = res
continue
}
txResults[i] = &abci.ExecTxResult{
Code: 1,
Log: fmt.Sprintf("[BUG] giga executor error: %v", execErr),
}
continue
}

txResults[i] = result
metrics.IncrTxProcessTypeCounter(metrics.SYNCHRONOUS)
}

return txResults
}

type ChannelResult struct {
txIndex int
result *abci.ExecTxResult
Expand Down Expand Up @@ -1369,12 +1409,16 @@

// ExecuteTxsConcurrently calls the appropriate function for processing transacitons
func (app *App) ExecuteTxsConcurrently(ctx sdk.Context, txs [][]byte, typedTxs []sdk.Tx, absoluteTxIndices []int) ([]*abci.ExecTxResult, sdk.Context) {
// TODO after OCC release, remove this check and call ProcessTXsWithOCC directly
if ctx.IsOCCEnabled() {
return app.ProcessTXsWithOCC(ctx, txs, typedTxs, absoluteTxIndices)
// Giga only supports synchronous execution for now
if app.EvmKeeper.GigaExecutorEnabled {
results := app.ProcessTxsSynchronousGiga(ctx, txs, typedTxs, absoluteTxIndices)
return results, ctx
} else if !ctx.IsOCCEnabled() {
results := app.ProcessTxsSynchronousV2(ctx, txs, typedTxs, absoluteTxIndices)
return results, ctx
}
results := app.ProcessBlockSynchronous(ctx, txs, typedTxs, absoluteTxIndices)
return results, ctx

return app.ProcessTXsWithOCC(ctx, txs, typedTxs, absoluteTxIndices)
}

func (app *App) GetDeliverTxEntry(ctx sdk.Context, txIndex int, absoluateIndex int, bz []byte, tx sdk.Tx) (res *sdk.DeliverTxEntry) {
Expand Down Expand Up @@ -1468,12 +1512,9 @@
}
}()

// 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)
// TODO: for now Giga OCC calls ProcessBlockWithGigaExecutorOCC, WIP
if app.EvmKeeper.GigaExecutorEnabled && app.EvmKeeper.GigaOCCEnabled {
return app.ProcessBlockWithGigaExecutorOCC(ctx, txs, req, lastCommit, simulate)
}

ctx = ctx.WithIsOCCEnabled(app.OccEnabled())
Expand Down Expand Up @@ -1536,6 +1577,7 @@
// 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.
// NOTE: This is not currently used in the codebase, but might be in the future.
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() {
Expand Down Expand Up @@ -1586,12 +1628,9 @@
// 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",
}
res := app.DeliverTxWithResult(ctx, txBytes, decodedTx)
// Non-EVM transaction - fall back to standard processing
txResults[i] = res
continue
}

Expand All @@ -1600,9 +1639,15 @@
// Execute EVM transaction through giga executor
result, execErr := app.executeEVMTxWithGigaExecutor(ctx, i, evmMsg)
if execErr != nil {
// Check if this is a fail-fast error (Cosmos precompile interop detected)
if gigautils.ShouldExecutionAbort(execErr) {
res := app.DeliverTxWithResult(ctx, txBytes, decodedTx)
txResults[i] = res
continue
}
txResults[i] = &abci.ExecTxResult{
Code: 1,
Log: fmt.Sprintf("giga executor error: %v", execErr),
Log: fmt.Sprintf("[BUG] giga executor error: %v", execErr),
}
continue
}
Expand Down Expand Up @@ -1647,7 +1692,14 @@
}, nil
}

// Prepare context for EVM transaction (set infinite gas meter like original flow)
// Use a fresh EventManager to collect ALL events during this transaction
ctx = ctx.WithGasMeter(sdk.NewInfiniteGasMeterWithMultiplier(ctx))
ctx = ctx.WithTxIndex(txIndex)
ctx = ctx.WithEventManager(sdk.NewEventManager())

// Associate the address if not already associated (same as EVMPreprocessDecorator)
// This emits address_associated event
if _, isAssociated := app.EvmKeeper.GetEVMAddress(ctx, seiAddr); !isAssociated {
associateHelper := helpers.NewAssociationHelper(&app.EvmKeeper, app.BankKeeper, &app.AccountKeeper)
if err := associateHelper.AssociateAddresses(ctx, seiAddr, sender, pubkey); err != nil {
Expand All @@ -1658,11 +1710,7 @@
}
}

// 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
// Create state DB for this transaction (uses ctx with our EventManager)
stateDB := evmstate.NewDBImpl(ctx, &app.EvmKeeper, false)
defer stateDB.Cleanup()

Expand All @@ -1682,18 +1730,42 @@
sstore := app.EvmKeeper.GetParams(ctx).SeiSstoreSetGasEip2200
cfg := evmtypes.DefaultChainConfig().EthereumConfigWithSstore(app.EvmKeeper.ChainID(ctx), &sstore)

// Create Giga executor VM (wraps evmone)
gigaExecutor := gigaexecutor.NewEvmoneExecutor(app.EvmKeeper.EvmoneVM, *blockCtx, stateDB, cfg, vm.Config{}, app.EvmKeeper.CustomPrecompiles(ctx))
// Pre-charge gas fee (like V2 ante handler does)
// This emits gas-related events BEFORE message execution, matching V2's event order
evmMsg := app.EvmKeeper.GetEVMMessage(ctx, ethTx, sender)
preChargeEVM := vm.NewEVM(*blockCtx, stateDB, cfg, vm.Config{}, app.EvmKeeper.CustomPrecompiles(ctx))
preChargeEVM.SetTxContext(core.NewEVMTxContext(evmMsg))
preChargeST := core.NewStateTransition(preChargeEVM, evmMsg, &gp, false, false) // feeCharged=false to call BuyGas
if buyGasErr := preChargeST.BuyGas(); buyGasErr != nil {
return &abci.ExecTxResult{
Code: 1,
Log: fmt.Sprintf("failed to buy gas: %v", buyGasErr),
}, nil
}

// Record the count of "ante" events (association + gas charging)
// In V2, these events appear BEFORE message:action
anteEventCount := len(ctx.EventManager().Events())

// Create Giga executor VM
gigaExecutor := gigaexecutor.NewGethExecutor(*blockCtx, stateDB, cfg, vm.Config{}, gigaprecompiles.AllCustomPrecompilesFailFast)

// Execute the transaction through giga VM
execResult, execErr := gigaExecutor.ExecuteTransaction(ethTx, sender, app.EvmKeeper.GetBaseFee(ctx), &gp)
// Execute the transaction with feeCharged=true since we already charged gas above
// This prevents double-charging and ensures gas events are in "ante" position
execResult, execErr := gigaExecutor.ExecuteTransactionWithOptions(ethTx, sender, app.EvmKeeper.GetBaseFee(ctx), &gp, true, true)
if execErr != nil {
return &abci.ExecTxResult{
Code: 1,
Log: fmt.Sprintf("giga executor apply message error: %v", execErr),
}, nil
}

// Check if the execution hit a fail-fast precompile (Cosmos interop detected)
// Return the error to the caller so it can handle accordingly (e.g., fallback to standard execution)
if execResult.Err != nil && gigautils.ShouldExecutionAbort(execResult.Err) {
return nil, execResult.Err
}

// Finalize state changes
_, ferr := stateDB.Finalize()
if ferr != nil {
Expand All @@ -1709,19 +1781,7 @@
vmError = execResult.Err.Error()
}

// Create core.Message from ethTx for WriteReceipt
// WriteReceipt needs msg for GasPrice, To, From, Data, Nonce fields
evmMsg := &core.Message{
Nonce: ethTx.Nonce(),
GasLimit: ethTx.Gas(),
GasPrice: ethTx.GasPrice(),
GasFeeCap: ethTx.GasFeeCap(),
GasTipCap: ethTx.GasTipCap(),
To: ethTx.To(),
Value: ethTx.Value(),
Data: ethTx.Data(),
From: sender,
}
// WriteReceipt uses the evmMsg created earlier for gas pre-charge
receipt, rerr := app.EvmKeeper.WriteReceipt(ctx, stateDB, evmMsg, uint32(ethTx.Type()), ethTx.Hash(), execResult.UsedGas, vmError)
if rerr != nil {
return &abci.ExecTxResult{
Expand Down Expand Up @@ -1749,12 +1809,62 @@
// isn't committed, so we pass the receipt through the response for later processing.
receiptBytes, _ := receipt.Marshal()

// Collect all events from the EventManager
// In V2, events are structured as: anteEvents + message:action + msgResult.Events
// We insert message:action at the correct position (after ante events)
eventMsgName := sdk.MsgTypeURL(msg)
allEventsSlice := ctx.EventManager().Events()

// Split events: ante events (before execution) and exec events (during execution)
// IMPORTANT: Make copies to avoid slice aliasing issues with append
anteEvents := make(sdk.Events, anteEventCount)
copy(anteEvents, allEventsSlice[:anteEventCount])
execEvents := make(sdk.Events, len(allEventsSlice)-anteEventCount)
copy(execEvents, allEventsSlice[anteEventCount:])

// Check if anteEvents or execEvents already contains a message:action event
// (This can happen if the execution path emits one internally)
hasMessageAction := false
for _, e := range allEventsSlice {
if e.Type == sdk.EventTypeMessage {
for _, attr := range e.Attributes {
if string(attr.Key) == sdk.AttributeKeyAction {
hasMessageAction = true
break
}
}
}
if hasMessageAction {
break
}
}

// Build final events: anteEvents + message:action + execEvents (matches V2 exactly)
// Only add message:action if not already present
var allEvents sdk.Events
if hasMessageAction {
// message:action already exists, just combine without adding another
allEvents = anteEvents.AppendEvents(execEvents)
} else {
// Insert message:action between ante and exec events
msgActionEvent := sdk.Events{
sdk.NewEvent(sdk.EventTypeMessage, sdk.NewAttribute(sdk.AttributeKeyAction, eventMsgName)),
}
allEvents = anteEvents.AppendEvents(msgActionEvent).AppendEvents(execEvents)
}

// Create message log (same format as RunMsgs)
msgLogs := sdk.ABCIMessageLogs{
sdk.NewABCIMessageLog(uint32(0), vmError, allEvents),
}

//nolint:gosec // G115: safe, UsedGas won't exceed int64 max
return &abci.ExecTxResult{
Code: code,
Data: receiptBytes,
GasUsed: int64(execResult.UsedGas),
Log: vmError,
Log: strings.TrimSpace(msgLogs.String()),
Events: sdk.MarkEventsToIndex(allEvents.ToABCIEvents(), app.IndexEvents),
EvmTxInfo: &abci.EvmTxInfo{
TxHash: ethTx.Hash().Hex(),
VmError: vmError,
Expand Down Expand Up @@ -1911,6 +2021,12 @@

result, err := app.executeEVMTxWithGigaExecutor(ctx, ctx.TxIndex(), evmMsg)
if err != nil {
// Check if this is a fail-fast error (Cosmos precompile interop detected)
if gigautils.ShouldExecutionAbort(err) {
// Transaction requires Cosmos interop - not supported in giga mode
// TODO: implement fallback to standard execution path
return abci.ResponseDeliverTx{Code: 1, Log: fmt.Sprintf("giga executor: cosmos interop not supported: %v", err)}
}
return abci.ResponseDeliverTx{Code: 1, Log: fmt.Sprintf("giga executor error: %v", err)}
}

Expand Down
10 changes: 9 additions & 1 deletion giga/executor/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,20 @@ func NewGethExecutor(blockCtx vm.BlockContext, stateDB vm.StateDB, chainConfig *
}

func (e *Executor) ExecuteTransaction(tx *types.Transaction, sender common.Address, baseFee *big.Int, gasPool *core.GasPool) (*core.ExecutionResult, error) {
return e.ExecuteTransactionWithOptions(tx, sender, baseFee, gasPool, false, true)
}

// ExecuteTransactionWithOptions executes a transaction with explicit control over fee and nonce handling.
// - feeCharged: if true, assumes gas fee was already charged (skips BuyGas). Use true to match V2 behavior.
// - shouldIncrementNonce: if true, increments the sender's nonce during execution.
func (e *Executor) ExecuteTransactionWithOptions(tx *types.Transaction, sender common.Address, baseFee *big.Int, gasPool *core.GasPool, feeCharged bool, shouldIncrementNonce bool) (*core.ExecutionResult, error) {
message, err := core.TransactionToMessage(tx, &internal.Signer{From: sender}, baseFee)
if err != nil {
return nil, err
}

executionResult, err := core.ApplyMessage(e.evm, message, gasPool)
e.evm.SetTxContext(core.NewEVMTxContext(message))
executionResult, err := core.NewStateTransition(e.evm, message, gasPool, feeCharged, shouldIncrementNonce).Execute()
if err != nil {
return nil, err
}
Expand Down
20 changes: 18 additions & 2 deletions giga/executor/precompiles/failfast.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package precompiles

import (
"errors"
"math/big"

"github.com/ethereum/go-ethereum/common"
Expand Down Expand Up @@ -38,7 +37,24 @@ var FailFastPrecompileAddresses = []common.Address{
common.HexToAddress(p256.P256VerifyAddress),
}

var ErrInvalidPrecompileCall = errors.New("invalid precompile call")
// InvalidPrecompileCallError is an error type that implements vm.AbortError,
// signaling that execution should abort and this error should propagate
// through the entire call stack.
type InvalidPrecompileCallError struct{}

func (e *InvalidPrecompileCallError) Error() string {
return "invalid precompile call"
}

// IsAbortError implements vm.AbortError interface, signaling that this error
// should propagate through the EVM call stack instead of being swallowed.
func (e *InvalidPrecompileCallError) IsAbortError() bool {
return true
}

// ErrInvalidPrecompileCall is the singleton error instance for invalid precompile calls.
// It implements vm.AbortError to ensure it propagates through the call stack.
var ErrInvalidPrecompileCall error = &InvalidPrecompileCallError{}

type FailFastPrecompile struct{}

Expand Down
14 changes: 10 additions & 4 deletions giga/executor/utils/errors.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package utils

import (
"errors"

"github.com/sei-protocol/sei-chain/giga/executor/precompiles"
"github.com/ethereum/go-ethereum/core/vm"
)

// ShouldExecutionAbort checks if the given error is an AbortError that should
// cause Giga execution to abort and fall back to standard execution.
func ShouldExecutionAbort(err error) bool {
return errors.Is(err, precompiles.ErrInvalidPrecompileCall)
if err == nil {
return false
}
if abortErr, ok := err.(vm.AbortError); ok {
return abortErr.IsAbortError()
}
return false
}
Loading
Loading