Skip to content

fix(rpc): decode EVM logs from tx data + align receipt/nonce fields with cosmos/evm v0.6.0#292

Merged
puneet2019 merged 12 commits into
mainfrom
fix/rpc-evm-logs
Jul 3, 2026
Merged

fix(rpc): decode EVM logs from tx data + align receipt/nonce fields with cosmos/evm v0.6.0#292
puneet2019 merged 12 commits into
mainfrom
fix/rpc-evm-logs

Conversation

@puneet2019

@puneet2019 puneet2019 commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

What & why

Part of the cosmos/evm v0.6.0 migration review (#278). Fixes BLOCKER 2 plus the three grouped RPC concerns, all verified against cosmos/evm@v0.6.0 rpc/backend/.

BLOCKER 2 — eth_getLogs / receipt logs / logsBloom / WS log subscriptions always empty

In cosmos/evm v0.6.0, AttributeKeyTxLog is defined but emitted nowhere — EVM logs now live in the tx response Data (a marshalled MsgEthereumTxResponse). The migration kept the old event-parsing extraction and merely repointed EventTypeTxLog → EventTypeEthereumTx, so every log consumer saw empty results (breaking wallets, indexers, The Graph, DeFi).

Fix (mirrors upstream rpc/backend/utils.go + tx_info.go):

  • GetLogsFromBlockResultsevmtypes.DecodeTxLogs(txResult.Data, height) (one entry per tx).
  • Receipt log path (tx_info.go) and eth_getTransactionLogs (eth namespace) → evmtypes.DecodeMsgLogs(txResult.Data, msgIndex, height).
  • Removed the now-dead helpers AllTxLogsFromEvents / TxLogsFromEvents / ParseTxLogsFromEvent.

RPC concerns (grouped here per the review's PR plan)

  1. Receipt signer (tx_info.go): branch like upstream — protected → LatestSignerForChainID(ethTx.ChainId()), unprotected → ethtypes.FrontierSigner{} (their ChainId() is 0, so a chain-id signer fails to recover the sender). Removes the prior unconditional LatestSignerForChainID(b.ChainID()).
  2. effectiveGasPrice (tx_info.go): now set on all receipt types (legacy/access-list = tx.GasPrice(); dynamic-fee = baseFee + min(gasTipCap, gasFeeCap - baseFee)), matching geth/upstream RPCMarshalReceipt. Previously only dynamic-fee receipts carried it. (Also fixes the empty-logs placeholder type [][]*Log{}[]*Log{}.)
  3. getAccountNonce pending signer (utils.go): use the precomputed b.chainID instead of the redundant b.ChainID() RPC, which — before the EIP-155 fork height — returns an error and makes us silently skip counting pending txs. Mirrors upstream, which signs with b.EvmChainID here.

Note: b.chainID is moca's de-facto signing chain id throughout the RPC layer (call_tx / sign_tx / tracing). The separate cosmos-vs-EVM chain-id default question is tracked in #288 and is out of scope here.

Added types.SafeUint64 (mirrors the existing types.SafeInt64) for the int64 → uint64 height conversion that upstream does via utils.SafeUint64.

Tests

Unit (rpc/backend, rpc/types):

  • TestGetLogsFromBlockResults (utils_test.go): crafts a ResultBlockResults whose TxsResults[i].Data is a marshalled MsgEthereumTxResponse with a log; asserts the decoded logs are non-empty and correct (address / data / blockNumber / txHash), and that empty Data yields empty logs without error.
  • TestGetTransactionReceiptFields (tx_info_test.go): directly asserts the receipt fields this PR fixes — populated logs / logsBloom, recovered from, effectiveGasPrice on a legacy receipt (== gasPrice), status, and type.
  • DecodeMsgLogs index-selection coverage for the Cosmos-msg-index → EVM-response-index mapping (the mixed [non-EVM, MsgEthereumTx] case from review); the all-EVM-tx invariant is documented.
  • Updated TestGetLogs + mock (RegisterBlockResultsWithEventLog) to carry logs in tx Data (the cosmos/evm v0.6.0 form) instead of events.

E2E (e2e/kind/tests/test_rpc_suite.sh, against a live kind chain):

  • eth_newFilter / eth_getFilterChanges rehydration + eth_getLogs.
  • eth_subscribe("logs") over the WebSocket transport (first-party go-ethereum client, e2e/kind/tools/wslogs).
  • Both log paths assert the returned log carries the finalized block context this PR restores — transactionHash (== the emitting tx), blockHash (== the finalized block), and a non-zero blockTimestamp — so the tests fail on the pre-fix behavior, not just on address/topics.

Scope

Fixes BLOCKER 2 + the RPC concerns from #278. Does not touch BLOCKER 1 (precompile value guard), BLOCKER 3 (feemarket min_gas_price), or the storage/nit cleanups — those are separate stacked PRs.

🤖 Generated with Claude Code

@puneet2019 puneet2019 force-pushed the fix/rpc-evm-logs branch 2 times, most recently from 82616a8 to 041dd8f Compare June 30, 2026 04:24
@puneet2019 puneet2019 requested a review from phenix3443 June 30, 2026 04:55
Base automatically changed from chore/cosmos-evm-add to main June 30, 2026 05:10
@puneet2019 puneet2019 requested a review from a team June 30, 2026 05:10

@phenix3443 phenix3443 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One blocker remains in the new log-decoding path.

DecodeMsgLogs is being called with res.MsgIndex, which is the message index in the full Cosmos tx. The decoder, however, indexes into the filtered MsgEthereumTxResponse list stored in TxMsgData. Those only line up when the EVM message is also at Cosmos message position 0. For mixed Cosmos txs with a non-EVM message before the EVM message, this now errors and receipt / eth_getTransactionLogs logs become empty again.

Please remap that index to the EVM-response position (or copy the exact upstream logic) and add a regression test for a tx shaped like [non-EVM msg, MsgEthereumTx].

Comment thread rpc/backend/tx_info.go

@phenix3443 phenix3443 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous MsgIndex concern is resolved, but I still see one blocking issue and one remaining gap.

Blocking:

  • golangci is still failing on new issues introduced in rpc/backend/utils_test.go: two gosec G115 findings for the int64 -> uint64 conversions and one misspell (marshalled). This needs to be fixed before the PR is merge-ready.

Non-blocking but still worth tightening:

  • The new decoder/unit tests are good, but TestGetTransactionReceipt still does not assert the receipt fields this PR claims to fix (logs, logsBloom, signer recovery, effectiveGasPrice). Adding one direct receipt-path assertion would make this much safer to maintain.

Comment thread rpc/backend/utils_test.go Outdated
…mos/evm v0.6.0

cosmos/evm v0.6.0 no longer emits per-log cosmos events (AttributeKeyTxLog
is defined but emitted nowhere); EVM logs are carried in the tx response
Data as a marshalled MsgEthereumTxResponse. The migration kept the old
event-parsing extraction (just repointing EventTypeTxLog -> EventTypeEthereumTx),
so eth_getLogs, receipt logs/logsBloom, and WS log subscriptions were always
empty.

- GetLogsFromBlockResults: decode via evmtypes.DecodeTxLogs(txResult.Data, height)
- Receipt + eth_getTransactionLogs per-msg path: evmtypes.DecodeMsgLogs(Data, msgIndex, height)
- Remove now-dead event-parsing helpers (AllTxLogsFromEvents/TxLogsFromEvents/ParseTxLogsFromEvent)

Also align three RPC concerns with cosmos/evm v0.6.0:
- receipt signer: chain-id signer for protected txs, FrontierSigner for unprotected
- effectiveGasPrice: set on all receipt types (legacy/access-list = GasPrice())
- getAccountNonce: use the precomputed b.chainID instead of the redundant
  b.ChainID() RPC that silently skips pending-tx counting pre-EIP155

Add types.SafeUint64 and a unit test for GetLogsFromBlockResults; update the
TestGetLogs mock to carry logs in tx Data.

Signed-off-by: puneetmahajan <59960662+puneet2019@users.noreply.github.com>
…nvariant

Addresses review feedback on #292. A MsgEthereumTx is only ever included in
an all-EVM tx: routing into the EVM ante requires the ExtensionOptionsEthereumTx
option, the EVM ante decorators reject any non-EVM message, and the cosmos
ante's RejectMessagesDecorator rejects MsgEthereumTx elsewhere. So res.MsgIndex
equals the EVM message's position in the filtered MsgEthereumTxResponse slice
that DecodeMsgLogs indexes into -- matching cosmos/evm v0.6.0 rpc/backend.

- Add TestDecodeMsgLogsByMsgIndex covering the only multi-message shape moca
  permits (two MsgEthereumTx): MsgIndex 0 and 1 each select the correct
  response's logs.
- Document the invariant at both decode sites (receipt + eth_getTransactionLogs).

Signed-off-by: puneetmahajan <59960662+puneet2019@users.noreply.github.com>
The buildLogTxResultData helper cast the height int64 to uint64 directly
(twice), tripping gosec G115, and its doc comment misspelled "marshalled".
Convert height once via mocatypes.SafeUint64 — mirroring the production
rpc/backend/utils.go convention — and fix the spelling, so golangci is green.
eth_subscribe("logs") and eth_getFilterChanges delivered logs with correct
content (address/topics/data) but zeroed TxHash, BlockHash and BlockTimestamp:
they decoded logs from the live CometBFT tx event Data, which does not carry the
tx/block context (BlockHash is not known at execution time — it is backfilled
into the finalized block result).

Source each tx's logs from the finalized block result instead — via
GetLogsByHeight (filter API) / GetLogsFromBlockResults (websockets), indexed by
the tx's block position — matching the eth_getLogs path. Subscribers and pollers
now receive fully-populated logs that correlate to their tx and block, mirroring
cosmos/evm v0.6.0 subscription semantics.

Verified live: eth_subscribe and eth_getFilterChanges return the canonical
txHash and blockHash (matches eth_getLogs and the block header).
@phenix3443

Copy link
Copy Markdown
Contributor

Re-reviewed this against the current diff and the matching cosmos/evm v0.6.0 paths. I did not find a blocking issue in the tx-data log decoding, receipt field alignment, or the MsgIndex-based log selection.

One residual risk remains around end-to-end coverage for the live subscription paths (eth_subscribe("logs") / eth_getFilterChanges), since they now rehydrate logs from finalized block results instead of the tx event payload. I do not see a concrete bug there in the current implementation, but that is still the area I would watch most closely.

staticcheck QF1008 flagged the promoted-field accesses added with the EVM
log-rehydration handlers (eth_subscribe logs, eth_getFilterChanges): dataTx is a
tmtypes.EventDataTx which embeds abci.TxResult, so use the promoted dataTx.Height
/ dataTx.Index directly. No behavior change.

Signed-off-by: puneetmahajan <59960662+puneet2019@users.noreply.github.com>
Deploy TestERC20, open a logs filter, emit a Transfer, and assert the receipt
logs, eth_getFilterChanges (the live NewFilter poller) and eth_getLogs all
surface the decoded log from the finalized-block-result rehydration
(GetLogsByHeight / GetLogsFromBlockResults) -- the path called out in review for
eth_subscribe("logs") / eth_getFilterChanges. Full RPC suite green locally (9/9).

Signed-off-by: puneetmahajan <59960662+puneet2019@users.noreply.github.com>
Resolve CHANGELOG Unreleased > Bug Fixes conflict: keep both the #292 (rpc) and #287 (payment) entries. Code (rpc/*, types/*) merged cleanly; build + rpc/types tests green.

Signed-off-by: puneetmahajan <59960662+puneet2019@users.noreply.github.com>
@phenix3443

Copy link
Copy Markdown
Contributor

Re-reviewed the current diff in an isolated worktree and re-ran go test ./rpc/... ./types/... successfully.

I did not find a new blocking correctness issue in the tx-data log decoding, receipt field alignment, or the MsgIndex-based selection after the follow-up fixes.

One remaining gap I still see is test coverage:

  • The new e2e added in e2e/kind/tests/test_rpc_suite.sh validates eth_newFilter / eth_getFilterChanges and eth_getLogs, but it does not actually exercise the eth_subscribe("logs") websocket path in rpc/websockets.go that this PR also changed.
  • rpc/backend/tx_info_test.go still does not directly assert the receipt fields this PR fixes (logs, logsBloom, signer recovery, effectiveGasPrice), so the receipt-path behavior is still lightly guarded.

I’m treating those as follow-up coverage gaps rather than blockers for this PR.

Add TestGetTransactionReceiptFields, a passing receipt test that directly asserts the fields reworked in the cosmos/evm v0.6.0 migration: logs decoded from the tx response Data, the derived logsBloom, GetSenderLegacy sender recovery, effectiveGasPrice (now populated on legacy receipts), and status/type. Previously only a failing NotEqual case existed.

Signed-off-by: puneetmahajan <59960662+puneet2019@users.noreply.github.com>
Add e2e/kind/tools/wslogs (a first-party go-ethereum SubscribeFilterLogs client; no external ws CLI) and test_evm_ws_log_subscription: subscribe over WS, emit a Transfer, assert the pushed log -- exercising the rpc/websockets.go subscribeLogs push path that the eth_getFilterChanges/eth_getLogs HTTP tests don't reach. Adds a gated Go setup step to the RPC-suite CI job for the host-side tool build. Validated 10/10 in a local kind run.

Signed-off-by: puneetmahajan <59960662+puneet2019@users.noreply.github.com>
…-tag)

Semgrep's 'Scan' gate blocks newly-introduced mutable action tags on the PR diff. Pin the RPC-suite job's setup-go to the v5.6.0 commit SHA (identical to what @v5 resolves to today) to clear the finding while keeping the explicit Go setup the WS e2e's host-side build relies on.

Signed-off-by: puneetmahajan <59960662+puneet2019@users.noreply.github.com>
@puneet2019 puneet2019 dismissed phenix3443’s stale review July 2, 2026 10:57

fixed all issues

puneet2019 added a commit that referenced this pull request Jul 2, 2026
Delete moca's ~16k-LOC forked rpc/ tree and serve the EVM JSON-RPC from
github.com/cosmos/evm/rpc (v0.6.0): backend, namespaces, and the
newHeads/logs/pendingTx subscriptions now come from cosmos/evm, fed by an
in-process CometBFT event stream and an ante PendingTxListener hook on *Moca.
The indexer moves to cosmos/evm's server/types.TxResult.

The only retained moca RPC code is a thin server/websockets.go shim: a copy of
cosmos/evm's WS server with newHeads emitting the canonical CometBFT block hash
(moca #232 / issue #229) instead of the derived eth-header hash, completing the
half-fix in cosmos/evm#725 (the canonical hash is carried on the stream but
unused in newHeads upstream). Removable once that lands and we bump.

Also drop now-unused config keys (query-timeout, getlogs-rate-limit,
getlogs-burst-limit, fix-revert-gas-refund-height) and the dead
ethermint.types.v1.TxResult / EVMTxIndexer types; add json-rpc.ws-origins.

Depends on #292 (WS/getFilterChanges e2e gate); merge after #292.

Verified: go build ./... green; e2e RPC suite 10/10 (incl. eth_subscribe logs).
Signed-off-by: puneetmahajan <59960662+puneet2019@users.noreply.github.com>
@phenix3443

Copy link
Copy Markdown
Contributor

Re-reviewed the latest head (c8f9c352) and re-ran go test ./rpc/... ./types/... plus go build ./e2e/kind/tools/wslogs in an isolated worktree.

I do not see a new blocking correctness issue in the current code, but I do still see one important coverage gap in the new e2e additions:

  • test_evm_ws_log_subscription now exercises the real eth_subscribe("logs") transport, which is good, but it only asserts address and topics[0].
  • test_evm_log_subscription does the same for eth_getFilterChanges.

The bug this PR fixed for the subscription/filter paths was not that address/topics/data were wrong; it was that the pushed logs were missing finalized block context (txHash, blockHash, blockTimestamp) because they were sourced from the live tx event payload instead of the finalized block result.

So with the current assertions, the pre-fix broken behavior could still satisfy both new tests. I would strongly prefer at least one assertion in each path that the returned log carries canonical finalized context, e.g. non-zero / expected txHash and blockHash (and optionally blockTimestamp).

@phenix3443

Copy link
Copy Markdown
Contributor

Re-reviewed the PR again on the current head (c8f9c352); there do not appear to be any new commits since the last pass.

I still do not see a new correctness bug in the implementation itself, but the same coverage gap remains in the new e2e tests:

  • test_evm_ws_log_subscription only asserts address and topics[0].
  • test_evm_log_subscription / eth_getFilterChanges does the same.

That is weaker than the bug this PR actually fixed. The regression here was not that address/topics/data were wrong; it was that the subscription/filter logs were missing finalized tx/block context (txHash, blockHash, blockTimestamp) because they were being sourced from the live tx event payload instead of the finalized block result.

So the current tests could still stay green against the old broken behavior. I still strongly recommend asserting at least canonical txHash and blockHash in both paths (and optionally blockTimestamp) so the new coverage matches the actual fix.

The WS (eth_subscribe logs) and getFilterChanges e2e tests asserted only address + topics[0], which the pre-fix behavior also satisfied. Assert the returned log carries the finalized block context this PR restores — transactionHash (== the mint tx), blockHash (== the finalized block), and a non-zero blockTimestamp — so the tests now fail on the pre-fix code. Closes the review coverage gap.

Signed-off-by: puneetmahajan <59960662+puneet2019@users.noreply.github.com>
@puneet2019 puneet2019 merged commit 8ad01d8 into main Jul 3, 2026
31 checks passed
@puneet2019 puneet2019 deleted the fix/rpc-evm-logs branch July 3, 2026 08:29
puneet2019 added a commit that referenced this pull request Jul 3, 2026
Delete moca's ~16k-LOC forked rpc/ tree and serve the EVM JSON-RPC from
github.com/cosmos/evm/rpc (v0.6.0): backend, namespaces, and the
newHeads/logs/pendingTx subscriptions now come from cosmos/evm, fed by an
in-process CometBFT event stream and an ante PendingTxListener hook on *Moca.
The indexer moves to cosmos/evm's server/types.TxResult.

The only retained moca RPC code is a thin server/websockets.go shim: a copy of
cosmos/evm's WS server with newHeads emitting the canonical CometBFT block hash
(moca #232 / issue #229) instead of the derived eth-header hash, completing the
half-fix in cosmos/evm#725 (the canonical hash is carried on the stream but
unused in newHeads upstream). Removable once that lands and we bump.

Also drop now-unused config keys (query-timeout, getlogs-rate-limit,
getlogs-burst-limit, fix-revert-gas-refund-height) and the dead
ethermint.types.v1.TxResult / EVMTxIndexer types; add json-rpc.ws-origins.

Depends on #292 (WS/getFilterChanges e2e gate); merge after #292.

Verified: go build ./... green; e2e RPC suite 10/10 (incl. eth_subscribe logs).
Signed-off-by: puneetmahajan <59960662+puneet2019@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants