[F-2026-17740][Re-eval] KV indexer path misses derived transactions#25
Open
AryaLanjewar3005 wants to merge 2 commits into
Open
[F-2026-17740][Re-eval] KV indexer path misses derived transactions#25AryaLanjewar3005 wants to merge 2 commits into
AryaLanjewar3005 wants to merge 2 commits into
Conversation
…ugh event-based log parsing
…additional fields on KV hit
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
KV Indexer: Derived Tx Support — Design and Port Notes
Background
Finding: F-2026-17740 — KV indexer path misses derived transactions
The KV indexer (
indexer/kv_indexer.go) skipped all non-eth Cosmos transactions duringIndexBlock. Derived EVM txs — internal EVM executions triggered by Cosmos messages (e.g., IBC transfers, Universal Executor payloads) that emitethereum_txandtx_logABCI events but carry no embeddedMsgEthereumTx— were therefore invisible to the indexer.When a client queried
eth_getTransactionByHashoreth_getTransactionReceiptfor a derived tx hash while the KV indexer was active, the lookup returned an error immediately instead of falling through to the CometBFTtx_searchpath. The derived tx was unreachable via the JSON-RPC API even though it existed on chain.The original fix was authored on the
audit-fixesbranch (targeting the 0.2.0 architecture). This document describes how the fix works and what changed when porting it to the 0.5.0 architecture onaudit/evm-merge-kv-index.How the Fix Works
1. KV indexer: index derived txs during
IndexBlockIndexBlockiterates every Cosmos tx in a block. Previously, non-eth txs hitcontinueimmediately. The fix inserts a call toindexDerivedTxsbefore thatcontinue:indexDerivedTxscallsrpctypes.ParseTxResult(result, nil)(passingnilfor the SDK tx because there is no embeddedMsgEthereumTxto decode) and iterates the parsed entries looking forDerivedTxType. For each one:TxResultis written viasaveTxResult(same schema as native txs: hash key + block-index key).DerivedTxHashKey(hash)is written in the same batch, committing atomically.ethTxIndexcounter is incremented once per derived tx — keeping it aligned with the counter the keeper emits for both derived and standard txs in the same block (fixed by F-2026-17745).2. New marker key and
IsDerivedTx/IsDerivedTxByBlockAndIndexA third key prefix is added alongside
KeyPrefixTxHash = 1andKeyPrefixTxIndex = 2:DerivedTxHashKey(hash)returns[]byte{3} + hash.Bytes(). The value is a single sentinel byte{1}.Two new interface methods gate the (more expensive) event-reparse behind a single cheap key read:
IsDerivedTx(hash)— checksDerivedTxHashKey(hash)directly.IsDerivedTxByBlockAndIndex(height, txIndex)— reads the block-index entry to get the hash, then delegates toIsDerivedTx. Two key reads total.3. RPC backend: rebuild
TxResultAdditionalFieldson KV hitThe KV indexer stores only
TxResult(height, txIndex, msgIndex, ethTxIndex, gasUsed, failed). For standard txs this is enough — callers decode theMsgEthereumTxdirectly from the block. For derived txs there is noMsgEthereumTxto decode; the RPC backend panics trying to cast.derivedTxAdditionalFields(hash, res)solves this:IsDerivedTx(hash)— returns(nil, nil)cheaply for standard txs.buildDerivedAdditional(res), which:BlockResultsforres.Height.ParseTxResulton the relevantTxsResults[res.TxIndex]entry.res.MsgIndex(the positional index stored during indexing).TxResultAdditionalFieldsfrom the parsed event fields.GetTxByEthHashandGetTxByEthHashAndMsgIndexcallderivedTxAdditionalFieldson every KV hit and return the result alongside theTxResult.GetTxByTxIndexusesIsDerivedTxByBlockAndIndexthenbuildDerivedAdditionalon the block-index path, so trace predecessors are reconstructed correctly.4. KV miss: fall through to CometBFT
tx_searchBefore the fix, a KV indexer error on lookup immediately propagated to the caller. After the fix,
err != nilon a KV lookup is treated as a miss and falls through to the CometBFTtx_searchpath. This preserves correctness for blocks indexed before the fix was deployed — derived txs in those blocks have no KV entries but are still reachable via event-based search.Differences Between 0.2.0 (
audit-fixes) and 0.5.0 (audit/evm-merge-kv-index)Type and package renames
audit-fixes)audit/evm-merge-kv-index)TxResulttypecosmosevmtypes.TxResult(fromgithub.com/cosmos/evm/types)servertypes.TxResult(fromgithub.com/cosmos/evm/server/types)EVMTxIndexerinterface filetypes/indexer.goserver/types/indexer.govar _ cosmosevmtypes.EVMTxIndexer = &KVIndexer{}var _ servertypes.EVMTxIndexer = &KVIndexer{}In 0.5.0 the monolithic
types/package was split and server-specific types (includingTxResultandEVMTxIndexer) moved underserver/types/. The indexer imports and struct constructors were updated accordingly.Backend receiver field names
The
Backendstruct in 0.2.0 uses unexported fields; 0.5.0 promotes them to exported:b.rpcClientb.RPCClientb.ctxb.Ctxb.indexerb.IndexerAll three appear in
buildDerivedAdditionaland theGetTxBy*lookup functions. Every reference was updated.buildDerivedAdditionalfactored out as a separate functionIn 0.2.0,
derivedTxAdditionalFieldscontained both theIsDerivedTxmarker check and the fullBlockResults+ParseTxResultreconstruction logic in a single function.In 0.5.0, the reconstruction logic was extracted into
buildDerivedAdditional.derivedTxAdditionalFieldsnow only does theIsDerivedTxgate and delegates tobuildDerivedAdditional. This is required becauseGetTxByTxIndexusesIsDerivedTxByBlockAndIndex(notIsDerivedTx) and needs to call the reconstruction step directly without re-doing the marker check.IsDerivedTxByBlockAndIndexadded to the interfaceThe 0.2.0 commit (
970f1c1b/cf0237af) adds onlyIsDerivedTxto theEVMTxIndexerinterface. TheIsDerivedTxByBlockAndIndexmethod (used byGetTxByTxIndex) was added in a subsequent commit in the same PR chain and squashed into the final merge commit4b0cc66e.In 0.5.0, both methods land together in
server/types/indexer.gofrom the start, reflecting the complete interface contract.CometBFT tx_search fallback — already present in the original
Both the 0.2.0 commit and the 0.5.0 port treat a KV indexer miss as fall-through to
tx_search. The comment changed from// indexer miss — fall through to event-query (tx_search) reconstructionto// Indexer miss — fall through to CometBFT tx_search for derived tx reconstruction.The behavior is identical; the 0.5.0 version uses "CometBFT" consistently (Tendermint was renamed).Method name:
queryTendermintTxIndexer→QueryCometTxIndexerThe CometBFT fallback in
GetTxByEthHashcallsb.queryTendermintTxIndexerin 0.2.0 andb.QueryCometTxIndexerin 0.5.0. Same function, renamed as part of the broader Tendermint → CometBFT migration in 0.5.0.MockIndexerin tests0.5.0 unit tests (
tx_info_test.go) use aMockIndexerstruct. Adding the two new interface methods required two stub implementations:These are no-ops (always returning
false) because the mock is used for standard-tx test paths only.Summary of All Changed Files
indexer/kv_indexer.goKeyPrefixTxDerivedconstant;indexDerivedTxs;IsDerivedTx;IsDerivedTxByBlockAndIndex;DerivedTxHashKey;IndexBlockwired to callindexDerivedTxsserver/types/indexer.goIsDerivedTxandIsDerivedTxByBlockAndIndexadded toEVMTxIndexerinterfacerpc/backend/tx_info.goderivedTxAdditionalFields;buildDerivedAdditional;GetTxByEthHashKV hit path;GetTxByEthHashAndMsgIndexKV hit path;GetTxByTxIndexderived-reconstruction branch; KV miss fall-through on all threerpc/backend/tx_info_test.goIsDerivedTxandIsDerivedTxByBlockAndIndexstubs added toMockIndexerCloses: #XXXX
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...
mainbranch