Skip to content

fix(rpc): derived transaction support for tracing and block responses - #28

Merged
AryaLanjewar3005 merged 4 commits into
audit/evm-merge-tracefrom
audit-other-fixes
Jun 15, 2026
Merged

fix(rpc): derived transaction support for tracing and block responses#28
AryaLanjewar3005 merged 4 commits into
audit/evm-merge-tracefrom
audit-other-fixes

Conversation

@AryaLanjewar3005

Copy link
Copy Markdown
Collaborator

Description

fix(rpc): derived transaction support for tracing and block responses

Summary

Two bugs in the 0.5.0 merge caused derived transactions (EVM executions synthesized from non-EVM Cosmos messages) to fail in two separate RPC endpoints. Both regressions are present in audit/evm-merge-inconsistent-logs but not in feat/derived-tx-evm-0.4.0.


Bug 1 — debug_traceTransaction returns "invalid transaction v, r, s values" for derived txs

Root Cause

traceTx in x/vm/keeper/grpc_query.go calls core.TransactionToMessage(tx, signer, cfg.BaseFee) unconditionally. Derived transactions have no Ethereum signature — they are synthesized from Cosmos ABCI events and carry V=R=S=0. TransactionToMessage tries to recover the sender from these zero-value signature fields and fails with:

rpc error: code = Internal desc = invalid transaction v, r, s values

The predecessor loop (lines 552–560) was already correctly patched with an isUnsigned guard that calls unsignedTxAsMessage(from, tx, baseFee) instead of TransactionToMessage. However, traceTx itself — which processes the target transaction, not predecessors — was never updated with the same logic. The from common.Address parameter was passed in from the call site (common.BytesToAddress(req.Msg.GetFrom())) but was silently ignored inside traceTx.

Fix

x/vm/keeper/grpc_query.gotraceTx function:

Added the same isUnsigned check that already exists in the predecessor loop. When the target tx is unsigned (derived), construct the EVM message from the pre-populated from address directly instead of attempting signature recovery.

// Before
msg, err := core.TransactionToMessage(tx, signer, cfg.BaseFee)
if err != nil {
    return nil, 0, status.Error(codes.Internal, err.Error())
}
return k.traceTxWithMsg(ctx, cfg, txConfig, msg, traceConfig, commitMessage)

// After
var msg *core.Message
if isUnsigned(tx) {
    m := unsignedTxAsMessage(from, tx, cfg.BaseFee)
    msg = &m
} else {
    var err error
    msg, err = core.TransactionToMessage(tx, signer, cfg.BaseFee)
    if err != nil {
        return nil, 0, status.Error(codes.Internal, err.Error())
    }
}
return k.traceTxWithMsg(ctx, cfg, txConfig, msg, traceConfig, commitMessage)

Bug 2 — eth_getBlockByHash returns 0 transactions when the block contains only derived txs

Root Cause

The 0.5.0 merge split block building across two functions:

  • EthBlockFromCometBlock — builds a proper *ethtypes.Block. It intentionally excludes derived txs from the block body (correct: the Ethereum block trie should only contain signed native EVM txs for hash integrity). The comment at that line reads "exclude derived txs from the ETH block body".
  • RPCMarshalBlock — converts the ethBlock to the JSON-RPC response. It builds the "transactions" field from block.Transactions(), which are the transactions in the ethBlock body — i.e., only native EVM txs. Derived txs are gone.

RPCBlockFromCometBlock calls both and passes a msgs slice (which does include derived txs) to RPCMarshalBlock, but RPCMarshalBlock never reads msgs — it only reads block.Transactions().

In feat/derived-tx-evm-0.4.0, RPCBlockFromCometBlock built the transaction list manually from msgs + txsAdditional, using the ABCI event-assigned hash for each derived tx. That path was lost in the 0.5.0 restructure.

Fix

rpc/backend/comet_to_eth.goRPCBlockFromCometBlock function:

After RPCMarshalBlock returns the base block fields, override the "transactions" field with a list built from msgs + txsAdditional:

  • For hash-only responses (fullTx=false): use the ABCI event hash for derived txs and ethMsg.Hash() for native EVM txs.
  • For full-tx responses (fullTx=true): use NewTransactionFromMsg for native EVM txs and NewRPCTransactionFromIncompleteMsg (with the event hash) for derived txs.

This restores the behaviour from the working branch while keeping EthBlockFromCometBlock correct (derived txs remain excluded from the actual *ethtypes.Block body used for hash computation).


Files Changed

File Change
x/vm/keeper/grpc_query.go Added isUnsigned guard in traceTx to use unsignedTxAsMessage for derived txs instead of failing on signature recovery
rpc/backend/comet_to_eth.go RPCBlockFromCometBlock now overrides the "transactions" field using msgs+txsAdditional so derived txs appear in block responses

Testing

All 11 existing BackendTestSuite/TestTraceTransaction* unit tests pass (-tags=test). The two fixes are independently verifiable by tracing a derived-tx hash and by calling eth_getBlockByHash on a block that contains only non-EVM Cosmos txs that triggered EVM executions.


Author Checklist

All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.

I have...

  • tackled an existing issue or discussed with a team member
  • left instructions on how to review the changes
  • targeted the main branch

@github-actions github-actions Bot added the tests label Jun 14, 2026
@AryaLanjewar3005
AryaLanjewar3005 changed the base branch from audit/evm-merge to audit/evm-merge-trace June 15, 2026 09:28
@AryaLanjewar3005
AryaLanjewar3005 merged commit 25adde0 into audit/evm-merge-trace Jun 15, 2026
10 of 21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant