diff --git a/app/ante/evm_delivertx.go b/app/ante/evm_delivertx.go index bc1d761092..5168afa5c2 100644 --- a/app/ante/evm_delivertx.go +++ b/app/ante/evm_delivertx.go @@ -90,6 +90,7 @@ func DecorateNonceCallback(ctx sdk.Context, ek *evmkeeper.Keeper, evmAddr common // bump nonce if it is for some reason not incremented (e.g. ante failure) if ek.GetNonce(callCtx, evmAddr) == startingNonce { ek.SetNonce(callCtx, evmAddr, startingNonce+1) + ek.SetNonceBumped(callCtx) } }) } diff --git a/evmrpc/block.go b/evmrpc/block.go index 0883ba516b..e896bbe622 100644 --- a/evmrpc/block.go +++ b/evmrpc/block.go @@ -342,7 +342,11 @@ func EncodeTmBlock( case *types.MsgEVMTransaction: ethtx, _ := m.AsTransaction() hash := ethtx.Hash() - receipt, _ := k.GetReceipt(latestCtx, hash) + receipt, err := k.GetReceipt(latestCtx, hash) + if err != nil { + // tx doesn't have a receipt because of nonce mismatch + continue + } if !fullTx { transactions = append(transactions, hash.Hex()) } else { diff --git a/evmrpc/info.go b/evmrpc/info.go index 0eaea427e8..943ff0687e 100644 --- a/evmrpc/info.go +++ b/evmrpc/info.go @@ -292,7 +292,8 @@ func (i *InfoAPI) getRewards(block *coretypes.ResultBlock, baseFee *big.Int, rew // okay to get from latest since receipt is immutable receipt, err := i.keeper.GetReceipt(i.ctxProvider(LatestCtxHeight), ethtx.Hash()) if err != nil { - return nil, err + // tx doesn't have a receipt because of nonce mismatch + continue } receiptEffectiveGasPrice := new(big.Int).SetUint64(receipt.EffectiveGasPrice) if receiptEffectiveGasPrice.Cmp(baseFee) < 0 { diff --git a/x/evm/ante/basic.go b/x/evm/ante/basic.go index 2879150fdf..519f4675eb 100644 --- a/x/evm/ante/basic.go +++ b/x/evm/ante/basic.go @@ -34,6 +34,7 @@ func (gl BasicDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, n // bump nonce if it is for some reason not incremented (e.g. ante failure) if gl.k.GetNonce(callCtx, msg.Derived.SenderEVMAddr) == startingNonce { gl.k.SetNonce(callCtx, msg.Derived.SenderEVMAddr, startingNonce+1) + gl.k.SetNonceBumped(callCtx) } }) } diff --git a/x/evm/keeper/abci.go b/x/evm/keeper/abci.go index da4a75edae..9db9285761 100644 --- a/x/evm/keeper/abci.go +++ b/x/evm/keeper/abci.go @@ -101,6 +101,9 @@ func (k *Keeper) EndBlock(ctx sdk.Context, height int64, blockGasUsed int64) { for _, deferredInfo := range evmTxDeferredInfoList { txHash := common.BytesToHash(deferredInfo.TxHash) if deferredInfo.Error != "" && txHash.Cmp(ethtypes.EmptyTxsHash) != 0 { + if !k.GetNonceBumped(ctx, deferredInfo.TxIndex) { + continue + } _ = k.SetTransientReceipt(ctx, txHash, &types.Receipt{ TxHashHex: txHash.Hex(), TransactionIndex: deferredInfo.TxIndex, diff --git a/x/evm/keeper/abci_test.go b/x/evm/keeper/abci_test.go new file mode 100644 index 0000000000..032c6a9386 --- /dev/null +++ b/x/evm/keeper/abci_test.go @@ -0,0 +1,53 @@ +package keeper_test + +import ( + "testing" + + "github.com/sei-protocol/sei-chain/app" + abci "github.com/sei-protocol/sei-chain/sei-tendermint/abci/types" + "github.com/sei-protocol/sei-chain/x/evm/types" + "github.com/stretchr/testify/require" +) + +func TestEndBlock_NoReceiptForNonceMismatch(t *testing.T) { + a := app.Setup(t, false, false, false) + k := a.EvmKeeper + ctx := a.GetContextForDeliverTx([]byte{}).WithBlockHeight(8) + + msg := mockEVMTransactionMessage(t) + etx, _ := msg.AsTransaction() + txHash := etx.Hash() + + k.BeginBlock(ctx) + k.SetMsgs([]*types.MsgEVMTransaction{msg}) + k.SetTxResults([]*abci.ExecTxResult{{Code: 1, Log: "nonce mismatch"}}) + // No SetNonceBumped call — simulates a tx where startingNonce != txNonce, + // so the nonce bump callback was never registered/executed. + k.EndBlock(ctx, 0, 0) + + _, err := k.GetTransientReceipt(ctx, txHash, 0) + require.Error(t, err, "should not create a receipt when nonce was not bumped") +} + +func TestEndBlock_ReceiptCreatedWhenNonceBumped(t *testing.T) { + a := app.Setup(t, false, false, false) + k := a.EvmKeeper + ctx := a.GetContextForDeliverTx([]byte{}).WithBlockHeight(8) + + msg := mockEVMTransactionMessage(t) + etx, _ := msg.AsTransaction() + txHash := etx.Hash() + + k.BeginBlock(ctx) + k.SetMsgs([]*types.MsgEVMTransaction{msg}) + k.SetTxResults([]*abci.ExecTxResult{{Code: 1, Log: "some ante error"}}) + // Simulate that the nonce bump callback ran (startingNonce == txNonce). + k.SetNonceBumped(ctx.WithTxIndex(0)) + k.EndBlock(ctx, 0, 0) + + receipt, err := k.GetTransientReceipt(ctx, txHash, 0) + require.NoError(t, err, "should create a receipt when nonce was bumped") + require.Equal(t, txHash.Hex(), receipt.TxHashHex) + require.Equal(t, "some ante error", receipt.VmError) + require.Equal(t, uint64(8), receipt.BlockNumber) +} diff --git a/x/evm/keeper/deferred.go b/x/evm/keeper/deferred.go index 505bc72224..15a9ef7129 100644 --- a/x/evm/keeper/deferred.go +++ b/x/evm/keeper/deferred.go @@ -67,6 +67,18 @@ func (k *Keeper) AppendToEvmTxDeferredInfo(ctx sdk.Context, bloom ethtypes.Bloom prefix.NewStore(ctx.TransientStore(k.transientStoreKey), types.DeferredInfoPrefix).Set(key, bz) } +func (k *Keeper) SetNonceBumped(ctx sdk.Context) { + key := make([]byte, 8) + binary.BigEndian.PutUint64(key, uint64(ctx.TxIndex())) //nolint:gosec + prefix.NewStore(ctx.TransientStore(k.transientStoreKey), types.NonceBumpPrefix).Set(key, []byte{1}) +} + +func (k *Keeper) GetNonceBumped(ctx sdk.Context, txIndex uint32) bool { + key := make([]byte, 8) + binary.BigEndian.PutUint64(key, uint64(txIndex)) + return prefix.NewStore(ctx.TransientStore(k.transientStoreKey), types.NonceBumpPrefix).Has(key) +} + func (k *Keeper) GetEVMTxDeferredInfo(ctx sdk.Context) (*types.DeferredInfo, bool) { key := make([]byte, 8) binary.BigEndian.PutUint64(key, uint64(ctx.TxIndex())) //nolint:gosec diff --git a/x/evm/module_test.go b/x/evm/module_test.go index bf18dd4c44..149570f70f 100644 --- a/x/evm/module_test.go +++ b/x/evm/module_test.go @@ -119,6 +119,7 @@ func TestABCI(t *testing.T) { msg := mockEVMTransactionMessage(t) k.SetMsgs([]*types.MsgEVMTransaction{msg}) k.SetTxResults([]*abci.ExecTxResult{{Code: 1, Log: "test error"}}) + k.SetNonceBumped(ctx.WithTxIndex(0)) k.EndBlock(ctx, 0, 0) err = k.FlushTransientReceipts(ctx) require.NoError(t, err) diff --git a/x/evm/types/keys.go b/x/evm/types/keys.go index 9484f929d2..bd12e57e90 100644 --- a/x/evm/types/keys.go +++ b/x/evm/types/keys.go @@ -62,6 +62,7 @@ var ( NextBaseFeePerGasPrefix = []byte{0x1c} EvmOnlyBlockBloomPrefix = []byte{0x1d} ZeroStorageCleanupCheckpointKey = []byte{0x1e} + NonceBumpPrefix = []byte{0x1f} // transient ) var (