diff --git a/l1-contracts/src/core/Rollup.sol b/l1-contracts/src/core/Rollup.sol index 71deebe305e..bf11afbaa74 100644 --- a/l1-contracts/src/core/Rollup.sol +++ b/l1-contracts/src/core/Rollup.sol @@ -30,7 +30,7 @@ contract Rollup is IRollup { uint256 public immutable VERSION; AvailabilityOracle public immutable AVAILABILITY_ORACLE; - bytes32 public rollupStateHash; + 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 @@ -47,29 +47,26 @@ contract Rollup is IRollup { * @notice Process an incoming L2 block and progress the state * @param _header - The L2 block header. * @param _archive - A snapshot (root and next available leaf index) of the archive tree after the L2 block is applied - * @param _l1_to_l2_msgs - The L1 to L2 messages processed in this block - * @param _l2_to_l1_msgs - The L2 to L1 messages processed in this block + * @param _body - The L2 block body. * @param _proof - The proof of correct execution. */ function process( bytes calldata _header, bytes calldata _archive, - bytes calldata _l1_to_l2_msgs, - bytes calldata _l2_to_l1_msgs, + bytes calldata _body, // Note: this will be replaced with _txsHash once the separation is finished. bytes memory _proof ) external override(IRollup) { - _validateHeader(_header); + // TODO: @benejsan Should we represent this values from header as a nice struct? + HeaderDecoder.Header memory header = HeaderDecoder.decode(_header); - // Decode the header - (uint256 l2BlockNumber, bytes32 oldStateHash, bytes32 newStateHash) = - HeaderDecoder.decode(_l2Block[:HeaderDecoder.BLOCK_HEADER_SIZE]); + _validateHeader(header); // Check if the data is available using availability oracle (change availability oracle if you want a different DA layer) bytes32 txsHash; { // @todo @LHerskind Hack such that the node is unchanged for now. // should be removed when we have a proper block publication. - txsHash = AVAILABILITY_ORACLE.publish(_l2Block[HeaderDecoder.BLOCK_HEADER_SIZE:]); + txsHash = AVAILABILITY_ORACLE.publish(_body); } if (!AVAILABILITY_ORACLE.isAvailable(txsHash)) { @@ -79,10 +76,7 @@ contract Rollup is IRollup { // Decode the cross-chain messages (bytes32 inHash,, bytes32[] memory l1ToL2Msgs, bytes32[] memory l2ToL1Msgs) = - MessagesDecoder.decode(_l2Block[HeaderDecoder.BLOCK_HEADER_SIZE:]); - - bytes32 publicInputHash = - _computePublicInputHash(_l2Block[:HeaderDecoder.BLOCK_HEADER_SIZE], txsHash, inHash); + MessagesDecoder.decode(_body); // @todo @LHerskind Proper genesis state. If the state is empty, we allow anything for now. // TODO(#3936): Temporarily disabling this because L2Block encoding has not yet been updated. @@ -91,13 +85,14 @@ contract Rollup is IRollup { // } bytes32[] memory publicInputs = new bytes32[](1); - publicInputs[0] = publicInputHash; + publicInputs[0] = _computePublicInputHash(_header, txsHash, inHash); if (!VERIFIER.verify(_proof, publicInputs)) { revert Errors.Rollup__InvalidProof(); } - rollupStateHash = newStateHash; + // TODO: @benejsan Manually extracting the root here is ugly. TODO: Re-think how to represent archive snap. + archive = bytes32(_header[:0x20]); lastBlockTs = block.timestamp; // @todo (issue #605) handle fee collector @@ -107,24 +102,19 @@ contract Rollup is IRollup { IOutbox outbox = REGISTRY.getOutbox(); outbox.sendL1Messages(l2ToL1Msgs); - emit L2BlockProcessed(l2BlockNumber); + emit L2BlockProcessed(header.blockNumber); } - function _validateHeader(bytes calldata _header) internal view { - uint256 chainId = uint256(bytes32(_header[:0x20])); - uint256 version = uint256(bytes32(_header[0x20:0x40])); - uint256 ts = uint256(bytes32(_header[0x60:0x80])); - // block number already constrained by start state hash - - if (block.chainid != chainId) { - revert Errors.Rollup__InvalidChainId(chainId, block.chainid); + function _validateHeader(HeaderDecoder.Header memory header) internal view { + if (block.chainid != header.chainId) { + revert Errors.Rollup__InvalidChainId(header.chainId, block.chainid); } - if (version != VERSION) { - revert Errors.Rollup__InvalidVersion(version, VERSION); + if (header.version != VERSION) { + revert Errors.Rollup__InvalidVersion(header.version, VERSION); } - if (ts > block.timestamp) { + if (header.timestamp > block.timestamp) { revert Errors.Rollup__TimestampInFuture(); } @@ -132,9 +122,13 @@ contract Rollup is IRollup { // This will make multiple l2 blocks in the same l1 block impractical. // e.g., the first block will update timestamp which will make the second fail. // Could possibly allow multiple blocks if in same l1 block - if (ts < lastBlockTs) { + if (header.timestamp < lastBlockTs) { revert Errors.Rollup__TimestampTooOld(); } + + if (archive != header.lastArchive) { + revert Errors.Rollup__InvalidArchive(archive, header.lastArchive); + } } function _computePublicInputHash(bytes calldata _header, bytes32 _txsHash, bytes32 _inHash) diff --git a/l1-contracts/src/core/interfaces/IRollup.sol b/l1-contracts/src/core/interfaces/IRollup.sol index ab2fca20e18..264625275ff 100644 --- a/l1-contracts/src/core/interfaces/IRollup.sol +++ b/l1-contracts/src/core/interfaces/IRollup.sol @@ -3,7 +3,12 @@ pragma solidity >=0.8.18; interface IRollup { - event L2BlockProcessed(uint256 indexed blockNum); + event L2BlockProcessed(uint256 indexed blockNumber); - function process(bytes memory _proof, bytes calldata _l2Block) external; + function process( + bytes calldata _header, + bytes calldata _archive, + bytes calldata _body, + bytes memory _proof + ) external; } diff --git a/l1-contracts/src/core/libraries/Errors.sol b/l1-contracts/src/core/libraries/Errors.sol index e8d6785f183..39ec6a692bc 100644 --- a/l1-contracts/src/core/libraries/Errors.sol +++ b/l1-contracts/src/core/libraries/Errors.sol @@ -48,7 +48,7 @@ library Errors { ); // 0x5e789f34 // Rollup - error Rollup__InvalidStateHash(bytes32 expected, bytes32 actual); // 0xa3cfaab3 + error Rollup__InvalidArchive(bytes32 expected, bytes32 actual); // 0xb682a40e error Rollup__InvalidProof(); // 0xa5b2ba17 error Rollup__InvalidChainId(uint256 expected, uint256 actual); // 0x37b5bc12 error Rollup__InvalidVersion(uint256 expected, uint256 actual); // 0x9ef30794 diff --git a/l1-contracts/src/core/libraries/decoders/HeaderDecoder.sol b/l1-contracts/src/core/libraries/decoders/HeaderDecoder.sol index d902a257577..a016eb0ab0a 100644 --- a/l1-contracts/src/core/libraries/decoders/HeaderDecoder.sol +++ b/l1-contracts/src/core/libraries/decoders/HeaderDecoder.sol @@ -48,6 +48,15 @@ import {Hash} from "../Hash.sol"; * | --- | --- | --- */ library HeaderDecoder { + // TODO: This is only partial + struct Header { + uint256 chainId; + uint256 version; + uint256 blockNumber; + uint256 timestamp; + bytes32 lastArchive; + } + // DECODING OFFSET CONSTANTS // Where the start of trees metadata begins in the block uint256 private constant START_TREES_BLOCK_HEADER_OFFSET = 0x80; @@ -65,21 +74,21 @@ library HeaderDecoder { /** * @notice Decodes the header - * @param _header - The L2 block calldata. - * @return l2BlockNumber - The L2 block number - * @return startStateHash - The start state hash - * @return endStateHash - The end state hash + * @param _header - The header calldata. */ - function decode(bytes calldata _header) - internal - pure - returns (uint256 l2BlockNumber, bytes32 startStateHash, bytes32 endStateHash) - { - l2BlockNumber = uint256(bytes32(_header[0x40:0x60])); - // Note, for startStateHash to match the storage, the l2 block number must be new - 1. - // Only jumping 1 block at a time. - startStateHash = computeStateHash(l2BlockNumber - 1, START_TREES_BLOCK_HEADER_OFFSET, _header); - endStateHash = computeStateHash(l2BlockNumber, END_TREES_BLOCK_HEADER_OFFSET, _header); + function decode(bytes calldata _header) internal pure returns (Header memory) { + Header memory header; + + header.chainId = uint256(bytes32(_header[:0x20])); + header.version = uint256(bytes32(_header[0x20:0x40])); + header.blockNumber = uint256(bytes32(_header[0x40:0x60])); + header.timestamp = uint256(bytes32(_header[0x60:0x80])); + + // The rest is needed only by verifier and hence not decoded here. + + header.lastArchive = bytes32(_header[0x134:0x154]); + + return header; } /**