From 6b1f30aaf702f7ad530ebdc56a6298e9cd7027d8 Mon Sep 17 00:00:00 2001 From: mmsqe Date: Mon, 26 Aug 2024 09:58:22 +0800 Subject: [PATCH] Problem: handle for integer overflow is messy --- CHANGELOG.md | 1 + app/ante/fee_checker.go | 9 ++-- ethereum/eip712/domain.go | 4 +- ethereum/eip712/eip712.go | 7 ++- ethereum/eip712/eip712_legacy.go | 7 ++- indexer/kv_indexer.go | 14 ++++-- rpc/backend/account_info.go | 15 ++++--- rpc/backend/blocks.go | 6 ++- rpc/backend/chain_info.go | 23 +++++++--- rpc/backend/tracing.go | 8 +++- rpc/backend/tx_info.go | 31 ++++++++++--- rpc/namespaces/ethereum/debug/api.go | 58 +++++++++++++++++++----- rpc/types/block.go | 9 ++-- rpc/types/events.go | 13 ++++-- testutil/tx/cosmos.go | 3 +- types/int.go | 67 +++++++++++++++++++++++++--- x/evm/keeper/msg_server.go | 12 ++++- x/evm/keeper/state_transition.go | 12 ++++- x/evm/simulation/operations.go | 4 +- x/evm/types/eth.go | 6 ++- x/feemarket/keeper/abci.go | 19 ++++---- x/feemarket/keeper/grpc_query.go | 9 ++-- 22 files changed, 260 insertions(+), 77 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7980fc8963..b597bf12a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -84,6 +84,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (ante) [#497](https://github.com/crypto-org-chain/ethermint/pull/497) Enforce positive value check in eth transaction. * (deps) [#505](https://github.com/crypto-org-chain/ethermint/pull/505) Update cometbft to v0.38.10. * (ante) [#504](https://github.com/crypto-org-chain/ethermint/pull/504) Optimize AnteHandle method to skip checks if disabledMsgs is empty. +* [#517](https://github.com/crypto-org-chain/ethermint/pull/517) Add check for integer overflow to ensure safe conversion. ## v0.21.x-cronos diff --git a/app/ante/fee_checker.go b/app/ante/fee_checker.go index 3a97ecb0b2..53b890311b 100644 --- a/app/ante/fee_checker.go +++ b/app/ante/fee_checker.go @@ -118,7 +118,10 @@ func checkTxFeeWithValidatorMinGasPrices(ctx sdk.Context, tx sdk.Tx) (sdk.Coins, } feeCoins := feeTx.GetFee() - gas := feeTx.GetGas() + gas, err := ethermint.SafeInt64(feeTx.GetGas()) + if err != nil { + return nil, 0, err + } minGasPrices := ctx.MinGasPrices() // Ensure that the provided fees meet a minimum threshold for the validator, @@ -129,7 +132,7 @@ func checkTxFeeWithValidatorMinGasPrices(ctx sdk.Context, tx sdk.Tx) (sdk.Coins, // Determine the required fees by multiplying each required minimum gas // price by the gas limit, where fee = ceil(minGasPrice * gasLimit). - glDec := sdkmath.LegacyNewDec(int64(gas)) + glDec := sdkmath.LegacyNewDec(gas) for i, gp := range minGasPrices { fee := gp.Amount.Mul(glDec) @@ -141,7 +144,7 @@ func checkTxFeeWithValidatorMinGasPrices(ctx sdk.Context, tx sdk.Tx) (sdk.Coins, } } - priority := getTxPriority(feeCoins, int64(gas)) + priority := getTxPriority(feeCoins, gas) return feeCoins, priority, nil } diff --git a/ethereum/eip712/domain.go b/ethereum/eip712/domain.go index 5c63df4299..6390d53e9e 100644 --- a/ethereum/eip712/domain.go +++ b/ethereum/eip712/domain.go @@ -21,11 +21,11 @@ import ( ) // createEIP712Domain creates the typed data domain for the given chainID. -func createEIP712Domain(chainID uint64) apitypes.TypedDataDomain { +func createEIP712Domain(chainID int64) apitypes.TypedDataDomain { domain := apitypes.TypedDataDomain{ Name: "Cosmos Web3", Version: "1.0.0", - ChainId: math.NewHexOrDecimal256(int64(chainID)), // #nosec G701 + ChainId: math.NewHexOrDecimal256(chainID), VerifyingContract: "cosmos", Salt: "0", } diff --git a/ethereum/eip712/eip712.go b/ethereum/eip712/eip712.go index cef97b31b1..e87de9491e 100644 --- a/ethereum/eip712/eip712.go +++ b/ethereum/eip712/eip712.go @@ -17,6 +17,7 @@ package eip712 import ( "github.com/ethereum/go-ethereum/signer/core/apitypes" + ethermint "github.com/evmos/ethermint/types" ) // WrapTxToTypedData wraps an Amino-encoded Cosmos Tx JSON SignDoc @@ -36,7 +37,11 @@ func WrapTxToTypedData( return apitypes.TypedData{}, err } - domain := createEIP712Domain(chainID) + value, err := ethermint.SafeInt64(chainID) + if err != nil { + return apitypes.TypedData{}, err + } + domain := createEIP712Domain(value) typedData := apitypes.TypedData{ Types: types, diff --git a/ethereum/eip712/eip712_legacy.go b/ethereum/eip712/eip712_legacy.go index e093065ee2..57b3376b0a 100644 --- a/ethereum/eip712/eip712_legacy.go +++ b/ethereum/eip712/eip712_legacy.go @@ -29,6 +29,7 @@ import ( "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" sdk "github.com/cosmos/cosmos-sdk/types" errortypes "github.com/cosmos/cosmos-sdk/types/errors" + ethermint "github.com/evmos/ethermint/types" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" @@ -52,6 +53,10 @@ func LegacyWrapTxToTypedData( data []byte, feeDelegation *FeeDelegationOptions, ) (apitypes.TypedData, error) { + value, err := ethermint.SafeInt64(chainID) + if err != nil { + return apitypes.TypedData{}, err + } txData := make(map[string]interface{}) if err := json.Unmarshal(data, &txData); err != nil { @@ -61,7 +66,7 @@ func LegacyWrapTxToTypedData( domain := apitypes.TypedDataDomain{ Name: "Cosmos Web3", Version: "1.0.0", - ChainId: math.NewHexOrDecimal256(int64(chainID)), + ChainId: math.NewHexOrDecimal256(value), VerifyingContract: "cosmos", Salt: "0", } diff --git a/indexer/kv_indexer.go b/indexer/kv_indexer.go index 7849418021..c6ca943a67 100644 --- a/indexer/kv_indexer.go +++ b/indexer/kv_indexer.go @@ -70,6 +70,10 @@ func (kv *KVIndexer) IndexBlock(block *tmtypes.Block, txResults []*abci.ExecTxRe // record index of valid eth tx during the iteration var ethTxIndex int32 for txIndex, tx := range block.Txs { + txIdx, err := ethermint.SafeUint32(txIndex) + if err != nil { + return err + } result := txResults[txIndex] if !rpctypes.TxSuccessOrExceedsBlockGasLimit(result) { continue @@ -93,13 +97,17 @@ func (kv *KVIndexer) IndexBlock(block *tmtypes.Block, txResults []*abci.ExecTxRe var cumulativeGasUsed uint64 for msgIndex, msg := range tx.GetMsgs() { + msgIdx, err := ethermint.SafeUint32(msgIndex) + if err != nil { + return err + } ethMsg := msg.(*evmtypes.MsgEthereumTx) var txHash common.Hash txResult := ethermint.TxResult{ Height: height, - TxIndex: uint32(txIndex), - MsgIndex: uint32(msgIndex), + TxIndex: txIdx, + MsgIndex: msgIdx, EthTxIndex: ethTxIndex, } if result.Code != abci.CodeTypeOK { @@ -243,5 +251,5 @@ func parseBlockNumberFromKey(key []byte) (int64, error) { return 0, fmt.Errorf("wrong tx index key length, expect: %d, got: %d", TxIndexKeyLength, len(key)) } - return int64(sdk.BigEndianToUint64(key[1:9])), nil + return ethermint.SafeInt64(sdk.BigEndianToUint64(key[1:9])) } diff --git a/rpc/backend/account_info.go b/rpc/backend/account_info.go index ea4c130179..b3069038a2 100644 --- a/rpc/backend/account_info.go +++ b/rpc/backend/account_info.go @@ -16,8 +16,6 @@ package backend import ( - "fmt" - "math" "math/big" errorsmod "cosmossdk.io/errors" @@ -29,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" rpctypes "github.com/evmos/ethermint/rpc/types" + ethermint "github.com/evmos/ethermint/types" evmtypes "github.com/evmos/ethermint/x/evm/types" "github.com/pkg/errors" ) @@ -74,11 +73,10 @@ func (b *Backend) GetProof(address common.Address, storageKeys []string, blockNr return nil, err } - if bn > math.MaxInt64 { - return nil, fmt.Errorf("not able to query block number greater than MaxInt64") + height, err = ethermint.SafeHexToInt64(bn) + if err != nil { + return nil, err } - - height = int64(bn) } clientCtx := b.clientCtx.WithHeight(height) @@ -195,8 +193,11 @@ func (b *Backend) GetTransactionCount(address common.Address, blockNum rpctypes. if err != nil { return &n, err } + currentHeight, err := ethermint.SafeHexToInt64(bn) + if err != nil { + return nil, err + } height := blockNum.Int64() - currentHeight := int64(bn) if height > currentHeight { return &n, errorsmod.Wrapf( sdkerrors.ErrInvalidHeight, diff --git a/rpc/backend/blocks.go b/rpc/backend/blocks.go index fe946ebfc1..4b7f70a1d2 100644 --- a/rpc/backend/blocks.go +++ b/rpc/backend/blocks.go @@ -30,6 +30,7 @@ import ( ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/trie" rpctypes "github.com/evmos/ethermint/rpc/types" + ethermint "github.com/evmos/ethermint/types" evmtypes "github.com/evmos/ethermint/x/evm/types" "github.com/pkg/errors" "google.golang.org/grpc" @@ -179,7 +180,10 @@ func (b *Backend) TendermintBlockByNumber(blockNum rpctypes.BlockNumber) (*tmrpc if err != nil { return nil, err } - height = int64(n) + height, err = ethermint.SafeHexToInt64(n) + if err != nil { + return nil, err + } } resBlock, err := b.clientCtx.Client.Block(b.ctx, &height) if err != nil { diff --git a/rpc/backend/chain_info.go b/rpc/backend/chain_info.go index 883b062544..78af6d1f98 100644 --- a/rpc/backend/chain_info.go +++ b/rpc/backend/chain_info.go @@ -182,18 +182,25 @@ func (b *Backend) FeeHistory( return nil, fmt.Errorf("%w: #%d:%f > #%d:%f", errInvalidPercentile, i-1, rewardPercentiles[i-1], i, p) } } - blockNumber, err := b.BlockNumber() + blkNumber, err := b.BlockNumber() + if err != nil { + return nil, err + } + blockNumber, err := ethermint.SafeHexToInt64(blkNumber) if err != nil { return nil, err } blockEnd := int64(lastBlock) if blockEnd < 0 { - blockEnd = int64(blockNumber) - } else if int64(blockNumber) < blockEnd { - return nil, fmt.Errorf("%w: requested %d, head %d", errRequestBeyondHead, blockEnd, int64(blockNumber)) + blockEnd = blockNumber + } else if blockNumber < blockEnd { + return nil, fmt.Errorf("%w: requested %d, head %d", errRequestBeyondHead, blockEnd, blockNumber) } - blocks := int64(userBlockCount) + blocks, err := ethermint.SafeInt64(uint64(userBlockCount)) + if err != nil { + return nil, err + } maxBlockCount := int64(b.cfg.JSONRPC.FeeHistoryCap) if blocks > maxBlockCount { return nil, fmt.Errorf("FeeHistory user block count %d higher than %d", blocks, maxBlockCount) @@ -226,6 +233,10 @@ func (b *Backend) FeeHistory( if blockID+int64(i) >= blockEnd+1 { break } + value := blockID - blockStart + int64(i) + if value > math.MaxInt32 || value < math.MinInt32 { + return nil, fmt.Errorf("integer overflow: calculated value %d exceeds int32 limits", value) + } wg.Add(1) go func(index int32) { defer func() { @@ -282,7 +293,7 @@ func (b *Backend) FeeHistory( } } } - }(int32(blockID - blockStart + int64(i))) + }(int32(value)) //nolint:gosec // checked } go func() { wg.Wait() diff --git a/rpc/backend/tracing.go b/rpc/backend/tracing.go index 2b80baba1d..ab0845dc5a 100644 --- a/rpc/backend/tracing.go +++ b/rpc/backend/tracing.go @@ -24,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" rpctypes "github.com/evmos/ethermint/rpc/types" + ethermint "github.com/evmos/ethermint/types" evmtypes "github.com/evmos/ethermint/x/evm/types" "github.com/pkg/errors" ) @@ -48,9 +49,12 @@ func (b *Backend) TraceTransaction(hash common.Hash, config *rpctypes.TraceConfi b.logger.Debug("block not found", "height", transaction.Height) return nil, err } - + total, err := ethermint.SafeUint32(len(blk.Block.Txs)) + if err != nil { + return nil, err + } // check tx index is not out of bound - if uint32(len(blk.Block.Txs)) < transaction.TxIndex { + if total < transaction.TxIndex { b.logger.Debug("tx index out of bounds", "index", transaction.TxIndex, "hash", hash.String(), "height", blk.Block.Height) return nil, fmt.Errorf("transaction not included in block %v", blk.Block.Height) } diff --git a/rpc/backend/tx_info.go b/rpc/backend/tx_info.go index 2e626e2565..e004b413ee 100644 --- a/rpc/backend/tx_info.go +++ b/rpc/backend/tx_info.go @@ -65,8 +65,12 @@ func (b *Backend) GetTransactionByHash(txHash common.Hash) (*rpctypes.RPCTransac // Fallback to find tx index by iterating all valid eth transactions msgs := b.EthMsgsFromTendermintBlock(block, blockRes) for i := range msgs { + idx, err := ethermint.SafeIntToInt32(i) + if err != nil { + return nil, err + } if msgs[i].Hash() == txHash { - res.EthTxIndex = int32(i) + res.EthTxIndex = idx break } } @@ -209,8 +213,12 @@ func (b *Backend) GetTransactionReceipt(hash common.Hash) (map[string]interface{ // Fallback to find tx index by iterating all valid eth transactions msgs := b.EthMsgsFromTendermintBlock(resBlock, blockRes) for i := range msgs { + idx, err := ethermint.SafeIntToInt32(i) + if err != nil { + return nil, err + } if msgs[i].Hash() == hash { - res.EthTxIndex = int32(i) + res.EthTxIndex = idx break } } @@ -328,9 +336,17 @@ func (b *Backend) GetTxByEthHash(hash common.Hash) (*ethermint.TxResult, error) } // GetTxByTxIndex uses `/tx_query` to find transaction by tx index of valid ethereum txs -func (b *Backend) GetTxByTxIndex(height int64, index uint) (*ethermint.TxResult, error) { +func (b *Backend) GetTxByTxIndex(height int64, i uint) (*ethermint.TxResult, error) { + index, err := ethermint.SafeUintToInt32(i) + if err != nil { + return nil, err + } + idx, err := ethermint.SafeInt(i) + if err != nil { + return nil, err + } if b.indexer != nil { - return b.indexer.GetByBlockAndIndex(height, int32(index)) + return b.indexer.GetByBlockAndIndex(height, index) } // fallback to tendermint tx indexer @@ -339,7 +355,7 @@ func (b *Backend) GetTxByTxIndex(height int64, index uint) (*ethermint.TxResult, evmtypes.AttributeKeyTxIndex, index, ) txResult, err := b.queryTendermintTxIndexer(query, func(txs *rpctypes.ParsedTxs) *rpctypes.ParsedTx { - return txs.GetTxByTxIndex(int(index)) + return txs.GetTxByTxIndex(idx) }) if err != nil { return nil, errorsmod.Wrapf(err, "GetTxByTxIndex %d %d", height, index) @@ -398,7 +414,10 @@ func (b *Backend) GetTransactionByBlockAndIndex(block *tmrpctypes.ResultBlock, i return nil, nil } } else { - i := int(idx) + i, err := ethermint.SafeHexToInt(idx) + if err != nil { + return nil, err + } ethMsgs := b.EthMsgsFromTendermintBlock(block, blockRes) if i >= len(ethMsgs) { b.logger.Debug("block txs index out of bound", "index", i) diff --git a/rpc/namespaces/ethereum/debug/api.go b/rpc/namespaces/ethereum/debug/api.go index bfdae08af9..52219c9685 100644 --- a/rpc/namespaces/ethereum/debug/api.go +++ b/rpc/namespaces/ethereum/debug/api.go @@ -29,8 +29,8 @@ import ( "github.com/davecgh/go-spew/spew" + ethermint "github.com/evmos/ethermint/types" evmtypes "github.com/evmos/ethermint/x/evm/types" - stderrors "github.com/pkg/errors" "github.com/cosmos/cosmos-sdk/server" @@ -128,26 +128,41 @@ func (a *API) TraceCall( return a.backend.TraceCall(args, blockNrOrHash, config) } +func parseDuration(nsec uint) (time.Duration, error) { + if nsec > uint(time.Duration(1<<63-1)/time.Second) { + return time.Duration(0), fmt.Errorf("value %d exceeds maximum duration for time.Duration", nsec) + } + return time.Duration(nsec) * time.Second, nil //nolint:gosec // checked +} + // BlockProfile turns on goroutine profiling for nsec seconds and writes profile data to // file. It uses a profile rate of 1 for most accurate information. If a different rate is // desired, set the rate and write the profile manually. func (a *API) BlockProfile(file string, nsec uint) error { + d, err := parseDuration(nsec) + if err != nil { + return err + } a.logger.Debug("debug_blockProfile", "file", file, "nsec", nsec) runtime.SetBlockProfileRate(1) defer runtime.SetBlockProfileRate(0) - time.Sleep(time.Duration(nsec) * time.Second) + time.Sleep(d) return writeProfile("block", file, a.logger) } // CpuProfile turns on CPU profiling for nsec seconds and writes // profile data to file. func (a *API) CpuProfile(file string, nsec uint) error { //nolint: golint, stylecheck, revive + d, err := parseDuration(nsec) + if err != nil { + return err + } a.logger.Debug("debug_cpuProfile", "file", file, "nsec", nsec) if err := a.StartCPUProfile(file); err != nil { return err } - time.Sleep(time.Duration(nsec) * time.Second) + time.Sleep(d) return a.StopCPUProfile() } @@ -162,11 +177,15 @@ func (a *API) GcStats() *debug.GCStats { // GoTrace turns on tracing for nsec seconds and writes // trace data to file. func (a *API) GoTrace(file string, nsec uint) error { + d, err := parseDuration(nsec) + if err != nil { + return err + } a.logger.Debug("debug_goTrace", "file", file, "nsec", nsec) if err := a.StartGoTrace(file); err != nil { return err } - time.Sleep(time.Duration(nsec) * time.Second) + time.Sleep(d) return a.StopGoTrace() } @@ -280,9 +299,13 @@ func (a *API) WriteMemProfile(file string) error { // It uses a profile rate of 1 for most accurate information. If a different rate is // desired, set the rate and write the profile manually. func (a *API) MutexProfile(file string, nsec uint) error { + d, err := parseDuration(nsec) + if err != nil { + return err + } a.logger.Debug("debug_mutexProfile", "file", file, "nsec", nsec) runtime.SetMutexProfileFraction(1) - time.Sleep(time.Duration(nsec) * time.Second) + time.Sleep(d) defer runtime.SetMutexProfileFraction(0) return writeProfile("mutex", file, a.logger) } @@ -314,7 +337,11 @@ func (a *API) SetGCPercent(v int) int { // GetHeaderRlp retrieves the RLP encoded for of a single header. func (a *API) GetHeaderRlp(number uint64) (hexutil.Bytes, error) { - header, err := a.backend.HeaderByNumber(rpctypes.BlockNumber(number)) + value, err := ethermint.SafeInt64(number) + if err != nil { + return nil, err + } + header, err := a.backend.HeaderByNumber(rpctypes.BlockNumber(value)) if err != nil { return nil, err } @@ -324,7 +351,11 @@ func (a *API) GetHeaderRlp(number uint64) (hexutil.Bytes, error) { // GetBlockRlp retrieves the RLP encoded for of a single block. func (a *API) GetBlockRlp(number uint64) (hexutil.Bytes, error) { - block, err := a.backend.EthBlockByNumber(rpctypes.BlockNumber(number)) + value, err := ethermint.SafeInt64(number) + if err != nil { + return nil, err + } + block, err := a.backend.EthBlockByNumber(rpctypes.BlockNumber(value)) if err != nil { return nil, err } @@ -334,17 +365,24 @@ func (a *API) GetBlockRlp(number uint64) (hexutil.Bytes, error) { // PrintBlock retrieves a block and returns its pretty printed form. func (a *API) PrintBlock(number uint64) (string, error) { - block, err := a.backend.EthBlockByNumber(rpctypes.BlockNumber(number)) + value, err := ethermint.SafeInt64(number) + if err != nil { + return "", err + } + block, err := a.backend.EthBlockByNumber(rpctypes.BlockNumber(value)) if err != nil { return "", err } - return spew.Sdump(block), nil } // SeedHash retrieves the seed hash of a block. func (a *API) SeedHash(number uint64) (string, error) { - _, err := a.backend.HeaderByNumber(rpctypes.BlockNumber(number)) + value, err := ethermint.SafeInt64(number) + if err != nil { + return "", err + } + _, err = a.backend.HeaderByNumber(rpctypes.BlockNumber(value)) if err != nil { return "", err } diff --git a/rpc/types/block.go b/rpc/types/block.go index 9f4b533950..eeaf5d1557 100644 --- a/rpc/types/block.go +++ b/rpc/types/block.go @@ -20,7 +20,6 @@ import ( "encoding/json" "errors" "fmt" - "math" "math/big" "strings" @@ -103,11 +102,11 @@ func (bn *BlockNumber) UnmarshalJSON(data []byte) error { } else if err != nil { return err } - - if blckNum > math.MaxInt64 { - return fmt.Errorf("block number larger than int64") + b, err := ethermint.SafeInt64(blckNum) + if err != nil { + return err } - *bn = BlockNumber(blckNum) + *bn = BlockNumber(b) return nil } diff --git a/rpc/types/events.go b/rpc/types/events.go index a0ce40a56a..16bc3f46ef 100644 --- a/rpc/types/events.go +++ b/rpc/types/events.go @@ -162,11 +162,14 @@ func ParseTxIndexerResult(txResult *tmrpctypes.ResultTx, tx sdk.Tx, getter func( if parsedTx == nil { return nil, fmt.Errorf("ethereum tx not found in msgs: block %d, index %d", txResult.Height, txResult.Index) } - + msgIndex, err := ethermint.SafeUint32(parsedTx.MsgIndex) + if err != nil { + return nil, err + } return ðermint.TxResult{ Height: txResult.Height, TxIndex: txResult.Index, - MsgIndex: uint32(parsedTx.MsgIndex), + MsgIndex: msgIndex, EthTxIndex: parsedTx.EthTxIndex, Failed: parsedTx.Failed, GasUsed: parsedTx.GasUsed, @@ -251,7 +254,11 @@ func fillTxAttribute(tx *ParsedTx, key []byte, value []byte) error { if err != nil { return err } - tx.EthTxIndex = int32(txIndex) + txIdx, err := ethermint.SafeUint64ToInt32(txIndex) + if err != nil { + return err + } + tx.EthTxIndex = txIdx case evmtypes.AttributeKeyTxGasUsed: gasUsed, err := strconv.ParseUint(string(value), 10, 64) if err != nil { diff --git a/testutil/tx/cosmos.go b/testutil/tx/cosmos.go index 91ce574057..43f9d51191 100644 --- a/testutil/tx/cosmos.go +++ b/testutil/tx/cosmos.go @@ -67,7 +67,8 @@ func PrepareCosmosTx( var fees sdk.Coins if args.GasPrice != nil { - fees = sdk.Coins{{Denom: evmtypes.DefaultEVMDenom, Amount: args.GasPrice.MulRaw(int64(args.Gas))}} + gas := int64(args.Gas) //nolint:gosec // test only + fees = sdk.Coins{{Denom: evmtypes.DefaultEVMDenom, Amount: args.GasPrice.MulRaw(gas)}} } else { fees = sdk.Coins{DefaultFee} } diff --git a/types/int.go b/types/int.go index 9342b836c6..3c717d3c68 100644 --- a/types/int.go +++ b/types/int.go @@ -21,9 +21,8 @@ import ( "math/big" "math/bits" - errorsmod "cosmossdk.io/errors" sdkmath "cosmossdk.io/math" - errortypes "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/ethereum/go-ethereum/common/hexutil" ) const ( @@ -40,18 +39,74 @@ func init() { // SafeInt64 checks for overflows while casting a uint64 to int64 value. func SafeInt64(value uint64) (int64, error) { if value > uint64(math.MaxInt64) { - return 0, errorsmod.Wrapf(errortypes.ErrInvalidHeight, "uint64 value %v cannot exceed %v", value, int64(math.MaxInt64)) + return 0, fmt.Errorf("uint64 value %v cannot exceed %v", value, math.MaxInt64) } - return int64(value), nil + return int64(value), nil //nolint:gosec // checked +} + +func SafeUint64ToInt32(value uint64) (int32, error) { + if value > uint64(math.MaxInt64) { + return 0, fmt.Errorf("uint64 value %v cannot exceed %v", value, math.MaxInt64) + } + + return int32(value), nil //nolint:gosec // checked +} + +func SafeUint64ToInt(value uint64) (int, error) { + if value > uint64(math.MaxInt64) { + return 0, fmt.Errorf("uint64 value %v cannot exceed %v", value, math.MaxInt64) + } + + return int(value), nil //nolint:gosec // checked +} + +func SafeHexToInt64(value hexutil.Uint64) (int64, error) { + if value > math.MaxInt64 { + return 0, fmt.Errorf("hexutil.Uint64 value %v cannot exceed %v", value, math.MaxInt64) + } + + return int64(value), nil //nolint:gosec // checked +} + +func SafeUint32(value int) (uint32, error) { + if value > math.MaxUint32 { + return 0, fmt.Errorf("int value %v cannot exceed %v", value, math.MaxUint32) + } + + return uint32(value), nil //nolint:gosec // checked +} + +func SafeUintToInt32(value uint) (int32, error) { + if value > uint(math.MaxInt32) { + return 0, fmt.Errorf("uint value %v cannot exceed %v", value, math.MaxUint32) + } + + return int32(value), nil //nolint:gosec // checked +} + +func SafeIntToInt32(value int) (int32, error) { + if value > int(math.MaxInt32) { + return 0, fmt.Errorf("int value %v cannot exceed %v", value, math.MaxUint32) + } + + return int32(value), nil //nolint:gosec // checked } func SafeInt(value uint) (int, error) { if value > uint(math.MaxInt64) { - return 0, errorsmod.Wrapf(errortypes.ErrInvalidHeight, "uint value %v cannot exceed %v", value, int(math.MaxInt64)) + return 0, fmt.Errorf("uint value %v cannot exceed %v", value, math.MaxInt64) + } + + return int(value), nil //nolint:gosec // checked +} + +func SafeHexToInt(value hexutil.Uint) (int, error) { + if value > hexutil.Uint(math.MaxInt) { + return 0, fmt.Errorf("hexutil.Uint value %v cannot exceed %v", value, math.MaxInt) } - return int(value), nil + return int(value), nil //nolint:gosec // checked } // SafeNewIntFromBigInt constructs Int from big.Int, return error if more than 256bits diff --git a/x/evm/keeper/msg_server.go b/x/evm/keeper/msg_server.go index cb23aad6fc..ed799c6ea7 100644 --- a/x/evm/keeper/msg_server.go +++ b/x/evm/keeper/msg_server.go @@ -31,6 +31,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/hashicorp/go-metrics" + ethermint "github.com/evmos/ethermint/types" "github.com/evmos/ethermint/x/evm/types" ) @@ -75,8 +76,15 @@ func (k *Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*t // Observe which users define a gas limit >> gas used. Note, that // gas_limit and gas_used are always > 0 - gasLimit := sdkmath.LegacyNewDec(int64(tx.Gas())) - gasRatio, err := gasLimit.QuoInt64(int64(response.GasUsed)).Float64() + gasLimit, err := ethermint.SafeInt64(tx.Gas()) + if err != nil { + k.Logger(ctx).Error("failed to cast gas to int64", "error", err) + } + gasUsed, err := ethermint.SafeInt64(response.GasUsed) + if err != nil { + k.Logger(ctx).Error("failed to cast gasUsed to int64", "error", err) + } + gasRatio, err := sdkmath.LegacyNewDec(gasLimit).QuoInt64(gasUsed).Float64() if err == nil { telemetry.SetGaugeWithLabels( []string{"tx", "msg", "ethereum_tx", "gas_limit", "per", "gas_used"}, diff --git a/x/evm/keeper/state_transition.go b/x/evm/keeper/state_transition.go index 0c7189f1e0..02e534f822 100644 --- a/x/evm/keeper/state_transition.go +++ b/x/evm/keeper/state_transition.go @@ -418,7 +418,11 @@ func (k *Keeper) ApplyMessageWithConfig( // calculate a minimum amount of gas to be charged to sender if GasLimit // is considerably higher than GasUsed to stay more aligned with Tendermint gas mechanics // for more info https://github.com/evmos/ethermint/issues/1085 - gasLimit := sdkmath.LegacyNewDec(int64(msg.GasLimit)) + limit, err := ethermint.SafeInt64(msg.GasLimit) + if err != nil { + return nil, err + } + gasLimit := sdkmath.LegacyNewDec(limit) minGasMultiplier := cfg.FeeMarketParams.MinGasMultiplier if minGasMultiplier.IsNil() { // in case we are executing eth_call on a legacy block, returns a zero value. @@ -429,8 +433,12 @@ func (k *Keeper) ApplyMessageWithConfig( if msg.GasLimit < leftoverGas { return nil, errorsmod.Wrapf(types.ErrGasOverflow, "message gas limit < leftover gas (%d < %d)", msg.GasLimit, leftoverGas) } + tempGasUsed, err := ethermint.SafeInt64(temporaryGasUsed) + if err != nil { + return nil, err + } - gasUsed := sdkmath.LegacyMaxDec(minimumGasUsed, sdkmath.LegacyNewDec(int64(temporaryGasUsed))).TruncateInt().Uint64() + gasUsed := sdkmath.LegacyMaxDec(minimumGasUsed, sdkmath.LegacyNewDec(tempGasUsed)).TruncateInt().Uint64() // reset leftoverGas, to be used by the tracer leftoverGas = msg.GasLimit - gasUsed diff --git a/x/evm/simulation/operations.go b/x/evm/simulation/operations.go index 89ed26009a..d09603496d 100644 --- a/x/evm/simulation/operations.go +++ b/x/evm/simulation/operations.go @@ -215,7 +215,7 @@ func CreateRandomValidEthTx(ctx *simulateContext, return nil, err } // we suppose that gasLimit should be larger than estimateGas to ensure tx validity - gasLimit := estimateGas + uint64(ctx.rand.Intn(int(sdktx.MaxGasWanted-estimateGas))) + gasLimit := estimateGas + uint64(ctx.rand.Intn(int(sdktx.MaxGasWanted-estimateGas))) //nolint:gosec // test only ethChainID := ctx.keeper.ChainID() chainConfig := ctx.keeper.GetParams(ctx.context).ChainConfig.EthereumConfig(ethChainID) gasPrice := ctx.keeper.GetBaseFee(ctx.context, chainConfig) @@ -256,7 +256,7 @@ func EstimateGas(ctx *simulateContext, from, to *common.Address, data *hexutil.B // Transferable amount is between the range [0, spendable), spendable = balance - gasFeeCap * GasLimit. func RandomTransferableAmount(ctx *simulateContext, address common.Address, estimateGas uint64, gasFeeCap *big.Int) (amount *big.Int, err error) { balance := ctx.keeper.GetEVMDenomBalance(ctx.context, address) - feeLimit := new(big.Int).Mul(gasFeeCap, big.NewInt(int64(estimateGas))) + feeLimit := new(big.Int).Mul(gasFeeCap, big.NewInt(int64(estimateGas))) //nolint:gosec // test only if (feeLimit.Cmp(balance)) > 0 { return nil, ErrNoEnoughBalance } diff --git a/x/evm/types/eth.go b/x/evm/types/eth.go index e1fb2655bf..81290c20f8 100644 --- a/x/evm/types/eth.go +++ b/x/evm/types/eth.go @@ -21,7 +21,11 @@ func (tx EthereumTx) Size() int { if tx.Transaction == nil { return 0 } - return int(tx.Transaction.Size()) + size, err := types.SafeUint64ToInt(tx.Transaction.Size()) + if err != nil { + panic(err) + } + return size } func (tx EthereumTx) MarshalTo(dst []byte) (int, error) { diff --git a/x/feemarket/keeper/abci.go b/x/feemarket/keeper/abci.go index d721496da9..be02d8e151 100644 --- a/x/feemarket/keeper/abci.go +++ b/x/feemarket/keeper/abci.go @@ -17,13 +17,13 @@ package keeper import ( "fmt" - "math" "github.com/evmos/ethermint/x/feemarket/types" sdkmath "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" + ethermint "github.com/evmos/ethermint/types" ) // BeginBlock updates base fee @@ -56,14 +56,13 @@ func (k *Keeper) BeginBlock(ctx sdk.Context) error { // an empty slice. func (k *Keeper) EndBlock(ctx sdk.Context) error { gasWanted := ctx.BlockGasWanted() - gasUsed := ctx.BlockGasUsed() - - if gasWanted > math.MaxInt64 { - return fmt.Errorf("integer overflow by integer type conversion. Gas wanted %d > MaxInt64", gasWanted) + gw, err := ethermint.SafeInt64(gasWanted) + if err != nil { + return err } - - if gasUsed > math.MaxInt64 { - return fmt.Errorf("integer overflow by integer type conversion. Gas used %d > MaxInt64", gasUsed) + gasUsed, err := ethermint.SafeInt64(ctx.BlockGasUsed()) + if err != nil { + return err } // to prevent BaseFee manipulation we limit the gasWanted so that @@ -71,8 +70,8 @@ func (k *Keeper) EndBlock(ctx sdk.Context) error { // this will be keep BaseFee protected from un-penalized manipulation // more info here https://github.com/evmos/ethermint/pull/1105#discussion_r888798925 minGasMultiplier := k.GetParams(ctx).MinGasMultiplier - limitedGasWanted := sdkmath.LegacyNewDec(int64(gasWanted)).Mul(minGasMultiplier) - gasWanted = sdkmath.LegacyMaxDec(limitedGasWanted, sdkmath.LegacyNewDec(int64(gasUsed))).TruncateInt().Uint64() + limitedGasWanted := sdkmath.LegacyNewDec(gw).Mul(minGasMultiplier) + gasWanted = sdkmath.LegacyMaxDec(limitedGasWanted, sdkmath.LegacyNewDec(gasUsed)).TruncateInt().Uint64() k.SetBlockGasWanted(ctx, gasWanted) defer func() { diff --git a/x/feemarket/keeper/grpc_query.go b/x/feemarket/keeper/grpc_query.go index 82ea42b6bd..d77be6d5ff 100644 --- a/x/feemarket/keeper/grpc_query.go +++ b/x/feemarket/keeper/grpc_query.go @@ -21,6 +21,7 @@ import ( sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" + ethermint "github.com/evmos/ethermint/types" "github.com/evmos/ethermint/x/feemarket/types" ) @@ -54,9 +55,11 @@ func (k Keeper) BaseFee(c context.Context, _ *types.QueryBaseFeeRequest) (*types // BlockGas implements the Query/BlockGas gRPC method func (k Keeper) BlockGas(c context.Context, _ *types.QueryBlockGasRequest) (*types.QueryBlockGasResponse, error) { ctx := sdk.UnwrapSDKContext(c) - gas := k.GetBlockGasWanted(ctx) - + gas, err := ethermint.SafeInt64(k.GetBlockGasWanted(ctx)) + if err != nil { + return nil, err + } return &types.QueryBlockGasResponse{ - Gas: int64(gas), + Gas: gas, }, nil }