mirror of
https://github.com/0glabs/0g-chain.git
synced 2025-04-04 15:55:23 +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"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/evmos/ethermint/server/config"
|
|
||||||
evmtypes "github.com/evmos/ethermint/x/evm/types"
|
evmtypes "github.com/evmos/ethermint/x/evm/types"
|
||||||
|
|
||||||
"github.com/0glabs/0g-chain/x/evmutil/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
|
// CallEVM performs a smart contract method call using given args
|
||||||
func (k Keeper) CallEVM(
|
func (k Keeper) CallEVM(
|
||||||
ctx sdk.Context,
|
ctx sdk.Context,
|
||||||
@ -89,7 +93,15 @@ func (k Keeper) CallEVMWithData(
|
|||||||
return nil, err
|
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
|
// EstimateGas applies the transaction against current block state to get
|
||||||
// optimal gas value. Since this is done right before the ApplyMessage
|
// 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.)
|
// apply, tx order is the same, etc.)
|
||||||
gasRes, err := k.evmKeeper.EstimateGas(sdk.WrapSDKContext(ethGasContext), &evmtypes.EthCallRequest{
|
gasRes, err := k.evmKeeper.EstimateGas(sdk.WrapSDKContext(ethGasContext), &evmtypes.EthCallRequest{
|
||||||
Args: args,
|
Args: args,
|
||||||
GasCap: config.DefaultGasCap,
|
GasCap: maxGas,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errorsmod.Wrap(evmtypes.ErrVMExecution, err.Error())
|
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(
|
msg := ethtypes.NewMessage(
|
||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
@ -129,6 +146,11 @@ func (k Keeper) CallEVMWithData(
|
|||||||
return nil, errorsmod.Wrap(evmtypes.ErrVMExecution, res.VmError)
|
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")
|
ctx.GasMeter().ConsumeGas(res.GasUsed, "evm gas consumed")
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
|
@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/evmos/ethermint/x/evm/statedb"
|
"github.com/evmos/ethermint/x/evm/statedb"
|
||||||
"github.com/evmos/ethermint/x/evm/types"
|
"github.com/evmos/ethermint/x/evm/types"
|
||||||
|
|
||||||
|
"github.com/0glabs/0g-chain/x/evmutil/keeper"
|
||||||
"github.com/0glabs/0g-chain/x/evmutil/testutil"
|
"github.com/0glabs/0g-chain/x/evmutil/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -135,3 +136,56 @@ func (suite *evmKeeperTestSuite) TestEvmKeeper_SetAccount() {
|
|||||||
func TestEvmKeeperTestSuite(t *testing.T) {
|
func TestEvmKeeperTestSuite(t *testing.T) {
|
||||||
suite.Run(t, new(evmKeeperTestSuite))
|
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")
|
ErrInvalidCosmosDenom = errorsmod.Register(ModuleName, 7, "invalid cosmos denom")
|
||||||
ErrSDKConversionNotEnabled = errorsmod.Register(ModuleName, 8, "sdk.Coin not enabled to convert to ERC20 token")
|
ErrSDKConversionNotEnabled = errorsmod.Register(ModuleName, 8, "sdk.Coin not enabled to convert to ERC20 token")
|
||||||
ErrInsufficientConversionAmount = errorsmod.Register(ModuleName, 9, "insufficient conversion amount")
|
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