Skip to content
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
134 changes: 77 additions & 57 deletions l1-contracts/src/core/Decoder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,31 @@ pragma solidity >=0.8.18;
* |--- |--- | ---
*/
contract Decoder {
struct ArrayLengths {
uint256 commitmentCount;
uint256 nullifierCount;
uint256 dataWritesCount;
uint256 l2ToL1MsgsCount;
uint256 contractCount;
uint256 l1Tol2MsgsCount;
}

struct ArrayOffsets {
uint256 commitmentOffset;
uint256 nullifierOffset;
uint256 publicDataOffset;
uint256 l2ToL1MsgsOffset;
uint256 contractOffset;
uint256 contractDataOffset;
uint256 l1ToL2MsgsOffset;
}

uint256 internal constant COMMITMENTS_PER_KERNEL = 4;
uint256 internal constant NULLIFIERS_PER_KERNEL = 4;
uint256 internal constant PUBLIC_DATA_WRITES_PER_KERNEL = 4;
uint256 internal constant L2_TO_L1_MSGS_PER_KERNEL = 2;
uint256 internal constant CONTRACTS_PER_KERNEL = 1;
uint256 internal constant L1_TO_L2_MESSAGES_PER_ROLLUP = 16;
uint256 internal constant L1_TO_L2_MSGS_PER_ROLLUP = 16;

// Prime field order
uint256 internal constant P =
Expand All @@ -81,6 +100,8 @@ contract Decoder {
* @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
Expand All @@ -89,40 +110,44 @@ contract Decoder {
uint256 l2BlockNumber,
bytes32 startStateHash,
bytes32 endStateHash,
bytes32 publicInputHash
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, 0x4, _l2Block);
endStateHash = _computeStateHash(l2BlockNumber, 0x120, _l2Block);
publicInputHash = _computePublicInputsHash(_l2Block);

bytes32 diffRoot;
bytes32 l1ToL2MsgsHash;
(diffRoot, l1ToL2MsgsHash, l2ToL1Msgs, l1ToL2Msgs) = _computeConsumables(_l2Block);
publicInputHash = _computePublicInputHash(_l2Block, diffRoot, l1ToL2MsgsHash);
}

/**
* Computes a hash of the public inputs from the calldata
* @notice Computes the public input hash
* @dev Uses sha256 to field
* @param _l2Block - The L2 block calldata.
* @return sha256(header[0x4: 0x23c], diffRoot, l1Tol2MessagesHash)
* @param _diffRoot - The root of the diff merkle tree
* @param _l1ToL2MsgsHash - The hash of the L1 to L2 messages
* @return publicInputHash - The hash of the public inputs (sha256 to field)
*/
function _computePublicInputsHash(bytes calldata _l2Block) internal pure returns (bytes32) {
// header size - block number size + one value for the diffRoot + one value for l1ToL2MessagesHash
function _computePublicInputHash(
bytes calldata _l2Block,
bytes32 _diffRoot,
bytes32 _l1ToL2MsgsHash
) internal pure returns (bytes32) {
uint256 size = 0x23c - 0x04 + 0x20 + 0x20;

// Compute the public inputs hash
bytes memory temp = new bytes(size);
assembly {
calldatacopy(add(temp, 0x20), add(_l2Block.offset, 0x04), size)
}

// Diff root
(bytes32 diffRoot, bytes32 l1ToL2messagesHash) = _computeDiffRootAndMessagesHash(_l2Block);
assembly {
let endOfTreesData := sub(0x23c, 0x04)
mstore(add(temp, add(0x20, endOfTreesData)), diffRoot)
mstore(add(temp, add(0x40, endOfTreesData)), l1ToL2messagesHash)
mstore(add(temp, add(0x20, endOfTreesData)), _diffRoot)
mstore(add(temp, add(0x40, endOfTreesData)), _l1ToL2MsgsHash)
}

return bytes32(uint256(sha256(temp)) % P);
}

Expand Down Expand Up @@ -165,36 +190,21 @@ contract Decoder {
return sha256(temp);
}

struct ArrayLengths {
uint256 commitmentCount;
uint256 nullifierCount;
uint256 dataWritesCount;
uint256 l2ToL1MessagesCount;
uint256 contractCount;
uint256 l1Tol2MessagesCount;
}

struct ArrayOffsets {
uint256 commitmentOffset;
uint256 nullifierOffset;
uint256 publicDataOffset;
uint256 l2ToL1MsgsOffset;
uint256 contractOffset;
uint256 contractDataOffset;
uint256 l1ToL2MessagesOffset;
}

/**
* @notice Creates a "diff" tree and compute its root
* @notice Computes consumables for the block
* @param _l2Block - The L2 block calldata.
* @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 _computeDiffRootAndMessagesHash(bytes calldata _l2Block)
function _computeConsumables(bytes calldata _l2Block)
internal
pure
returns (bytes32, bytes32)
returns (bytes32, bytes32, bytes32[] memory, bytes32[] memory)
{
// Find the lengths of the different inputs
// TOOD: Naming / getting the messages root within this function is a bit weird
// TODO: Naming / getting the messages root within this function is a bit weird
ArrayLengths memory lengths;
ArrayOffsets memory offsets;
{
Expand All @@ -206,35 +216,47 @@ contract Decoder {
offset := add(add(offset, 0x4), mul(nullifierCount, 0x20))
let dataWritesCount := and(shr(224, calldataload(offset)), 0xffffffff)
offset := add(add(offset, 0x4), mul(nullifierCount, 0x40))
let l2ToL1Count := and(shr(224, calldataload(offset)), 0xffffffff)
offset := add(add(offset, 0x4), mul(l2ToL1Count, 0x20))
let l2ToL1MsgsCount := and(shr(224, calldataload(offset)), 0xffffffff)
offset := add(add(offset, 0x4), mul(l2ToL1MsgsCount, 0x20))
let contractCount := and(shr(224, calldataload(offset)), 0xffffffff)
offset := add(add(offset, 0x4), mul(contractCount, 0x54))
let l1Tol2MessagesCount := and(shr(224, calldataload(offset)), 0xffffffff)
let l1Tol2MsgsCount := and(shr(224, calldataload(offset)), 0xffffffff)

// Store it in lengths
mstore(lengths, commitmentCount)
mstore(add(lengths, 0x20), nullifierCount)
mstore(add(lengths, 0x40), dataWritesCount)
mstore(add(lengths, 0x60), l2ToL1Count)
mstore(add(lengths, 0x60), l2ToL1MsgsCount)
mstore(add(lengths, 0x80), contractCount)
mstore(add(lengths, 0xa0), l1Tol2MessagesCount) // currently included to allow optimisation where empty messages are not included in calldata
mstore(add(lengths, 0xa0), l1Tol2MsgsCount) // currently included to allow optimisation where empty messages are not included in calldata
}
}

bytes32[] memory baseLeafs = new bytes32[](
lengths.commitmentCount / (COMMITMENTS_PER_KERNEL * 2)
);
bytes32[] memory l2ToL1Msgs = new bytes32[](
lengths.l2ToL1MsgsCount
);

// Data starts after header. Look at L2 Block Data specification at the top of this file.
{
offsets.commitmentOffset = 0x240;
offsets.nullifierOffset = offsets.commitmentOffset + 0x4 + lengths.commitmentCount * 0x20;
offsets.publicDataOffset = offsets.nullifierOffset + 0x4 + lengths.nullifierCount * 0x20;
offsets.l2ToL1MsgsOffset = offsets.publicDataOffset + 0x4 + lengths.dataWritesCount * 0x40;
offsets.contractOffset = offsets.l2ToL1MsgsOffset + 0x4 + lengths.l2ToL1MessagesCount * 0x20;
offsets.contractOffset = offsets.l2ToL1MsgsOffset + 0x4 + lengths.l2ToL1MsgsCount * 0x20;
offsets.contractDataOffset = offsets.contractOffset + lengths.contractCount * 0x20;
offsets.l1ToL2MessagesOffset = offsets.contractDataOffset + 0x4 + lengths.contractCount * 0x34;
offsets.l1ToL2MsgsOffset = offsets.contractDataOffset + 0x4 + lengths.contractCount * 0x34;

// load the l2 to l1 msgs (done here as offset will be altered in loop)
assembly {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can this array copy be moved down to where the other one is performed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm using that we just computer the offset values. We will be updating them every loop so was a nice way to have it right away.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

understood

calldatacopy(
add(l2ToL1Msgs, 0x20),
add(_l2Block.offset, mload(add(offsets, 0x60))),
mul(mload(add(lengths, 0x60)), 0x20)
)
}

for (uint256 i = 0; i < baseLeafs.length; i++) {
/**
Expand Down Expand Up @@ -341,25 +363,23 @@ contract Decoder {
}

bytes32 diffRoot = _computeRoot(baseLeafs);

bytes32 messagesHash;
bytes32[] memory l1ToL2Msgs;
bytes32 l1ToL2MsgsHash;
{
uint256 messagesHashPreimageSize = 0x20 * L1_TO_L2_MESSAGES_PER_ROLLUP;
bytes memory messagesHashPreimage = new bytes(
messagesHashPreimageSize
);
uint256 l1ToL2MsgsHashPreimageSize = 0x20 * L1_TO_L2_MSGS_PER_ROLLUP;
l1ToL2Msgs = new bytes32[](L1_TO_L2_MSGS_PER_ROLLUP);
assembly {
calldatacopy(
add(messagesHashPreimage, 0x20),
add(l1ToL2Msgs, 0x20),
add(_l2Block.offset, mload(add(offsets, 0xc0))),
messagesHashPreimageSize
l1ToL2MsgsHashPreimageSize
)
}

messagesHash = sha256(messagesHashPreimage);
l1ToL2MsgsHash = sha256(abi.encodePacked(l1ToL2Msgs));
}

return (diffRoot, messagesHash);
return (diffRoot, l1ToL2MsgsHash, l2ToL1Msgs, l1ToL2Msgs);
}

/**
Expand Down
10 changes: 8 additions & 2 deletions l1-contracts/src/core/Rollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,14 @@ contract Rollup is Decoder {
* @param _l2Block - The L2Block data, formatted as outlined in `Decoder.sol`
*/
function process(bytes memory _proof, bytes calldata _l2Block) external {
(uint256 l2BlockNumber, bytes32 oldStateHash, bytes32 newStateHash, bytes32 publicInputHash) =
_decode(_l2Block);
(
uint256 l2BlockNumber,
bytes32 oldStateHash,
bytes32 newStateHash,
bytes32 publicInputHash,
bytes32[] memory l2ToL1Msgs,
bytes32[] memory l1ToL2Msgs
) = _decode(_l2Block);

// @todo Proper genesis state. If the state is empty, we allow anything for now.
if (rollupStateHash != bytes32(0) && rollupStateHash != oldStateHash) {
Expand Down
36 changes: 32 additions & 4 deletions l1-contracts/test/Decoder.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,14 @@ contract DecoderTest is Test {
"Invalid messages hash"
);

(uint256 l2BlockNumber, bytes32 startStateHash, bytes32 endStateHash, bytes32 publicInputsHash)
= helper.decode(block_empty_1);
(
uint256 l2BlockNumber,
bytes32 startStateHash,
bytes32 endStateHash,
bytes32 publicInputsHash,
bytes32[] memory l2ToL1Msgs,
bytes32[] memory l1ToL2Msgs
) = helper.decode(block_empty_1);

assertEq(l2BlockNumber, 1, "Invalid block number");
assertEq(
Expand All @@ -64,6 +70,13 @@ contract DecoderTest is Test {

rollup.process(bytes(""), block_empty_1);

for (uint256 i = 0; i < l2ToL1Msgs.length; i++) {
assertEq(l2ToL1Msgs[i], bytes32(0), "Invalid l2ToL1Msgs");
}
for (uint256 i = 0; i < l1ToL2Msgs.length; i++) {
assertEq(l1ToL2Msgs[i], bytes32(0), "Invalid l1ToL2Msgs");
}

assertEq(rollup.rollupStateHash(), endStateHash, "Invalid rollup state hash");
}

Expand All @@ -82,8 +95,14 @@ contract DecoderTest is Test {
"Invalid messages hash"
);

(uint256 l2BlockNumber, bytes32 startStateHash, bytes32 endStateHash, bytes32 publicInputsHash)
= helper.decode(block_mixed_1);
(
uint256 l2BlockNumber,
bytes32 startStateHash,
bytes32 endStateHash,
bytes32 publicInputsHash,
bytes32[] memory l2ToL1Msgs,
bytes32[] memory l1ToL2Msgs
) = helper.decode(block_mixed_1);

assertEq(l2BlockNumber, 1, "Invalid block number");
assertEq(
Expand All @@ -102,6 +121,15 @@ contract DecoderTest is Test {
"Invalid public input hash"
);

for (uint256 i = 0; i < l2ToL1Msgs.length; i++) {
// recreate the value generated by `integration_l1_publisher.test.ts`.
bytes32 expectedValue = bytes32(uint256(0x300 + 32 * (1 + i / 2) + i % 2));
assertEq(l2ToL1Msgs[i], expectedValue, "Invalid l2ToL1Msgs");
}
for (uint256 i = 0; i < l1ToL2Msgs.length; i++) {
assertEq(l1ToL2Msgs[i], bytes32(uint256(0x401 + i)), "Invalid l1ToL2Msgs");
}

rollup.process(bytes(""), block_mixed_1);

assertEq(rollup.rollupStateHash(), endStateHash, "Invalid rollup state hash");
Expand Down
4 changes: 2 additions & 2 deletions l1-contracts/test/DecoderHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ contract DecoderHelper is Decoder {
function decode(bytes calldata _l2Block)
external
pure
returns (uint256, bytes32, bytes32, bytes32)
returns (uint256, bytes32, bytes32, bytes32, bytes32[] memory, bytes32[] memory)
{
return _decode(_l2Block);
}
Expand All @@ -19,7 +19,7 @@ contract DecoderHelper is Decoder {
pure
returns (bytes32, bytes32)
{
(bytes32 diffRoot, bytes32 l1ToL2MessagesHash) = _computeDiffRootAndMessagesHash(_l2Block);
(bytes32 diffRoot, bytes32 l1ToL2MessagesHash,,) = _computeConsumables(_l2Block);
return (diffRoot, l1ToL2MessagesHash);
}
}