From 0b071aff743becb561fa288b3d31dfd175f06716 Mon Sep 17 00:00:00 2001 From: Thomas Nguy Date: Tue, 29 Jul 2025 09:38:58 +0900 Subject: [PATCH 1/9] support query gas limit flag --- evmd/app.go | 1 + x/vm/keeper/grpc_query.go | 36 ++++++++++++++++++++++++++++-------- x/vm/keeper/keeper.go | 8 ++++++++ x/vm/keeper/keeper_test.go | 1 + x/vm/types/tx_args.go | 4 ++-- 5 files changed, 40 insertions(+), 10 deletions(-) diff --git a/evmd/app.go b/evmd/app.go index 4f6ecb0c2..a8409918c 100644 --- a/evmd/app.go +++ b/evmd/app.go @@ -481,6 +481,7 @@ func NewExampleApp( app.FeeMarketKeeper, &app.Erc20Keeper, tracer, + cast.ToUint64(appOpts.Get(server.FlagQueryGasLimit)), ) app.Erc20Keeper = erc20keeper.NewKeeper( diff --git a/x/vm/keeper/grpc_query.go b/x/vm/keeper/grpc_query.go index a3149998b..4df983175 100644 --- a/x/vm/keeper/grpc_query.go +++ b/x/vm/keeper/grpc_query.go @@ -240,7 +240,17 @@ func (k Keeper) EthCall(c context.Context, req *types.EthCallRequest) (*types.Ms nonce := k.GetNonce(ctx, args.GetFrom()) args.Nonce = (*hexutil.Uint64)(&nonce) - msg, err := args.ToMessage(req.GasCap, cfg.BaseFee, false, false) + // Enforce the gas limit cap + gasCap := req.GasCap + if k.queryGasLimit != GasNoLimit { + if gasCap == 0 { + gasCap = k.queryGasLimit + } else if k.queryGasLimit < gasCap { + gasCap = k.queryGasLimit + } + } + + msg, err := args.ToMessage(gasCap, cfg.BaseFee, false, false) if err != nil { return nil, status.Error(codes.InvalidArgument, err.Error()) } @@ -283,11 +293,20 @@ func (k Keeper) EstimateGasInternal(c context.Context, req *types.EthCallRequest return nil, status.Error(codes.InvalidArgument, err.Error()) } + // Enforce the gas limit cap + gasCap := req.GasCap + if k.queryGasLimit != GasNoLimit { + if gasCap == 0 { + gasCap = k.queryGasLimit + } else if k.queryGasLimit < gasCap { + gasCap = k.queryGasLimit + } + } + // Binary search the gas requirement, as it may be higher than the amount used var ( - lo = ethparams.TxGas - 1 - hi uint64 - gasCap uint64 + lo = ethparams.TxGas - 1 + hi uint64 ) // Determine the highest gas limit can be used during the estimation. @@ -304,11 +323,12 @@ func (k Keeper) EstimateGasInternal(c context.Context, req *types.EthCallRequest } // Recap the highest gas allowance with specified gascap. - if req.GasCap != 0 && hi > req.GasCap { - hi = req.GasCap + if gasCap != 0 && hi > gasCap { + hi = gasCap + } else { + gasCap = hi } - gasCap = hi cfg, err := k.EVMConfig(ctx, GetProposerAddress(ctx, req.ProposerAddress)) if err != nil { return nil, status.Error(codes.Internal, "failed to load evm config") @@ -321,7 +341,7 @@ func (k Keeper) EstimateGasInternal(c context.Context, req *types.EthCallRequest txConfig := statedb.NewEmptyTxConfig(common.BytesToHash(ctx.HeaderHash())) // convert the tx args to an ethereum message - msg, err := args.ToMessage(req.GasCap, cfg.BaseFee, true, true) + msg, err := args.ToMessage(gasCap, cfg.BaseFee, true, true) if err != nil { return nil, status.Error(codes.Internal, err.Error()) } diff --git a/x/vm/keeper/keeper.go b/x/vm/keeper/keeper.go index 69c781ee9..8082e4caf 100644 --- a/x/vm/keeper/keeper.go +++ b/x/vm/keeper/keeper.go @@ -26,6 +26,9 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) +// GasNoLimit is the value for keeper.queryGasLimit in case there is no limit +const GasNoLimit = 0 + // Keeper grants access to the EVM module state and implements the go-ethereum StateDB interface. type Keeper struct { // Protobuf codec @@ -70,6 +73,9 @@ type Keeper struct { // Some of these precompiled contracts might not be active depending on the EVM // parameters. precompiles map[common.Address]vm.PrecompiledContract + + // queryGasLimit max amount of gas allowed on queries, 0 means no limit + queryGasLimit uint64 } // NewKeeper generates new evm module keeper @@ -84,6 +90,7 @@ func NewKeeper( fmk types.FeeMarketKeeper, erc20Keeper types.Erc20Keeper, tracer string, + queryGasLimit uint64, ) *Keeper { // ensure evm module account is set if addr := ak.GetModuleAddress(types.ModuleName); addr == nil { @@ -111,6 +118,7 @@ func NewKeeper( tracer: tracer, erc20Keeper: erc20Keeper, storeKeys: keys, + queryGasLimit: queryGasLimit, } } diff --git a/x/vm/keeper/keeper_test.go b/x/vm/keeper/keeper_test.go index 40bd87c7a..62b263523 100644 --- a/x/vm/keeper/keeper_test.go +++ b/x/vm/keeper/keeper_test.go @@ -94,6 +94,7 @@ func (suite *KeeperTestSuite) SetupTest() { suite.fmKeeper, suite.erc20Keeper, "", + vmkeeper.GasNoLimit, ) } diff --git a/x/vm/types/tx_args.go b/x/vm/types/tx_args.go index c4acdc11d..8961d5ba8 100644 --- a/x/vm/types/tx_args.go +++ b/x/vm/types/tx_args.go @@ -3,7 +3,6 @@ package types import ( "errors" "fmt" - "math" "math/big" "github.com/ethereum/go-ethereum/common" @@ -183,7 +182,8 @@ func (args *TransactionArgs) ToMessage(globalGasCap uint64, baseFee *big.Int, sk // Set default gas & gas price if none were set gas := globalGasCap if gas == 0 { - gas = uint64(math.MaxUint64 / 2) + // Ethereum block size is ~36000000, we use this value as default in case neither gas or global cap is specified, to protect against DOS + gas = uint64(100000000) } if args.Gas != nil { gas = uint64(*args.Gas) From 7974e849dc8d20691af47b59cf59d59a794f97c8 Mon Sep 17 00:00:00 2001 From: Thomas Nguy Date: Tue, 29 Jul 2025 09:46:06 +0900 Subject: [PATCH 2/9] add change log --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e01c14ebc..85f203f89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - Fixed example chain's cmd by adding NoOpEVMOptions to tmpApp in root.go - Added RPC support for `--legacy` transactions (Non EIP-1559) - [\#296](https://github.com/cosmos/evm/pull/296) Sanity checks for TraceTx +- [\#368](https://github.com/cosmos/evm/pull/368) Support query gas limit flag ### IMPROVEMENTS From 55afc854e70232af5b40fdcd17edd991b2c1a5e1 Mon Sep 17 00:00:00 2001 From: Thomas Nguy Date: Wed, 30 Jul 2025 14:39:35 +0900 Subject: [PATCH 3/9] refactor --- x/vm/keeper/grpc_query.go | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/x/vm/keeper/grpc_query.go b/x/vm/keeper/grpc_query.go index 4df983175..2577ad94f 100644 --- a/x/vm/keeper/grpc_query.go +++ b/x/vm/keeper/grpc_query.go @@ -241,15 +241,7 @@ func (k Keeper) EthCall(c context.Context, req *types.EthCallRequest) (*types.Ms args.Nonce = (*hexutil.Uint64)(&nonce) // Enforce the gas limit cap - gasCap := req.GasCap - if k.queryGasLimit != GasNoLimit { - if gasCap == 0 { - gasCap = k.queryGasLimit - } else if k.queryGasLimit < gasCap { - gasCap = k.queryGasLimit - } - } - + gasCap := k.GlobalQueryGasLimit(req) msg, err := args.ToMessage(gasCap, cfg.BaseFee, false, false) if err != nil { return nil, status.Error(codes.InvalidArgument, err.Error()) @@ -293,16 +285,6 @@ func (k Keeper) EstimateGasInternal(c context.Context, req *types.EthCallRequest return nil, status.Error(codes.InvalidArgument, err.Error()) } - // Enforce the gas limit cap - gasCap := req.GasCap - if k.queryGasLimit != GasNoLimit { - if gasCap == 0 { - gasCap = k.queryGasLimit - } else if k.queryGasLimit < gasCap { - gasCap = k.queryGasLimit - } - } - // Binary search the gas requirement, as it may be higher than the amount used var ( lo = ethparams.TxGas - 1 @@ -323,6 +305,7 @@ func (k Keeper) EstimateGasInternal(c context.Context, req *types.EthCallRequest } // Recap the highest gas allowance with specified gascap. + gasCap := k.GlobalQueryGasLimit(req) if gasCap != 0 && hi > gasCap { hi = gasCap } else { @@ -735,3 +718,15 @@ func (k Keeper) Config(_ context.Context, _ *types.QueryConfigRequest) (*types.Q return &types.QueryConfigResponse{Config: config}, nil } + +func (k Keeper) GlobalQueryGasLimit(req *types.EthCallRequest) uint64 { + gasCap := req.GasCap + if k.queryGasLimit != GasNoLimit { + if gasCap == 0 { + gasCap = k.queryGasLimit + } else if k.queryGasLimit < gasCap { + gasCap = k.queryGasLimit + } + } + return gasCap +} From 66ce0b587d3adf85dcaa043a212b78d6c7ea0606 Mon Sep 17 00:00:00 2001 From: "Thomas N." <81727899+thomas-nguy@users.noreply.github.com> Date: Wed, 30 Jul 2025 14:47:20 +0900 Subject: [PATCH 4/9] Update CHANGELOG.md --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 466127d96..1f36f2f2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,6 @@ - Fixed example chain's cmd by adding NoOpEVMOptions to tmpApp in root.go - Added RPC support for `--legacy` transactions (Non EIP-1559) -- [\#296](https://github.com/cosmos/evm/pull/296) Sanity checks for TraceTx - [\#368](https://github.com/cosmos/evm/pull/368) Support query gas limit flag ### IMPROVEMENTS From ffad7ca43f554334c8dae82559f3c6c8d4e5ba6f Mon Sep 17 00:00:00 2001 From: "Thomas N." <81727899+thomas-nguy@users.noreply.github.com> Date: Wed, 30 Jul 2025 14:48:02 +0900 Subject: [PATCH 5/9] Update grpc_query.go --- x/vm/keeper/grpc_query.go | 1 - 1 file changed, 1 deletion(-) diff --git a/x/vm/keeper/grpc_query.go b/x/vm/keeper/grpc_query.go index 4dfa23895..e96327208 100644 --- a/x/vm/keeper/grpc_query.go +++ b/x/vm/keeper/grpc_query.go @@ -772,4 +772,3 @@ func buildTraceCtx(ctx sdk.Context, gasLimit uint64) sdk.Context { return evmante.BuildEvmExecutionCtx(ctx). WithGasMeter(cosmosevmtypes.NewInfiniteGasMeterWithLimit(gasLimit)) } - From 8da2614101c442300263bca6cfc26d47d99e9334 Mon Sep 17 00:00:00 2001 From: "Thomas N." <81727899+thomas-nguy@users.noreply.github.com> Date: Wed, 30 Jul 2025 14:49:37 +0900 Subject: [PATCH 6/9] Update keeper_test.go --- x/vm/keeper/keeper_test.go | 43 -------------------------------------- 1 file changed, 43 deletions(-) diff --git a/x/vm/keeper/keeper_test.go b/x/vm/keeper/keeper_test.go index b763806ab..1f3f924e7 100644 --- a/x/vm/keeper/keeper_test.go +++ b/x/vm/keeper/keeper_test.go @@ -42,49 +42,6 @@ func (suite *KeeperTestSuite) TestBaseFee() { suite.enableLondonHF = true } -func (suite *KeeperTestSuite) SetupTest() { - keys := storetypes.NewKVStoreKeys( - authtypes.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey, - minttypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey, - govtypes.StoreKey, paramstypes.StoreKey, consensusparamtypes.StoreKey, - upgradetypes.StoreKey, feegrant.StoreKey, evidencetypes.StoreKey, authzkeeper.StoreKey, - // ibc keys - ibcexported.StoreKey, ibctransfertypes.StoreKey, - // Cosmos EVM store keys - vmtypes.StoreKey, feemarkettypes.StoreKey, erc20types.StoreKey, precisebanktypes.StoreKey, - ) - key := storetypes.NewKVStoreKey(vmtypes.StoreKey) - transientKey := storetypes.NewTransientStoreKey(vmtypes.TransientKey) - testCtx := testutil.DefaultContextWithDB(suite.T(), key, storetypes.NewTransientStoreKey("transient_test")) - ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Time: cmttime.Now()}) - encCfg := moduletestutil.MakeTestEncodingConfig() - - // storeService := runtime.NewKVStoreService(key) - authority := sdk.AccAddress("foobar") - - suite.bankKeeper = mocks.NewBankKeeper(suite.T()) - suite.accKeeper = mocks.NewAccountKeeper(suite.T()) - suite.stakingKeeper = mocks.NewStakingKeeper(suite.T()) - suite.fmKeeper = mocks.NewFeeMarketKeeper(suite.T()) - suite.erc20Keeper = mocks.NewErc20Keeper(suite.T()) - suite.ctx = ctx - - suite.accKeeper.On("GetModuleAddress", vmtypes.ModuleName).Return(sdk.AccAddress("evm")) - suite.vmKeeper = vmkeeper.NewKeeper( - encCfg.Codec, - key, - transientKey, - keys, - authority, - suite.accKeeper, - suite.bankKeeper, - suite.stakingKeeper, - suite.fmKeeper, - suite.erc20Keeper, - "", - vmkeeper.GasNoLimit, - ) - func (suite *KeeperTestSuite) TestGetAccountStorage() { var ctx sdk.Context testCases := []struct { From ccd48f27103457c61e33a5502e099d6639c3ae4f Mon Sep 17 00:00:00 2001 From: "Thomas N." <81727899+thomas-nguy@users.noreply.github.com> Date: Tue, 5 Aug 2025 09:13:57 +0900 Subject: [PATCH 7/9] revert tx_args.go --- x/vm/types/tx_args.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x/vm/types/tx_args.go b/x/vm/types/tx_args.go index 0ef62b41b..e0ad56cb1 100644 --- a/x/vm/types/tx_args.go +++ b/x/vm/types/tx_args.go @@ -3,6 +3,7 @@ package types import ( "errors" "fmt" + "math" "math/big" "github.com/ethereum/go-ethereum/common" @@ -181,8 +182,7 @@ func (args *TransactionArgs) ToMessage(globalGasCap uint64, baseFee *big.Int, sk // Set default gas & gas price if none were set gas := globalGasCap if gas == 0 { - // Ethereum block size is ~36000000, we use this value as default in case neither gas or global cap is specified, to protect against DOS - gas = uint64(100000000) + gas = uint64(math.MaxUint64 / 2) } if args.Gas != nil { gas = uint64(*args.Gas) From 62b78795cd1da9e3c70b1721a46aeade5fc7d510 Mon Sep 17 00:00:00 2001 From: "Thomas N." <81727899+thomas-nguy@users.noreply.github.com> Date: Wed, 6 Aug 2025 09:21:23 +0900 Subject: [PATCH 8/9] Update CHANGELOG.md --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe2febdba..e2616e672 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,6 @@ - [\#368](https://github.com/cosmos/evm/pull/368) Support query gas limit flag - [\#296](https://github.com/cosmos/evm/pull/296) Sanity checks for TraceTx - ### IMPROVEMENTS - [\#183](https://github.com/cosmos/evm/pull/183) Enforce `msg.sender == requester` on From 17f1ec1c6a3ef0323f5cf52ddd692f2d9e656638 Mon Sep 17 00:00:00 2001 From: Thomas Nguy Date: Tue, 12 Aug 2025 16:51:55 +0900 Subject: [PATCH 9/9] add query limit i baseapp --- evmd/cmd/evmd/cmd/creator.go | 1 + evmd/cmd/evmd/cmd/root.go | 1 + 2 files changed, 2 insertions(+) diff --git a/evmd/cmd/evmd/cmd/creator.go b/evmd/cmd/evmd/cmd/creator.go index c6238998d..350a089d6 100644 --- a/evmd/cmd/evmd/cmd/creator.go +++ b/evmd/cmd/evmd/cmd/creator.go @@ -86,6 +86,7 @@ func (a appCreator) newApp( baseapp.SetIndexEvents(cast.ToStringSlice(appOpts.Get(server.FlagIndexEvents))), baseapp.SetSnapshot(snapshotStore, snapshotOptions), baseapp.SetIAVLCacheSize(cast.ToInt(appOpts.Get(server.FlagIAVLCacheSize))), + baseapp.SetQueryGasLimit(cast.ToUint64(appOpts.Get(server.FlagQueryGasLimit))), } return evmd.NewExampleApp( diff --git a/evmd/cmd/evmd/cmd/root.go b/evmd/cmd/evmd/cmd/root.go index dc715e02d..b75a2ab35 100644 --- a/evmd/cmd/evmd/cmd/root.go +++ b/evmd/cmd/evmd/cmd/root.go @@ -313,6 +313,7 @@ func newApp( baseapp.SetIAVLCacheSize(cast.ToInt(appOpts.Get(sdkserver.FlagIAVLCacheSize))), baseapp.SetIAVLDisableFastNode(cast.ToBool(appOpts.Get(sdkserver.FlagDisableIAVLFastNode))), baseapp.SetChainID(chainID), + baseapp.SetQueryGasLimit(cast.ToUint64(appOpts.Get(sdkserver.FlagQueryGasLimit))), } // Set up the required mempool and ABCI proposal handlers for Cosmos EVM