From 45127624470b4b72654139d6351e73a0e8f5b8e6 Mon Sep 17 00:00:00 2001 From: Leo Date: Tue, 29 Jul 2025 17:39:21 +0400 Subject: [PATCH 01/12] added initiliser to checkpoint tracker comment event --- src/protocol/CheckpointTracker.sol | 25 +++++++++++++++++++++---- src/protocol/ICheckpointTracker.sol | 8 ++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/protocol/CheckpointTracker.sol b/src/protocol/CheckpointTracker.sol index 42cb6ea1..2f63b869 100644 --- a/src/protocol/CheckpointTracker.sol +++ b/src/protocol/CheckpointTracker.sol @@ -16,14 +16,22 @@ contract CheckpointTracker is ICheckpointTracker { IInbox public immutable inbox; IVerifier public immutable verifier; ICommitmentStore public immutable commitmentStore; - address public immutable proverManager; + + address public proverManager; + + bool private _proverManagerInitialized; + + /// @dev Modifier to check if proverManager has been initialized + modifier checkProverInitialized() { + require(_proverManagerInitialized, "ProverManager not initialized"); + _; + } /// @param _genesis the checkpoint commitment describing the initial state of the rollup /// @param _inbox the inbox contract that contains the publication feed /// @param _verifier a contract that can verify the validity of a transition from one checkpoint to another - /// @param _proverManager contract responsible for managing the prover auction /// @param _commitmentStore contract responsible storing historical commitments - constructor(bytes32 _genesis, address _inbox, address _verifier, address _proverManager, address _commitmentStore) { + constructor(bytes32 _genesis, address _inbox, address _verifier, address _commitmentStore) { // set the genesis checkpoint commitment of the rollup - genesis is trusted to be correct require(_genesis != 0, "genesis checkpoint commitment cannot be 0"); inbox = IInbox(_inbox); @@ -31,17 +39,26 @@ contract CheckpointTracker is ICheckpointTracker { verifier = IVerifier(_verifier); commitmentStore = ICommitmentStore(_commitmentStore); - proverManager = _proverManager; _saveCommitment(latestPublicationId, _genesis); } + /// @inheritdoc ICheckpointTracker + /// @dev Can only be called once, allowed prover manager to be zero + function initializeProverManager(address _proverManager) external { + require(!_proverManagerInitialized, "ProverManager already initialized"); + proverManager = _proverManager; + _proverManagerInitialized = true; + emit ProverManagerInitialized(_proverManager); + } + /// @inheritdoc ICheckpointTracker /// @dev Accepts the last proven checkpoint (or an older one) as the start checkpoint. The reason we allow for an /// older checkpoint is to prevent cases where a prover spends time generating a larger proof and the checkpoint /// changes in the mean time. function proveTransition(Checkpoint calldata start, Checkpoint calldata end, bytes calldata proof) external + checkProverInitialized returns (uint256 numPublications, uint256 numDelayedPublications) { require( diff --git a/src/protocol/ICheckpointTracker.sol b/src/protocol/ICheckpointTracker.sol index 363aba61..f6943a5f 100644 --- a/src/protocol/ICheckpointTracker.sol +++ b/src/protocol/ICheckpointTracker.sol @@ -18,9 +18,17 @@ interface ICheckpointTracker { /// @param commitment the commitment of the latest proven checkpoint event CommitmentSaved(uint256 indexed publicationId, bytes32 commitment); + /// @notice Emitted when ProverManager is initialized + /// @param proverManager The address of the ProverManager contract + event ProverManagerInitialized(address indexed proverManager); + /// @return _ The last proven publication ID function provenPublicationId() external view returns (uint256); + /// @notice Initialize the proverManager address + /// @param _proverManager The address of the ProverManager contract + function initializeProverManager(address _proverManager) external; + /// @notice Verifies a transition between two checkpoints. Update the latest `provenCheckpoint` if possible /// @param start The initial checkpoint before the transition /// @param end The final checkpoint after the transition From 3a7f2ca738e33985454d2798bc2a465e307f8db9 Mon Sep 17 00:00:00 2001 From: Leo Date: Tue, 29 Jul 2025 18:04:24 +0400 Subject: [PATCH 02/12] remove circular import --- src/libs/LibProvingPeriod.sol | 4 +-- src/protocol/BaseProverManager.sol | 2 +- src/protocol/CheckpointTracker.sol | 23 ++++++++----- src/protocol/ICheckpointTracker.sol | 4 +-- test/CheckpointTracker.t.sol | 50 +++++++++++++++++++++------- test/mocks/MockCheckpointTracker.sol | 2 ++ 6 files changed, 59 insertions(+), 26 deletions(-) diff --git a/src/libs/LibProvingPeriod.sol b/src/libs/LibProvingPeriod.sol index f1b7f8e5..a3f1324f 100644 --- a/src/libs/LibProvingPeriod.sol +++ b/src/libs/LibProvingPeriod.sol @@ -52,7 +52,7 @@ library LibProvingPeriod { period.stake = stake; } - /// @dev Whether the period has been initialized + /// @dev Whether the period has been initialised function isInitialized(Period storage period) internal view returns (bool) { return period.prover != address(0); } @@ -87,7 +87,7 @@ library LibProvingPeriod { return block.timestamp > period.deadline && period.deadline != 0; } - /// @dev Whether the period is vacant (i.e. not initialized and open) + /// @dev Whether the period is vacant (i.e. not initialised and open) function isVacant(Period storage period) internal view returns (bool) { return !isInitialized(period) && isOpen(period); } diff --git a/src/protocol/BaseProverManager.sol b/src/protocol/BaseProverManager.sol index 09e02413..65372487 100644 --- a/src/protocol/BaseProverManager.sol +++ b/src/protocol/BaseProverManager.sol @@ -71,7 +71,7 @@ abstract contract BaseProverManager is IProposerFees, IProverManager, BalanceAcc /// @dev Proving period is closed error ProvingPeriodClosed(); - /// @dev Period is not initialized + /// @dev Period is not initialised error PeriodNotInitialized(); /// @dev Offered fee is higher than the required fee diff --git a/src/protocol/CheckpointTracker.sol b/src/protocol/CheckpointTracker.sol index 2f63b869..486bc6fb 100644 --- a/src/protocol/CheckpointTracker.sol +++ b/src/protocol/CheckpointTracker.sol @@ -6,7 +6,9 @@ import {ICommitmentStore} from "./ICommitmentStore.sol"; import {IInbox} from "./IInbox.sol"; import {IVerifier} from "./IVerifier.sol"; -contract CheckpointTracker is ICheckpointTracker { +import {Ownable} from "openzeppelin-contracts/contracts/access/Ownable.sol"; + +contract CheckpointTracker is ICheckpointTracker, Ownable { /// @dev The number of delayed publications up to the proven checkpoint uint256 private _totalDelayedPublications; @@ -19,11 +21,11 @@ contract CheckpointTracker is ICheckpointTracker { address public proverManager; - bool private _proverManagerInitialized; + bool private _proverManagerInitialised; - /// @dev Modifier to check if proverManager has been initialized + /// @dev Modifier to check if proverManager has been initialised modifier checkProverInitialized() { - require(_proverManagerInitialized, "ProverManager not initialized"); + require(_proverManagerInitialised, "ProverManager not initialised"); _; } @@ -31,7 +33,10 @@ contract CheckpointTracker is ICheckpointTracker { /// @param _inbox the inbox contract that contains the publication feed /// @param _verifier a contract that can verify the validity of a transition from one checkpoint to another /// @param _commitmentStore contract responsible storing historical commitments - constructor(bytes32 _genesis, address _inbox, address _verifier, address _commitmentStore) { + /// @param _owner Owner that is allowed to set prover manager address + constructor(bytes32 _genesis, address _inbox, address _verifier, address _commitmentStore, address _owner) + Ownable(_owner) + { // set the genesis checkpoint commitment of the rollup - genesis is trusted to be correct require(_genesis != 0, "genesis checkpoint commitment cannot be 0"); inbox = IInbox(_inbox); @@ -45,11 +50,11 @@ contract CheckpointTracker is ICheckpointTracker { /// @inheritdoc ICheckpointTracker /// @dev Can only be called once, allowed prover manager to be zero - function initializeProverManager(address _proverManager) external { - require(!_proverManagerInitialized, "ProverManager already initialized"); + function initializeProverManager(address _proverManager) external onlyOwner { + require(!_proverManagerInitialised, "ProverManager already initialised"); proverManager = _proverManager; - _proverManagerInitialized = true; - emit ProverManagerInitialized(_proverManager); + _proverManagerInitialised = true; + emit ProverManagerInitialised(_proverManager); } /// @inheritdoc ICheckpointTracker diff --git a/src/protocol/ICheckpointTracker.sol b/src/protocol/ICheckpointTracker.sol index f6943a5f..6ecad01b 100644 --- a/src/protocol/ICheckpointTracker.sol +++ b/src/protocol/ICheckpointTracker.sol @@ -18,9 +18,9 @@ interface ICheckpointTracker { /// @param commitment the commitment of the latest proven checkpoint event CommitmentSaved(uint256 indexed publicationId, bytes32 commitment); - /// @notice Emitted when ProverManager is initialized + /// @notice Emitted when ProverManager is initialised /// @param proverManager The address of the ProverManager contract - event ProverManagerInitialized(address indexed proverManager); + event ProverManagerInitialised(address indexed proverManager); /// @return _ The last proven publication ID function provenPublicationId() external view returns (uint256); diff --git a/test/CheckpointTracker.t.sol b/test/CheckpointTracker.t.sol index edcb6d12..b86bbe70 100644 --- a/test/CheckpointTracker.t.sol +++ b/test/CheckpointTracker.t.sol @@ -8,13 +8,16 @@ import {CheckpointTracker} from "src/protocol/CheckpointTracker.sol"; import {ICheckpointTracker} from "src/protocol/ICheckpointTracker.sol"; import {SignalService} from "src/protocol/SignalService.sol"; +import {Ownable} from "openzeppelin-contracts/contracts/access/Ownable.sol"; + contract CheckpointTrackerTest is Test { CheckpointTracker tracker; MockInbox inbox; MockVerifier verifier; SignalService signalService; - address proverManager = _randomAddress("proverManager"); + address proverManager = makeAddr("proverManager"); bytes32 genesis = keccak256(abi.encode("genesis")); + address owner = makeAddr("owner"); ICheckpointTracker.Checkpoint start; ICheckpointTracker.Checkpoint end; @@ -24,13 +27,14 @@ contract CheckpointTrackerTest is Test { inbox = new MockInbox(); verifier = new MockVerifier(); signalService = new SignalService(); - tracker = - new CheckpointTracker(genesis, address(inbox), address(verifier), proverManager, address(signalService)); + tracker = new CheckpointTracker(genesis, address(inbox), address(verifier), address(signalService), owner); + vm.prank(owner); + tracker.initializeProverManager(address(proverManager)); } function test_constructor_shouldRevertWithZeroGenesis() public { vm.expectRevert("genesis checkpoint commitment cannot be 0"); - new CheckpointTracker(bytes32(0), address(inbox), address(verifier), proverManager, address(signalService)); + new CheckpointTracker(bytes32(0), address(inbox), address(verifier), address(signalService), owner); } function test_constructor_shouldSetExternalContracts() public view { @@ -49,6 +53,36 @@ contract CheckpointTrackerTest is Test { assertEq(savedCommitment, genesis, "Did not save genesis"); } + function test_initialiseProverManager_shouldSetProverManager() public { + CheckpointTracker uninitialisedTracker = + new CheckpointTracker(genesis, address(inbox), address(verifier), address(signalService), owner); + vm.prank(owner); + uninitialisedTracker.initializeProverManager(proverManager); + assertEq(address(uninitialisedTracker.proverManager()), proverManager, "Did not set prover manager"); + } + + function test_proveTransition_shouldRevertIfProverManagerNotInitialised() public { + CheckpointTracker uninitialisedTracker = + new CheckpointTracker(genesis, address(inbox), address(verifier), address(signalService), owner); + _constructValidTransition(); + vm.expectRevert("ProverManager not initialised"); + uninitialisedTracker.proveTransition(start, end, proof); + } + + function test_initialiseProverManager_onlyOwner() public { + CheckpointTracker uninitialisedTracker = + new CheckpointTracker(genesis, address(inbox), address(verifier), address(signalService), owner); + vm.prank(makeAddr("notowner")); + vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, makeAddr("notowner"))); + uninitialisedTracker.initializeProverManager(proverManager); + } + + function test_initialiseProverManager_shouldRevertIfAlreadyInitialised() public { + vm.prank(owner); + vm.expectRevert("ProverManager already initialised"); + tracker.initializeProverManager(address(proverManager)); + } + function test_proveTransition_shouldRevertIfNotCalledByProverManager() public { _constructValidTransition(); vm.expectRevert("Only the prover manager can call this function"); @@ -178,12 +212,4 @@ contract CheckpointTrackerTest is Test { end.commitment = keccak256(abi.encode("end")); end.totalDelayedPublications = start.totalDelayedPublications + 2; } - - function _randomAddress(string memory name) internal pure returns (address) { - return address(uint160(uint256(keccak256(abi.encode(_domainSeparator(), name))))); - } - - function _domainSeparator() internal pure returns (bytes32) { - return keccak256("CheckpointTracker"); - } } diff --git a/test/mocks/MockCheckpointTracker.sol b/test/mocks/MockCheckpointTracker.sol index 55ec8070..8eb0ae54 100644 --- a/test/mocks/MockCheckpointTracker.sol +++ b/test/mocks/MockCheckpointTracker.sol @@ -24,6 +24,8 @@ contract MockCheckpointTracker is ICheckpointTracker { return (nPublications, nDelayedPublications); } + function initializeProverManager(address _proverManager) external {} + function setValid(bool isValid) external { valid = isValid; } From 27194f3f62a74b7a83149f6c976744dcd13ce94c Mon Sep 17 00:00:00 2001 From: Leo Date: Tue, 29 Jul 2025 18:15:24 +0400 Subject: [PATCH 03/12] add same check in inbox --- src/protocol/IInbox.sol | 7 +++++ src/protocol/taiko_alethia/TaikoInbox.sol | 34 +++++++++++++++++------ test/mocks/MockInbox.sol | 4 +++ 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/protocol/IInbox.sol b/src/protocol/IInbox.sol index e02778e8..4e4c7ee9 100644 --- a/src/protocol/IInbox.sol +++ b/src/protocol/IInbox.sol @@ -17,6 +17,9 @@ interface IInbox { /// @param attributes The data contained within the publication event Published(bytes32 indexed pubHash, PublicationHeader header, bytes[] attributes); + /// @dev Event emitted when ProposerFees is initialised + event ProposerFeesInitialised(address proposerFees); + /// @notice Publish blobs with anchor block information /// @param nBlobs Number of blobs to publish /// @param anchorBlockId The anchor block ID @@ -35,4 +38,8 @@ interface IInbox { /// @param header The header to validate /// @return _ True if the header is valid, false otherwise function validateHeader(PublicationHeader calldata header) external view returns (bool); + + /// @notice Initialize the proposer fees contract address (prover manager contract) + /// @param _proposerFees Address of the proposer fees contract + function initializeProposerFees(address _proposerFees) external; } diff --git a/src/protocol/taiko_alethia/TaikoInbox.sol b/src/protocol/taiko_alethia/TaikoInbox.sol index b2fdb329..25836947 100644 --- a/src/protocol/taiko_alethia/TaikoInbox.sol +++ b/src/protocol/taiko_alethia/TaikoInbox.sol @@ -10,7 +10,9 @@ import {IInbox} from "../IInbox.sol"; import {ILookahead} from "../ILookahead.sol"; import {IProposerFees} from "../IProposerFees.sol"; -contract TaikoInbox is IInbox, DelayedInclusionStore { +import {Ownable} from "openzeppelin-contracts/contracts/access/Ownable.sol"; + +contract TaikoInbox is IInbox, DelayedInclusionStore, Ownable { /// @dev Caller is not the current preconfer error NotCurrentPreconfer(); /// @dev Anchor block ID is too old @@ -25,9 +27,17 @@ contract TaikoInbox is IInbox, DelayedInclusionStore { } ILookahead public immutable lookahead; - IProposerFees public immutable proposerFees; + IProposerFees public proposerFees; uint256 public immutable maxAnchorBlockIdOffset; + bool private proposerFeesInitialised; + + /// @dev Modifier to check if proposerFees has been initialised + modifier checkProposerFeesInitialized() { + require(proposerFeesInitialised, "ProposerFees not initialised"); + _; + } + // attributes associated with the publication uint256 private constant METADATA = 0; uint256 private constant BLOB_REFERENCE = 1; @@ -39,21 +49,27 @@ contract TaikoInbox is IInbox, DelayedInclusionStore { address _lookahead, address _blobRefRegistry, uint256 _maxAnchorBlockIdOffset, - address _proposerFees, - uint256 _inclusionDelay - ) DelayedInclusionStore(_inclusionDelay, _blobRefRegistry) { - require(_proposerFees != address(0), "Invalid proposer fees address"); - + uint256 _inclusionDelay, + address _owner + ) DelayedInclusionStore(_inclusionDelay, _blobRefRegistry) Ownable(_owner) { lookahead = ILookahead(_lookahead); maxAnchorBlockIdOffset = _maxAnchorBlockIdOffset; - proposerFees = IProposerFees(_proposerFees); // guarantee there is always a previous hash _publicationHashes.push(0); } /// @inheritdoc IInbox - function publish(uint256 nBlobs, uint64 anchorBlockId) external { + function initializeProposerFees(address _proposerFees) external onlyOwner { + require(!proposerFeesInitialised, "ProposerFees already initialised"); + require(_proposerFees != address(0), "ProposerFees cannot be zero"); + proposerFees = IProposerFees(_proposerFees); + proposerFeesInitialised = true; + emit ProposerFeesInitialised(_proposerFees); + } + + /// @inheritdoc IInbox + function publish(uint256 nBlobs, uint64 anchorBlockId) external checkProposerFeesInitialized { if (address(lookahead) != address(0)) { require(lookahead.isCurrentPreconfer(msg.sender), NotCurrentPreconfer()); } diff --git a/test/mocks/MockInbox.sol b/test/mocks/MockInbox.sol index 17d39984..ede59e06 100644 --- a/test/mocks/MockInbox.sol +++ b/test/mocks/MockInbox.sol @@ -16,6 +16,10 @@ contract MockInbox is IInbox { revert NotImplemented(); } + function initializeProposerFees(address) external pure { + revert NotImplemented(); + } + function getPublicationHash(uint256 id) external view returns (bytes32) { if (id >= numPublications) return 0; return keccak256(abi.encode("MockInbox", id)); From b20e6e0a60162a0bc59870b3242a02da6114e8bc Mon Sep 17 00:00:00 2001 From: Leo Date: Tue, 29 Jul 2025 18:36:40 +0400 Subject: [PATCH 04/12] remove unnecessary init var --- src/protocol/taiko_alethia/TaikoInbox.sol | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/protocol/taiko_alethia/TaikoInbox.sol b/src/protocol/taiko_alethia/TaikoInbox.sol index 25836947..58353279 100644 --- a/src/protocol/taiko_alethia/TaikoInbox.sol +++ b/src/protocol/taiko_alethia/TaikoInbox.sol @@ -30,11 +30,9 @@ contract TaikoInbox is IInbox, DelayedInclusionStore, Ownable { IProposerFees public proposerFees; uint256 public immutable maxAnchorBlockIdOffset; - bool private proposerFeesInitialised; - /// @dev Modifier to check if proposerFees has been initialised modifier checkProposerFeesInitialized() { - require(proposerFeesInitialised, "ProposerFees not initialised"); + require(address(proposerFees) != address(0), "ProposerFees not initialised"); _; } @@ -61,10 +59,9 @@ contract TaikoInbox is IInbox, DelayedInclusionStore, Ownable { /// @inheritdoc IInbox function initializeProposerFees(address _proposerFees) external onlyOwner { - require(!proposerFeesInitialised, "ProposerFees already initialised"); + require(address(proposerFees) != address(0), "ProposerFees already initialised"); require(_proposerFees != address(0), "ProposerFees cannot be zero"); proposerFees = IProposerFees(_proposerFees); - proposerFeesInitialised = true; emit ProposerFeesInitialised(_proposerFees); } From 798ee0beada48520d62273de9b2898cebe23c19e Mon Sep 17 00:00:00 2001 From: Leo Date: Tue, 29 Jul 2025 18:37:49 +0400 Subject: [PATCH 05/12] add comment --- src/protocol/CheckpointTracker.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/src/protocol/CheckpointTracker.sol b/src/protocol/CheckpointTracker.sol index 486bc6fb..3507d886 100644 --- a/src/protocol/CheckpointTracker.sol +++ b/src/protocol/CheckpointTracker.sol @@ -21,6 +21,7 @@ contract CheckpointTracker is ICheckpointTracker, Ownable { address public proverManager; + /// @dev need to track initialisation separately to address as initialised prover manager may be zero bool private _proverManagerInitialised; /// @dev Modifier to check if proverManager has been initialised From 7262781e3fd295fc3dcf64d21b2c081f881c0828 Mon Sep 17 00:00:00 2001 From: Leo Date: Thu, 31 Jul 2025 14:48:14 +0400 Subject: [PATCH 06/12] s -> z --- src/libs/LibProvingPeriod.sol | 4 ++-- src/protocol/BaseProverManager.sol | 2 +- src/protocol/CheckpointTracker.sol | 14 ++++++------ src/protocol/ICheckpointTracker.sol | 4 ++-- src/protocol/IInbox.sol | 4 ++-- src/protocol/taiko_alethia/TaikoInbox.sol | 8 +++---- test/CheckpointTracker.t.sol | 26 +++++++++++------------ 7 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/libs/LibProvingPeriod.sol b/src/libs/LibProvingPeriod.sol index a3f1324f..f1b7f8e5 100644 --- a/src/libs/LibProvingPeriod.sol +++ b/src/libs/LibProvingPeriod.sol @@ -52,7 +52,7 @@ library LibProvingPeriod { period.stake = stake; } - /// @dev Whether the period has been initialised + /// @dev Whether the period has been initialized function isInitialized(Period storage period) internal view returns (bool) { return period.prover != address(0); } @@ -87,7 +87,7 @@ library LibProvingPeriod { return block.timestamp > period.deadline && period.deadline != 0; } - /// @dev Whether the period is vacant (i.e. not initialised and open) + /// @dev Whether the period is vacant (i.e. not initialized and open) function isVacant(Period storage period) internal view returns (bool) { return !isInitialized(period) && isOpen(period); } diff --git a/src/protocol/BaseProverManager.sol b/src/protocol/BaseProverManager.sol index 65372487..09e02413 100644 --- a/src/protocol/BaseProverManager.sol +++ b/src/protocol/BaseProverManager.sol @@ -71,7 +71,7 @@ abstract contract BaseProverManager is IProposerFees, IProverManager, BalanceAcc /// @dev Proving period is closed error ProvingPeriodClosed(); - /// @dev Period is not initialised + /// @dev Period is not initialized error PeriodNotInitialized(); /// @dev Offered fee is higher than the required fee diff --git a/src/protocol/CheckpointTracker.sol b/src/protocol/CheckpointTracker.sol index 3507d886..d64a6ef0 100644 --- a/src/protocol/CheckpointTracker.sol +++ b/src/protocol/CheckpointTracker.sol @@ -21,12 +21,12 @@ contract CheckpointTracker is ICheckpointTracker, Ownable { address public proverManager; - /// @dev need to track initialisation separately to address as initialised prover manager may be zero - bool private _proverManagerInitialised; + /// @dev need to track initialisation separately to address as initialized prover manager may be zero + bool private _proverManagerInitialized; - /// @dev Modifier to check if proverManager has been initialised + /// @dev Modifier to check if proverManager has been initialized modifier checkProverInitialized() { - require(_proverManagerInitialised, "ProverManager not initialised"); + require(_proverManagerInitialized, "ProverManager not initialized"); _; } @@ -52,10 +52,10 @@ contract CheckpointTracker is ICheckpointTracker, Ownable { /// @inheritdoc ICheckpointTracker /// @dev Can only be called once, allowed prover manager to be zero function initializeProverManager(address _proverManager) external onlyOwner { - require(!_proverManagerInitialised, "ProverManager already initialised"); + require(!_proverManagerInitialized, "ProverManager already initialized"); proverManager = _proverManager; - _proverManagerInitialised = true; - emit ProverManagerInitialised(_proverManager); + _proverManagerInitialized = true; + emit ProverManagerInitialized(_proverManager); } /// @inheritdoc ICheckpointTracker diff --git a/src/protocol/ICheckpointTracker.sol b/src/protocol/ICheckpointTracker.sol index 6ecad01b..f6943a5f 100644 --- a/src/protocol/ICheckpointTracker.sol +++ b/src/protocol/ICheckpointTracker.sol @@ -18,9 +18,9 @@ interface ICheckpointTracker { /// @param commitment the commitment of the latest proven checkpoint event CommitmentSaved(uint256 indexed publicationId, bytes32 commitment); - /// @notice Emitted when ProverManager is initialised + /// @notice Emitted when ProverManager is initialized /// @param proverManager The address of the ProverManager contract - event ProverManagerInitialised(address indexed proverManager); + event ProverManagerInitialized(address indexed proverManager); /// @return _ The last proven publication ID function provenPublicationId() external view returns (uint256); diff --git a/src/protocol/IInbox.sol b/src/protocol/IInbox.sol index 4e4c7ee9..db3a5847 100644 --- a/src/protocol/IInbox.sol +++ b/src/protocol/IInbox.sol @@ -17,8 +17,8 @@ interface IInbox { /// @param attributes The data contained within the publication event Published(bytes32 indexed pubHash, PublicationHeader header, bytes[] attributes); - /// @dev Event emitted when ProposerFees is initialised - event ProposerFeesInitialised(address proposerFees); + /// @dev Event emitted when ProposerFees is initialized + event ProposerFeesInitialized(address proposerFees); /// @notice Publish blobs with anchor block information /// @param nBlobs Number of blobs to publish diff --git a/src/protocol/taiko_alethia/TaikoInbox.sol b/src/protocol/taiko_alethia/TaikoInbox.sol index 58353279..04986bd4 100644 --- a/src/protocol/taiko_alethia/TaikoInbox.sol +++ b/src/protocol/taiko_alethia/TaikoInbox.sol @@ -30,9 +30,9 @@ contract TaikoInbox is IInbox, DelayedInclusionStore, Ownable { IProposerFees public proposerFees; uint256 public immutable maxAnchorBlockIdOffset; - /// @dev Modifier to check if proposerFees has been initialised + /// @dev Modifier to check if proposerFees has been initialized modifier checkProposerFeesInitialized() { - require(address(proposerFees) != address(0), "ProposerFees not initialised"); + require(address(proposerFees) != address(0), "ProposerFees not initialized"); _; } @@ -59,10 +59,10 @@ contract TaikoInbox is IInbox, DelayedInclusionStore, Ownable { /// @inheritdoc IInbox function initializeProposerFees(address _proposerFees) external onlyOwner { - require(address(proposerFees) != address(0), "ProposerFees already initialised"); + require(address(proposerFees) != address(0), "ProposerFees already initialized"); require(_proposerFees != address(0), "ProposerFees cannot be zero"); proposerFees = IProposerFees(_proposerFees); - emit ProposerFeesInitialised(_proposerFees); + emit ProposerFeesInitialized(_proposerFees); } /// @inheritdoc IInbox diff --git a/test/CheckpointTracker.t.sol b/test/CheckpointTracker.t.sol index b86bbe70..05eb62c6 100644 --- a/test/CheckpointTracker.t.sol +++ b/test/CheckpointTracker.t.sol @@ -53,33 +53,33 @@ contract CheckpointTrackerTest is Test { assertEq(savedCommitment, genesis, "Did not save genesis"); } - function test_initialiseProverManager_shouldSetProverManager() public { - CheckpointTracker uninitialisedTracker = + function test_initializeProverManager_shouldSetProverManager() public { + CheckpointTracker uninitializedTracker = new CheckpointTracker(genesis, address(inbox), address(verifier), address(signalService), owner); vm.prank(owner); - uninitialisedTracker.initializeProverManager(proverManager); - assertEq(address(uninitialisedTracker.proverManager()), proverManager, "Did not set prover manager"); + uninitializedTracker.initializeProverManager(proverManager); + assertEq(address(uninitializedTracker.proverManager()), proverManager, "Did not set prover manager"); } - function test_proveTransition_shouldRevertIfProverManagerNotInitialised() public { - CheckpointTracker uninitialisedTracker = + function test_proveTransition_shouldRevertIfProverManagerNotInitialized() public { + CheckpointTracker uninitializedTracker = new CheckpointTracker(genesis, address(inbox), address(verifier), address(signalService), owner); _constructValidTransition(); - vm.expectRevert("ProverManager not initialised"); - uninitialisedTracker.proveTransition(start, end, proof); + vm.expectRevert("ProverManager not initialized"); + uninitializedTracker.proveTransition(start, end, proof); } - function test_initialiseProverManager_onlyOwner() public { - CheckpointTracker uninitialisedTracker = + function test_initializeProverManager_onlyOwner() public { + CheckpointTracker uninitializedTracker = new CheckpointTracker(genesis, address(inbox), address(verifier), address(signalService), owner); vm.prank(makeAddr("notowner")); vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, makeAddr("notowner"))); - uninitialisedTracker.initializeProverManager(proverManager); + uninitializedTracker.initializeProverManager(proverManager); } - function test_initialiseProverManager_shouldRevertIfAlreadyInitialised() public { + function test_initializeProverManager_shouldRevertIfAlreadyInitialized() public { vm.prank(owner); - vm.expectRevert("ProverManager already initialised"); + vm.expectRevert("ProverManager already initialized"); tracker.initializeProverManager(address(proverManager)); } From 0bf4d7e6e1805426a01147a24c3f379821667c9e Mon Sep 17 00:00:00 2001 From: Leo Date: Thu, 31 Jul 2025 15:18:33 +0400 Subject: [PATCH 07/12] address bug --- src/protocol/taiko_alethia/TaikoInbox.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/protocol/taiko_alethia/TaikoInbox.sol b/src/protocol/taiko_alethia/TaikoInbox.sol index 04986bd4..65438464 100644 --- a/src/protocol/taiko_alethia/TaikoInbox.sol +++ b/src/protocol/taiko_alethia/TaikoInbox.sol @@ -59,7 +59,7 @@ contract TaikoInbox is IInbox, DelayedInclusionStore, Ownable { /// @inheritdoc IInbox function initializeProposerFees(address _proposerFees) external onlyOwner { - require(address(proposerFees) != address(0), "ProposerFees already initialized"); + require(address(proposerFees) == address(0), "ProposerFees already initialized"); require(_proposerFees != address(0), "ProposerFees cannot be zero"); proposerFees = IProposerFees(_proposerFees); emit ProposerFeesInitialized(_proposerFees); From ef30453396067c083f5dd7879436e29ae39e9898 Mon Sep 17 00:00:00 2001 From: Leo Date: Thu, 31 Jul 2025 15:36:46 +0400 Subject: [PATCH 08/12] update to remove ownable --- src/protocol/CheckpointTracker.sol | 21 ++++++----- src/protocol/taiko_alethia/TaikoInbox.sol | 44 ++++++++++++++++------- test/CheckpointTracker.t.sol | 28 +++++++-------- 3 files changed, 59 insertions(+), 34 deletions(-) diff --git a/src/protocol/CheckpointTracker.sol b/src/protocol/CheckpointTracker.sol index d64a6ef0..60df9e17 100644 --- a/src/protocol/CheckpointTracker.sol +++ b/src/protocol/CheckpointTracker.sol @@ -6,9 +6,7 @@ import {ICommitmentStore} from "./ICommitmentStore.sol"; import {IInbox} from "./IInbox.sol"; import {IVerifier} from "./IVerifier.sol"; -import {Ownable} from "openzeppelin-contracts/contracts/access/Ownable.sol"; - -contract CheckpointTracker is ICheckpointTracker, Ownable { +contract CheckpointTracker is ICheckpointTracker { /// @dev The number of delayed publications up to the proven checkpoint uint256 private _totalDelayedPublications; @@ -24,20 +22,25 @@ contract CheckpointTracker is ICheckpointTracker, Ownable { /// @dev need to track initialisation separately to address as initialized prover manager may be zero bool private _proverManagerInitialized; + address private immutable deployer; + /// @dev Modifier to check if proverManager has been initialized modifier checkProverInitialized() { require(_proverManagerInitialized, "ProverManager not initialized"); _; } + /// @dev Modifier to check if the caller is the deployer + modifier onlyDeployer() { + require(msg.sender == deployer, "Only deployer can call this function"); + _; + } + /// @param _genesis the checkpoint commitment describing the initial state of the rollup /// @param _inbox the inbox contract that contains the publication feed /// @param _verifier a contract that can verify the validity of a transition from one checkpoint to another /// @param _commitmentStore contract responsible storing historical commitments - /// @param _owner Owner that is allowed to set prover manager address - constructor(bytes32 _genesis, address _inbox, address _verifier, address _commitmentStore, address _owner) - Ownable(_owner) - { + constructor(bytes32 _genesis, address _inbox, address _verifier, address _commitmentStore) { // set the genesis checkpoint commitment of the rollup - genesis is trusted to be correct require(_genesis != 0, "genesis checkpoint commitment cannot be 0"); inbox = IInbox(_inbox); @@ -46,12 +49,14 @@ contract CheckpointTracker is ICheckpointTracker, Ownable { verifier = IVerifier(_verifier); commitmentStore = ICommitmentStore(_commitmentStore); + deployer = msg.sender; + _saveCommitment(latestPublicationId, _genesis); } /// @inheritdoc ICheckpointTracker /// @dev Can only be called once, allowed prover manager to be zero - function initializeProverManager(address _proverManager) external onlyOwner { + function initializeProverManager(address _proverManager) external onlyDeployer { require(!_proverManagerInitialized, "ProverManager already initialized"); proverManager = _proverManager; _proverManagerInitialized = true; diff --git a/src/protocol/taiko_alethia/TaikoInbox.sol b/src/protocol/taiko_alethia/TaikoInbox.sol index 65438464..89db3aca 100644 --- a/src/protocol/taiko_alethia/TaikoInbox.sol +++ b/src/protocol/taiko_alethia/TaikoInbox.sol @@ -10,9 +10,7 @@ import {IInbox} from "../IInbox.sol"; import {ILookahead} from "../ILookahead.sol"; import {IProposerFees} from "../IProposerFees.sol"; -import {Ownable} from "openzeppelin-contracts/contracts/access/Ownable.sol"; - -contract TaikoInbox is IInbox, DelayedInclusionStore, Ownable { +contract TaikoInbox is IInbox, DelayedInclusionStore { /// @dev Caller is not the current preconfer error NotCurrentPreconfer(); /// @dev Anchor block ID is too old @@ -30,9 +28,34 @@ contract TaikoInbox is IInbox, DelayedInclusionStore, Ownable { IProposerFees public proposerFees; uint256 public immutable maxAnchorBlockIdOffset; + address private immutable deployer; + + // Custom error (saves ~24 gas vs require with string) + error ProposerFeesNotInitialized(); + /// @dev Modifier to check if proposerFees has been initialized modifier checkProposerFeesInitialized() { - require(address(proposerFees) != address(0), "ProposerFees not initialized"); + assembly { + // Load proposerFees address directly from storage + // Assuming proposerFees is at storage slot 0 (adjust if needed) + if iszero(sload(0)) { + // Revert with custom error selector + mstore(0x00, 0x7c946ed7) // selector for ProposerFeesNotInitialized() + revert(0x00, 0x04) + } + } + _; + } + + // /// @dev Modifier to check if proposerFees has been initialized + // modifier checkProposerFeesInitialized() { + // require(address(proposerFees) != address(0), "ProposerFees not initialized"); + // _; + // } + + /// @dev Modifier to check if the caller is the deployer + modifier onlyDeployer() { + require(msg.sender == deployer, "Only deployer can call this function"); _; } @@ -43,22 +66,19 @@ contract TaikoInbox is IInbox, DelayedInclusionStore, Ownable { bytes32[] private _publicationHashes; - constructor( - address _lookahead, - address _blobRefRegistry, - uint256 _maxAnchorBlockIdOffset, - uint256 _inclusionDelay, - address _owner - ) DelayedInclusionStore(_inclusionDelay, _blobRefRegistry) Ownable(_owner) { + constructor(address _lookahead, address _blobRefRegistry, uint256 _maxAnchorBlockIdOffset, uint256 _inclusionDelay) + DelayedInclusionStore(_inclusionDelay, _blobRefRegistry) + { lookahead = ILookahead(_lookahead); maxAnchorBlockIdOffset = _maxAnchorBlockIdOffset; + deployer = msg.sender; // guarantee there is always a previous hash _publicationHashes.push(0); } /// @inheritdoc IInbox - function initializeProposerFees(address _proposerFees) external onlyOwner { + function initializeProposerFees(address _proposerFees) external onlyDeployer { require(address(proposerFees) == address(0), "ProposerFees already initialized"); require(_proposerFees != address(0), "ProposerFees cannot be zero"); proposerFees = IProposerFees(_proposerFees); diff --git a/test/CheckpointTracker.t.sol b/test/CheckpointTracker.t.sol index 05eb62c6..8e279611 100644 --- a/test/CheckpointTracker.t.sol +++ b/test/CheckpointTracker.t.sol @@ -8,8 +8,6 @@ import {CheckpointTracker} from "src/protocol/CheckpointTracker.sol"; import {ICheckpointTracker} from "src/protocol/ICheckpointTracker.sol"; import {SignalService} from "src/protocol/SignalService.sol"; -import {Ownable} from "openzeppelin-contracts/contracts/access/Ownable.sol"; - contract CheckpointTrackerTest is Test { CheckpointTracker tracker; MockInbox inbox; @@ -17,7 +15,7 @@ contract CheckpointTrackerTest is Test { SignalService signalService; address proverManager = makeAddr("proverManager"); bytes32 genesis = keccak256(abi.encode("genesis")); - address owner = makeAddr("owner"); + address deployer = makeAddr("deployer"); ICheckpointTracker.Checkpoint start; ICheckpointTracker.Checkpoint end; @@ -27,14 +25,15 @@ contract CheckpointTrackerTest is Test { inbox = new MockInbox(); verifier = new MockVerifier(); signalService = new SignalService(); - tracker = new CheckpointTracker(genesis, address(inbox), address(verifier), address(signalService), owner); - vm.prank(owner); + vm.startPrank(deployer); + tracker = new CheckpointTracker(genesis, address(inbox), address(verifier), address(signalService)); tracker.initializeProverManager(address(proverManager)); + vm.stopPrank(); } function test_constructor_shouldRevertWithZeroGenesis() public { vm.expectRevert("genesis checkpoint commitment cannot be 0"); - new CheckpointTracker(bytes32(0), address(inbox), address(verifier), address(signalService), owner); + new CheckpointTracker(bytes32(0), address(inbox), address(verifier), address(signalService)); } function test_constructor_shouldSetExternalContracts() public view { @@ -54,31 +53,32 @@ contract CheckpointTrackerTest is Test { } function test_initializeProverManager_shouldSetProverManager() public { + vm.startPrank(deployer); CheckpointTracker uninitializedTracker = - new CheckpointTracker(genesis, address(inbox), address(verifier), address(signalService), owner); - vm.prank(owner); + new CheckpointTracker(genesis, address(inbox), address(verifier), address(signalService)); uninitializedTracker.initializeProverManager(proverManager); assertEq(address(uninitializedTracker.proverManager()), proverManager, "Did not set prover manager"); } function test_proveTransition_shouldRevertIfProverManagerNotInitialized() public { CheckpointTracker uninitializedTracker = - new CheckpointTracker(genesis, address(inbox), address(verifier), address(signalService), owner); + new CheckpointTracker(genesis, address(inbox), address(verifier), address(signalService)); _constructValidTransition(); vm.expectRevert("ProverManager not initialized"); uninitializedTracker.proveTransition(start, end, proof); } - function test_initializeProverManager_onlyOwner() public { + function test_initializeProverManager_onlyDeployer() public { + vm.prank(deployer); CheckpointTracker uninitializedTracker = - new CheckpointTracker(genesis, address(inbox), address(verifier), address(signalService), owner); - vm.prank(makeAddr("notowner")); - vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, makeAddr("notowner"))); + new CheckpointTracker(genesis, address(inbox), address(verifier), address(signalService)); + vm.prank(makeAddr("notdeployer")); + vm.expectRevert("Only deployer can call this function"); uninitializedTracker.initializeProverManager(proverManager); } function test_initializeProverManager_shouldRevertIfAlreadyInitialized() public { - vm.prank(owner); + vm.prank(deployer); vm.expectRevert("ProverManager already initialized"); tracker.initializeProverManager(address(proverManager)); } From 043d88277fd2d5e0c5f06b630d81e4f8e8e785cc Mon Sep 17 00:00:00 2001 From: Leo Date: Thu, 31 Jul 2025 15:52:53 +0400 Subject: [PATCH 09/12] gas optimised approach --- src/protocol/CheckpointTracker.sol | 9 ++++++++- src/protocol/ICheckpointTracker.sol | 3 +++ src/protocol/taiko_alethia/TaikoInbox.sol | 17 +++++------------ test/CheckpointTracker.t.sol | 2 +- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/protocol/CheckpointTracker.sol b/src/protocol/CheckpointTracker.sol index 60df9e17..af31727e 100644 --- a/src/protocol/CheckpointTracker.sol +++ b/src/protocol/CheckpointTracker.sol @@ -26,7 +26,14 @@ contract CheckpointTracker is ICheckpointTracker { /// @dev Modifier to check if proverManager has been initialized modifier checkProverInitialized() { - require(_proverManagerInitialized, "ProverManager not initialized"); + bytes4 errorSelector = ProverManagerNotInitialized.selector; + assembly { + let initialized := sload(_proverManagerInitialized.slot) + if iszero(initialized) { + mstore(0, errorSelector) + revert(0x00, 0x04) + } + } _; } diff --git a/src/protocol/ICheckpointTracker.sol b/src/protocol/ICheckpointTracker.sol index f6943a5f..375a3227 100644 --- a/src/protocol/ICheckpointTracker.sol +++ b/src/protocol/ICheckpointTracker.sol @@ -22,6 +22,9 @@ interface ICheckpointTracker { /// @param proverManager The address of the ProverManager contract event ProverManagerInitialized(address indexed proverManager); + /// @notice Thrown when no prover manager was set + error ProverManagerNotInitialized(); + /// @return _ The last proven publication ID function provenPublicationId() external view returns (uint256); diff --git a/src/protocol/taiko_alethia/TaikoInbox.sol b/src/protocol/taiko_alethia/TaikoInbox.sol index 89db3aca..1eeb03b2 100644 --- a/src/protocol/taiko_alethia/TaikoInbox.sol +++ b/src/protocol/taiko_alethia/TaikoInbox.sol @@ -30,29 +30,22 @@ contract TaikoInbox is IInbox, DelayedInclusionStore { address private immutable deployer; - // Custom error (saves ~24 gas vs require with string) + /// @notice Thrown when no proposer fee address is set error ProposerFeesNotInitialized(); /// @dev Modifier to check if proposerFees has been initialized modifier checkProposerFeesInitialized() { + bytes4 errorSelector = ProposerFeesNotInitialized.selector; assembly { - // Load proposerFees address directly from storage - // Assuming proposerFees is at storage slot 0 (adjust if needed) - if iszero(sload(0)) { - // Revert with custom error selector - mstore(0x00, 0x7c946ed7) // selector for ProposerFeesNotInitialized() + let fees := sload(proposerFees.slot) + if iszero(fees) { + mstore(0, errorSelector) revert(0x00, 0x04) } } _; } - // /// @dev Modifier to check if proposerFees has been initialized - // modifier checkProposerFeesInitialized() { - // require(address(proposerFees) != address(0), "ProposerFees not initialized"); - // _; - // } - /// @dev Modifier to check if the caller is the deployer modifier onlyDeployer() { require(msg.sender == deployer, "Only deployer can call this function"); diff --git a/test/CheckpointTracker.t.sol b/test/CheckpointTracker.t.sol index 8e279611..8b995544 100644 --- a/test/CheckpointTracker.t.sol +++ b/test/CheckpointTracker.t.sol @@ -64,7 +64,7 @@ contract CheckpointTrackerTest is Test { CheckpointTracker uninitializedTracker = new CheckpointTracker(genesis, address(inbox), address(verifier), address(signalService)); _constructValidTransition(); - vm.expectRevert("ProverManager not initialized"); + vm.expectRevert(ICheckpointTracker.ProverManagerNotInitialized.selector); uninitializedTracker.proveTransition(start, end, proof); } From 4c01b06facdf4b5c0d10ee86b813407063e525d8 Mon Sep 17 00:00:00 2001 From: Leo Date: Mon, 4 Aug 2025 12:02:05 +0200 Subject: [PATCH 10/12] fix gas --- src/protocol/taiko_alethia/TaikoInbox.sol | 12 +++--------- test/Gas/Inbox.t.sol | 5 ++--- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/protocol/taiko_alethia/TaikoInbox.sol b/src/protocol/taiko_alethia/TaikoInbox.sol index 1a4062b3..1dd3bf54 100644 --- a/src/protocol/taiko_alethia/TaikoInbox.sol +++ b/src/protocol/taiko_alethia/TaikoInbox.sol @@ -67,16 +67,10 @@ contract TaikoInbox is IInbox, DelayedInclusionStore { /// @param _lookahead Address of the lookahead contract /// @param _blobRefRegistry Address of the blob reference registry contract /// @param _maxAnchorBlockIdOffset Maximum offset allowed for anchor block ID - /// @param _proposerFees Address of the proposer fees contract (usually prover manager) /// @param _inclusionDelay How long before delayed inclusion must be processed - constructor( - address _lookahead, - address _blobRefRegistry, - uint256 _maxAnchorBlockIdOffset, - address _proposerFees, - uint256 _inclusionDelay - ) DelayedInclusionStore(_inclusionDelay, _blobRefRegistry) { - require(_proposerFees != address(0), ZeroProposerFees()); + constructor(address _lookahead, address _blobRefRegistry, uint256 _maxAnchorBlockIdOffset, uint256 _inclusionDelay) + DelayedInclusionStore(_inclusionDelay, _blobRefRegistry) + { lookahead = ILookahead(_lookahead); maxAnchorBlockIdOffset = _maxAnchorBlockIdOffset; deployer = msg.sender; diff --git a/test/Gas/Inbox.t.sol b/test/Gas/Inbox.t.sol index 4ba7c1c0..2bac6ae6 100644 --- a/test/Gas/Inbox.t.sol +++ b/test/Gas/Inbox.t.sol @@ -26,9 +26,8 @@ contract TaikoInboxTest is Test { function setUp() public { MockProposerFees mockProposerFees = new MockProposerFees(); blobRefRegistry = new BlobRefRegistry(); - taikoInbox = new TaikoInbox( - lookaheadAddr, address(blobRefRegistry), maxAnchorBlockIdOffset, address(mockProposerFees), inclusionDelay - ); + taikoInbox = new TaikoInbox(lookaheadAddr, address(blobRefRegistry), maxAnchorBlockIdOffset, inclusionDelay); + taikoInbox.initializeProposerFees(address(mockProposerFees)); } function test_gas_TaikoPublishFunction() public ProposeMultiplePublications(10) { From 68f0a23338ea09a60200d9a79e61225e5af6b772 Mon Sep 17 00:00:00 2001 From: Leo Date: Wed, 6 Aug 2025 10:30:44 +0200 Subject: [PATCH 11/12] update checkpoint --- src/protocol/CheckpointTracker.sol | 40 +++++----------------------- src/protocol/ICheckpointTracker.sol | 11 +++----- test/CheckpointTracker.t.sol | 27 ++++++++++--------- test/mocks/MockCheckpointTracker.sol | 2 +- 4 files changed, 26 insertions(+), 54 deletions(-) diff --git a/src/protocol/CheckpointTracker.sol b/src/protocol/CheckpointTracker.sol index 962d31c0..1cfad866 100644 --- a/src/protocol/CheckpointTracker.sol +++ b/src/protocol/CheckpointTracker.sol @@ -6,7 +6,9 @@ import {ICommitmentStore} from "./ICommitmentStore.sol"; import {IInbox} from "./IInbox.sol"; import {IVerifier} from "./IVerifier.sol"; -contract CheckpointTracker is ICheckpointTracker { +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; + +contract CheckpointTracker is ICheckpointTracker, Ownable { /// @dev The number of delayed publications up to the proven checkpoint uint256 private _totalDelayedPublications; @@ -19,35 +21,11 @@ contract CheckpointTracker is ICheckpointTracker { address public proverManager; - /// @dev need to track initialisation separately to address as initialized prover manager may be zero - bool private _proverManagerInitialized; - - address private immutable deployer; - - /// @dev Modifier to check if proverManager has been initialized - modifier checkProverInitialized() { - bytes4 errorSelector = ProverManagerNotInitialized.selector; - assembly { - let initialized := sload(_proverManagerInitialized.slot) - if iszero(initialized) { - mstore(0, errorSelector) - revert(0x00, 0x04) - } - } - _; - } - - /// @dev Modifier to check if the caller is the deployer - modifier onlyDeployer() { - require(msg.sender == deployer, "Only deployer can call this function"); - _; - } - /// @param _genesis the checkpoint commitment describing the initial state of the rollup /// @param _inbox the inbox contract that contains the publication feed /// @param _verifier a contract that can verify the validity of a transition from one checkpoint to another /// @param _commitmentStore contract responsible storing historical commitments - constructor(bytes32 _genesis, address _inbox, address _verifier, address _commitmentStore) { + constructor(bytes32 _genesis, address _inbox, address _verifier, address _commitmentStore) Ownable(msg.sender) { // set the genesis checkpoint commitment of the rollup - genesis is trusted to be correct require(_genesis != 0, ZeroGenesisCommitment()); inbox = IInbox(_inbox); @@ -56,18 +34,13 @@ contract CheckpointTracker is ICheckpointTracker { verifier = IVerifier(_verifier); commitmentStore = ICommitmentStore(_commitmentStore); - deployer = msg.sender; - _saveCommitment(latestPublicationId, _genesis); } /// @inheritdoc ICheckpointTracker - /// @dev Can only be called once, allowed prover manager to be zero - function initializeProverManager(address _proverManager) external onlyDeployer { - require(!_proverManagerInitialized, "ProverManager already initialized"); + function updateProverManager(address _proverManager) external onlyOwner { proverManager = _proverManager; - _proverManagerInitialized = true; - emit ProverManagerInitialized(_proverManager); + emit ProverManagerUpdated(_proverManager); } /// @inheritdoc ICheckpointTracker @@ -76,7 +49,6 @@ contract CheckpointTracker is ICheckpointTracker { /// changes in the mean time. function proveTransition(Checkpoint calldata start, Checkpoint calldata end, bytes calldata proof) external - checkProverInitialized returns (uint256 numPublications, uint256 numDelayedPublications) { require(proverManager == address(0) || msg.sender == proverManager, OnlyProverManager()); diff --git a/src/protocol/ICheckpointTracker.sol b/src/protocol/ICheckpointTracker.sol index 78067113..10fa4ecb 100644 --- a/src/protocol/ICheckpointTracker.sol +++ b/src/protocol/ICheckpointTracker.sol @@ -43,19 +43,16 @@ interface ICheckpointTracker { /// @param commitment the commitment of the latest proven checkpoint event CommitmentSaved(uint256 indexed publicationId, bytes32 commitment); - /// @notice Emitted when ProverManager is initialized + /// @notice Emitted when ProverManager is updated /// @param proverManager The address of the ProverManager contract - event ProverManagerInitialized(address indexed proverManager); - - /// @notice Thrown when no prover manager was set - error ProverManagerNotInitialized(); + event ProverManagerUpdated(address indexed proverManager); /// @return _ The last proven publication ID function provenPublicationId() external view returns (uint256); - /// @notice Initialize the proverManager address + /// @notice Updates the address of the ProverManager contract /// @param _proverManager The address of the ProverManager contract - function initializeProverManager(address _proverManager) external; + function updateProverManager(address _proverManager) external; /// @notice Verifies a transition between two checkpoints. Update the latest `provenCheckpoint` if possible /// @param start The initial checkpoint before the transition diff --git a/test/CheckpointTracker.t.sol b/test/CheckpointTracker.t.sol index 8c713ca1..ba6e4461 100644 --- a/test/CheckpointTracker.t.sol +++ b/test/CheckpointTracker.t.sol @@ -3,6 +3,8 @@ pragma solidity ^0.8.28; import {MockInbox} from "./mocks/MockInbox.sol"; import {MockVerifier} from "./mocks/MockVerifier.sol"; + +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import "forge-std/Test.sol"; import {CheckpointTracker} from "src/protocol/CheckpointTracker.sol"; import {ICheckpointTracker} from "src/protocol/ICheckpointTracker.sol"; @@ -27,7 +29,7 @@ contract CheckpointTrackerTest is Test { signalService = new SignalService(); vm.startPrank(deployer); tracker = new CheckpointTracker(genesis, address(inbox), address(verifier), address(signalService)); - tracker.initializeProverManager(address(proverManager)); + tracker.updateProverManager(address(proverManager)); vm.stopPrank(); } @@ -52,35 +54,36 @@ contract CheckpointTrackerTest is Test { assertEq(savedCommitment, genesis, "Did not save genesis"); } - function test_initializeProverManager_shouldSetProverManager() public { + function test_updateProverManager_shouldSetProverManager() public { vm.startPrank(deployer); CheckpointTracker uninitializedTracker = new CheckpointTracker(genesis, address(inbox), address(verifier), address(signalService)); - uninitializedTracker.initializeProverManager(proverManager); + uninitializedTracker.updateProverManager(proverManager); assertEq(address(uninitializedTracker.proverManager()), proverManager, "Did not set prover manager"); } - function test_proveTransition_shouldRevertIfProverManagerNotInitialized() public { + function test_proveTransition_shouldSucceedWithNoProverManager() public { CheckpointTracker uninitializedTracker = new CheckpointTracker(genesis, address(inbox), address(verifier), address(signalService)); _constructValidTransition(); - vm.expectRevert(ICheckpointTracker.ProverManagerNotInitialized.selector); uninitializedTracker.proveTransition(start, end, proof); } - function test_initializeProverManager_onlyDeployer() public { + function test_initializeProverManager_onlyOwner() public { vm.prank(deployer); CheckpointTracker uninitializedTracker = new CheckpointTracker(genesis, address(inbox), address(verifier), address(signalService)); - vm.prank(makeAddr("notdeployer")); - vm.expectRevert("Only deployer can call this function"); - uninitializedTracker.initializeProverManager(proverManager); + + address notDeployer = makeAddr("notdeployer"); + vm.prank(notDeployer); + vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, notDeployer)); + uninitializedTracker.updateProverManager(proverManager); } - function test_initializeProverManager_shouldRevertIfAlreadyInitialized() public { + function test_updateProverManager_shouldUpdateProverManager() public { vm.prank(deployer); - vm.expectRevert("ProverManager already initialized"); - tracker.initializeProverManager(address(proverManager)); + tracker.updateProverManager(makeAddr("newProverManager")); + assertEq(address(tracker.proverManager()), makeAddr("newProverManager"), "Did not update prover manager"); } function test_proveTransition_shouldRevertIfNotCalledByProverManager() public { diff --git a/test/mocks/MockCheckpointTracker.sol b/test/mocks/MockCheckpointTracker.sol index 8eb0ae54..e47d99d7 100644 --- a/test/mocks/MockCheckpointTracker.sol +++ b/test/mocks/MockCheckpointTracker.sol @@ -24,7 +24,7 @@ contract MockCheckpointTracker is ICheckpointTracker { return (nPublications, nDelayedPublications); } - function initializeProverManager(address _proverManager) external {} + function updateProverManager(address _proverManager) external {} function setValid(bool isValid) external { valid = isValid; From 573ff53a6222297ab852f4c214fa35b2db9a2b81 Mon Sep 17 00:00:00 2001 From: Leo Date: Wed, 6 Aug 2025 10:34:24 +0200 Subject: [PATCH 12/12] update inbox --- gas-reports/minimal_inbox_publish.json | 4 +-- snapshots/TaikoInboxTest.json | 2 +- src/protocol/IInbox.sol | 4 +-- src/protocol/taiko_alethia/TaikoInbox.sol | 43 +++++------------------ test/Gas/Inbox.t.sol | 2 -- test/mocks/MockInbox.sol | 2 +- 6 files changed, 15 insertions(+), 42 deletions(-) diff --git a/gas-reports/minimal_inbox_publish.json b/gas-reports/minimal_inbox_publish.json index c258a2f2..933678ac 100644 --- a/gas-reports/minimal_inbox_publish.json +++ b/gas-reports/minimal_inbox_publish.json @@ -1,2 +1,2 @@ -{ "num_publications": 20, "average_gas_used_publish": 44563 } - +{num_publications:20, +average_gas_used_publish: 43453} \ No newline at end of file diff --git a/snapshots/TaikoInboxTest.json b/snapshots/TaikoInboxTest.json index 9f6b2da8..1c536975 100644 --- a/snapshots/TaikoInboxTest.json +++ b/snapshots/TaikoInboxTest.json @@ -1,3 +1,3 @@ { - "publish": "891278" + "publish": "869078" } \ No newline at end of file diff --git a/src/protocol/IInbox.sol b/src/protocol/IInbox.sol index db3a5847..886ba015 100644 --- a/src/protocol/IInbox.sol +++ b/src/protocol/IInbox.sol @@ -39,7 +39,7 @@ interface IInbox { /// @return _ True if the header is valid, false otherwise function validateHeader(PublicationHeader calldata header) external view returns (bool); - /// @notice Initialize the proposer fees contract address (prover manager contract) + /// @notice Updates the proposer fees contract address (prover manager contract) /// @param _proposerFees Address of the proposer fees contract - function initializeProposerFees(address _proposerFees) external; + function updateProposerFees(address _proposerFees) external; } diff --git a/src/protocol/taiko_alethia/TaikoInbox.sol b/src/protocol/taiko_alethia/TaikoInbox.sol index 1dd3bf54..1ea46316 100644 --- a/src/protocol/taiko_alethia/TaikoInbox.sol +++ b/src/protocol/taiko_alethia/TaikoInbox.sol @@ -10,7 +10,9 @@ import {IInbox} from "../IInbox.sol"; import {ILookahead} from "../ILookahead.sol"; import {IProposerFees} from "../IProposerFees.sol"; -contract TaikoInbox is IInbox, DelayedInclusionStore { +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; + +contract TaikoInbox is IInbox, DelayedInclusionStore, Ownable { /// @dev Caller is not the current preconfer error NotCurrentPreconfer(); @@ -20,9 +22,6 @@ contract TaikoInbox is IInbox, DelayedInclusionStore { /// @dev Blockhash is not available for the anchor block error BlockhashUnavailable(); - /// @dev Proposer fee set to zero address - error ZeroProposerFees(); - struct Metadata { uint256 anchorBlockId; bytes32 anchorBlockHash; @@ -33,30 +32,6 @@ contract TaikoInbox is IInbox, DelayedInclusionStore { IProposerFees public proposerFees; uint256 public immutable maxAnchorBlockIdOffset; - address private immutable deployer; - - /// @notice Thrown when no proposer fee address is set - error ProposerFeesNotInitialized(); - - /// @dev Modifier to check if proposerFees has been initialized - modifier checkProposerFeesInitialized() { - bytes4 errorSelector = ProposerFeesNotInitialized.selector; - assembly { - let fees := sload(proposerFees.slot) - if iszero(fees) { - mstore(0, errorSelector) - revert(0x00, 0x04) - } - } - _; - } - - /// @dev Modifier to check if the caller is the deployer - modifier onlyDeployer() { - require(msg.sender == deployer, "Only deployer can call this function"); - _; - } - // attributes associated with the publication uint256 private constant METADATA = 0; uint256 private constant BLOB_REFERENCE = 1; @@ -70,25 +45,23 @@ contract TaikoInbox is IInbox, DelayedInclusionStore { /// @param _inclusionDelay How long before delayed inclusion must be processed constructor(address _lookahead, address _blobRefRegistry, uint256 _maxAnchorBlockIdOffset, uint256 _inclusionDelay) DelayedInclusionStore(_inclusionDelay, _blobRefRegistry) + Ownable(msg.sender) { lookahead = ILookahead(_lookahead); maxAnchorBlockIdOffset = _maxAnchorBlockIdOffset; - deployer = msg.sender; // guarantee there is always a previous hash _publicationHashes.push(0); } /// @inheritdoc IInbox - function initializeProposerFees(address _proposerFees) external onlyDeployer { - require(address(proposerFees) == address(0), "ProposerFees already initialized"); - require(_proposerFees != address(0), "ProposerFees cannot be zero"); + function updateProposerFees(address _proposerFees) external onlyOwner { proposerFees = IProposerFees(_proposerFees); emit ProposerFeesInitialized(_proposerFees); } /// @inheritdoc IInbox - function publish(uint256 nBlobs, uint64 anchorBlockId) external checkProposerFeesInitialized { + function publish(uint256 nBlobs, uint64 anchorBlockId) external { if (address(lookahead) != address(0)) { require(lookahead.isCurrentPreconfer(msg.sender), NotCurrentPreconfer()); } @@ -127,7 +100,9 @@ contract TaikoInbox is IInbox, DelayedInclusionStore { /// @param attributes The data to publish /// @param isDelayed Whether this is a delayed inclusion publication function _publish(bytes[] memory attributes, bool isDelayed) internal { - proposerFees.payPublicationFee(msg.sender, isDelayed); + if (address(proposerFees) != address(0)) { + proposerFees.payPublicationFee(msg.sender, isDelayed); + } uint256 nAttributes = attributes.length; bytes32[] memory attributeHashes = new bytes32[](nAttributes); diff --git a/test/Gas/Inbox.t.sol b/test/Gas/Inbox.t.sol index 2bac6ae6..a542ab90 100644 --- a/test/Gas/Inbox.t.sol +++ b/test/Gas/Inbox.t.sol @@ -24,10 +24,8 @@ contract TaikoInboxTest is Test { uint256 inclusionDelay = uint256(10000000); function setUp() public { - MockProposerFees mockProposerFees = new MockProposerFees(); blobRefRegistry = new BlobRefRegistry(); taikoInbox = new TaikoInbox(lookaheadAddr, address(blobRefRegistry), maxAnchorBlockIdOffset, inclusionDelay); - taikoInbox.initializeProposerFees(address(mockProposerFees)); } function test_gas_TaikoPublishFunction() public ProposeMultiplePublications(10) { diff --git a/test/mocks/MockInbox.sol b/test/mocks/MockInbox.sol index ede59e06..5bc26da1 100644 --- a/test/mocks/MockInbox.sol +++ b/test/mocks/MockInbox.sol @@ -16,7 +16,7 @@ contract MockInbox is IInbox { revert NotImplemented(); } - function initializeProposerFees(address) external pure { + function updateProposerFees(address) external pure { revert NotImplemented(); }