Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
b3617f1
Suppress existing failing tests
nikeshnazareth Jun 11, 2025
039b488
Simplify MockInbox and MockCheckpoint
nikeshnazareth Jun 11, 2025
414532f
Make both prover managers concrete classes
nikeshnazareth Jun 13, 2025
fbff90b
Create InitialState
nikeshnazareth Jun 12, 2025
4e783a2
Expose config parameters with external getters
nikeshnazareth Jun 16, 2025
72298b7
Test balances of initial state
nikeshnazareth Jun 16, 2025
2cd50fb
Test initial state periods
nikeshnazareth Jun 16, 2025
48f3292
Include Invariant tests
nikeshnazareth Jun 16, 2025
2127149
Test Deposits/Withdrawals
nikeshnazareth Jun 16, 2025
5df27da
Use explicit scaleByPercentage function
nikeshnazareth Jun 17, 2025
276ac33
Test payments when the current period is active
nikeshnazareth Jun 17, 2025
05bdf73
Ensure Test contract do not perform any state changes
nikeshnazareth Jun 17, 2025
6310363
Test advancing a period with a new publication
nikeshnazareth Jun 17, 2025
edde7fa
Require underbid to be strictly less than the existing fee.
nikeshnazareth Jun 17, 2025
9428fe3
Test bid on open period
nikeshnazareth Jun 17, 2025
6825a23
Clarify that you can bid on a period until it is started by a publica…
nikeshnazareth Jun 18, 2025
9b9cd3e
Make _prepareForDeposit a virtual function in InitialState
nikeshnazareth Jun 18, 2025
dcf15d8
Test bid on contested period
nikeshnazareth Jun 18, 2025
ca204de
Bug fix: prevent evicting a vacant prover
nikeshnazareth Jun 18, 2025
b320e8e
Create new scenarios
nikeshnazareth Jun 18, 2025
790f712
Bug fix: cannot evict based on publication before period
nikeshnazareth Jun 18, 2025
fef76b7
Extend periods so they can contain unproven publications
nikeshnazareth Jun 18, 2025
790213d
Move initial state test instantiations to PeriodScenarios.t.sol
nikeshnazareth Jun 18, 2025
e82359d
Emit original bond in ProverEvicted (rather than slashed value)
nikeshnazareth Jun 19, 2025
b3fdc74
Test evictProver
nikeshnazareth Jun 19, 2025
3cc1082
Test prover exit
nikeshnazareth Jun 19, 2025
891bd78
Create ProverVacancyClaimed event
nikeshnazareth Jun 19, 2025
8370c8e
Create tests for CurrentPeriodIsVacant state
nikeshnazareth Jun 19, 2025
8174181
Fix test name
nikeshnazareth Jun 20, 2025
f302e9d
Include number of delayed publications in checkpoint
nikeshnazareth Jun 20, 2025
c1520cb
Include intermediateCheckpoint in verifier. Also remove getProvenChec…
nikeshnazareth Jun 20, 2025
15ef241
Test prove in current period
nikeshnazareth Jun 21, 2025
42595ef
Test previous period proofs
nikeshnazareth Jun 23, 2025
876c71a
Test finalizing periods
nikeshnazareth Jun 23, 2025
d80e1ab
Test getCurrentFees
nikeshnazareth Jun 23, 2025
502209c
Remove obsolete ProverManager tests
nikeshnazareth Jun 23, 2025
47c75a5
Test checkpoint tracker
nikeshnazareth Jun 23, 2025
57767dc
Fix missing return statement
nikeshnazareth Jun 23, 2025
d2d4213
Fix precondition check
nikeshnazareth Jun 23, 2025
9acb0fa
Use create2 for ERC20ProverManager instead of fixing the code directly
nikeshnazareth Jun 26, 2025
9318d44
Merge branch 'main' into test/state-structure/prover
LeoPatOZ Jul 10, 2025
0c13176
natspec in config
LeoPatOZ Jul 10, 2025
852ff05
typo
LeoPatOZ Jul 10, 2025
5728ca3
gas report ignore
LeoPatOZ Jul 10, 2025
55132c8
Remove obsolete "suppressed" files.
nikeshnazareth Jul 15, 2025
df3afab
Remove outdated MockAnchor contract
nikeshnazareth Jul 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ evm_version = "cancun"
always_use_create_2_factory = true
sender = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"

gas_reports_ignore = ["MockDelayedInclusionStore", "MockCheckpointTracker", "SampleDepositProof", "SampleProof"]
gas_reports_ignore = ["MockDelayedInclusionStore", "MockCheckpointTracker", "SampleDepositProof", "SampleProof", "MockInbox", "MockERC20", "MockVerifier"]

remappings = [
"@optimism/=lib/optimism/",
Expand Down
3 changes: 1 addition & 2 deletions offchain/deposit_signal_proof.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use alloy::primitives::{address, bytes, Address, U256};
use alloy::primitives::{address, bytes, U256};
use eyre::Result;

mod utils;
Expand All @@ -22,7 +22,6 @@ async fn main() -> Result<()> {
let data = bytes!();
let sender = address!("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266");
let amount = U256::from(4000000000000000000_u128);
let relayer = Address::ZERO;

let (provider, _, _) = get_provider()?;

Expand Down
2 changes: 1 addition & 1 deletion src/libs/LibProvingPeriod.sol
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ library LibProvingPeriod {

/// @dev The period fee, scaled by `delayedFeePercentage` if the publication is delayed
function publicationFee(Period storage period, bool isDelayed) internal view returns (uint96) {
return isDelayed ? period.fee.scaleBy(period.delayedFeePercentage, LibPercentage.PERCENT) : period.fee;
return isDelayed ? period.fee.scaleByPercentage(period.delayedFeePercentage) : period.fee;
}

/// @dev The period has no end timestamp
Expand Down
31 changes: 20 additions & 11 deletions src/protocol/BaseProverManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ abstract contract BaseProverManager is IProposerFees, IProverManager, BalanceAcc
/// @dev Only current prover can call `exit`
error OnlyCurrentProver();

/// @dev Publication is before the current period
error PublicationBeforeCurrentPeriod();

/// @dev Publication has not passed liveness window
error PublicationNotOldEnough();

Expand Down Expand Up @@ -122,6 +125,8 @@ abstract contract BaseProverManager is IProposerFees, IProverManager, BalanceAcc
/// @dev The offered fee has to be at most `maxBidFraction` of the current best price.
/// @dev The current best price may be the current prover's fee or the fee of the next bid, depending on whether the
/// period is open or closed.
/// @dev Note that even if the current period (that has the last publication) is over, the auction will continue
/// until the next publication triggers a new period.
function bid(uint96 offeredFee) external {
uint256 currentPeriodId_ = _currentPeriodId;
LibProvingPeriod.Period storage currentPeriod = _periods[currentPeriodId_];
Expand All @@ -146,24 +151,29 @@ abstract contract BaseProverManager is IProposerFees, IProverManager, BalanceAcc
/// @dev This can be called by anyone, and they get `evictorIncentiveFraction` of the liveness bond as an
/// incentive.
function evictProver(IInbox.PublicationHeader calldata publicationHeader) external {
uint256 periodId = _currentPeriodId;
uint40 previousPeriodEnd = periodId > 0 ? _periods[periodId - 1].end : 0;

require(inbox.validateHeader(publicationHeader), InvalidPublication());
require(publicationHeader.timestamp + _livenessWindow() < block.timestamp, PublicationNotOldEnough());
require(publicationHeader.timestamp > previousPeriodEnd, PublicationBeforeCurrentPeriod());

LibProvingPeriod.Period storage period = _periods[_currentPeriodId];
LibProvingPeriod.Period storage period = _periods[periodId];
require(period.isInitialized(), PeriodNotInitialized());
require(period.isOpen(), ProvingPeriodClosed());

ICheckpointTracker.Checkpoint memory lastProven = checkpointTracker.getProvenCheckpoint();
require(publicationHeader.id > lastProven.publicationId, PublicationAlreadyProven());
require(publicationHeader.id > checkpointTracker.provenPublicationId(), PublicationAlreadyProven());

// We use this to mark the prover as evicted
(uint40 end,) = period.close(_exitDelay(), 0);

// Reward the evictor and slash the prover
uint96 evictorIncentive = period.stake.scaleByBPS(_evictorIncentiveFraction());
uint96 bond = period.stake;
uint96 evictorIncentive = bond.scaleByBPS(_evictorIncentiveFraction());
_increaseBalance(msg.sender, evictorIncentive);
period.slash(evictorIncentive);

emit ProverEvicted(period.prover, msg.sender, end, period.stake);
emit ProverEvicted(period.prover, msg.sender, end, bond);
}

/// @inheritdoc IProverManager
Expand All @@ -190,7 +200,6 @@ abstract contract BaseProverManager is IProposerFees, IProverManager, BalanceAcc
ICheckpointTracker.Checkpoint calldata end,
IInbox.PublicationHeader calldata firstPub,
IInbox.PublicationHeader calldata lastPub,
uint256 numDelayedPublications,
bytes calldata proof,
uint256 periodId
) external {
Expand All @@ -205,7 +214,7 @@ abstract contract BaseProverManager is IProposerFees, IProverManager, BalanceAcc
require(start.publicationId + 1 == firstPub.id, InvalidStartPublication());
require(firstPub.timestamp > previousPeriodEnd, FirstPublicationIsBeforePeriod());

uint256 numPublications = checkpointTracker.proveTransition(start, end, numDelayedPublications, proof);
(uint256 numPublications, uint256 numDelayedPublications) = checkpointTracker.proveTransition(start, end, proof);

if (period.isDeadlinePassed()) {
period.assignReward(msg.sender);
Expand All @@ -217,9 +226,7 @@ abstract contract BaseProverManager is IProposerFees, IProverManager, BalanceAcc
/// @inheritdoc IProverManager
function finalizePastPeriod(uint256 periodId, IInbox.PublicationHeader calldata provenPublication) external {
require(inbox.validateHeader(provenPublication), InvalidPublication());

ICheckpointTracker.Checkpoint memory lastProven = checkpointTracker.getProvenCheckpoint();
require(lastProven.publicationId >= provenPublication.id, PublicationNotProven());
require(checkpointTracker.provenPublicationId() >= provenPublication.id, PublicationNotProven());

LibProvingPeriod.Period storage period = _periods[periodId];
require(period.isInitialized(), PeriodNotInitialized());
Expand Down Expand Up @@ -259,7 +266,7 @@ abstract contract BaseProverManager is IProposerFees, IProverManager, BalanceAcc
/// @param offeredFee The new bid
function _ensureSufficientUnderbid(uint96 fee, uint96 offeredFee) internal view virtual {
uint96 requiredMaxFee = fee.scaleByBPS(_maxBidFraction());
require(offeredFee <= requiredMaxFee, OfferedFeeTooHigh());
require(offeredFee <= requiredMaxFee && fee > 0, OfferedFeeTooHigh());
}

/// @dev implementation of `IProverManager.claimProvingVacancy` with the option to specify a prover
Expand All @@ -276,6 +283,8 @@ abstract contract BaseProverManager is IProposerFees, IProverManager, BalanceAcc

_decreaseBalance(prover, _livenessBond());
nextPeriod.init(prover, fee, _delayedFeePercentage(), _livenessBond());

emit ProverVacancyClaimed(prover, periodId + 1, fee, _livenessBond());
}

/// @dev mark the next period as active. Future publications will be assigned to the new period
Expand Down
70 changes: 34 additions & 36 deletions src/protocol/CheckpointTracker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import {IInbox} from "./IInbox.sol";
import {IVerifier} from "./IVerifier.sol";

contract CheckpointTracker is ICheckpointTracker {
/// @dev The publication id of the current proven checkpoint representing
/// the latest verified state of the rollup
uint256 private _provenPublicationId;
/// @dev The number of delayed publications up to the proven checkpoint
uint256 private _totalDelayedPublications;

/// @dev The publication id of the current proven checkpoint representing the latest verified state of the rollup
uint256 public provenPublicationId;

IInbox public immutable inbox;
IVerifier public immutable verifier;
Expand All @@ -31,35 +33,28 @@ contract CheckpointTracker is ICheckpointTracker {
commitmentStore = ICommitmentStore(_commitmentStore);
proverManager = _proverManager;

_updateCheckpoint(latestPublicationId, _genesis);
_saveCommitment(latestPublicationId, _genesis);
}

/// @inheritdoc ICheckpointTracker
/// @dev Accepts the last proven checkpoint(or an older one) as the start checkpoint. The reason we allow for an
/// @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 under his feet.
function proveTransition(
Checkpoint calldata start,
Checkpoint calldata end,
uint256 numDelayedPublications,
bytes calldata proof
) external returns (uint256) {
/// changes in the mean time.
function proveTransition(Checkpoint calldata start, Checkpoint calldata end, bytes calldata proof)
external
returns (uint256 numPublications, uint256 numDelayedPublications)
{
require(
proverManager == address(0) || msg.sender == proverManager, "Only the prover manager can call this function"
);

require(start.commitment != 0, "Start checkpoint commitment cannot be 0");
require(end.commitment != 0, "End checkpoint commitment cannot be 0");
require(start.publicationId <= provenPublicationId, "Start publication must precede latest proven checkpoint");

Checkpoint memory latestProvenCheckpoint = getProvenCheckpoint();
require(
start.publicationId <= latestProvenCheckpoint.publicationId,
"Start publication must precede latest proven checkpoint"
);

// Only count publications that have not been proven yet for `numPublications`
// TODO: We should also ensure that `numDelayedPublications` only accounts for unproven publications
uint256 numPublications = end.publicationId - latestProvenCheckpoint.publicationId;
// Only count publications that have not been proven yet
numPublications = end.publicationId - provenPublicationId;
numDelayedPublications = end.totalDelayedPublications - _totalDelayedPublications;
require(
numDelayedPublications <= numPublications,
"Number of delayed publications cannot be greater than the total number of publications"
Expand All @@ -70,27 +65,30 @@ contract CheckpointTracker is ICheckpointTracker {
require(endPublicationHash != 0, "End publication does not exist");

verifier.verifyProof(
startPublicationHash, endPublicationHash, start.commitment, end.commitment, numDelayedPublications, proof
startPublicationHash,
endPublicationHash,
start.commitment,
end.commitment,
_provenCommitment(),
numDelayedPublications,
proof
);

_updateCheckpoint(end.publicationId, end.commitment);

return numPublications;
_saveCommitment(end.publicationId, end.commitment);
_totalDelayedPublications = end.totalDelayedPublications;
}

/// @inheritdoc ICheckpointTracker
function getProvenCheckpoint() public view returns (Checkpoint memory provenCheckpoint) {
provenCheckpoint.publicationId = _provenPublicationId;
provenCheckpoint.commitment = commitmentStore.commitmentAt(address(this), provenCheckpoint.publicationId);
}

/// @dev Updates the proven checkpoint to a new publication ID and commitment
/// @dev Stores the commitment in the commitment store and emits an event
/// @dev Saves the latest commitment under the publication ID and emit an event
/// @dev Disregard the totalDelayedPublications because it has no meaning on layer 2
/// @param publicationId The ID of the publication to set as the latest proven checkpoint
/// @param commitment The checkpoint commitment representing the state at the given publication ID
function _updateCheckpoint(uint256 publicationId, bytes32 commitment) internal {
_provenPublicationId = publicationId;
function _saveCommitment(uint256 publicationId, bytes32 commitment) internal {
provenPublicationId = publicationId;
commitmentStore.storeCommitment(publicationId, commitment);
emit CheckpointUpdated(publicationId, commitment);
emit CommitmentSaved(publicationId, commitment);
}

function _provenCommitment() internal view returns (bytes32) {
return commitmentStore.commitmentAt(address(this), provenPublicationId);
}
}
2 changes: 1 addition & 1 deletion src/protocol/ERC20ProverManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
/// contract for bids, stake and paying for publication fees.
/// @dev This contract expects the `_initialProver` to have already approved this address to spend at least the initial
/// liveness bond. This amount of tokens will be transferred from the `_initialProver` on the constructor at deployment.
abstract contract ERC20ProverManager is BaseProverManager, IERC20Depositor {
contract ERC20ProverManager is BaseProverManager, IERC20Depositor {
using SafeERC20 for IERC20;

IERC20 public immutable token;
Expand Down
2 changes: 1 addition & 1 deletion src/protocol/ETHProverManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {IETHDepositor} from "./IProverManager.sol";
/// @title ETHProverManager
/// @notice Implementation of the `BaseProverManager` contract that uses ETH for bids, stake and paying for publication
/// fees.
abstract contract ETHProverManager is BaseProverManager, IETHDepositor {
contract ETHProverManager is BaseProverManager, IETHDepositor {
constructor(address _inbox, address _checkpointTracker, address _initialProver, uint96 _initialFee)
payable
BaseProverManager(_inbox, _checkpointTracker, _initialProver, _initialFee, msg.value)
Expand Down
24 changes: 12 additions & 12 deletions src/protocol/ICheckpointTracker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,27 @@ interface ICheckpointTracker {
/// @dev We recommend using the `keccak256(stateRoot, blockHash)` or similar to ensure both uniqueness and being
/// able to verify messages across chains
bytes32 commitment;
/// @dev The cumulative number of delayed publications up to this checkpoint (since genesis)
/// @dev This enables computing the delayed publications between two checkpoints
uint256 totalDelayedPublications;
}

/// @notice Emitted when the proven checkpoint is updated
/// @notice Emitted when the latest commitment is saved
/// @param publicationId the publication ID of the latest proven checkpoint
/// @param commitment the commitment of the latest proven checkpoint
event CheckpointUpdated(uint256 indexed publicationId, bytes32 commitment);
event CommitmentSaved(uint256 indexed publicationId, bytes32 commitment);

/// @return _ The last proven checkpoint
function getProvenCheckpoint() external view returns (Checkpoint memory);
/// @return _ The last proven publication ID
function provenPublicationId() external view returns (uint256);

/// @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
/// @param numDelayedPublications The number of delayed publications from the total of publications being proven
/// @param proof Arbitrary data passed to the `verifier` contract to confirm the transition validity
/// @return _ The number of new publications that were proven. Note this may be lower than end.publicationId -
/// start.publicationId
function proveTransition(
Checkpoint calldata start,
Checkpoint calldata end,
uint256 numDelayedPublications,
bytes calldata proof
) external returns (uint256);
/// start.publicationId because this proof may overlap with some proven checkpoints.
/// @return _ The number of new delayed publications that were proven.
function proveTransition(Checkpoint calldata start, Checkpoint calldata end, bytes calldata proof)
external
returns (uint256, uint256);
}
12 changes: 8 additions & 4 deletions src/protocol/IProverManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ interface IProverManager {
/// @param prover The address of the prover that made the bid
/// @param periodId The period that the prover is bidding to prove
/// @param fee The fee that the prover is willing to charge for proving each publication
/// @param stake The stake that the prover is going to put as stake for the period
/// @param stake The value that the prover is going to put as stake for the period
event ProverOffer(address indexed prover, uint256 periodId, uint256 fee, uint256 stake);

/// @notice Emitted when a prover is evicted from the prover role
Expand All @@ -29,6 +29,13 @@ interface IProverManager {
/// @param periodId The id of the new period
event NewPeriod(uint256 periodId);

/// @notice Emitted when a prover claims a vacant period
/// @param prover The address of the prover that made the claim
/// @param periodId The period that the prover will claim (after the vacant period)
/// @param fee The fee that the prover is willing to charge for proving each publication
/// @param stake The value that the prover is going to put as stake for the period
event ProverVacancyClaimed(address indexed prover, uint256 periodId, uint256 fee, uint256 stake);

/// @notice Bid to become the prover for the next period
/// @param offeredFee The fee you are willing to charge for proving each publication
function bid(uint96 offeredFee) external;
Expand Down Expand Up @@ -61,16 +68,13 @@ interface IProverManager {
/// @param firstPub The first publication header in the transition. Note that since checkpoints refer to the
/// publication they follow, this should have an id `start.publicationId + 1`
/// @param lastPub The last publication header in the transition
/// @param numDelayedPublications The number of delayed publications from the total number of publications being
/// proven
/// @param proof Arbitrary data passed to the `verifier` contract to confirm the transition validity
/// @param periodId The id of the period for which the proof is submitted
function prove(
ICheckpointTracker.Checkpoint calldata start,
ICheckpointTracker.Checkpoint calldata end,
IInbox.PublicationHeader calldata firstPub,
IInbox.PublicationHeader calldata lastPub,
uint256 numDelayedPublications,
bytes calldata proof,
uint256 periodId
) external;
Expand Down
3 changes: 3 additions & 0 deletions src/protocol/IVerifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ pragma solidity ^0.8.24;

interface IVerifier {
/// @notice Verifies a proof of a checkpoint between two publication hashes
/// @dev The numDelayedPublications value corresponds to the delayed publications
/// between intermediateCheckpoint (which may be the startCheckPoint) and endCheckPoint
function verifyProof(
bytes32 startPublicationHash,
bytes32 endPublicationHash,
bytes32 startCheckPoint,
bytes32 endCheckPoint,
bytes32 intermediateCheckPoint,
uint256 numDelayedPublications,
bytes calldata proof
) external;
Expand Down
Loading