Skip to content

[F-2026-17738] - Reverted derived execution still emits events and mutates block bloom #16

Merged
0xNilesh merged 5 commits into
audit-fixesfrom
audit/revert-tx-mutates-block-bloom-issue
Jun 11, 2026
Merged

[F-2026-17738] - Reverted derived execution still emits events and mutates block bloom #16
0xNilesh merged 5 commits into
audit-fixesfrom
audit/revert-tx-mutates-block-bloom-issue

Conversation

@AryaLanjewar3005

Copy link
Copy Markdown
Collaborator

[F-2026-17738] - Reverted derived execution still emits events and mutates block bloom

Issue

In DerivedEVMCallWithData, when commit=true and the EVM execution reverts (res.Failed()==true), the event emission block is only gated on if commit — with no check on whether the execution succeeded. This causes three incorrect side effects on the failure path:

File: x/vm/keeper/call_evm.go

Bug 1 — EventTypeTxLog emitted for reverted transactions

txLogAttrs := make([]sdk.Attribute, len(res.Logs))
for i, log := range res.Logs {
    ...
}
ctx.EventManager().EmitEvents(sdk.Events{
    sdk.NewEvent(types.EventTypeTxLog, txLogAttrs...),
})

In Ethereum, a reverted transaction produces no logs — the EVM clears them on REVERT. Emitting EventTypeTxLog for a failed execution creates ghost log entries that downstream indexers, block explorers, and RPC clients treat as real, even though no corresponding state change exists in the store.

Bug 2 — Block bloom filter polluted by reverted logs

logs := types.LogsToEthereum(res.Logs)
if len(logs) > 0 {
    bloom := k.GetBlockBloomTransient(ctx)
    bloom.Or(bloom, big.NewInt(0).SetBytes(ethtypes.LogsBloom(logs)))
    k.SetBlockBloomTransient(ctx, bloomReceipt.Big())
    k.SetLogSizeTransient(ctx, ...)
}

The block bloom filter only covers logs from successful transactions. Updating it with logs from a reverted execution means eth_getLogs bloom lookups return false positives — clients believe a contract emitted an event in a block when it did not.

Bug 3 — Internal contradiction

The code explicitly acknowledges the failure by appending EthereumTxFailed to the receipt event attributes, yet immediately proceeds to emit EventTypeTxLog with logs from that same failed execution.

Impact

Clients observing logs, receipts, and bloom effects see uncommitted state. This causes inconsistency across explorers, traces, and downstream indexers — they record events for executions that left no trace in the actual chain state.


Solution

Split the event emission into two parts:

Always emit (for both success and failure):

  • EventTypeEthereumTx — this is the transaction receipt; failed transactions still appear in the block in Ethereum, just with a failed status. Indexers need this to know the tx was attempted.
  • EventTypeMessage — module-level bookkeeping, not state-dependent.

Only emit on !res.Failed():

  • EventTypeTxLog — reverted transactions produce no logs.
  • SetBlockBloomTransient — bloom only covers successful tx logs.
  • SetLogSizeTransient — only counts committed logs.
  • txLogAttrs construction — moved inside the guard since it is only needed for EventTypeTxLog.

Behavior after fix

commit res.Failed() Receipt event emitted Logs emitted Bloom updated
true no yes yes yes
true yes yes (with vm error) no no
false either no no no

Failed derived executions remain trackable via the EventTypeEthereumTx receipt event (which carries the EthereumTxFailed attribute), but produce no phantom logs and do not corrupt the block bloom — matching Ethereum's own behavior for reverted transactions.

Closes: #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...

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

AryaLanjewar3005 and others added 5 commits June 8, 2026 15:41
…F-2026-17738)

A reverted derived call must not contribute logs or bloom for uncommitted state,
but it must still emit a tx_log event paired with its ethereum_tx. The JSON-RPC log
builder (TxLogsFromEvents) matches logs to txs positionally — the Nth tx_log belongs
to the Nth eth tx — so dropping the tx_log on failure desyncs logs across the other
derived txs in the same block (wrong logs on one receipt, missing logs on another).

Always emit the ethereum_tx + tx_log + message triple under commit; on a revert
res.Logs is empty so the tx_log carries no logs, and the bloom/log-size transients
are guarded behind !res.Failed(). The failed tx therefore shows a status-0 receipt
with empty logs and no bloom side effects.

Adds regression tests:
- EthTxLogEventsStayPaired: interleaved success/failure derived calls keep the
  ethereum_tx and tx_log event counts equal (fails on the drop-tx_log approach).
- FailedExecutionNoBloomSideEffect: a reverted call leaves bloom/log-size unchanged
  while still emitting the ethereum_tx + empty tx_log pair.
@github-actions github-actions Bot added the tests label Jun 11, 2026
@0xNilesh 0xNilesh merged commit 1a8d637 into audit-fixes Jun 11, 2026
15 of 16 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.

2 participants