Skip to content

fix(event): keep event notifications consistent with committed blocks - #25

Open
0xbigapple wants to merge 1 commit into
release_v4.8.3from
fix/fork-event-rollback
Open

0xbigapple wants to merge 1 commit into
release_v4.8.3from
fix/fork-event-rollback

Conversation

@0xbigapple

@0xbigapple 0xbigapple commented Nov 3, 2026 •

Copy link
Copy Markdown
Owner

What does this PR do?

  • Posts contract triggers only after the block that produced them is committed: from blockTrigger on the regular path, and after the whole candidate branch has been applied on a fork switch. They stay ahead of the block trigger, so the order seen by the event plugin is unchanged.
  • After a failed fork switch restores the previous branch, re-emits that branch's forward notifications: contract triggers, JSON-RPC logs filter events, and event-subscribe block and transaction triggers. The JSON-RPC block filter is left alone because its original notification was never withdrawn. If the restore itself fails partway, the blocks already recommitted are re-emitted before the failure propagates.
  • Treats a notification failure after a fork switch the way blockTrigger already does: the ledger is committed and the notification cannot be repaired, so the node requests exit. On the restore path the original switch exception is attached as suppressed.

Why are these changes required?

Contract triggers were posted at the end of processTransaction, as each transaction executed. Anything that throws later inside applyBlock lands in the catch (Throwable) of pushBlock, which rolls back the session, drops the block from khaosDb and rethrows. Nothing there withdraws the triggers already posted for the block's earlier transactions. A fork switch has the same gap: when a later block of the candidate branch fails, switchFork erases the applied blocks but their triggers stay published. Subscribers were left holding forward events (removed=false) for blocks that never became canonical, with no removal to follow.

The restore path had the mirror problem, missing events instead of extra ones. Erasing the previous branch withdrew its logs filter events and block/transaction triggers (removed=true). Re-applying it went through applyBlock, which re-posted contract triggers from processTransaction, but reApplyBlockEvents runs only when the candidate branch succeeds, so the restored branch's logs filter events and block/transaction triggers were never published again.

This PR has been tested by:

  • Unit Tests: ManagerTest
    • pushBlockShouldPostContractTriggersOnlyAfterCommit: through the real VM, a block that fails after executing a contract deployment posts nothing; the committed block posts one forward trigger. Fails on the previous code.
    • switchForkShouldReemitOldBranchEventsAfterFailedCandidateSwitch: the old head's logs filter and contract trigger go forward, removed, restored; its block filter is not republished; the failed candidate blocks emit nothing; eth_getFilterChanges returns the removal, then the restoration.
    • switchForkShouldReemitRestoredPrefixEventsWhenRestoreFails: through the real VM, a two-block old branch whose restore fails on the second block; the first block's contract and logs filter events are withdrawn then republished, its block filter is not, and the failed blocks emit nothing. Fails on the previous code.
    • testPostContractTriggersUsesAppliedBlockTraceAndCapturedSolidifiedHeight, testPostForkBranchEventsRequestsExitOnReplayFailure, postContractTriggersSkipsTransactionsWithoutTrace: the deferred-trigger helper on its own (block hash and solidified height, exit on replay failure, skipping a transaction without a trace).

Follow up

None.

Extra details

Deferred triggers are rebuilt from the transaction traces kept on the applied block, so nothing is copied while the outcome is unknown. The solidified block number attached to them is captured before the block is applied, which is the value the immediate path reported too: processTransaction runs before the consensus step that advances it. The forward and reorg-removal paths share one per-block helper, so the removal path now also skips a transaction loaded without a trace (a block read from the database after a restart) instead of dereferencing null. processTransaction no longer reads the block's merkle root, so ManagerMockTest moves its timing stub to another call to keep covering the slow-transaction log branch.


Summary by cubic

Fixes event notifications so subscribers only see events for blocks that actually become canonical: contract triggers are deferred until commit, and a failed fork switch now republishes the restored branch's events.

  • Contract triggers were posted during transaction execution; a block that failed afterward, or a candidate branch that failed to switch, left forward notifications with no matching removal. They now post from blockTrigger on the regular path and after the whole branch is applied on a fork switch.
  • After a failed fork switch restores the previous branch, that branch's contract triggers, logs filter events, and event-subscribe block/transaction triggers are re-emitted. The JSON-RPC block filter is not, since its original notification was never withdrawn.
  • If the restore fails partway, the already re-committed prefix is re-emitted before the failure propagates.
  • A notification failure after a fork switch now requests node exit, matching how blockTrigger already handles it.
  • The shared per-block helper also makes the reorg-removal path skip transactions loaded without a trace after a restart instead of dereferencing null.
  • Adds tests covering deferred triggers through the real VM, failed-switch restoration, partial-restore failure, and the null-trace skip.

Written for commit c660c2d. Summary will update on new commits.

View guided diff Turn on auto-fix

Contract triggers were posted from processTransaction before a block's
outcome was known, so a block that failed after executing transactions,
or a candidate branch that failed to switch, leaked forward events with
no matching removal. Post them only after the block is committed: from
blockTrigger on the regular path, and after the whole branch is applied
on a fork switch.

After a failed switch, re-emit the restored branch's forward
notifications (contract triggers, logs filter, block/transaction
triggers); skip the block filter, whose original notification was never
removed. If the restore itself fails partway, re-emit the already
recommitted prefix before propagating the failure so it is not left
withdrawn. A notification failure after commit requests a node exit, as
blockTrigger already does.

Share one per-block helper between the forward and reorg-removal paths,
which also guards the removal path against a transaction loaded without
a trace after a restart.
@coderabbitai

coderabbitai Bot commented Nov 3, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: a1907935-e14f-4c74-9bfd-f25d5770228d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Tip

🎯 Customize your risk assessments

Guide how CodeRabbit assesses merge risk and security architecture risk. Highlight sensitive changes and explain your security boundaries using Merge Risk Instructions and Security Risk Instructions in your review settings. Security architecture risk assessments require the Advanced plan or above.

✅ Automatic approvals, with your rules

Let CodeRabbit approve pull requests once required threads are resolved, pre-merge checks pass, and your risk limits are met. Enable Automatic approvals in review settings, with overrides for teams or file paths.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

CodeRabbit Verify

  • Run CodeRabbit Verify to test the changes in this pull request.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-03T08:06:48.620480Z c660c2d PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 3 files

View guided diff | Turn on auto-fix | Re-trigger cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant