Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: l1-publisher cleanup #8148

Merged
merged 3 commits into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
168 changes: 142 additions & 26 deletions l1-contracts/src/core/Rollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import {Errors} from "./libraries/Errors.sol";
import {Constants} from "./libraries/ConstantsGen.sol";
import {MerkleLib} from "./libraries/MerkleLib.sol";
import {SignatureLib} from "./sequencer_selection/SignatureLib.sol";
import {SafeCast} from "@oz/utils/math/SafeCast.sol";
import {DataStructures} from "./libraries/DataStructures.sol";

// Contracts
import {MockVerifier} from "../mock/MockVerifier.sol";
Expand All @@ -31,6 +33,8 @@ import {Leonidas} from "./sequencer_selection/Leonidas.sol";
* not giving a damn about gas costs.
*/
contract Rollup is Leonidas, IRollup, ITestRollup {
using SafeCast for uint256;

struct BlockLog {
bytes32 archive;
bytes32 blockHash;
Expand Down Expand Up @@ -381,6 +385,69 @@ contract Rollup is Leonidas, IRollup, ITestRollup {
return blocks[_blockNumber].archive;
}

/**
* @notice Check if a proposer can propose at a given time
*
* @param _ts - The timestamp to check
* @param _proposer - The proposer to check
* @param _archive - The archive to check (should be the latest archive)
*
* @return uint256 - The slot at the given timestamp
* @return uint256 - The block number at the given timestamp
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The wording here probably got slightly weird. But it is the block number at the timestamp, e.g., the next block. I am inclined to fix it in a later pr along with other things because of ci times.

*/
function canProposeAtTime(uint256 _ts, address _proposer, bytes32 _archive)
external
view
override(IRollup)
returns (uint256, uint256)
{
uint256 slot = getSlotAt(_ts);

uint256 lastSlot = uint256(blocks[pendingBlockCount - 1].slotNumber);
if (slot <= lastSlot) {
revert Errors.Rollup__SlotAlreadyInChain(lastSlot, slot);
}

bytes32 tipArchive = archive();
if (tipArchive != _archive) {
revert Errors.Rollup__InvalidArchive(tipArchive, _archive);
}

if (isDevNet) {
_devnetSequencerSubmissionChecks(_proposer);
} else {
address proposer = getProposerAt(_ts);
if (proposer != address(0) && proposer != _proposer) {
revert Errors.Leonidas__InvalidProposer(proposer, _proposer);
}
}

return (slot, pendingBlockCount);
}

/**
* @notice Validate a header for submission
*
* @dev This is a convenience function that can be used by the sequencer to validate a "partial" header
* without having to deal with viem or anvil for simulating timestamps in the future.
*
* @param _header - The header to validate
* @param _signatures - The signatures to validate
* @param _digest - The digest to validate
* @param _currentTime - The current time
* @param _flags - The flags to validate
*/
function validateHeader(
bytes calldata _header,
SignatureLib.Signature[] memory _signatures,
bytes32 _digest,
uint256 _currentTime,
DataStructures.ExecutionFlags memory _flags
) external view override(IRollup) {
HeaderLib.Header memory header = HeaderLib.decode(_header);
_validateHeader(header, _signatures, _digest, _currentTime, _flags);
}

/**
* @notice Processes an incoming L2 block with signatures
*
Expand All @@ -397,15 +464,19 @@ contract Rollup is Leonidas, IRollup, ITestRollup {
) public override(IRollup) {
// Decode and validate header
HeaderLib.Header memory header = HeaderLib.decode(_header);
_validateHeaderForSubmissionBase(header);
_validateHeaderForSubmissionSequencerSelection(header, _signatures, _archive);
setupEpoch();
_validateHeader({
_header: header,
_signatures: _signatures,
_digest: _archive,
_currentTime: block.timestamp,
_flags: DataStructures.ExecutionFlags({ignoreDA: false, ignoreSignatures: false})
});

// As long as the header is passing validity check in `_validateHeaderForSubmissionBase` we can safely cast
// the slot number to uint128
blocks[pendingBlockCount++] = BlockLog({
archive: _archive,
blockHash: _blockHash,
slotNumber: uint128(header.globalVariables.slotNumber),
slotNumber: header.globalVariables.slotNumber.toUint128(),
isProven: false
});

Expand Down Expand Up @@ -497,6 +568,29 @@ contract Rollup is Leonidas, IRollup, ITestRollup {
}
}

/**
* @notice Validates the header for submission
*
* @param _header - The proposed block header
* @param _signatures - The signatures for the attestations
* @param _digest - The digest that signatures signed
* @param _currentTime - The time of execution
* @dev - This value is provided to allow for simple simulation of future
* @param _flags - Flags specific to the execution, whether certain checks should be skipped
*/
function _validateHeader(
HeaderLib.Header memory _header,
SignatureLib.Signature[] memory _signatures,
bytes32 _digest,
uint256 _currentTime,
DataStructures.ExecutionFlags memory _flags
) internal view {
_validateHeaderForSubmissionBase(_header, _currentTime, _flags);
_validateHeaderForSubmissionSequencerSelection(
_header.globalVariables.slotNumber, _signatures, _digest, _currentTime, _flags
);
}

/**
* @notice Validate a header for submission to the pending chain (sequencer selection checks)
*
Expand All @@ -511,15 +605,17 @@ contract Rollup is Leonidas, IRollup, ITestRollup {
*
* @dev While in isDevNet, we allow skipping all of the checks as we simply assume only TRUSTED sequencers
*
* @param _header - The header to validate
* @param _slot - The slot of the header to validate
* @param _signatures - The signatures to validate
* @param _archive - The archive root of the block
* @param _digest - The digest that signatures sign over
*/
function _validateHeaderForSubmissionSequencerSelection(
HeaderLib.Header memory _header,
uint256 _slot,
SignatureLib.Signature[] memory _signatures,
bytes32 _archive
) internal {
bytes32 _digest,
uint256 _currentTime,
DataStructures.ExecutionFlags memory _flags
) internal view {
if (isDevNet) {
// @note If we are running in a devnet, we don't want to perform all the consensus
// checks, we instead simply require that either there are NO validators or
Expand All @@ -528,36 +624,29 @@ contract Rollup is Leonidas, IRollup, ITestRollup {
// This means that we relaxes the condition that the block must land in the
// correct slot and epoch to make it more fluid for the devnet launch
// or for testing.
if (getValidatorCount() == 0) {
return;
}

if (!isValidator(msg.sender)) {
revert Errors.Leonidas__InvalidProposer(getValidatorAt(0), msg.sender);
}
_devnetSequencerSubmissionChecks(msg.sender);
return;
}

uint256 slot = _header.globalVariables.slotNumber;

// Ensure that the slot proposed is NOT in the future
uint256 currentSlot = getCurrentSlot();
if (slot != currentSlot) {
revert Errors.HeaderLib__InvalidSlotNumber(currentSlot, slot);
uint256 currentSlot = getSlotAt(_currentTime);
if (_slot != currentSlot) {
revert Errors.HeaderLib__InvalidSlotNumber(currentSlot, _slot);
}

// @note We are currently enforcing that the slot is in the current epoch
// If this is not the case, there could potentially be a weird reorg
// of an entire epoch if no-one from the new epoch committee have seen
// those blocks or behaves as if they did not.

uint256 epochNumber = getEpochAt(getTimestampForSlot(slot));
uint256 currentEpoch = getCurrentEpoch();
uint256 epochNumber = getEpochAt(getTimestampForSlot(_slot));
uint256 currentEpoch = getEpochAt(_currentTime);
if (epochNumber != currentEpoch) {
revert Errors.Rollup__InvalidEpoch(currentEpoch, epochNumber);
}

_processPendingBlock(epochNumber, slot, _signatures, _archive);
_processPendingBlock(_slot, _signatures, _digest, _flags);
}

/**
Expand All @@ -577,7 +666,11 @@ contract Rollup is Leonidas, IRollup, ITestRollup {
*
* @param _header - The header to validate
*/
function _validateHeaderForSubmissionBase(HeaderLib.Header memory _header) internal view {
function _validateHeaderForSubmissionBase(
HeaderLib.Header memory _header,
uint256 _currentTime,
DataStructures.ExecutionFlags memory _flags
) internal view {
if (block.chainid != _header.globalVariables.chainId) {
revert Errors.Rollup__InvalidChainId(block.chainid, _header.globalVariables.chainId);
}
Expand Down Expand Up @@ -612,9 +705,32 @@ contract Rollup is Leonidas, IRollup, ITestRollup {
revert Errors.Rollup__InvalidTimestamp(timestamp, _header.globalVariables.timestamp);
}

if (timestamp > _currentTime) {
// @note If you are hitting this error, it is likely because the chain you use have a blocktime that differs
// from the value that we have in the constants.
// When you are encountering this, it will likely be as the sequencer expects to be able to include
// an Aztec block in the "next" ethereum block based on a timestamp that is 12 seconds in the future
// from the last block. However, if the actual will only be 1 second in the future, you will end up
// expecting this value to be in the future.
revert Errors.Rollup__TimestampInFuture(_currentTime, timestamp);
}

// Check if the data is available using availability oracle (change availability oracle if you want a different DA layer)
if (!AVAILABILITY_ORACLE.isAvailable(_header.contentCommitment.txsEffectsHash)) {
if (
!_flags.ignoreDA && !AVAILABILITY_ORACLE.isAvailable(_header.contentCommitment.txsEffectsHash)
) {
revert Errors.Rollup__UnavailableTxs(_header.contentCommitment.txsEffectsHash);
}
}

function _devnetSequencerSubmissionChecks(address _proposer) internal view {
if (getValidatorCount() == 0) {
return;
}

if (!isValidator(_proposer)) {
revert Errors.DevNet__InvalidProposer(getValidatorAt(0), _proposer);
}
return;
}
}
13 changes: 13 additions & 0 deletions l1-contracts/src/core/interfaces/IRollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {IInbox} from "../interfaces/messagebridge/IInbox.sol";
import {IOutbox} from "../interfaces/messagebridge/IOutbox.sol";

import {SignatureLib} from "../sequencer_selection/SignatureLib.sol";
import {DataStructures} from "../libraries/DataStructures.sol";

interface ITestRollup {
function setDevNet(bool _devNet) external;
Expand All @@ -20,6 +21,18 @@ interface IRollup {
event ProgressedState(uint256 provenBlockCount, uint256 pendingBlockCount);
event PrunedPending(uint256 provenBlockCount, uint256 pendingBlockCount);

function canProposeAtTime(uint256 _ts, address _proposer, bytes32 _archive)
external
view
returns (uint256, uint256);
function validateHeader(
bytes calldata _header,
SignatureLib.Signature[] memory _signatures,
bytes32 _digest,
uint256 _currentTime,
DataStructures.ExecutionFlags memory _flags
) external view;

function prune() external;

function INBOX() external view returns (IInbox);
Expand Down
5 changes: 5 additions & 0 deletions l1-contracts/src/core/libraries/DataStructures.sol
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,9 @@ library DataStructures {
uint256 blockNumber;
}
// docs:end:registry_snapshot

struct ExecutionFlags {
bool ignoreDA;
bool ignoreSignatures;
}
}
3 changes: 2 additions & 1 deletion l1-contracts/src/core/libraries/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pragma solidity >=0.8.18;
library Errors {
// DEVNET related
error DevNet__NoPruningAllowed(); // 0x6984c590
error DevNet__InvalidProposer(address expected, address actual); // 0x11e6e6f7

// Inbox
error Inbox__Unauthorized(); // 0xe5336a6b
Expand Down Expand Up @@ -55,7 +56,7 @@ library Errors {
error Rollup__InvalidChainId(uint256 expected, uint256 actual); // 0x37b5bc12
error Rollup__InvalidVersion(uint256 expected, uint256 actual); // 0x9ef30794
error Rollup__InvalidTimestamp(uint256 expected, uint256 actual); // 0x3132e895
error Rollup__TimestampInFuture(); // 0xbc1ce916
error Rollup__TimestampInFuture(uint256 max, uint256 actual); // 0x89f30690
error Rollup__TimestampTooOld(); // 0x72ed9c81
error Rollup__UnavailableTxs(bytes32 txsHash); // 0x414906c3
error Rollup__NothingToPrune(); // 0x850defd3
Expand Down
36 changes: 18 additions & 18 deletions l1-contracts/src/core/sequencer_selection/Leonidas.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Copyright 2024 Aztec Labs.
pragma solidity >=0.8.18;

import {DataStructures} from "../libraries/DataStructures.sol";
import {Errors} from "../libraries/Errors.sol";
import {EnumerableSet} from "@oz/utils/structs/EnumerableSet.sol";
import {Ownable} from "@oz/access/Ownable.sol";
Expand Down Expand Up @@ -349,29 +350,18 @@ contract Leonidas is Ownable, ILeonidas {
* - If the proposer is not the real proposer AND the proposer is not open
* - If the number of valid attestations is insufficient
*
* @param _epochNumber - The epoch number of the block
* @param _slot - The slot of the block
* @param _signatures - The signatures of the committee members
* @param _digest - The digest of the block
*/
function _processPendingBlock(
uint256 _epochNumber,
uint256 _slot,
SignatureLib.Signature[] memory _signatures,
bytes32 _digest
) internal {
// @note Setup the CURRENT epoch if not already done.
// not necessarily the one we are processing!
setupEpoch();

Epoch storage epoch = epochs[_epochNumber];

// We should never enter this case because of `setupEpoch`
if (epoch.sampleSeed == 0) {
revert Errors.Leonidas__EpochNotSetup();
}

address proposer = getProposerAt(getTimestampForSlot(_slot));
bytes32 _digest,
DataStructures.ExecutionFlags memory _flags
) internal view {
uint256 ts = getTimestampForSlot(_slot);
address proposer = getProposerAt(ts);

// If the proposer is open, we allow anyone to propose without needing any signatures
if (proposer == address(0)) {
Expand All @@ -383,7 +373,17 @@ contract Leonidas is Ownable, ILeonidas {
revert Errors.Leonidas__InvalidProposer(proposer, msg.sender);
}

uint256 needed = epoch.committee.length * 2 / 3 + 1;
// @note This is NOT the efficient way to do it, but it is a very convenient way for us to do it
// that allows us to reduce the number of code paths. Also when changed with optimistic for
// pleistarchus, this will be changed, so we can live with it.

if (_flags.ignoreSignatures) {
return;
}

address[] memory committee = getCommitteeAt(ts);

uint256 needed = committee.length * 2 / 3 + 1;
if (_signatures.length < needed) {
revert Errors.Leonidas__InsufficientAttestationsProvided(needed, _signatures.length);
}
Expand All @@ -400,7 +400,7 @@ contract Leonidas is Ownable, ILeonidas {
}

// The verification will throw if invalid
signature.verify(epoch.committee[i], ethSignedDigest);
signature.verify(committee[i], ethSignedDigest);
validAttestations++;
}

Expand Down
Loading
Loading