Skip to content

[Provers M02] Missing Event on Prover Implementation setting on Pointer#84

Open
luiz-lvj wants to merge 6 commits intomainfrom
fix/provers-M02
Open

[Provers M02] Missing Event on Prover Implementation setting on Pointer#84
luiz-lvj wants to merge 6 commits intomainfrom
fix/provers-M02

Conversation

@luiz-lvj
Copy link
Collaborator

@luiz-lvj luiz-lvj commented Feb 18, 2026

StateProverPointer.setImplementationAddress() updates the stored implementation address and code hash, but does not emit an event. In this system, prover updates on the source chain must be mirrored on destination chains by deploying a matching StateProverCopy and calling Receiver.updateStateProverCopy so the cached copy’s codehash matches the expected remote value.

Without an event, relayers/operators/indexers have no reliable on-chain signal to detect that the prover has been upgraded. They must instead continuously poll state or perform trace/storage-diff monitoring to notice that the pointer slot changed. This increases the chance of missed or delayed updates across chains.

This becomes especially problematic during security response. If the previous prover version is discovered to be vulnerable and the owner upgrades the pointer to a patched prover on the source chain, destination chains may continue verifying proofs using their cached (old) StateProverCopy until operators detect the change and update the copy. That unnecessarily extends the window in which the known-vulnerable prover remains usable on the other chains.

This PR adds an event to the StateProverPointer contract to be emitted when the setImplementationAddress is called.

Summary by CodeRabbit

  • New Features

    • Added a public event to record implementation address updates, including version, new address, code hash, and previous address for improved observability and auditability.
  • Tests

    • Expanded tests to expect and verify the new event emission across multiple network scenarios, ensuring the event is emitted with correct parameters and timing when the implementation address is updated.

@coderabbitai
Copy link

coderabbitai bot commented Feb 18, 2026

No actionable comments were generated in the recent review. 🎉


📝 Walkthrough

Walkthrough

Adds an ImplementationAddressSet event to the IStateProverPointer interface and emits it from StateProverPointer.setImplementationAddress. Tests were updated to expect this event across multiple chain scenarios before calling setImplementationAddress. (50 words)

Changes

Cohort / File(s) Summary
Interface
src/contracts/interfaces/IStateProverPointer.sol
Declare new event ImplementationAddressSet(uint256 indexed newVersion, address newImplementationAddress, bytes32 newCodeHash, address oldImplementationAddress).
Contract
src/contracts/StateProverPointer.sol
Emit ImplementationAddressSet(newVersion, _newImplementationAddress, _newImplementationAddress.codehash, currentImplementationAddress) inside setImplementationAddress immediately after updating implementation and codehash.
Tests
test/Receiver.ethereum.t.sol, test/Receiver.t.sol
Inserted vm.expectEmit() and expected IStateProverPointer.ImplementationAddressSet(...) assertions before setImplementationAddress calls across multiple chain test cases (Ethereum, Arbitrum, Optimism, Linea, ZkSync, Scroll).

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested reviewers

  • pepebndc

Poem

🐰
I hopped upon the pointer, gave a cheerful chime,
I log the new address, one version at a time.
With codehash tucked beneath my paw so neat,
I trumpet every change with tiny feet,
Hooray — a signal set, the ledger's beat! 🥕✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: adding a missing event emission when the prover implementation address is set on the StateProverPointer contract.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/provers-M02

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

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/contracts/StateProverPointer.sol (1)

71-75: Cache _newImplementationAddress.codehash to avoid double EXTCODEHASH opcode.

_newImplementationAddress.codehash is evaluated twice: once passed into _setCodeHash (line 71) and again as the newCodeHash argument to the emit (line 74).

♻️ Cache in a local variable
-        _implementationAddress = _newImplementationAddress;
-        _setCodeHash(_newImplementationAddress.codehash);
-
-        emit ImplementationAddressSet(
-            newVersion, _newImplementationAddress, _newImplementationAddress.codehash, currentImplementationAddress
-        );
+        _implementationAddress = _newImplementationAddress;
+        bytes32 newCodeHash = _newImplementationAddress.codehash;
+        _setCodeHash(newCodeHash);
+
+        emit ImplementationAddressSet(newVersion, _newImplementationAddress, newCodeHash, currentImplementationAddress);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/contracts/StateProverPointer.sol` around lines 71 - 75, Cache the value
of _newImplementationAddress.codehash in a local bytes32 variable and use that
variable for both the _setCodeHash call and the ImplementationAddressSet emit to
avoid executing EXTCODEHASH twice; e.g., compute bytes32 newCodeHash =
_newImplementationAddress.codehash, then call _setCodeHash(newCodeHash) and emit
ImplementationAddressSet(newVersion, _newImplementationAddress, newCodeHash,
currentImplementationAddress).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/contracts/StateProverPointer.sol`:
- Around line 19-24: Move the event declaration for ImplementationAddressSet
from the concrete StateProverPointer contract into the IStateProverPointer
interface so off-chain consumers using the interface ABI can subscribe to it;
specifically, add the event signature (ImplementationAddressSet(uint256 indexed
newVersion, address newImplementationAddress, bytes32 newCodeHash, address
oldImplementationAddress)) to IStateProverPointer, then remove the duplicate
event declaration from StateProverPointer and keep only the emit
ImplementationAddressSet(...) call where the pointer update occurs.

---

Nitpick comments:
In `@src/contracts/StateProverPointer.sol`:
- Around line 71-75: Cache the value of _newImplementationAddress.codehash in a
local bytes32 variable and use that variable for both the _setCodeHash call and
the ImplementationAddressSet emit to avoid executing EXTCODEHASH twice; e.g.,
compute bytes32 newCodeHash = _newImplementationAddress.codehash, then call
_setCodeHash(newCodeHash) and emit ImplementationAddressSet(newVersion,
_newImplementationAddress, newCodeHash, currentImplementationAddress).

Comment on lines 19 to 24
event ImplementationAddressSet(
uint256 indexed newVersion,
address newImplementationAddress,
bytes32 newCodeHash,
address oldImplementationAddress
);
Copy link

@coderabbitai coderabbitai bot Feb 18, 2026

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

ImplementationAddressSet should be declared in IStateProverPointer.

The event is declared only in the concrete contract. Since the PR's explicit goal is enabling relayers/operators/indexers to detect pointer updates, any off-chain consumer (or on-chain subscriber) that works against the IStateProverPointer ABI will not see this event in its ABI and won't be able to subscribe to it without importing the concrete contract.

♻️ Move event declaration to IStateProverPointer

In IStateProverPointer:

+    event ImplementationAddressSet(
+        uint256 indexed newVersion,
+        address newImplementationAddress,
+        bytes32 newCodeHash,
+        address oldImplementationAddress
+    );

In StateProverPointer.sol, remove the duplicate declaration and keep only the emit.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/contracts/StateProverPointer.sol` around lines 19 - 24, Move the event
declaration for ImplementationAddressSet from the concrete StateProverPointer
contract into the IStateProverPointer interface so off-chain consumers using the
interface ABI can subscribe to it; specifically, add the event signature
(ImplementationAddressSet(uint256 indexed newVersion, address
newImplementationAddress, bytes32 newCodeHash, address
oldImplementationAddress)) to IStateProverPointer, then remove the duplicate
event declaration from StateProverPointer and keep only the emit
ImplementationAddressSet(...) call where the pointer update occurs.

Copy link
Collaborator

Choose a reason for hiding this comment

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

apply @luiz-lvj

Copy link

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
src/contracts/StateProverPointer.sol (2)

70-75: Cache codehash to avoid reading it twice

_newImplementationAddress.codehash is evaluated on both line 71 and line 74 (two EXTCODEHASH opcode invocations). Caching it in a local variable makes the intent clearer and avoids the redundant read.

♻️ Proposed refactor
+        bytes32 newCodeHash = _newImplementationAddress.codehash;
         _implementationAddress = _newImplementationAddress;
-        _setCodeHash(_newImplementationAddress.codehash);
+        _setCodeHash(newCodeHash);
 
         emit ImplementationAddressSet(
-            newVersion, _newImplementationAddress, _newImplementationAddress.codehash, currentImplementationAddress
+            newVersion, _newImplementationAddress, newCodeHash, currentImplementationAddress
         );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/contracts/StateProverPointer.sol` around lines 70 - 75, Cache the
codehash of _newImplementationAddress in a local variable to avoid calling
_newImplementationAddress.codehash twice; compute e.g. bytes32 newImplCodeHash =
_newImplementationAddress.codehash once, call _setCodeHash(newImplCodeHash) and
use newImplCodeHash in the emit ImplementationAddressSet(newVersion,
_newImplementationAddress, newImplCodeHash, currentImplementationAddress) while
keeping the assignment _implementationAddress = _newImplementationAddress
unchanged.

19-24: Move ImplementationAddressSet to IStateProverPointer

The event is declared only in the concrete contract. Off-chain indexers and relayers that subscribe to events via the IStateProverPointer ABI (which is the public contract surface) won't find the event definition there. The PR's stated goal is to let relayers/operators detect upgrades via on-chain signals, so the event belongs in the interface.

♻️ Suggested change

In IStateProverPointer:

+    event ImplementationAddressSet(
+        uint256 indexed newVersion,
+        address newImplementationAddress,
+        bytes32 newCodeHash,
+        address oldImplementationAddress
+    );

In StateProverPointer.sol:

-    event ImplementationAddressSet(
-        uint256 indexed newVersion,
-        address newImplementationAddress,
-        bytes32 newCodeHash,
-        address oldImplementationAddress
-    );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/contracts/StateProverPointer.sol` around lines 19 - 24, The
ImplementationAddressSet event is declared only in StateProverPointer but should
be part of the public ABI; move the event declaration into the
IStateProverPointer interface and remove the duplicate from StateProverPointer
so the interface exposes the event to off‑chain indexers. Ensure the event
signature (ImplementationAddressSet(uint256 indexed newVersion, address
newImplementationAddress, bytes32 newCodeHash, address
oldImplementationAddress)) is identical in IStateProverPointer, update any
imports or references if needed, and run/update tests that assert the event is
emitted by StateProverPointer (no other code changes required).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@test/Receiver.ethereum.t.sol`:
- Around line 135-139: The test uses the incorrect function-call syntax
parentToChildProver.codehash() when emitting and asserting
StateProverPointer.ImplementationAddressSet; change all occurrences to the
property access parentToChildProver.codehash (no parentheses) so the emitted
event and the call to
stateProverPointer.setImplementationAddress(address(parentToChildProver))
compare the correct codehash value; update the expectEmit/emit line referencing
StateProverPointer.ImplementationAddressSet accordingly.

---

Nitpick comments:
In `@src/contracts/StateProverPointer.sol`:
- Around line 70-75: Cache the codehash of _newImplementationAddress in a local
variable to avoid calling _newImplementationAddress.codehash twice; compute e.g.
bytes32 newImplCodeHash = _newImplementationAddress.codehash once, call
_setCodeHash(newImplCodeHash) and use newImplCodeHash in the emit
ImplementationAddressSet(newVersion, _newImplementationAddress, newImplCodeHash,
currentImplementationAddress) while keeping the assignment
_implementationAddress = _newImplementationAddress unchanged.
- Around line 19-24: The ImplementationAddressSet event is declared only in
StateProverPointer but should be part of the public ABI; move the event
declaration into the IStateProverPointer interface and remove the duplicate from
StateProverPointer so the interface exposes the event to off‑chain indexers.
Ensure the event signature (ImplementationAddressSet(uint256 indexed newVersion,
address newImplementationAddress, bytes32 newCodeHash, address
oldImplementationAddress)) is identical in IStateProverPointer, update any
imports or references if needed, and run/update tests that assert the event is
emitted by StateProverPointer (no other code changes required).

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
src/contracts/interfaces/IStateProverPointer.sol (2)

11-17: Event addition is correct and well-structured — consider indexing newImplementationAddress.

The core fix is solid. One ergonomic gap: relayers and indexers whose primary task is detecting a specific prover's upgrade will want to filter logs by implementation address, but newImplementationAddress is not indexed. newVersion alone forces consumers to scan all ImplementationAddressSet events across all pointer contracts rather than issuing a targeted filter by address.

Since only one of the three available indexed slots is used, adding a second index here costs nothing:

♻️ Suggested tweak
     event ImplementationAddressSet(
         uint256 indexed newVersion,
-        address newImplementationAddress,
+        address indexed newImplementationAddress,
         bytes32 newCodeHash,
         address oldImplementationAddress
     );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/contracts/interfaces/IStateProverPointer.sol` around lines 11 - 17, The
event ImplementationAddressSet should index the newImplementationAddress to
allow log filtering by implementation; update the event declaration for
ImplementationAddressSet to add the indexed keyword to the
newImplementationAddress parameter so relayers/indexers can efficiently filter
by that address while preserving existing fields (newVersion, newCodeHash,
oldImplementationAddress).

11-17: setImplementationAddress is absent from the interface.

The interface declares the event emitted by setImplementationAddress but not the function itself. Consumers of IStateProverPointer can observe the event ABI but cannot call the mutation through this interface, which may be intentional (access-controlled admin function) or an oversight. If any cross-chain tooling or governance contract needs to invoke the setter polymorphically via this interface, consider adding the declaration:

/// `@notice` Set a new implementation address.
function setImplementationAddress(address newImplementation) external;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/contracts/interfaces/IStateProverPointer.sol` around lines 11 - 17, The
interface IStateProverPointer currently only declares the
ImplementationAddressSet event but omits the setter function; add a declaration
for function setImplementationAddress(address newImplementation) external (with
an appropriate NatSpec line like "/// `@notice` Set a new implementation
address.") so callers can invoke the mutation polymorphically; ensure the
function name setImplementationAddress and the event ImplementationAddressSet
remain consistent with the implementing contract's signature and access control.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/contracts/interfaces/IStateProverPointer.sol`:
- Around line 11-17: The event ImplementationAddressSet should index the
newImplementationAddress to allow log filtering by implementation; update the
event declaration for ImplementationAddressSet to add the indexed keyword to the
newImplementationAddress parameter so relayers/indexers can efficiently filter
by that address while preserving existing fields (newVersion, newCodeHash,
oldImplementationAddress).
- Around line 11-17: The interface IStateProverPointer currently only declares
the ImplementationAddressSet event but omits the setter function; add a
declaration for function setImplementationAddress(address newImplementation)
external (with an appropriate NatSpec line like "/// `@notice` Set a new
implementation address.") so callers can invoke the mutation polymorphically;
ensure the function name setImplementationAddress and the event
ImplementationAddressSet remain consistent with the implementing contract's
signature and access control.

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.

2 participants

Comments