cosmos / cosmos/evm

[Bug] eth_getBlockReceipts("latest") pairs the txs of block H with the results of block H+1 (panic / wrong receipts)

Open Beginner friendly
#1,280 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Go
Stars
164
Forks
213
Avg merge
3d 58m
Merged PRs (30d)
12

Description

## Type

- [x] Bug
- [ ] Feature
- [ ] Proposal / Discussion

## Summary

`eth_getBlockReceipts("latest")` resolves the block by a concrete height H, but then fetches the block results with `blockNum.CmtHeight()`, which is `nil` for `latest` and makes CometBFT return the results of its *current* head. If a block is committed between the two calls, the txs of block H are paired with the results of block H+1:

https://github.com/cosmos/evm/blob/469142db41aee479102cf878f8ac2bbe2b0c7a22/rpc/backend/blocks.go#L210-L224

```go
resBlock, err := b.CometBlockByNumber(ctx, blockNum) // block H
...
blockRes, err := b.RPCClient.BlockResults(ctx, blockNum.CmtHeight()) // nil -> latest, may be H+1
...
msgs := b.EthMsgsFromCometBlock(ctx, resBlock, blockRes) // block.Txs[i] <-> txResults[i]
```

`EthMsgsFromCometBlock` indexes `txResults[i]` for every tx of block H: if H+1 has fewer txs it panics (index out of range, surfaced to the client as `method handler crashed`); if it has as many or more, the receipts come back with the status/gasUsed/logs of the wrong block. Every other block query in the backend (`GetBlockByNumber`, `HeaderByNumber`, `getBlockTransactionCount`, ...) uses `&resBlock.Block.Height`; this is the only one that forwards the sentinel. The `resBlock == nil` branch also dereferences `*blockNum.CmtHeight()`, which is nil for `latest`.

## Reproduction (for bugs)

Deterministic repro with the backend mocks (`tests/integration/rpc/backend`), `main` (`469142d`): register `Block(&10)` returning a block with one eth tx, and `BlockResults(nil)` returning an empty result set for height 11 (a newer block):

```go
client.EXPECT().Block(mock.Anything, mock.MatchedBy(func(h *int64) bool { return h != nil && *h == 10 })).
Return(&cmtrpctypes.ResultBlock{Block: blockWithOneTx}, nil)
client.EXPECT().BlockResults(mock.Anything, mock.MatchedBy(func(h *int64) bool { return h == nil })).
Return(&cmtrpctypes.ResultBlockResults{Height: 11, TxsResults: []*abci.ExecTxResult{}}, nil)

latest := rpctypes.EthLatestBlockNumber
_, err := s.backend.GetBlockReceipts(s.Ctx(), rpctypes.BlockNumberOrHash{BlockNumber: &latest})
```

```
panic: runtime error: index out of range [0] with length 0
rpc/backend/comet_to_eth.go EthMsgsFromCometBlock
```

On a live node this shows up as sporadic `method handler crashed` responses from `eth_getBlockReceipts("latest")` on chains with ~1s blocks, or receipts whose `gasUsed`/`status` do not match `eth_getTransactionReceipt`.

## Impact

Indexers and explorers typically poll `eth_getBlockReceipts("latest")`; they either get a 500 or, worse, silently wrong receipts. One-line fix (`BlockResults(ctx, &resBlock.Block.Height)`) plus a nil-block guard; PR with a regression test ready.

## Related

- #1047 (previous receipt fix in the same code path)

## Checklist

- [x] Linked to a GitHub Issue (or this is the Issue)
- [x] Repro steps included (for bugs)
- [x] Impact described
- [x] I understand minor typo/style doc fixes will not be accepted

Contributor guide

Open the contributing guide

Research direction

Start in rpc/backend/blocks.go at GetBlockReceipts and compare its block-results height handling with the other block queries. Review EthMsgsFromCometBlock in rpc/backend/comet_to_eth.go and the backend integration mocks, then run the relevant tests. Done means latest receipts use the resolved block height, nil results are guarded, and the regression no longer panics or mismatches transactions and results.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
api, backend
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
88/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.