Skip to content

Commit

Permalink
Problem: method eth_chainId crashed occasionally (#516)
Browse files Browse the repository at this point in the history
* Problem: method eth_chainId crashed occasionally

add fallback default config to ensure no panic in IsEIP155 check

* cleanup

---------

Co-authored-by: huangyi <[email protected]>
  • Loading branch information
mmsqe and yihuang authored Sep 10, 2024
1 parent d26adad commit 452b0cf
Show file tree
Hide file tree
Showing 18 changed files with 225 additions and 59 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (rpc) [#488](https://github.com/crypto-org-chain/ethermint/pull/488) Fix handling of pending transactions related APIs.
* (rpc) [#501](https://github.com/crypto-org-chain/ethermint/pull/501) Avoid invalid chain id for signer error when rpc call before chain id set in BeginBlock.
* (block-stm) [#510](https://github.com/crypto-org-chain/ethermint/pull/510) Include a fix to avoid nondeterministic account set when stm workers execute in parallel.
* (rpc) [#516](https://github.com/crypto-org-chain/ethermint/pull/516) Avoid method eth_chainId crashed due to nil pointer on IsEIP155 check.

### Improvements

Expand All @@ -80,7 +81,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (app) [#483](https://github.com/crypto-org-chain/ethermint/pull/483) Make keyring-backend client config accessible in app.
* (deps) [#489](https://github.com/crypto-org-chain/ethermint/pull/489) Update cosmos-sdk to `v0.50.7`.
* (rpc) [#491](https://github.com/crypto-org-chain/ethermint/pull/491) Avoid unnecessary tx decode in tx listener.
* [#496](https://github.com/crypto-org-chain/cronos/pull/496) Set mempool MaxTx from config.
* [#496](https://github.com/crypto-org-chain/ethermint/pull/496) Set mempool MaxTx from config.
* (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.
Expand Down
12 changes: 10 additions & 2 deletions indexer/kv_indexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,16 @@ func TxHashKey(hash common.Hash) []byte {

// TxIndexKey returns the key for db entry: `(block number, tx index) -> tx hash`
func TxIndexKey(blockNumber int64, txIndex int32) []byte {
bz1 := sdk.Uint64ToBigEndian(uint64(blockNumber))
bz2 := sdk.Uint64ToBigEndian(uint64(txIndex))
value, err := ethermint.SafeUint64(blockNumber)
if err != nil {
panic(err)
}
bz1 := sdk.Uint64ToBigEndian(value)
value, err = ethermint.SafeInt32ToUint64(txIndex)
if err != nil {
panic(err)
}
bz2 := sdk.Uint64ToBigEndian(value)
return append(append([]byte{KeyPrefixTxIndex}, bz1...), bz2...)
}

Expand Down
23 changes: 16 additions & 7 deletions rpc/backend/blocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,10 @@ func (b *Backend) RPCBlockFromTendermintBlock(
) (map[string]interface{}, error) {
ethRPCTxs := []interface{}{}
block := resBlock.Block

height, err := ethermint.SafeUint64(block.Height)
if err != nil {
return nil, err
}
baseFee, err := b.BaseFee(blockRes)
if err != nil {
// handle the error for pruned node.
Expand All @@ -398,12 +401,15 @@ func (b *Backend) RPCBlockFromTendermintBlock(
ethRPCTxs = append(ethRPCTxs, ethMsg.Hash())
continue
}

index, err := ethermint.SafeIntToUint64(txIndex)
if err != nil {
return nil, err
}
rpcTx, err := rpctypes.NewRPCTransaction(
ethMsg,
common.BytesToHash(block.Hash()),
uint64(block.Height),
uint64(txIndex),
height,
index,
baseFee,
b.chainID,
)
Expand Down Expand Up @@ -450,15 +456,18 @@ func (b *Backend) RPCBlockFromTendermintBlock(
b.logger.Error("failed to query consensus params", "error", err.Error())
}

gasUsed := uint64(0)

var gasUsed uint64
for _, txsResult := range blockRes.TxsResults {
// workaround for cosmos-sdk bug. https://github.com/cosmos/cosmos-sdk/issues/10832
if ShouldIgnoreGasUsed(txsResult) {
// block gas limit has exceeded, other txs must have failed with same reason.
break
}
gasUsed += uint64(txsResult.GetGasUsed())
gas, err := ethermint.SafeUint64(txsResult.GetGasUsed())
if err != nil {
return nil, err
}
gasUsed += gas
}

formattedBlock := rpctypes.FormatBlock(
Expand Down
16 changes: 9 additions & 7 deletions rpc/backend/chain_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,20 @@ import (

// ChainID is the EIP-155 replay-protection chain id for the current ethereum chain config.
func (b *Backend) ChainID() (*hexutil.Big, error) {
eip155ChainID, err := ethermint.ParseChainID(b.clientCtx.ChainID)
if err != nil {
panic(err)
}
// if current block is at or past the EIP-155 replay-protection fork block, return chainID from config
bn, err := b.BlockNumber()
if err != nil {
b.logger.Debug("failed to fetch latest block number", "error", err.Error())
return (*hexutil.Big)(eip155ChainID), nil
return (*hexutil.Big)(b.chainID), nil
}

config := b.ChainConfig()
if config == nil {
// assume eip-155 is enabled
return (*hexutil.Big)(b.chainID), nil
}

if config := b.ChainConfig(); config.IsEIP155(new(big.Int).SetUint64(uint64(bn))) {
if config.IsEIP155(new(big.Int).SetUint64(uint64(bn))) {
return (*hexutil.Big)(config.ChainID), nil
}

Expand Down Expand Up @@ -293,7 +295,7 @@ func (b *Backend) FeeHistory(
}
}
}
}(int32(value)) //nolint:gosec // checked
}(int32(value))
}
go func() {
wg.Wait()
Expand Down
13 changes: 10 additions & 3 deletions rpc/backend/node_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,17 @@ func (b *Backend) Syncing() (interface{}, error) {
if !status.SyncInfo.CatchingUp {
return false, nil
}

start, err := ethermint.SafeUint64(status.SyncInfo.EarliestBlockHeight)
if err != nil {
return false, err
}
current, err := ethermint.SafeUint64(status.SyncInfo.LatestBlockHeight)
if err != nil {
return false, err
}
return map[string]interface{}{
"startingBlock": hexutil.Uint64(status.SyncInfo.EarliestBlockHeight),
"currentBlock": hexutil.Uint64(status.SyncInfo.LatestBlockHeight),
"startingBlock": hexutil.Uint64(start),
"currentBlock": hexutil.Uint64(current),
// "highestBlock": nil, // NA
// "pulledStates": nil, // NA
// "knownStates": nil, // NA
Expand Down
48 changes: 37 additions & 11 deletions rpc/backend/tx_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ func (b *Backend) GetTransactionByHash(txHash common.Hash) (*rpctypes.RPCTransac
if err != nil {
return b.getTransactionByHashPending(txHash)
}

height, err := ethermint.SafeUint64(res.Height)
if err != nil {
return nil, err
}
block, err := b.TendermintBlockByNumber(rpctypes.BlockNumber(res.Height))
if err != nil {
return nil, err
Expand Down Expand Up @@ -79,18 +82,20 @@ func (b *Backend) GetTransactionByHash(txHash common.Hash) (*rpctypes.RPCTransac
if res.EthTxIndex == -1 {
return nil, errors.New("can't find index of ethereum tx")
}

index, err := ethermint.SafeInt32ToUint64(res.EthTxIndex)
if err != nil {
return nil, err
}
baseFee, err := b.BaseFee(blockRes)
if err != nil {
// handle the error for pruned node.
b.logger.Error("failed to fetch Base Fee from prunned block. Check node prunning configuration", "height", blockRes.Height, "error", err)
}

return rpctypes.NewTransactionFromMsg(
msg,
common.BytesToHash(block.BlockID.Hash.Bytes()),
uint64(res.Height),
uint64(res.EthTxIndex),
height,
index,
baseFee,
b.chainID,
)
Expand Down Expand Up @@ -171,14 +176,18 @@ func (b *Backend) GetTransactionReceipt(hash common.Hash) (map[string]interface{
return nil, err
}

cumulativeGasUsed := uint64(0)
var cumulativeGasUsed uint64
blockRes, err := b.TendermintBlockResultByNumber(&res.Height)
if err != nil {
b.logger.Debug("failed to retrieve block results", "height", res.Height, "error", err.Error())
return nil, nil
}
for _, txResult := range blockRes.TxsResults[0:res.TxIndex] {
cumulativeGasUsed += uint64(txResult.GasUsed)
gas, err := ethermint.SafeUint64(txResult.GasUsed)
if err != nil {
return nil, err
}
cumulativeGasUsed += gas
}
cumulativeGasUsed += res.CumulativeGasUsed

Expand All @@ -198,12 +207,16 @@ func (b *Backend) GetTransactionReceipt(hash common.Hash) (map[string]interface{
return nil, err
}

height, err := ethermint.SafeUint64(blockRes.Height)
if err != nil {
return nil, err
}
// parse tx logs from events
logs, err := evmtypes.DecodeMsgLogsFromEvents(
blockRes.TxsResults[res.TxIndex].Data,
blockRes.TxsResults[res.TxIndex].Events,
int(res.MsgIndex),
uint64(blockRes.Height),
height,
)
if err != nil {
b.logger.Debug("failed to parse logs", "hash", hash, "error", err.Error())
Expand All @@ -228,6 +241,14 @@ func (b *Backend) GetTransactionReceipt(hash common.Hash) (map[string]interface{
return nil, errors.New("can't find index of ethereum tx")
}

blockNumber, err := ethermint.SafeUint64(res.Height)
if err != nil {
return nil, err
}
transactionIndex, err := ethermint.SafeInt32ToUint64(res.EthTxIndex)
if err != nil {
return nil, err
}
receipt := map[string]interface{}{
// Consensus fields: These fields are defined by the Yellow Paper
"status": status,
Expand All @@ -244,8 +265,8 @@ func (b *Backend) GetTransactionReceipt(hash common.Hash) (map[string]interface{
// Inclusion information: These fields provide information about the inclusion of the
// transaction corresponding to this receipt.
"blockHash": common.BytesToHash(resBlock.Block.Header.Hash()).Hex(),
"blockNumber": hexutil.Uint64(res.Height),
"transactionIndex": hexutil.Uint64(res.EthTxIndex),
"blockNumber": hexutil.Uint64(blockNumber),
"transactionIndex": hexutil.Uint64(transactionIndex),

// sender and receiver (contract or EOA) addreses
"from": from,
Expand Down Expand Up @@ -433,10 +454,15 @@ func (b *Backend) GetTransactionByBlockAndIndex(block *tmrpctypes.ResultBlock, i
b.logger.Error("failed to fetch Base Fee from prunned block. Check node prunning configuration", "height", block.Block.Height, "error", err)
}

height, err := ethermint.SafeUint64(block.Block.Height)
if err != nil {
return nil, err
}

return rpctypes.NewTransactionFromMsg(
msg,
common.BytesToHash(block.Block.Hash()),
uint64(block.Block.Height),
height,
uint64(idx),
baseFee,
b.chainID,
Expand Down
12 changes: 10 additions & 2 deletions rpc/backend/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (

"github.com/cometbft/cometbft/proto/tendermint/crypto"
"github.com/evmos/ethermint/rpc/types"
ethermint "github.com/evmos/ethermint/types"
evmtypes "github.com/evmos/ethermint/x/evm/types"
feemarkettypes "github.com/evmos/ethermint/x/feemarket/types"
)
Expand Down Expand Up @@ -244,7 +245,10 @@ func (b *Backend) processBlock(
b.logger.Debug("failed to decode transaction in block", "height", blockHeight, "error", err.Error())
continue
}
txGasUsed := uint64(eachTendermintTxResult.GasUsed)
txGasUsed, err := ethermint.SafeUint64(eachTendermintTxResult.GasUsed)
if err != nil {
return err
}
for _, msg := range tx.GetMsgs() {
ethMsg, ok := msg.(*evmtypes.MsgEthereumTx)
if !ok {
Expand Down Expand Up @@ -290,9 +294,13 @@ func ShouldIgnoreGasUsed(res *abci.ExecTxResult) bool {

// GetLogsFromBlockResults returns the list of event logs from the tendermint block result response
func GetLogsFromBlockResults(blockRes *tmrpctypes.ResultBlockResults) ([][]*ethtypes.Log, error) {
height, err := ethermint.SafeUint64(blockRes.Height)
if err != nil {
return nil, err
}
blockLogs := [][]*ethtypes.Log{}
for _, txResult := range blockRes.TxsResults {
logs, err := evmtypes.DecodeTxLogsFromEvents(txResult.Data, txResult.Events, uint64(blockRes.Height))
logs, err := evmtypes.DecodeTxLogsFromEvents(txResult.Data, txResult.Events, height)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion rpc/namespaces/ethereum/debug/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ 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
return time.Duration(nsec) * time.Second, nil
}

// BlockProfile turns on goroutine profiling for nsec seconds and writes profile data to
Expand Down
7 changes: 5 additions & 2 deletions rpc/namespaces/ethereum/eth/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -448,13 +448,16 @@ func (e *PublicAPI) GetTransactionLogs(txHash common.Hash) ([]*ethtypes.Log, err
e.logger.Debug("block result not found", "number", res.Height, "error", err.Error())
return nil, nil
}

height, err := ethermint.SafeUint64(resBlockResult.Height)
if err != nil {
return nil, err
}
// parse tx logs from events
logs, err := evmtypes.DecodeMsgLogsFromEvents(
resBlockResult.TxsResults[res.TxIndex].Data,
resBlockResult.TxsResults[res.TxIndex].Events,
int(res.MsgIndex),
uint64(resBlockResult.Height),
height,
)
if err != nil {
e.logger.Debug("failed to parse tx logs", "error", err.Error())
Expand Down
7 changes: 6 additions & 1 deletion rpc/stream/rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/evmos/ethermint/rpc/types"
ethermint "github.com/evmos/ethermint/types"
evmtypes "github.com/evmos/ethermint/x/evm/types"
)

Expand Down Expand Up @@ -167,7 +168,11 @@ func (s *RPCStream) start(
s.logger.Error("event data type mismatch", "type", fmt.Sprintf("%T", ev.Data))
continue
}
txLogs, err := evmtypes.DecodeTxLogsFromEvents(dataTx.TxResult.Result.Data, dataTx.TxResult.Result.Events, uint64(dataTx.TxResult.Height))
height, err := ethermint.SafeUint64(dataTx.TxResult.Height)
if err != nil {
continue
}
txLogs, err := evmtypes.DecodeTxLogsFromEvents(dataTx.TxResult.Result.Data, dataTx.TxResult.Result.Events, height)
if err != nil {
s.logger.Error("fail to decode evm tx response", "error", err.Error())
continue
Expand Down
6 changes: 5 additions & 1 deletion rpc/types/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,11 @@ func ParseTxResult(result *abci.ExecTxResult, tx sdk.Tx) (*ParsedTxs, error) {

// some old versions miss some events, fill it with tx result
if len(p.Txs) == 1 {
p.Txs[0].GasUsed = uint64(result.GasUsed)
value, err := ethermint.SafeUint64(result.GasUsed)
if err != nil {
return nil, err
}
p.Txs[0].GasUsed = value
}

// this could only happen if tx exceeds block gas limit
Expand Down
Loading

0 comments on commit 452b0cf

Please sign in to comment.