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: Generate json blocks for tests #3923

Merged
merged 3 commits into from
Jan 11, 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
10 changes: 9 additions & 1 deletion l1-contracts/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,18 @@ 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/empty_block_1.json"},
]

[fmt]
line_length = 100
tab_width = 2
variable_override_spacing=false

[rpc_endpoints]
mainnet_fork="https://mainnet.infura.io/v3/9928b52099854248b3a096be07a6b23c"
mainnet_fork="https://mainnet.infura.io/v3/9928b52099854248b3a096be07a6b23c"

2 changes: 1 addition & 1 deletion l1-contracts/src/core/Rollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ contract Rollup is IRollup {
}

// Decode the cross-chain messages
(bytes32 inHash,, bytes32[] memory l2ToL1Msgs, bytes32[] memory l1ToL2Msgs) =
(bytes32 inHash,, bytes32[] memory l1ToL2Msgs, bytes32[] memory l2ToL1Msgs) =
MessagesDecoder.decode(_l2Block[HeaderDecoder.BLOCK_HEADER_SIZE:]);

bytes32 publicInputHash =
Expand Down
22 changes: 13 additions & 9 deletions l1-contracts/src/core/libraries/decoders/MessagesDecoder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,22 @@ library MessagesDecoder {
/**
* @notice Computes consumables for the block
* @param _body - The L2 block calldata.
* @return l1ToL2MsgsHash - The hash of the L1 to L2 messages
* @return l2ToL1MsgsHash - The hash of the L1 to L2 messages
* @return l2ToL1Msgs - The L2 to L1 messages of the block
* @return inHash - The hash of the L1 to L2 messages
* @return outHash - The hash of the L1 to L2 messages
* @return l1ToL2Msgs - The L1 to L2 messages of the block
* @return l2ToL1Msgs - The L2 to L1 messages of the block
*/
function decode(bytes calldata _body)
internal
pure
returns (bytes32, bytes32, bytes32[] memory, bytes32[] memory)
returns (
bytes32 inHash,
bytes32 outHash,
bytes32[] memory l1ToL2Msgs,
bytes32[] memory l2ToL1Msgs
)
{
bytes32[] memory l1ToL2Msgs = new bytes32[](Constants.NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
bytes32[] memory l2ToL1Msgs;
l1ToL2Msgs = new bytes32[](Constants.NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);

uint256 offset = 0;

Expand Down Expand Up @@ -91,10 +95,10 @@ library MessagesDecoder {
calldatacopy(add(l1ToL2Msgs, 0x20), add(_body.offset, add(offset, 0x04)), mul(count, 0x20))
}

bytes32 l1ToL2MsgsHash = sha256(abi.encodePacked(l1ToL2Msgs));
bytes32 l2ToL1MsgsHash = sha256(abi.encodePacked(l2ToL1Msgs));
inHash = sha256(abi.encodePacked(l1ToL2Msgs));
outHash = sha256(abi.encodePacked(l2ToL1Msgs));

return (l1ToL2MsgsHash, l2ToL1MsgsHash, l2ToL1Msgs, l1ToL2Msgs);
return (inHash, outHash, l1ToL2Msgs, l2ToL1Msgs);
}

/**
Expand Down
274 changes: 0 additions & 274 deletions l1-contracts/test/Decoder.t.sol

This file was deleted.

4 changes: 1 addition & 3 deletions l1-contracts/test/DecoderHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ contract DecoderHelper {
pure
returns (bytes32, uint256)
{
(bytes32 logsHash, uint256 offset) = Decoder.computeKernelLogsHash(0, _kernelLogs);

return (logsHash, offset);
return Decoder.computeKernelLogsHash(0, _kernelLogs);
}
}
127 changes: 82 additions & 45 deletions l1-contracts/test/Rollup.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ pragma solidity >=0.8.18;

import {Test} from "forge-std/Test.sol";

import {DecoderTest} from "./Decoder.t.sol";
import {DecoderTest} from "./decoders/Decoder.t.sol";
import {DecoderHelper} from "./DecoderHelper.sol";

import {DecoderBase} from "./decoders/Base.sol";

import {DataStructures} from "../src/core/libraries/DataStructures.sol";

Expand All @@ -18,35 +21,45 @@ import {Rollup} from "../src/core/Rollup.sol";
* Blocks are generated using the `integration_l1_publisher.test.ts` tests.
* Main use of these test is shorter cycles when updating the decoder contract.
*/
contract RollupTest is DecoderTest {
// Skipping this because the block is invalid after I changed the constants.
function __testEmptyBlock() public {
(,, bytes32 endStateHash,, bytes32[] memory l2ToL1Msgs, bytes32[] memory l1ToL2Msgs) =
helper.decode(block_empty_1);

vm.record();
rollup.process(bytes(""), block_empty_1);
contract RollupTest is DecoderBase {
DecoderHelper internal helper;
Registry internal registry;
Inbox internal inbox;
Outbox internal outbox;
Rollup internal rollup;

function setUp() public virtual {
helper = new DecoderHelper();

registry = new Registry();
inbox = new Inbox(address(registry));
outbox = new Outbox(address(registry));
rollup = new Rollup(registry);

registry.upgrade(address(rollup), address(inbox), address(outbox));
}

(, bytes32[] memory inboxWrites) = vm.accesses(address(inbox));
(, bytes32[] memory outboxWrites) = vm.accesses(address(outbox));
function testMixedBlock() public {
_testBlock("mixed_block_0");
}

assertEq(inboxWrites.length, 0, "Invalid inbox writes");
assertEq(outboxWrites.length, 0, "Invalid outbox writes");
function testConsecutiveMixedBlocks() public {
_testBlock("mixed_block_0");
_testBlock("mixed_block_1");
}

for (uint256 i = 0; i < l2ToL1Msgs.length; i++) {
assertEq(l2ToL1Msgs[i], bytes32(0), "Invalid l2ToL1Msgs");
assertFalse(outbox.contains(l2ToL1Msgs[i]), "msg in outbox");
}
for (uint256 i = 0; i < l1ToL2Msgs.length; i++) {
assertEq(l1ToL2Msgs[i], bytes32(0), "Invalid l1ToL2Msgs");
assertFalse(inbox.contains(l1ToL2Msgs[i]), "msg in inbox");
}
function testEmptyBlock() public {
_testBlock("empty_block_0");
}

assertEq(rollup.rollupStateHash(), endStateHash, "Invalid rollup state hash");
function testConsecutiveEmptyBlocks() public {
_testBlock("empty_block_0");
_testBlock("empty_block_1");
}

function testRevertInvalidChainId() public {
bytes memory block_ = block_empty_1;
bytes memory block_ = load("empty_block_0").block.body;

assembly {
mstore(add(block_, 0x20), 0x420)
}
Expand All @@ -56,7 +69,8 @@ contract RollupTest is DecoderTest {
}

function testRevertInvalidVersion() public {
bytes memory block_ = block_empty_1;
bytes memory block_ = load("empty_block_0").block.body;

assembly {
mstore(add(block_, 0x40), 0x420)
}
Expand All @@ -66,7 +80,7 @@ contract RollupTest is DecoderTest {
}

function testRevertTimestampInFuture() public {
bytes memory block_ = block_empty_1;
bytes memory block_ = load("empty_block_0").block.body;

uint256 ts = block.timestamp + 1;
assembly {
Expand All @@ -78,7 +92,7 @@ contract RollupTest is DecoderTest {
}

function testRevertTimestampTooOld() public {
bytes memory block_ = block_empty_1;
bytes memory block_ = load("empty_block_0").block.body;

// Overwrite in the rollup contract
vm.store(address(rollup), bytes32(uint256(1)), bytes32(uint256(block.timestamp)));
Expand All @@ -87,37 +101,60 @@ contract RollupTest is DecoderTest {
rollup.process(bytes(""), block_);
}

function testMixBlock() public {
(,, bytes32 endStateHash,, bytes32[] memory l2ToL1Msgs, bytes32[] memory l1ToL2Msgs) =
helper.decode(block_mixed_1);
function _testBlock(string memory name) public {
DecoderBase.Full memory full = load(name);
// We jump to the time of the block.
vm.warp(full.block.timestamp);

bytes32[] memory expectedL1ToL2Msgs = _populateInbox();
_populateInbox(full.populate.sender, full.populate.recipient, full.populate.l1ToL2Content);

for (uint256 i = 0; i < l1ToL2Msgs.length; i++) {
assertTrue(inbox.contains(l1ToL2Msgs[i]), "msg not in inbox");
for (uint256 i = 0; i < full.messages.l1ToL2Messages.length; i++) {
if (full.messages.l1ToL2Messages[i] == bytes32(0)) {
continue;
}
assertTrue(inbox.contains(full.messages.l1ToL2Messages[i]), "msg not in inbox");
}

vm.record();
rollup.process(bytes(""), block_mixed_1);
rollup.process(bytes(""), full.block.body);

(, bytes32[] memory inboxWrites) = vm.accesses(address(inbox));
(, bytes32[] memory outboxWrites) = vm.accesses(address(outbox));

assertEq(inboxWrites.length, 16, "Invalid inbox writes");
assertEq(outboxWrites.length, 8, "Invalid outbox writes");

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");
assertTrue(outbox.contains(l2ToL1Msgs[i]), "msg not in outbox");
{
uint256 count = 0;
for (uint256 i = 0; i < full.messages.l2ToL1Messages.length; i++) {
if (full.messages.l2ToL1Messages[i] == bytes32(0)) {
continue;
}
assertTrue(outbox.contains(full.messages.l2ToL1Messages[i]), "msg not in outbox");
count++;
}
assertEq(outboxWrites.length, count, "Invalid outbox writes");
}

for (uint256 i = 0; i < l1ToL2Msgs.length; i++) {
assertEq(l1ToL2Msgs[i], expectedL1ToL2Msgs[i], "Invalid l1ToL2Msgs");
assertFalse(inbox.contains(l1ToL2Msgs[i]), "msg not consumed");
{
uint256 count = 0;
for (uint256 i = 0; i < full.messages.l1ToL2Messages.length; i++) {
if (full.messages.l1ToL2Messages[i] == bytes32(0)) {
continue;
}
assertFalse(inbox.contains(full.messages.l1ToL2Messages[i]), "msg not consumed");
count++;
}
assertEq(inboxWrites.length, count, "Invalid inbox writes");
}

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

function _populateInbox(address _sender, bytes32 _recipient, bytes32[] memory _contents) internal {
uint32 deadline = type(uint32).max;
for (uint256 i = 0; i < _contents.length; i++) {
vm.prank(_sender);
inbox.sendL2Message(
DataStructures.L2Actor({actor: _recipient, version: 1}), deadline, _contents[i], bytes32(0)
);
}
}
}
45 changes: 45 additions & 0 deletions l1-contracts/test/decoders/Base.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Aztec Labs.
pragma solidity >=0.8.18;

import {Test} from "forge-std/Test.sol";

contract DecoderBase is Test {
// When I had data and messages as one combined struct it failed, but I can have this top-layer and it works :shrug:
struct Full {
Data block;
Messages messages;
Populate populate;
}

struct Populate {
bytes32[] l1ToL2Content;
bytes32 recipient;
address sender;
}

struct Messages {
bytes32[] l1ToL2Messages;
bytes32[] l2ToL1Messages;
}

struct Data {
uint256 blockNumber;
bytes body;
bytes32 calldataHash;
bytes32 endStateHash;
bytes32 l1ToL2MessagesHash;
bytes32 publicInputsHash;
bytes32 startStateHash;
uint256 timestamp;
}

function load(string memory name) public view returns (Full memory) {
string memory root = vm.projectRoot();
string memory path = string.concat(root, "/test/fixtures/", name, ".json");
string memory json = vm.readFile(path);
bytes memory json_bytes = vm.parseJson(json);
Full memory full = abi.decode(json_bytes, (Full));
return full;
}
}
Loading