Skip to content

Commit

Permalink
feat: Add log of blocks proposed and split pending/proven
Browse files Browse the repository at this point in the history
  • Loading branch information
LHerskind committed Jul 30, 2024
1 parent f35bac5 commit f28eefb
Show file tree
Hide file tree
Showing 15 changed files with 452 additions and 246 deletions.
4 changes: 2 additions & 2 deletions l1-contracts/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ remappings = [
# See more config options https://github.com/foundry-rs/foundry/tree/master/config

fs_permissions = [
{access = "read", path = "./test/fixtures/mixed_block_0.json"},
{access = "read", path = "./test/fixtures/mixed_block_1.json"},
{access = "read", path = "./test/fixtures/empty_block_0.json"},
{access = "read", path = "./test/fixtures/mixed_block_2.json"},
{access = "read", path = "./test/fixtures/empty_block_1.json"},
{access = "read", path = "./test/fixtures/empty_block_2.json"},
]

[fmt]
Expand Down
124 changes: 115 additions & 9 deletions l1-contracts/src/core/Rollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ import {Leonidas} from "./sequencer_selection/Leonidas.sol";
* not giving a damn about gas costs.
*/
contract Rollup is Leonidas, IRollup {
struct BlockLog {
bytes32 archive;
bool isProven;
}

IRegistry public immutable REGISTRY;
IAvailabilityOracle public immutable AVAILABILITY_ORACLE;
IInbox public immutable INBOX;
Expand All @@ -40,12 +45,22 @@ contract Rollup is Leonidas, IRollup {
IERC20 public immutable GAS_TOKEN;

IVerifier public verifier;
bytes32 public archive; // Root of the archive tree

uint256 public lastBlockTs;
// Tracks the last time time was warped on L2 ("warp" is the testing cheatcode).
// See https://github.com/AztecProtocol/aztec-packages/issues/1614
uint256 public lastWarpedBlockTs;

uint256 public pendingBlockCount;
uint256 public provenBlockCount;

// @todo Validate assumption:
// Currently we assume that the archive root following a block is specific to the block
// e.g., changing any values in the block or header should in the end make its way to the archive
//
// More direct approach would be storing keccak256(header) as well
mapping(uint256 blockNumber => BlockLog log) public blocks;

bytes32 public vkTreeRoot;

constructor(
Expand All @@ -62,6 +77,11 @@ contract Rollup is Leonidas, IRollup {
OUTBOX = new Outbox(address(this));
vkTreeRoot = _vkTreeRoot;
VERSION = 1;

// Genesis block
blocks[0] = BlockLog(bytes32(0), true);
pendingBlockCount = 1;
provenBlockCount = 1;
}

function setVerifier(address _verifier) external override(IRollup) {
Expand All @@ -73,6 +93,18 @@ contract Rollup is Leonidas, IRollup {
vkTreeRoot = _vkTreeRoot;
}

function archive() public view returns (bytes32) {
return blocks[pendingBlockCount - 1].archive;
}

function isBlockProven(uint256 _blockNumber) public view returns (bool) {
return blocks[_blockNumber].isProven;
}

function archiveAt(uint256 _blockNumber) public view returns (bytes32) {
return blocks[_blockNumber].archive;
}

/**
* @notice Process an incoming L2 block and progress the state
* @param _header - The L2 block header
Expand All @@ -88,14 +120,21 @@ contract Rollup is Leonidas, IRollup {

// Decode and validate header
HeaderLib.Header memory header = HeaderLib.decode(_header);
HeaderLib.validate(header, VERSION, lastBlockTs, archive);
HeaderLib.validate(header, VERSION, lastBlockTs, archive());

if (header.globalVariables.blockNumber != pendingBlockCount) {
revert Errors.Rollup__InvalidBlockNumber(
pendingBlockCount, header.globalVariables.blockNumber
);
}

// 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)) {
revert Errors.Rollup__UnavailableTxs(header.contentCommitment.txsEffectsHash);
}

archive = _archive;
blocks[pendingBlockCount++] = BlockLog(_archive, false);

lastBlockTs = block.timestamp;

bytes32 inHash = INBOX.consume();
Expand Down Expand Up @@ -124,6 +163,29 @@ contract Rollup is Leonidas, IRollup {
process(_header, _archive, emptySignatures);
}

/**
* @notice Submit a proof for a block in the pending chain
*
* @dev Will call `_progressState` to update the proven chain. Notice that this has
*
* @dev Will emit `L2ProofVerified` if the proof is valid
*
* @dev Will throw if:
* - The block number is past the pending chain
* - The last archive root of the header does not match the archive root of parent block
* - The archive root of the header does not match the archive root of the proposed block
* - The proof is invalid
*
* @dev We provide the `_archive` even if it could be read from storage itself because it allow for
* better error messages. Without passing it, we would just have a proof verification failure.
*
* @dev Following the `BlockLog` struct assumption
*
* @param _header - The header of the block (should match the block in the pending chain)
* @param _archive - The archive root of the block (should match the block in the pending chain)
* @param _aggregationObject - The aggregation object for the proof
* @param _proof - The proof to verify
*/
function submitProof(
bytes calldata _header,
bytes32 _archive,
Expand All @@ -132,6 +194,23 @@ contract Rollup is Leonidas, IRollup {
) external override(IRollup) {
HeaderLib.Header memory header = HeaderLib.decode(_header);

if (header.globalVariables.blockNumber >= pendingBlockCount) {
revert Errors.Rollup__TryingToProveNonExistingBlock();
}

bytes32 expectedLastArchive = blocks[header.globalVariables.blockNumber - 1].archive;
bytes32 expectedArchive = blocks[header.globalVariables.blockNumber].archive;

// We do it this way to provide better error messages than passing along the storage values
// TODO(#4148) Proper genesis state. If the state is empty, we allow anything for now.
if (expectedLastArchive != bytes32(0) && header.lastArchive.root != expectedLastArchive) {
revert Errors.Rollup__InvalidArchive(expectedLastArchive, header.lastArchive.root);
}

if (_archive != expectedArchive) {
revert Errors.Rollup__InvalidProposedArchive(expectedArchive, _archive);
}

bytes32[] memory publicInputs =
new bytes32[](3 + Constants.HEADER_LENGTH + Constants.AGGREGATION_OBJECT_LENGTH);
// the archive tree root
Expand Down Expand Up @@ -164,14 +243,41 @@ contract Rollup is Leonidas, IRollup {
revert Errors.Rollup__InvalidProof();
}

blocks[header.globalVariables.blockNumber].isProven = true;

_progressState();

emit L2ProofVerified(header.globalVariables.blockNumber);
}

function _computePublicInputHash(bytes calldata _header, bytes32 _archive)
internal
pure
returns (bytes32)
{
return Hash.sha256ToField(bytes.concat(_header, _archive));
/**
* @notice Progresses the state of the proven chain as far as possible
*
* @dev Emits `ProgressedState` if the state is progressed
*
* @dev Will continue along the pending chain as long as the blocks are proven
* stops at the first unproven block.
*
* @dev Have a potentially unbounded gas usage. @todo Will need a bounded version, such that it cannot be
* used as a DOS vector.
*/
function _progressState() internal {
if (pendingBlockCount == provenBlockCount) {
// We are already up to date
return;
}

uint256 cachedProvenBlockCount = provenBlockCount;

for (; cachedProvenBlockCount < pendingBlockCount; cachedProvenBlockCount++) {
if (!blocks[cachedProvenBlockCount].isProven) {
break;
}
}

if (cachedProvenBlockCount > provenBlockCount) {
provenBlockCount = cachedProvenBlockCount;
emit ProgressedState(provenBlockCount, pendingBlockCount);
}
}
}
1 change: 1 addition & 0 deletions l1-contracts/src/core/interfaces/IRollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pragma solidity >=0.8.18;
interface IRollup {
event L2BlockProcessed(uint256 indexed blockNumber);
event L2ProofVerified(uint256 indexed blockNumber);
event ProgressedState(uint256 provenBlockCount, uint256 pendingBlockCount);

function process(bytes calldata _header, bytes32 _archive) external;

Expand Down
3 changes: 3 additions & 0 deletions l1-contracts/src/core/libraries/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ library Errors {

// Rollup
error Rollup__InvalidArchive(bytes32 expected, bytes32 actual); // 0xb682a40e
error Rollup__InvalidProposedArchive(bytes32 expected, bytes32 actual); // 0x32532e73
error Rollup__InvalidBlockNumber(uint256 expected, uint256 actual); // 0xe5edf847
error Rollup__TryingToProveNonExistingBlock(); // 0x34ef4954
error Rollup__InvalidInHash(bytes32 expected, bytes32 actual); // 0xcd6f4233
error Rollup__InvalidProof(); // 0xa5b2ba17
error Rollup__InvalidChainId(uint256 expected, uint256 actual); // 0x37b5bc12
Expand Down
4 changes: 2 additions & 2 deletions l1-contracts/src/core/libraries/HeaderLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,11 @@ library HeaderLib {
view
{
if (block.chainid != _header.globalVariables.chainId) {
revert Errors.Rollup__InvalidChainId(_header.globalVariables.chainId, block.chainid);
revert Errors.Rollup__InvalidChainId(block.chainid, _header.globalVariables.chainId);
}

if (_header.globalVariables.version != _version) {
revert Errors.Rollup__InvalidVersion(_header.globalVariables.version, _version);
revert Errors.Rollup__InvalidVersion(_version, _header.globalVariables.version);
}

// block number already constrained by archive root check
Expand Down
Loading

0 comments on commit f28eefb

Please sign in to comment.