diff --git a/x/evmutil/keeper/evm.go b/x/evmutil/keeper/evm.go index 32d948ff..d829f1fc 100644 --- a/x/evmutil/keeper/evm.go +++ b/x/evmutil/keeper/evm.go @@ -25,12 +25,16 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - "github.com/evmos/ethermint/server/config" evmtypes "github.com/evmos/ethermint/x/evm/types" "github.com/0glabs/0g-chain/x/evmutil/types" ) +const ( + // MaxGasLimit defines the maximum gas that can be used in EVM execution + MaxGasLimit = uint64(30_000_000) +) + // CallEVM performs a smart contract method call using given args func (k Keeper) CallEVM( ctx sdk.Context, @@ -89,7 +93,15 @@ func (k Keeper) CallEVMWithData( return nil, err } - ethGasContext := ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + // Use a limited gas meter instead of infinite + maxGas := ctx.GasMeter().Limit() + if maxGas == 0 { + maxGas = MaxGasLimit + } + if maxGas > MaxGasLimit { + return nil, errorsmod.Wrapf(types.ErrGasLimitExceeded, "requested gas %d exceeds limit %d", maxGas, MaxGasLimit) + } + ethGasContext := ctx.WithGasMeter(sdk.NewGasMeter(maxGas)) // EstimateGas applies the transaction against current block state to get // optimal gas value. Since this is done right before the ApplyMessage @@ -100,12 +112,17 @@ func (k Keeper) CallEVMWithData( // apply, tx order is the same, etc.) gasRes, err := k.evmKeeper.EstimateGas(sdk.WrapSDKContext(ethGasContext), &evmtypes.EthCallRequest{ Args: args, - GasCap: config.DefaultGasCap, + GasCap: maxGas, }) if err != nil { return nil, errorsmod.Wrap(evmtypes.ErrVMExecution, err.Error()) } + // Validate estimated gas is within limits + if gasRes.Gas > maxGas { + return nil, errorsmod.Wrapf(types.ErrGasLimitExceeded, "estimated gas %d exceeds limit %d", gasRes.Gas, maxGas) + } + msg := ethtypes.NewMessage( from, to, @@ -129,6 +146,11 @@ func (k Keeper) CallEVMWithData( return nil, errorsmod.Wrap(evmtypes.ErrVMExecution, res.VmError) } + // Validate gas used is within limits + if res.GasUsed > maxGas { + return nil, errorsmod.Wrapf(types.ErrGasLimitExceeded, "gas used %d exceeds limit %d", res.GasUsed, maxGas) + } + ctx.GasMeter().ConsumeGas(res.GasUsed, "evm gas consumed") return res, nil diff --git a/x/evmutil/keeper/evm_test.go b/x/evmutil/keeper/evm_test.go index 343d8a6c..631eed78 100644 --- a/x/evmutil/keeper/evm_test.go +++ b/x/evmutil/keeper/evm_test.go @@ -17,6 +17,7 @@ import ( "github.com/evmos/ethermint/x/evm/statedb" "github.com/evmos/ethermint/x/evm/types" + "github.com/0glabs/0g-chain/x/evmutil/keeper" "github.com/0glabs/0g-chain/x/evmutil/testutil" ) @@ -135,3 +136,56 @@ func (suite *evmKeeperTestSuite) TestEvmKeeper_SetAccount() { func TestEvmKeeperTestSuite(t *testing.T) { suite.Run(t, new(evmKeeperTestSuite)) } + +func (suite *evmKeeperTestSuite) TestCallEVMWithData_GasLimits() { + // Setup test contract + addr := testutil.GenerateAddress() + contract := types.NewInternalEVMAddress(addr) + from := common.BytesToAddress(suite.Key1.PubKey().Address()) + + testCases := []struct { + name string + gasLimit uint64 + expPass bool + }{ + { + "success - gas within limits", + keeper.MaxGasLimit - 1, + true, + }, + { + "success - gas at max limit", + keeper.MaxGasLimit, + true, + }, + { + "fail - gas exceeds max limit", + keeper.MaxGasLimit + 1, + false, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + ctx := suite.Ctx.WithGasMeter(sdk.NewGasMeter(tc.gasLimit)) + + // Create dummy contract call data + data := crypto.Keccak256([]byte("test")) + + // Execute contract call + _, err := suite.App.EvmutilKeeper.CallEVMWithData( + ctx, + from, + &contract, + data, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + suite.Require().ErrorIs(err, types.ErrGasLimitExceeded) + } + }) + } +} diff --git a/x/evmutil/types/errors.go b/x/evmutil/types/errors.go index dc35468f..fa771310 100644 --- a/x/evmutil/types/errors.go +++ b/x/evmutil/types/errors.go @@ -12,4 +12,5 @@ var ( ErrInvalidCosmosDenom = errorsmod.Register(ModuleName, 7, "invalid cosmos denom") ErrSDKConversionNotEnabled = errorsmod.Register(ModuleName, 8, "sdk.Coin not enabled to convert to ERC20 token") ErrInsufficientConversionAmount = errorsmod.Register(ModuleName, 9, "insufficient conversion amount") + ErrGasLimitExceeded = errorsmod.Register(ModuleName, 10, "gas limit exceeded maximum allowed") )