fix(evmutil): implement gas validation for EVM calls - Add MaxGasLimit constant (30M gas) - Replace infinite gas meter with limited gas meter - Add validation for gas estimation and usage - Add unit tests for gas validation - Add new error type for gas limit exceeded

This commit is contained in:
0xalkindivv.eth 2025-03-04 16:57:19 +07:00
parent 11b62b3305
commit 2fcc7916c6
3 changed files with 80 additions and 3 deletions

View File

@ -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

View File

@ -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)
}
})
}
}

View File

@ -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")
)