mirror of
				https://github.com/0glabs/0g-chain.git
				synced 2025-11-04 02:07:52 +00:00 
			
		
		
		
	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:
		
							parent
							
								
									11b62b3305
								
							
						
					
					
						commit
						2fcc7916c6
					
				@ -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
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user