Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
benesjan committed Jan 15, 2024
1 parent e28a6bf commit 9bc7bf4
Show file tree
Hide file tree
Showing 24 changed files with 482 additions and 514 deletions.
62 changes: 33 additions & 29 deletions l1-contracts/src/core/Rollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -44,23 +44,29 @@ contract Rollup is IRollup {
}

/**
* @notice Process an incoming L2Block and progress the state
* @param _proof - The proof of correct execution
* @param _l2Block - The L2Block data, formatted as outlined in `Decoder.sol`
* @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 _body - The L2 block body.
* @param _proof - The proof of correct execution.
*/
function process(bytes memory _proof, bytes calldata _l2Block) external override(IRollup) {
_constrainGlobals(_l2Block);
function process(
bytes calldata _header,
bytes calldata _archive,
bytes calldata _body, // Note: this will be replaced with _txsHash once the separation is finished.
bytes memory _proof
) external override(IRollup) {
// 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)) {
Expand All @@ -70,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.
Expand All @@ -82,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
Expand All @@ -98,34 +102,34 @@ contract Rollup is IRollup {
IOutbox outbox = REGISTRY.getOutbox();
outbox.sendL1Messages(l2ToL1Msgs);

emit L2BlockProcessed(l2BlockNumber);
emit L2BlockProcessed(header.blockNumber);
}

function _constrainGlobals(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();
}

// @todo @LHerskind consider if this is too strict
// 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();
}

// @todo @LHerskind Proper genesis state. If the state is empty, we allow anything for now.
if (archive != bytes32(0) && archive != header.lastArchive) {
revert Errors.Rollup__InvalidArchive(archive, header.lastArchive);
}
}

function _computePublicInputHash(bytes calldata _header, bytes32 _txsHash, bytes32 _inHash)
Expand Down
9 changes: 7 additions & 2 deletions l1-contracts/src/core/interfaces/IRollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
2 changes: 1 addition & 1 deletion l1-contracts/src/core/libraries/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
83 changes: 24 additions & 59 deletions l1-contracts/src/core/libraries/decoders/Decoder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -107,40 +107,6 @@ library Decoder {
uint256 private constant BLOCK_HEADER_OFFSET =
START_TREES_BLOCK_HEADER_OFFSET + 2 * TREES_BLOCK_HEADER_SIZE;

/**
* @notice Decodes the inputs and computes values to check state against
* @param _l2Block - The L2 block calldata.
* @return l2BlockNumber - The L2 block number.
* @return startStateHash - The state hash expected prior the execution.
* @return endStateHash - The state hash expected after the execution.
* @return publicInputHash - The hash of the public inputs
* @return l2ToL1Msgs - The L2 to L1 messages
* @return l1ToL2Msgs - The L1 to L2 messages
*/
function decode(bytes calldata _l2Block)
internal
pure
returns (
uint256 l2BlockNumber,
bytes32 startStateHash,
bytes32 endStateHash,
bytes32 publicInputHash,
bytes32[] memory l2ToL1Msgs,
bytes32[] memory l1ToL2Msgs
)
{
l2BlockNumber = getL2BlockNumber(_l2Block);
// 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, _l2Block);
endStateHash = computeStateHash(l2BlockNumber, END_TREES_BLOCK_HEADER_OFFSET, _l2Block);

bytes32 diffRoot;
bytes32 l1ToL2MsgsHash;
(diffRoot, l1ToL2MsgsHash, l2ToL1Msgs, l1ToL2Msgs) = computeConsumables(_l2Block);
publicInputHash = computePublicInputHash(_l2Block, diffRoot, l1ToL2MsgsHash);
}

/**
* @notice Computes the public input hash
* @dev Uses sha256 to field
Expand Down Expand Up @@ -189,13 +155,13 @@ library Decoder {

/**
* @notice Computes consumables for the block
* @param _l2Block - The L2 block calldata.
* @param _body - The L2 block body.
* @return diffRoot - The root of the diff tree (new commitments, nullifiers etc)
* @return l1ToL2MsgsHash - The hash of the L1 to L2 messages
* @return l2ToL1Msgs - The L2 to L1 messages of the block
* @return l1ToL2Msgs - The L1 to L2 messages of the block
*/
function computeConsumables(bytes calldata _l2Block)
function computeConsumables(bytes calldata _body)
internal
pure
returns (bytes32, bytes32, bytes32[] memory, bytes32[] memory)
Expand All @@ -204,52 +170,53 @@ library Decoder {
ConsumablesVars memory vars;

{
uint256 offset = BLOCK_HEADER_OFFSET;
uint256 offset = 0;

// Commitments
uint256 count = read4(_l2Block, offset);
uint256 count = read4(_body, offset);
vars.baseLeaves = new bytes32[](count / Constants.MAX_NEW_COMMITMENTS_PER_TX);
offsets.commitment = BLOCK_HEADER_OFFSET + 0x4;
offset += 0x4 + count * 0x20;
offset += 0x4;
offsets.commitment = offset;
offset += count * 0x20;
offsets.nullifier = offset + 0x4; // + 0x4 to offset by next read4

// Nullifiers
count = read4(_l2Block, offset);
count = read4(_body, offset);
offset += 0x4 + count * 0x20;
offsets.publicData = offset + 0x4; // + 0x4 to offset by next read4

// Public data writes
count = read4(_l2Block, offset);
count = read4(_body, offset);
offset += 0x4 + count * 0x40;
offsets.l2ToL1Msgs = offset + 0x4; // + 0x4 to offset by next read4

// L2 to L1 messages
count = read4(_l2Block, offset);
count = read4(_body, offset);
vars.l2ToL1Msgs = new bytes32[](count);
assembly {
// load the l2 to l1 msgs (done here as offset will be altered in loop)
let l2ToL1Msgs := mload(add(vars, 0x20))
calldatacopy(
add(l2ToL1Msgs, 0x20), add(_l2Block.offset, mload(add(offsets, 0x60))), mul(count, 0x20)
add(l2ToL1Msgs, 0x20), add(_body.offset, mload(add(offsets, 0x60))), mul(count, 0x20)
)
}
offset += 0x4 + count * 0x20;
offsets.contracts = offset + 0x4; // + 0x4 to offset by next read4

// Contracts
count = read4(_l2Block, offset);
count = read4(_body, offset);
offsets.contractData = offsets.contracts + count * 0x20;
offset += 0x4 + count * 0x54;
offsets.l1ToL2Msgs = offset + 0x4; // + 0x4 to offset by next read4

// L1 to L2 messages
count = read4(_l2Block, offset);
count = read4(_body, offset);
vars.l1Tol2MsgsCount = count;
offset += 0x4 + count * 0x20;
offsets.encryptedLogs = offset + 0x4; // + 0x4 to offset by next read4

// Used as length in bytes down here
uint256 length = read4(_l2Block, offset);
uint256 length = read4(_body, offset);
offsets.unencryptedLogs = offsets.encryptedLogs + 0x4 + length;
}

Expand Down Expand Up @@ -278,26 +245,24 @@ library Decoder {
* Note: will advance offsets by the number of bytes processed.
*/
(vars.encryptedLogsHash, offsets.encryptedLogs) =
computeKernelLogsHash(offsets.encryptedLogs, _l2Block);
computeKernelLogsHash(offsets.encryptedLogs, _body);

(vars.unencryptedLogsHash, offsets.unencryptedLogs) =
computeKernelLogsHash(offsets.unencryptedLogs, _l2Block);
computeKernelLogsHash(offsets.unencryptedLogs, _body);

// Insertions are split into multiple `bytes.concat` to work around stack too deep.
vars.baseLeaf = bytes.concat(
bytes.concat(
slice(_l2Block, offsets.commitment, Constants.COMMITMENTS_NUM_BYTES_PER_BASE_ROLLUP),
slice(_l2Block, offsets.nullifier, Constants.NULLIFIERS_NUM_BYTES_PER_BASE_ROLLUP),
slice(
_l2Block, offsets.publicData, Constants.PUBLIC_DATA_WRITES_NUM_BYTES_PER_BASE_ROLLUP
),
slice(_l2Block, offsets.l2ToL1Msgs, Constants.L2_TO_L1_MSGS_NUM_BYTES_PER_BASE_ROLLUP),
slice(_l2Block, offsets.contracts, Constants.CONTRACTS_NUM_BYTES_PER_BASE_ROLLUP)
slice(_body, offsets.commitment, Constants.COMMITMENTS_NUM_BYTES_PER_BASE_ROLLUP),
slice(_body, offsets.nullifier, Constants.NULLIFIERS_NUM_BYTES_PER_BASE_ROLLUP),
slice(_body, offsets.publicData, Constants.PUBLIC_DATA_WRITES_NUM_BYTES_PER_BASE_ROLLUP),
slice(_body, offsets.l2ToL1Msgs, Constants.L2_TO_L1_MSGS_NUM_BYTES_PER_BASE_ROLLUP),
slice(_body, offsets.contracts, Constants.CONTRACTS_NUM_BYTES_PER_BASE_ROLLUP)
),
bytes.concat(
slice(_l2Block, offsets.contractData, 0x20), // newContractDataKernel.aztecAddress
slice(_body, offsets.contractData, 0x20), // newContractDataKernel.aztecAddress
bytes12(0),
slice(_l2Block, offsets.contractData + 0x20, 0x14) // newContractDataKernel.ethAddress
slice(_body, offsets.contractData + 0x20, 0x14) // newContractDataKernel.ethAddress
),
bytes.concat(vars.encryptedLogsHash, vars.unencryptedLogsHash)
);
Expand All @@ -324,7 +289,7 @@ library Decoder {
assembly {
calldatacopy(
add(l1ToL2Msgs, 0x20),
add(_l2Block.offset, mload(add(offsets, 0xc0))),
add(_body.offset, mload(add(offsets, 0xc0))),
l1ToL2MsgsHashPreimageSize
)
}
Expand Down
Loading

0 comments on commit 9bc7bf4

Please sign in to comment.