Skip to content
4 changes: 2 additions & 2 deletions src/libs/LibProvingPeriod.sol
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ library LibProvingPeriod {
period.stake = stake;
}

/// @dev Whether the period has been initialized
/// @dev Whether the period has been initialised
Comment thread
pepebndc marked this conversation as resolved.
Outdated
function isInitialized(Period storage period) internal view returns (bool) {
return period.prover != address(0);
}
Expand Down Expand Up @@ -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);
}
Expand Down
2 changes: 1 addition & 1 deletion src/protocol/BaseProverManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
32 changes: 27 additions & 5 deletions src/protocol/CheckpointTracker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -16,32 +18,52 @@ 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 _proverManagerInitialised;

/// @dev Modifier to check if proverManager has been initialised
modifier checkProverInitialized() {
require(_proverManagerInitialised, "ProverManager not initialised");
_;
}

/// @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) {
/// @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);
uint256 latestPublicationId = inbox.getNextPublicationId() - 1;

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 onlyOwner {
require(!_proverManagerInitialised, "ProverManager already initialised");
proverManager = _proverManager;
_proverManagerInitialised = true;
emit ProverManagerInitialised(_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(
Expand Down
8 changes: 8 additions & 0 deletions src/protocol/ICheckpointTracker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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 initialised
/// @param proverManager The address of the ProverManager contract
event ProverManagerInitialised(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
Expand Down
7 changes: 7 additions & 0 deletions src/protocol/IInbox.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
}
34 changes: 25 additions & 9 deletions src/protocol/taiko_alethia/TaikoInbox.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand All @@ -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());
}
Expand Down
50 changes: 38 additions & 12 deletions test/CheckpointTracker.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 {
Expand All @@ -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");
Expand Down Expand Up @@ -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");
}
}
2 changes: 2 additions & 0 deletions test/mocks/MockCheckpointTracker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ contract MockCheckpointTracker is ICheckpointTracker {
return (nPublications, nDelayedPublications);
}

function initializeProverManager(address _proverManager) external {}

function setValid(bool isValid) external {
valid = isValid;
}
Expand Down
4 changes: 4 additions & 0 deletions test/mocks/MockInbox.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down