Skip to content
Merged
Show file tree
Hide file tree
Changes from 49 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
66ad361
fix: evm tx index and log indexes are not patched
mmsqe Nov 7, 2025
68ef5bc
patch
mmsqe Nov 7, 2025
f5c9459
cleanup ResetTransientGasUsed
mmsqe Nov 7, 2025
2440987
add fallback
mmsqe Nov 7, 2025
b49d6be
more test
mmsqe Nov 7, 2025
64209c6
Merge branch 'main' into patch
aljo242 Nov 7, 2025
13bca57
Merge branch 'main' into patch
mmsqe Nov 12, 2025
0c9db23
Merge branch 'main' into patch
aljo242 Nov 12, 2025
108b10d
Merge branch 'main' into patch
mmsqe Nov 13, 2025
d566083
Merge branch 'main' into patch
aljo242 Nov 14, 2025
ef90267
Merge branch 'main' into patch
aljo242 Nov 14, 2025
909cd0c
Merge branch 'main' into patch
aljo242 Nov 19, 2025
7bea253
Merge branch 'main' into patch
aljo242 Nov 19, 2025
dfa7810
add doc
mmsqe Nov 20, 2025
5440a1b
Merge remote-tracking branch 'origin/main' into patch
mmsqe Nov 20, 2025
1d0ccfb
only overwrite tx index not found
mmsqe Nov 20, 2025
8019fd0
Merge branch 'main' into patch
aljo242 Nov 21, 2025
0af6bcb
cleanup
mmsqe Nov 22, 2025
c2c5f30
Merge remote-tracking branch 'origin/main' into patch
mmsqe Nov 22, 2025
977a5df
bump sdk
mmsqe Nov 26, 2025
a436262
fix lint later
mmsqe Nov 19, 2025
4a5f69f
Merge branch 'main' into patch
aljo242 Dec 4, 2025
91fa89a
Merge remote-tracking branch 'origin/main' into patch
mmsqe Dec 18, 2025
cddc9cb
Merge remote-tracking branch 'origin/main' into patch
mmsqe Jan 7, 2026
4333aab
Merge branch 'main' into patch
vladjdk Jan 8, 2026
645980c
Merge remote-tracking branch 'origin/main' into patch
mmsqe Mar 9, 2026
9bdc73c
fix resolve
mmsqe Mar 12, 2026
8455a9d
Merge remote-tracking branch 'origin/main' into patch
mmsqe Mar 12, 2026
a996d11
cleanup
mmsqe Mar 12, 2026
4432974
Merge pull request #1 from canu0205/chris/fix-patch
canu0205 Apr 2, 2026
2e638ba
Merge remote-tracking branch 'origin/main' into patch
mmsqe Apr 2, 2026
76cc259
fix build
mmsqe Apr 2, 2026
7a46237
lint
mmsqe Apr 2, 2026
9c400e7
cleanup
mmsqe Apr 2, 2026
cbc6ad6
continue
mmsqe Apr 16, 2026
61449bb
show fail tx
mmsqe Apr 16, 2026
f5d4de0
revert
mmsqe Apr 16, 2026
1deed0c
return err
mmsqe Apr 23, 2026
c31914e
Merge remote-tracking branch 'origin/main' into patch
mmsqe Apr 23, 2026
6d17b9a
alt design: keep ctx.TxIndex plumbing, narrow PatchTxResponses to log…
vladjdk Apr 24, 2026
1ab8829
add stm tx index tests
vladjdk Apr 24, 2026
fe7ee15
changelog fix
vladjdk Apr 24, 2026
2db3f08
fix: ethTxIndex counter diverges from the ante-handler-emitted Attrib…
vladjdk Apr 24, 2026
0af693b
fix: Hard failure on unmarshal for any successful non-EVM tx
vladjdk Apr 24, 2026
de04407
readd txindex correction
vladjdk Apr 24, 2026
d802095
comment
vladjdk Apr 24, 2026
44c57f2
merge main & fix conflicts
vladjdk Apr 24, 2026
3028b20
lints
vladjdk Apr 24, 2026
db56e36
Merge branch 'main' into vlad/pr-812-alt-design
vladjdk Apr 24, 2026
4334960
add edge case tests
vladjdk Apr 24, 2026
40b621f
fix double-counting
vladjdk Apr 24, 2026
801eb0a
fix divergence on ante failure
vladjdk Apr 24, 2026
c833608
convert FindEthTxIndexByHash to method
vladjdk Apr 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
- [\#774](https://github.com/cosmos/evm/pull/774) Emit proper allowance amount in erc20 event.
- [\#790](https://github.com/cosmos/evm/pull/790) fix panic in historical query due to missing EvmCoinInfo.
- [\#800](https://github.com/cosmos/evm/pull/800) Fix denom exponent validation in virtual fee deduct in vm module.
- [\#1132](https://github.com/cosmos/evm/pull/1132) Patch block-cumulative `log.Index` and eth-only `log.TxIndex` post-execution to fix indexing under BlockSTM.
- [\#817](https://github.com/cosmos/evm/pull/817) Align GetCoinbaseAddress to handle empty proposer address in contexts like CheckTx where proposer doesn't exist.
- [\#814](https://github.com/cosmos/evm/pull/814) Fix duplicated events in post tx processor.
- [\#816](https://github.com/cosmos/evm/pull/816) Avoid nil pointer when RPC requests execute before evmCoinInfo initialization in PreBlock with defaultEvmCoinInfo fallback.
Expand Down
9 changes: 3 additions & 6 deletions ante/evm/11_emit_event.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,10 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)

// EmitTxHashEvent emits the Ethereum tx
//
// FIXME: This is Technical debt. Ideally the sdk.Tx hash should be the Ethereum
// tx hash (msg.Hash) instead of using events for indexing Eth txs.
// EmitTxHashEvent emits the ethereum_tx event with the tx hash and the
// cosmos-tx index from ctx.TxIndex(). Emitting from the ante handler ensures
// expected-failure txs still produce the event for indexer lookups.
func EmitTxHashEvent(ctx sdk.Context, msg *evmtypes.MsgEthereumTx, blockTxIndex uint64) {
// emit ethereum tx hash as an event so that it can be indexed by CometBFT for query purposes
// it's emitted in ante handler, so we can query failed transaction (out of block gas limit).
ctx.EventManager().EmitEvent(
sdk.NewEvent(
evmtypes.EventTypeEthereumTx,
Expand Down
3 changes: 1 addition & 2 deletions ante/evm/mono_decorator.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,7 @@ func (md MonoDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, ne
return ctx, err
}

// Emit event unconditionally - ctx.TxIndex() will be valid during block execution
EmitTxHashEvent(ctx, ethMsg, uint64(ctx.TxIndex())) // #nosec G115 -- no overlfow here
EmitTxHashEvent(ctx, ethMsg, uint64(ctx.TxIndex())) // #nosec G115 -- no overflow here

if err := CheckTxFee(txFeeInfo, decUtils.TxFee, decUtils.TxGasLimit); err != nil {
return ctx, err
Expand Down
35 changes: 18 additions & 17 deletions evmd/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,18 @@ import (
"errors"
"fmt"
"os"

goruntime "runtime"

"github.com/ethereum/go-ethereum/common"
"github.com/spf13/cast"

// Force-load the tracer engines to trigger registration due to Go-Ethereum v1.10.15 changes
"github.com/cosmos/cosmos-sdk/baseapp/txnrunner"
"github.com/ethereum/go-ethereum/common"

_ "github.com/ethereum/go-ethereum/eth/tracers/js"
_ "github.com/ethereum/go-ethereum/eth/tracers/native"

abci "github.com/cometbft/cometbft/abci/types"

cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"

dbm "github.com/cosmos/cosmos-db"
evmante "github.com/cosmos/evm/ante"
antetypes "github.com/cosmos/evm/ante/types"
Expand All @@ -41,6 +38,7 @@ import (
ibccallbackskeeper "github.com/cosmos/evm/x/ibc/callbacks/keeper"
"github.com/cosmos/evm/x/vm"
evmkeeper "github.com/cosmos/evm/x/vm/keeper"
vmrunner "github.com/cosmos/evm/x/vm/runner"
evmtypes "github.com/cosmos/evm/x/vm/types"
"github.com/cosmos/gogoproto/proto"
ibccallbacks "github.com/cosmos/ibc-go/v11/modules/apps/callbacks"
Expand All @@ -61,9 +59,9 @@ import (
"cosmossdk.io/client/v2/autocli"
"cosmossdk.io/core/appmodule"
"cosmossdk.io/log/v2"
storetypes "github.com/cosmos/cosmos-sdk/store/v2/types"

"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/baseapp/txnrunner"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/grpc/cmtservice"
Expand All @@ -76,6 +74,7 @@ import (
"github.com/cosmos/cosmos-sdk/server/api"
"github.com/cosmos/cosmos-sdk/server/config"
servertypes "github.com/cosmos/cosmos-sdk/server/types"
storetypes "github.com/cosmos/cosmos-sdk/store/v2/types"
testdata_pulsar "github.com/cosmos/cosmos-sdk/testutil/testdata/testpb"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkmempool "github.com/cosmos/cosmos-sdk/types/mempool"
Expand Down Expand Up @@ -213,6 +212,7 @@ func NewExampleApp(
legacyAmino := encodingConfig.Amino
interfaceRegistry := encodingConfig.InterfaceRegistry
txConfig := encodingConfig.TxConfig
txDecoder := encodingConfig.TxConfig.TxDecoder()

// enable optimistic execution
baseAppOptions = append(
Expand All @@ -225,7 +225,7 @@ func NewExampleApp(
logger,
db,
// use transaction decoder to support the sdk.Tx interface instead of sdk.StdTx
encodingConfig.TxConfig.TxDecoder(),
txDecoder,
baseAppOptions...,
)
bApp.SetVersion(version.Version)
Expand All @@ -252,15 +252,6 @@ func NewExampleApp(
nonTransientKeys = append(nonTransientKeys, k)
}

// enable block stm for parallel execution
bApp.SetBlockSTMTxRunner(txnrunner.NewSTMRunner(
encodingConfig.TxConfig.TxDecoder(),
nonTransientKeys,
min(goruntime.GOMAXPROCS(0), goruntime.NumCPU()),
true,
func(ms storetypes.MultiStore) string { return sdk.DefaultBondDenom },
))

// disable block gas meter
bApp.SetDisableBlockGasMeter(true)

Expand Down Expand Up @@ -788,6 +779,16 @@ func NewExampleApp(
vmModule.HydrateGlobals(ctx)
}

vmrunner.SetRunner(bApp, txnrunner.NewSTMRunner(
txDecoder,
nonTransientKeys,
min(goruntime.GOMAXPROCS(0), goruntime.NumCPU()),
true,
func(ms storetypes.MultiStore) string {
return app.EVMKeeper.GetParams(sdk.NewContext(ms, cmtproto.Header{}, false, log.NewNopLogger())).EvmDenom
},
))

return app
}

Expand Down Expand Up @@ -1071,7 +1072,7 @@ func (app *EVMD) GetMempool() sdkmempool.ExtMempool {
}

func (app *EVMD) GetAnteHandler() sdk.AnteHandler {
return app.BaseApp.AnteHandler()
return app.AnteHandler()
}

// GetTxConfig implements the TestingApp interface.
Expand Down
3 changes: 1 addition & 2 deletions evmd/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ import (

tmproto "github.com/cometbft/cometbft/proto/tendermint/types"

storetypes "github.com/cosmos/cosmos-sdk/store/v2/types"

servertypes "github.com/cosmos/cosmos-sdk/server/types"
storetypes "github.com/cosmos/cosmos-sdk/store/v2/types"
sdk "github.com/cosmos/cosmos-sdk/types"
slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types"
"github.com/cosmos/cosmos-sdk/x/staking"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ import (
"math/big"
"testing"

sdkmath "cosmossdk.io/math"

"github.com/cosmos/cosmos-sdk/types"

"github.com/stretchr/testify/suite"

"github.com/cosmos/evm"
Expand All @@ -16,6 +12,10 @@ import (
debugprecompile "github.com/cosmos/evm/evmd/tests/testdata/debug"
evmibctesting "github.com/cosmos/evm/testutil/ibc"
testutiltypes "github.com/cosmos/evm/testutil/types"

sdkmath "cosmossdk.io/math"

"github.com/cosmos/cosmos-sdk/types"
)

// BalanceHandlerTestSuite tests the balance handler bug where recursive precompile calls
Expand Down
1 change: 0 additions & 1 deletion evmd/upgrades.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"

storetypes "github.com/cosmos/cosmos-sdk/store/v2/types"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
Expand Down
1 change: 0 additions & 1 deletion local_node.sh
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,6 @@ if [[ $overwrite == "y" || $overwrite == "Y" ]]; then
sed -i.bak 's/prometheus-retention-time = "0"/prometheus-retention-time = "1000000000000"/g' "$APP_TOML"
sed -i.bak 's/enabled = false/enabled = true/g' "$APP_TOML"
sed -i.bak 's/enable = false/enable = true/g' "$APP_TOML"
sed -i.bak 's/enable-indexer = false/enable-indexer = true/g' "$APP_TOML"

# --------- maybe generate additional users ---------
# start with provided/default list
Expand Down
9 changes: 9 additions & 0 deletions rpc/backend/comet_to_eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,15 @@ func (b *Backend) ReceiptsFromCometBlock(
return nil, fmt.Errorf("failed to convert tx result to eth receipt: %w", err)
}

if txResult.EthTxIndex == -1 {
var err error
// Fallback to find tx index by iterating all valid eth transactions
txResult.EthTxIndex, err = FindEthTxIndexByHash(ctx, ethMsg.Hash(), resBlock, blockRes, b)
if err != nil {
return nil, err
}
}

bloom := ethtypes.CreateBloom(&ethtypes.Receipt{Logs: logs})

receipt := &ethtypes.Receipt{
Expand Down
18 changes: 4 additions & 14 deletions rpc/backend/tx_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"encoding/json"
"fmt"
"math"
"math/big"
"time"

Expand Down Expand Up @@ -66,22 +65,13 @@ func (b *Backend) GetTransactionByHash(ctx context.Context, txHash common.Hash)
}

if res.EthTxIndex == -1 {
var err error
// Fallback to find tx index by iterating all valid eth transactions
msgs := b.EthMsgsFromCometBlock(ctx, block, blockRes)
for i := range msgs {
if msgs[i].Hash() == txHash {
if i > math.MaxInt32 {
return nil, errors.New("tx index overflow")
}
res.EthTxIndex = int32(i) //#nosec G115 -- checked for int overflow already
break
}
res.EthTxIndex, err = FindEthTxIndexByHash(ctx, txHash, block, blockRes, b)
if err != nil {
return nil, err
}
}
// if we still unable to find the eth tx index, return error, shouldn't happen.
if res.EthTxIndex == -1 {
return nil, errors.New("can't find index of ethereum tx")
}

baseFee, err := b.BaseFee(ctx, blockRes)
if err != nil {
Expand Down
88 changes: 87 additions & 1 deletion rpc/backend/tx_info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -407,11 +407,16 @@ func TestCreateAccessList(t *testing.T) {
}

func buildMsgEthereumTx(t *testing.T) *evmtypes.MsgEthereumTx {
t.Helper()
return buildMsgEthereumTxWithNonce(t, 0)
}

func buildMsgEthereumTxWithNonce(t *testing.T, nonce uint64) *evmtypes.MsgEthereumTx {
t.Helper()
from, _ := utiltx.NewAddrKey()
ethTxParams := evmtypes.EvmTxArgs{
ChainID: new(big.Int).SetUint64(constants.ExampleChainID.EVMChainID),
Nonce: uint64(0),
Nonce: nonce,
To: &common.Address{},
Amount: big.NewInt(0),
GasLimit: 100000,
Expand Down Expand Up @@ -522,6 +527,87 @@ func TestReceiptsFromCometBlock(t *testing.T) {
}
}

// TestReceiptsLogIndexBlockGlobal asserts that after PatchTxResponses runs
// on a block's ExecTxResults, ReceiptsFromCometBlock surfaces block-global
// log.Index and eth-only log.TxIndex per the Ethereum JSON-RPC spec.
func TestReceiptsLogIndexBlockGlobal(t *testing.T) {
backend := setupMockBackend(t)
height := int64(200)
encodingConfig := encoding.MakeConfig(constants.ExampleChainID.EVMChainID)

addr := common.HexToAddress("0x1234567890abcdef1234567890abcdef12345678")
topic := common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")

// Pre-patch logs: log.TxIndex == cosmos tx index, log.Index per-tx local.
makeTxResponse := func(hash string, cosmosTxIndex uint64) *evmtypes.MsgEthereumTxResponse {
return &evmtypes.MsgEthereumTxResponse{
Hash: hash,
Logs: []*evmtypes.Log{
{Address: addr.String(), Topics: []string{topic.String()}, TxIndex: cosmosTxIndex, Index: 0},
{Address: addr.String(), Topics: []string{topic.String()}, TxIndex: cosmosTxIndex, Index: 1},
},
}
}

// Distinct nonces so the eth tx hashes differ.
msg0 := buildMsgEthereumTxWithNonce(t, 0)
msg1 := buildMsgEthereumTxWithNonce(t, 1)

encodeTxResult := func(resp *evmtypes.MsgEthereumTxResponse) []byte {
anyData := codectypes.UnsafePackAny(resp)
txMsgData := &sdk.TxMsgData{MsgResponses: []*codectypes.Any{anyData}}
data, err := encodingConfig.Codec.Marshal(txMsgData)
require.NoError(t, err)
return data
}

tx0Data := encodeTxResult(makeTxResponse(msg0.Hash().Hex(), 0))
tx1Data := encodeTxResult(makeTxResponse(msg1.Hash().Hex(), 1))

resBlock := &tmrpctypes.ResultBlock{
Block: &tmtypes.Block{Header: tmtypes.Header{Height: height}},
}
blockRes := &tmrpctypes.ResultBlockResults{
Height: height,
TxsResults: []*abcitypes.ExecTxResult{
{Code: 0, Data: tx0Data},
{Code: 0, Data: tx1Data},
},
}

patched, err := evmtypes.PatchTxResponses(blockRes.TxsResults)
require.NoError(t, err)
blockRes.TxsResults = patched

mockIndexer := &MockIndexer{
txResults: map[common.Hash]*servertypes.TxResult{
msg0.Hash(): {Height: height, TxIndex: 0, EthTxIndex: 0, MsgIndex: 0},
msg1.Hash(): {Height: height, TxIndex: 1, EthTxIndex: 1, MsgIndex: 0},
},
}
backend.Indexer = mockIndexer

mockEVMQueryClient := backend.QueryClient.QueryClient.(*mocks.EVMQueryClient)
mockEVMQueryClient.On("BaseFee", mock.Anything, mock.Anything).Return(&evmtypes.QueryBaseFeeResponse{}, nil)

msgs := []*evmtypes.MsgEthereumTx{msg0, msg1}
receipts, err := backend.ReceiptsFromCometBlock(rpctypes.NewContextWithHeight(1), resBlock, blockRes, msgs)
require.NoError(t, err)
require.Len(t, receipts, 2)

require.Len(t, receipts[0].Logs, 2)
require.Equal(t, uint(0), receipts[0].Logs[0].Index)
require.Equal(t, uint(1), receipts[0].Logs[1].Index)
require.Equal(t, uint(0), receipts[0].Logs[0].TxIndex)
require.Equal(t, uint(0), receipts[0].Logs[1].TxIndex)

require.Len(t, receipts[1].Logs, 2)
require.Equal(t, uint(2), receipts[1].Logs[0].Index)
require.Equal(t, uint(3), receipts[1].Logs[1].Index)
require.Equal(t, uint(1), receipts[1].Logs[0].TxIndex)
require.Equal(t, uint(1), receipts[1].Logs[1].TxIndex)
}

func TestEthMsgsFromCometBlockSkipStateDBCommitFailure(t *testing.T) {
backend := setupMockBackend(t)
successMsg := buildMsgEthereumTx(t)
Expand Down
14 changes: 14 additions & 0 deletions rpc/backend/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -355,3 +355,17 @@ func unwrapBlockNOrHash(blockNOrHash types.BlockNumberOrHash) string {
}
return ""
}

// Fallback to find tx index by iterating all valid eth transactions for legacy blocks that don't have AttributeKeyTxIndex in events
func FindEthTxIndexByHash(ctx context.Context, txHash common.Hash, block *cmtrpctypes.ResultBlock, blockRes *cmtrpctypes.ResultBlockResults, b *Backend) (int32, error) {
Comment thread
vladjdk marked this conversation as resolved.
Outdated
msgs := b.EthMsgsFromCometBlock(ctx, block, blockRes)
for i := range msgs {
if msgs[i].Hash() == txHash {
if i > math.MaxInt32 {
return -1, fmt.Errorf("tx index overflow")
}
return int32(i), nil //#nosec G115 -- checked for int overflow already
}
}
return -1, fmt.Errorf("can't find index of ethereum tx")
}
3 changes: 3 additions & 0 deletions rpc/types/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,9 @@ func (p *ParsedTxs) updateTx(eventIndex int, attrs []abci.EventAttribute) error
// if hash is different, index the new one too
p.TxHashes[tx.Hash] = eventIndex
}
if tx.EthTxIndex == -1 {
tx.EthTxIndex = p.Txs[eventIndex].EthTxIndex
}
// override the tx because the second event is more trustworthy
p.Txs[eventIndex] = tx
return nil
Expand Down
Loading
Loading