From 06ca5283c55c5cf96c7ce1cc21806351251c9c4d Mon Sep 17 00:00:00 2001 From: Esau Date: Mon, 4 Mar 2024 19:02:27 +0100 Subject: [PATCH 01/62] initial --- l1-contracts/test/merkle/Merkle.t.sol | 17 +++++++++++++++++ l1-contracts/test/merkle/Naive.sol | 25 ++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/l1-contracts/test/merkle/Merkle.t.sol b/l1-contracts/test/merkle/Merkle.t.sol index f430b72e43f..03ebff15f0b 100644 --- a/l1-contracts/test/merkle/Merkle.t.sol +++ b/l1-contracts/test/merkle/Merkle.t.sol @@ -27,4 +27,21 @@ contract MerkleTest is Test { assertEq(merkle.computeRoot(), frontier.root(), "Frontier Roots should be equal"); } } + + function testNaive() public { + uint256 upper = merkle.SIZE(); + + // function for bounding the test env + // uint idx = bound(_index, 0, upper - 1); + uint256 idx = 128; + + for (uint256 i = 0; i < upper; i++) { + bytes32 leaf = sha256(abi.encode(i + 1)); + merkle.insertLeaf(leaf); + } + + (bytes32[] memory path, bytes32 leaf) = merkle.computeSiblingPath(idx); + + assertTrue(merkle.verifyMembership(path, leaf, idx)); + } } diff --git a/l1-contracts/test/merkle/Naive.sol b/l1-contracts/test/merkle/Naive.sol index 1e6bc9c5584..0d325535cd8 100644 --- a/l1-contracts/test/merkle/Naive.sol +++ b/l1-contracts/test/merkle/Naive.sol @@ -2,7 +2,9 @@ // Copyright 2023 Aztec Labs. pragma solidity >=0.8.18; -contract NaiveMerkle { +import {Test} from "forge-std/Test.sol"; + +contract NaiveMerkle is Test { uint256 public immutable DEPTH; uint256 public immutable SIZE; @@ -34,4 +36,25 @@ contract NaiveMerkle { } return nodes[0]; } + + function computeSiblingPath(uint256 _index) public view returns (bytes32[] memory, bytes32) { + bytes32[] memory path = new bytes32[](DEPTH); + + // IMPLEMENT + return (path, leafs[_index]); + } + + function verifyMembership(bytes32[] memory _path, bytes32 _leaf, uint256 _index) public returns (bool) { + bytes32 root; + uint256 index = _index; + + for (uint256 i = 0; i < _path.length; i++) { + if (i == 0) { + root = sha256(bytes.concat(_leaf, _path[i])); + } + emit log_named_bytes32("Root", root); + } + + return computeRoot() == root; + } } From 8c14998c6e3b0eaf323041e233201ac7fe6b7330 Mon Sep 17 00:00:00 2001 From: Esau Date: Tue, 5 Mar 2024 19:50:10 +0100 Subject: [PATCH 02/62] new --- l1-contracts/src/core/libraries/Errors.sol | 5 + .../src/core/messagebridge/NewOubox.sol | 111 ++++++++++++++++++ l1-contracts/test/merkle/Merkle.t.sol | 37 +++++- l1-contracts/test/merkle/Naive.sol | 43 +++++-- 4 files changed, 186 insertions(+), 10 deletions(-) create mode 100644 l1-contracts/src/core/messagebridge/NewOubox.sol diff --git a/l1-contracts/src/core/libraries/Errors.sol b/l1-contracts/src/core/libraries/Errors.sol index 568bbda37c6..7b80afec8e1 100644 --- a/l1-contracts/src/core/libraries/Errors.sol +++ b/l1-contracts/src/core/libraries/Errors.sol @@ -46,6 +46,11 @@ library Errors { uint32 storedDeadline, uint32 deadlinePassed ); // 0x5e789f34 + error Outbox__InvalidPathLength(uint256 expected, uint256 actual); + error Outbox__InvalidRoot(bytes32 expected, bytes32 actual); + error Outbox__RootAlreadySet(uint256 l2BlockNumber); + error Outbox__InvalidRecipient(address expected, address actual); + error Outbox__AlreadyNullified(uint256 l2BlockNumber, uint256 leafIndex); // Rollup error Rollup__InvalidArchive(bytes32 expected, bytes32 actual); // 0xb682a40e diff --git a/l1-contracts/src/core/messagebridge/NewOubox.sol b/l1-contracts/src/core/messagebridge/NewOubox.sol new file mode 100644 index 00000000000..39256cd9d0a --- /dev/null +++ b/l1-contracts/src/core/messagebridge/NewOubox.sol @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 Aztec Labs. +pragma solidity >=0.8.18; + +// Libraries +import {DataStructures} from "../libraries/DataStructures.sol"; +import {Errors} from "../libraries/Errors.sol"; +import {Hash} from "../libraries/Hash.sol"; +import {MessageBox} from "../libraries/MessageBox.sol"; + +/** + * @title Outbox + * @author Aztec Labs + * @notice Lives on L1 and is used to consume L2 -> L1 messages. Messages are inserted by the rollup contract + * and will be consumed by the portal contracts. + */ +contract Outbox { + using Hash for DataStructures.L2ToL1Msg; + + event RootAdded(uint256 indexed l2BlockNumber, bytes32 indexed root, uint256 height); + event MessageConsumed(uint256 indexed l2BlockNumber, bytes32 indexed root, bytes32 indexed messageHash); + + struct RootData { + bytes32 root; + uint256 height; + mapping(uint256 => bool) nullified; + } + + address public immutable STATE_TRANSITIONER; + + mapping(uint256 l2BlockNumber => RootData) public roots; + + constructor(address _stateTransitioner) { + STATE_TRANSITIONER = _stateTransitioner; + } + + function insert(uint256 _l2BlockNumber, bytes32 _root, uint256 _height) external { + if (msg.sender != STATE_TRANSITIONER) { + revert Errors.Outbox__Unauthorized(); + } + + if (roots[_l2BlockNumber].root != bytes32(0)) { + revert Errors.Outbox__RootAlreadySet(_l2BlockNumber); + } + + roots[_l2BlockNumber].root = _root; + roots[_l2BlockNumber].height = _height; + + emit RootAdded(_l2BlockNumber, _root, _height); + } + + function consume( + uint256 _l2BlockNumber, + uint256 _leafIndex, + DataStructures.L2ToL1Msg memory _message, + bytes32[] memory _path + ) public { + if (msg.sender != _message.recipient.actor) { + revert Errors.Outbox__InvalidRecipient(_message.recipient.actor, msg.sender); + } + + if (block.chainid != _message.recipient.chainId) { + revert Errors.Outbox__InvalidChainId(); + } + + if (roots[_l2BlockNumber].nullified[_leafIndex]) { + revert Errors.Outbox__AlreadyNullified(_l2BlockNumber, _leafIndex); + } + + bytes32 root = roots[_l2BlockNumber].root; + + bytes32 messageHash = _verifyMembership(_path, _message, _leafIndex, root, roots[_l2BlockNumber].height); + + roots[_l2BlockNumber].nullified[_leafIndex] = true; + + emit MessageConsumed(_l2BlockNumber, root, messageHash); + } + + function _verifyMembership( + bytes32[] memory _path, + DataStructures.L2ToL1Msg memory _message, + uint256 _index, + bytes32 _root, + uint256 _height + ) internal pure returns (bytes32) { + if (_height != _path.length) { + revert Errors.Outbox__InvalidPathLength(_height, _path.length); + } + + bytes32 leaf = _message.sha256ToField(); + + bytes32 root; + uint256 index = _index; + + for (uint256 i = 0; i < _path.length; i++) { + bool isRight = (index & 1) == 1; + + root = isRight + ? sha256(bytes.concat(_path[i], i == 0 ? leaf : root)) + : sha256(bytes.concat(i == 0 ? leaf : root, _path[i])); + + index /= 2; + } + + if (root != _root) { + revert Errors.Outbox__InvalidRoot(_root, root); + } + + return leaf; + } +} diff --git a/l1-contracts/test/merkle/Merkle.t.sol b/l1-contracts/test/merkle/Merkle.t.sol index 03ebff15f0b..9dbcc18ca8e 100644 --- a/l1-contracts/test/merkle/Merkle.t.sol +++ b/l1-contracts/test/merkle/Merkle.t.sol @@ -28,12 +28,11 @@ contract MerkleTest is Test { } } - function testNaive() public { + function testNaive(uint256 _idx) public { uint256 upper = merkle.SIZE(); // function for bounding the test env - // uint idx = bound(_index, 0, upper - 1); - uint256 idx = 128; + uint256 idx = bound(_idx, 0, upper - 1); for (uint256 i = 0; i < upper; i++) { bytes32 leaf = sha256(abi.encode(i + 1)); @@ -42,6 +41,38 @@ contract MerkleTest is Test { (bytes32[] memory path, bytes32 leaf) = merkle.computeSiblingPath(idx); + for (uint256 i = 0; i < merkle.DEPTH(); i++) { + emit log_named_bytes32("path", path[i]); + } + + emit log_named_bytes32("test", leaf); + emit log_named_bytes32("size", bytes32(merkle.SIZE())); + emit log_named_bytes32("root", merkle.computeRoot()); + + assertTrue(merkle.verifyMembership(path, leaf, idx)); + } + + function testNaive(uint256 _idx) public { + uint256 upper = merkle.SIZE(); + + // function for bounding the test env + uint256 idx = bound(_idx, 0, upper - 1); + + for (uint256 i = 0; i < upper; i++) { + bytes32 leaf = sha256(abi.encode(i + 1)); + merkle.insertLeaf(leaf); + } + + (bytes32[] memory path, bytes32 leaf) = merkle.computeSiblingPath(idx); + + for (uint256 i = 0; i < merkle.DEPTH(); i++) { + emit log_named_bytes32("path", path[i]); + } + + emit log_named_bytes32("test", leaf); + emit log_named_bytes32("size", bytes32(merkle.SIZE())); + emit log_named_bytes32("root", merkle.computeRoot()); + assertTrue(merkle.verifyMembership(path, leaf, idx)); } } diff --git a/l1-contracts/test/merkle/Naive.sol b/l1-contracts/test/merkle/Naive.sol index 0d325535cd8..7675f48ba7d 100644 --- a/l1-contracts/test/merkle/Naive.sol +++ b/l1-contracts/test/merkle/Naive.sol @@ -37,22 +37,51 @@ contract NaiveMerkle is Test { return nodes[0]; } - function computeSiblingPath(uint256 _index) public view returns (bytes32[] memory, bytes32) { + function computeSiblingPath(uint256 _index) public returns (bytes32[] memory, bytes32) { + bytes32[] memory nodes = new bytes32[](SIZE / 2); bytes32[] memory path = new bytes32[](DEPTH); - // IMPLEMENT + uint256 idx = _index; + + uint256 size = SIZE; + for (uint256 i = 0; i < DEPTH; i++) { + bool isRight = (idx & 1) == 1; + if (i > 0) { + path[i] = isRight ? nodes[idx - 1] : nodes[idx + 1]; + } else { + path[i] = isRight ? leafs[idx - 1] : leafs[idx + 1]; + } + + for (uint256 j = 0; j < size; j += 2) { + if (i == 0) { + nodes[j / 2] = sha256(bytes.concat(leafs[j], leafs[j + 1])); + } else { + nodes[j / 2] = sha256(bytes.concat(nodes[j], nodes[j + 1])); + } + } + + idx /= 2; + size /= 2; + } + return (path, leafs[_index]); } - function verifyMembership(bytes32[] memory _path, bytes32 _leaf, uint256 _index) public returns (bool) { + function verifyMembership(bytes32[] memory _path, bytes32 _leaf, uint256 _index) + public + returns (bool) + { bytes32 root; uint256 index = _index; for (uint256 i = 0; i < _path.length; i++) { - if (i == 0) { - root = sha256(bytes.concat(_leaf, _path[i])); - } - emit log_named_bytes32("Root", root); + bool isRight = (index & 1) == 1; + + root = isRight + ? sha256(bytes.concat(_path[i], i == 0 ? _leaf : root)) + : sha256(bytes.concat(i == 0 ? _leaf : root, _path[i])); + + index /= 2; } return computeRoot() == root; From f6dd799c9df82d00ae6490293ebdd86b0a86e6e1 Mon Sep 17 00:00:00 2001 From: Esau Date: Wed, 6 Mar 2024 18:42:30 +0100 Subject: [PATCH 03/62] fix name --- .../src/core/messagebridge/{NewOubox.sol => NewOutbox.sol} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename l1-contracts/src/core/messagebridge/{NewOubox.sol => NewOutbox.sol} (100%) diff --git a/l1-contracts/src/core/messagebridge/NewOubox.sol b/l1-contracts/src/core/messagebridge/NewOutbox.sol similarity index 100% rename from l1-contracts/src/core/messagebridge/NewOubox.sol rename to l1-contracts/src/core/messagebridge/NewOutbox.sol From 9ddf5fe1c609095e95c41501bb7f13292bf3b75b Mon Sep 17 00:00:00 2001 From: Esau Date: Wed, 6 Mar 2024 22:12:35 +0100 Subject: [PATCH 04/62] fix --- .../interfaces/messagebridge/INewOutbox.sol | 46 ++++++ .../src/core/messagebridge/NewOutbox.sol | 20 +-- l1-contracts/test/NewOutbox.t.sol | 134 ++++++++++++++++++ l1-contracts/test/merkle/Merkle.t.sol | 2 +- l1-contracts/test/merkle/Naive.sol | 3 +- 5 files changed, 193 insertions(+), 12 deletions(-) create mode 100644 l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol create mode 100644 l1-contracts/test/NewOutbox.t.sol diff --git a/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol b/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol new file mode 100644 index 00000000000..2197ef78aed --- /dev/null +++ b/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 Aztec Labs. +pragma solidity >=0.8.18; + +import {DataStructures} from "../../libraries/DataStructures.sol"; + +/** + * @title INewOutbox + * @author Aztec Labs + * @notice Lives on L1 and is used to consume L2 -> L1 messages. Messages are inserted by the rollup contract + * and will be consumed by the portal contracts. + */ +// TODO: rename to IOutbox once all the pieces of the new message model are in place. +interface INewOutbox { + event RootAdded(uint256 indexed l2BlockNumber, bytes32 indexed root, uint256 height); + event MessageConsumed( + uint256 indexed l2BlockNumber, bytes32 indexed root, bytes32 indexed messageHash + ); + + /* + * @notice Inserts the root of a merkle tree containing all of the L2 to L1 messages in + * a block specified by _blockNumber. + * @dev Only callable by the rollup contract (state transitioner) + * @dev Emits the `RootAdded` upon inserting the root successfully + * @param _l2BlockNumber - The L2 Block Number in which the L2 to L1 message reside + * @param _root - The merkle root of the tree where all the L2 to L1 messages are leaves + * @param _height - The height of the merkle tree that the root corresponds to + */ + function insert(uint256 _l2BlockNumber, bytes32 _root, uint256 _height) external; + + /* + * @notice Consumes an entry from the Outbox + * @dev Only meaningfully callable by portals / recipients of messages + * @dev Emits the `MessageConsumed` event when consuming messages + * @param _l2BlockNumber - The block number specifying the block that contains the message we want to consume + * @param _leafIndex - The index where the message resides inside the merkle tree + * @param _message - The L2 to L1 message + * @param _path - The sibling path used to prove inclusion of the message + */ + function consume( + uint256 _l2BlockNumber, + uint256 _leafIndex, + DataStructures.L2ToL1Msg memory _message, + bytes32[] memory _path + ) external; +} diff --git a/l1-contracts/src/core/messagebridge/NewOutbox.sol b/l1-contracts/src/core/messagebridge/NewOutbox.sol index 39256cd9d0a..a5a37bc0aa6 100644 --- a/l1-contracts/src/core/messagebridge/NewOutbox.sol +++ b/l1-contracts/src/core/messagebridge/NewOutbox.sol @@ -6,20 +6,17 @@ pragma solidity >=0.8.18; import {DataStructures} from "../libraries/DataStructures.sol"; import {Errors} from "../libraries/Errors.sol"; import {Hash} from "../libraries/Hash.sol"; -import {MessageBox} from "../libraries/MessageBox.sol"; +import {INewOutbox} from "../interfaces/messagebridge/INewOutbox.sol"; /** - * @title Outbox + * @title NewOutbox * @author Aztec Labs * @notice Lives on L1 and is used to consume L2 -> L1 messages. Messages are inserted by the rollup contract * and will be consumed by the portal contracts. */ -contract Outbox { +contract NewOutbox is INewOutbox { using Hash for DataStructures.L2ToL1Msg; - event RootAdded(uint256 indexed l2BlockNumber, bytes32 indexed root, uint256 height); - event MessageConsumed(uint256 indexed l2BlockNumber, bytes32 indexed root, bytes32 indexed messageHash); - struct RootData { bytes32 root; uint256 height; @@ -27,14 +24,16 @@ contract Outbox { } address public immutable STATE_TRANSITIONER; - mapping(uint256 l2BlockNumber => RootData) public roots; constructor(address _stateTransitioner) { STATE_TRANSITIONER = _stateTransitioner; } - function insert(uint256 _l2BlockNumber, bytes32 _root, uint256 _height) external { + function insert(uint256 _l2BlockNumber, bytes32 _root, uint256 _height) + external + override(INewOutbox) + { if (msg.sender != STATE_TRANSITIONER) { revert Errors.Outbox__Unauthorized(); } @@ -54,7 +53,7 @@ contract Outbox { uint256 _leafIndex, DataStructures.L2ToL1Msg memory _message, bytes32[] memory _path - ) public { + ) public override(INewOutbox) { if (msg.sender != _message.recipient.actor) { revert Errors.Outbox__InvalidRecipient(_message.recipient.actor, msg.sender); } @@ -69,7 +68,8 @@ contract Outbox { bytes32 root = roots[_l2BlockNumber].root; - bytes32 messageHash = _verifyMembership(_path, _message, _leafIndex, root, roots[_l2BlockNumber].height); + bytes32 messageHash = + _verifyMembership(_path, _message, _leafIndex, root, roots[_l2BlockNumber].height); roots[_l2BlockNumber].nullified[_leafIndex] = true; diff --git a/l1-contracts/test/NewOutbox.t.sol b/l1-contracts/test/NewOutbox.t.sol new file mode 100644 index 00000000000..65a0a7841a8 --- /dev/null +++ b/l1-contracts/test/NewOutbox.t.sol @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Aztec Labs. +pragma solidity >=0.8.18; + +import {Test} from "forge-std/Test.sol"; +import {IOutbox} from "../src/core/interfaces/messagebridge/IOutbox.sol"; +import {Outbox} from "../src/core/messagebridge/Outbox.sol"; +import {Registry} from "../src/core/messagebridge/Registry.sol"; +import {Errors} from "../src/core/libraries/Errors.sol"; +import {DataStructures} from "../src/core/libraries/DataStructures.sol"; +import {MessageBox} from "../src/core/libraries/MessageBox.sol"; + +contract OutboxTest is Test { + Outbox internal outbox; + uint256 internal version = 0; + + event MessageAdded(bytes32 indexed entryKey); + event MessageConsumed(bytes32 indexed entryKey, address indexed recipient); + + function setUp() public { + address rollup = address(this); + registry = new Registry(); + outbox = new Outbox(address(registry)); + version = registry.upgrade(rollup, address(0x0), address(outbox)); + } + + function _fakeMessage() internal view returns (DataStructures.L2ToL1Msg memory) { + return DataStructures.L2ToL1Msg({ + sender: DataStructures.L2Actor({ + actor: 0x2000000000000000000000000000000000000000000000000000000000000000, + version: version + }), + recipient: DataStructures.L1Actor({actor: address(this), chainId: block.chainid}), + content: 0x3000000000000000000000000000000000000000000000000000000000000000 + }); + } + + function testRevertIfInsertingFromNonRollup() public { + vm.prank(address(0x1)); + bytes32[] memory entryKeys = new bytes32[](1); + entryKeys[0] = bytes32("random"); + vm.expectRevert( + abi.encodeWithSelector(Errors.Registry__RollupNotRegistered.selector, address(1)) + ); + outbox.sendL1Messages(entryKeys); + } + + // fuzz batch insert -> check inserted. event emitted + function testFuzzBatchInsert(bytes32[] memory _entryKeys) public { + // expected events + for (uint256 i = 0; i < _entryKeys.length; i++) { + if (_entryKeys[i] == bytes32(0)) continue; + vm.expectEmit(true, false, false, false); + emit MessageAdded(_entryKeys[i]); + } + + outbox.sendL1Messages(_entryKeys); + for (uint256 i = 0; i < _entryKeys.length; i++) { + if (_entryKeys[i] == bytes32(0)) continue; + bytes32 key = _entryKeys[i]; + DataStructures.Entry memory entry = outbox.get(key); + assertGt(entry.count, 0); + assertEq(entry.fee, 0); + assertEq(entry.deadline, 0); + } + } + + function testRevertIfConsumingFromWrongRecipient() public { + DataStructures.L2ToL1Msg memory message = _fakeMessage(); + message.recipient.actor = address(0x1); + vm.expectRevert(Errors.Outbox__Unauthorized.selector); + outbox.consume(message); + } + + function testRevertIfConsumingForWrongChain() public { + DataStructures.L2ToL1Msg memory message = _fakeMessage(); + message.recipient.chainId = 2; + vm.expectRevert(Errors.Outbox__InvalidChainId.selector); + outbox.consume(message); + } + + function testRevertIfConsumingMessageThatDoesntExist() public { + DataStructures.L2ToL1Msg memory message = _fakeMessage(); + bytes32 entryKey = outbox.computeEntryKey(message); + vm.expectRevert(abi.encodeWithSelector(Errors.Outbox__NothingToConsume.selector, entryKey)); + outbox.consume(message); + } + + function testRevertIfInsertingFromWrongRollup() public { + address wrongRollup = address(0xbeeffeed); + uint256 wrongVersion = registry.upgrade(wrongRollup, address(0x0), address(outbox)); + + DataStructures.L2ToL1Msg memory message = _fakeMessage(); + // correctly set message.recipient to this address + message.recipient = DataStructures.L1Actor({actor: address(this), chainId: block.chainid}); + + bytes32 expectedEntryKey = outbox.computeEntryKey(message); + bytes32[] memory entryKeys = new bytes32[](1); + entryKeys[0] = expectedEntryKey; + + vm.prank(wrongRollup); + outbox.sendL1Messages(entryKeys); + + vm.prank(message.recipient.actor); + vm.expectRevert( + abi.encodeWithSelector(Errors.Outbox__InvalidVersion.selector, wrongVersion, version) + ); + outbox.consume(message); + } + + function testFuzzConsume(DataStructures.L2ToL1Msg memory _message) public { + // correctly set message.recipient to this address + _message.recipient = DataStructures.L1Actor({actor: address(this), chainId: block.chainid}); + + // correctly set the message.sender.version + _message.sender.version = version; + + bytes32 expectedEntryKey = outbox.computeEntryKey(_message); + bytes32[] memory entryKeys = new bytes32[](1); + entryKeys[0] = expectedEntryKey; + outbox.sendL1Messages(entryKeys); + + vm.prank(_message.recipient.actor); + vm.expectEmit(true, true, false, false); + emit MessageConsumed(expectedEntryKey, _message.recipient.actor); + outbox.consume(_message); + + // ensure no such message to consume: + vm.expectRevert( + abi.encodeWithSelector(Errors.Outbox__NothingToConsume.selector, expectedEntryKey) + ); + outbox.consume(_message); + } +} diff --git a/l1-contracts/test/merkle/Merkle.t.sol b/l1-contracts/test/merkle/Merkle.t.sol index 9dbcc18ca8e..49e90e1a94a 100644 --- a/l1-contracts/test/merkle/Merkle.t.sol +++ b/l1-contracts/test/merkle/Merkle.t.sol @@ -52,7 +52,7 @@ contract MerkleTest is Test { assertTrue(merkle.verifyMembership(path, leaf, idx)); } - function testNaive(uint256 _idx) public { + function testNaive(uint256 _idx) public { uint256 upper = merkle.SIZE(); // function for bounding the test env diff --git a/l1-contracts/test/merkle/Naive.sol b/l1-contracts/test/merkle/Naive.sol index 7675f48ba7d..c9afcddff60 100644 --- a/l1-contracts/test/merkle/Naive.sol +++ b/l1-contracts/test/merkle/Naive.sol @@ -37,7 +37,7 @@ contract NaiveMerkle is Test { return nodes[0]; } - function computeSiblingPath(uint256 _index) public returns (bytes32[] memory, bytes32) { + function computeSiblingPath(uint256 _index) public view returns (bytes32[] memory, bytes32) { bytes32[] memory nodes = new bytes32[](SIZE / 2); bytes32[] memory path = new bytes32[](DEPTH); @@ -69,6 +69,7 @@ contract NaiveMerkle is Test { function verifyMembership(bytes32[] memory _path, bytes32 _leaf, uint256 _index) public + view returns (bool) { bytes32 root; From 76091ea87138973ad142cf85ef85e5b16501bbd4 Mon Sep 17 00:00:00 2001 From: Esau Date: Wed, 6 Mar 2024 23:55:18 +0100 Subject: [PATCH 05/62] Add error cases --- l1-contracts/src/core/libraries/Errors.sol | 1 + .../src/core/messagebridge/NewOutbox.sol | 27 +- l1-contracts/test/NewOutbox.t.sol | 282 +++++++++++++----- l1-contracts/test/merkle/Merkle.t.sol | 24 -- l1-contracts/test/merkle/Naive.sol | 4 +- 5 files changed, 219 insertions(+), 119 deletions(-) diff --git a/l1-contracts/src/core/libraries/Errors.sol b/l1-contracts/src/core/libraries/Errors.sol index 7b80afec8e1..c90f6c06f7b 100644 --- a/l1-contracts/src/core/libraries/Errors.sol +++ b/l1-contracts/src/core/libraries/Errors.sol @@ -51,6 +51,7 @@ library Errors { error Outbox__RootAlreadySet(uint256 l2BlockNumber); error Outbox__InvalidRecipient(address expected, address actual); error Outbox__AlreadyNullified(uint256 l2BlockNumber, uint256 leafIndex); + error Outbox__NothingToConsumeAtBlock(uint256 l2BlockNumber); // Rollup error Rollup__InvalidArchive(bytes32 expected, bytes32 actual); // 0xb682a40e diff --git a/l1-contracts/src/core/messagebridge/NewOutbox.sol b/l1-contracts/src/core/messagebridge/NewOutbox.sol index a5a37bc0aa6..66a14bd51d3 100644 --- a/l1-contracts/src/core/messagebridge/NewOutbox.sol +++ b/l1-contracts/src/core/messagebridge/NewOutbox.sol @@ -62,31 +62,36 @@ contract NewOutbox is INewOutbox { revert Errors.Outbox__InvalidChainId(); } + if (roots[_l2BlockNumber].root == 0) { + revert Errors.Outbox__NothingToConsumeAtBlock(_l2BlockNumber); + } + if (roots[_l2BlockNumber].nullified[_leafIndex]) { revert Errors.Outbox__AlreadyNullified(_l2BlockNumber, _leafIndex); } - bytes32 root = roots[_l2BlockNumber].root; + uint256 expectedHeight = roots[_l2BlockNumber].height; + + if (expectedHeight != _path.length) { + revert Errors.Outbox__InvalidPathLength(expectedHeight, _path.length); + } + + bytes32 expectedRoot = roots[_l2BlockNumber].root; bytes32 messageHash = - _verifyMembership(_path, _message, _leafIndex, root, roots[_l2BlockNumber].height); + _verifyMembership(_path, _message, _leafIndex, expectedRoot); roots[_l2BlockNumber].nullified[_leafIndex] = true; - emit MessageConsumed(_l2BlockNumber, root, messageHash); + emit MessageConsumed(_l2BlockNumber, expectedRoot, messageHash); } function _verifyMembership( bytes32[] memory _path, DataStructures.L2ToL1Msg memory _message, uint256 _index, - bytes32 _root, - uint256 _height + bytes32 _expectedRoot ) internal pure returns (bytes32) { - if (_height != _path.length) { - revert Errors.Outbox__InvalidPathLength(_height, _path.length); - } - bytes32 leaf = _message.sha256ToField(); bytes32 root; @@ -102,8 +107,8 @@ contract NewOutbox is INewOutbox { index /= 2; } - if (root != _root) { - revert Errors.Outbox__InvalidRoot(_root, root); + if (root != _expectedRoot) { + revert Errors.Outbox__InvalidRoot(_expectedRoot, root); } return leaf; diff --git a/l1-contracts/test/NewOutbox.t.sol b/l1-contracts/test/NewOutbox.t.sol index 65a0a7841a8..b3dd9fc3372 100644 --- a/l1-contracts/test/NewOutbox.t.sol +++ b/l1-contracts/test/NewOutbox.t.sol @@ -1,27 +1,32 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright 2023 Aztec Labs. +// Copyright 2024 Aztec Labs. pragma solidity >=0.8.18; import {Test} from "forge-std/Test.sol"; -import {IOutbox} from "../src/core/interfaces/messagebridge/IOutbox.sol"; -import {Outbox} from "../src/core/messagebridge/Outbox.sol"; -import {Registry} from "../src/core/messagebridge/Registry.sol"; +import {NewOutbox} from "../src/core/messagebridge/NewOutbox.sol"; import {Errors} from "../src/core/libraries/Errors.sol"; import {DataStructures} from "../src/core/libraries/DataStructures.sol"; -import {MessageBox} from "../src/core/libraries/MessageBox.sol"; +import {Hash} from "../src/core/libraries/Hash.sol"; +import {NaiveMerkle} from "./merkle/Naive.sol"; -contract OutboxTest is Test { - Outbox internal outbox; + +contract NewOutboxTest is Test { + using Hash for DataStructures.L2ToL1Msg; + + uint256 internal immutable TREE_HEIGHT = 2; + NewOutbox internal outbox; + NaiveMerkle internal zeroedTree; uint256 internal version = 0; - event MessageAdded(bytes32 indexed entryKey); - event MessageConsumed(bytes32 indexed entryKey, address indexed recipient); + + event RootAdded(uint256 indexed l2BlockNumber, bytes32 indexed root, uint256 height); + event MessageConsumed( + uint256 indexed l2BlockNumber, bytes32 indexed root, bytes32 indexed messageHash + ); function setUp() public { - address rollup = address(this); - registry = new Registry(); - outbox = new Outbox(address(registry)); - version = registry.upgrade(rollup, address(0x0), address(outbox)); + outbox = new NewOutbox(address(0x0)); + zeroedTree = new NaiveMerkle(TREE_HEIGHT); } function _fakeMessage() internal view returns (DataStructures.L2ToL1Msg memory) { @@ -36,99 +41,214 @@ contract OutboxTest is Test { } function testRevertIfInsertingFromNonRollup() public { - vm.prank(address(0x1)); - bytes32[] memory entryKeys = new bytes32[](1); - entryKeys[0] = bytes32("random"); + bytes32 root = zeroedTree.computeRoot(); + + vm.prank(address(0x69)); vm.expectRevert( - abi.encodeWithSelector(Errors.Registry__RollupNotRegistered.selector, address(1)) + abi.encodeWithSelector(Errors.Outbox__Unauthorized.selector) ); - outbox.sendL1Messages(entryKeys); + outbox.insert(1, root, TREE_HEIGHT); } - // fuzz batch insert -> check inserted. event emitted - function testFuzzBatchInsert(bytes32[] memory _entryKeys) public { - // expected events - for (uint256 i = 0; i < _entryKeys.length; i++) { - if (_entryKeys[i] == bytes32(0)) continue; - vm.expectEmit(true, false, false, false); - emit MessageAdded(_entryKeys[i]); - } - - outbox.sendL1Messages(_entryKeys); - for (uint256 i = 0; i < _entryKeys.length; i++) { - if (_entryKeys[i] == bytes32(0)) continue; - bytes32 key = _entryKeys[i]; - DataStructures.Entry memory entry = outbox.get(key); - assertGt(entry.count, 0); - assertEq(entry.fee, 0); - assertEq(entry.deadline, 0); - } + function testRevertIfInsertingDuplicate() public { + bytes32 root = zeroedTree.computeRoot(); + + vm.prank(address(0x0)); + outbox.insert(1, root, TREE_HEIGHT); + + vm.prank(address(0x0)); + vm.expectRevert( + abi.encodeWithSelector(Errors.Outbox__RootAlreadySet.selector, 1) + ); + outbox.insert(1, root, TREE_HEIGHT); } - function testRevertIfConsumingFromWrongRecipient() public { - DataStructures.L2ToL1Msg memory message = _fakeMessage(); - message.recipient.actor = address(0x1); - vm.expectRevert(Errors.Outbox__Unauthorized.selector); - outbox.consume(message); + function testRevertIfConsumingMessageBelongingToOther() public { + DataStructures.L2ToL1Msg memory fakeMessage = _fakeMessage(); + + (bytes32[] memory path,) = zeroedTree.computeSiblingPath(0); + + vm.prank(address(0x69)); + vm.expectRevert( + abi.encodeWithSelector(Errors.Outbox__InvalidRecipient.selector, address(this), address(0x69)) + ); + outbox.consume(1, 1, fakeMessage, path); + } + + function testRevertIfNothingInsertedAtBlockNumber() public { + uint256 blockNumber = 1; + DataStructures.L2ToL1Msg memory fakeMessage = _fakeMessage(); + + (bytes32[] memory path,) = zeroedTree.computeSiblingPath(0); + + vm.expectRevert( + abi.encodeWithSelector(Errors.Outbox__NothingToConsumeAtBlock.selector, blockNumber) + ); + outbox.consume(blockNumber, 1, fakeMessage, path); } - function testRevertIfConsumingForWrongChain() public { - DataStructures.L2ToL1Msg memory message = _fakeMessage(); - message.recipient.chainId = 2; - vm.expectRevert(Errors.Outbox__InvalidChainId.selector); - outbox.consume(message); + function testRevertIfConsumingMessageWithInvalidChainId() public { + DataStructures.L2ToL1Msg memory fakeMessage = _fakeMessage(); + + (bytes32[] memory path,) = zeroedTree.computeSiblingPath(0); + + fakeMessage.recipient.chainId = block.chainid + 1; + + vm.expectRevert( + abi.encodeWithSelector(Errors.Outbox__InvalidChainId.selector) + ); + outbox.consume(1, 1, fakeMessage, path); } - function testRevertIfConsumingMessageThatDoesntExist() public { - DataStructures.L2ToL1Msg memory message = _fakeMessage(); - bytes32 entryKey = outbox.computeEntryKey(message); - vm.expectRevert(abi.encodeWithSelector(Errors.Outbox__NothingToConsume.selector, entryKey)); - outbox.consume(message); + function testRevertIfTryingToConsumeSameMessage() public { + DataStructures.L2ToL1Msg memory fakeMessage = _fakeMessage(); + bytes32 leaf = fakeMessage.sha256ToField(); + + NaiveMerkle tree = new NaiveMerkle(TREE_HEIGHT); + tree.insertLeaf(leaf); + bytes32 root = tree.computeRoot(); + + vm.prank(address(0x0)); + outbox.insert(1, root, TREE_HEIGHT); + + (bytes32[] memory path,) = tree.computeSiblingPath(0); + outbox.consume(1, 0, fakeMessage, path); + vm.expectRevert( + abi.encodeWithSelector(Errors.Outbox__AlreadyNullified.selector, 1, 0) + ); + outbox.consume(1, 0, fakeMessage, path); } - function testRevertIfInsertingFromWrongRollup() public { - address wrongRollup = address(0xbeeffeed); - uint256 wrongVersion = registry.upgrade(wrongRollup, address(0x0), address(outbox)); + function testRevertIfPathHeightMismatch() public { + DataStructures.L2ToL1Msg memory fakeMessage = _fakeMessage(); + bytes32 leaf = fakeMessage.sha256ToField(); - DataStructures.L2ToL1Msg memory message = _fakeMessage(); - // correctly set message.recipient to this address - message.recipient = DataStructures.L1Actor({actor: address(this), chainId: block.chainid}); + NaiveMerkle tree = new NaiveMerkle(TREE_HEIGHT); + tree.insertLeaf(leaf); + bytes32 root = tree.computeRoot(); - bytes32 expectedEntryKey = outbox.computeEntryKey(message); - bytes32[] memory entryKeys = new bytes32[](1); - entryKeys[0] = expectedEntryKey; + vm.prank(address(0x0)); + outbox.insert(1, root, TREE_HEIGHT); - vm.prank(wrongRollup); - outbox.sendL1Messages(entryKeys); + NaiveMerkle biggerTree = new NaiveMerkle(TREE_HEIGHT + 1); + tree.insertLeaf(leaf); - vm.prank(message.recipient.actor); + (bytes32[] memory path,) = biggerTree.computeSiblingPath(0); vm.expectRevert( - abi.encodeWithSelector(Errors.Outbox__InvalidVersion.selector, wrongVersion, version) + abi.encodeWithSelector(Errors.Outbox__InvalidPathLength.selector, TREE_HEIGHT, TREE_HEIGHT + 1) ); - outbox.consume(message); + outbox.consume(1, 0, fakeMessage, path); } - function testFuzzConsume(DataStructures.L2ToL1Msg memory _message) public { - // correctly set message.recipient to this address - _message.recipient = DataStructures.L1Actor({actor: address(this), chainId: block.chainid}); + function testRevertIfTryingToConsumeMessageNotInTree() public { + DataStructures.L2ToL1Msg memory fakeMessage = _fakeMessage(); + bytes32 leaf = fakeMessage.sha256ToField(); + fakeMessage.content = bytes32 (uint256(42069)); + bytes32 modifiedLeaf = fakeMessage.sha256ToField(); + + NaiveMerkle tree = new NaiveMerkle(TREE_HEIGHT); + tree.insertLeaf(leaf); + bytes32 root = tree.computeRoot(); - // correctly set the message.sender.version - _message.sender.version = version; + NaiveMerkle modifiedTree = new NaiveMerkle(TREE_HEIGHT); + modifiedTree.insertLeaf(modifiedLeaf); + bytes32 modifiedRoot = modifiedTree.computeRoot(); - bytes32 expectedEntryKey = outbox.computeEntryKey(_message); - bytes32[] memory entryKeys = new bytes32[](1); - entryKeys[0] = expectedEntryKey; - outbox.sendL1Messages(entryKeys); + vm.prank(address(0x0)); + outbox.insert(1, root, TREE_HEIGHT); - vm.prank(_message.recipient.actor); - vm.expectEmit(true, true, false, false); - emit MessageConsumed(expectedEntryKey, _message.recipient.actor); - outbox.consume(_message); + (bytes32[] memory path,) = modifiedTree.computeSiblingPath(0); - // ensure no such message to consume: vm.expectRevert( - abi.encodeWithSelector(Errors.Outbox__NothingToConsume.selector, expectedEntryKey) + abi.encodeWithSelector(Errors.Outbox__InvalidRoot.selector, root, modifiedRoot) ); - outbox.consume(_message); + outbox.consume(1, 0, fakeMessage, path); } + +// // fuzz batch insert -> check inserted. event emitted +// function testFuzzBatchInsert(bytes32[] memory _entryKeys) public { +// // expected events +// for (uint256 i = 0; i < _entryKeys.length; i++) { +// if (_entryKeys[i] == bytes32(0)) continue; +// vm.expectEmit(true, false, false, false); +// emit MessageAdded(_entryKeys[i]); +// } + +// outbox.sendL1Messages(_entryKeys); +// for (uint256 i = 0; i < _entryKeys.length; i++) { +// if (_entryKeys[i] == bytes32(0)) continue; +// bytes32 key = _entryKeys[i]; +// DataStructures.Entry memory entry = outbox.get(key); +// assertGt(entry.count, 0); +// assertEq(entry.fee, 0); +// assertEq(entry.deadline, 0); +// } +// } + +// function testRevertIfConsumingFromWrongRecipient() public { +// DataStructures.L2ToL1Msg memory message = _fakeMessage(); +// message.recipient.actor = address(0x1); +// vm.expectRevert(Errors.Outbox__Unauthorized.selector); +// outbox.consume(message); +// } + +// function testRevertIfConsumingForWrongChain() public { +// DataStructures.L2ToL1Msg memory message = _fakeMessage(); +// message.recipient.chainId = 2; +// vm.expectRevert(Errors.Outbox__InvalidChainId.selector); +// outbox.consume(message); +// } + +// function testRevertIfConsumingMessageThatDoesntExist() public { +// DataStructures.L2ToL1Msg memory message = _fakeMessage(); +// bytes32 entryKey = outbox.computeEntryKey(message); +// vm.expectRevert(abi.encodeWithSelector(Errors.Outbox__NothingToConsume.selector, entryKey)); +// outbox.consume(message); +// } + +// function testRevertIfInsertingFromWrongRollup() public { +// address wrongRollup = address(0xbeeffeed); +// uint256 wrongVersion = registry.upgrade(wrongRollup, address(0x0), address(outbox)); + +// DataStructures.L2ToL1Msg memory message = _fakeMessage(); +// // correctly set message.recipient to this address +// message.recipient = DataStructures.L1Actor({actor: address(this), chainId: block.chainid}); + +// bytes32 expectedEntryKey = outbox.computeEntryKey(message); +// bytes32[] memory entryKeys = new bytes32[](1); +// entryKeys[0] = expectedEntryKey; + +// vm.prank(wrongRollup); +// outbox.sendL1Messages(entryKeys); + +// vm.prank(message.recipient.actor); +// vm.expectRevert( +// abi.encodeWithSelector(Errors.Outbox__InvalidVersion.selector, wrongVersion, version) +// ); +// outbox.consume(message); +// } + +// function testFuzzConsume(DataStructures.L2ToL1Msg memory _message) public { +// // correctly set message.recipient to this address +// _message.recipient = DataStructures.L1Actor({actor: address(this), chainId: block.chainid}); + +// // correctly set the message.sender.version +// _message.sender.version = version; + +// bytes32 expectedEntryKey = outbox.computeEntryKey(_message); +// bytes32[] memory entryKeys = new bytes32[](1); +// entryKeys[0] = expectedEntryKey; +// outbox.sendL1Messages(entryKeys); + +// vm.prank(_message.recipient.actor); +// vm.expectEmit(true, true, false, false); +// emit MessageConsumed(expectedEntryKey, _message.recipient.actor); +// outbox.consume(_message); + +// // ensure no such message to consume: +// vm.expectRevert( +// abi.encodeWithSelector(Errors.Outbox__NothingToConsume.selector, expectedEntryKey) +// ); +// outbox.consume(_message); +// } } diff --git a/l1-contracts/test/merkle/Merkle.t.sol b/l1-contracts/test/merkle/Merkle.t.sol index 49e90e1a94a..09d55c2bb99 100644 --- a/l1-contracts/test/merkle/Merkle.t.sol +++ b/l1-contracts/test/merkle/Merkle.t.sol @@ -51,28 +51,4 @@ contract MerkleTest is Test { assertTrue(merkle.verifyMembership(path, leaf, idx)); } - - function testNaive(uint256 _idx) public { - uint256 upper = merkle.SIZE(); - - // function for bounding the test env - uint256 idx = bound(_idx, 0, upper - 1); - - for (uint256 i = 0; i < upper; i++) { - bytes32 leaf = sha256(abi.encode(i + 1)); - merkle.insertLeaf(leaf); - } - - (bytes32[] memory path, bytes32 leaf) = merkle.computeSiblingPath(idx); - - for (uint256 i = 0; i < merkle.DEPTH(); i++) { - emit log_named_bytes32("path", path[i]); - } - - emit log_named_bytes32("test", leaf); - emit log_named_bytes32("size", bytes32(merkle.SIZE())); - emit log_named_bytes32("root", merkle.computeRoot()); - - assertTrue(merkle.verifyMembership(path, leaf, idx)); - } } diff --git a/l1-contracts/test/merkle/Naive.sol b/l1-contracts/test/merkle/Naive.sol index c9afcddff60..7ed7f62a1bc 100644 --- a/l1-contracts/test/merkle/Naive.sol +++ b/l1-contracts/test/merkle/Naive.sol @@ -2,9 +2,7 @@ // Copyright 2023 Aztec Labs. pragma solidity >=0.8.18; -import {Test} from "forge-std/Test.sol"; - -contract NaiveMerkle is Test { +contract NaiveMerkle { uint256 public immutable DEPTH; uint256 public immutable SIZE; From d411a652cb813c3a6cabed97ef14ecce7b1a19d6 Mon Sep 17 00:00:00 2001 From: Esau Date: Thu, 7 Mar 2024 16:04:44 +0100 Subject: [PATCH 06/62] fix --- .../src/core/messagebridge/NewOutbox.sol | 6 +++--- l1-contracts/test/NewOutbox.t.sol | 21 ++++++++++--------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/l1-contracts/src/core/messagebridge/NewOutbox.sol b/l1-contracts/src/core/messagebridge/NewOutbox.sol index 66a14bd51d3..85b5d2648c2 100644 --- a/l1-contracts/src/core/messagebridge/NewOutbox.sol +++ b/l1-contracts/src/core/messagebridge/NewOutbox.sol @@ -62,7 +62,9 @@ contract NewOutbox is INewOutbox { revert Errors.Outbox__InvalidChainId(); } - if (roots[_l2BlockNumber].root == 0) { + bytes32 expectedRoot = roots[_l2BlockNumber].root; + + if (expectedRoot == 0) { revert Errors.Outbox__NothingToConsumeAtBlock(_l2BlockNumber); } @@ -76,8 +78,6 @@ contract NewOutbox is INewOutbox { revert Errors.Outbox__InvalidPathLength(expectedHeight, _path.length); } - bytes32 expectedRoot = roots[_l2BlockNumber].root; - bytes32 messageHash = _verifyMembership(_path, _message, _leafIndex, expectedRoot); diff --git a/l1-contracts/test/NewOutbox.t.sol b/l1-contracts/test/NewOutbox.t.sol index b3dd9fc3372..bec46a7cbbd 100644 --- a/l1-contracts/test/NewOutbox.t.sol +++ b/l1-contracts/test/NewOutbox.t.sol @@ -13,11 +13,12 @@ import {NaiveMerkle} from "./merkle/Naive.sol"; contract NewOutboxTest is Test { using Hash for DataStructures.L2ToL1Msg; - uint256 internal immutable TREE_HEIGHT = 2; + address internal constant STATE_TRANSITIONER = address(0x42069123); + uint256 internal constant TREE_HEIGHT = 2; + uint256 internal constant version = 0; + NewOutbox internal outbox; NaiveMerkle internal zeroedTree; - uint256 internal version = 0; - event RootAdded(uint256 indexed l2BlockNumber, bytes32 indexed root, uint256 height); event MessageConsumed( @@ -25,7 +26,7 @@ contract NewOutboxTest is Test { ); function setUp() public { - outbox = new NewOutbox(address(0x0)); + outbox = new NewOutbox(STATE_TRANSITIONER); zeroedTree = new NaiveMerkle(TREE_HEIGHT); } @@ -53,10 +54,10 @@ contract NewOutboxTest is Test { function testRevertIfInsertingDuplicate() public { bytes32 root = zeroedTree.computeRoot(); - vm.prank(address(0x0)); + vm.prank(STATE_TRANSITIONER); outbox.insert(1, root, TREE_HEIGHT); - vm.prank(address(0x0)); + vm.prank(STATE_TRANSITIONER); vm.expectRevert( abi.encodeWithSelector(Errors.Outbox__RootAlreadySet.selector, 1) ); @@ -108,7 +109,7 @@ contract NewOutboxTest is Test { tree.insertLeaf(leaf); bytes32 root = tree.computeRoot(); - vm.prank(address(0x0)); + vm.prank(STATE_TRANSITIONER); outbox.insert(1, root, TREE_HEIGHT); (bytes32[] memory path,) = tree.computeSiblingPath(0); @@ -127,7 +128,7 @@ contract NewOutboxTest is Test { tree.insertLeaf(leaf); bytes32 root = tree.computeRoot(); - vm.prank(address(0x0)); + vm.prank(STATE_TRANSITIONER); outbox.insert(1, root, TREE_HEIGHT); NaiveMerkle biggerTree = new NaiveMerkle(TREE_HEIGHT + 1); @@ -154,7 +155,7 @@ contract NewOutboxTest is Test { modifiedTree.insertLeaf(modifiedLeaf); bytes32 modifiedRoot = modifiedTree.computeRoot(); - vm.prank(address(0x0)); + vm.prank(STATE_TRANSITIONER); outbox.insert(1, root, TREE_HEIGHT); (bytes32[] memory path,) = modifiedTree.computeSiblingPath(0); @@ -208,7 +209,7 @@ contract NewOutboxTest is Test { // function testRevertIfInsertingFromWrongRollup() public { // address wrongRollup = address(0xbeeffeed); -// uint256 wrongVersion = registry.upgrade(wrongRollup, address(0x0), address(outbox)); +// uint256 wrongVersion = registry.upgrade(wrongRollup, STATE_TRANSITIONER, address(outbox)); // DataStructures.L2ToL1Msg memory message = _fakeMessage(); // // correctly set message.recipient to this address From 813a59282a5f089d855e4b0495cda5cf2baa47c5 Mon Sep 17 00:00:00 2001 From: Esau Date: Fri, 8 Mar 2024 00:21:17 +0100 Subject: [PATCH 07/62] fomrat fix comments --- l1-contracts/slither_output.md | 244 +++------------- .../interfaces/messagebridge/INewOutbox.sol | 10 +- .../src/core/messagebridge/NewOutbox.sol | 34 ++- l1-contracts/test/NewOutbox.t.sol | 260 ++++++++---------- 4 files changed, 195 insertions(+), 353 deletions(-) diff --git a/l1-contracts/slither_output.md b/l1-contracts/slither_output.md index dff0feae6a1..9abb467f0a7 100644 --- a/l1-contracts/slither_output.md +++ b/l1-contracts/slither_output.md @@ -1,43 +1,36 @@ Summary - - [pess-unprotected-setter](#pess-unprotected-setter) (1 results) (High) - - [uninitialized-local](#uninitialized-local) (2 results) (Medium) + - [uninitialized-local](#uninitialized-local) (3 results) (Medium) - [unused-return](#unused-return) (1 results) (Medium) - - [pess-dubious-typecast](#pess-dubious-typecast) (8 results) (Medium) - - [missing-zero-check](#missing-zero-check) (1 results) (Low) + - [missing-zero-check](#missing-zero-check) (2 results) (Low) - [reentrancy-events](#reentrancy-events) (2 results) (Low) - [timestamp](#timestamp) (4 results) (Low) - - [pess-public-vs-external](#pess-public-vs-external) (6 results) (Low) - [assembly](#assembly) (2 results) (Informational) - [dead-code](#dead-code) (5 results) (Informational) - [solc-version](#solc-version) (1 results) (Informational) - [low-level-calls](#low-level-calls) (1 results) (Informational) - - [similar-names](#similar-names) (3 results) (Informational) + - [similar-names](#similar-names) (4 results) (Informational) - [constable-states](#constable-states) (1 results) (Optimization) - - [pess-multiple-storage-read](#pess-multiple-storage-read) (5 results) (Optimization) -## pess-unprotected-setter -Impact: High -Confidence: Medium - - [ ] ID-0 -Function [Rollup.process(bytes,bytes32,bytes,bytes)](src/core/Rollup.sol#L53-L91) is a non-protected setter archive is written - -src/core/Rollup.sol#L53-L91 - - ## uninitialized-local Impact: Medium Confidence: Medium - - [ ] ID-1 + - [ ] ID-0 [HeaderLib.decode(bytes).header](src/core/libraries/HeaderLib.sol#L150) is a local variable never initialized src/core/libraries/HeaderLib.sol#L150 - - [ ] ID-2 + - [ ] ID-1 [TxsDecoder.decode(bytes).vars](src/core/libraries/decoders/TxsDecoder.sol#L86) is a local variable never initialized src/core/libraries/decoders/TxsDecoder.sol#L86 + - [ ] ID-2 +[NewOutbox._verifyMembership(bytes32[],DataStructures.L2ToL1Msg,uint256,bytes32).root](src/core/messagebridge/NewOutbox.sol#L114) is a local variable never initialized + +src/core/messagebridge/NewOutbox.sol#L114 + + ## unused-return Impact: Medium Confidence: Medium @@ -47,101 +40,27 @@ Confidence: Medium src/core/Rollup.sol#L53-L91 -## pess-dubious-typecast -Impact: Medium -Confidence: High - - [ ] ID-4 -Dubious typecast in [TxsDecoder.read4(bytes,uint256)](src/core/libraries/decoders/TxsDecoder.sol#L359-L361): - bytes => bytes4 casting occurs in [uint256(uint32(bytes4(slice(_data,_offset,4))))](src/core/libraries/decoders/TxsDecoder.sol#L360) - -src/core/libraries/decoders/TxsDecoder.sol#L359-L361 - - - - [ ] ID-5 -Dubious typecast in [MessagesDecoder.read1(bytes,uint256)](src/core/libraries/decoders/MessagesDecoder.sol#L158-L160): - bytes => bytes1 casting occurs in [uint256(uint8(bytes1(_data)))](src/core/libraries/decoders/MessagesDecoder.sol#L159) - -src/core/libraries/decoders/MessagesDecoder.sol#L158-L160 - - - - [ ] ID-6 -Dubious typecast in [Outbox.sendL1Messages(bytes32[])](src/core/messagebridge/Outbox.sol#L38-L46): - uint256 => uint32 casting occurs in [version = uint32(REGISTRY.getVersionFor(msg.sender))](src/core/messagebridge/Outbox.sol#L40) - -src/core/messagebridge/Outbox.sol#L38-L46 - - - - [ ] ID-7 -Dubious typecast in [Inbox.sendL2Message(DataStructures.L2Actor,uint32,bytes32,bytes32)](src/core/messagebridge/Inbox.sol#L45-L91): - uint256 => uint64 casting occurs in [fee = uint64(msg.value)](src/core/messagebridge/Inbox.sol#L64) - uint256 => uint32 casting occurs in [entries.insert(key,fee,uint32(_recipient.version),_deadline,_errIncompatibleEntryArguments)](src/core/messagebridge/Inbox.sol#L76) - -src/core/messagebridge/Inbox.sol#L45-L91 - - - - [ ] ID-8 -Dubious typecast in [TxsDecoder.read1(bytes,uint256)](src/core/libraries/decoders/TxsDecoder.sol#L349-L351): - bytes => bytes1 casting occurs in [uint256(uint8(bytes1(slice(_data,_offset,1))))](src/core/libraries/decoders/TxsDecoder.sol#L350) - -src/core/libraries/decoders/TxsDecoder.sol#L349-L351 - - - - [ ] ID-9 -Dubious typecast in [MessagesDecoder.read4(bytes,uint256)](src/core/libraries/decoders/MessagesDecoder.sol#L168-L170): - bytes => bytes4 casting occurs in [uint256(uint32(bytes4(_data)))](src/core/libraries/decoders/MessagesDecoder.sol#L169) - -src/core/libraries/decoders/MessagesDecoder.sol#L168-L170 - - - - [ ] ID-10 -Dubious typecast in [HeaderLib.decode(bytes)](src/core/libraries/HeaderLib.sol#L145-L189): - bytes => bytes32 casting occurs in [header.lastArchive = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L153-L155) - bytes => bytes4 casting occurs in [header.lastArchive = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L153-L155) - bytes => bytes32 casting occurs in [header.contentCommitment.txTreeHeight = uint256(bytes32(_header))](src/core/libraries/HeaderLib.sol#L158) - bytes => bytes32 casting occurs in [header.contentCommitment.txsHash = bytes32(_header)](src/core/libraries/HeaderLib.sol#L159) - bytes => bytes32 casting occurs in [header.contentCommitment.inHash = bytes32(_header)](src/core/libraries/HeaderLib.sol#L160) - bytes => bytes32 casting occurs in [header.contentCommitment.outHash = bytes32(_header)](src/core/libraries/HeaderLib.sol#L161) - bytes => bytes32 casting occurs in [header.stateReference.l1ToL2MessageTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L164-L166) - bytes => bytes4 casting occurs in [header.stateReference.l1ToL2MessageTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L164-L166) - bytes => bytes32 casting occurs in [header.stateReference.partialStateReference.noteHashTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L167-L169) - bytes => bytes4 casting occurs in [header.stateReference.partialStateReference.noteHashTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L167-L169) - bytes => bytes32 casting occurs in [header.stateReference.partialStateReference.nullifierTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L170-L172) - bytes => bytes4 casting occurs in [header.stateReference.partialStateReference.nullifierTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L170-L172) - bytes => bytes32 casting occurs in [header.stateReference.partialStateReference.contractTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L173-L175) - bytes => bytes4 casting occurs in [header.stateReference.partialStateReference.contractTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L173-L175) - bytes => bytes32 casting occurs in [header.stateReference.partialStateReference.publicDataTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L176-L178) - bytes => bytes4 casting occurs in [header.stateReference.partialStateReference.publicDataTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L176-L178) - bytes => bytes32 casting occurs in [header.globalVariables.chainId = uint256(bytes32(_header))](src/core/libraries/HeaderLib.sol#L181) - bytes => bytes32 casting occurs in [header.globalVariables.version = uint256(bytes32(_header))](src/core/libraries/HeaderLib.sol#L182) - bytes => bytes32 casting occurs in [header.globalVariables.blockNumber = uint256(bytes32(_header))](src/core/libraries/HeaderLib.sol#L183) - bytes => bytes32 casting occurs in [header.globalVariables.timestamp = uint256(bytes32(_header))](src/core/libraries/HeaderLib.sol#L184) - bytes => bytes20 casting occurs in [header.globalVariables.coinbase = address(bytes20(_header))](src/core/libraries/HeaderLib.sol#L185) - bytes => bytes32 casting occurs in [header.globalVariables.feeRecipient = bytes32(_header)](src/core/libraries/HeaderLib.sol#L186) - -src/core/libraries/HeaderLib.sol#L145-L189 - - - - [ ] ID-11 -Dubious typecast in [Inbox.batchConsume(bytes32[],address)](src/core/messagebridge/Inbox.sol#L122-L143): - uint256 => uint32 casting occurs in [expectedVersion = uint32(REGISTRY.getVersionFor(msg.sender))](src/core/messagebridge/Inbox.sol#L128) - -src/core/messagebridge/Inbox.sol#L122-L143 - - ## missing-zero-check Impact: Low Confidence: Medium - - [ ] ID-12 + - [ ] ID-4 [NewInbox.constructor(address,uint256)._rollup](src/core/messagebridge/NewInbox.sol#L41) lacks a zero-check on : - [ROLLUP = _rollup](src/core/messagebridge/NewInbox.sol#L42) src/core/messagebridge/NewInbox.sol#L41 + - [ ] ID-5 +[NewOutbox.constructor(address)._stateTransitioner](src/core/messagebridge/NewOutbox.sol#L29) lacks a zero-check on : + - [STATE_TRANSITIONER = _stateTransitioner](src/core/messagebridge/NewOutbox.sol#L30) + +src/core/messagebridge/NewOutbox.sol#L29 + + ## reentrancy-events Impact: Low Confidence: Medium - - [ ] ID-13 + - [ ] ID-6 Reentrancy in [Rollup.process(bytes,bytes32,bytes,bytes)](src/core/Rollup.sol#L53-L91): External calls: - [inbox.batchConsume(l1ToL2Msgs,msg.sender)](src/core/Rollup.sol#L85) @@ -152,7 +71,7 @@ Reentrancy in [Rollup.process(bytes,bytes32,bytes,bytes)](src/core/Rollup.sol#L5 src/core/Rollup.sol#L53-L91 - - [ ] ID-14 + - [ ] ID-7 Reentrancy in [NewInbox.sendL2Message(DataStructures.L2Actor,bytes32,bytes32)](src/core/messagebridge/NewInbox.sol#L62-L99): External calls: - [index = currentTree.insertLeaf(leaf)](src/core/messagebridge/NewInbox.sol#L95) @@ -165,7 +84,7 @@ src/core/messagebridge/NewInbox.sol#L62-L99 ## timestamp Impact: Low Confidence: Medium - - [ ] ID-15 + - [ ] ID-8 [Inbox.batchConsume(bytes32[],address)](src/core/messagebridge/Inbox.sol#L122-L143) uses timestamp for comparisons Dangerous comparisons: - [block.timestamp > entry.deadline](src/core/messagebridge/Inbox.sol#L136) @@ -173,7 +92,7 @@ Confidence: Medium src/core/messagebridge/Inbox.sol#L122-L143 - - [ ] ID-16 + - [ ] ID-9 [HeaderLib.validate(HeaderLib.Header,uint256,uint256,bytes32)](src/core/libraries/HeaderLib.sol#L108-L138) uses timestamp for comparisons Dangerous comparisons: - [_header.globalVariables.timestamp > block.timestamp](src/core/libraries/HeaderLib.sol#L122) @@ -181,7 +100,7 @@ src/core/messagebridge/Inbox.sol#L122-L143 src/core/libraries/HeaderLib.sol#L108-L138 - - [ ] ID-17 + - [ ] ID-10 [Inbox.sendL2Message(DataStructures.L2Actor,uint32,bytes32,bytes32)](src/core/messagebridge/Inbox.sol#L45-L91) uses timestamp for comparisons Dangerous comparisons: - [_deadline <= block.timestamp](src/core/messagebridge/Inbox.sol#L54) @@ -189,7 +108,7 @@ src/core/libraries/HeaderLib.sol#L108-L138 src/core/messagebridge/Inbox.sol#L45-L91 - - [ ] ID-18 + - [ ] ID-11 [Inbox.cancelL2Message(DataStructures.L1ToL2Msg,address)](src/core/messagebridge/Inbox.sol#L102-L113) uses timestamp for comparisons Dangerous comparisons: - [block.timestamp <= _message.deadline](src/core/messagebridge/Inbox.sol#L108) @@ -197,58 +116,10 @@ src/core/messagebridge/Inbox.sol#L45-L91 src/core/messagebridge/Inbox.sol#L102-L113 -## pess-public-vs-external -Impact: Low -Confidence: Medium - - [ ] ID-19 -The following public functions could be turned into external in [FrontierMerkle](src/core/messagebridge/frontier_tree/Frontier.sol#L7-L93) contract: - [FrontierMerkle.constructor(uint256)](src/core/messagebridge/frontier_tree/Frontier.sol#L19-L27) - -src/core/messagebridge/frontier_tree/Frontier.sol#L7-L93 - - - - [ ] ID-20 -The following public functions could be turned into external in [Registry](src/core/messagebridge/Registry.sol#L22-L129) contract: - [Registry.constructor()](src/core/messagebridge/Registry.sol#L29-L33) - -src/core/messagebridge/Registry.sol#L22-L129 - - - - [ ] ID-21 -The following public functions could be turned into external in [Rollup](src/core/Rollup.sol#L27-L100) contract: - [Rollup.constructor(IRegistry,IAvailabilityOracle)](src/core/Rollup.sol#L39-L44) - -src/core/Rollup.sol#L27-L100 - - - - [ ] ID-22 -The following public functions could be turned into external in [Outbox](src/core/messagebridge/Outbox.sol#L21-L148) contract: - [Outbox.constructor(address)](src/core/messagebridge/Outbox.sol#L29-L31) - [Outbox.get(bytes32)](src/core/messagebridge/Outbox.sol#L77-L84) - [Outbox.contains(bytes32)](src/core/messagebridge/Outbox.sol#L91-L93) - -src/core/messagebridge/Outbox.sol#L21-L148 - - - - [ ] ID-23 -The following public functions could be turned into external in [Inbox](src/core/messagebridge/Inbox.sol#L21-L231) contract: - [Inbox.constructor(address)](src/core/messagebridge/Inbox.sol#L30-L32) - [Inbox.contains(bytes32)](src/core/messagebridge/Inbox.sol#L174-L176) - -src/core/messagebridge/Inbox.sol#L21-L231 - - - - [ ] ID-24 -The following public functions could be turned into external in [NewInbox](src/core/messagebridge/NewInbox.sol#L25-L128) contract: - [NewInbox.constructor(address,uint256)](src/core/messagebridge/NewInbox.sol#L41-L52) - -src/core/messagebridge/NewInbox.sol#L25-L128 - - ## assembly Impact: Informational Confidence: High - - [ ] ID-25 + - [ ] ID-12 [MessagesDecoder.decode(bytes)](src/core/libraries/decoders/MessagesDecoder.sol#L60-L150) uses assembly - [INLINE ASM](src/core/libraries/decoders/MessagesDecoder.sol#L79-L81) - [INLINE ASM](src/core/libraries/decoders/MessagesDecoder.sol#L112-L118) @@ -256,7 +127,7 @@ Confidence: High src/core/libraries/decoders/MessagesDecoder.sol#L60-L150 - - [ ] ID-26 + - [ ] ID-13 [TxsDecoder.computeRoot(bytes32[])](src/core/libraries/decoders/TxsDecoder.sol#L291-L310) uses assembly - [INLINE ASM](src/core/libraries/decoders/TxsDecoder.sol#L298-L300) @@ -266,31 +137,31 @@ src/core/libraries/decoders/TxsDecoder.sol#L291-L310 ## dead-code Impact: Informational Confidence: Medium - - [ ] ID-27 + - [ ] ID-14 [Inbox._errIncompatibleEntryArguments(bytes32,uint64,uint64,uint32,uint32,uint32,uint32)](src/core/messagebridge/Inbox.sol#L212-L230) is never used and should be removed src/core/messagebridge/Inbox.sol#L212-L230 - - [ ] ID-28 + - [ ] ID-15 [Outbox._errNothingToConsume(bytes32)](src/core/messagebridge/Outbox.sol#L114-L116) is never used and should be removed src/core/messagebridge/Outbox.sol#L114-L116 - - [ ] ID-29 + - [ ] ID-16 [Hash.sha256ToField(bytes32)](src/core/libraries/Hash.sol#L59-L61) is never used and should be removed src/core/libraries/Hash.sol#L59-L61 - - [ ] ID-30 + - [ ] ID-17 [Inbox._errNothingToConsume(bytes32)](src/core/messagebridge/Inbox.sol#L197-L199) is never used and should be removed src/core/messagebridge/Inbox.sol#L197-L199 - - [ ] ID-31 + - [ ] ID-18 [Outbox._errIncompatibleEntryArguments(bytes32,uint64,uint64,uint32,uint32,uint32,uint32)](src/core/messagebridge/Outbox.sol#L129-L147) is never used and should be removed src/core/messagebridge/Outbox.sol#L129-L147 @@ -299,13 +170,13 @@ src/core/messagebridge/Outbox.sol#L129-L147 ## solc-version Impact: Informational Confidence: High - - [ ] ID-32 + - [ ] ID-19 solc-0.8.21 is not recommended for deployment ## low-level-calls Impact: Informational Confidence: High - - [ ] ID-33 + - [ ] ID-20 Low level call in [Inbox.withdrawFees()](src/core/messagebridge/Inbox.sol#L148-L153): - [(success) = msg.sender.call{value: balance}()](src/core/messagebridge/Inbox.sol#L151) @@ -315,19 +186,25 @@ src/core/messagebridge/Inbox.sol#L148-L153 ## similar-names Impact: Informational Confidence: Medium - - [ ] ID-34 + - [ ] ID-21 Variable [Constants.LOGS_HASHES_NUM_BYTES_PER_BASE_ROLLUP](src/core/libraries/ConstantsGen.sol#L129) is too similar to [Constants.NOTE_HASHES_NUM_BYTES_PER_BASE_ROLLUP](src/core/libraries/ConstantsGen.sol#L122) src/core/libraries/ConstantsGen.sol#L129 - - [ ] ID-35 + - [ ] ID-22 Variable [Constants.L1_TO_L2_MESSAGE_LENGTH](src/core/libraries/ConstantsGen.sol#L109) is too similar to [Constants.L2_TO_L1_MESSAGE_LENGTH](src/core/libraries/ConstantsGen.sol#L110) src/core/libraries/ConstantsGen.sol#L109 - - [ ] ID-36 + - [ ] ID-23 +Variable [NewOutbox.STATE_TRANSITIONER](src/core/messagebridge/NewOutbox.sol#L26) is too similar to [NewOutbox.constructor(address)._stateTransitioner](src/core/messagebridge/NewOutbox.sol#L29) + +src/core/messagebridge/NewOutbox.sol#L26 + + + - [ ] ID-24 Variable [Rollup.AVAILABILITY_ORACLE](src/core/Rollup.sol#L30) is too similar to [Rollup.constructor(IRegistry,IAvailabilityOracle)._availabilityOracle](src/core/Rollup.sol#L39) src/core/Rollup.sol#L30 @@ -336,42 +213,9 @@ src/core/Rollup.sol#L30 ## constable-states Impact: Optimization Confidence: High - - [ ] ID-37 + - [ ] ID-25 [Rollup.lastWarpedBlockTs](src/core/Rollup.sol#L37) should be constant src/core/Rollup.sol#L37 -## pess-multiple-storage-read -Impact: Optimization -Confidence: High - - [ ] ID-38 -In a function [NewInbox.sendL2Message(DataStructures.L2Actor,bytes32,bytes32)](src/core/messagebridge/NewInbox.sol#L62-L99) variable [NewInbox.inProgress](src/core/messagebridge/NewInbox.sol#L37) is read multiple times - -src/core/messagebridge/NewInbox.sol#L62-L99 - - - - [ ] ID-39 -In a function [FrontierMerkle.root()](src/core/messagebridge/frontier_tree/Frontier.sol#L43-L76) variable [FrontierMerkle.HEIGHT](src/core/messagebridge/frontier_tree/Frontier.sol#L8) is read multiple times - -src/core/messagebridge/frontier_tree/Frontier.sol#L43-L76 - - - - [ ] ID-40 -In a function [NewInbox.consume()](src/core/messagebridge/NewInbox.sol#L108-L127) variable [NewInbox.inProgress](src/core/messagebridge/NewInbox.sol#L37) is read multiple times - -src/core/messagebridge/NewInbox.sol#L108-L127 - - - - [ ] ID-41 -In a function [NewInbox.consume()](src/core/messagebridge/NewInbox.sol#L108-L127) variable [NewInbox.toConsume](src/core/messagebridge/NewInbox.sol#L35) is read multiple times - -src/core/messagebridge/NewInbox.sol#L108-L127 - - - - [ ] ID-42 -In a function [FrontierMerkle.root()](src/core/messagebridge/frontier_tree/Frontier.sol#L43-L76) variable [FrontierMerkle.frontier](src/core/messagebridge/frontier_tree/Frontier.sol#L13) is read multiple times - -src/core/messagebridge/frontier_tree/Frontier.sol#L43-L76 - - diff --git a/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol b/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol index 2197ef78aed..653e3db5980 100644 --- a/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol +++ b/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol @@ -20,9 +20,9 @@ interface INewOutbox { /* * @notice Inserts the root of a merkle tree containing all of the L2 to L1 messages in * a block specified by _blockNumber. - * @dev Only callable by the rollup contract (state transitioner) - * @dev Emits the `RootAdded` upon inserting the root successfully - * @param _l2BlockNumber - The L2 Block Number in which the L2 to L1 message reside + * @dev Only callable by the state transitioner (rollup contract) + * @dev Emits `RootAdded` upon inserting the root successfully + * @param _l2BlockNumber - The L2 Block Number in which the L2 to L1 messages reside * @param _root - The merkle root of the tree where all the L2 to L1 messages are leaves * @param _height - The height of the merkle tree that the root corresponds to */ @@ -31,9 +31,9 @@ interface INewOutbox { /* * @notice Consumes an entry from the Outbox * @dev Only meaningfully callable by portals / recipients of messages - * @dev Emits the `MessageConsumed` event when consuming messages + * @dev Emits `MessageConsumed` when consuming messages * @param _l2BlockNumber - The block number specifying the block that contains the message we want to consume - * @param _leafIndex - The index where the message resides inside the merkle tree + * @param _leafIndex - The index inside the merkle tree where the message is located * @param _message - The L2 to L1 message * @param _path - The sibling path used to prove inclusion of the message */ diff --git a/l1-contracts/src/core/messagebridge/NewOutbox.sol b/l1-contracts/src/core/messagebridge/NewOutbox.sol index 85b5d2648c2..e194fd4351f 100644 --- a/l1-contracts/src/core/messagebridge/NewOutbox.sol +++ b/l1-contracts/src/core/messagebridge/NewOutbox.sol @@ -11,7 +11,7 @@ import {INewOutbox} from "../interfaces/messagebridge/INewOutbox.sol"; /** * @title NewOutbox * @author Aztec Labs - * @notice Lives on L1 and is used to consume L2 -> L1 messages. Messages are inserted by the rollup contract + * @notice Lives on L1 and is used to consume L2 -> L1 messages. Messages are inserted by the state transitioner * and will be consumed by the portal contracts. */ contract NewOutbox is INewOutbox { @@ -30,6 +30,15 @@ contract NewOutbox is INewOutbox { STATE_TRANSITIONER = _stateTransitioner; } + /* + * @notice Inserts the root of a merkle tree containing all of the L2 to L1 messages in + * a block specified by _blockNumber. + * @dev Only callable by the state transitioner (rollup contract) + * @dev Emits `RootAdded` upon inserting the root successfully + * @param _l2BlockNumber - The L2 Block Number in which the L2 to L1 messages reside + * @param _root - The merkle root of the tree where all the L2 to L1 messages are leaves + * @param _height - The height of the merkle tree that the root corresponds to + */ function insert(uint256 _l2BlockNumber, bytes32 _root, uint256 _height) external override(INewOutbox) @@ -48,6 +57,15 @@ contract NewOutbox is INewOutbox { emit RootAdded(_l2BlockNumber, _root, _height); } + /* + * @notice Consumes an entry from the Outbox + * @dev Only meaningfully callable by portals / recipients of messages + * @dev Emits `MessageConsumed` when consuming messages + * @param _l2BlockNumber - The block number specifying the block that contains the message we want to consume + * @param _leafIndex - The index inside the merkle tree where the message is located + * @param _message - The L2 to L1 message + * @param _path - The sibling path used to prove inclusion of the message + */ function consume( uint256 _l2BlockNumber, uint256 _leafIndex, @@ -78,14 +96,24 @@ contract NewOutbox is INewOutbox { revert Errors.Outbox__InvalidPathLength(expectedHeight, _path.length); } - bytes32 messageHash = - _verifyMembership(_path, _message, _leafIndex, expectedRoot); + bytes32 messageHash = _verifyMembership(_path, _message, _leafIndex, expectedRoot); roots[_l2BlockNumber].nullified[_leafIndex] = true; emit MessageConsumed(_l2BlockNumber, expectedRoot, messageHash); } + /* + * @notice Verifies the membership of an L2 to L1 message against an expected root. + * @dev This function assumes a valid path height, as well as sane inputs. + * @dev In the case of a mismatched root, and subsequent inability to verify membership, this function throws + * and does not return anything. + * @param _path - The sibling path of the message as a leaf, used to prove message inclusion + * @param _message - The message we are trying to prove inclusion for + * @param _index - The index of the message inside the L2 to L1 message tree + * @param _expectedRoot - The expected root to check the validity of the message and sibling path with. + * @returns - The leaf of the message in the L2 to L1 tree, which is the L2 to L1 message hashed via SHA256. + */ function _verifyMembership( bytes32[] memory _path, DataStructures.L2ToL1Msg memory _message, diff --git a/l1-contracts/test/NewOutbox.t.sol b/l1-contracts/test/NewOutbox.t.sol index bec46a7cbbd..5d290b8d81f 100644 --- a/l1-contracts/test/NewOutbox.t.sol +++ b/l1-contracts/test/NewOutbox.t.sol @@ -9,13 +9,14 @@ import {DataStructures} from "../src/core/libraries/DataStructures.sol"; import {Hash} from "../src/core/libraries/Hash.sol"; import {NaiveMerkle} from "./merkle/Naive.sol"; - contract NewOutboxTest is Test { using Hash for DataStructures.L2ToL1Msg; - address internal constant STATE_TRANSITIONER = address(0x42069123); - uint256 internal constant TREE_HEIGHT = 2; - uint256 internal constant version = 0; + address internal constant _STATE_TRANSITIONER = address(0x42069123); + address internal constant _NOT_STATE_TRANSITIONER = address(0x69); + address internal constant _NOT_RECIPIENT = address(0x420); + uint256 internal constant _DEFAULT_TREE_HEIGHT = 2; + uint256 internal constant _VERSION = 0; NewOutbox internal outbox; NaiveMerkle internal zeroedTree; @@ -26,17 +27,17 @@ contract NewOutboxTest is Test { ); function setUp() public { - outbox = new NewOutbox(STATE_TRANSITIONER); - zeroedTree = new NaiveMerkle(TREE_HEIGHT); + outbox = new NewOutbox(_STATE_TRANSITIONER); + zeroedTree = new NaiveMerkle(_DEFAULT_TREE_HEIGHT); } - function _fakeMessage() internal view returns (DataStructures.L2ToL1Msg memory) { + function _fakeMessage(address _recipient) internal view returns (DataStructures.L2ToL1Msg memory) { return DataStructures.L2ToL1Msg({ sender: DataStructures.L2Actor({ actor: 0x2000000000000000000000000000000000000000000000000000000000000000, - version: version + version: _VERSION }), - recipient: DataStructures.L1Actor({actor: address(this), chainId: block.chainid}), + recipient: DataStructures.L1Actor({actor: _recipient, chainId: block.chainid}), content: 0x3000000000000000000000000000000000000000000000000000000000000000 }); } @@ -44,212 +45,181 @@ contract NewOutboxTest is Test { function testRevertIfInsertingFromNonRollup() public { bytes32 root = zeroedTree.computeRoot(); - vm.prank(address(0x69)); - vm.expectRevert( - abi.encodeWithSelector(Errors.Outbox__Unauthorized.selector) - ); - outbox.insert(1, root, TREE_HEIGHT); + vm.prank(_NOT_STATE_TRANSITIONER); + vm.expectRevert(abi.encodeWithSelector(Errors.Outbox__Unauthorized.selector)); + outbox.insert(1, root, _DEFAULT_TREE_HEIGHT); } function testRevertIfInsertingDuplicate() public { bytes32 root = zeroedTree.computeRoot(); - vm.prank(STATE_TRANSITIONER); - outbox.insert(1, root, TREE_HEIGHT); + vm.prank(_STATE_TRANSITIONER); + outbox.insert(1, root, _DEFAULT_TREE_HEIGHT); - vm.prank(STATE_TRANSITIONER); - vm.expectRevert( - abi.encodeWithSelector(Errors.Outbox__RootAlreadySet.selector, 1) - ); - outbox.insert(1, root, TREE_HEIGHT); + vm.prank(_STATE_TRANSITIONER); + vm.expectRevert(abi.encodeWithSelector(Errors.Outbox__RootAlreadySet.selector, 1)); + outbox.insert(1, root, _DEFAULT_TREE_HEIGHT); } function testRevertIfConsumingMessageBelongingToOther() public { - DataStructures.L2ToL1Msg memory fakeMessage = _fakeMessage(); + DataStructures.L2ToL1Msg memory fakeMessage = _fakeMessage(address(this)); (bytes32[] memory path,) = zeroedTree.computeSiblingPath(0); - vm.prank(address(0x69)); + vm.prank(_NOT_RECIPIENT); vm.expectRevert( - abi.encodeWithSelector(Errors.Outbox__InvalidRecipient.selector, address(this), address(0x69)) + abi.encodeWithSelector( + Errors.Outbox__InvalidRecipient.selector, address(this), _NOT_RECIPIENT + ) ); outbox.consume(1, 1, fakeMessage, path); } - function testRevertIfNothingInsertedAtBlockNumber() public { - uint256 blockNumber = 1; - DataStructures.L2ToL1Msg memory fakeMessage = _fakeMessage(); + function testRevertIfConsumingMessageWithInvalidChainId() public { + DataStructures.L2ToL1Msg memory fakeMessage = _fakeMessage(address(this)); (bytes32[] memory path,) = zeroedTree.computeSiblingPath(0); - vm.expectRevert( - abi.encodeWithSelector(Errors.Outbox__NothingToConsumeAtBlock.selector, blockNumber) - ); - outbox.consume(blockNumber, 1, fakeMessage, path); + fakeMessage.recipient.chainId = block.chainid + 1; + + vm.expectRevert(abi.encodeWithSelector(Errors.Outbox__InvalidChainId.selector)); + outbox.consume(1, 1, fakeMessage, path); } - function testRevertIfConsumingMessageWithInvalidChainId() public { - DataStructures.L2ToL1Msg memory fakeMessage = _fakeMessage(); + function testRevertIfNothingInsertedAtBlockNumber() public { + uint256 blockNumber = 1; + DataStructures.L2ToL1Msg memory fakeMessage = _fakeMessage(address(this)); (bytes32[] memory path,) = zeroedTree.computeSiblingPath(0); - fakeMessage.recipient.chainId = block.chainid + 1; - vm.expectRevert( - abi.encodeWithSelector(Errors.Outbox__InvalidChainId.selector) + abi.encodeWithSelector(Errors.Outbox__NothingToConsumeAtBlock.selector, blockNumber) ); - outbox.consume(1, 1, fakeMessage, path); + outbox.consume(blockNumber, 1, fakeMessage, path); } function testRevertIfTryingToConsumeSameMessage() public { - DataStructures.L2ToL1Msg memory fakeMessage = _fakeMessage(); + DataStructures.L2ToL1Msg memory fakeMessage = _fakeMessage(address(this)); bytes32 leaf = fakeMessage.sha256ToField(); - NaiveMerkle tree = new NaiveMerkle(TREE_HEIGHT); + NaiveMerkle tree = new NaiveMerkle(_DEFAULT_TREE_HEIGHT); tree.insertLeaf(leaf); bytes32 root = tree.computeRoot(); - vm.prank(STATE_TRANSITIONER); - outbox.insert(1, root, TREE_HEIGHT); + vm.prank(_STATE_TRANSITIONER); + outbox.insert(1, root, _DEFAULT_TREE_HEIGHT); (bytes32[] memory path,) = tree.computeSiblingPath(0); outbox.consume(1, 0, fakeMessage, path); - vm.expectRevert( - abi.encodeWithSelector(Errors.Outbox__AlreadyNullified.selector, 1, 0) - ); + vm.expectRevert(abi.encodeWithSelector(Errors.Outbox__AlreadyNullified.selector, 1, 0)); outbox.consume(1, 0, fakeMessage, path); } function testRevertIfPathHeightMismatch() public { - DataStructures.L2ToL1Msg memory fakeMessage = _fakeMessage(); + DataStructures.L2ToL1Msg memory fakeMessage = _fakeMessage(address(this)); bytes32 leaf = fakeMessage.sha256ToField(); - NaiveMerkle tree = new NaiveMerkle(TREE_HEIGHT); + NaiveMerkle tree = new NaiveMerkle(_DEFAULT_TREE_HEIGHT); tree.insertLeaf(leaf); bytes32 root = tree.computeRoot(); - vm.prank(STATE_TRANSITIONER); - outbox.insert(1, root, TREE_HEIGHT); + vm.prank(_STATE_TRANSITIONER); + outbox.insert(1, root, _DEFAULT_TREE_HEIGHT); - NaiveMerkle biggerTree = new NaiveMerkle(TREE_HEIGHT + 1); + NaiveMerkle biggerTree = new NaiveMerkle(_DEFAULT_TREE_HEIGHT + 1); tree.insertLeaf(leaf); (bytes32[] memory path,) = biggerTree.computeSiblingPath(0); vm.expectRevert( - abi.encodeWithSelector(Errors.Outbox__InvalidPathLength.selector, TREE_HEIGHT, TREE_HEIGHT + 1) + abi.encodeWithSelector( + Errors.Outbox__InvalidPathLength.selector, _DEFAULT_TREE_HEIGHT, _DEFAULT_TREE_HEIGHT + 1 + ) ); outbox.consume(1, 0, fakeMessage, path); } function testRevertIfTryingToConsumeMessageNotInTree() public { - DataStructures.L2ToL1Msg memory fakeMessage = _fakeMessage(); + DataStructures.L2ToL1Msg memory fakeMessage = _fakeMessage(address(this)); bytes32 leaf = fakeMessage.sha256ToField(); - fakeMessage.content = bytes32 (uint256(42069)); + fakeMessage.content = bytes32(uint256(42069)); bytes32 modifiedLeaf = fakeMessage.sha256ToField(); - NaiveMerkle tree = new NaiveMerkle(TREE_HEIGHT); + NaiveMerkle tree = new NaiveMerkle(_DEFAULT_TREE_HEIGHT); tree.insertLeaf(leaf); bytes32 root = tree.computeRoot(); - NaiveMerkle modifiedTree = new NaiveMerkle(TREE_HEIGHT); + NaiveMerkle modifiedTree = new NaiveMerkle(_DEFAULT_TREE_HEIGHT); modifiedTree.insertLeaf(modifiedLeaf); bytes32 modifiedRoot = modifiedTree.computeRoot(); - vm.prank(STATE_TRANSITIONER); - outbox.insert(1, root, TREE_HEIGHT); + vm.prank(_STATE_TRANSITIONER); + outbox.insert(1, root, _DEFAULT_TREE_HEIGHT); (bytes32[] memory path,) = modifiedTree.computeSiblingPath(0); - vm.expectRevert( - abi.encodeWithSelector(Errors.Outbox__InvalidRoot.selector, root, modifiedRoot) - ); + vm.expectRevert(abi.encodeWithSelector(Errors.Outbox__InvalidRoot.selector, root, modifiedRoot)); outbox.consume(1, 0, fakeMessage, path); } -// // fuzz batch insert -> check inserted. event emitted -// function testFuzzBatchInsert(bytes32[] memory _entryKeys) public { -// // expected events -// for (uint256 i = 0; i < _entryKeys.length; i++) { -// if (_entryKeys[i] == bytes32(0)) continue; -// vm.expectEmit(true, false, false, false); -// emit MessageAdded(_entryKeys[i]); -// } - -// outbox.sendL1Messages(_entryKeys); -// for (uint256 i = 0; i < _entryKeys.length; i++) { -// if (_entryKeys[i] == bytes32(0)) continue; -// bytes32 key = _entryKeys[i]; -// DataStructures.Entry memory entry = outbox.get(key); -// assertGt(entry.count, 0); -// assertEq(entry.fee, 0); -// assertEq(entry.deadline, 0); -// } -// } - -// function testRevertIfConsumingFromWrongRecipient() public { -// DataStructures.L2ToL1Msg memory message = _fakeMessage(); -// message.recipient.actor = address(0x1); -// vm.expectRevert(Errors.Outbox__Unauthorized.selector); -// outbox.consume(message); -// } - -// function testRevertIfConsumingForWrongChain() public { -// DataStructures.L2ToL1Msg memory message = _fakeMessage(); -// message.recipient.chainId = 2; -// vm.expectRevert(Errors.Outbox__InvalidChainId.selector); -// outbox.consume(message); -// } - -// function testRevertIfConsumingMessageThatDoesntExist() public { -// DataStructures.L2ToL1Msg memory message = _fakeMessage(); -// bytes32 entryKey = outbox.computeEntryKey(message); -// vm.expectRevert(abi.encodeWithSelector(Errors.Outbox__NothingToConsume.selector, entryKey)); -// outbox.consume(message); -// } - -// function testRevertIfInsertingFromWrongRollup() public { -// address wrongRollup = address(0xbeeffeed); -// uint256 wrongVersion = registry.upgrade(wrongRollup, STATE_TRANSITIONER, address(outbox)); - -// DataStructures.L2ToL1Msg memory message = _fakeMessage(); -// // correctly set message.recipient to this address -// message.recipient = DataStructures.L1Actor({actor: address(this), chainId: block.chainid}); - -// bytes32 expectedEntryKey = outbox.computeEntryKey(message); -// bytes32[] memory entryKeys = new bytes32[](1); -// entryKeys[0] = expectedEntryKey; - -// vm.prank(wrongRollup); -// outbox.sendL1Messages(entryKeys); - -// vm.prank(message.recipient.actor); -// vm.expectRevert( -// abi.encodeWithSelector(Errors.Outbox__InvalidVersion.selector, wrongVersion, version) -// ); -// outbox.consume(message); -// } - -// function testFuzzConsume(DataStructures.L2ToL1Msg memory _message) public { -// // correctly set message.recipient to this address -// _message.recipient = DataStructures.L1Actor({actor: address(this), chainId: block.chainid}); - -// // correctly set the message.sender.version -// _message.sender.version = version; - -// bytes32 expectedEntryKey = outbox.computeEntryKey(_message); -// bytes32[] memory entryKeys = new bytes32[](1); -// entryKeys[0] = expectedEntryKey; -// outbox.sendL1Messages(entryKeys); - -// vm.prank(_message.recipient.actor); -// vm.expectEmit(true, true, false, false); -// emit MessageConsumed(expectedEntryKey, _message.recipient.actor); -// outbox.consume(_message); - -// // ensure no such message to consume: -// vm.expectRevert( -// abi.encodeWithSelector(Errors.Outbox__NothingToConsume.selector, expectedEntryKey) -// ); -// outbox.consume(_message); -// } + function testInsertVariedLeafs(bytes32[] calldata _messageLeafs) public { + vm.assume(_messageLeafs.length < 256); + vm.assume(_messageLeafs.length > 128); + + // This correct only when fuzzing using _messageLeafs.length 128 < length < 256 + NaiveMerkle tree = new NaiveMerkle(8); + + for (uint256 i = 0; i < _messageLeafs.length; i++) { + vm.assume(_messageLeafs[i] != bytes32(0)); + tree.insertLeaf(_messageLeafs[i]); + } + + bytes32 root = tree.computeRoot(); + + vm.expectEmit(true, true, true, true, address(outbox)); + emit RootAdded(1, root, 8); + vm.prank(_STATE_TRANSITIONER); + outbox.insert(1, root, 8); + } + + // This test takes awhile so to keep it somewhat reasonable wev'e set a limit on the amount of fuzz runs + /// forge-config: default.fuzz.runs = 16 + function testInsertAndConsumeWithVariedRecipients( + address[] calldata _recipients, + uint256 _blockNumber + ) public { + // We most likely will not have > 128 L2 -> L1 Messages in a single block but are testing an upper bound here + vm.assume(_recipients.length < 256); + vm.assume(_recipients.length > 128); + DataStructures.L2ToL1Msg[] memory messages = new DataStructures.L2ToL1Msg[](_recipients.length); + + // This correct only when fuzzing using _messageLeafs.length 128 < length < 256 hence the assumption above + uint256 bigTreeHeight = 8; + + NaiveMerkle tree = new NaiveMerkle(bigTreeHeight); + + for (uint256 i = 0; i < _recipients.length; i++) { + vm.assume(_recipients[i] != address(0)); + DataStructures.L2ToL1Msg memory fakeMessage = _fakeMessage(_recipients[i]); + messages[i] = fakeMessage; + bytes32 modifiedLeaf = fakeMessage.sha256ToField(); + + tree.insertLeaf(modifiedLeaf); + } + + bytes32 root = tree.computeRoot(); + + vm.expectEmit(true, true, true, true, address(outbox)); + emit RootAdded(_blockNumber, root, bigTreeHeight); + vm.prank(_STATE_TRANSITIONER); + outbox.insert(_blockNumber, root, bigTreeHeight); + + for (uint256 i = 0; i < _recipients.length; i++) { + (bytes32[] memory path, bytes32 leaf) = tree.computeSiblingPath(i); + + vm.expectEmit(true, true, true, true, address(outbox)); + emit MessageConsumed(_blockNumber, root, leaf); + vm.prank(_recipients[i]); + outbox.consume(_blockNumber, i, messages[i], path); + } + } } From ec90ee8cfe2b1236315e24bf5c0c202c7eb0faa8 Mon Sep 17 00:00:00 2001 From: Esau Date: Fri, 8 Mar 2024 15:15:12 +0100 Subject: [PATCH 08/62] slither --- l1-contracts/slither_output.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/l1-contracts/slither_output.md b/l1-contracts/slither_output.md index 9abb467f0a7..87857250661 100644 --- a/l1-contracts/slither_output.md +++ b/l1-contracts/slither_output.md @@ -26,9 +26,9 @@ src/core/libraries/decoders/TxsDecoder.sol#L86 - [ ] ID-2 -[NewOutbox._verifyMembership(bytes32[],DataStructures.L2ToL1Msg,uint256,bytes32).root](src/core/messagebridge/NewOutbox.sol#L114) is a local variable never initialized +[NewOutbox._verifyMembership(bytes32[],DataStructures.L2ToL1Msg,uint256,bytes32).root](src/core/messagebridge/NewOutbox.sol#L125) is a local variable never initialized -src/core/messagebridge/NewOutbox.sol#L114 +src/core/messagebridge/NewOutbox.sol#L125 ## unused-return From 6131ecfe520bfd3defcc5eea0278fbc00470e92d Mon Sep 17 00:00:00 2001 From: Esau Date: Fri, 8 Mar 2024 15:16:05 +0100 Subject: [PATCH 09/62] slither --- l1-contracts/slither_output.md | 83 ++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 38 deletions(-) diff --git a/l1-contracts/slither_output.md b/l1-contracts/slither_output.md index 87857250661..d64a6e76dcf 100644 --- a/l1-contracts/slither_output.md +++ b/l1-contracts/slither_output.md @@ -1,6 +1,6 @@ Summary - [uninitialized-local](#uninitialized-local) (3 results) (Medium) - - [unused-return](#unused-return) (1 results) (Medium) + - [unused-return](#unused-return) (2 results) (Medium) - [missing-zero-check](#missing-zero-check) (2 results) (Low) - [reentrancy-events](#reentrancy-events) (2 results) (Low) - [timestamp](#timestamp) (4 results) (Low) @@ -35,22 +35,28 @@ src/core/messagebridge/NewOutbox.sol#L125 Impact: Medium Confidence: Medium - [ ] ID-3 -[Rollup.process(bytes,bytes32,bytes,bytes)](src/core/Rollup.sol#L53-L91) ignores return value by [(l1ToL2Msgs,l2ToL1Msgs) = MessagesDecoder.decode(_body)](src/core/Rollup.sol#L69) +[Rollup.process(bytes,bytes32,bytes,bytes)](src/core/Rollup.sol#L58-L103) ignores return value by [NEW_INBOX.consume()](src/core/Rollup.sol#L93) -src/core/Rollup.sol#L53-L91 +src/core/Rollup.sol#L58-L103 + + + - [ ] ID-4 +[Rollup.process(bytes,bytes32,bytes,bytes)](src/core/Rollup.sol#L58-L103) ignores return value by [(l1ToL2Msgs,l2ToL1Msgs) = MessagesDecoder.decode(_body)](src/core/Rollup.sol#L74) + +src/core/Rollup.sol#L58-L103 ## missing-zero-check Impact: Low Confidence: Medium - - [ ] ID-4 + - [ ] ID-5 [NewInbox.constructor(address,uint256)._rollup](src/core/messagebridge/NewInbox.sol#L41) lacks a zero-check on : - [ROLLUP = _rollup](src/core/messagebridge/NewInbox.sol#L42) src/core/messagebridge/NewInbox.sol#L41 - - [ ] ID-5 + - [ ] ID-6 [NewOutbox.constructor(address)._stateTransitioner](src/core/messagebridge/NewOutbox.sol#L29) lacks a zero-check on : - [STATE_TRANSITIONER = _stateTransitioner](src/core/messagebridge/NewOutbox.sol#L30) @@ -60,17 +66,6 @@ src/core/messagebridge/NewOutbox.sol#L29 ## reentrancy-events Impact: Low Confidence: Medium - - [ ] ID-6 -Reentrancy in [Rollup.process(bytes,bytes32,bytes,bytes)](src/core/Rollup.sol#L53-L91): - External calls: - - [inbox.batchConsume(l1ToL2Msgs,msg.sender)](src/core/Rollup.sol#L85) - - [outbox.sendL1Messages(l2ToL1Msgs)](src/core/Rollup.sol#L88) - Event emitted after the call(s): - - [L2BlockProcessed(header.globalVariables.blockNumber)](src/core/Rollup.sol#L90) - -src/core/Rollup.sol#L53-L91 - - - [ ] ID-7 Reentrancy in [NewInbox.sendL2Message(DataStructures.L2Actor,bytes32,bytes32)](src/core/messagebridge/NewInbox.sol#L62-L99): External calls: @@ -81,10 +76,22 @@ Reentrancy in [NewInbox.sendL2Message(DataStructures.L2Actor,bytes32,bytes32)](s src/core/messagebridge/NewInbox.sol#L62-L99 + - [ ] ID-8 +Reentrancy in [Rollup.process(bytes,bytes32,bytes,bytes)](src/core/Rollup.sol#L58-L103): + External calls: + - [inbox.batchConsume(l1ToL2Msgs,msg.sender)](src/core/Rollup.sol#L90) + - [NEW_INBOX.consume()](src/core/Rollup.sol#L93) + - [outbox.sendL1Messages(l2ToL1Msgs)](src/core/Rollup.sol#L100) + Event emitted after the call(s): + - [L2BlockProcessed(header.globalVariables.blockNumber)](src/core/Rollup.sol#L102) + +src/core/Rollup.sol#L58-L103 + + ## timestamp Impact: Low Confidence: Medium - - [ ] ID-8 + - [ ] ID-9 [Inbox.batchConsume(bytes32[],address)](src/core/messagebridge/Inbox.sol#L122-L143) uses timestamp for comparisons Dangerous comparisons: - [block.timestamp > entry.deadline](src/core/messagebridge/Inbox.sol#L136) @@ -92,7 +99,7 @@ Confidence: Medium src/core/messagebridge/Inbox.sol#L122-L143 - - [ ] ID-9 + - [ ] ID-10 [HeaderLib.validate(HeaderLib.Header,uint256,uint256,bytes32)](src/core/libraries/HeaderLib.sol#L108-L138) uses timestamp for comparisons Dangerous comparisons: - [_header.globalVariables.timestamp > block.timestamp](src/core/libraries/HeaderLib.sol#L122) @@ -100,7 +107,7 @@ src/core/messagebridge/Inbox.sol#L122-L143 src/core/libraries/HeaderLib.sol#L108-L138 - - [ ] ID-10 + - [ ] ID-11 [Inbox.sendL2Message(DataStructures.L2Actor,uint32,bytes32,bytes32)](src/core/messagebridge/Inbox.sol#L45-L91) uses timestamp for comparisons Dangerous comparisons: - [_deadline <= block.timestamp](src/core/messagebridge/Inbox.sol#L54) @@ -108,7 +115,7 @@ src/core/libraries/HeaderLib.sol#L108-L138 src/core/messagebridge/Inbox.sol#L45-L91 - - [ ] ID-11 + - [ ] ID-12 [Inbox.cancelL2Message(DataStructures.L1ToL2Msg,address)](src/core/messagebridge/Inbox.sol#L102-L113) uses timestamp for comparisons Dangerous comparisons: - [block.timestamp <= _message.deadline](src/core/messagebridge/Inbox.sol#L108) @@ -119,7 +126,7 @@ src/core/messagebridge/Inbox.sol#L102-L113 ## assembly Impact: Informational Confidence: High - - [ ] ID-12 + - [ ] ID-13 [MessagesDecoder.decode(bytes)](src/core/libraries/decoders/MessagesDecoder.sol#L60-L150) uses assembly - [INLINE ASM](src/core/libraries/decoders/MessagesDecoder.sol#L79-L81) - [INLINE ASM](src/core/libraries/decoders/MessagesDecoder.sol#L112-L118) @@ -127,7 +134,7 @@ Confidence: High src/core/libraries/decoders/MessagesDecoder.sol#L60-L150 - - [ ] ID-13 + - [ ] ID-14 [TxsDecoder.computeRoot(bytes32[])](src/core/libraries/decoders/TxsDecoder.sol#L291-L310) uses assembly - [INLINE ASM](src/core/libraries/decoders/TxsDecoder.sol#L298-L300) @@ -137,31 +144,31 @@ src/core/libraries/decoders/TxsDecoder.sol#L291-L310 ## dead-code Impact: Informational Confidence: Medium - - [ ] ID-14 + - [ ] ID-15 [Inbox._errIncompatibleEntryArguments(bytes32,uint64,uint64,uint32,uint32,uint32,uint32)](src/core/messagebridge/Inbox.sol#L212-L230) is never used and should be removed src/core/messagebridge/Inbox.sol#L212-L230 - - [ ] ID-15 + - [ ] ID-16 [Outbox._errNothingToConsume(bytes32)](src/core/messagebridge/Outbox.sol#L114-L116) is never used and should be removed src/core/messagebridge/Outbox.sol#L114-L116 - - [ ] ID-16 + - [ ] ID-17 [Hash.sha256ToField(bytes32)](src/core/libraries/Hash.sol#L59-L61) is never used and should be removed src/core/libraries/Hash.sol#L59-L61 - - [ ] ID-17 + - [ ] ID-18 [Inbox._errNothingToConsume(bytes32)](src/core/messagebridge/Inbox.sol#L197-L199) is never used and should be removed src/core/messagebridge/Inbox.sol#L197-L199 - - [ ] ID-18 + - [ ] ID-19 [Outbox._errIncompatibleEntryArguments(bytes32,uint64,uint64,uint32,uint32,uint32,uint32)](src/core/messagebridge/Outbox.sol#L129-L147) is never used and should be removed src/core/messagebridge/Outbox.sol#L129-L147 @@ -170,13 +177,13 @@ src/core/messagebridge/Outbox.sol#L129-L147 ## solc-version Impact: Informational Confidence: High - - [ ] ID-19 + - [ ] ID-20 solc-0.8.21 is not recommended for deployment ## low-level-calls Impact: Informational Confidence: High - - [ ] ID-20 + - [ ] ID-21 Low level call in [Inbox.withdrawFees()](src/core/messagebridge/Inbox.sol#L148-L153): - [(success) = msg.sender.call{value: balance}()](src/core/messagebridge/Inbox.sol#L151) @@ -186,36 +193,36 @@ src/core/messagebridge/Inbox.sol#L148-L153 ## similar-names Impact: Informational Confidence: Medium - - [ ] ID-21 + - [ ] ID-22 Variable [Constants.LOGS_HASHES_NUM_BYTES_PER_BASE_ROLLUP](src/core/libraries/ConstantsGen.sol#L129) is too similar to [Constants.NOTE_HASHES_NUM_BYTES_PER_BASE_ROLLUP](src/core/libraries/ConstantsGen.sol#L122) src/core/libraries/ConstantsGen.sol#L129 - - [ ] ID-22 + - [ ] ID-23 Variable [Constants.L1_TO_L2_MESSAGE_LENGTH](src/core/libraries/ConstantsGen.sol#L109) is too similar to [Constants.L2_TO_L1_MESSAGE_LENGTH](src/core/libraries/ConstantsGen.sol#L110) src/core/libraries/ConstantsGen.sol#L109 - - [ ] ID-23 + - [ ] ID-24 Variable [NewOutbox.STATE_TRANSITIONER](src/core/messagebridge/NewOutbox.sol#L26) is too similar to [NewOutbox.constructor(address)._stateTransitioner](src/core/messagebridge/NewOutbox.sol#L29) src/core/messagebridge/NewOutbox.sol#L26 - - [ ] ID-24 -Variable [Rollup.AVAILABILITY_ORACLE](src/core/Rollup.sol#L30) is too similar to [Rollup.constructor(IRegistry,IAvailabilityOracle)._availabilityOracle](src/core/Rollup.sol#L39) + - [ ] ID-25 +Variable [Rollup.AVAILABILITY_ORACLE](src/core/Rollup.sol#L33) is too similar to [Rollup.constructor(IRegistry,IAvailabilityOracle)._availabilityOracle](src/core/Rollup.sol#L43) -src/core/Rollup.sol#L30 +src/core/Rollup.sol#L33 ## constable-states Impact: Optimization Confidence: High - - [ ] ID-25 -[Rollup.lastWarpedBlockTs](src/core/Rollup.sol#L37) should be constant + - [ ] ID-26 +[Rollup.lastWarpedBlockTs](src/core/Rollup.sol#L41) should be constant -src/core/Rollup.sol#L37 +src/core/Rollup.sol#L41 From 9899a936aae4e4a2c4602b99717e34648892cb60 Mon Sep 17 00:00:00 2001 From: Esau Date: Fri, 8 Mar 2024 18:18:24 +0100 Subject: [PATCH 10/62] correct slither version --- l1-contracts/slither_output.md | 249 +++++++++++++++++++++++++++++---- 1 file changed, 222 insertions(+), 27 deletions(-) diff --git a/l1-contracts/slither_output.md b/l1-contracts/slither_output.md index d64a6e76dcf..f27ddef336d 100644 --- a/l1-contracts/slither_output.md +++ b/l1-contracts/slither_output.md @@ -1,31 +1,44 @@ Summary + - [pess-unprotected-setter](#pess-unprotected-setter) (1 results) (High) - [uninitialized-local](#uninitialized-local) (3 results) (Medium) - [unused-return](#unused-return) (2 results) (Medium) + - [pess-dubious-typecast](#pess-dubious-typecast) (8 results) (Medium) - [missing-zero-check](#missing-zero-check) (2 results) (Low) - [reentrancy-events](#reentrancy-events) (2 results) (Low) - [timestamp](#timestamp) (4 results) (Low) + - [pess-public-vs-external](#pess-public-vs-external) (7 results) (Low) - [assembly](#assembly) (2 results) (Informational) - [dead-code](#dead-code) (5 results) (Informational) - [solc-version](#solc-version) (1 results) (Informational) - [low-level-calls](#low-level-calls) (1 results) (Informational) - [similar-names](#similar-names) (4 results) (Informational) - [constable-states](#constable-states) (1 results) (Optimization) + - [pess-multiple-storage-read](#pess-multiple-storage-read) (7 results) (Optimization) +## pess-unprotected-setter +Impact: High +Confidence: Medium + - [ ] ID-0 +Function [Rollup.process(bytes,bytes32,bytes,bytes)](src/core/Rollup.sol#L58-L103) is a non-protected setter archive is written + +src/core/Rollup.sol#L58-L103 + + ## uninitialized-local Impact: Medium Confidence: Medium - - [ ] ID-0 + - [ ] ID-1 [HeaderLib.decode(bytes).header](src/core/libraries/HeaderLib.sol#L150) is a local variable never initialized src/core/libraries/HeaderLib.sol#L150 - - [ ] ID-1 + - [ ] ID-2 [TxsDecoder.decode(bytes).vars](src/core/libraries/decoders/TxsDecoder.sol#L86) is a local variable never initialized src/core/libraries/decoders/TxsDecoder.sol#L86 - - [ ] ID-2 + - [ ] ID-3 [NewOutbox._verifyMembership(bytes32[],DataStructures.L2ToL1Msg,uint256,bytes32).root](src/core/messagebridge/NewOutbox.sol#L125) is a local variable never initialized src/core/messagebridge/NewOutbox.sol#L125 @@ -34,29 +47,110 @@ src/core/messagebridge/NewOutbox.sol#L125 ## unused-return Impact: Medium Confidence: Medium - - [ ] ID-3 + - [ ] ID-4 [Rollup.process(bytes,bytes32,bytes,bytes)](src/core/Rollup.sol#L58-L103) ignores return value by [NEW_INBOX.consume()](src/core/Rollup.sol#L93) src/core/Rollup.sol#L58-L103 - - [ ] ID-4 + - [ ] ID-5 [Rollup.process(bytes,bytes32,bytes,bytes)](src/core/Rollup.sol#L58-L103) ignores return value by [(l1ToL2Msgs,l2ToL1Msgs) = MessagesDecoder.decode(_body)](src/core/Rollup.sol#L74) src/core/Rollup.sol#L58-L103 +## pess-dubious-typecast +Impact: Medium +Confidence: High + - [ ] ID-6 +Dubious typecast in [TxsDecoder.read4(bytes,uint256)](src/core/libraries/decoders/TxsDecoder.sol#L359-L361): + bytes => bytes4 casting occurs in [uint256(uint32(bytes4(slice(_data,_offset,4))))](src/core/libraries/decoders/TxsDecoder.sol#L360) + +src/core/libraries/decoders/TxsDecoder.sol#L359-L361 + + + - [ ] ID-7 +Dubious typecast in [MessagesDecoder.read1(bytes,uint256)](src/core/libraries/decoders/MessagesDecoder.sol#L158-L160): + bytes => bytes1 casting occurs in [uint256(uint8(bytes1(_data)))](src/core/libraries/decoders/MessagesDecoder.sol#L159) + +src/core/libraries/decoders/MessagesDecoder.sol#L158-L160 + + + - [ ] ID-8 +Dubious typecast in [Outbox.sendL1Messages(bytes32[])](src/core/messagebridge/Outbox.sol#L38-L46): + uint256 => uint32 casting occurs in [version = uint32(REGISTRY.getVersionFor(msg.sender))](src/core/messagebridge/Outbox.sol#L40) + +src/core/messagebridge/Outbox.sol#L38-L46 + + + - [ ] ID-9 +Dubious typecast in [Inbox.sendL2Message(DataStructures.L2Actor,uint32,bytes32,bytes32)](src/core/messagebridge/Inbox.sol#L45-L91): + uint256 => uint64 casting occurs in [fee = uint64(msg.value)](src/core/messagebridge/Inbox.sol#L64) + uint256 => uint32 casting occurs in [entries.insert(key,fee,uint32(_recipient.version),_deadline,_errIncompatibleEntryArguments)](src/core/messagebridge/Inbox.sol#L76) + +src/core/messagebridge/Inbox.sol#L45-L91 + + + - [ ] ID-10 +Dubious typecast in [TxsDecoder.read1(bytes,uint256)](src/core/libraries/decoders/TxsDecoder.sol#L349-L351): + bytes => bytes1 casting occurs in [uint256(uint8(bytes1(slice(_data,_offset,1))))](src/core/libraries/decoders/TxsDecoder.sol#L350) + +src/core/libraries/decoders/TxsDecoder.sol#L349-L351 + + + - [ ] ID-11 +Dubious typecast in [MessagesDecoder.read4(bytes,uint256)](src/core/libraries/decoders/MessagesDecoder.sol#L168-L170): + bytes => bytes4 casting occurs in [uint256(uint32(bytes4(_data)))](src/core/libraries/decoders/MessagesDecoder.sol#L169) + +src/core/libraries/decoders/MessagesDecoder.sol#L168-L170 + + + - [ ] ID-12 +Dubious typecast in [Inbox.batchConsume(bytes32[],address)](src/core/messagebridge/Inbox.sol#L122-L143): + uint256 => uint32 casting occurs in [expectedVersion = uint32(REGISTRY.getVersionFor(msg.sender))](src/core/messagebridge/Inbox.sol#L128) + +src/core/messagebridge/Inbox.sol#L122-L143 + + + - [ ] ID-13 +Dubious typecast in [HeaderLib.decode(bytes)](src/core/libraries/HeaderLib.sol#L145-L189): + bytes => bytes32 casting occurs in [header.lastArchive = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L153-L155) + bytes => bytes4 casting occurs in [header.lastArchive = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L153-L155) + bytes => bytes32 casting occurs in [header.contentCommitment.txTreeHeight = uint256(bytes32(_header))](src/core/libraries/HeaderLib.sol#L158) + bytes => bytes32 casting occurs in [header.contentCommitment.txsEffectsHash = bytes32(_header)](src/core/libraries/HeaderLib.sol#L159) + bytes => bytes32 casting occurs in [header.contentCommitment.inHash = bytes32(_header)](src/core/libraries/HeaderLib.sol#L160) + bytes => bytes32 casting occurs in [header.contentCommitment.outHash = bytes32(_header)](src/core/libraries/HeaderLib.sol#L161) + bytes => bytes32 casting occurs in [header.stateReference.l1ToL2MessageTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L164-L166) + bytes => bytes4 casting occurs in [header.stateReference.l1ToL2MessageTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L164-L166) + bytes => bytes32 casting occurs in [header.stateReference.partialStateReference.noteHashTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L167-L169) + bytes => bytes4 casting occurs in [header.stateReference.partialStateReference.noteHashTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L167-L169) + bytes => bytes32 casting occurs in [header.stateReference.partialStateReference.nullifierTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L170-L172) + bytes => bytes4 casting occurs in [header.stateReference.partialStateReference.nullifierTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L170-L172) + bytes => bytes32 casting occurs in [header.stateReference.partialStateReference.contractTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L173-L175) + bytes => bytes4 casting occurs in [header.stateReference.partialStateReference.contractTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L173-L175) + bytes => bytes32 casting occurs in [header.stateReference.partialStateReference.publicDataTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L176-L178) + bytes => bytes4 casting occurs in [header.stateReference.partialStateReference.publicDataTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L176-L178) + bytes => bytes32 casting occurs in [header.globalVariables.chainId = uint256(bytes32(_header))](src/core/libraries/HeaderLib.sol#L181) + bytes => bytes32 casting occurs in [header.globalVariables.version = uint256(bytes32(_header))](src/core/libraries/HeaderLib.sol#L182) + bytes => bytes32 casting occurs in [header.globalVariables.blockNumber = uint256(bytes32(_header))](src/core/libraries/HeaderLib.sol#L183) + bytes => bytes32 casting occurs in [header.globalVariables.timestamp = uint256(bytes32(_header))](src/core/libraries/HeaderLib.sol#L184) + bytes => bytes20 casting occurs in [header.globalVariables.coinbase = address(bytes20(_header))](src/core/libraries/HeaderLib.sol#L185) + bytes => bytes32 casting occurs in [header.globalVariables.feeRecipient = bytes32(_header)](src/core/libraries/HeaderLib.sol#L186) + +src/core/libraries/HeaderLib.sol#L145-L189 + + ## missing-zero-check Impact: Low Confidence: Medium - - [ ] ID-5 + - [ ] ID-14 [NewInbox.constructor(address,uint256)._rollup](src/core/messagebridge/NewInbox.sol#L41) lacks a zero-check on : - [ROLLUP = _rollup](src/core/messagebridge/NewInbox.sol#L42) src/core/messagebridge/NewInbox.sol#L41 - - [ ] ID-6 + - [ ] ID-15 [NewOutbox.constructor(address)._stateTransitioner](src/core/messagebridge/NewOutbox.sol#L29) lacks a zero-check on : - [STATE_TRANSITIONER = _stateTransitioner](src/core/messagebridge/NewOutbox.sol#L30) @@ -66,7 +160,7 @@ src/core/messagebridge/NewOutbox.sol#L29 ## reentrancy-events Impact: Low Confidence: Medium - - [ ] ID-7 + - [ ] ID-16 Reentrancy in [NewInbox.sendL2Message(DataStructures.L2Actor,bytes32,bytes32)](src/core/messagebridge/NewInbox.sol#L62-L99): External calls: - [index = currentTree.insertLeaf(leaf)](src/core/messagebridge/NewInbox.sol#L95) @@ -76,7 +170,7 @@ Reentrancy in [NewInbox.sendL2Message(DataStructures.L2Actor,bytes32,bytes32)](s src/core/messagebridge/NewInbox.sol#L62-L99 - - [ ] ID-8 + - [ ] ID-17 Reentrancy in [Rollup.process(bytes,bytes32,bytes,bytes)](src/core/Rollup.sol#L58-L103): External calls: - [inbox.batchConsume(l1ToL2Msgs,msg.sender)](src/core/Rollup.sol#L90) @@ -91,7 +185,7 @@ src/core/Rollup.sol#L58-L103 ## timestamp Impact: Low Confidence: Medium - - [ ] ID-9 + - [ ] ID-18 [Inbox.batchConsume(bytes32[],address)](src/core/messagebridge/Inbox.sol#L122-L143) uses timestamp for comparisons Dangerous comparisons: - [block.timestamp > entry.deadline](src/core/messagebridge/Inbox.sol#L136) @@ -99,7 +193,7 @@ Confidence: Medium src/core/messagebridge/Inbox.sol#L122-L143 - - [ ] ID-10 + - [ ] ID-19 [HeaderLib.validate(HeaderLib.Header,uint256,uint256,bytes32)](src/core/libraries/HeaderLib.sol#L108-L138) uses timestamp for comparisons Dangerous comparisons: - [_header.globalVariables.timestamp > block.timestamp](src/core/libraries/HeaderLib.sol#L122) @@ -107,7 +201,7 @@ src/core/messagebridge/Inbox.sol#L122-L143 src/core/libraries/HeaderLib.sol#L108-L138 - - [ ] ID-11 + - [ ] ID-20 [Inbox.sendL2Message(DataStructures.L2Actor,uint32,bytes32,bytes32)](src/core/messagebridge/Inbox.sol#L45-L91) uses timestamp for comparisons Dangerous comparisons: - [_deadline <= block.timestamp](src/core/messagebridge/Inbox.sol#L54) @@ -115,7 +209,7 @@ src/core/libraries/HeaderLib.sol#L108-L138 src/core/messagebridge/Inbox.sol#L45-L91 - - [ ] ID-12 + - [ ] ID-21 [Inbox.cancelL2Message(DataStructures.L1ToL2Msg,address)](src/core/messagebridge/Inbox.sol#L102-L113) uses timestamp for comparisons Dangerous comparisons: - [block.timestamp <= _message.deadline](src/core/messagebridge/Inbox.sol#L108) @@ -123,10 +217,66 @@ src/core/messagebridge/Inbox.sol#L45-L91 src/core/messagebridge/Inbox.sol#L102-L113 +## pess-public-vs-external +Impact: Low +Confidence: Medium + - [ ] ID-22 +The following public functions could be turned into external in [FrontierMerkle](src/core/messagebridge/frontier_tree/Frontier.sol#L7-L93) contract: + [FrontierMerkle.constructor(uint256)](src/core/messagebridge/frontier_tree/Frontier.sol#L19-L27) + +src/core/messagebridge/frontier_tree/Frontier.sol#L7-L93 + + + - [ ] ID-23 +The following public functions could be turned into external in [Registry](src/core/messagebridge/Registry.sol#L22-L129) contract: + [Registry.constructor()](src/core/messagebridge/Registry.sol#L29-L33) + +src/core/messagebridge/Registry.sol#L22-L129 + + + - [ ] ID-24 +The following public functions could be turned into external in [NewOutbox](src/core/messagebridge/NewOutbox.sol#L17-L144) contract: + [NewOutbox.constructor(address)](src/core/messagebridge/NewOutbox.sol#L29-L31) + [NewOutbox.consume(uint256,uint256,DataStructures.L2ToL1Msg,bytes32[])](src/core/messagebridge/NewOutbox.sol#L69-L104) + +src/core/messagebridge/NewOutbox.sol#L17-L144 + + + - [ ] ID-25 +The following public functions could be turned into external in [Rollup](src/core/Rollup.sol#L30-L112) contract: + [Rollup.constructor(IRegistry,IAvailabilityOracle)](src/core/Rollup.sol#L43-L49) + +src/core/Rollup.sol#L30-L112 + + + - [ ] ID-26 +The following public functions could be turned into external in [Outbox](src/core/messagebridge/Outbox.sol#L21-L148) contract: + [Outbox.constructor(address)](src/core/messagebridge/Outbox.sol#L29-L31) + [Outbox.get(bytes32)](src/core/messagebridge/Outbox.sol#L77-L84) + [Outbox.contains(bytes32)](src/core/messagebridge/Outbox.sol#L91-L93) + +src/core/messagebridge/Outbox.sol#L21-L148 + + + - [ ] ID-27 +The following public functions could be turned into external in [Inbox](src/core/messagebridge/Inbox.sol#L21-L231) contract: + [Inbox.constructor(address)](src/core/messagebridge/Inbox.sol#L30-L32) + [Inbox.contains(bytes32)](src/core/messagebridge/Inbox.sol#L174-L176) + +src/core/messagebridge/Inbox.sol#L21-L231 + + + - [ ] ID-28 +The following public functions could be turned into external in [NewInbox](src/core/messagebridge/NewInbox.sol#L25-L128) contract: + [NewInbox.constructor(address,uint256)](src/core/messagebridge/NewInbox.sol#L41-L52) + +src/core/messagebridge/NewInbox.sol#L25-L128 + + ## assembly Impact: Informational Confidence: High - - [ ] ID-13 + - [ ] ID-29 [MessagesDecoder.decode(bytes)](src/core/libraries/decoders/MessagesDecoder.sol#L60-L150) uses assembly - [INLINE ASM](src/core/libraries/decoders/MessagesDecoder.sol#L79-L81) - [INLINE ASM](src/core/libraries/decoders/MessagesDecoder.sol#L112-L118) @@ -134,7 +284,7 @@ Confidence: High src/core/libraries/decoders/MessagesDecoder.sol#L60-L150 - - [ ] ID-14 + - [ ] ID-30 [TxsDecoder.computeRoot(bytes32[])](src/core/libraries/decoders/TxsDecoder.sol#L291-L310) uses assembly - [INLINE ASM](src/core/libraries/decoders/TxsDecoder.sol#L298-L300) @@ -144,31 +294,31 @@ src/core/libraries/decoders/TxsDecoder.sol#L291-L310 ## dead-code Impact: Informational Confidence: Medium - - [ ] ID-15 + - [ ] ID-31 [Inbox._errIncompatibleEntryArguments(bytes32,uint64,uint64,uint32,uint32,uint32,uint32)](src/core/messagebridge/Inbox.sol#L212-L230) is never used and should be removed src/core/messagebridge/Inbox.sol#L212-L230 - - [ ] ID-16 + - [ ] ID-32 [Outbox._errNothingToConsume(bytes32)](src/core/messagebridge/Outbox.sol#L114-L116) is never used and should be removed src/core/messagebridge/Outbox.sol#L114-L116 - - [ ] ID-17 + - [ ] ID-33 [Hash.sha256ToField(bytes32)](src/core/libraries/Hash.sol#L59-L61) is never used and should be removed src/core/libraries/Hash.sol#L59-L61 - - [ ] ID-18 + - [ ] ID-34 [Inbox._errNothingToConsume(bytes32)](src/core/messagebridge/Inbox.sol#L197-L199) is never used and should be removed src/core/messagebridge/Inbox.sol#L197-L199 - - [ ] ID-19 + - [ ] ID-35 [Outbox._errIncompatibleEntryArguments(bytes32,uint64,uint64,uint32,uint32,uint32,uint32)](src/core/messagebridge/Outbox.sol#L129-L147) is never used and should be removed src/core/messagebridge/Outbox.sol#L129-L147 @@ -177,13 +327,13 @@ src/core/messagebridge/Outbox.sol#L129-L147 ## solc-version Impact: Informational Confidence: High - - [ ] ID-20 + - [ ] ID-36 solc-0.8.21 is not recommended for deployment ## low-level-calls Impact: Informational Confidence: High - - [ ] ID-21 + - [ ] ID-37 Low level call in [Inbox.withdrawFees()](src/core/messagebridge/Inbox.sol#L148-L153): - [(success) = msg.sender.call{value: balance}()](src/core/messagebridge/Inbox.sol#L151) @@ -193,25 +343,25 @@ src/core/messagebridge/Inbox.sol#L148-L153 ## similar-names Impact: Informational Confidence: Medium - - [ ] ID-22 + - [ ] ID-38 Variable [Constants.LOGS_HASHES_NUM_BYTES_PER_BASE_ROLLUP](src/core/libraries/ConstantsGen.sol#L129) is too similar to [Constants.NOTE_HASHES_NUM_BYTES_PER_BASE_ROLLUP](src/core/libraries/ConstantsGen.sol#L122) src/core/libraries/ConstantsGen.sol#L129 - - [ ] ID-23 + - [ ] ID-39 Variable [Constants.L1_TO_L2_MESSAGE_LENGTH](src/core/libraries/ConstantsGen.sol#L109) is too similar to [Constants.L2_TO_L1_MESSAGE_LENGTH](src/core/libraries/ConstantsGen.sol#L110) src/core/libraries/ConstantsGen.sol#L109 - - [ ] ID-24 + - [ ] ID-40 Variable [NewOutbox.STATE_TRANSITIONER](src/core/messagebridge/NewOutbox.sol#L26) is too similar to [NewOutbox.constructor(address)._stateTransitioner](src/core/messagebridge/NewOutbox.sol#L29) src/core/messagebridge/NewOutbox.sol#L26 - - [ ] ID-25 + - [ ] ID-41 Variable [Rollup.AVAILABILITY_ORACLE](src/core/Rollup.sol#L33) is too similar to [Rollup.constructor(IRegistry,IAvailabilityOracle)._availabilityOracle](src/core/Rollup.sol#L43) src/core/Rollup.sol#L33 @@ -220,9 +370,54 @@ src/core/Rollup.sol#L33 ## constable-states Impact: Optimization Confidence: High - - [ ] ID-26 + - [ ] ID-42 [Rollup.lastWarpedBlockTs](src/core/Rollup.sol#L41) should be constant src/core/Rollup.sol#L41 +## pess-multiple-storage-read +Impact: Optimization +Confidence: High + - [ ] ID-43 +In a function [NewOutbox.insert(uint256,bytes32,uint256)](src/core/messagebridge/NewOutbox.sol#L42-L58) variable [NewOutbox.roots](src/core/messagebridge/NewOutbox.sol#L27) is read multiple times + +src/core/messagebridge/NewOutbox.sol#L42-L58 + + + - [ ] ID-44 +In a function [NewInbox.sendL2Message(DataStructures.L2Actor,bytes32,bytes32)](src/core/messagebridge/NewInbox.sol#L62-L99) variable [NewInbox.inProgress](src/core/messagebridge/NewInbox.sol#L37) is read multiple times + +src/core/messagebridge/NewInbox.sol#L62-L99 + + + - [ ] ID-45 +In a function [FrontierMerkle.root()](src/core/messagebridge/frontier_tree/Frontier.sol#L43-L76) variable [FrontierMerkle.HEIGHT](src/core/messagebridge/frontier_tree/Frontier.sol#L8) is read multiple times + +src/core/messagebridge/frontier_tree/Frontier.sol#L43-L76 + + + - [ ] ID-46 +In a function [NewInbox.consume()](src/core/messagebridge/NewInbox.sol#L108-L127) variable [NewInbox.inProgress](src/core/messagebridge/NewInbox.sol#L37) is read multiple times + +src/core/messagebridge/NewInbox.sol#L108-L127 + + + - [ ] ID-47 +In a function [NewInbox.consume()](src/core/messagebridge/NewInbox.sol#L108-L127) variable [NewInbox.toConsume](src/core/messagebridge/NewInbox.sol#L35) is read multiple times + +src/core/messagebridge/NewInbox.sol#L108-L127 + + + - [ ] ID-48 +In a function [NewOutbox.consume(uint256,uint256,DataStructures.L2ToL1Msg,bytes32[])](src/core/messagebridge/NewOutbox.sol#L69-L104) variable [NewOutbox.roots](src/core/messagebridge/NewOutbox.sol#L27) is read multiple times + +src/core/messagebridge/NewOutbox.sol#L69-L104 + + + - [ ] ID-49 +In a function [FrontierMerkle.root()](src/core/messagebridge/frontier_tree/Frontier.sol#L43-L76) variable [FrontierMerkle.frontier](src/core/messagebridge/frontier_tree/Frontier.sol#L13) is read multiple times + +src/core/messagebridge/frontier_tree/Frontier.sol#L43-L76 + + From 1c1b87e647d48461980c59333cc665c89235ee37 Mon Sep 17 00:00:00 2001 From: Esau Date: Sat, 9 Mar 2024 19:53:04 +0100 Subject: [PATCH 11/62] add fn selectors comments comments fix fix comments comments comments test test comment https://github.com/ethereum/solidity/issues/14430 comments test comments liberryfy test asdf add manual test asdf test fix asdf asdf comments comments --- l1-contracts/foundry.toml | 2 +- .../interfaces/messagebridge/INewOutbox.sol | 10 +- l1-contracts/src/core/libraries/Errors.sol | 15 +-- l1-contracts/src/core/libraries/Merkle.sol | 53 +++++++++ .../src/core/messagebridge/NewOutbox.sol | 57 +++++----- l1-contracts/test/NewOutbox.t.sol | 100 +++++++++++------ l1-contracts/test/merkle/Merkle.t.sol | 101 +++++++++++++++--- l1-contracts/test/merkle/Naive.sol | 21 ---- 8 files changed, 253 insertions(+), 106 deletions(-) create mode 100644 l1-contracts/src/core/libraries/Merkle.sol diff --git a/l1-contracts/foundry.toml b/l1-contracts/foundry.toml index 31c93e28280..ff36d4ac7bf 100644 --- a/l1-contracts/foundry.toml +++ b/l1-contracts/foundry.toml @@ -2,7 +2,7 @@ src = 'src' out = 'out' libs = ['lib'] -solc = "0.8.21" +solc = "0.8.23" remappings = [ "@oz/=lib/openzeppelin-contracts/contracts/" diff --git a/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol b/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol index 653e3db5980..c28cb1136f9 100644 --- a/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol +++ b/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol @@ -14,12 +14,12 @@ import {DataStructures} from "../../libraries/DataStructures.sol"; interface INewOutbox { event RootAdded(uint256 indexed l2BlockNumber, bytes32 indexed root, uint256 height); event MessageConsumed( - uint256 indexed l2BlockNumber, bytes32 indexed root, bytes32 indexed messageHash + uint256 indexed l2BlockNumber, bytes32 indexed root, bytes32 indexed messageHash, uint256 leafIndex ); /* * @notice Inserts the root of a merkle tree containing all of the L2 to L1 messages in - * a block specified by _blockNumber. + * a block specified by _l2BlockNumber. * @dev Only callable by the state transitioner (rollup contract) * @dev Emits `RootAdded` upon inserting the root successfully * @param _l2BlockNumber - The L2 Block Number in which the L2 to L1 messages reside @@ -30,12 +30,14 @@ interface INewOutbox { /* * @notice Consumes an entry from the Outbox - * @dev Only meaningfully callable by portals / recipients of messages + * @dev Only useable by portals / recipients of messages * @dev Emits `MessageConsumed` when consuming messages * @param _l2BlockNumber - The block number specifying the block that contains the message we want to consume * @param _leafIndex - The index inside the merkle tree where the message is located * @param _message - The L2 to L1 message - * @param _path - The sibling path used to prove inclusion of the message + * @param _path - The sibling path used to prove inclusion of the message, the _path length directly depends + * on the total amount of L2 to L1 messages in the block. i.e. the length of _path is equal to the depth of the + * L1 to L2 message tree. */ function consume( uint256 _l2BlockNumber, diff --git a/l1-contracts/src/core/libraries/Errors.sol b/l1-contracts/src/core/libraries/Errors.sol index c90f6c06f7b..dd0b56d54ce 100644 --- a/l1-contracts/src/core/libraries/Errors.sol +++ b/l1-contracts/src/core/libraries/Errors.sol @@ -46,12 +46,12 @@ library Errors { uint32 storedDeadline, uint32 deadlinePassed ); // 0x5e789f34 - error Outbox__InvalidPathLength(uint256 expected, uint256 actual); - error Outbox__InvalidRoot(bytes32 expected, bytes32 actual); - error Outbox__RootAlreadySet(uint256 l2BlockNumber); - error Outbox__InvalidRecipient(address expected, address actual); - error Outbox__AlreadyNullified(uint256 l2BlockNumber, uint256 leafIndex); - error Outbox__NothingToConsumeAtBlock(uint256 l2BlockNumber); + error Outbox__InvalidPathLength(uint256 expected, uint256 actual); // 0x481bcd9c + error Outbox__InsertingInvalidRoot(); // 0x73c2daca + error Outbox__RootAlreadySetAtBlock(uint256 l2BlockNumber); // 0x3eccfd3e + error Outbox__InvalidRecipient(address expected, address actual); // 0x57aad581 + error Outbox__AlreadyNullified(uint256 l2BlockNumber, uint256 leafIndex); // 0xfd71c2d4 + error Outbox__NothingToConsumeAtBlock(uint256 l2BlockNumber); // 0xa4508f22 // Rollup error Rollup__InvalidArchive(bytes32 expected, bytes32 actual); // 0xb682a40e @@ -68,4 +68,7 @@ library Errors { // HeaderLib error HeaderLib__InvalidHeaderSize(uint256 expected, uint256 actual); // 0xf3ccb247 + + // MerkleLib + error MerkleLib__InvalidRoot(bytes32 expected, bytes32 actual); // 0xb77e99 } diff --git a/l1-contracts/src/core/libraries/Merkle.sol b/l1-contracts/src/core/libraries/Merkle.sol new file mode 100644 index 00000000000..dd41447c6f2 --- /dev/null +++ b/l1-contracts/src/core/libraries/Merkle.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 Aztec Labs. +pragma solidity >=0.8.18; + +import {Errors} from "../libraries/Errors.sol"; + +/** + * @title + * @author Aztec Labs + * @notice + */ +library Merkle { + /* + * @notice Verifies the membership of a leaf and path against an expected root. + * @dev In the case of a mismatched root, and subsequent inability to verify membership, this function throws. + * @param _path - The sibling path of the message as a leaf, used to prove message inclusion + * @param _leaf - The hash of the message we are trying to prove inclusion for + * @param _index - The index of the message inside the L2 to L1 message tree + * @param _expectedRoot - The expected root to check the validity of the message and sibling path with. + * @remark - + * E.g. A sibling path for a leaf at index 3 in a tree of depth 3 (between 5 and 8 leafs) consists of the 3 elements denoted as *'s + * d0: [ root ] + * d1: [ ] [*] + * d2: [*] [ ] [ ] [ ] + * d3: [ ] [ ] [*] [ ] [ ] [ ] [ ] [ ]. + * And the elements would be ordered as: [ d3_index_2, d2_index_0, d1_index_1 ]. + * @remark - We use the indexAtHeight to see whether our child of the next subtree is at the left or the right side, as any odd indexes are right-sided children. + * This is important and we have assigned it into its own variable isRight, because this affects the way we concat our two children to then hash and calculate the root. + */ + function _verifyMembership( + bytes32[] memory _path, + bytes32 _leaf, + uint256 _index, + bytes32 _expectedRoot + ) internal pure { + bytes32 subtreeRoot = _leaf; + uint256 indexAtHeight = _index; + + for (uint256 height = 0; height < _path.length; height++) { + bool isRight = (indexAtHeight & 1) == 1; + + subtreeRoot = isRight + ? sha256(bytes.concat(_path[height], subtreeRoot)) + : sha256(bytes.concat(subtreeRoot, _path[height])); + + indexAtHeight /= 2; + } + + if (subtreeRoot != _expectedRoot) { + revert Errors.MerkleLib__InvalidRoot(_expectedRoot, subtreeRoot); + } + } +} diff --git a/l1-contracts/src/core/messagebridge/NewOutbox.sol b/l1-contracts/src/core/messagebridge/NewOutbox.sol index e194fd4351f..17aef510e52 100644 --- a/l1-contracts/src/core/messagebridge/NewOutbox.sol +++ b/l1-contracts/src/core/messagebridge/NewOutbox.sol @@ -5,6 +5,7 @@ pragma solidity >=0.8.18; // Libraries import {DataStructures} from "../libraries/DataStructures.sol"; import {Errors} from "../libraries/Errors.sol"; +import {Merkle} from "../libraries/Merkle.sol"; import {Hash} from "../libraries/Hash.sol"; import {INewOutbox} from "../interfaces/messagebridge/INewOutbox.sol"; @@ -32,7 +33,7 @@ contract NewOutbox is INewOutbox { /* * @notice Inserts the root of a merkle tree containing all of the L2 to L1 messages in - * a block specified by _blockNumber. + * a block specified by _l2BlockNumber. * @dev Only callable by the state transitioner (rollup contract) * @dev Emits `RootAdded` upon inserting the root successfully * @param _l2BlockNumber - The L2 Block Number in which the L2 to L1 messages reside @@ -48,7 +49,11 @@ contract NewOutbox is INewOutbox { } if (roots[_l2BlockNumber].root != bytes32(0)) { - revert Errors.Outbox__RootAlreadySet(_l2BlockNumber); + revert Errors.Outbox__RootAlreadySetAtBlock(_l2BlockNumber); + } + + if (_root == bytes32(0)) { + revert Errors.Outbox__InsertingInvalidRoot(); } roots[_l2BlockNumber].root = _root; @@ -59,19 +64,21 @@ contract NewOutbox is INewOutbox { /* * @notice Consumes an entry from the Outbox - * @dev Only meaningfully callable by portals / recipients of messages + * @dev Only useable by portals / recipients of messages * @dev Emits `MessageConsumed` when consuming messages * @param _l2BlockNumber - The block number specifying the block that contains the message we want to consume * @param _leafIndex - The index inside the merkle tree where the message is located * @param _message - The L2 to L1 message - * @param _path - The sibling path used to prove inclusion of the message + * @param _path - The sibling path used to prove inclusion of the message, the _path length directly depends + * on the total amount of L2 to L1 messages in the block. i.e. the length of _path is equal to the depth of the + * L1 to L2 message tree. */ function consume( uint256 _l2BlockNumber, uint256 _leafIndex, DataStructures.L2ToL1Msg memory _message, bytes32[] memory _path - ) public override(INewOutbox) { + ) external override(INewOutbox) { if (msg.sender != _message.recipient.actor) { revert Errors.Outbox__InvalidRecipient(_message.recipient.actor, msg.sender); } @@ -96,49 +103,45 @@ contract NewOutbox is INewOutbox { revert Errors.Outbox__InvalidPathLength(expectedHeight, _path.length); } - bytes32 messageHash = _verifyMembership(_path, _message, _leafIndex, expectedRoot); + bytes32 messageHash = _message.sha256ToField(); + + Merkle._verifyMembership(_path, messageHash, _leafIndex, expectedRoot); roots[_l2BlockNumber].nullified[_leafIndex] = true; - emit MessageConsumed(_l2BlockNumber, expectedRoot, messageHash); + emit MessageConsumed(_l2BlockNumber, expectedRoot, messageHash, _leafIndex); } /* * @notice Verifies the membership of an L2 to L1 message against an expected root. * @dev This function assumes a valid path height, as well as sane inputs. - * @dev In the case of a mismatched root, and subsequent inability to verify membership, this function throws - * and does not return anything. + * @dev In the case of a mismatched root, and subsequent inability to verify membership, this function throws. * @param _path - The sibling path of the message as a leaf, used to prove message inclusion - * @param _message - The message we are trying to prove inclusion for + * @param _leaf - The hash of the message we are trying to prove inclusion for * @param _index - The index of the message inside the L2 to L1 message tree * @param _expectedRoot - The expected root to check the validity of the message and sibling path with. - * @returns - The leaf of the message in the L2 to L1 tree, which is the L2 to L1 message hashed via SHA256. */ function _verifyMembership( bytes32[] memory _path, - DataStructures.L2ToL1Msg memory _message, + bytes32 _leaf, uint256 _index, bytes32 _expectedRoot - ) internal pure returns (bytes32) { - bytes32 leaf = _message.sha256ToField(); + ) internal pure { + bytes32 subtreeRoot = _leaf; + uint256 indexAtHeight = _index; - bytes32 root; - uint256 index = _index; - - for (uint256 i = 0; i < _path.length; i++) { - bool isRight = (index & 1) == 1; + for (uint256 height = 0; height < _path.length; height++) { + bool isRight = (indexAtHeight & 1) == 1; - root = isRight - ? sha256(bytes.concat(_path[i], i == 0 ? leaf : root)) - : sha256(bytes.concat(i == 0 ? leaf : root, _path[i])); + subtreeRoot = isRight + ? sha256(bytes.concat(_path[height], subtreeRoot)) + : sha256(bytes.concat(subtreeRoot, _path[height])); - index /= 2; + indexAtHeight /= 2; } - if (root != _expectedRoot) { - revert Errors.Outbox__InvalidRoot(_expectedRoot, root); + if (subtreeRoot != _expectedRoot) { + revert Errors.MerkleLib__InvalidRoot(_expectedRoot, subtreeRoot); } - - return leaf; } } diff --git a/l1-contracts/test/NewOutbox.t.sol b/l1-contracts/test/NewOutbox.t.sol index 5d290b8d81f..b69a28e6385 100644 --- a/l1-contracts/test/NewOutbox.t.sol +++ b/l1-contracts/test/NewOutbox.t.sol @@ -4,6 +4,7 @@ pragma solidity >=0.8.18; import {Test} from "forge-std/Test.sol"; import {NewOutbox} from "../src/core/messagebridge/NewOutbox.sol"; +import {INewOutbox} from "../src/core/interfaces/messagebridge/INewOutbox.sol"; import {Errors} from "../src/core/libraries/Errors.sol"; import {DataStructures} from "../src/core/libraries/DataStructures.sol"; import {Hash} from "../src/core/libraries/Hash.sol"; @@ -21,11 +22,6 @@ contract NewOutboxTest is Test { NewOutbox internal outbox; NaiveMerkle internal zeroedTree; - event RootAdded(uint256 indexed l2BlockNumber, bytes32 indexed root, uint256 height); - event MessageConsumed( - uint256 indexed l2BlockNumber, bytes32 indexed root, bytes32 indexed messageHash - ); - function setUp() public { outbox = new NewOutbox(_STATE_TRANSITIONER); zeroedTree = new NaiveMerkle(_DEFAULT_TREE_HEIGHT); @@ -57,10 +53,31 @@ contract NewOutboxTest is Test { outbox.insert(1, root, _DEFAULT_TREE_HEIGHT); vm.prank(_STATE_TRANSITIONER); - vm.expectRevert(abi.encodeWithSelector(Errors.Outbox__RootAlreadySet.selector, 1)); + vm.expectRevert(abi.encodeWithSelector(Errors.Outbox__RootAlreadySetAtBlock.selector, 1)); outbox.insert(1, root, _DEFAULT_TREE_HEIGHT); } + function testInsertVariedLeafs(bytes32[] calldata _messageLeafs) public { + uint256 bigTreeHeight = calculateTreeHeightFromSize(_messageLeafs.length); + NaiveMerkle tree = new NaiveMerkle(bigTreeHeight); + + for (uint256 i = 0; i < _messageLeafs.length; i++) { + vm.assume(_messageLeafs[i] != bytes32(0)); + tree.insertLeaf(_messageLeafs[i]); + } + + bytes32 root = tree.computeRoot(); + + vm.expectEmit(true, true, true, true, address(outbox)); + emit INewOutbox.RootAdded(1, root, bigTreeHeight); + vm.prank(_STATE_TRANSITIONER); + outbox.insert(1, root, bigTreeHeight); + + (bytes32 actualRoot, uint256 actualHeight) = outbox.roots(1); + assertEq(root, actualRoot); + assertEq(bigTreeHeight, actualHeight); + } + function testRevertIfConsumingMessageBelongingToOther() public { DataStructures.L2ToL1Msg memory fakeMessage = _fakeMessage(address(this)); @@ -157,48 +174,40 @@ contract NewOutboxTest is Test { (bytes32[] memory path,) = modifiedTree.computeSiblingPath(0); - vm.expectRevert(abi.encodeWithSelector(Errors.Outbox__InvalidRoot.selector, root, modifiedRoot)); + vm.expectRevert(abi.encodeWithSelector(Errors.MerkleLib__InvalidRoot.selector, root, modifiedRoot)); outbox.consume(1, 0, fakeMessage, path); } - function testInsertVariedLeafs(bytes32[] calldata _messageLeafs) public { - vm.assume(_messageLeafs.length < 256); - vm.assume(_messageLeafs.length > 128); + function testValidInsertAndConsume() public { + DataStructures.L2ToL1Msg memory fakeMessage = _fakeMessage(address(this)); + bytes32 leaf = fakeMessage.sha256ToField(); - // This correct only when fuzzing using _messageLeafs.length 128 < length < 256 - NaiveMerkle tree = new NaiveMerkle(8); + NaiveMerkle tree = new NaiveMerkle(_DEFAULT_TREE_HEIGHT); + tree.insertLeaf(leaf); + bytes32 root = tree.computeRoot(); - for (uint256 i = 0; i < _messageLeafs.length; i++) { - vm.assume(_messageLeafs[i] != bytes32(0)); - tree.insertLeaf(_messageLeafs[i]); - } + vm.prank(_STATE_TRANSITIONER); + outbox.insert(1, root, _DEFAULT_TREE_HEIGHT); - bytes32 root = tree.computeRoot(); + (bytes32[] memory path,) = tree.computeSiblingPath(0); vm.expectEmit(true, true, true, true, address(outbox)); - emit RootAdded(1, root, 8); - vm.prank(_STATE_TRANSITIONER); - outbox.insert(1, root, 8); + emit INewOutbox.MessageConsumed(1, root, leaf, 0); + outbox.consume(1, 0, fakeMessage, path); } - // This test takes awhile so to keep it somewhat reasonable wev'e set a limit on the amount of fuzz runs - /// forge-config: default.fuzz.runs = 16 + // This test takes awhile so to keep it somewhat reasonable we've set a limit on the amount of fuzz runs + /// forge-config: default.fuzz.runs = 32 function testInsertAndConsumeWithVariedRecipients( address[] calldata _recipients, uint256 _blockNumber ) public { - // We most likely will not have > 128 L2 -> L1 Messages in a single block but are testing an upper bound here - vm.assume(_recipients.length < 256); - vm.assume(_recipients.length > 128); DataStructures.L2ToL1Msg[] memory messages = new DataStructures.L2ToL1Msg[](_recipients.length); - // This correct only when fuzzing using _messageLeafs.length 128 < length < 256 hence the assumption above - uint256 bigTreeHeight = 8; - + uint256 bigTreeHeight = calculateTreeHeightFromSize(_recipients.length); NaiveMerkle tree = new NaiveMerkle(bigTreeHeight); for (uint256 i = 0; i < _recipients.length; i++) { - vm.assume(_recipients[i] != address(0)); DataStructures.L2ToL1Msg memory fakeMessage = _fakeMessage(_recipients[i]); messages[i] = fakeMessage; bytes32 modifiedLeaf = fakeMessage.sha256ToField(); @@ -209,7 +218,7 @@ contract NewOutboxTest is Test { bytes32 root = tree.computeRoot(); vm.expectEmit(true, true, true, true, address(outbox)); - emit RootAdded(_blockNumber, root, bigTreeHeight); + emit INewOutbox.RootAdded(_blockNumber, root, bigTreeHeight); vm.prank(_STATE_TRANSITIONER); outbox.insert(_blockNumber, root, bigTreeHeight); @@ -217,9 +226,38 @@ contract NewOutboxTest is Test { (bytes32[] memory path, bytes32 leaf) = tree.computeSiblingPath(i); vm.expectEmit(true, true, true, true, address(outbox)); - emit MessageConsumed(_blockNumber, root, leaf); + emit INewOutbox.MessageConsumed(_blockNumber, root, leaf, i); vm.prank(_recipients[i]); outbox.consume(_blockNumber, i, messages[i], path); } } + + function calculateTreeHeightFromSize(uint256 _number) internal pure returns (uint256) { + if (_number == 1) { + return 1; + } + + uint256 originalNumber = _number; + + uint256 height = 0; + while (_number > 1) { + _number >>= 1; + height++; + } + + return 2 ** height != originalNumber ? ++height : height; + } + + function testCalculateNextHighestPowerOfTwo() external { + assertEq(calculateTreeHeightFromSize(0), 1); + assertEq(calculateTreeHeightFromSize(1), 1); + assertEq(calculateTreeHeightFromSize(2), 1); + assertEq(calculateTreeHeightFromSize(3), 2); + assertEq(calculateTreeHeightFromSize(4), 2); + assertEq(calculateTreeHeightFromSize(5), 3); + assertEq(calculateTreeHeightFromSize(6), 3); + assertEq(calculateTreeHeightFromSize(7), 3); + assertEq(calculateTreeHeightFromSize(8), 3); + assertEq(calculateTreeHeightFromSize(9), 4); + } } diff --git a/l1-contracts/test/merkle/Merkle.t.sol b/l1-contracts/test/merkle/Merkle.t.sol index 09d55c2bb99..b4c4d33a471 100644 --- a/l1-contracts/test/merkle/Merkle.t.sol +++ b/l1-contracts/test/merkle/Merkle.t.sol @@ -5,17 +5,41 @@ pragma solidity >=0.8.18; import {Test} from "forge-std/Test.sol"; import {NaiveMerkle} from "./Naive.sol"; +import {Merkle} from "../../src/core/libraries/Merkle.sol"; +import {Errors} from "../../src/core/libraries/Errors.sol"; import {FrontierMerkle} from "./../../src/core/messagebridge/frontier_tree/Frontier.sol"; -contract MerkleTest is Test { +abstract contract MerkleLibWrapper { + function _verifyMembershipWrapper( + bytes32[] memory _path, + bytes32 _leaf, + uint256 _index, + bytes32 _expectedRoot + ) external pure { + Merkle._verifyMembership(_path, _leaf, _index, _expectedRoot); + } +} + +contract MerkleTest is Test, MerkleLibWrapper { NaiveMerkle internal merkle; FrontierMerkle internal frontier; + NaiveMerkle internal testNaiveMerkle; + uint256 public constant DEPTH = 10; function setUp() public { - merkle = new NaiveMerkle(DEPTH); + // Set up testFrontier frontier = new FrontierMerkle(DEPTH); + merkle = new NaiveMerkle(DEPTH); + + // Set up testNative + testNaiveMerkle = new NaiveMerkle(DEPTH); + uint256 treeSize = testNaiveMerkle.SIZE(); + for (uint256 i = 0; i < treeSize; i++) { + bytes32 generatedLeaf = sha256(abi.encode(i + 1)); + testNaiveMerkle.insertLeaf(generatedLeaf); + } } function testFrontier() public { @@ -28,27 +52,72 @@ contract MerkleTest is Test { } } - function testNaive(uint256 _idx) public { - uint256 upper = merkle.SIZE(); + function testNaiveSiblingPathAndMembershipVerification(uint256 _idx) public view { + uint256 leafIndex = bound(_idx, 0, merkle.SIZE() - 1); - // function for bounding the test env - uint256 idx = bound(_idx, 0, upper - 1); + (bytes32[] memory path, bytes32 leaf) = testNaiveMerkle.computeSiblingPath(leafIndex); - for (uint256 i = 0; i < upper; i++) { - bytes32 leaf = sha256(abi.encode(i + 1)); - merkle.insertLeaf(leaf); + Merkle._verifyMembership(path, leaf, leafIndex, testNaiveMerkle.computeRoot()); + } + + function testNaiveBadSiblingPathAndMembershipVerification(uint256 _idx) public { + uint256 leafIndex = bound(_idx, 0, merkle.SIZE() - 1); + bytes32 expectedRoot = testNaiveMerkle.computeRoot(); + + // Tests garbled path + (bytes32[] memory path1, bytes32 leaf) = testNaiveMerkle.computeSiblingPath(leafIndex); + bytes32 temp1 = path1[0]; + path1[0] = path1[path1.length - 1]; + path1[path1.length - 1] = temp1; + vm.expectRevert(); + this._verifyMembershipWrapper(path1, leaf, leafIndex, expectedRoot); + + // Tests truncated path + (bytes32[] memory path2,) = testNaiveMerkle.computeSiblingPath(leafIndex); + bytes32[] memory truncatedPath = new bytes32[](path2.length - 1); + for (uint256 i = 0; i < truncatedPath.length; i++) { + truncatedPath[i] = path2[i]; } - (bytes32[] memory path, bytes32 leaf) = merkle.computeSiblingPath(idx); + vm.expectRevert(); + this._verifyMembershipWrapper(truncatedPath, leaf, leafIndex, expectedRoot); - for (uint256 i = 0; i < merkle.DEPTH(); i++) { - emit log_named_bytes32("path", path[i]); + // Tests empty path + bytes32[] memory emptyPath = new bytes32[](0); + vm.expectRevert(); + this._verifyMembershipWrapper(emptyPath, leaf, leafIndex, expectedRoot); + } + + function testComputeSiblingPathManually() public { + NaiveMerkle manualTree = new NaiveMerkle(3); + + for (uint256 i = 1; i <= 8; i++) { + bytes32 generatedLeaf = bytes32(abi.encode(i)); + manualTree.insertLeaf(generatedLeaf); } - emit log_named_bytes32("test", leaf); - emit log_named_bytes32("size", bytes32(merkle.SIZE())); - emit log_named_bytes32("root", merkle.computeRoot()); + bytes32[3] memory expectedPath1 = [ + bytes32(abi.encode(2)), + sha256(bytes.concat(bytes32(abi.encode(3)), bytes32(abi.encode(4)))), + sha256(bytes.concat(sha256(bytes.concat(bytes32(abi.encode(5)), bytes32(abi.encode(6)))), sha256(bytes.concat(bytes32(abi.encode(7)), bytes32(abi.encode(8)))))) + ]; + + (bytes32[] memory path1, bytes32 leaf1) = manualTree.computeSiblingPath(0); + assertEq(leaf1, bytes32(abi.encode(1))); + assertEq(path1[0], expectedPath1[0]); + assertEq(path1[1], expectedPath1[1]); + assertEq(path1[2], expectedPath1[2]); + + bytes32[3] memory expectedPath2 = [ + bytes32(abi.encode(7)), + sha256(bytes.concat(bytes32(abi.encode(5)), bytes32(abi.encode(6)))), + sha256(bytes.concat(sha256(bytes.concat(bytes32(abi.encode(1)), bytes32(abi.encode(2)))), sha256(bytes.concat(bytes32(abi.encode(3)), bytes32(abi.encode(4)))))) + ]; - assertTrue(merkle.verifyMembership(path, leaf, idx)); + (bytes32[] memory path2, bytes32 leaf2) = manualTree.computeSiblingPath(7); + assertEq(leaf2, bytes32(abi.encode(8))); + assertEq(path2[0], expectedPath2[0]); + assertEq(path2[1], expectedPath2[1]); + assertEq(path2[2], expectedPath2[2]); } } diff --git a/l1-contracts/test/merkle/Naive.sol b/l1-contracts/test/merkle/Naive.sol index 7ed7f62a1bc..e564c2d6db6 100644 --- a/l1-contracts/test/merkle/Naive.sol +++ b/l1-contracts/test/merkle/Naive.sol @@ -64,25 +64,4 @@ contract NaiveMerkle { return (path, leafs[_index]); } - - function verifyMembership(bytes32[] memory _path, bytes32 _leaf, uint256 _index) - public - view - returns (bool) - { - bytes32 root; - uint256 index = _index; - - for (uint256 i = 0; i < _path.length; i++) { - bool isRight = (index & 1) == 1; - - root = isRight - ? sha256(bytes.concat(_path[i], i == 0 ? _leaf : root)) - : sha256(bytes.concat(i == 0 ? _leaf : root, _path[i])); - - index /= 2; - } - - return computeRoot() == root; - } } From 02f6fcd0e3735dc8394081c81609dd18db6c0839 Mon Sep 17 00:00:00 2001 From: Esau Date: Mon, 11 Mar 2024 13:10:28 +0100 Subject: [PATCH 12/62] slither --- l1-contracts/slither_output.md | 205 ++++++++++++++++----------------- 1 file changed, 101 insertions(+), 104 deletions(-) diff --git a/l1-contracts/slither_output.md b/l1-contracts/slither_output.md index f27ddef336d..1751098c481 100644 --- a/l1-contracts/slither_output.md +++ b/l1-contracts/slither_output.md @@ -1,6 +1,6 @@ Summary - [pess-unprotected-setter](#pess-unprotected-setter) (1 results) (High) - - [uninitialized-local](#uninitialized-local) (3 results) (Medium) + - [uninitialized-local](#uninitialized-local) (2 results) (Medium) - [unused-return](#unused-return) (2 results) (Medium) - [pess-dubious-typecast](#pess-dubious-typecast) (8 results) (Medium) - [missing-zero-check](#missing-zero-check) (2 results) (Low) @@ -8,7 +8,7 @@ Summary - [timestamp](#timestamp) (4 results) (Low) - [pess-public-vs-external](#pess-public-vs-external) (7 results) (Low) - [assembly](#assembly) (2 results) (Informational) - - [dead-code](#dead-code) (5 results) (Informational) + - [dead-code](#dead-code) (6 results) (Informational) - [solc-version](#solc-version) (1 results) (Informational) - [low-level-calls](#low-level-calls) (1 results) (Informational) - [similar-names](#similar-names) (4 results) (Informational) @@ -27,33 +27,27 @@ src/core/Rollup.sol#L58-L103 Impact: Medium Confidence: Medium - [ ] ID-1 -[HeaderLib.decode(bytes).header](src/core/libraries/HeaderLib.sol#L150) is a local variable never initialized +[HeaderLib.decode(bytes).header](src/core/libraries/HeaderLib.sol#L148) is a local variable never initialized -src/core/libraries/HeaderLib.sol#L150 +src/core/libraries/HeaderLib.sol#L148 - [ ] ID-2 -[TxsDecoder.decode(bytes).vars](src/core/libraries/decoders/TxsDecoder.sol#L86) is a local variable never initialized +[TxsDecoder.decode(bytes).vars](src/core/libraries/decoders/TxsDecoder.sol#L79) is a local variable never initialized -src/core/libraries/decoders/TxsDecoder.sol#L86 - - - - [ ] ID-3 -[NewOutbox._verifyMembership(bytes32[],DataStructures.L2ToL1Msg,uint256,bytes32).root](src/core/messagebridge/NewOutbox.sol#L125) is a local variable never initialized - -src/core/messagebridge/NewOutbox.sol#L125 +src/core/libraries/decoders/TxsDecoder.sol#L79 ## unused-return Impact: Medium Confidence: Medium - - [ ] ID-4 + - [ ] ID-3 [Rollup.process(bytes,bytes32,bytes,bytes)](src/core/Rollup.sol#L58-L103) ignores return value by [NEW_INBOX.consume()](src/core/Rollup.sol#L93) src/core/Rollup.sol#L58-L103 - - [ ] ID-5 + - [ ] ID-4 [Rollup.process(bytes,bytes32,bytes,bytes)](src/core/Rollup.sol#L58-L103) ignores return value by [(l1ToL2Msgs,l2ToL1Msgs) = MessagesDecoder.decode(_body)](src/core/Rollup.sol#L74) src/core/Rollup.sol#L58-L103 @@ -62,28 +56,21 @@ src/core/Rollup.sol#L58-L103 ## pess-dubious-typecast Impact: Medium Confidence: High - - [ ] ID-6 -Dubious typecast in [TxsDecoder.read4(bytes,uint256)](src/core/libraries/decoders/TxsDecoder.sol#L359-L361): - bytes => bytes4 casting occurs in [uint256(uint32(bytes4(slice(_data,_offset,4))))](src/core/libraries/decoders/TxsDecoder.sol#L360) - -src/core/libraries/decoders/TxsDecoder.sol#L359-L361 - - - - [ ] ID-7 -Dubious typecast in [MessagesDecoder.read1(bytes,uint256)](src/core/libraries/decoders/MessagesDecoder.sol#L158-L160): - bytes => bytes1 casting occurs in [uint256(uint8(bytes1(_data)))](src/core/libraries/decoders/MessagesDecoder.sol#L159) + - [ ] ID-5 +Dubious typecast in [TxsDecoder.read1(bytes,uint256)](src/core/libraries/decoders/TxsDecoder.sol#L314-L316): + bytes => bytes1 casting occurs in [uint256(uint8(bytes1(slice(_data,_offset,1))))](src/core/libraries/decoders/TxsDecoder.sol#L315) -src/core/libraries/decoders/MessagesDecoder.sol#L158-L160 +src/core/libraries/decoders/TxsDecoder.sol#L314-L316 - - [ ] ID-8 + - [ ] ID-6 Dubious typecast in [Outbox.sendL1Messages(bytes32[])](src/core/messagebridge/Outbox.sol#L38-L46): uint256 => uint32 casting occurs in [version = uint32(REGISTRY.getVersionFor(msg.sender))](src/core/messagebridge/Outbox.sol#L40) src/core/messagebridge/Outbox.sol#L38-L46 - - [ ] ID-9 + - [ ] ID-7 Dubious typecast in [Inbox.sendL2Message(DataStructures.L2Actor,uint32,bytes32,bytes32)](src/core/messagebridge/Inbox.sol#L45-L91): uint256 => uint64 casting occurs in [fee = uint64(msg.value)](src/core/messagebridge/Inbox.sol#L64) uint256 => uint32 casting occurs in [entries.insert(key,fee,uint32(_recipient.version),_deadline,_errIncompatibleEntryArguments)](src/core/messagebridge/Inbox.sol#L76) @@ -91,76 +78,81 @@ Dubious typecast in [Inbox.sendL2Message(DataStructures.L2Actor,uint32,bytes32,b src/core/messagebridge/Inbox.sol#L45-L91 - - [ ] ID-10 -Dubious typecast in [TxsDecoder.read1(bytes,uint256)](src/core/libraries/decoders/TxsDecoder.sol#L349-L351): - bytes => bytes1 casting occurs in [uint256(uint8(bytes1(slice(_data,_offset,1))))](src/core/libraries/decoders/TxsDecoder.sol#L350) + - [ ] ID-8 +Dubious typecast in [TxsDecoder.read4(bytes,uint256)](src/core/libraries/decoders/TxsDecoder.sol#L324-L326): + bytes => bytes4 casting occurs in [uint256(uint32(bytes4(slice(_data,_offset,4))))](src/core/libraries/decoders/TxsDecoder.sol#L325) -src/core/libraries/decoders/TxsDecoder.sol#L349-L351 +src/core/libraries/decoders/TxsDecoder.sol#L324-L326 - - [ ] ID-11 -Dubious typecast in [MessagesDecoder.read4(bytes,uint256)](src/core/libraries/decoders/MessagesDecoder.sol#L168-L170): - bytes => bytes4 casting occurs in [uint256(uint32(bytes4(_data)))](src/core/libraries/decoders/MessagesDecoder.sol#L169) + - [ ] ID-9 +Dubious typecast in [MessagesDecoder.read4(bytes,uint256)](src/core/libraries/decoders/MessagesDecoder.sol#L160-L162): + bytes => bytes4 casting occurs in [uint256(uint32(bytes4(_data)))](src/core/libraries/decoders/MessagesDecoder.sol#L161) -src/core/libraries/decoders/MessagesDecoder.sol#L168-L170 +src/core/libraries/decoders/MessagesDecoder.sol#L160-L162 - - [ ] ID-12 + - [ ] ID-10 Dubious typecast in [Inbox.batchConsume(bytes32[],address)](src/core/messagebridge/Inbox.sol#L122-L143): uint256 => uint32 casting occurs in [expectedVersion = uint32(REGISTRY.getVersionFor(msg.sender))](src/core/messagebridge/Inbox.sol#L128) src/core/messagebridge/Inbox.sol#L122-L143 - - [ ] ID-13 -Dubious typecast in [HeaderLib.decode(bytes)](src/core/libraries/HeaderLib.sol#L145-L189): - bytes => bytes32 casting occurs in [header.lastArchive = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L153-L155) - bytes => bytes4 casting occurs in [header.lastArchive = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L153-L155) - bytes => bytes32 casting occurs in [header.contentCommitment.txTreeHeight = uint256(bytes32(_header))](src/core/libraries/HeaderLib.sol#L158) - bytes => bytes32 casting occurs in [header.contentCommitment.txsEffectsHash = bytes32(_header)](src/core/libraries/HeaderLib.sol#L159) - bytes => bytes32 casting occurs in [header.contentCommitment.inHash = bytes32(_header)](src/core/libraries/HeaderLib.sol#L160) - bytes => bytes32 casting occurs in [header.contentCommitment.outHash = bytes32(_header)](src/core/libraries/HeaderLib.sol#L161) - bytes => bytes32 casting occurs in [header.stateReference.l1ToL2MessageTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L164-L166) - bytes => bytes4 casting occurs in [header.stateReference.l1ToL2MessageTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L164-L166) - bytes => bytes32 casting occurs in [header.stateReference.partialStateReference.noteHashTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L167-L169) - bytes => bytes4 casting occurs in [header.stateReference.partialStateReference.noteHashTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L167-L169) - bytes => bytes32 casting occurs in [header.stateReference.partialStateReference.nullifierTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L170-L172) - bytes => bytes4 casting occurs in [header.stateReference.partialStateReference.nullifierTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L170-L172) - bytes => bytes32 casting occurs in [header.stateReference.partialStateReference.contractTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L173-L175) - bytes => bytes4 casting occurs in [header.stateReference.partialStateReference.contractTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L173-L175) - bytes => bytes32 casting occurs in [header.stateReference.partialStateReference.publicDataTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L176-L178) - bytes => bytes4 casting occurs in [header.stateReference.partialStateReference.publicDataTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L176-L178) - bytes => bytes32 casting occurs in [header.globalVariables.chainId = uint256(bytes32(_header))](src/core/libraries/HeaderLib.sol#L181) - bytes => bytes32 casting occurs in [header.globalVariables.version = uint256(bytes32(_header))](src/core/libraries/HeaderLib.sol#L182) - bytes => bytes32 casting occurs in [header.globalVariables.blockNumber = uint256(bytes32(_header))](src/core/libraries/HeaderLib.sol#L183) - bytes => bytes32 casting occurs in [header.globalVariables.timestamp = uint256(bytes32(_header))](src/core/libraries/HeaderLib.sol#L184) - bytes => bytes20 casting occurs in [header.globalVariables.coinbase = address(bytes20(_header))](src/core/libraries/HeaderLib.sol#L185) - bytes => bytes32 casting occurs in [header.globalVariables.feeRecipient = bytes32(_header)](src/core/libraries/HeaderLib.sol#L186) - -src/core/libraries/HeaderLib.sol#L145-L189 + - [ ] ID-11 +Dubious typecast in [HeaderLib.decode(bytes)](src/core/libraries/HeaderLib.sol#L143-L184): + bytes => bytes32 casting occurs in [header.lastArchive = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L151-L153) + bytes => bytes4 casting occurs in [header.lastArchive = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L151-L153) + bytes => bytes32 casting occurs in [header.contentCommitment.txTreeHeight = uint256(bytes32(_header))](src/core/libraries/HeaderLib.sol#L156) + bytes => bytes32 casting occurs in [header.contentCommitment.txsEffectsHash = bytes32(_header)](src/core/libraries/HeaderLib.sol#L157) + bytes => bytes32 casting occurs in [header.contentCommitment.inHash = bytes32(_header)](src/core/libraries/HeaderLib.sol#L158) + bytes => bytes32 casting occurs in [header.contentCommitment.outHash = bytes32(_header)](src/core/libraries/HeaderLib.sol#L159) + bytes => bytes32 casting occurs in [header.stateReference.l1ToL2MessageTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L162-L164) + bytes => bytes4 casting occurs in [header.stateReference.l1ToL2MessageTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L162-L164) + bytes => bytes32 casting occurs in [header.stateReference.partialStateReference.noteHashTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L165-L167) + bytes => bytes4 casting occurs in [header.stateReference.partialStateReference.noteHashTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L165-L167) + bytes => bytes32 casting occurs in [header.stateReference.partialStateReference.nullifierTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L168-L170) + bytes => bytes4 casting occurs in [header.stateReference.partialStateReference.nullifierTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L168-L170) + bytes => bytes32 casting occurs in [header.stateReference.partialStateReference.publicDataTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L171-L173) + bytes => bytes4 casting occurs in [header.stateReference.partialStateReference.publicDataTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L171-L173) + bytes => bytes32 casting occurs in [header.globalVariables.chainId = uint256(bytes32(_header))](src/core/libraries/HeaderLib.sol#L176) + bytes => bytes32 casting occurs in [header.globalVariables.version = uint256(bytes32(_header))](src/core/libraries/HeaderLib.sol#L177) + bytes => bytes32 casting occurs in [header.globalVariables.blockNumber = uint256(bytes32(_header))](src/core/libraries/HeaderLib.sol#L178) + bytes => bytes32 casting occurs in [header.globalVariables.timestamp = uint256(bytes32(_header))](src/core/libraries/HeaderLib.sol#L179) + bytes => bytes20 casting occurs in [header.globalVariables.coinbase = address(bytes20(_header))](src/core/libraries/HeaderLib.sol#L180) + bytes => bytes32 casting occurs in [header.globalVariables.feeRecipient = bytes32(_header)](src/core/libraries/HeaderLib.sol#L181) + +src/core/libraries/HeaderLib.sol#L143-L184 + + + - [ ] ID-12 +Dubious typecast in [MessagesDecoder.read1(bytes,uint256)](src/core/libraries/decoders/MessagesDecoder.sol#L150-L152): + bytes => bytes1 casting occurs in [uint256(uint8(bytes1(_data)))](src/core/libraries/decoders/MessagesDecoder.sol#L151) + +src/core/libraries/decoders/MessagesDecoder.sol#L150-L152 ## missing-zero-check Impact: Low Confidence: Medium - - [ ] ID-14 + - [ ] ID-13 [NewInbox.constructor(address,uint256)._rollup](src/core/messagebridge/NewInbox.sol#L41) lacks a zero-check on : - [ROLLUP = _rollup](src/core/messagebridge/NewInbox.sol#L42) src/core/messagebridge/NewInbox.sol#L41 - - [ ] ID-15 -[NewOutbox.constructor(address)._stateTransitioner](src/core/messagebridge/NewOutbox.sol#L29) lacks a zero-check on : - - [STATE_TRANSITIONER = _stateTransitioner](src/core/messagebridge/NewOutbox.sol#L30) + - [ ] ID-14 +[NewOutbox.constructor(address)._stateTransitioner](src/core/messagebridge/NewOutbox.sol#L30) lacks a zero-check on : + - [STATE_TRANSITIONER = _stateTransitioner](src/core/messagebridge/NewOutbox.sol#L31) -src/core/messagebridge/NewOutbox.sol#L29 +src/core/messagebridge/NewOutbox.sol#L30 ## reentrancy-events Impact: Low Confidence: Medium - - [ ] ID-16 + - [ ] ID-15 Reentrancy in [NewInbox.sendL2Message(DataStructures.L2Actor,bytes32,bytes32)](src/core/messagebridge/NewInbox.sol#L62-L99): External calls: - [index = currentTree.insertLeaf(leaf)](src/core/messagebridge/NewInbox.sol#L95) @@ -170,7 +162,7 @@ Reentrancy in [NewInbox.sendL2Message(DataStructures.L2Actor,bytes32,bytes32)](s src/core/messagebridge/NewInbox.sol#L62-L99 - - [ ] ID-17 + - [ ] ID-16 Reentrancy in [Rollup.process(bytes,bytes32,bytes,bytes)](src/core/Rollup.sol#L58-L103): External calls: - [inbox.batchConsume(l1ToL2Msgs,msg.sender)](src/core/Rollup.sol#L90) @@ -185,7 +177,7 @@ src/core/Rollup.sol#L58-L103 ## timestamp Impact: Low Confidence: Medium - - [ ] ID-18 + - [ ] ID-17 [Inbox.batchConsume(bytes32[],address)](src/core/messagebridge/Inbox.sol#L122-L143) uses timestamp for comparisons Dangerous comparisons: - [block.timestamp > entry.deadline](src/core/messagebridge/Inbox.sol#L136) @@ -193,15 +185,15 @@ Confidence: Medium src/core/messagebridge/Inbox.sol#L122-L143 - - [ ] ID-19 -[HeaderLib.validate(HeaderLib.Header,uint256,uint256,bytes32)](src/core/libraries/HeaderLib.sol#L108-L138) uses timestamp for comparisons + - [ ] ID-18 +[HeaderLib.validate(HeaderLib.Header,uint256,uint256,bytes32)](src/core/libraries/HeaderLib.sol#L106-L136) uses timestamp for comparisons Dangerous comparisons: - - [_header.globalVariables.timestamp > block.timestamp](src/core/libraries/HeaderLib.sol#L122) + - [_header.globalVariables.timestamp > block.timestamp](src/core/libraries/HeaderLib.sol#L120) -src/core/libraries/HeaderLib.sol#L108-L138 +src/core/libraries/HeaderLib.sol#L106-L136 - - [ ] ID-20 + - [ ] ID-19 [Inbox.sendL2Message(DataStructures.L2Actor,uint32,bytes32,bytes32)](src/core/messagebridge/Inbox.sol#L45-L91) uses timestamp for comparisons Dangerous comparisons: - [_deadline <= block.timestamp](src/core/messagebridge/Inbox.sol#L54) @@ -209,7 +201,7 @@ src/core/libraries/HeaderLib.sol#L108-L138 src/core/messagebridge/Inbox.sol#L45-L91 - - [ ] ID-21 + - [ ] ID-20 [Inbox.cancelL2Message(DataStructures.L1ToL2Msg,address)](src/core/messagebridge/Inbox.sol#L102-L113) uses timestamp for comparisons Dangerous comparisons: - [block.timestamp <= _message.deadline](src/core/messagebridge/Inbox.sol#L108) @@ -220,36 +212,28 @@ src/core/messagebridge/Inbox.sol#L102-L113 ## pess-public-vs-external Impact: Low Confidence: Medium - - [ ] ID-22 + - [ ] ID-21 The following public functions could be turned into external in [FrontierMerkle](src/core/messagebridge/frontier_tree/Frontier.sol#L7-L93) contract: [FrontierMerkle.constructor(uint256)](src/core/messagebridge/frontier_tree/Frontier.sol#L19-L27) src/core/messagebridge/frontier_tree/Frontier.sol#L7-L93 - - [ ] ID-23 + - [ ] ID-22 The following public functions could be turned into external in [Registry](src/core/messagebridge/Registry.sol#L22-L129) contract: [Registry.constructor()](src/core/messagebridge/Registry.sol#L29-L33) src/core/messagebridge/Registry.sol#L22-L129 - - [ ] ID-24 -The following public functions could be turned into external in [NewOutbox](src/core/messagebridge/NewOutbox.sol#L17-L144) contract: - [NewOutbox.constructor(address)](src/core/messagebridge/NewOutbox.sol#L29-L31) - [NewOutbox.consume(uint256,uint256,DataStructures.L2ToL1Msg,bytes32[])](src/core/messagebridge/NewOutbox.sol#L69-L104) - -src/core/messagebridge/NewOutbox.sol#L17-L144 - - - - [ ] ID-25 + - [ ] ID-23 The following public functions could be turned into external in [Rollup](src/core/Rollup.sol#L30-L112) contract: [Rollup.constructor(IRegistry,IAvailabilityOracle)](src/core/Rollup.sol#L43-L49) src/core/Rollup.sol#L30-L112 - - [ ] ID-26 + - [ ] ID-24 The following public functions could be turned into external in [Outbox](src/core/messagebridge/Outbox.sol#L21-L148) contract: [Outbox.constructor(address)](src/core/messagebridge/Outbox.sol#L29-L31) [Outbox.get(bytes32)](src/core/messagebridge/Outbox.sol#L77-L84) @@ -258,7 +242,7 @@ The following public functions could be turned into external in [Outbox](src/cor src/core/messagebridge/Outbox.sol#L21-L148 - - [ ] ID-27 + - [ ] ID-25 The following public functions could be turned into external in [Inbox](src/core/messagebridge/Inbox.sol#L21-L231) contract: [Inbox.constructor(address)](src/core/messagebridge/Inbox.sol#L30-L32) [Inbox.contains(bytes32)](src/core/messagebridge/Inbox.sol#L174-L176) @@ -266,7 +250,14 @@ The following public functions could be turned into external in [Inbox](src/core src/core/messagebridge/Inbox.sol#L21-L231 - - [ ] ID-28 + - [ ] ID-26 +The following public functions could be turned into external in [NewOutbox](src/core/messagebridge/NewOutbox.sol#L18-L147) contract: + [NewOutbox.constructor(address)](src/core/messagebridge/NewOutbox.sol#L30-L32) + +src/core/messagebridge/NewOutbox.sol#L18-L147 + + + - [ ] ID-27 The following public functions could be turned into external in [NewInbox](src/core/messagebridge/NewInbox.sol#L25-L128) contract: [NewInbox.constructor(address,uint256)](src/core/messagebridge/NewInbox.sol#L41-L52) @@ -276,24 +267,30 @@ src/core/messagebridge/NewInbox.sol#L25-L128 ## assembly Impact: Informational Confidence: High - - [ ] ID-29 -[MessagesDecoder.decode(bytes)](src/core/libraries/decoders/MessagesDecoder.sol#L60-L150) uses assembly + - [ ] ID-28 +[MessagesDecoder.decode(bytes)](src/core/libraries/decoders/MessagesDecoder.sol#L60-L142) uses assembly - [INLINE ASM](src/core/libraries/decoders/MessagesDecoder.sol#L79-L81) - [INLINE ASM](src/core/libraries/decoders/MessagesDecoder.sol#L112-L118) -src/core/libraries/decoders/MessagesDecoder.sol#L60-L150 +src/core/libraries/decoders/MessagesDecoder.sol#L60-L142 - - [ ] ID-30 -[TxsDecoder.computeRoot(bytes32[])](src/core/libraries/decoders/TxsDecoder.sol#L291-L310) uses assembly - - [INLINE ASM](src/core/libraries/decoders/TxsDecoder.sol#L298-L300) + - [ ] ID-29 +[TxsDecoder.computeRoot(bytes32[])](src/core/libraries/decoders/TxsDecoder.sol#L256-L275) uses assembly + - [INLINE ASM](src/core/libraries/decoders/TxsDecoder.sol#L263-L265) -src/core/libraries/decoders/TxsDecoder.sol#L291-L310 +src/core/libraries/decoders/TxsDecoder.sol#L256-L275 ## dead-code Impact: Informational Confidence: Medium + - [ ] ID-30 +[NewOutbox._verifyMembership(bytes32[],bytes32,uint256,bytes32)](src/core/messagebridge/NewOutbox.sol#L124-L146) is never used and should be removed + +src/core/messagebridge/NewOutbox.sol#L124-L146 + + - [ ] ID-31 [Inbox._errIncompatibleEntryArguments(bytes32,uint64,uint64,uint32,uint32,uint32,uint32)](src/core/messagebridge/Inbox.sol#L212-L230) is never used and should be removed @@ -328,7 +325,7 @@ src/core/messagebridge/Outbox.sol#L129-L147 Impact: Informational Confidence: High - [ ] ID-36 -solc-0.8.21 is not recommended for deployment +solc-0.8.23 is not recommended for deployment ## low-level-calls Impact: Informational @@ -356,9 +353,9 @@ src/core/libraries/ConstantsGen.sol#L109 - [ ] ID-40 -Variable [NewOutbox.STATE_TRANSITIONER](src/core/messagebridge/NewOutbox.sol#L26) is too similar to [NewOutbox.constructor(address)._stateTransitioner](src/core/messagebridge/NewOutbox.sol#L29) +Variable [NewOutbox.STATE_TRANSITIONER](src/core/messagebridge/NewOutbox.sol#L27) is too similar to [NewOutbox.constructor(address)._stateTransitioner](src/core/messagebridge/NewOutbox.sol#L30) -src/core/messagebridge/NewOutbox.sol#L26 +src/core/messagebridge/NewOutbox.sol#L27 - [ ] ID-41 @@ -380,9 +377,9 @@ src/core/Rollup.sol#L41 Impact: Optimization Confidence: High - [ ] ID-43 -In a function [NewOutbox.insert(uint256,bytes32,uint256)](src/core/messagebridge/NewOutbox.sol#L42-L58) variable [NewOutbox.roots](src/core/messagebridge/NewOutbox.sol#L27) is read multiple times +In a function [NewOutbox.insert(uint256,bytes32,uint256)](src/core/messagebridge/NewOutbox.sol#L43-L63) variable [NewOutbox.roots](src/core/messagebridge/NewOutbox.sol#L28) is read multiple times -src/core/messagebridge/NewOutbox.sol#L42-L58 +src/core/messagebridge/NewOutbox.sol#L43-L63 - [ ] ID-44 @@ -410,9 +407,9 @@ src/core/messagebridge/NewInbox.sol#L108-L127 - [ ] ID-48 -In a function [NewOutbox.consume(uint256,uint256,DataStructures.L2ToL1Msg,bytes32[])](src/core/messagebridge/NewOutbox.sol#L69-L104) variable [NewOutbox.roots](src/core/messagebridge/NewOutbox.sol#L27) is read multiple times +In a function [NewOutbox.consume(uint256,uint256,DataStructures.L2ToL1Msg,bytes32[])](src/core/messagebridge/NewOutbox.sol#L76-L113) variable [NewOutbox.roots](src/core/messagebridge/NewOutbox.sol#L28) is read multiple times -src/core/messagebridge/NewOutbox.sol#L69-L104 +src/core/messagebridge/NewOutbox.sol#L76-L113 - [ ] ID-49 From e964b7db02d14d283940ed08cf4a03ca9b029020 Mon Sep 17 00:00:00 2001 From: Esau Date: Mon, 11 Mar 2024 13:14:03 +0100 Subject: [PATCH 13/62] fix --- l1-contracts/src/core/messagebridge/NewOutbox.sol | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/l1-contracts/src/core/messagebridge/NewOutbox.sol b/l1-contracts/src/core/messagebridge/NewOutbox.sol index 17aef510e52..d032723614b 100644 --- a/l1-contracts/src/core/messagebridge/NewOutbox.sol +++ b/l1-contracts/src/core/messagebridge/NewOutbox.sol @@ -87,17 +87,19 @@ contract NewOutbox is INewOutbox { revert Errors.Outbox__InvalidChainId(); } - bytes32 expectedRoot = roots[_l2BlockNumber].root; + RootData storage rootData = roots[_l2BlockNumber]; + + bytes32 expectedRoot = rootData.root; if (expectedRoot == 0) { revert Errors.Outbox__NothingToConsumeAtBlock(_l2BlockNumber); } - if (roots[_l2BlockNumber].nullified[_leafIndex]) { + if (rootData.nullified[_leafIndex]) { revert Errors.Outbox__AlreadyNullified(_l2BlockNumber, _leafIndex); } - uint256 expectedHeight = roots[_l2BlockNumber].height; + uint256 expectedHeight = rootData.height; if (expectedHeight != _path.length) { revert Errors.Outbox__InvalidPathLength(expectedHeight, _path.length); @@ -107,7 +109,7 @@ contract NewOutbox is INewOutbox { Merkle._verifyMembership(_path, messageHash, _leafIndex, expectedRoot); - roots[_l2BlockNumber].nullified[_leafIndex] = true; + rootData.nullified[_leafIndex] = true; emit MessageConsumed(_l2BlockNumber, expectedRoot, messageHash, _leafIndex); } From 66575879aff816c12c5aa15c38d1d2c4bb07fdbc Mon Sep 17 00:00:00 2001 From: Esau Date: Mon, 11 Mar 2024 13:21:00 +0100 Subject: [PATCH 14/62] use 8.22 --- l1-contracts/foundry.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l1-contracts/foundry.toml b/l1-contracts/foundry.toml index ff36d4ac7bf..b51ddc4ac76 100644 --- a/l1-contracts/foundry.toml +++ b/l1-contracts/foundry.toml @@ -2,7 +2,7 @@ src = 'src' out = 'out' libs = ['lib'] -solc = "0.8.23" +solc = "0.8.22" remappings = [ "@oz/=lib/openzeppelin-contracts/contracts/" From 622375c14c5c9d20d2ccebeed3d2bd81a061f4e8 Mon Sep 17 00:00:00 2001 From: Esau Date: Mon, 11 Mar 2024 13:51:54 +0100 Subject: [PATCH 15/62] comment --- l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol b/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol index c28cb1136f9..3af11e18bc7 100644 --- a/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol +++ b/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol @@ -7,7 +7,7 @@ import {DataStructures} from "../../libraries/DataStructures.sol"; /** * @title INewOutbox * @author Aztec Labs - * @notice Lives on L1 and is used to consume L2 -> L1 messages. Messages are inserted by the rollup contract + * @notice Lives on L1 and is used to consume L2 -> L1 messages. Messages are inserted by the state transitioner * and will be consumed by the portal contracts. */ // TODO: rename to IOutbox once all the pieces of the new message model are in place. From 486dbeb999872db670df27ecbb39a6fc68187396 Mon Sep 17 00:00:00 2001 From: Esau Date: Mon, 11 Mar 2024 13:55:30 +0100 Subject: [PATCH 16/62] comments --- l1-contracts/src/core/libraries/Merkle.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l1-contracts/src/core/libraries/Merkle.sol b/l1-contracts/src/core/libraries/Merkle.sol index dd41447c6f2..849f69ec54e 100644 --- a/l1-contracts/src/core/libraries/Merkle.sol +++ b/l1-contracts/src/core/libraries/Merkle.sol @@ -5,7 +5,7 @@ pragma solidity >=0.8.18; import {Errors} from "../libraries/Errors.sol"; /** - * @title + * @title Merkle Library * @author Aztec Labs * @notice */ From cd899262a43d12950761e7f0e0b6851bc7557dff Mon Sep 17 00:00:00 2001 From: Esau Date: Mon, 11 Mar 2024 13:56:31 +0100 Subject: [PATCH 17/62] remove --- .../src/core/messagebridge/NewOutbox.sol | 33 ------------------- 1 file changed, 33 deletions(-) diff --git a/l1-contracts/src/core/messagebridge/NewOutbox.sol b/l1-contracts/src/core/messagebridge/NewOutbox.sol index d032723614b..cc82258c491 100644 --- a/l1-contracts/src/core/messagebridge/NewOutbox.sol +++ b/l1-contracts/src/core/messagebridge/NewOutbox.sol @@ -113,37 +113,4 @@ contract NewOutbox is INewOutbox { emit MessageConsumed(_l2BlockNumber, expectedRoot, messageHash, _leafIndex); } - - /* - * @notice Verifies the membership of an L2 to L1 message against an expected root. - * @dev This function assumes a valid path height, as well as sane inputs. - * @dev In the case of a mismatched root, and subsequent inability to verify membership, this function throws. - * @param _path - The sibling path of the message as a leaf, used to prove message inclusion - * @param _leaf - The hash of the message we are trying to prove inclusion for - * @param _index - The index of the message inside the L2 to L1 message tree - * @param _expectedRoot - The expected root to check the validity of the message and sibling path with. - */ - function _verifyMembership( - bytes32[] memory _path, - bytes32 _leaf, - uint256 _index, - bytes32 _expectedRoot - ) internal pure { - bytes32 subtreeRoot = _leaf; - uint256 indexAtHeight = _index; - - for (uint256 height = 0; height < _path.length; height++) { - bool isRight = (indexAtHeight & 1) == 1; - - subtreeRoot = isRight - ? sha256(bytes.concat(_path[height], subtreeRoot)) - : sha256(bytes.concat(subtreeRoot, _path[height])); - - indexAtHeight /= 2; - } - - if (subtreeRoot != _expectedRoot) { - revert Errors.MerkleLib__InvalidRoot(_expectedRoot, subtreeRoot); - } - } } From 2163906734e72fea29120116412212a8a7481da9 Mon Sep 17 00:00:00 2001 From: Esau Date: Mon, 11 Mar 2024 13:59:18 +0100 Subject: [PATCH 18/62] format --- .../interfaces/messagebridge/INewOutbox.sol | 5 ++- l1-contracts/src/core/libraries/Merkle.sol | 2 +- l1-contracts/test/NewOutbox.t.sol | 4 ++- l1-contracts/test/merkle/Merkle.t.sol | 34 ++++++++++++------- 4 files changed, 30 insertions(+), 15 deletions(-) diff --git a/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol b/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol index 3af11e18bc7..1d9bf9dd1cd 100644 --- a/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol +++ b/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol @@ -14,7 +14,10 @@ import {DataStructures} from "../../libraries/DataStructures.sol"; interface INewOutbox { event RootAdded(uint256 indexed l2BlockNumber, bytes32 indexed root, uint256 height); event MessageConsumed( - uint256 indexed l2BlockNumber, bytes32 indexed root, bytes32 indexed messageHash, uint256 leafIndex + uint256 indexed l2BlockNumber, + bytes32 indexed root, + bytes32 indexed messageHash, + uint256 leafIndex ); /* diff --git a/l1-contracts/src/core/libraries/Merkle.sol b/l1-contracts/src/core/libraries/Merkle.sol index 849f69ec54e..cd4d7f1681d 100644 --- a/l1-contracts/src/core/libraries/Merkle.sol +++ b/l1-contracts/src/core/libraries/Merkle.sol @@ -9,7 +9,7 @@ import {Errors} from "../libraries/Errors.sol"; * @author Aztec Labs * @notice */ -library Merkle { +library Merkle { /* * @notice Verifies the membership of a leaf and path against an expected root. * @dev In the case of a mismatched root, and subsequent inability to verify membership, this function throws. diff --git a/l1-contracts/test/NewOutbox.t.sol b/l1-contracts/test/NewOutbox.t.sol index b69a28e6385..bfb26ff3903 100644 --- a/l1-contracts/test/NewOutbox.t.sol +++ b/l1-contracts/test/NewOutbox.t.sol @@ -174,7 +174,9 @@ contract NewOutboxTest is Test { (bytes32[] memory path,) = modifiedTree.computeSiblingPath(0); - vm.expectRevert(abi.encodeWithSelector(Errors.MerkleLib__InvalidRoot.selector, root, modifiedRoot)); + vm.expectRevert( + abi.encodeWithSelector(Errors.MerkleLib__InvalidRoot.selector, root, modifiedRoot) + ); outbox.consume(1, 0, fakeMessage, path); } diff --git a/l1-contracts/test/merkle/Merkle.t.sol b/l1-contracts/test/merkle/Merkle.t.sol index b4c4d33a471..4f3eeab0193 100644 --- a/l1-contracts/test/merkle/Merkle.t.sol +++ b/l1-contracts/test/merkle/Merkle.t.sol @@ -10,14 +10,14 @@ import {Errors} from "../../src/core/libraries/Errors.sol"; import {FrontierMerkle} from "./../../src/core/messagebridge/frontier_tree/Frontier.sol"; abstract contract MerkleLibWrapper { - function _verifyMembershipWrapper( - bytes32[] memory _path, - bytes32 _leaf, - uint256 _index, - bytes32 _expectedRoot - ) external pure { - Merkle._verifyMembership(_path, _leaf, _index, _expectedRoot); - } + function _verifyMembershipWrapper( + bytes32[] memory _path, + bytes32 _leaf, + uint256 _index, + bytes32 _expectedRoot + ) external pure { + Merkle._verifyMembership(_path, _leaf, _index, _expectedRoot); + } } contract MerkleTest is Test, MerkleLibWrapper { @@ -97,9 +97,14 @@ contract MerkleTest is Test, MerkleLibWrapper { } bytes32[3] memory expectedPath1 = [ - bytes32(abi.encode(2)), + bytes32(abi.encode(2)), sha256(bytes.concat(bytes32(abi.encode(3)), bytes32(abi.encode(4)))), - sha256(bytes.concat(sha256(bytes.concat(bytes32(abi.encode(5)), bytes32(abi.encode(6)))), sha256(bytes.concat(bytes32(abi.encode(7)), bytes32(abi.encode(8)))))) + sha256( + bytes.concat( + sha256(bytes.concat(bytes32(abi.encode(5)), bytes32(abi.encode(6)))), + sha256(bytes.concat(bytes32(abi.encode(7)), bytes32(abi.encode(8)))) + ) + ) ]; (bytes32[] memory path1, bytes32 leaf1) = manualTree.computeSiblingPath(0); @@ -109,9 +114,14 @@ contract MerkleTest is Test, MerkleLibWrapper { assertEq(path1[2], expectedPath1[2]); bytes32[3] memory expectedPath2 = [ - bytes32(abi.encode(7)), + bytes32(abi.encode(7)), sha256(bytes.concat(bytes32(abi.encode(5)), bytes32(abi.encode(6)))), - sha256(bytes.concat(sha256(bytes.concat(bytes32(abi.encode(1)), bytes32(abi.encode(2)))), sha256(bytes.concat(bytes32(abi.encode(3)), bytes32(abi.encode(4)))))) + sha256( + bytes.concat( + sha256(bytes.concat(bytes32(abi.encode(1)), bytes32(abi.encode(2)))), + sha256(bytes.concat(bytes32(abi.encode(3)), bytes32(abi.encode(4)))) + ) + ) ]; (bytes32[] memory path2, bytes32 leaf2) = manualTree.computeSiblingPath(7); From b960106b1ca515be0e8b41517fecee443cf24628 Mon Sep 17 00:00:00 2001 From: Esau Date: Mon, 11 Mar 2024 16:04:32 +0100 Subject: [PATCH 19/62] lint --- l1-contracts/src/core/libraries/Merkle.sol | 2 +- l1-contracts/src/core/messagebridge/NewOutbox.sol | 2 +- l1-contracts/test/merkle/Merkle.t.sol | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/l1-contracts/src/core/libraries/Merkle.sol b/l1-contracts/src/core/libraries/Merkle.sol index cd4d7f1681d..b0342c21a6f 100644 --- a/l1-contracts/src/core/libraries/Merkle.sol +++ b/l1-contracts/src/core/libraries/Merkle.sol @@ -27,7 +27,7 @@ library Merkle { * @remark - We use the indexAtHeight to see whether our child of the next subtree is at the left or the right side, as any odd indexes are right-sided children. * This is important and we have assigned it into its own variable isRight, because this affects the way we concat our two children to then hash and calculate the root. */ - function _verifyMembership( + function verifyMembership( bytes32[] memory _path, bytes32 _leaf, uint256 _index, diff --git a/l1-contracts/src/core/messagebridge/NewOutbox.sol b/l1-contracts/src/core/messagebridge/NewOutbox.sol index cc82258c491..fedc3dd9472 100644 --- a/l1-contracts/src/core/messagebridge/NewOutbox.sol +++ b/l1-contracts/src/core/messagebridge/NewOutbox.sol @@ -107,7 +107,7 @@ contract NewOutbox is INewOutbox { bytes32 messageHash = _message.sha256ToField(); - Merkle._verifyMembership(_path, messageHash, _leafIndex, expectedRoot); + Merkle.verifyMembership(_path, messageHash, _leafIndex, expectedRoot); rootData.nullified[_leafIndex] = true; diff --git a/l1-contracts/test/merkle/Merkle.t.sol b/l1-contracts/test/merkle/Merkle.t.sol index 4f3eeab0193..ade9b279037 100644 --- a/l1-contracts/test/merkle/Merkle.t.sol +++ b/l1-contracts/test/merkle/Merkle.t.sol @@ -10,13 +10,13 @@ import {Errors} from "../../src/core/libraries/Errors.sol"; import {FrontierMerkle} from "./../../src/core/messagebridge/frontier_tree/Frontier.sol"; abstract contract MerkleLibWrapper { - function _verifyMembershipWrapper( + function verifyMembershipWrapper( bytes32[] memory _path, bytes32 _leaf, uint256 _index, bytes32 _expectedRoot ) external pure { - Merkle._verifyMembership(_path, _leaf, _index, _expectedRoot); + Merkle.verifyMembership(_path, _leaf, _index, _expectedRoot); } } @@ -57,7 +57,7 @@ contract MerkleTest is Test, MerkleLibWrapper { (bytes32[] memory path, bytes32 leaf) = testNaiveMerkle.computeSiblingPath(leafIndex); - Merkle._verifyMembership(path, leaf, leafIndex, testNaiveMerkle.computeRoot()); + Merkle.verifyMembership(path, leaf, leafIndex, testNaiveMerkle.computeRoot()); } function testNaiveBadSiblingPathAndMembershipVerification(uint256 _idx) public { @@ -70,7 +70,7 @@ contract MerkleTest is Test, MerkleLibWrapper { path1[0] = path1[path1.length - 1]; path1[path1.length - 1] = temp1; vm.expectRevert(); - this._verifyMembershipWrapper(path1, leaf, leafIndex, expectedRoot); + this.verifyMembershipWrapper(path1, leaf, leafIndex, expectedRoot); // Tests truncated path (bytes32[] memory path2,) = testNaiveMerkle.computeSiblingPath(leafIndex); @@ -80,12 +80,12 @@ contract MerkleTest is Test, MerkleLibWrapper { } vm.expectRevert(); - this._verifyMembershipWrapper(truncatedPath, leaf, leafIndex, expectedRoot); + this.verifyMembershipWrapper(truncatedPath, leaf, leafIndex, expectedRoot); // Tests empty path bytes32[] memory emptyPath = new bytes32[](0); vm.expectRevert(); - this._verifyMembershipWrapper(emptyPath, leaf, leafIndex, expectedRoot); + this.verifyMembershipWrapper(emptyPath, leaf, leafIndex, expectedRoot); } function testComputeSiblingPathManually() public { From 97d34977f84bd95a178223e16f4785d3bce42471 Mon Sep 17 00:00:00 2001 From: Esau Date: Mon, 11 Mar 2024 16:18:54 +0100 Subject: [PATCH 20/62] slither --- l1-contracts/slither_output.md | 56 +++++++++++++--------------------- 1 file changed, 22 insertions(+), 34 deletions(-) diff --git a/l1-contracts/slither_output.md b/l1-contracts/slither_output.md index 1751098c481..8c9b28de93f 100644 --- a/l1-contracts/slither_output.md +++ b/l1-contracts/slither_output.md @@ -8,12 +8,12 @@ Summary - [timestamp](#timestamp) (4 results) (Low) - [pess-public-vs-external](#pess-public-vs-external) (7 results) (Low) - [assembly](#assembly) (2 results) (Informational) - - [dead-code](#dead-code) (6 results) (Informational) + - [dead-code](#dead-code) (5 results) (Informational) - [solc-version](#solc-version) (1 results) (Informational) - [low-level-calls](#low-level-calls) (1 results) (Informational) - [similar-names](#similar-names) (4 results) (Informational) - [constable-states](#constable-states) (1 results) (Optimization) - - [pess-multiple-storage-read](#pess-multiple-storage-read) (7 results) (Optimization) + - [pess-multiple-storage-read](#pess-multiple-storage-read) (6 results) (Optimization) ## pess-unprotected-setter Impact: High Confidence: Medium @@ -251,10 +251,10 @@ src/core/messagebridge/Inbox.sol#L21-L231 - [ ] ID-26 -The following public functions could be turned into external in [NewOutbox](src/core/messagebridge/NewOutbox.sol#L18-L147) contract: +The following public functions could be turned into external in [NewOutbox](src/core/messagebridge/NewOutbox.sol#L18-L116) contract: [NewOutbox.constructor(address)](src/core/messagebridge/NewOutbox.sol#L30-L32) -src/core/messagebridge/NewOutbox.sol#L18-L147 +src/core/messagebridge/NewOutbox.sol#L18-L116 - [ ] ID-27 @@ -286,36 +286,30 @@ src/core/libraries/decoders/TxsDecoder.sol#L256-L275 Impact: Informational Confidence: Medium - [ ] ID-30 -[NewOutbox._verifyMembership(bytes32[],bytes32,uint256,bytes32)](src/core/messagebridge/NewOutbox.sol#L124-L146) is never used and should be removed - -src/core/messagebridge/NewOutbox.sol#L124-L146 - - - - [ ] ID-31 [Inbox._errIncompatibleEntryArguments(bytes32,uint64,uint64,uint32,uint32,uint32,uint32)](src/core/messagebridge/Inbox.sol#L212-L230) is never used and should be removed src/core/messagebridge/Inbox.sol#L212-L230 - - [ ] ID-32 + - [ ] ID-31 [Outbox._errNothingToConsume(bytes32)](src/core/messagebridge/Outbox.sol#L114-L116) is never used and should be removed src/core/messagebridge/Outbox.sol#L114-L116 - - [ ] ID-33 + - [ ] ID-32 [Hash.sha256ToField(bytes32)](src/core/libraries/Hash.sol#L59-L61) is never used and should be removed src/core/libraries/Hash.sol#L59-L61 - - [ ] ID-34 + - [ ] ID-33 [Inbox._errNothingToConsume(bytes32)](src/core/messagebridge/Inbox.sol#L197-L199) is never used and should be removed src/core/messagebridge/Inbox.sol#L197-L199 - - [ ] ID-35 + - [ ] ID-34 [Outbox._errIncompatibleEntryArguments(bytes32,uint64,uint64,uint32,uint32,uint32,uint32)](src/core/messagebridge/Outbox.sol#L129-L147) is never used and should be removed src/core/messagebridge/Outbox.sol#L129-L147 @@ -324,13 +318,13 @@ src/core/messagebridge/Outbox.sol#L129-L147 ## solc-version Impact: Informational Confidence: High - - [ ] ID-36 -solc-0.8.23 is not recommended for deployment + - [ ] ID-35 +solc-0.8.22 is not recommended for deployment ## low-level-calls Impact: Informational Confidence: High - - [ ] ID-37 + - [ ] ID-36 Low level call in [Inbox.withdrawFees()](src/core/messagebridge/Inbox.sol#L148-L153): - [(success) = msg.sender.call{value: balance}()](src/core/messagebridge/Inbox.sol#L151) @@ -340,25 +334,25 @@ src/core/messagebridge/Inbox.sol#L148-L153 ## similar-names Impact: Informational Confidence: Medium - - [ ] ID-38 + - [ ] ID-37 Variable [Constants.LOGS_HASHES_NUM_BYTES_PER_BASE_ROLLUP](src/core/libraries/ConstantsGen.sol#L129) is too similar to [Constants.NOTE_HASHES_NUM_BYTES_PER_BASE_ROLLUP](src/core/libraries/ConstantsGen.sol#L122) src/core/libraries/ConstantsGen.sol#L129 - - [ ] ID-39 + - [ ] ID-38 Variable [Constants.L1_TO_L2_MESSAGE_LENGTH](src/core/libraries/ConstantsGen.sol#L109) is too similar to [Constants.L2_TO_L1_MESSAGE_LENGTH](src/core/libraries/ConstantsGen.sol#L110) src/core/libraries/ConstantsGen.sol#L109 - - [ ] ID-40 + - [ ] ID-39 Variable [NewOutbox.STATE_TRANSITIONER](src/core/messagebridge/NewOutbox.sol#L27) is too similar to [NewOutbox.constructor(address)._stateTransitioner](src/core/messagebridge/NewOutbox.sol#L30) src/core/messagebridge/NewOutbox.sol#L27 - - [ ] ID-41 + - [ ] ID-40 Variable [Rollup.AVAILABILITY_ORACLE](src/core/Rollup.sol#L33) is too similar to [Rollup.constructor(IRegistry,IAvailabilityOracle)._availabilityOracle](src/core/Rollup.sol#L43) src/core/Rollup.sol#L33 @@ -367,7 +361,7 @@ src/core/Rollup.sol#L33 ## constable-states Impact: Optimization Confidence: High - - [ ] ID-42 + - [ ] ID-41 [Rollup.lastWarpedBlockTs](src/core/Rollup.sol#L41) should be constant src/core/Rollup.sol#L41 @@ -376,43 +370,37 @@ src/core/Rollup.sol#L41 ## pess-multiple-storage-read Impact: Optimization Confidence: High - - [ ] ID-43 + - [ ] ID-42 In a function [NewOutbox.insert(uint256,bytes32,uint256)](src/core/messagebridge/NewOutbox.sol#L43-L63) variable [NewOutbox.roots](src/core/messagebridge/NewOutbox.sol#L28) is read multiple times src/core/messagebridge/NewOutbox.sol#L43-L63 - - [ ] ID-44 + - [ ] ID-43 In a function [NewInbox.sendL2Message(DataStructures.L2Actor,bytes32,bytes32)](src/core/messagebridge/NewInbox.sol#L62-L99) variable [NewInbox.inProgress](src/core/messagebridge/NewInbox.sol#L37) is read multiple times src/core/messagebridge/NewInbox.sol#L62-L99 - - [ ] ID-45 + - [ ] ID-44 In a function [FrontierMerkle.root()](src/core/messagebridge/frontier_tree/Frontier.sol#L43-L76) variable [FrontierMerkle.HEIGHT](src/core/messagebridge/frontier_tree/Frontier.sol#L8) is read multiple times src/core/messagebridge/frontier_tree/Frontier.sol#L43-L76 - - [ ] ID-46 + - [ ] ID-45 In a function [NewInbox.consume()](src/core/messagebridge/NewInbox.sol#L108-L127) variable [NewInbox.inProgress](src/core/messagebridge/NewInbox.sol#L37) is read multiple times src/core/messagebridge/NewInbox.sol#L108-L127 - - [ ] ID-47 + - [ ] ID-46 In a function [NewInbox.consume()](src/core/messagebridge/NewInbox.sol#L108-L127) variable [NewInbox.toConsume](src/core/messagebridge/NewInbox.sol#L35) is read multiple times src/core/messagebridge/NewInbox.sol#L108-L127 - - [ ] ID-48 -In a function [NewOutbox.consume(uint256,uint256,DataStructures.L2ToL1Msg,bytes32[])](src/core/messagebridge/NewOutbox.sol#L76-L113) variable [NewOutbox.roots](src/core/messagebridge/NewOutbox.sol#L28) is read multiple times - -src/core/messagebridge/NewOutbox.sol#L76-L113 - - - - [ ] ID-49 + - [ ] ID-47 In a function [FrontierMerkle.root()](src/core/messagebridge/frontier_tree/Frontier.sol#L43-L76) variable [FrontierMerkle.frontier](src/core/messagebridge/frontier_tree/Frontier.sol#L13) is read multiple times src/core/messagebridge/frontier_tree/Frontier.sol#L43-L76 From 10ded0e4857e42017182bc02398daf14aba2dadd Mon Sep 17 00:00:00 2001 From: Esau Date: Mon, 11 Mar 2024 21:01:43 +0100 Subject: [PATCH 21/62] upgrade to .24 --- l1-contracts/foundry.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l1-contracts/foundry.toml b/l1-contracts/foundry.toml index b51ddc4ac76..9393d57e65a 100644 --- a/l1-contracts/foundry.toml +++ b/l1-contracts/foundry.toml @@ -2,7 +2,7 @@ src = 'src' out = 'out' libs = ['lib'] -solc = "0.8.22" +solc = "0.8.24" remappings = [ "@oz/=lib/openzeppelin-contracts/contracts/" From 3ee4dadddb35aef4711b390404e5bbb03491ae8a Mon Sep 17 00:00:00 2001 From: Esau Date: Mon, 11 Mar 2024 21:37:45 +0100 Subject: [PATCH 22/62] fix --- l1-contracts/foundry.toml | 2 +- l1-contracts/src/core/libraries/Merkle.sol | 52 +++++++++++++++------- l1-contracts/test/NewOutbox.t.sol | 41 ++++++----------- 3 files changed, 50 insertions(+), 45 deletions(-) diff --git a/l1-contracts/foundry.toml b/l1-contracts/foundry.toml index 9393d57e65a..ff36d4ac7bf 100644 --- a/l1-contracts/foundry.toml +++ b/l1-contracts/foundry.toml @@ -2,7 +2,7 @@ src = 'src' out = 'out' libs = ['lib'] -solc = "0.8.24" +solc = "0.8.23" remappings = [ "@oz/=lib/openzeppelin-contracts/contracts/" diff --git a/l1-contracts/src/core/libraries/Merkle.sol b/l1-contracts/src/core/libraries/Merkle.sol index b0342c21a6f..fd6b57a7af8 100644 --- a/l1-contracts/src/core/libraries/Merkle.sol +++ b/l1-contracts/src/core/libraries/Merkle.sol @@ -11,22 +11,22 @@ import {Errors} from "../libraries/Errors.sol"; */ library Merkle { /* - * @notice Verifies the membership of a leaf and path against an expected root. - * @dev In the case of a mismatched root, and subsequent inability to verify membership, this function throws. - * @param _path - The sibling path of the message as a leaf, used to prove message inclusion - * @param _leaf - The hash of the message we are trying to prove inclusion for - * @param _index - The index of the message inside the L2 to L1 message tree - * @param _expectedRoot - The expected root to check the validity of the message and sibling path with. - * @remark - - * E.g. A sibling path for a leaf at index 3 in a tree of depth 3 (between 5 and 8 leafs) consists of the 3 elements denoted as *'s - * d0: [ root ] - * d1: [ ] [*] - * d2: [*] [ ] [ ] [ ] - * d3: [ ] [ ] [*] [ ] [ ] [ ] [ ] [ ]. - * And the elements would be ordered as: [ d3_index_2, d2_index_0, d1_index_1 ]. - * @remark - We use the indexAtHeight to see whether our child of the next subtree is at the left or the right side, as any odd indexes are right-sided children. - * This is important and we have assigned it into its own variable isRight, because this affects the way we concat our two children to then hash and calculate the root. - */ + * @notice Verifies the membership of a leaf and path against an expected root. + * @dev In the case of a mismatched root, and subsequent inability to verify membership, this function throws. + * @param _path - The sibling path of the message as a leaf, used to prove message inclusion + * @param _leaf - The hash of the message we are trying to prove inclusion for + * @param _index - The index of the message inside the L2 to L1 message tree + * @param _expectedRoot - The expected root to check the validity of the message and sibling path with. + * @remark - + * E.g. A sibling path for a leaf at index 3 in a tree of depth 3 (between 5 and 8 leafs) consists of the 3 elements denoted as *'s + * d0: [ root ] + * d1: [ ] [*] + * d2: [*] [ ] [ ] [ ] + * d3: [ ] [ ] [*] [ ] [ ] [ ] [ ] [ ]. + * And the elements would be ordered as: [ d3_index_2, d2_index_0, d1_index_1 ]. + * @remark - We use the indexAtHeight to see whether our child of the next subtree is at the left or the right side, as any odd indexes are right-sided children. + * This is important and we have assigned it into its own variable isRight, because this affects the way we concat our two children to then hash and calculate the root. + */ function verifyMembership( bytes32[] memory _path, bytes32 _leaf, @@ -50,4 +50,24 @@ library Merkle { revert Errors.MerkleLib__InvalidRoot(_expectedRoot, subtreeRoot); } } + + /* + * @notice Calculates a tree height from the amount of elements in the tree, essentially 1 if x = 0 | 1, otherwise Math.ceil(Math.log2(x)) + * @param _size - The amount of elements in the tree + */ + function calculateTreeHeightFromSize(uint256 _size) internal pure returns (uint256) { + if (_size == 1) { + return 1; + } + + uint256 originalNumber = _size; + + uint256 height = 0; + while (_size > 1) { + _size >>= 1; + height++; + } + + return 2 ** height != originalNumber ? ++height : height; + } } diff --git a/l1-contracts/test/NewOutbox.t.sol b/l1-contracts/test/NewOutbox.t.sol index bfb26ff3903..e1c59bdbcca 100644 --- a/l1-contracts/test/NewOutbox.t.sol +++ b/l1-contracts/test/NewOutbox.t.sol @@ -7,6 +7,7 @@ import {NewOutbox} from "../src/core/messagebridge/NewOutbox.sol"; import {INewOutbox} from "../src/core/interfaces/messagebridge/INewOutbox.sol"; import {Errors} from "../src/core/libraries/Errors.sol"; import {DataStructures} from "../src/core/libraries/DataStructures.sol"; +import {Merkle} from "../src/core/libraries/Merkle.sol"; import {Hash} from "../src/core/libraries/Hash.sol"; import {NaiveMerkle} from "./merkle/Naive.sol"; @@ -58,7 +59,7 @@ contract NewOutboxTest is Test { } function testInsertVariedLeafs(bytes32[] calldata _messageLeafs) public { - uint256 bigTreeHeight = calculateTreeHeightFromSize(_messageLeafs.length); + uint256 bigTreeHeight = Merkle.calculateTreeHeightFromSize(_messageLeafs.length); NaiveMerkle tree = new NaiveMerkle(bigTreeHeight); for (uint256 i = 0; i < _messageLeafs.length; i++) { @@ -206,7 +207,7 @@ contract NewOutboxTest is Test { ) public { DataStructures.L2ToL1Msg[] memory messages = new DataStructures.L2ToL1Msg[](_recipients.length); - uint256 bigTreeHeight = calculateTreeHeightFromSize(_recipients.length); + uint256 bigTreeHeight = Merkle.calculateTreeHeightFromSize(_recipients.length); NaiveMerkle tree = new NaiveMerkle(bigTreeHeight); for (uint256 i = 0; i < _recipients.length; i++) { @@ -234,32 +235,16 @@ contract NewOutboxTest is Test { } } - function calculateTreeHeightFromSize(uint256 _number) internal pure returns (uint256) { - if (_number == 1) { - return 1; - } - - uint256 originalNumber = _number; - - uint256 height = 0; - while (_number > 1) { - _number >>= 1; - height++; - } - - return 2 ** height != originalNumber ? ++height : height; - } - function testCalculateNextHighestPowerOfTwo() external { - assertEq(calculateTreeHeightFromSize(0), 1); - assertEq(calculateTreeHeightFromSize(1), 1); - assertEq(calculateTreeHeightFromSize(2), 1); - assertEq(calculateTreeHeightFromSize(3), 2); - assertEq(calculateTreeHeightFromSize(4), 2); - assertEq(calculateTreeHeightFromSize(5), 3); - assertEq(calculateTreeHeightFromSize(6), 3); - assertEq(calculateTreeHeightFromSize(7), 3); - assertEq(calculateTreeHeightFromSize(8), 3); - assertEq(calculateTreeHeightFromSize(9), 4); + assertEq(Merkle.calculateTreeHeightFromSize(0), 1); + assertEq(Merkle.calculateTreeHeightFromSize(1), 1); + assertEq(Merkle.calculateTreeHeightFromSize(2), 1); + assertEq(Merkle.calculateTreeHeightFromSize(3), 2); + assertEq(Merkle.calculateTreeHeightFromSize(4), 2); + assertEq(Merkle.calculateTreeHeightFromSize(5), 3); + assertEq(Merkle.calculateTreeHeightFromSize(6), 3); + assertEq(Merkle.calculateTreeHeightFromSize(7), 3); + assertEq(Merkle.calculateTreeHeightFromSize(8), 3); + assertEq(Merkle.calculateTreeHeightFromSize(9), 4); } } From 8393d6a912c5a18ae869f22e41c66009838f4dde Mon Sep 17 00:00:00 2001 From: Esau Date: Mon, 11 Mar 2024 21:38:53 +0100 Subject: [PATCH 23/62] slither --- l1-contracts/slither_output.md | 36 ++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/l1-contracts/slither_output.md b/l1-contracts/slither_output.md index 8c9b28de93f..1c351854abf 100644 --- a/l1-contracts/slither_output.md +++ b/l1-contracts/slither_output.md @@ -8,7 +8,7 @@ Summary - [timestamp](#timestamp) (4 results) (Low) - [pess-public-vs-external](#pess-public-vs-external) (7 results) (Low) - [assembly](#assembly) (2 results) (Informational) - - [dead-code](#dead-code) (5 results) (Informational) + - [dead-code](#dead-code) (6 results) (Informational) - [solc-version](#solc-version) (1 results) (Informational) - [low-level-calls](#low-level-calls) (1 results) (Informational) - [similar-names](#similar-names) (4 results) (Informational) @@ -310,6 +310,12 @@ src/core/messagebridge/Inbox.sol#L197-L199 - [ ] ID-34 +[Merkle.calculateTreeHeightFromSize(uint256)](src/core/libraries/Merkle.sol#L58-L72) is never used and should be removed + +src/core/libraries/Merkle.sol#L58-L72 + + + - [ ] ID-35 [Outbox._errIncompatibleEntryArguments(bytes32,uint64,uint64,uint32,uint32,uint32,uint32)](src/core/messagebridge/Outbox.sol#L129-L147) is never used and should be removed src/core/messagebridge/Outbox.sol#L129-L147 @@ -318,13 +324,13 @@ src/core/messagebridge/Outbox.sol#L129-L147 ## solc-version Impact: Informational Confidence: High - - [ ] ID-35 -solc-0.8.22 is not recommended for deployment + - [ ] ID-36 +solc-0.8.23 is not recommended for deployment ## low-level-calls Impact: Informational Confidence: High - - [ ] ID-36 + - [ ] ID-37 Low level call in [Inbox.withdrawFees()](src/core/messagebridge/Inbox.sol#L148-L153): - [(success) = msg.sender.call{value: balance}()](src/core/messagebridge/Inbox.sol#L151) @@ -334,25 +340,25 @@ src/core/messagebridge/Inbox.sol#L148-L153 ## similar-names Impact: Informational Confidence: Medium - - [ ] ID-37 + - [ ] ID-38 Variable [Constants.LOGS_HASHES_NUM_BYTES_PER_BASE_ROLLUP](src/core/libraries/ConstantsGen.sol#L129) is too similar to [Constants.NOTE_HASHES_NUM_BYTES_PER_BASE_ROLLUP](src/core/libraries/ConstantsGen.sol#L122) src/core/libraries/ConstantsGen.sol#L129 - - [ ] ID-38 + - [ ] ID-39 Variable [Constants.L1_TO_L2_MESSAGE_LENGTH](src/core/libraries/ConstantsGen.sol#L109) is too similar to [Constants.L2_TO_L1_MESSAGE_LENGTH](src/core/libraries/ConstantsGen.sol#L110) src/core/libraries/ConstantsGen.sol#L109 - - [ ] ID-39 + - [ ] ID-40 Variable [NewOutbox.STATE_TRANSITIONER](src/core/messagebridge/NewOutbox.sol#L27) is too similar to [NewOutbox.constructor(address)._stateTransitioner](src/core/messagebridge/NewOutbox.sol#L30) src/core/messagebridge/NewOutbox.sol#L27 - - [ ] ID-40 + - [ ] ID-41 Variable [Rollup.AVAILABILITY_ORACLE](src/core/Rollup.sol#L33) is too similar to [Rollup.constructor(IRegistry,IAvailabilityOracle)._availabilityOracle](src/core/Rollup.sol#L43) src/core/Rollup.sol#L33 @@ -361,7 +367,7 @@ src/core/Rollup.sol#L33 ## constable-states Impact: Optimization Confidence: High - - [ ] ID-41 + - [ ] ID-42 [Rollup.lastWarpedBlockTs](src/core/Rollup.sol#L41) should be constant src/core/Rollup.sol#L41 @@ -370,37 +376,37 @@ src/core/Rollup.sol#L41 ## pess-multiple-storage-read Impact: Optimization Confidence: High - - [ ] ID-42 + - [ ] ID-43 In a function [NewOutbox.insert(uint256,bytes32,uint256)](src/core/messagebridge/NewOutbox.sol#L43-L63) variable [NewOutbox.roots](src/core/messagebridge/NewOutbox.sol#L28) is read multiple times src/core/messagebridge/NewOutbox.sol#L43-L63 - - [ ] ID-43 + - [ ] ID-44 In a function [NewInbox.sendL2Message(DataStructures.L2Actor,bytes32,bytes32)](src/core/messagebridge/NewInbox.sol#L62-L99) variable [NewInbox.inProgress](src/core/messagebridge/NewInbox.sol#L37) is read multiple times src/core/messagebridge/NewInbox.sol#L62-L99 - - [ ] ID-44 + - [ ] ID-45 In a function [FrontierMerkle.root()](src/core/messagebridge/frontier_tree/Frontier.sol#L43-L76) variable [FrontierMerkle.HEIGHT](src/core/messagebridge/frontier_tree/Frontier.sol#L8) is read multiple times src/core/messagebridge/frontier_tree/Frontier.sol#L43-L76 - - [ ] ID-45 + - [ ] ID-46 In a function [NewInbox.consume()](src/core/messagebridge/NewInbox.sol#L108-L127) variable [NewInbox.inProgress](src/core/messagebridge/NewInbox.sol#L37) is read multiple times src/core/messagebridge/NewInbox.sol#L108-L127 - - [ ] ID-46 + - [ ] ID-47 In a function [NewInbox.consume()](src/core/messagebridge/NewInbox.sol#L108-L127) variable [NewInbox.toConsume](src/core/messagebridge/NewInbox.sol#L35) is read multiple times src/core/messagebridge/NewInbox.sol#L108-L127 - - [ ] ID-47 + - [ ] ID-48 In a function [FrontierMerkle.root()](src/core/messagebridge/frontier_tree/Frontier.sol#L43-L76) variable [FrontierMerkle.frontier](src/core/messagebridge/frontier_tree/Frontier.sol#L13) is read multiple times src/core/messagebridge/frontier_tree/Frontier.sol#L43-L76 From 98e9779f7f23a7e5b9659ca5c42bab75fec810e8 Mon Sep 17 00:00:00 2001 From: Esau Date: Mon, 11 Mar 2024 21:41:05 +0100 Subject: [PATCH 24/62] test --- l1-contracts/test/NewOutbox.t.sol | 13 ------------- l1-contracts/test/merkle/Merkle.t.sol | 13 +++++++++++++ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/l1-contracts/test/NewOutbox.t.sol b/l1-contracts/test/NewOutbox.t.sol index e1c59bdbcca..55b3bbe385c 100644 --- a/l1-contracts/test/NewOutbox.t.sol +++ b/l1-contracts/test/NewOutbox.t.sol @@ -234,17 +234,4 @@ contract NewOutboxTest is Test { outbox.consume(_blockNumber, i, messages[i], path); } } - - function testCalculateNextHighestPowerOfTwo() external { - assertEq(Merkle.calculateTreeHeightFromSize(0), 1); - assertEq(Merkle.calculateTreeHeightFromSize(1), 1); - assertEq(Merkle.calculateTreeHeightFromSize(2), 1); - assertEq(Merkle.calculateTreeHeightFromSize(3), 2); - assertEq(Merkle.calculateTreeHeightFromSize(4), 2); - assertEq(Merkle.calculateTreeHeightFromSize(5), 3); - assertEq(Merkle.calculateTreeHeightFromSize(6), 3); - assertEq(Merkle.calculateTreeHeightFromSize(7), 3); - assertEq(Merkle.calculateTreeHeightFromSize(8), 3); - assertEq(Merkle.calculateTreeHeightFromSize(9), 4); - } } diff --git a/l1-contracts/test/merkle/Merkle.t.sol b/l1-contracts/test/merkle/Merkle.t.sol index ade9b279037..54658248bd6 100644 --- a/l1-contracts/test/merkle/Merkle.t.sol +++ b/l1-contracts/test/merkle/Merkle.t.sol @@ -130,4 +130,17 @@ contract MerkleTest is Test, MerkleLibWrapper { assertEq(path2[1], expectedPath2[1]); assertEq(path2[2], expectedPath2[2]); } + + function testCalculateNextHighestPowerOfTwo() external { + assertEq(Merkle.calculateTreeHeightFromSize(0), 1); + assertEq(Merkle.calculateTreeHeightFromSize(1), 1); + assertEq(Merkle.calculateTreeHeightFromSize(2), 1); + assertEq(Merkle.calculateTreeHeightFromSize(3), 2); + assertEq(Merkle.calculateTreeHeightFromSize(4), 2); + assertEq(Merkle.calculateTreeHeightFromSize(5), 3); + assertEq(Merkle.calculateTreeHeightFromSize(6), 3); + assertEq(Merkle.calculateTreeHeightFromSize(7), 3); + assertEq(Merkle.calculateTreeHeightFromSize(8), 3); + assertEq(Merkle.calculateTreeHeightFromSize(9), 4); + } } From 2580dce78926c7e157cd6c9219583245daf24d87 Mon Sep 17 00:00:00 2001 From: Esau Date: Tue, 12 Mar 2024 01:10:25 +0100 Subject: [PATCH 25/62] new detail --- l1-contracts/test/NewOutbox.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l1-contracts/test/NewOutbox.t.sol b/l1-contracts/test/NewOutbox.t.sol index 55b3bbe385c..4c76920d16b 100644 --- a/l1-contracts/test/NewOutbox.t.sol +++ b/l1-contracts/test/NewOutbox.t.sol @@ -200,7 +200,7 @@ contract NewOutboxTest is Test { } // This test takes awhile so to keep it somewhat reasonable we've set a limit on the amount of fuzz runs - /// forge-config: default.fuzz.runs = 32 + /// forge-config: default.fuzz.runs = 64 function testInsertAndConsumeWithVariedRecipients( address[] calldata _recipients, uint256 _blockNumber From 1e1cfe488333e4aba9209dcecded6046744cf840 Mon Sep 17 00:00:00 2001 From: esau <152162806+sklppy88@users.noreply.github.com> Date: Mon, 11 Mar 2024 19:12:51 -0500 Subject: [PATCH 26/62] Update l1-contracts/test/merkle/Merkle.t.sol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Nicolás Venturo --- l1-contracts/test/merkle/Merkle.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l1-contracts/test/merkle/Merkle.t.sol b/l1-contracts/test/merkle/Merkle.t.sol index 54658248bd6..b7d7626aa08 100644 --- a/l1-contracts/test/merkle/Merkle.t.sol +++ b/l1-contracts/test/merkle/Merkle.t.sol @@ -131,7 +131,7 @@ contract MerkleTest is Test, MerkleLibWrapper { assertEq(path2[2], expectedPath2[2]); } - function testCalculateNextHighestPowerOfTwo() external { + function testCalculateTreeHeightFromSize() external { assertEq(Merkle.calculateTreeHeightFromSize(0), 1); assertEq(Merkle.calculateTreeHeightFromSize(1), 1); assertEq(Merkle.calculateTreeHeightFromSize(2), 1); From 1d9f120197fc18898ebeb75ae91bbe0e3a1fcbf3 Mon Sep 17 00:00:00 2001 From: esau <152162806+sklppy88@users.noreply.github.com> Date: Mon, 11 Mar 2024 19:13:19 -0500 Subject: [PATCH 27/62] Update l1-contracts/test/merkle/Merkle.t.sol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Nicolás Venturo --- l1-contracts/test/merkle/Merkle.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l1-contracts/test/merkle/Merkle.t.sol b/l1-contracts/test/merkle/Merkle.t.sol index b7d7626aa08..3fc13305252 100644 --- a/l1-contracts/test/merkle/Merkle.t.sol +++ b/l1-contracts/test/merkle/Merkle.t.sol @@ -33,7 +33,7 @@ contract MerkleTest is Test, MerkleLibWrapper { frontier = new FrontierMerkle(DEPTH); merkle = new NaiveMerkle(DEPTH); - // Set up testNative + // Set up testNaive testNaiveMerkle = new NaiveMerkle(DEPTH); uint256 treeSize = testNaiveMerkle.SIZE(); for (uint256 i = 0; i < treeSize; i++) { From 69046ac3b36f339b8177561170c300c2a4d0f584 Mon Sep 17 00:00:00 2001 From: Esau Date: Tue, 12 Mar 2024 01:18:25 +0100 Subject: [PATCH 28/62] naming --- l1-contracts/src/core/messagebridge/NewOutbox.sol | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/l1-contracts/src/core/messagebridge/NewOutbox.sol b/l1-contracts/src/core/messagebridge/NewOutbox.sol index fedc3dd9472..b2b508c11b0 100644 --- a/l1-contracts/src/core/messagebridge/NewOutbox.sol +++ b/l1-contracts/src/core/messagebridge/NewOutbox.sol @@ -89,9 +89,9 @@ contract NewOutbox is INewOutbox { RootData storage rootData = roots[_l2BlockNumber]; - bytes32 expectedRoot = rootData.root; + bytes32 outMessageTreeRoot = rootData.root; - if (expectedRoot == 0) { + if (outMessageTreeRoot == 0) { revert Errors.Outbox__NothingToConsumeAtBlock(_l2BlockNumber); } @@ -99,18 +99,18 @@ contract NewOutbox is INewOutbox { revert Errors.Outbox__AlreadyNullified(_l2BlockNumber, _leafIndex); } - uint256 expectedHeight = rootData.height; + uint256 outMessageTreeHeight = rootData.height; - if (expectedHeight != _path.length) { - revert Errors.Outbox__InvalidPathLength(expectedHeight, _path.length); + if (outMessageTreeHeight != _path.length) { + revert Errors.Outbox__InvalidPathLength(outMessageTreeHeight, _path.length); } bytes32 messageHash = _message.sha256ToField(); - Merkle.verifyMembership(_path, messageHash, _leafIndex, expectedRoot); + Merkle.verifyMembership(_path, messageHash, _leafIndex, outMessageTreeRoot); rootData.nullified[_leafIndex] = true; - emit MessageConsumed(_l2BlockNumber, expectedRoot, messageHash, _leafIndex); + emit MessageConsumed(_l2BlockNumber, outMessageTreeRoot, messageHash, _leafIndex); } } From e74ba1daf27ef06cd9157f568ea744403d30cbb0 Mon Sep 17 00:00:00 2001 From: Esau Date: Tue, 12 Mar 2024 01:34:37 +0100 Subject: [PATCH 29/62] fix --- l1-contracts/src/core/libraries/Merkle.sol | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/l1-contracts/src/core/libraries/Merkle.sol b/l1-contracts/src/core/libraries/Merkle.sol index fd6b57a7af8..1925643624b 100644 --- a/l1-contracts/src/core/libraries/Merkle.sol +++ b/l1-contracts/src/core/libraries/Merkle.sol @@ -10,22 +10,20 @@ import {Errors} from "../libraries/Errors.sol"; * @notice */ library Merkle { - /* + /** * @notice Verifies the membership of a leaf and path against an expected root. * @dev In the case of a mismatched root, and subsequent inability to verify membership, this function throws. * @param _path - The sibling path of the message as a leaf, used to prove message inclusion * @param _leaf - The hash of the message we are trying to prove inclusion for * @param _index - The index of the message inside the L2 to L1 message tree * @param _expectedRoot - The expected root to check the validity of the message and sibling path with. - * @remark - + * @notice - * E.g. A sibling path for a leaf at index 3 in a tree of depth 3 (between 5 and 8 leafs) consists of the 3 elements denoted as *'s * d0: [ root ] * d1: [ ] [*] * d2: [*] [ ] [ ] [ ] * d3: [ ] [ ] [*] [ ] [ ] [ ] [ ] [ ]. * And the elements would be ordered as: [ d3_index_2, d2_index_0, d1_index_1 ]. - * @remark - We use the indexAtHeight to see whether our child of the next subtree is at the left or the right side, as any odd indexes are right-sided children. - * This is important and we have assigned it into its own variable isRight, because this affects the way we concat our two children to then hash and calculate the root. */ function verifyMembership( bytes32[] memory _path, @@ -34,16 +32,18 @@ library Merkle { bytes32 _expectedRoot ) internal pure { bytes32 subtreeRoot = _leaf; + /// @notice - We use the indexAtHeight to see whether our child of the next subtree is at the left or the right side uint256 indexAtHeight = _index; for (uint256 height = 0; height < _path.length; height++) { + /// @notice - This affects the way we concatenate our two children to then hash and calculate the root, as any odd indexes (index bit-masked with least significant bit) are right-sided children. bool isRight = (indexAtHeight & 1) == 1; subtreeRoot = isRight ? sha256(bytes.concat(_path[height], subtreeRoot)) : sha256(bytes.concat(subtreeRoot, _path[height])); - - indexAtHeight /= 2; + /// @notice - We divide by two here to get the index of the parent of the current subtreeRoot in its own layer + indexAtHeight >>= 1; } if (subtreeRoot != _expectedRoot) { From 7dbafcfef284e893b9682a926f986687420c99b3 Mon Sep 17 00:00:00 2001 From: Esau Date: Tue, 12 Mar 2024 02:03:10 +0100 Subject: [PATCH 30/62] comments --- l1-contracts/test/merkle/Merkle.t.sol | 53 ++++++++++++++++++++------- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/l1-contracts/test/merkle/Merkle.t.sol b/l1-contracts/test/merkle/Merkle.t.sol index 3fc13305252..12a4d5ee235 100644 --- a/l1-contracts/test/merkle/Merkle.t.sol +++ b/l1-contracts/test/merkle/Merkle.t.sol @@ -88,14 +88,22 @@ contract MerkleTest is Test, MerkleLibWrapper { this.verifyMembershipWrapper(emptyPath, leaf, leafIndex, expectedRoot); } - function testComputeSiblingPathManually() public { + function testComputeSiblingPathManuallyLeftChild() public { + /// Creates a merkle tree with depth 3 and size 8, with leafs from 1 - 8 NaiveMerkle manualTree = new NaiveMerkle(3); - for (uint256 i = 1; i <= 8; i++) { bytes32 generatedLeaf = bytes32(abi.encode(i)); manualTree.insertLeaf(generatedLeaf); } + /** We manually make a path; this is the sibling path of the leaf with the value of one. + * In this path, from leaf to root, a, b, and c; which correspond to the value of 2, then the hash of 3 and 4, + * and finally, the hash of 5 and 6 concatenated with the hash of 7 and 8; + * d0: [ root ] + * d1: [ ] [c] + * d2: [ ] [b] [ ] [ ] + * d3: [1] [a] [3] [4] [5] [6] [7] [8]. + */ bytes32[3] memory expectedPath1 = [ bytes32(abi.encode(2)), sha256(bytes.concat(bytes32(abi.encode(3)), bytes32(abi.encode(4)))), @@ -107,13 +115,31 @@ contract MerkleTest is Test, MerkleLibWrapper { ) ]; - (bytes32[] memory path1, bytes32 leaf1) = manualTree.computeSiblingPath(0); - assertEq(leaf1, bytes32(abi.encode(1))); - assertEq(path1[0], expectedPath1[0]); - assertEq(path1[1], expectedPath1[1]); - assertEq(path1[2], expectedPath1[2]); + /// We then compute the sibling path using the tree and expect that our manual calculation should equal the computed one + (bytes32[] memory path, bytes32 leaf) = manualTree.computeSiblingPath(0); + assertEq(leaf, bytes32(abi.encode(1))); + assertEq(path[0], expectedPath[0]); + assertEq(path[1], expectedPath[1]); + assertEq(path[2], expectedPath[2]); + } + + function testComputeSiblingPathManuallyRightChild() public { + /// Creates a merkle tree with depth 3 and size 8, with leafs from 1 - 8 + NaiveMerkle manualTree = new NaiveMerkle(3); + for (uint256 i = 1; i <= 8; i++) { + bytes32 generatedLeaf = bytes32(abi.encode(i)); + manualTree.insertLeaf(generatedLeaf); + } - bytes32[3] memory expectedPath2 = [ + /** We manually make a path; this is the sibling path of the leaf with the value of one. + * In this path, from leaf to root, a, b, and c; which correspond to the value of 2, then the hash of 3 and 4, + * and finally, the hash of 5 and 6 concatenated with the hash of 7 and 8; + * d0: [ root ] + * d1: [ ] [c] + * d2: [ ] [b] [ ] [ ] + * d3: [1] [a] [3] [4] [5] [6] [7] [8]. + */ + bytes32[3] memory expectedPath = [ bytes32(abi.encode(7)), sha256(bytes.concat(bytes32(abi.encode(5)), bytes32(abi.encode(6)))), sha256( @@ -124,11 +150,12 @@ contract MerkleTest is Test, MerkleLibWrapper { ) ]; - (bytes32[] memory path2, bytes32 leaf2) = manualTree.computeSiblingPath(7); - assertEq(leaf2, bytes32(abi.encode(8))); - assertEq(path2[0], expectedPath2[0]); - assertEq(path2[1], expectedPath2[1]); - assertEq(path2[2], expectedPath2[2]); + /// We then compute the sibling path using the tree and expect that our manual calculation should equal the computed one + (bytes32[] memory path, bytes32 leaf) = manualTree.computeSiblingPath(7); + assertEq(leaf, bytes32(abi.encode(8))); + assertEq(path[0], expectedPath[0]); + assertEq(path[1], expectedPath[1]); + assertEq(path[2], expectedPath[2]); } function testCalculateTreeHeightFromSize() external { From 0a19afa7968c2a9a4915885b5e001a7db1665e34 Mon Sep 17 00:00:00 2001 From: Esau Date: Tue, 12 Mar 2024 02:20:57 +0100 Subject: [PATCH 31/62] test --- l1-contracts/src/core/libraries/Merkle.sol | 8 +++++++- l1-contracts/test/merkle/Merkle.t.sol | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/l1-contracts/src/core/libraries/Merkle.sol b/l1-contracts/src/core/libraries/Merkle.sol index 1925643624b..51888b0f9a5 100644 --- a/l1-contracts/src/core/libraries/Merkle.sol +++ b/l1-contracts/src/core/libraries/Merkle.sol @@ -56,18 +56,24 @@ library Merkle { * @param _size - The amount of elements in the tree */ function calculateTreeHeightFromSize(uint256 _size) internal pure returns (uint256) { + /// The code / formula that works below has one edge case at _size = 1, which we handle here if (_size == 1) { return 1; } + /// We need to store the original numer to check at the end if we are a power of two uint256 originalNumber = _size; + /// While size > 1, we divide by two, and count how many times we do this; producing a rudimentary way of calculating Math.Floor(Math.log2(x)) uint256 height = 0; while (_size > 1) { _size >>= 1; height++; } - return 2 ** height != originalNumber ? ++height : height; + /// @notice - We check if 2 ** height does not euqal our original number. If so, this means that our size is not a power of two, + /// and hence we've rounded down (Math.floor) and have obtained the next lowest power of two instead of rounding up (Math.ceil) to obtain the next highest power of two and therefore we need to increment height before returning it. + /// If 2 ** height equals our original number, it means that we have a perfect power of two and Math.floor(Math.log2(x)) = Math.ceil(Math.log2(x)) and we can return height as-is + return (2 ** height) != originalNumber ? ++height : height; } } diff --git a/l1-contracts/test/merkle/Merkle.t.sol b/l1-contracts/test/merkle/Merkle.t.sol index 12a4d5ee235..abc7c85ae8f 100644 --- a/l1-contracts/test/merkle/Merkle.t.sol +++ b/l1-contracts/test/merkle/Merkle.t.sol @@ -104,7 +104,7 @@ contract MerkleTest is Test, MerkleLibWrapper { * d2: [ ] [b] [ ] [ ] * d3: [1] [a] [3] [4] [5] [6] [7] [8]. */ - bytes32[3] memory expectedPath1 = [ + bytes32[3] memory expectedPath = [ bytes32(abi.encode(2)), sha256(bytes.concat(bytes32(abi.encode(3)), bytes32(abi.encode(4)))), sha256( From 8d6dbb32ff29bb5a591f44f578cbd9ea7c6d8111 Mon Sep 17 00:00:00 2001 From: Esau Date: Tue, 12 Mar 2024 02:24:44 +0100 Subject: [PATCH 32/62] comments --- l1-contracts/src/core/libraries/Merkle.sol | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/l1-contracts/src/core/libraries/Merkle.sol b/l1-contracts/src/core/libraries/Merkle.sol index 51888b0f9a5..a40b19ee4e3 100644 --- a/l1-contracts/src/core/libraries/Merkle.sol +++ b/l1-contracts/src/core/libraries/Merkle.sol @@ -52,7 +52,7 @@ library Merkle { } /* - * @notice Calculates a tree height from the amount of elements in the tree, essentially 1 if x = 0 | 1, otherwise Math.ceil(Math.log2(x)) + * @notice Calculates a tree height from the amount of elements in the tree * @param _size - The amount of elements in the tree */ function calculateTreeHeightFromSize(uint256 _size) internal pure returns (uint256) { @@ -64,8 +64,11 @@ library Merkle { /// We need to store the original numer to check at the end if we are a power of two uint256 originalNumber = _size; - /// While size > 1, we divide by two, and count how many times we do this; producing a rudimentary way of calculating Math.Floor(Math.log2(x)) + /// We need the height of the tree that will contain all of our leaves, + /// hence the next highest power of two from the amount of leaves - Math.ceil(Math.log2(x)) uint256 height = 0; + + /// While size > 1, we divide by two, and count how many times we do this; producing a rudimentary way of calculating Math.Floor(Math.log2(x)) while (_size > 1) { _size >>= 1; height++; From a08f272019020fcc537039dace42e96b800775fd Mon Sep 17 00:00:00 2001 From: Esau Date: Tue, 12 Mar 2024 02:28:24 +0100 Subject: [PATCH 33/62] asdf --- l1-contracts/test/merkle/Merkle.t.sol | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/l1-contracts/test/merkle/Merkle.t.sol b/l1-contracts/test/merkle/Merkle.t.sol index abc7c85ae8f..10f976ae640 100644 --- a/l1-contracts/test/merkle/Merkle.t.sol +++ b/l1-contracts/test/merkle/Merkle.t.sol @@ -96,8 +96,8 @@ contract MerkleTest is Test, MerkleLibWrapper { manualTree.insertLeaf(generatedLeaf); } - /** We manually make a path; this is the sibling path of the leaf with the value of one. - * In this path, from leaf to root, a, b, and c; which correspond to the value of 2, then the hash of 3 and 4, + /** We manually make a path; this is the sibling path of the leaf with the value of 1. + * This path, from leaf to root, consists a, b, and c; which correspond to the value of 2, then the hash of 3 and 4, * and finally, the hash of 5 and 6 concatenated with the hash of 7 and 8; * d0: [ root ] * d1: [ ] [c] @@ -131,13 +131,13 @@ contract MerkleTest is Test, MerkleLibWrapper { manualTree.insertLeaf(generatedLeaf); } - /** We manually make a path; this is the sibling path of the leaf with the value of one. - * In this path, from leaf to root, a, b, and c; which correspond to the value of 2, then the hash of 3 and 4, - * and finally, the hash of 5 and 6 concatenated with the hash of 7 and 8; + /** We manually make a path; this is the sibling path of the leaf with the value of 8. + * This path, from leaf to root, consists of c a, b, and c; which correspond to the value of 7, then the hash of 5 and 6, + * and finally, the hash of 1 and 2 concatenated with the hash of 3 and 4; * d0: [ root ] - * d1: [ ] [c] - * d2: [ ] [b] [ ] [ ] - * d3: [1] [a] [3] [4] [5] [6] [7] [8]. + * d1: [c] [ ] + * d2: [ ] [b] [b] [ ] + * d3: [1] [2] [3] [4] [5] [6] [a] [8]. */ bytes32[3] memory expectedPath = [ bytes32(abi.encode(7)), From 6e114dc0664f2d688a0f3bfe87ee59dcbf595812 Mon Sep 17 00:00:00 2001 From: Esau Date: Tue, 12 Mar 2024 12:25:43 +0100 Subject: [PATCH 34/62] fix test asdf asdf format --- .../interfaces/messagebridge/INewOutbox.sol | 15 ++++++-- l1-contracts/src/core/libraries/Merkle.sol | 30 ++++++++-------- .../src/core/messagebridge/NewOutbox.sol | 19 +++++++++-- l1-contracts/test/NewOutbox.t.sol | 11 ++++++ l1-contracts/test/merkle/Merkle.t.sol | 34 ++++++++++--------- 5 files changed, 74 insertions(+), 35 deletions(-) diff --git a/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol b/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol index 1d9bf9dd1cd..e4de5846374 100644 --- a/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol +++ b/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol @@ -20,7 +20,7 @@ interface INewOutbox { uint256 leafIndex ); - /* + /** * @notice Inserts the root of a merkle tree containing all of the L2 to L1 messages in * a block specified by _l2BlockNumber. * @dev Only callable by the state transitioner (rollup contract) @@ -31,7 +31,7 @@ interface INewOutbox { */ function insert(uint256 _l2BlockNumber, bytes32 _root, uint256 _height) external; - /* + /** * @notice Consumes an entry from the Outbox * @dev Only useable by portals / recipients of messages * @dev Emits `MessageConsumed` when consuming messages @@ -48,4 +48,15 @@ interface INewOutbox { DataStructures.L2ToL1Msg memory _message, bytes32[] memory _path ) external; + + /** + * @notice Checks to see if an index of the L2 to L1 message tree for a specific block has been consumed + * @dev - This message does not throw, and out-of-bounds access is considered valid, but will always return false + * @param _l2BlockNumber - The block number specifying the block that contains the index of the message we want to check + * @param _leafIndex - The index inside the merkle tree where the message is located + */ + function hasMessageBeenConsumedAtBlockAndIndex(uint256 _l2BlockNumber, uint256 _leafIndex) + external + view + returns (bool); } diff --git a/l1-contracts/src/core/libraries/Merkle.sol b/l1-contracts/src/core/libraries/Merkle.sol index a40b19ee4e3..70346770771 100644 --- a/l1-contracts/src/core/libraries/Merkle.sol +++ b/l1-contracts/src/core/libraries/Merkle.sol @@ -11,20 +11,20 @@ import {Errors} from "../libraries/Errors.sol"; */ library Merkle { /** - * @notice Verifies the membership of a leaf and path against an expected root. - * @dev In the case of a mismatched root, and subsequent inability to verify membership, this function throws. - * @param _path - The sibling path of the message as a leaf, used to prove message inclusion - * @param _leaf - The hash of the message we are trying to prove inclusion for - * @param _index - The index of the message inside the L2 to L1 message tree - * @param _expectedRoot - The expected root to check the validity of the message and sibling path with. - * @notice - - * E.g. A sibling path for a leaf at index 3 in a tree of depth 3 (between 5 and 8 leafs) consists of the 3 elements denoted as *'s - * d0: [ root ] - * d1: [ ] [*] - * d2: [*] [ ] [ ] [ ] - * d3: [ ] [ ] [*] [ ] [ ] [ ] [ ] [ ]. - * And the elements would be ordered as: [ d3_index_2, d2_index_0, d1_index_1 ]. - */ + * @notice Verifies the membership of a leaf and path against an expected root. + * @dev In the case of a mismatched root, and subsequent inability to verify membership, this function throws. + * @param _path - The sibling path of the message as a leaf, used to prove message inclusion + * @param _leaf - The hash of the message we are trying to prove inclusion for + * @param _index - The index of the message inside the L2 to L1 message tree + * @param _expectedRoot - The expected root to check the validity of the message and sibling path with. + * @notice - + * E.g. A sibling path for a leaf at index 3 in a tree of depth 3 (between 5 and 8 leafs) consists of the 3 elements denoted as *'s + * d0: [ root ] + * d1: [ ] [*] + * d2: [*] [ ] [ ] [ ] + * d3: [ ] [ ] [*] [ ] [ ] [ ] [ ] [ ]. + * And the elements would be ordered as: [ d3_index_2, d2_index_0, d1_index_1 ]. + */ function verifyMembership( bytes32[] memory _path, bytes32 _leaf, @@ -74,7 +74,7 @@ library Merkle { height++; } - /// @notice - We check if 2 ** height does not euqal our original number. If so, this means that our size is not a power of two, + /// @notice - We check if 2 ** height does not euqal our original number. If so, this means that our size is not a power of two, /// and hence we've rounded down (Math.floor) and have obtained the next lowest power of two instead of rounding up (Math.ceil) to obtain the next highest power of two and therefore we need to increment height before returning it. /// If 2 ** height equals our original number, it means that we have a perfect power of two and Math.floor(Math.log2(x)) = Math.ceil(Math.log2(x)) and we can return height as-is return (2 ** height) != originalNumber ? ++height : height; diff --git a/l1-contracts/src/core/messagebridge/NewOutbox.sol b/l1-contracts/src/core/messagebridge/NewOutbox.sol index b2b508c11b0..6ce25d1358d 100644 --- a/l1-contracts/src/core/messagebridge/NewOutbox.sol +++ b/l1-contracts/src/core/messagebridge/NewOutbox.sol @@ -31,7 +31,7 @@ contract NewOutbox is INewOutbox { STATE_TRANSITIONER = _stateTransitioner; } - /* + /** * @notice Inserts the root of a merkle tree containing all of the L2 to L1 messages in * a block specified by _l2BlockNumber. * @dev Only callable by the state transitioner (rollup contract) @@ -62,7 +62,7 @@ contract NewOutbox is INewOutbox { emit RootAdded(_l2BlockNumber, _root, _height); } - /* + /** * @notice Consumes an entry from the Outbox * @dev Only useable by portals / recipients of messages * @dev Emits `MessageConsumed` when consuming messages @@ -113,4 +113,19 @@ contract NewOutbox is INewOutbox { emit MessageConsumed(_l2BlockNumber, outMessageTreeRoot, messageHash, _leafIndex); } + + /** + * @notice Checks to see if an index of the L2 to L1 message tree for a specific block has been consumed + * @dev - This message does not throw, and out-of-bounds access is considered valid, but will always return false + * @param _l2BlockNumber - The block number specifying the block that contains the index of the message we want to check + * @param _leafIndex - The index inside the merkle tree where the message is located + */ + function hasMessageBeenConsumedAtBlockAndIndex(uint256 _l2BlockNumber, uint256 _leafIndex) + external + view + override(INewOutbox) + returns (bool) + { + return roots[_l2BlockNumber].nullified[_leafIndex]; + } } diff --git a/l1-contracts/test/NewOutbox.t.sol b/l1-contracts/test/NewOutbox.t.sol index 4c76920d16b..25d823b94cc 100644 --- a/l1-contracts/test/NewOutbox.t.sol +++ b/l1-contracts/test/NewOutbox.t.sol @@ -194,9 +194,15 @@ contract NewOutboxTest is Test { (bytes32[] memory path,) = tree.computeSiblingPath(0); + bool statusBeforeConsumption = outbox.hasMessageBeenConsumedAtBlockAndIndex(1, 0); + assertEq(abi.encode(0), abi.encode(statusBeforeConsumption)); + vm.expectEmit(true, true, true, true, address(outbox)); emit INewOutbox.MessageConsumed(1, root, leaf, 0); outbox.consume(1, 0, fakeMessage, path); + + bool statusAfterConsumption = outbox.hasMessageBeenConsumedAtBlockAndIndex(1, 0); + assertEq(abi.encode(1), abi.encode(statusAfterConsumption)); } // This test takes awhile so to keep it somewhat reasonable we've set a limit on the amount of fuzz runs @@ -234,4 +240,9 @@ contract NewOutboxTest is Test { outbox.consume(_blockNumber, i, messages[i], path); } } + + function testCheckOutOfBoundsStatus(uint256 _blockNumber, uint256 _leafIndex) external { + bool outOfBounds = outbox.hasMessageBeenConsumedAtBlockAndIndex(_blockNumber, _leafIndex); + assertEq(abi.encode(0), abi.encode(outOfBounds)); + } } diff --git a/l1-contracts/test/merkle/Merkle.t.sol b/l1-contracts/test/merkle/Merkle.t.sol index 10f976ae640..516f78b30a5 100644 --- a/l1-contracts/test/merkle/Merkle.t.sol +++ b/l1-contracts/test/merkle/Merkle.t.sol @@ -96,14 +96,15 @@ contract MerkleTest is Test, MerkleLibWrapper { manualTree.insertLeaf(generatedLeaf); } - /** We manually make a path; this is the sibling path of the leaf with the value of 1. - * This path, from leaf to root, consists a, b, and c; which correspond to the value of 2, then the hash of 3 and 4, - * and finally, the hash of 5 and 6 concatenated with the hash of 7 and 8; - * d0: [ root ] - * d1: [ ] [c] - * d2: [ ] [b] [ ] [ ] - * d3: [1] [a] [3] [4] [5] [6] [7] [8]. - */ + /** + * We manually make a path; this is the sibling path of the leaf with the value of 1. + * This path, from leaf to root, consists a, b, and c; which correspond to the value of 2, then the hash of 3 and 4, + * and finally, the hash of 5 and 6 concatenated with the hash of 7 and 8; + * d0: [ root ] + * d1: [ ] [c] + * d2: [ ] [b] [ ] [ ] + * d3: [1] [a] [3] [4] [5] [6] [7] [8]. + */ bytes32[3] memory expectedPath = [ bytes32(abi.encode(2)), sha256(bytes.concat(bytes32(abi.encode(3)), bytes32(abi.encode(4)))), @@ -131,14 +132,15 @@ contract MerkleTest is Test, MerkleLibWrapper { manualTree.insertLeaf(generatedLeaf); } - /** We manually make a path; this is the sibling path of the leaf with the value of 8. - * This path, from leaf to root, consists of c a, b, and c; which correspond to the value of 7, then the hash of 5 and 6, - * and finally, the hash of 1 and 2 concatenated with the hash of 3 and 4; - * d0: [ root ] - * d1: [c] [ ] - * d2: [ ] [b] [b] [ ] - * d3: [1] [2] [3] [4] [5] [6] [a] [8]. - */ + /** + * We manually make a path; this is the sibling path of the leaf with the value of 8. + * This path, from leaf to root, consists of c a, b, and c; which correspond to the value of 7, then the hash of 5 and 6, + * and finally, the hash of 1 and 2 concatenated with the hash of 3 and 4; + * d0: [ root ] + * d1: [c] [ ] + * d2: [ ] [b] [b] [ ] + * d3: [1] [2] [3] [4] [5] [6] [a] [8]. + */ bytes32[3] memory expectedPath = [ bytes32(abi.encode(7)), sha256(bytes.concat(bytes32(abi.encode(5)), bytes32(abi.encode(6)))), From de503db2870e2362e72a7682e6908182012ab2b9 Mon Sep 17 00:00:00 2001 From: Esau Date: Tue, 12 Mar 2024 15:45:53 +0100 Subject: [PATCH 35/62] fix --- l1-contracts/test/NewOutbox.t.sol | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/l1-contracts/test/NewOutbox.t.sol b/l1-contracts/test/NewOutbox.t.sol index 25d823b94cc..7d0e8144e40 100644 --- a/l1-contracts/test/NewOutbox.t.sol +++ b/l1-contracts/test/NewOutbox.t.sol @@ -208,15 +208,17 @@ contract NewOutboxTest is Test { // This test takes awhile so to keep it somewhat reasonable we've set a limit on the amount of fuzz runs /// forge-config: default.fuzz.runs = 64 function testInsertAndConsumeWithVariedRecipients( - address[] calldata _recipients, - uint256 _blockNumber + address[256] calldata _recipients, + uint256 _blockNumber, + uint8 _size ) public { - DataStructures.L2ToL1Msg[] memory messages = new DataStructures.L2ToL1Msg[](_recipients.length); + uint256 numberOfMessages = bound(_size, 1, _recipients.length); + DataStructures.L2ToL1Msg[] memory messages = new DataStructures.L2ToL1Msg[](numberOfMessages); - uint256 bigTreeHeight = Merkle.calculateTreeHeightFromSize(_recipients.length); + uint256 bigTreeHeight = Merkle.calculateTreeHeightFromSize(numberOfMessages); NaiveMerkle tree = new NaiveMerkle(bigTreeHeight); - for (uint256 i = 0; i < _recipients.length; i++) { + for (uint256 i = 0; i < numberOfMessages; i++) { DataStructures.L2ToL1Msg memory fakeMessage = _fakeMessage(_recipients[i]); messages[i] = fakeMessage; bytes32 modifiedLeaf = fakeMessage.sha256ToField(); @@ -231,7 +233,7 @@ contract NewOutboxTest is Test { vm.prank(_STATE_TRANSITIONER); outbox.insert(_blockNumber, root, bigTreeHeight); - for (uint256 i = 0; i < _recipients.length; i++) { + for (uint256 i = 0; i < numberOfMessages; i++) { (bytes32[] memory path, bytes32 leaf) = tree.computeSiblingPath(i); vm.expectEmit(true, true, true, true, address(outbox)); From cde49435dd98ca0c76040eaefc78683cc9e1804b Mon Sep 17 00:00:00 2001 From: Esau Date: Tue, 12 Mar 2024 15:46:46 +0100 Subject: [PATCH 36/62] comments --- l1-contracts/src/core/messagebridge/NewOutbox.sol | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/l1-contracts/src/core/messagebridge/NewOutbox.sol b/l1-contracts/src/core/messagebridge/NewOutbox.sol index 6ce25d1358d..594d582e323 100644 --- a/l1-contracts/src/core/messagebridge/NewOutbox.sol +++ b/l1-contracts/src/core/messagebridge/NewOutbox.sol @@ -89,9 +89,9 @@ contract NewOutbox is INewOutbox { RootData storage rootData = roots[_l2BlockNumber]; - bytes32 outMessageTreeRoot = rootData.root; + bytes32 blockRoot = rootData.root; - if (outMessageTreeRoot == 0) { + if (blockRoot == 0) { revert Errors.Outbox__NothingToConsumeAtBlock(_l2BlockNumber); } @@ -99,19 +99,19 @@ contract NewOutbox is INewOutbox { revert Errors.Outbox__AlreadyNullified(_l2BlockNumber, _leafIndex); } - uint256 outMessageTreeHeight = rootData.height; + uint256 treeHeight = rootData.height; - if (outMessageTreeHeight != _path.length) { - revert Errors.Outbox__InvalidPathLength(outMessageTreeHeight, _path.length); + if (treeHeight != _path.length) { + revert Errors.Outbox__InvalidPathLength(treeHeight, _path.length); } bytes32 messageHash = _message.sha256ToField(); - Merkle.verifyMembership(_path, messageHash, _leafIndex, outMessageTreeRoot); + Merkle.verifyMembership(_path, messageHash, _leafIndex, blockRoot); rootData.nullified[_leafIndex] = true; - emit MessageConsumed(_l2BlockNumber, outMessageTreeRoot, messageHash, _leafIndex); + emit MessageConsumed(_l2BlockNumber, blockRoot, messageHash, _leafIndex); } /** From a1dad2514503fedaecede8c2d18ae033d14355f6 Mon Sep 17 00:00:00 2001 From: Esau Date: Tue, 12 Mar 2024 15:47:43 +0100 Subject: [PATCH 37/62] slith --- l1-contracts/slither_output.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/l1-contracts/slither_output.md b/l1-contracts/slither_output.md index 1c351854abf..55b3b913e4c 100644 --- a/l1-contracts/slither_output.md +++ b/l1-contracts/slither_output.md @@ -251,10 +251,10 @@ src/core/messagebridge/Inbox.sol#L21-L231 - [ ] ID-26 -The following public functions could be turned into external in [NewOutbox](src/core/messagebridge/NewOutbox.sol#L18-L116) contract: +The following public functions could be turned into external in [NewOutbox](src/core/messagebridge/NewOutbox.sol#L18-L131) contract: [NewOutbox.constructor(address)](src/core/messagebridge/NewOutbox.sol#L30-L32) -src/core/messagebridge/NewOutbox.sol#L18-L116 +src/core/messagebridge/NewOutbox.sol#L18-L131 - [ ] ID-27 @@ -310,9 +310,9 @@ src/core/messagebridge/Inbox.sol#L197-L199 - [ ] ID-34 -[Merkle.calculateTreeHeightFromSize(uint256)](src/core/libraries/Merkle.sol#L58-L72) is never used and should be removed +[Merkle.calculateTreeHeightFromSize(uint256)](src/core/libraries/Merkle.sol#L58-L81) is never used and should be removed -src/core/libraries/Merkle.sol#L58-L72 +src/core/libraries/Merkle.sol#L58-L81 - [ ] ID-35 From c19828a4c6c6ee0829f56e09f0588dd9162bdaf4 Mon Sep 17 00:00:00 2001 From: Esau Date: Tue, 12 Mar 2024 21:05:13 +0100 Subject: [PATCH 38/62] test --- l1-contracts/slither_output.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/l1-contracts/slither_output.md b/l1-contracts/slither_output.md index 55b3b913e4c..7d248f7bcf8 100644 --- a/l1-contracts/slither_output.md +++ b/l1-contracts/slither_output.md @@ -341,15 +341,15 @@ src/core/messagebridge/Inbox.sol#L148-L153 Impact: Informational Confidence: Medium - [ ] ID-38 -Variable [Constants.LOGS_HASHES_NUM_BYTES_PER_BASE_ROLLUP](src/core/libraries/ConstantsGen.sol#L129) is too similar to [Constants.NOTE_HASHES_NUM_BYTES_PER_BASE_ROLLUP](src/core/libraries/ConstantsGen.sol#L122) +Variable [Constants.LOGS_HASHES_NUM_BYTES_PER_BASE_ROLLUP](src/core/libraries/ConstantsGen.sol#L131) is too similar to [Constants.NOTE_HASHES_NUM_BYTES_PER_BASE_ROLLUP](src/core/libraries/ConstantsGen.sol#L124) -src/core/libraries/ConstantsGen.sol#L129 +src/core/libraries/ConstantsGen.sol#L131 - [ ] ID-39 -Variable [Constants.L1_TO_L2_MESSAGE_LENGTH](src/core/libraries/ConstantsGen.sol#L109) is too similar to [Constants.L2_TO_L1_MESSAGE_LENGTH](src/core/libraries/ConstantsGen.sol#L110) +Variable [Constants.L1_TO_L2_MESSAGE_LENGTH](src/core/libraries/ConstantsGen.sol#L111) is too similar to [Constants.L2_TO_L1_MESSAGE_LENGTH](src/core/libraries/ConstantsGen.sol#L112) -src/core/libraries/ConstantsGen.sol#L109 +src/core/libraries/ConstantsGen.sol#L111 - [ ] ID-40 From 2b8748f6aed01276427449daef37060323786178 Mon Sep 17 00:00:00 2001 From: esau <152162806+sklppy88@users.noreply.github.com> Date: Wed, 13 Mar 2024 13:08:40 -0500 Subject: [PATCH 39/62] Update l1-contracts/src/core/libraries/Merkle.sol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jan Beneš --- l1-contracts/src/core/libraries/Merkle.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l1-contracts/src/core/libraries/Merkle.sol b/l1-contracts/src/core/libraries/Merkle.sol index 70346770771..5632ada070c 100644 --- a/l1-contracts/src/core/libraries/Merkle.sol +++ b/l1-contracts/src/core/libraries/Merkle.sol @@ -74,7 +74,7 @@ library Merkle { height++; } - /// @notice - We check if 2 ** height does not euqal our original number. If so, this means that our size is not a power of two, + /// @notice - We check if 2 ** height does not equal our original number. If so, this means that our size is not a power of two, /// and hence we've rounded down (Math.floor) and have obtained the next lowest power of two instead of rounding up (Math.ceil) to obtain the next highest power of two and therefore we need to increment height before returning it. /// If 2 ** height equals our original number, it means that we have a perfect power of two and Math.floor(Math.log2(x)) = Math.ceil(Math.log2(x)) and we can return height as-is return (2 ** height) != originalNumber ? ++height : height; From ed6398f4d443d5b1b83c4520433d125737691910 Mon Sep 17 00:00:00 2001 From: Esau Date: Wed, 13 Mar 2024 19:23:04 +0100 Subject: [PATCH 40/62] move out of core add comments comments comments fix fix --- .../interfaces/messagebridge/INewOutbox.sol | 4 +- .../libraries/{Merkle.sol => MerkleLib.sol} | 33 +------- .../src/core/messagebridge/NewOutbox.sol | 12 +-- l1-contracts/test/NewOutbox.t.sol | 74 +++++++++--------- l1-contracts/test/merkle/Merkle.t.sol | 75 ++++++++++++------- .../test/merkle/helpers/MerkleLibHelper.sol | 17 +++++ 6 files changed, 113 insertions(+), 102 deletions(-) rename l1-contracts/src/core/libraries/{Merkle.sol => MerkleLib.sol} (60%) create mode 100644 l1-contracts/test/merkle/helpers/MerkleLibHelper.sol diff --git a/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol b/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol index e4de5846374..05430d535b4 100644 --- a/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol +++ b/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol @@ -7,7 +7,7 @@ import {DataStructures} from "../../libraries/DataStructures.sol"; /** * @title INewOutbox * @author Aztec Labs - * @notice Lives on L1 and is used to consume L2 -> L1 messages. Messages are inserted by the state transitioner + * @notice Lives on L1 and is used to consume L2 -> L1 messages. Messages are inserted by the Rollup * and will be consumed by the portal contracts. */ // TODO: rename to IOutbox once all the pieces of the new message model are in place. @@ -23,7 +23,7 @@ interface INewOutbox { /** * @notice Inserts the root of a merkle tree containing all of the L2 to L1 messages in * a block specified by _l2BlockNumber. - * @dev Only callable by the state transitioner (rollup contract) + * @dev Only callable by the rollup contract * @dev Emits `RootAdded` upon inserting the root successfully * @param _l2BlockNumber - The L2 Block Number in which the L2 to L1 messages reside * @param _root - The merkle root of the tree where all the L2 to L1 messages are leaves diff --git a/l1-contracts/src/core/libraries/Merkle.sol b/l1-contracts/src/core/libraries/MerkleLib.sol similarity index 60% rename from l1-contracts/src/core/libraries/Merkle.sol rename to l1-contracts/src/core/libraries/MerkleLib.sol index 5632ada070c..ec62404a6bc 100644 --- a/l1-contracts/src/core/libraries/Merkle.sol +++ b/l1-contracts/src/core/libraries/MerkleLib.sol @@ -7,9 +7,9 @@ import {Errors} from "../libraries/Errors.sol"; /** * @title Merkle Library * @author Aztec Labs - * @notice + * @notice Library that contains functions useful when interacting with Merkle Trees */ -library Merkle { +library MerkleLib { /** * @notice Verifies the membership of a leaf and path against an expected root. * @dev In the case of a mismatched root, and subsequent inability to verify membership, this function throws. @@ -50,33 +50,4 @@ library Merkle { revert Errors.MerkleLib__InvalidRoot(_expectedRoot, subtreeRoot); } } - - /* - * @notice Calculates a tree height from the amount of elements in the tree - * @param _size - The amount of elements in the tree - */ - function calculateTreeHeightFromSize(uint256 _size) internal pure returns (uint256) { - /// The code / formula that works below has one edge case at _size = 1, which we handle here - if (_size == 1) { - return 1; - } - - /// We need to store the original numer to check at the end if we are a power of two - uint256 originalNumber = _size; - - /// We need the height of the tree that will contain all of our leaves, - /// hence the next highest power of two from the amount of leaves - Math.ceil(Math.log2(x)) - uint256 height = 0; - - /// While size > 1, we divide by two, and count how many times we do this; producing a rudimentary way of calculating Math.Floor(Math.log2(x)) - while (_size > 1) { - _size >>= 1; - height++; - } - - /// @notice - We check if 2 ** height does not equal our original number. If so, this means that our size is not a power of two, - /// and hence we've rounded down (Math.floor) and have obtained the next lowest power of two instead of rounding up (Math.ceil) to obtain the next highest power of two and therefore we need to increment height before returning it. - /// If 2 ** height equals our original number, it means that we have a perfect power of two and Math.floor(Math.log2(x)) = Math.ceil(Math.log2(x)) and we can return height as-is - return (2 ** height) != originalNumber ? ++height : height; - } } diff --git a/l1-contracts/src/core/messagebridge/NewOutbox.sol b/l1-contracts/src/core/messagebridge/NewOutbox.sol index 594d582e323..3f5246e4d48 100644 --- a/l1-contracts/src/core/messagebridge/NewOutbox.sol +++ b/l1-contracts/src/core/messagebridge/NewOutbox.sol @@ -5,14 +5,14 @@ pragma solidity >=0.8.18; // Libraries import {DataStructures} from "../libraries/DataStructures.sol"; import {Errors} from "../libraries/Errors.sol"; -import {Merkle} from "../libraries/Merkle.sol"; +import {MerkleLib} from "../libraries/MerkleLib.sol"; import {Hash} from "../libraries/Hash.sol"; import {INewOutbox} from "../interfaces/messagebridge/INewOutbox.sol"; /** * @title NewOutbox * @author Aztec Labs - * @notice Lives on L1 and is used to consume L2 -> L1 messages. Messages are inserted by the state transitioner + * @notice Lives on L1 and is used to consume L2 -> L1 messages. Messages are inserted by the Rollup * and will be consumed by the portal contracts. */ contract NewOutbox is INewOutbox { @@ -34,7 +34,7 @@ contract NewOutbox is INewOutbox { /** * @notice Inserts the root of a merkle tree containing all of the L2 to L1 messages in * a block specified by _l2BlockNumber. - * @dev Only callable by the state transitioner (rollup contract) + * @dev Only callable by the rollup contract * @dev Emits `RootAdded` upon inserting the root successfully * @param _l2BlockNumber - The L2 Block Number in which the L2 to L1 messages reside * @param _root - The merkle root of the tree where all the L2 to L1 messages are leaves @@ -76,8 +76,8 @@ contract NewOutbox is INewOutbox { function consume( uint256 _l2BlockNumber, uint256 _leafIndex, - DataStructures.L2ToL1Msg memory _message, - bytes32[] memory _path + DataStructures.L2ToL1Msg calldata _message, + bytes32[] calldata _path ) external override(INewOutbox) { if (msg.sender != _message.recipient.actor) { revert Errors.Outbox__InvalidRecipient(_message.recipient.actor, msg.sender); @@ -107,7 +107,7 @@ contract NewOutbox is INewOutbox { bytes32 messageHash = _message.sha256ToField(); - Merkle.verifyMembership(_path, messageHash, _leafIndex, blockRoot); + MerkleLib.verifyMembership(_path, messageHash, _leafIndex, blockRoot); rootData.nullified[_leafIndex] = true; diff --git a/l1-contracts/test/NewOutbox.t.sol b/l1-contracts/test/NewOutbox.t.sol index 7d0e8144e40..334cbe1ce07 100644 --- a/l1-contracts/test/NewOutbox.t.sol +++ b/l1-contracts/test/NewOutbox.t.sol @@ -7,32 +7,34 @@ import {NewOutbox} from "../src/core/messagebridge/NewOutbox.sol"; import {INewOutbox} from "../src/core/interfaces/messagebridge/INewOutbox.sol"; import {Errors} from "../src/core/libraries/Errors.sol"; import {DataStructures} from "../src/core/libraries/DataStructures.sol"; -import {Merkle} from "../src/core/libraries/Merkle.sol"; import {Hash} from "../src/core/libraries/Hash.sol"; import {NaiveMerkle} from "./merkle/Naive.sol"; +import {MerkleTest} from "./merkle/Merkle.t.sol"; contract NewOutboxTest is Test { using Hash for DataStructures.L2ToL1Msg; - address internal constant _STATE_TRANSITIONER = address(0x42069123); - address internal constant _NOT_STATE_TRANSITIONER = address(0x69); - address internal constant _NOT_RECIPIENT = address(0x420); - uint256 internal constant _DEFAULT_TREE_HEIGHT = 2; - uint256 internal constant _VERSION = 0; + address internal constant STATE_TRANSITIONER = address(0x42069123); + address internal constant NOT_STATE_TRANSITIONER = address(0x69); + address internal constant NOT_RECIPIENT = address(0x420); + uint256 internal constant DEFAULT_TREE_HEIGHT = 2; + uint256 internal constant AZTEC_VERSION = 0; NewOutbox internal outbox; NaiveMerkle internal zeroedTree; + MerkleTest internal merkleTest; function setUp() public { - outbox = new NewOutbox(_STATE_TRANSITIONER); - zeroedTree = new NaiveMerkle(_DEFAULT_TREE_HEIGHT); + outbox = new NewOutbox(STATE_TRANSITIONER); + zeroedTree = new NaiveMerkle(DEFAULT_TREE_HEIGHT); + merkleTest = new MerkleTest(); } function _fakeMessage(address _recipient) internal view returns (DataStructures.L2ToL1Msg memory) { return DataStructures.L2ToL1Msg({ sender: DataStructures.L2Actor({ actor: 0x2000000000000000000000000000000000000000000000000000000000000000, - version: _VERSION + version: AZTEC_VERSION }), recipient: DataStructures.L1Actor({actor: _recipient, chainId: block.chainid}), content: 0x3000000000000000000000000000000000000000000000000000000000000000 @@ -42,24 +44,24 @@ contract NewOutboxTest is Test { function testRevertIfInsertingFromNonRollup() public { bytes32 root = zeroedTree.computeRoot(); - vm.prank(_NOT_STATE_TRANSITIONER); + vm.prank(NOT_STATE_TRANSITIONER); vm.expectRevert(abi.encodeWithSelector(Errors.Outbox__Unauthorized.selector)); - outbox.insert(1, root, _DEFAULT_TREE_HEIGHT); + outbox.insert(1, root, DEFAULT_TREE_HEIGHT); } function testRevertIfInsertingDuplicate() public { bytes32 root = zeroedTree.computeRoot(); - vm.prank(_STATE_TRANSITIONER); - outbox.insert(1, root, _DEFAULT_TREE_HEIGHT); + vm.prank(STATE_TRANSITIONER); + outbox.insert(1, root, DEFAULT_TREE_HEIGHT); - vm.prank(_STATE_TRANSITIONER); + vm.prank(STATE_TRANSITIONER); vm.expectRevert(abi.encodeWithSelector(Errors.Outbox__RootAlreadySetAtBlock.selector, 1)); - outbox.insert(1, root, _DEFAULT_TREE_HEIGHT); + outbox.insert(1, root, DEFAULT_TREE_HEIGHT); } function testInsertVariedLeafs(bytes32[] calldata _messageLeafs) public { - uint256 bigTreeHeight = Merkle.calculateTreeHeightFromSize(_messageLeafs.length); + uint256 bigTreeHeight = merkleTest.calculateTreeHeightFromSize(_messageLeafs.length); NaiveMerkle tree = new NaiveMerkle(bigTreeHeight); for (uint256 i = 0; i < _messageLeafs.length; i++) { @@ -71,7 +73,7 @@ contract NewOutboxTest is Test { vm.expectEmit(true, true, true, true, address(outbox)); emit INewOutbox.RootAdded(1, root, bigTreeHeight); - vm.prank(_STATE_TRANSITIONER); + vm.prank(STATE_TRANSITIONER); outbox.insert(1, root, bigTreeHeight); (bytes32 actualRoot, uint256 actualHeight) = outbox.roots(1); @@ -84,10 +86,10 @@ contract NewOutboxTest is Test { (bytes32[] memory path,) = zeroedTree.computeSiblingPath(0); - vm.prank(_NOT_RECIPIENT); + vm.prank(NOT_RECIPIENT); vm.expectRevert( abi.encodeWithSelector( - Errors.Outbox__InvalidRecipient.selector, address(this), _NOT_RECIPIENT + Errors.Outbox__InvalidRecipient.selector, address(this), NOT_RECIPIENT ) ); outbox.consume(1, 1, fakeMessage, path); @@ -120,12 +122,12 @@ contract NewOutboxTest is Test { DataStructures.L2ToL1Msg memory fakeMessage = _fakeMessage(address(this)); bytes32 leaf = fakeMessage.sha256ToField(); - NaiveMerkle tree = new NaiveMerkle(_DEFAULT_TREE_HEIGHT); + NaiveMerkle tree = new NaiveMerkle(DEFAULT_TREE_HEIGHT); tree.insertLeaf(leaf); bytes32 root = tree.computeRoot(); - vm.prank(_STATE_TRANSITIONER); - outbox.insert(1, root, _DEFAULT_TREE_HEIGHT); + vm.prank(STATE_TRANSITIONER); + outbox.insert(1, root, DEFAULT_TREE_HEIGHT); (bytes32[] memory path,) = tree.computeSiblingPath(0); outbox.consume(1, 0, fakeMessage, path); @@ -137,20 +139,20 @@ contract NewOutboxTest is Test { DataStructures.L2ToL1Msg memory fakeMessage = _fakeMessage(address(this)); bytes32 leaf = fakeMessage.sha256ToField(); - NaiveMerkle tree = new NaiveMerkle(_DEFAULT_TREE_HEIGHT); + NaiveMerkle tree = new NaiveMerkle(DEFAULT_TREE_HEIGHT); tree.insertLeaf(leaf); bytes32 root = tree.computeRoot(); - vm.prank(_STATE_TRANSITIONER); - outbox.insert(1, root, _DEFAULT_TREE_HEIGHT); + vm.prank(STATE_TRANSITIONER); + outbox.insert(1, root, DEFAULT_TREE_HEIGHT); - NaiveMerkle biggerTree = new NaiveMerkle(_DEFAULT_TREE_HEIGHT + 1); + NaiveMerkle biggerTree = new NaiveMerkle(DEFAULT_TREE_HEIGHT + 1); tree.insertLeaf(leaf); (bytes32[] memory path,) = biggerTree.computeSiblingPath(0); vm.expectRevert( abi.encodeWithSelector( - Errors.Outbox__InvalidPathLength.selector, _DEFAULT_TREE_HEIGHT, _DEFAULT_TREE_HEIGHT + 1 + Errors.Outbox__InvalidPathLength.selector, DEFAULT_TREE_HEIGHT, DEFAULT_TREE_HEIGHT + 1 ) ); outbox.consume(1, 0, fakeMessage, path); @@ -162,16 +164,16 @@ contract NewOutboxTest is Test { fakeMessage.content = bytes32(uint256(42069)); bytes32 modifiedLeaf = fakeMessage.sha256ToField(); - NaiveMerkle tree = new NaiveMerkle(_DEFAULT_TREE_HEIGHT); + NaiveMerkle tree = new NaiveMerkle(DEFAULT_TREE_HEIGHT); tree.insertLeaf(leaf); bytes32 root = tree.computeRoot(); - NaiveMerkle modifiedTree = new NaiveMerkle(_DEFAULT_TREE_HEIGHT); + NaiveMerkle modifiedTree = new NaiveMerkle(DEFAULT_TREE_HEIGHT); modifiedTree.insertLeaf(modifiedLeaf); bytes32 modifiedRoot = modifiedTree.computeRoot(); - vm.prank(_STATE_TRANSITIONER); - outbox.insert(1, root, _DEFAULT_TREE_HEIGHT); + vm.prank(STATE_TRANSITIONER); + outbox.insert(1, root, DEFAULT_TREE_HEIGHT); (bytes32[] memory path,) = modifiedTree.computeSiblingPath(0); @@ -185,12 +187,12 @@ contract NewOutboxTest is Test { DataStructures.L2ToL1Msg memory fakeMessage = _fakeMessage(address(this)); bytes32 leaf = fakeMessage.sha256ToField(); - NaiveMerkle tree = new NaiveMerkle(_DEFAULT_TREE_HEIGHT); + NaiveMerkle tree = new NaiveMerkle(DEFAULT_TREE_HEIGHT); tree.insertLeaf(leaf); bytes32 root = tree.computeRoot(); - vm.prank(_STATE_TRANSITIONER); - outbox.insert(1, root, _DEFAULT_TREE_HEIGHT); + vm.prank(STATE_TRANSITIONER); + outbox.insert(1, root, DEFAULT_TREE_HEIGHT); (bytes32[] memory path,) = tree.computeSiblingPath(0); @@ -215,7 +217,7 @@ contract NewOutboxTest is Test { uint256 numberOfMessages = bound(_size, 1, _recipients.length); DataStructures.L2ToL1Msg[] memory messages = new DataStructures.L2ToL1Msg[](numberOfMessages); - uint256 bigTreeHeight = Merkle.calculateTreeHeightFromSize(numberOfMessages); + uint256 bigTreeHeight = merkleTest.calculateTreeHeightFromSize(numberOfMessages); NaiveMerkle tree = new NaiveMerkle(bigTreeHeight); for (uint256 i = 0; i < numberOfMessages; i++) { @@ -230,7 +232,7 @@ contract NewOutboxTest is Test { vm.expectEmit(true, true, true, true, address(outbox)); emit INewOutbox.RootAdded(_blockNumber, root, bigTreeHeight); - vm.prank(_STATE_TRANSITIONER); + vm.prank(STATE_TRANSITIONER); outbox.insert(_blockNumber, root, bigTreeHeight); for (uint256 i = 0; i < numberOfMessages; i++) { diff --git a/l1-contracts/test/merkle/Merkle.t.sol b/l1-contracts/test/merkle/Merkle.t.sol index 516f78b30a5..67432de00dd 100644 --- a/l1-contracts/test/merkle/Merkle.t.sol +++ b/l1-contracts/test/merkle/Merkle.t.sol @@ -5,24 +5,15 @@ pragma solidity >=0.8.18; import {Test} from "forge-std/Test.sol"; import {NaiveMerkle} from "./Naive.sol"; -import {Merkle} from "../../src/core/libraries/Merkle.sol"; +import {MerkleLib} from "../../src/core/libraries/MerkleLib.sol"; +import {MerkleLibHelper} from "./helpers/MerkleLibHelper.sol"; import {Errors} from "../../src/core/libraries/Errors.sol"; import {FrontierMerkle} from "./../../src/core/messagebridge/frontier_tree/Frontier.sol"; -abstract contract MerkleLibWrapper { - function verifyMembershipWrapper( - bytes32[] memory _path, - bytes32 _leaf, - uint256 _index, - bytes32 _expectedRoot - ) external pure { - Merkle.verifyMembership(_path, _leaf, _index, _expectedRoot); - } -} - -contract MerkleTest is Test, MerkleLibWrapper { +contract MerkleTest is Test { NaiveMerkle internal merkle; FrontierMerkle internal frontier; + MerkleLibHelper internal merkleLibHelper; NaiveMerkle internal testNaiveMerkle; @@ -32,6 +23,7 @@ contract MerkleTest is Test, MerkleLibWrapper { // Set up testFrontier frontier = new FrontierMerkle(DEPTH); merkle = new NaiveMerkle(DEPTH); + merkleLibHelper = new MerkleLibHelper(); // Set up testNaive testNaiveMerkle = new NaiveMerkle(DEPTH); @@ -57,7 +49,7 @@ contract MerkleTest is Test, MerkleLibWrapper { (bytes32[] memory path, bytes32 leaf) = testNaiveMerkle.computeSiblingPath(leafIndex); - Merkle.verifyMembership(path, leaf, leafIndex, testNaiveMerkle.computeRoot()); + merkleLibHelper.verifyMembership(path, leaf, leafIndex, testNaiveMerkle.computeRoot()); } function testNaiveBadSiblingPathAndMembershipVerification(uint256 _idx) public { @@ -70,7 +62,7 @@ contract MerkleTest is Test, MerkleLibWrapper { path1[0] = path1[path1.length - 1]; path1[path1.length - 1] = temp1; vm.expectRevert(); - this.verifyMembershipWrapper(path1, leaf, leafIndex, expectedRoot); + merkleLibHelper.verifyMembership(path1, leaf, leafIndex, expectedRoot); // Tests truncated path (bytes32[] memory path2,) = testNaiveMerkle.computeSiblingPath(leafIndex); @@ -80,12 +72,12 @@ contract MerkleTest is Test, MerkleLibWrapper { } vm.expectRevert(); - this.verifyMembershipWrapper(truncatedPath, leaf, leafIndex, expectedRoot); + merkleLibHelper.verifyMembership(truncatedPath, leaf, leafIndex, expectedRoot); // Tests empty path bytes32[] memory emptyPath = new bytes32[](0); vm.expectRevert(); - this.verifyMembershipWrapper(emptyPath, leaf, leafIndex, expectedRoot); + merkleLibHelper.verifyMembership(emptyPath, leaf, leafIndex, expectedRoot); } function testComputeSiblingPathManuallyLeftChild() public { @@ -161,15 +153,44 @@ contract MerkleTest is Test, MerkleLibWrapper { } function testCalculateTreeHeightFromSize() external { - assertEq(Merkle.calculateTreeHeightFromSize(0), 1); - assertEq(Merkle.calculateTreeHeightFromSize(1), 1); - assertEq(Merkle.calculateTreeHeightFromSize(2), 1); - assertEq(Merkle.calculateTreeHeightFromSize(3), 2); - assertEq(Merkle.calculateTreeHeightFromSize(4), 2); - assertEq(Merkle.calculateTreeHeightFromSize(5), 3); - assertEq(Merkle.calculateTreeHeightFromSize(6), 3); - assertEq(Merkle.calculateTreeHeightFromSize(7), 3); - assertEq(Merkle.calculateTreeHeightFromSize(8), 3); - assertEq(Merkle.calculateTreeHeightFromSize(9), 4); + assertEq(calculateTreeHeightFromSize(0), 1); + assertEq(calculateTreeHeightFromSize(1), 1); + assertEq(calculateTreeHeightFromSize(2), 1); + assertEq(calculateTreeHeightFromSize(3), 2); + assertEq(calculateTreeHeightFromSize(4), 2); + assertEq(calculateTreeHeightFromSize(5), 3); + assertEq(calculateTreeHeightFromSize(6), 3); + assertEq(calculateTreeHeightFromSize(7), 3); + assertEq(calculateTreeHeightFromSize(8), 3); + assertEq(calculateTreeHeightFromSize(9), 4); + } + + /* + * @notice Calculates a tree height from the amount of elements in the tree + * @param _size - The amount of elements in the tree + */ + function calculateTreeHeightFromSize(uint256 _size) public pure returns (uint256) { + /// The code / formula that works below has one edge case at _size = 1, which we handle here + if (_size == 1) { + return 1; + } + + /// We need to store the original numer to check at the end if we are a power of two + uint256 originalNumber = _size; + + /// We need the height of the tree that will contain all of our leaves, + /// hence the next highest power of two from the amount of leaves - Math.ceil(Math.log2(x)) + uint256 height = 0; + + /// While size > 1, we divide by two, and count how many times we do this; producing a rudimentary way of calculating Math.Floor(Math.log2(x)) + while (_size > 1) { + _size >>= 1; + height++; + } + + /// @notice - We check if 2 ** height does not equal our original number. If so, this means that our size is not a power of two, + /// and hence we've rounded down (Math.floor) and have obtained the next lowest power of two instead of rounding up (Math.ceil) to obtain the next highest power of two and therefore we need to increment height before returning it. + /// If 2 ** height equals our original number, it means that we have a perfect power of two and Math.floor(Math.log2(x)) = Math.ceil(Math.log2(x)) and we can return height as-is + return (2 ** height) != originalNumber ? ++height : height; } } diff --git a/l1-contracts/test/merkle/helpers/MerkleLibHelper.sol b/l1-contracts/test/merkle/helpers/MerkleLibHelper.sol new file mode 100644 index 00000000000..e3444150c81 --- /dev/null +++ b/l1-contracts/test/merkle/helpers/MerkleLibHelper.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 Aztec Labs. +pragma solidity >=0.8.18; + +import {MerkleLib} from "../../../src/core/libraries/MerkleLib.sol"; + +// A wrapper used to be able to "call" library functions, instead of "jumping" to them, allowing forge to catch the reverts +contract MerkleLibHelper { + function verifyMembership( + bytes32[] memory _path, + bytes32 _leaf, + uint256 _index, + bytes32 _expectedRoot + ) external pure { + MerkleLib.verifyMembership(_path, _leaf, _index, _expectedRoot); + } +} From 03b4676ae45d4fef4d07b7a54d53f25281826d03 Mon Sep 17 00:00:00 2001 From: Esau Date: Wed, 13 Mar 2024 21:19:31 +0100 Subject: [PATCH 41/62] abc test fix move move fix test --- l1-contracts/src/core/libraries/MerkleLib.sol | 2 +- l1-contracts/test/NewOutbox.t.sol | 14 +- l1-contracts/test/Parity.t.sol | 85 ++++++ l1-contracts/test/merkle/Frontier.t.sol | 27 ++ l1-contracts/test/merkle/Merkle.t.sol | 252 ------------------ l1-contracts/test/merkle/MerkleLib.t.sol | 120 +++++++++ l1-contracts/test/merkle/Naive.t.sol | 83 ++++++ .../test/merkle/helpers/MerkleLibHelper.sol | 2 +- 8 files changed, 326 insertions(+), 259 deletions(-) create mode 100644 l1-contracts/test/Parity.t.sol create mode 100644 l1-contracts/test/merkle/Frontier.t.sol delete mode 100644 l1-contracts/test/merkle/Merkle.t.sol create mode 100644 l1-contracts/test/merkle/MerkleLib.t.sol create mode 100644 l1-contracts/test/merkle/Naive.t.sol diff --git a/l1-contracts/src/core/libraries/MerkleLib.sol b/l1-contracts/src/core/libraries/MerkleLib.sol index ec62404a6bc..85fbd247ad9 100644 --- a/l1-contracts/src/core/libraries/MerkleLib.sol +++ b/l1-contracts/src/core/libraries/MerkleLib.sol @@ -26,7 +26,7 @@ library MerkleLib { * And the elements would be ordered as: [ d3_index_2, d2_index_0, d1_index_1 ]. */ function verifyMembership( - bytes32[] memory _path, + bytes32[] calldata _path, bytes32 _leaf, uint256 _index, bytes32 _expectedRoot diff --git a/l1-contracts/test/NewOutbox.t.sol b/l1-contracts/test/NewOutbox.t.sol index 334cbe1ce07..45c617c9195 100644 --- a/l1-contracts/test/NewOutbox.t.sol +++ b/l1-contracts/test/NewOutbox.t.sol @@ -9,7 +9,7 @@ import {Errors} from "../src/core/libraries/Errors.sol"; import {DataStructures} from "../src/core/libraries/DataStructures.sol"; import {Hash} from "../src/core/libraries/Hash.sol"; import {NaiveMerkle} from "./merkle/Naive.sol"; -import {MerkleTest} from "./merkle/Merkle.t.sol"; +import {MerkleLibTest} from "./merkle/MerkleLib.t.sol"; contract NewOutboxTest is Test { using Hash for DataStructures.L2ToL1Msg; @@ -22,12 +22,12 @@ contract NewOutboxTest is Test { NewOutbox internal outbox; NaiveMerkle internal zeroedTree; - MerkleTest internal merkleTest; + MerkleLibTest internal merkleLibTest; function setUp() public { outbox = new NewOutbox(STATE_TRANSITIONER); zeroedTree = new NaiveMerkle(DEFAULT_TREE_HEIGHT); - merkleTest = new MerkleTest(); + merkleLibTest = new MerkleLibTest(); } function _fakeMessage(address _recipient) internal view returns (DataStructures.L2ToL1Msg memory) { @@ -60,8 +60,12 @@ contract NewOutboxTest is Test { outbox.insert(1, root, DEFAULT_TREE_HEIGHT); } + // This function tests the insertion of random arrays of L2 to L1 messages + // We make a naive tree with a computed height, insert the leafs into it, and compute a root. We then add the root as the root of the + // L2 to L1 message tree, expect for the correct event to be emitted, and then query for the root in the contract—making sure the roots, as well as the + // the tree height (which is also the length of the sibling path) match function testInsertVariedLeafs(bytes32[] calldata _messageLeafs) public { - uint256 bigTreeHeight = merkleTest.calculateTreeHeightFromSize(_messageLeafs.length); + uint256 bigTreeHeight = merkleLibTest.calculateTreeHeightFromSize(_messageLeafs.length); NaiveMerkle tree = new NaiveMerkle(bigTreeHeight); for (uint256 i = 0; i < _messageLeafs.length; i++) { @@ -217,7 +221,7 @@ contract NewOutboxTest is Test { uint256 numberOfMessages = bound(_size, 1, _recipients.length); DataStructures.L2ToL1Msg[] memory messages = new DataStructures.L2ToL1Msg[](numberOfMessages); - uint256 bigTreeHeight = merkleTest.calculateTreeHeightFromSize(numberOfMessages); + uint256 bigTreeHeight = merkleLibTest.calculateTreeHeightFromSize(numberOfMessages); NaiveMerkle tree = new NaiveMerkle(bigTreeHeight); for (uint256 i = 0; i < numberOfMessages; i++) { diff --git a/l1-contracts/test/Parity.t.sol b/l1-contracts/test/Parity.t.sol new file mode 100644 index 00000000000..af3d51a7db4 --- /dev/null +++ b/l1-contracts/test/Parity.t.sol @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 Aztec Labs. +pragma solidity >=0.8.18; + +import {Test} from "forge-std/Test.sol"; + +import {FrontierMerkle} from "./../src/core/messagebridge/frontier_tree/Frontier.sol"; +import {Constants} from "../src/core/libraries/ConstantsGen.sol"; + +contract ParityTest is Test { + function setUp() public {} + + // Checks whether sha root matches output of base parity circuit + function testRootMatchesBaseParity() public { + uint256[4] memory msgs = [ + 0x151de48ca3efbae39f180fe00b8f472ec9f25be10b4f283a87c6d78393537039, + 0x14c2ea9dedf77698d4afe23bc663263eed0bf9aa3a8b17d9b74812f185610f9e, + 0x1570cc6641699e3ae87fa258d80a6d853f7b8ccb211dc244d017e2ca6530f8a1, + 0x2806c860af67e9cd50000378411b8c4c4db172ceb2daa862b259b689ccbdc1e0 + ]; + + // We can't use Constants.NUM_MSGS_PER_BASE_PARITY directly when defining the array so we do the check here to + // ensure it does not get outdated. + assertEq( + msgs.length, + Constants.NUM_MSGS_PER_BASE_PARITY, + "NUM_MSGS_PER_BASE_PARITY changed, update msgs." + ); + + uint256 treeHeight = 2; // log_2(NUM_MSGS_PER_BASE_PARITY) + // We don't have log_2 directly accessible in solidity so I just do the following check here to ensure + // the hardcoded value is not outdated. + assertEq( + 2 ** treeHeight, + Constants.NUM_MSGS_PER_BASE_PARITY, + "Base parity circuit subtree height changed, update treeHeight." + ); + + FrontierMerkle frontier = new FrontierMerkle(treeHeight); + + for (uint256 i = 0; i < msgs.length; i++) { + frontier.insertLeaf(bytes32(msgs[i])); + } + + bytes32 expectedRoot = 0xb3a3fc1968999f2c2d798b900bdf0de41311be2a4d20496a7e792a521fc8abac; + assertEq(frontier.root(), expectedRoot, "Root does not match base parity circuit root"); + } + + // Checks whether sha root matches output of root parity circuit + function testRootMatchesRootParity() public { + // sha256 roots coming out of base parity circuits + uint256[4] memory baseRoots = [ + 0xb3a3fc1968999f2c2d798b900bdf0de41311be2a4d20496a7e792a521fc8abac, + 0x43f78e0ebc9633ce336a8c086064d898c32fb5d7d6011f5427459c0b8d14e91f, + 0x024259b6404280addcc9319bc5a32c9a5d56af5c93b2f941fa326064fbe9636c, + 0x53042d820859d80c474d4694e03778f8dc0ac88fc1c3a97b4369c1096e904ae7 + ]; + + // We can't use Constants.NUM_BASE_PARITY_PER_ROOT_PARITY directly when defining the array so we do the check here + // to ensure it does not get outdated. + assertEq( + baseRoots.length, + Constants.NUM_BASE_PARITY_PER_ROOT_PARITY, + "NUM_BASE_PARITY_PER_ROOT_PARITY changed, update baseRoots." + ); + + uint256 treeHeight = 2; // log_2(NUM_BASE_PARITY_PER_ROOT_PARITY) + // We don't have log_2 directly accessible in solidity so I just do the following check here to ensure + // the hardcoded value is not outdated. + assertEq( + 2 ** treeHeight, + Constants.NUM_BASE_PARITY_PER_ROOT_PARITY, + "Root parity circuit subtree height changed, update treeHeight." + ); + + FrontierMerkle frontier = new FrontierMerkle(treeHeight); + + for (uint256 i = 0; i < baseRoots.length; i++) { + frontier.insertLeaf(bytes32(baseRoots[i])); + } + + bytes32 expectedRoot = 0x8e7d8bf0ef7ebd1607cc7ff9f2fbacf4574ee5b692a5a5ac1e7b1594067b9049; + assertEq(frontier.root(), expectedRoot, "Root does not match root parity circuit root"); + } +} \ No newline at end of file diff --git a/l1-contracts/test/merkle/Frontier.t.sol b/l1-contracts/test/merkle/Frontier.t.sol new file mode 100644 index 00000000000..fa4cb85956e --- /dev/null +++ b/l1-contracts/test/merkle/Frontier.t.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 Aztec Labs. +pragma solidity >=0.8.18; + +import {Test} from "forge-std/Test.sol"; + +import {NaiveMerkle} from "./Naive.sol"; +import {FrontierMerkle} from "./../../src/core/messagebridge/frontier_tree/Frontier.sol"; + +contract FrontierTest is Test { + function setUp() public {} + + function testFrontier() public { + uint256 depth = 10; + + NaiveMerkle merkle = new NaiveMerkle(depth); + FrontierMerkle frontier = new FrontierMerkle(depth); + + uint256 upper = frontier.SIZE(); + for (uint256 i = 0; i < upper; i++) { + bytes32 leaf = sha256(abi.encode(i + 1)); + merkle.insertLeaf(leaf); + frontier.insertLeaf(leaf); + assertEq(merkle.computeRoot(), frontier.root(), "Frontier Roots should be equal"); + } + } +} diff --git a/l1-contracts/test/merkle/Merkle.t.sol b/l1-contracts/test/merkle/Merkle.t.sol deleted file mode 100644 index fc57fd37552..00000000000 --- a/l1-contracts/test/merkle/Merkle.t.sol +++ /dev/null @@ -1,252 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2023 Aztec Labs. -pragma solidity >=0.8.18; - -import {Test} from "forge-std/Test.sol"; - -import {NaiveMerkle} from "./Naive.sol"; -import {MerkleLib} from "../../src/core/libraries/MerkleLib.sol"; -import {MerkleLibHelper} from "./helpers/MerkleLibHelper.sol"; -import {Errors} from "../../src/core/libraries/Errors.sol"; -import {FrontierMerkle} from "./../../src/core/messagebridge/frontier_tree/Frontier.sol"; -import {Constants} from "../../src/core/libraries/ConstantsGen.sol"; - -contract MerkleTest is Test { - function setUp() public {} - - function testFrontier() public { - uint256 depth = 10; - - NaiveMerkle merkle = new NaiveMerkle(depth); - FrontierMerkle frontier = new FrontierMerkle(depth); - - uint256 upper = frontier.SIZE(); - for (uint256 i = 0; i < upper; i++) { - bytes32 leaf = sha256(abi.encode(i + 1)); - merkle.insertLeaf(leaf); - frontier.insertLeaf(leaf); - assertEq(merkle.computeRoot(), frontier.root(), "Frontier Roots should be equal"); - } - } - - function testNaiveSiblingPathAndMembershipVerification(uint256 _idx) public view { - uint256 leafIndex = bound(_idx, 0, merkle.SIZE() - 1); - - (bytes32[] memory path, bytes32 leaf) = testNaiveMerkle.computeSiblingPath(leafIndex); - - merkleLibHelper.verifyMembership(path, leaf, leafIndex, testNaiveMerkle.computeRoot()); - } - - function testNaiveBadSiblingPathAndMembershipVerification(uint256 _idx) public { - uint256 leafIndex = bound(_idx, 0, merkle.SIZE() - 1); - bytes32 expectedRoot = testNaiveMerkle.computeRoot(); - - // Tests garbled path - (bytes32[] memory path1, bytes32 leaf) = testNaiveMerkle.computeSiblingPath(leafIndex); - bytes32 temp1 = path1[0]; - path1[0] = path1[path1.length - 1]; - path1[path1.length - 1] = temp1; - vm.expectRevert(); - merkleLibHelper.verifyMembership(path1, leaf, leafIndex, expectedRoot); - - // Tests truncated path - (bytes32[] memory path2,) = testNaiveMerkle.computeSiblingPath(leafIndex); - bytes32[] memory truncatedPath = new bytes32[](path2.length - 1); - for (uint256 i = 0; i < truncatedPath.length; i++) { - truncatedPath[i] = path2[i]; - } - - vm.expectRevert(); - merkleLibHelper.verifyMembership(truncatedPath, leaf, leafIndex, expectedRoot); - - // Tests empty path - bytes32[] memory emptyPath = new bytes32[](0); - vm.expectRevert(); - merkleLibHelper.verifyMembership(emptyPath, leaf, leafIndex, expectedRoot); - } - - function testComputeSiblingPathManuallyLeftChild() public { - /// Creates a merkle tree with depth 3 and size 8, with leafs from 1 - 8 - NaiveMerkle manualTree = new NaiveMerkle(3); - for (uint256 i = 1; i <= 8; i++) { - bytes32 generatedLeaf = bytes32(abi.encode(i)); - manualTree.insertLeaf(generatedLeaf); - } - - /** - * We manually make a path; this is the sibling path of the leaf with the value of 1. - * This path, from leaf to root, consists a, b, and c; which correspond to the value of 2, then the hash of 3 and 4, - * and finally, the hash of 5 and 6 concatenated with the hash of 7 and 8; - * d0: [ root ] - * d1: [ ] [c] - * d2: [ ] [b] [ ] [ ] - * d3: [1] [a] [3] [4] [5] [6] [7] [8]. - */ - bytes32[3] memory expectedPath = [ - bytes32(abi.encode(2)), - sha256(bytes.concat(bytes32(abi.encode(3)), bytes32(abi.encode(4)))), - sha256( - bytes.concat( - sha256(bytes.concat(bytes32(abi.encode(5)), bytes32(abi.encode(6)))), - sha256(bytes.concat(bytes32(abi.encode(7)), bytes32(abi.encode(8)))) - ) - ) - ]; - - /// We then compute the sibling path using the tree and expect that our manual calculation should equal the computed one - (bytes32[] memory path, bytes32 leaf) = manualTree.computeSiblingPath(0); - assertEq(leaf, bytes32(abi.encode(1))); - assertEq(path[0], expectedPath[0]); - assertEq(path[1], expectedPath[1]); - assertEq(path[2], expectedPath[2]); - } - - function testComputeSiblingPathManuallyRightChild() public { - /// Creates a merkle tree with depth 3 and size 8, with leafs from 1 - 8 - NaiveMerkle manualTree = new NaiveMerkle(3); - for (uint256 i = 1; i <= 8; i++) { - bytes32 generatedLeaf = bytes32(abi.encode(i)); - manualTree.insertLeaf(generatedLeaf); - } - - /** - * We manually make a path; this is the sibling path of the leaf with the value of 8. - * This path, from leaf to root, consists of c a, b, and c; which correspond to the value of 7, then the hash of 5 and 6, - * and finally, the hash of 1 and 2 concatenated with the hash of 3 and 4; - * d0: [ root ] - * d1: [c] [ ] - * d2: [ ] [b] [b] [ ] - * d3: [1] [2] [3] [4] [5] [6] [a] [8]. - */ - bytes32[3] memory expectedPath = [ - bytes32(abi.encode(7)), - sha256(bytes.concat(bytes32(abi.encode(5)), bytes32(abi.encode(6)))), - sha256( - bytes.concat( - sha256(bytes.concat(bytes32(abi.encode(1)), bytes32(abi.encode(2)))), - sha256(bytes.concat(bytes32(abi.encode(3)), bytes32(abi.encode(4)))) - ) - ) - ]; - - /// We then compute the sibling path using the tree and expect that our manual calculation should equal the computed one - (bytes32[] memory path, bytes32 leaf) = manualTree.computeSiblingPath(7); - assertEq(leaf, bytes32(abi.encode(8))); - assertEq(path[0], expectedPath[0]); - assertEq(path[1], expectedPath[1]); - assertEq(path[2], expectedPath[2]); - } - - function testCalculateTreeHeightFromSize() external { - assertEq(calculateTreeHeightFromSize(0), 1); - assertEq(calculateTreeHeightFromSize(1), 1); - assertEq(calculateTreeHeightFromSize(2), 1); - assertEq(calculateTreeHeightFromSize(3), 2); - assertEq(calculateTreeHeightFromSize(4), 2); - assertEq(calculateTreeHeightFromSize(5), 3); - assertEq(calculateTreeHeightFromSize(6), 3); - assertEq(calculateTreeHeightFromSize(7), 3); - assertEq(calculateTreeHeightFromSize(8), 3); - assertEq(calculateTreeHeightFromSize(9), 4); - } - - /* - * @notice Calculates a tree height from the amount of elements in the tree - * @param _size - The amount of elements in the tree - */ - function calculateTreeHeightFromSize(uint256 _size) public pure returns (uint256) { - /// The code / formula that works below has one edge case at _size = 1, which we handle here - if (_size == 1) { - return 1; - } - - /// We need to store the original numer to check at the end if we are a power of two - uint256 originalNumber = _size; - - /// We need the height of the tree that will contain all of our leaves, - /// hence the next highest power of two from the amount of leaves - Math.ceil(Math.log2(x)) - uint256 height = 0; - - /// While size > 1, we divide by two, and count how many times we do this; producing a rudimentary way of calculating Math.Floor(Math.log2(x)) - while (_size > 1) { - _size >>= 1; - height++; - } - - /// @notice - We check if 2 ** height does not equal our original number. If so, this means that our size is not a power of two, - /// and hence we've rounded down (Math.floor) and have obtained the next lowest power of two instead of rounding up (Math.ceil) to obtain the next highest power of two and therefore we need to increment height before returning it. - /// If 2 ** height equals our original number, it means that we have a perfect power of two and Math.floor(Math.log2(x)) = Math.ceil(Math.log2(x)) and we can return height as-is - return (2 ** height) != originalNumber ? ++height : height; - // Checks whether sha root matches output of base parity circuit - function testRootMatchesBaseParity() public { - uint256[4] memory msgs = [ - 0x151de48ca3efbae39f180fe00b8f472ec9f25be10b4f283a87c6d78393537039, - 0x14c2ea9dedf77698d4afe23bc663263eed0bf9aa3a8b17d9b74812f185610f9e, - 0x1570cc6641699e3ae87fa258d80a6d853f7b8ccb211dc244d017e2ca6530f8a1, - 0x2806c860af67e9cd50000378411b8c4c4db172ceb2daa862b259b689ccbdc1e0 - ]; - - // We can't use Constants.NUM_MSGS_PER_BASE_PARITY directly when defining the array so we do the check here to - // ensure it does not get outdated. - assertEq( - msgs.length, - Constants.NUM_MSGS_PER_BASE_PARITY, - "NUM_MSGS_PER_BASE_PARITY changed, update msgs." - ); - - uint256 treeHeight = 2; // log_2(NUM_MSGS_PER_BASE_PARITY) - // We don't have log_2 directly accessible in solidity so I just do the following check here to ensure - // the hardcoded value is not outdated. - assertEq( - 2 ** treeHeight, - Constants.NUM_MSGS_PER_BASE_PARITY, - "Base parity circuit subtree height changed, update treeHeight." - ); - - FrontierMerkle frontier = new FrontierMerkle(treeHeight); - - for (uint256 i = 0; i < msgs.length; i++) { - frontier.insertLeaf(bytes32(msgs[i])); - } - - bytes32 expectedRoot = 0xb3a3fc1968999f2c2d798b900bdf0de41311be2a4d20496a7e792a521fc8abac; - assertEq(frontier.root(), expectedRoot, "Root does not match base parity circuit root"); - } - - // Checks whether sha root matches output of root parity circuit - function testRootMatchesRootParity() public { - // sha256 roots coming out of base parity circuits - uint256[4] memory baseRoots = [ - 0xb3a3fc1968999f2c2d798b900bdf0de41311be2a4d20496a7e792a521fc8abac, - 0x43f78e0ebc9633ce336a8c086064d898c32fb5d7d6011f5427459c0b8d14e91f, - 0x024259b6404280addcc9319bc5a32c9a5d56af5c93b2f941fa326064fbe9636c, - 0x53042d820859d80c474d4694e03778f8dc0ac88fc1c3a97b4369c1096e904ae7 - ]; - - // We can't use Constants.NUM_BASE_PARITY_PER_ROOT_PARITY directly when defining the array so we do the check here - // to ensure it does not get outdated. - assertEq( - baseRoots.length, - Constants.NUM_BASE_PARITY_PER_ROOT_PARITY, - "NUM_BASE_PARITY_PER_ROOT_PARITY changed, update baseRoots." - ); - - uint256 treeHeight = 2; // log_2(NUM_BASE_PARITY_PER_ROOT_PARITY) - // We don't have log_2 directly accessible in solidity so I just do the following check here to ensure - // the hardcoded value is not outdated. - assertEq( - 2 ** treeHeight, - Constants.NUM_BASE_PARITY_PER_ROOT_PARITY, - "Root parity circuit subtree height changed, update treeHeight." - ); - - FrontierMerkle frontier = new FrontierMerkle(treeHeight); - - for (uint256 i = 0; i < baseRoots.length; i++) { - frontier.insertLeaf(bytes32(baseRoots[i])); - } - - bytes32 expectedRoot = 0x8e7d8bf0ef7ebd1607cc7ff9f2fbacf4574ee5b692a5a5ac1e7b1594067b9049; - assertEq(frontier.root(), expectedRoot, "Root does not match root parity circuit root"); - } -} diff --git a/l1-contracts/test/merkle/MerkleLib.t.sol b/l1-contracts/test/merkle/MerkleLib.t.sol new file mode 100644 index 00000000000..473199c5c53 --- /dev/null +++ b/l1-contracts/test/merkle/MerkleLib.t.sol @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Aztec Labs. +pragma solidity >=0.8.18; + +import {Test} from "forge-std/Test.sol"; + +import {NaiveMerkle} from "./Naive.sol"; +import {MerkleLibHelper} from "./helpers/MerkleLibHelper.sol"; + +contract MerkleLibTest is Test { + MerkleLibHelper internal merkleLibHelper; + NaiveMerkle internal merkle; + uint256 public constant DEPTH = 10; + + function setUp() public { + merkleLibHelper = new MerkleLibHelper(); + + merkle = new NaiveMerkle(DEPTH); + uint256 treeSize = merkle.SIZE(); + for (uint256 i = 0; i < treeSize; i++) { + bytes32 generatedLeaf = sha256(abi.encode(i + 1)); + merkle.insertLeaf(generatedLeaf); + } + } + + function testVerifyMembership(uint256 _idx) public view { + uint256 leafIndex = bound(_idx, 0, merkle.SIZE() - 1); + + (bytes32[] memory path, bytes32 leaf) = merkle.computeSiblingPath(leafIndex); + + bytes32 expectedRoot = merkle.computeRoot(); + + merkleLibHelper.verifyMembership(path, leaf, leafIndex, expectedRoot); + } + + function testVerifyMembershipWithBadInput(uint256 _idx) public { + uint256 leafIndex = bound(_idx, 0, merkle.SIZE() - 1); + bytes32 expectedRoot = merkle.computeRoot(); + + // Tests garbled path + (bytes32[] memory path1, bytes32 leaf) = merkle.computeSiblingPath(leafIndex); + bytes32 temp1 = path1[0]; + path1[0] = path1[path1.length - 1]; + path1[path1.length - 1] = temp1; + vm.expectRevert(); + merkleLibHelper.verifyMembership(path1, leaf, leafIndex, expectedRoot); + + // Tests truncated path + (bytes32[] memory path2,) = merkle.computeSiblingPath(leafIndex); + bytes32[] memory truncatedPath = new bytes32[](path2.length - 1); + for (uint256 i = 0; i < truncatedPath.length; i++) { + truncatedPath[i] = path2[i]; + } + + vm.expectRevert(); + merkleLibHelper.verifyMembership(truncatedPath, leaf, leafIndex, expectedRoot); + + // Tests empty path + bytes32[] memory emptyPath = new bytes32[](0); + vm.expectRevert(); + merkleLibHelper.verifyMembership(emptyPath, leaf, leafIndex, expectedRoot); + } + + function testVerifyMembershipWithRandomSiblingPaths(uint256 _idx, bytes32[DEPTH] memory _siblingPath) public { + uint256 leafIndex = _idx % (2 ** DEPTH); + bytes32 expectedRoot = merkle.computeRoot(); + + bytes32[] memory siblingPath = new bytes32[](DEPTH); + for (uint256 i = 0; i < _siblingPath.length; i++) { + siblingPath[i] = _siblingPath[i]; + } + + bytes32 leaf = sha256(abi.encode(leafIndex + 1)); + + vm.expectRevert(); + merkleLibHelper.verifyMembership(siblingPath, leaf, leafIndex, expectedRoot); + } + + function testCalculateTreeHeightFromSize() external { + assertEq(calculateTreeHeightFromSize(0), 1); + assertEq(calculateTreeHeightFromSize(1), 1); + assertEq(calculateTreeHeightFromSize(2), 1); + assertEq(calculateTreeHeightFromSize(3), 2); + assertEq(calculateTreeHeightFromSize(4), 2); + assertEq(calculateTreeHeightFromSize(5), 3); + assertEq(calculateTreeHeightFromSize(6), 3); + assertEq(calculateTreeHeightFromSize(7), 3); + assertEq(calculateTreeHeightFromSize(8), 3); + assertEq(calculateTreeHeightFromSize(9), 4); + } + + /* + * @notice Calculates a tree height from the amount of elements in the tree + * @param _size - The amount of elements in the tree + */ + function calculateTreeHeightFromSize(uint256 _size) public pure returns (uint256) { + /// The code / formula that works below has one edge case at _size = 1, which we handle here + if (_size == 1) { + return 1; + } + + /// We need to store the original numer to check at the end if we are a power of two + uint256 originalNumber = _size; + + /// We need the height of the tree that will contain all of our leaves, + /// hence the next highest power of two from the amount of leaves - Math.ceil(Math.log2(x)) + uint256 height = 0; + + /// While size > 1, we divide by two, and count how many times we do this; producing a rudimentary way of calculating Math.Floor(Math.log2(x)) + while (_size > 1) { + _size >>= 1; + height++; + } + + /// @notice - We check if 2 ** height does not equal our original number. If so, this means that our size is not a power of two, + /// and hence we've rounded down (Math.floor) and have obtained the next lowest power of two instead of rounding up (Math.ceil) to obtain the next highest power of two and therefore we need to increment height before returning it. + /// If 2 ** height equals our original number, it means that we have a perfect power of two and Math.floor(Math.log2(x)) = Math.ceil(Math.log2(x)) and we can return height as-is + return (2 ** height) != originalNumber ? ++height : height; + } +} diff --git a/l1-contracts/test/merkle/Naive.t.sol b/l1-contracts/test/merkle/Naive.t.sol new file mode 100644 index 00000000000..91a7f9c01ca --- /dev/null +++ b/l1-contracts/test/merkle/Naive.t.sol @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Aztec Labs. +pragma solidity >=0.8.18; + +import {Test} from "forge-std/Test.sol"; + +import {NaiveMerkle} from "./Naive.sol"; + +contract MerkleTest is Test { + function setUp() public {} + + function testComputeSiblingPathManuallyLeftChild() public { + /// Creates a merkle tree with depth 3 and size 8, with leafs from 1 - 8 + NaiveMerkle manualTree = new NaiveMerkle(3); + for (uint256 i = 1; i <= 8; i++) { + bytes32 generatedLeaf = bytes32(abi.encode(i)); + manualTree.insertLeaf(generatedLeaf); + } + + /** + * We manually make a path; this is the sibling path of the leaf with the value of 1. + * This path, from leaf to root, consists a, b, and c; which correspond to the value of 2, then the hash of 3 and 4, + * and finally, the hash of 5 and 6 concatenated with the hash of 7 and 8; + * d0: [ root ] + * d1: [ ] [c] + * d2: [ ] [b] [ ] [ ] + * d3: [1] [a] [3] [4] [5] [6] [7] [8]. + */ + bytes32[3] memory expectedPath = [ + bytes32(abi.encode(2)), + sha256(bytes.concat(bytes32(abi.encode(3)), bytes32(abi.encode(4)))), + sha256( + bytes.concat( + sha256(bytes.concat(bytes32(abi.encode(5)), bytes32(abi.encode(6)))), + sha256(bytes.concat(bytes32(abi.encode(7)), bytes32(abi.encode(8)))) + ) + ) + ]; + + /// We then compute the sibling path using the tree and expect that our manual calculation should equal the computed one + (bytes32[] memory path, bytes32 leaf) = manualTree.computeSiblingPath(0); + assertEq(leaf, bytes32(abi.encode(1))); + assertEq(path[0], expectedPath[0]); + assertEq(path[1], expectedPath[1]); + assertEq(path[2], expectedPath[2]); + } + + function testComputeSiblingPathManuallyRightChild() public { + /// Creates a merkle tree with depth 3 and size 8, with leafs from 1 - 8 + NaiveMerkle manualTree = new NaiveMerkle(3); + for (uint256 i = 1; i <= 8; i++) { + bytes32 generatedLeaf = bytes32(abi.encode(i)); + manualTree.insertLeaf(generatedLeaf); + } + + /** + * We manually make a path; this is the sibling path of the leaf with the value of 8. + * This path, from leaf to root, consists of c a, b, and c; which correspond to the value of 7, then the hash of 5 and 6, + * and finally, the hash of 1 and 2 concatenated with the hash of 3 and 4; + * d0: [ root ] + * d1: [c] [ ] + * d2: [ ] [b] [b] [ ] + * d3: [1] [2] [3] [4] [5] [6] [a] [8]. + */ + bytes32[3] memory expectedPath = [ + bytes32(abi.encode(7)), + sha256(bytes.concat(bytes32(abi.encode(5)), bytes32(abi.encode(6)))), + sha256( + bytes.concat( + sha256(bytes.concat(bytes32(abi.encode(1)), bytes32(abi.encode(2)))), + sha256(bytes.concat(bytes32(abi.encode(3)), bytes32(abi.encode(4)))) + ) + ) + ]; + + /// We then compute the sibling path using the tree and expect that our manual calculation should equal the computed one + (bytes32[] memory path, bytes32 leaf) = manualTree.computeSiblingPath(7); + assertEq(leaf, bytes32(abi.encode(8))); + assertEq(path[0], expectedPath[0]); + assertEq(path[1], expectedPath[1]); + assertEq(path[2], expectedPath[2]); + } +} diff --git a/l1-contracts/test/merkle/helpers/MerkleLibHelper.sol b/l1-contracts/test/merkle/helpers/MerkleLibHelper.sol index e3444150c81..be8234ea023 100644 --- a/l1-contracts/test/merkle/helpers/MerkleLibHelper.sol +++ b/l1-contracts/test/merkle/helpers/MerkleLibHelper.sol @@ -7,7 +7,7 @@ import {MerkleLib} from "../../../src/core/libraries/MerkleLib.sol"; // A wrapper used to be able to "call" library functions, instead of "jumping" to them, allowing forge to catch the reverts contract MerkleLibHelper { function verifyMembership( - bytes32[] memory _path, + bytes32[] calldata _path, bytes32 _leaf, uint256 _index, bytes32 _expectedRoot From eecf80b81fa2b81cf2499c2864d59a9bd2e68b04 Mon Sep 17 00:00:00 2001 From: Esau Date: Wed, 13 Mar 2024 22:48:18 +0100 Subject: [PATCH 42/62] format --- l1-contracts/test/NewOutbox.t.sol | 6 ++---- l1-contracts/test/Parity.t.sol | 2 +- l1-contracts/test/merkle/MerkleLib.t.sol | 5 ++++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/l1-contracts/test/NewOutbox.t.sol b/l1-contracts/test/NewOutbox.t.sol index 45c617c9195..96d83279a7d 100644 --- a/l1-contracts/test/NewOutbox.t.sol +++ b/l1-contracts/test/NewOutbox.t.sol @@ -62,7 +62,7 @@ contract NewOutboxTest is Test { // This function tests the insertion of random arrays of L2 to L1 messages // We make a naive tree with a computed height, insert the leafs into it, and compute a root. We then add the root as the root of the - // L2 to L1 message tree, expect for the correct event to be emitted, and then query for the root in the contract—making sure the roots, as well as the + // L2 to L1 message tree, expect for the correct event to be emitted, and then query for the root in the contract—making sure the roots, as well as the // the tree height (which is also the length of the sibling path) match function testInsertVariedLeafs(bytes32[] calldata _messageLeafs) public { uint256 bigTreeHeight = merkleLibTest.calculateTreeHeightFromSize(_messageLeafs.length); @@ -92,9 +92,7 @@ contract NewOutboxTest is Test { vm.prank(NOT_RECIPIENT); vm.expectRevert( - abi.encodeWithSelector( - Errors.Outbox__InvalidRecipient.selector, address(this), NOT_RECIPIENT - ) + abi.encodeWithSelector(Errors.Outbox__InvalidRecipient.selector, address(this), NOT_RECIPIENT) ); outbox.consume(1, 1, fakeMessage, path); } diff --git a/l1-contracts/test/Parity.t.sol b/l1-contracts/test/Parity.t.sol index af3d51a7db4..de14ded13f8 100644 --- a/l1-contracts/test/Parity.t.sol +++ b/l1-contracts/test/Parity.t.sol @@ -82,4 +82,4 @@ contract ParityTest is Test { bytes32 expectedRoot = 0x8e7d8bf0ef7ebd1607cc7ff9f2fbacf4574ee5b692a5a5ac1e7b1594067b9049; assertEq(frontier.root(), expectedRoot, "Root does not match root parity circuit root"); } -} \ No newline at end of file +} diff --git a/l1-contracts/test/merkle/MerkleLib.t.sol b/l1-contracts/test/merkle/MerkleLib.t.sol index 473199c5c53..c1c86aad591 100644 --- a/l1-contracts/test/merkle/MerkleLib.t.sol +++ b/l1-contracts/test/merkle/MerkleLib.t.sol @@ -61,7 +61,10 @@ contract MerkleLibTest is Test { merkleLibHelper.verifyMembership(emptyPath, leaf, leafIndex, expectedRoot); } - function testVerifyMembershipWithRandomSiblingPaths(uint256 _idx, bytes32[DEPTH] memory _siblingPath) public { + function testVerifyMembershipWithRandomSiblingPaths( + uint256 _idx, + bytes32[DEPTH] memory _siblingPath + ) public { uint256 leafIndex = _idx % (2 ** DEPTH); bytes32 expectedRoot = merkle.computeRoot(); From 3bc409bcc710d0ce2d870c2e1c2201f739405ee2 Mon Sep 17 00:00:00 2001 From: Esau Date: Wed, 13 Mar 2024 22:50:39 +0100 Subject: [PATCH 43/62] slither --- l1-contracts/slither_output.md | 132 +++++++++++++++------------------ 1 file changed, 60 insertions(+), 72 deletions(-) diff --git a/l1-contracts/slither_output.md b/l1-contracts/slither_output.md index 7d248f7bcf8..65947f33e5f 100644 --- a/l1-contracts/slither_output.md +++ b/l1-contracts/slither_output.md @@ -1,14 +1,14 @@ Summary - [pess-unprotected-setter](#pess-unprotected-setter) (1 results) (High) - [uninitialized-local](#uninitialized-local) (2 results) (Medium) - - [unused-return](#unused-return) (2 results) (Medium) + - [unused-return](#unused-return) (1 results) (Medium) - [pess-dubious-typecast](#pess-dubious-typecast) (8 results) (Medium) - [missing-zero-check](#missing-zero-check) (2 results) (Low) - [reentrancy-events](#reentrancy-events) (2 results) (Low) - [timestamp](#timestamp) (4 results) (Low) - [pess-public-vs-external](#pess-public-vs-external) (7 results) (Low) - [assembly](#assembly) (2 results) (Informational) - - [dead-code](#dead-code) (6 results) (Informational) + - [dead-code](#dead-code) (5 results) (Informational) - [solc-version](#solc-version) (1 results) (Informational) - [low-level-calls](#low-level-calls) (1 results) (Informational) - [similar-names](#similar-names) (4 results) (Informational) @@ -18,9 +18,9 @@ Summary Impact: High Confidence: Medium - [ ] ID-0 -Function [Rollup.process(bytes,bytes32,bytes,bytes)](src/core/Rollup.sol#L58-L103) is a non-protected setter archive is written +Function [Rollup.process(bytes,bytes32,bytes,bytes)](src/core/Rollup.sol#L58-L101) is a non-protected setter archive is written -src/core/Rollup.sol#L58-L103 +src/core/Rollup.sol#L58-L101 ## uninitialized-local @@ -42,35 +42,29 @@ src/core/libraries/decoders/TxsDecoder.sol#L79 Impact: Medium Confidence: Medium - [ ] ID-3 -[Rollup.process(bytes,bytes32,bytes,bytes)](src/core/Rollup.sol#L58-L103) ignores return value by [NEW_INBOX.consume()](src/core/Rollup.sol#L93) +[Rollup.process(bytes,bytes32,bytes,bytes)](src/core/Rollup.sol#L58-L101) ignores return value by [(l1ToL2Msgs,l2ToL1Msgs) = MessagesDecoder.decode(_body)](src/core/Rollup.sol#L74) -src/core/Rollup.sol#L58-L103 - - - - [ ] ID-4 -[Rollup.process(bytes,bytes32,bytes,bytes)](src/core/Rollup.sol#L58-L103) ignores return value by [(l1ToL2Msgs,l2ToL1Msgs) = MessagesDecoder.decode(_body)](src/core/Rollup.sol#L74) - -src/core/Rollup.sol#L58-L103 +src/core/Rollup.sol#L58-L101 ## pess-dubious-typecast Impact: Medium Confidence: High - - [ ] ID-5 + - [ ] ID-4 Dubious typecast in [TxsDecoder.read1(bytes,uint256)](src/core/libraries/decoders/TxsDecoder.sol#L314-L316): bytes => bytes1 casting occurs in [uint256(uint8(bytes1(slice(_data,_offset,1))))](src/core/libraries/decoders/TxsDecoder.sol#L315) src/core/libraries/decoders/TxsDecoder.sol#L314-L316 - - [ ] ID-6 + - [ ] ID-5 Dubious typecast in [Outbox.sendL1Messages(bytes32[])](src/core/messagebridge/Outbox.sol#L38-L46): uint256 => uint32 casting occurs in [version = uint32(REGISTRY.getVersionFor(msg.sender))](src/core/messagebridge/Outbox.sol#L40) src/core/messagebridge/Outbox.sol#L38-L46 - - [ ] ID-7 + - [ ] ID-6 Dubious typecast in [Inbox.sendL2Message(DataStructures.L2Actor,uint32,bytes32,bytes32)](src/core/messagebridge/Inbox.sol#L45-L91): uint256 => uint64 casting occurs in [fee = uint64(msg.value)](src/core/messagebridge/Inbox.sol#L64) uint256 => uint32 casting occurs in [entries.insert(key,fee,uint32(_recipient.version),_deadline,_errIncompatibleEntryArguments)](src/core/messagebridge/Inbox.sol#L76) @@ -78,28 +72,28 @@ Dubious typecast in [Inbox.sendL2Message(DataStructures.L2Actor,uint32,bytes32,b src/core/messagebridge/Inbox.sol#L45-L91 - - [ ] ID-8 + - [ ] ID-7 Dubious typecast in [TxsDecoder.read4(bytes,uint256)](src/core/libraries/decoders/TxsDecoder.sol#L324-L326): bytes => bytes4 casting occurs in [uint256(uint32(bytes4(slice(_data,_offset,4))))](src/core/libraries/decoders/TxsDecoder.sol#L325) src/core/libraries/decoders/TxsDecoder.sol#L324-L326 - - [ ] ID-9 + - [ ] ID-8 Dubious typecast in [MessagesDecoder.read4(bytes,uint256)](src/core/libraries/decoders/MessagesDecoder.sol#L160-L162): bytes => bytes4 casting occurs in [uint256(uint32(bytes4(_data)))](src/core/libraries/decoders/MessagesDecoder.sol#L161) src/core/libraries/decoders/MessagesDecoder.sol#L160-L162 - - [ ] ID-10 + - [ ] ID-9 Dubious typecast in [Inbox.batchConsume(bytes32[],address)](src/core/messagebridge/Inbox.sol#L122-L143): uint256 => uint32 casting occurs in [expectedVersion = uint32(REGISTRY.getVersionFor(msg.sender))](src/core/messagebridge/Inbox.sol#L128) src/core/messagebridge/Inbox.sol#L122-L143 - - [ ] ID-11 + - [ ] ID-10 Dubious typecast in [HeaderLib.decode(bytes)](src/core/libraries/HeaderLib.sol#L143-L184): bytes => bytes32 casting occurs in [header.lastArchive = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L151-L153) bytes => bytes4 casting occurs in [header.lastArchive = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L151-L153) @@ -125,7 +119,7 @@ Dubious typecast in [HeaderLib.decode(bytes)](src/core/libraries/HeaderLib.sol#L src/core/libraries/HeaderLib.sol#L143-L184 - - [ ] ID-12 + - [ ] ID-11 Dubious typecast in [MessagesDecoder.read1(bytes,uint256)](src/core/libraries/decoders/MessagesDecoder.sol#L150-L152): bytes => bytes1 casting occurs in [uint256(uint8(bytes1(_data)))](src/core/libraries/decoders/MessagesDecoder.sol#L151) @@ -135,14 +129,14 @@ src/core/libraries/decoders/MessagesDecoder.sol#L150-L152 ## missing-zero-check Impact: Low Confidence: Medium - - [ ] ID-13 + - [ ] ID-12 [NewInbox.constructor(address,uint256)._rollup](src/core/messagebridge/NewInbox.sol#L41) lacks a zero-check on : - [ROLLUP = _rollup](src/core/messagebridge/NewInbox.sol#L42) src/core/messagebridge/NewInbox.sol#L41 - - [ ] ID-14 + - [ ] ID-13 [NewOutbox.constructor(address)._stateTransitioner](src/core/messagebridge/NewOutbox.sol#L30) lacks a zero-check on : - [STATE_TRANSITIONER = _stateTransitioner](src/core/messagebridge/NewOutbox.sol#L31) @@ -152,7 +146,7 @@ src/core/messagebridge/NewOutbox.sol#L30 ## reentrancy-events Impact: Low Confidence: Medium - - [ ] ID-15 + - [ ] ID-14 Reentrancy in [NewInbox.sendL2Message(DataStructures.L2Actor,bytes32,bytes32)](src/core/messagebridge/NewInbox.sol#L62-L99): External calls: - [index = currentTree.insertLeaf(leaf)](src/core/messagebridge/NewInbox.sol#L95) @@ -162,22 +156,22 @@ Reentrancy in [NewInbox.sendL2Message(DataStructures.L2Actor,bytes32,bytes32)](s src/core/messagebridge/NewInbox.sol#L62-L99 - - [ ] ID-16 -Reentrancy in [Rollup.process(bytes,bytes32,bytes,bytes)](src/core/Rollup.sol#L58-L103): + - [ ] ID-15 +Reentrancy in [Rollup.process(bytes,bytes32,bytes,bytes)](src/core/Rollup.sol#L58-L101): External calls: - [inbox.batchConsume(l1ToL2Msgs,msg.sender)](src/core/Rollup.sol#L90) - - [NEW_INBOX.consume()](src/core/Rollup.sol#L93) - - [outbox.sendL1Messages(l2ToL1Msgs)](src/core/Rollup.sol#L100) + - [inHash = NEW_INBOX.consume()](src/core/Rollup.sol#L92) + - [outbox.sendL1Messages(l2ToL1Msgs)](src/core/Rollup.sol#L98) Event emitted after the call(s): - - [L2BlockProcessed(header.globalVariables.blockNumber)](src/core/Rollup.sol#L102) + - [L2BlockProcessed(header.globalVariables.blockNumber)](src/core/Rollup.sol#L100) -src/core/Rollup.sol#L58-L103 +src/core/Rollup.sol#L58-L101 ## timestamp Impact: Low Confidence: Medium - - [ ] ID-17 + - [ ] ID-16 [Inbox.batchConsume(bytes32[],address)](src/core/messagebridge/Inbox.sol#L122-L143) uses timestamp for comparisons Dangerous comparisons: - [block.timestamp > entry.deadline](src/core/messagebridge/Inbox.sol#L136) @@ -185,7 +179,7 @@ Confidence: Medium src/core/messagebridge/Inbox.sol#L122-L143 - - [ ] ID-18 + - [ ] ID-17 [HeaderLib.validate(HeaderLib.Header,uint256,uint256,bytes32)](src/core/libraries/HeaderLib.sol#L106-L136) uses timestamp for comparisons Dangerous comparisons: - [_header.globalVariables.timestamp > block.timestamp](src/core/libraries/HeaderLib.sol#L120) @@ -193,7 +187,7 @@ src/core/messagebridge/Inbox.sol#L122-L143 src/core/libraries/HeaderLib.sol#L106-L136 - - [ ] ID-19 + - [ ] ID-18 [Inbox.sendL2Message(DataStructures.L2Actor,uint32,bytes32,bytes32)](src/core/messagebridge/Inbox.sol#L45-L91) uses timestamp for comparisons Dangerous comparisons: - [_deadline <= block.timestamp](src/core/messagebridge/Inbox.sol#L54) @@ -201,7 +195,7 @@ src/core/libraries/HeaderLib.sol#L106-L136 src/core/messagebridge/Inbox.sol#L45-L91 - - [ ] ID-20 + - [ ] ID-19 [Inbox.cancelL2Message(DataStructures.L1ToL2Msg,address)](src/core/messagebridge/Inbox.sol#L102-L113) uses timestamp for comparisons Dangerous comparisons: - [block.timestamp <= _message.deadline](src/core/messagebridge/Inbox.sol#L108) @@ -212,28 +206,28 @@ src/core/messagebridge/Inbox.sol#L102-L113 ## pess-public-vs-external Impact: Low Confidence: Medium - - [ ] ID-21 + - [ ] ID-20 The following public functions could be turned into external in [FrontierMerkle](src/core/messagebridge/frontier_tree/Frontier.sol#L7-L93) contract: [FrontierMerkle.constructor(uint256)](src/core/messagebridge/frontier_tree/Frontier.sol#L19-L27) src/core/messagebridge/frontier_tree/Frontier.sol#L7-L93 - - [ ] ID-22 + - [ ] ID-21 The following public functions could be turned into external in [Registry](src/core/messagebridge/Registry.sol#L22-L129) contract: [Registry.constructor()](src/core/messagebridge/Registry.sol#L29-L33) src/core/messagebridge/Registry.sol#L22-L129 - - [ ] ID-23 -The following public functions could be turned into external in [Rollup](src/core/Rollup.sol#L30-L112) contract: + - [ ] ID-22 +The following public functions could be turned into external in [Rollup](src/core/Rollup.sol#L30-L110) contract: [Rollup.constructor(IRegistry,IAvailabilityOracle)](src/core/Rollup.sol#L43-L49) -src/core/Rollup.sol#L30-L112 +src/core/Rollup.sol#L30-L110 - - [ ] ID-24 + - [ ] ID-23 The following public functions could be turned into external in [Outbox](src/core/messagebridge/Outbox.sol#L21-L148) contract: [Outbox.constructor(address)](src/core/messagebridge/Outbox.sol#L29-L31) [Outbox.get(bytes32)](src/core/messagebridge/Outbox.sol#L77-L84) @@ -242,7 +236,7 @@ The following public functions could be turned into external in [Outbox](src/cor src/core/messagebridge/Outbox.sol#L21-L148 - - [ ] ID-25 + - [ ] ID-24 The following public functions could be turned into external in [Inbox](src/core/messagebridge/Inbox.sol#L21-L231) contract: [Inbox.constructor(address)](src/core/messagebridge/Inbox.sol#L30-L32) [Inbox.contains(bytes32)](src/core/messagebridge/Inbox.sol#L174-L176) @@ -250,14 +244,14 @@ The following public functions could be turned into external in [Inbox](src/core src/core/messagebridge/Inbox.sol#L21-L231 - - [ ] ID-26 + - [ ] ID-25 The following public functions could be turned into external in [NewOutbox](src/core/messagebridge/NewOutbox.sol#L18-L131) contract: [NewOutbox.constructor(address)](src/core/messagebridge/NewOutbox.sol#L30-L32) src/core/messagebridge/NewOutbox.sol#L18-L131 - - [ ] ID-27 + - [ ] ID-26 The following public functions could be turned into external in [NewInbox](src/core/messagebridge/NewInbox.sol#L25-L128) contract: [NewInbox.constructor(address,uint256)](src/core/messagebridge/NewInbox.sol#L41-L52) @@ -267,7 +261,7 @@ src/core/messagebridge/NewInbox.sol#L25-L128 ## assembly Impact: Informational Confidence: High - - [ ] ID-28 + - [ ] ID-27 [MessagesDecoder.decode(bytes)](src/core/libraries/decoders/MessagesDecoder.sol#L60-L142) uses assembly - [INLINE ASM](src/core/libraries/decoders/MessagesDecoder.sol#L79-L81) - [INLINE ASM](src/core/libraries/decoders/MessagesDecoder.sol#L112-L118) @@ -275,7 +269,7 @@ Confidence: High src/core/libraries/decoders/MessagesDecoder.sol#L60-L142 - - [ ] ID-29 + - [ ] ID-28 [TxsDecoder.computeRoot(bytes32[])](src/core/libraries/decoders/TxsDecoder.sol#L256-L275) uses assembly - [INLINE ASM](src/core/libraries/decoders/TxsDecoder.sol#L263-L265) @@ -285,37 +279,31 @@ src/core/libraries/decoders/TxsDecoder.sol#L256-L275 ## dead-code Impact: Informational Confidence: Medium - - [ ] ID-30 + - [ ] ID-29 [Inbox._errIncompatibleEntryArguments(bytes32,uint64,uint64,uint32,uint32,uint32,uint32)](src/core/messagebridge/Inbox.sol#L212-L230) is never used and should be removed src/core/messagebridge/Inbox.sol#L212-L230 - - [ ] ID-31 + - [ ] ID-30 [Outbox._errNothingToConsume(bytes32)](src/core/messagebridge/Outbox.sol#L114-L116) is never used and should be removed src/core/messagebridge/Outbox.sol#L114-L116 - - [ ] ID-32 + - [ ] ID-31 [Hash.sha256ToField(bytes32)](src/core/libraries/Hash.sol#L59-L61) is never used and should be removed src/core/libraries/Hash.sol#L59-L61 - - [ ] ID-33 + - [ ] ID-32 [Inbox._errNothingToConsume(bytes32)](src/core/messagebridge/Inbox.sol#L197-L199) is never used and should be removed src/core/messagebridge/Inbox.sol#L197-L199 - - [ ] ID-34 -[Merkle.calculateTreeHeightFromSize(uint256)](src/core/libraries/Merkle.sol#L58-L81) is never used and should be removed - -src/core/libraries/Merkle.sol#L58-L81 - - - - [ ] ID-35 + - [ ] ID-33 [Outbox._errIncompatibleEntryArguments(bytes32,uint64,uint64,uint32,uint32,uint32,uint32)](src/core/messagebridge/Outbox.sol#L129-L147) is never used and should be removed src/core/messagebridge/Outbox.sol#L129-L147 @@ -324,13 +312,13 @@ src/core/messagebridge/Outbox.sol#L129-L147 ## solc-version Impact: Informational Confidence: High - - [ ] ID-36 + - [ ] ID-34 solc-0.8.23 is not recommended for deployment ## low-level-calls Impact: Informational Confidence: High - - [ ] ID-37 + - [ ] ID-35 Low level call in [Inbox.withdrawFees()](src/core/messagebridge/Inbox.sol#L148-L153): - [(success) = msg.sender.call{value: balance}()](src/core/messagebridge/Inbox.sol#L151) @@ -340,25 +328,25 @@ src/core/messagebridge/Inbox.sol#L148-L153 ## similar-names Impact: Informational Confidence: Medium - - [ ] ID-38 -Variable [Constants.LOGS_HASHES_NUM_BYTES_PER_BASE_ROLLUP](src/core/libraries/ConstantsGen.sol#L131) is too similar to [Constants.NOTE_HASHES_NUM_BYTES_PER_BASE_ROLLUP](src/core/libraries/ConstantsGen.sol#L124) + - [ ] ID-36 +Variable [Constants.LOGS_HASHES_NUM_BYTES_PER_BASE_ROLLUP](src/core/libraries/ConstantsGen.sol#L132) is too similar to [Constants.NOTE_HASHES_NUM_BYTES_PER_BASE_ROLLUP](src/core/libraries/ConstantsGen.sol#L125) -src/core/libraries/ConstantsGen.sol#L131 +src/core/libraries/ConstantsGen.sol#L132 - - [ ] ID-39 -Variable [Constants.L1_TO_L2_MESSAGE_LENGTH](src/core/libraries/ConstantsGen.sol#L111) is too similar to [Constants.L2_TO_L1_MESSAGE_LENGTH](src/core/libraries/ConstantsGen.sol#L112) + - [ ] ID-37 +Variable [Constants.L1_TO_L2_MESSAGE_LENGTH](src/core/libraries/ConstantsGen.sol#L112) is too similar to [Constants.L2_TO_L1_MESSAGE_LENGTH](src/core/libraries/ConstantsGen.sol#L113) -src/core/libraries/ConstantsGen.sol#L111 +src/core/libraries/ConstantsGen.sol#L112 - - [ ] ID-40 + - [ ] ID-38 Variable [NewOutbox.STATE_TRANSITIONER](src/core/messagebridge/NewOutbox.sol#L27) is too similar to [NewOutbox.constructor(address)._stateTransitioner](src/core/messagebridge/NewOutbox.sol#L30) src/core/messagebridge/NewOutbox.sol#L27 - - [ ] ID-41 + - [ ] ID-39 Variable [Rollup.AVAILABILITY_ORACLE](src/core/Rollup.sol#L33) is too similar to [Rollup.constructor(IRegistry,IAvailabilityOracle)._availabilityOracle](src/core/Rollup.sol#L43) src/core/Rollup.sol#L33 @@ -367,7 +355,7 @@ src/core/Rollup.sol#L33 ## constable-states Impact: Optimization Confidence: High - - [ ] ID-42 + - [ ] ID-40 [Rollup.lastWarpedBlockTs](src/core/Rollup.sol#L41) should be constant src/core/Rollup.sol#L41 @@ -376,37 +364,37 @@ src/core/Rollup.sol#L41 ## pess-multiple-storage-read Impact: Optimization Confidence: High - - [ ] ID-43 + - [ ] ID-41 In a function [NewOutbox.insert(uint256,bytes32,uint256)](src/core/messagebridge/NewOutbox.sol#L43-L63) variable [NewOutbox.roots](src/core/messagebridge/NewOutbox.sol#L28) is read multiple times src/core/messagebridge/NewOutbox.sol#L43-L63 - - [ ] ID-44 + - [ ] ID-42 In a function [NewInbox.sendL2Message(DataStructures.L2Actor,bytes32,bytes32)](src/core/messagebridge/NewInbox.sol#L62-L99) variable [NewInbox.inProgress](src/core/messagebridge/NewInbox.sol#L37) is read multiple times src/core/messagebridge/NewInbox.sol#L62-L99 - - [ ] ID-45 + - [ ] ID-43 In a function [FrontierMerkle.root()](src/core/messagebridge/frontier_tree/Frontier.sol#L43-L76) variable [FrontierMerkle.HEIGHT](src/core/messagebridge/frontier_tree/Frontier.sol#L8) is read multiple times src/core/messagebridge/frontier_tree/Frontier.sol#L43-L76 - - [ ] ID-46 + - [ ] ID-44 In a function [NewInbox.consume()](src/core/messagebridge/NewInbox.sol#L108-L127) variable [NewInbox.inProgress](src/core/messagebridge/NewInbox.sol#L37) is read multiple times src/core/messagebridge/NewInbox.sol#L108-L127 - - [ ] ID-47 + - [ ] ID-45 In a function [NewInbox.consume()](src/core/messagebridge/NewInbox.sol#L108-L127) variable [NewInbox.toConsume](src/core/messagebridge/NewInbox.sol#L35) is read multiple times src/core/messagebridge/NewInbox.sol#L108-L127 - - [ ] ID-48 + - [ ] ID-46 In a function [FrontierMerkle.root()](src/core/messagebridge/frontier_tree/Frontier.sol#L43-L76) variable [FrontierMerkle.frontier](src/core/messagebridge/frontier_tree/Frontier.sol#L13) is read multiple times src/core/messagebridge/frontier_tree/Frontier.sol#L43-L76 From bd639ae9de97b5a8cf2aa286d364a3f157d1b1e1 Mon Sep 17 00:00:00 2001 From: Esau Date: Wed, 13 Mar 2024 23:46:54 +0100 Subject: [PATCH 44/62] naming --- .../src/core/messagebridge/NewOutbox.sol | 6 ++--- l1-contracts/test/NewOutbox.t.sol | 24 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/l1-contracts/src/core/messagebridge/NewOutbox.sol b/l1-contracts/src/core/messagebridge/NewOutbox.sol index 3f5246e4d48..cd284ef0ddf 100644 --- a/l1-contracts/src/core/messagebridge/NewOutbox.sol +++ b/l1-contracts/src/core/messagebridge/NewOutbox.sol @@ -24,11 +24,11 @@ contract NewOutbox is INewOutbox { mapping(uint256 => bool) nullified; } - address public immutable STATE_TRANSITIONER; + address public immutable ROLLUP_CONTRACT; mapping(uint256 l2BlockNumber => RootData) public roots; constructor(address _stateTransitioner) { - STATE_TRANSITIONER = _stateTransitioner; + ROLLUP_CONTRACT = _stateTransitioner; } /** @@ -44,7 +44,7 @@ contract NewOutbox is INewOutbox { external override(INewOutbox) { - if (msg.sender != STATE_TRANSITIONER) { + if (msg.sender != ROLLUP_CONTRACT) { revert Errors.Outbox__Unauthorized(); } diff --git a/l1-contracts/test/NewOutbox.t.sol b/l1-contracts/test/NewOutbox.t.sol index 96d83279a7d..f5b10ecd850 100644 --- a/l1-contracts/test/NewOutbox.t.sol +++ b/l1-contracts/test/NewOutbox.t.sol @@ -14,8 +14,8 @@ import {MerkleLibTest} from "./merkle/MerkleLib.t.sol"; contract NewOutboxTest is Test { using Hash for DataStructures.L2ToL1Msg; - address internal constant STATE_TRANSITIONER = address(0x42069123); - address internal constant NOT_STATE_TRANSITIONER = address(0x69); + address internal constant ROLLUP_CONTRACT = address(0x42069123); + address internal constant NOT_ROLLUP_CONTRACT = address(0x69); address internal constant NOT_RECIPIENT = address(0x420); uint256 internal constant DEFAULT_TREE_HEIGHT = 2; uint256 internal constant AZTEC_VERSION = 0; @@ -25,7 +25,7 @@ contract NewOutboxTest is Test { MerkleLibTest internal merkleLibTest; function setUp() public { - outbox = new NewOutbox(STATE_TRANSITIONER); + outbox = new NewOutbox(ROLLUP_CONTRACT); zeroedTree = new NaiveMerkle(DEFAULT_TREE_HEIGHT); merkleLibTest = new MerkleLibTest(); } @@ -44,7 +44,7 @@ contract NewOutboxTest is Test { function testRevertIfInsertingFromNonRollup() public { bytes32 root = zeroedTree.computeRoot(); - vm.prank(NOT_STATE_TRANSITIONER); + vm.prank(NOT_ROLLUP_CONTRACT); vm.expectRevert(abi.encodeWithSelector(Errors.Outbox__Unauthorized.selector)); outbox.insert(1, root, DEFAULT_TREE_HEIGHT); } @@ -52,10 +52,10 @@ contract NewOutboxTest is Test { function testRevertIfInsertingDuplicate() public { bytes32 root = zeroedTree.computeRoot(); - vm.prank(STATE_TRANSITIONER); + vm.prank(ROLLUP_CONTRACT); outbox.insert(1, root, DEFAULT_TREE_HEIGHT); - vm.prank(STATE_TRANSITIONER); + vm.prank(ROLLUP_CONTRACT); vm.expectRevert(abi.encodeWithSelector(Errors.Outbox__RootAlreadySetAtBlock.selector, 1)); outbox.insert(1, root, DEFAULT_TREE_HEIGHT); } @@ -77,7 +77,7 @@ contract NewOutboxTest is Test { vm.expectEmit(true, true, true, true, address(outbox)); emit INewOutbox.RootAdded(1, root, bigTreeHeight); - vm.prank(STATE_TRANSITIONER); + vm.prank(ROLLUP_CONTRACT); outbox.insert(1, root, bigTreeHeight); (bytes32 actualRoot, uint256 actualHeight) = outbox.roots(1); @@ -128,7 +128,7 @@ contract NewOutboxTest is Test { tree.insertLeaf(leaf); bytes32 root = tree.computeRoot(); - vm.prank(STATE_TRANSITIONER); + vm.prank(ROLLUP_CONTRACT); outbox.insert(1, root, DEFAULT_TREE_HEIGHT); (bytes32[] memory path,) = tree.computeSiblingPath(0); @@ -145,7 +145,7 @@ contract NewOutboxTest is Test { tree.insertLeaf(leaf); bytes32 root = tree.computeRoot(); - vm.prank(STATE_TRANSITIONER); + vm.prank(ROLLUP_CONTRACT); outbox.insert(1, root, DEFAULT_TREE_HEIGHT); NaiveMerkle biggerTree = new NaiveMerkle(DEFAULT_TREE_HEIGHT + 1); @@ -174,7 +174,7 @@ contract NewOutboxTest is Test { modifiedTree.insertLeaf(modifiedLeaf); bytes32 modifiedRoot = modifiedTree.computeRoot(); - vm.prank(STATE_TRANSITIONER); + vm.prank(ROLLUP_CONTRACT); outbox.insert(1, root, DEFAULT_TREE_HEIGHT); (bytes32[] memory path,) = modifiedTree.computeSiblingPath(0); @@ -193,7 +193,7 @@ contract NewOutboxTest is Test { tree.insertLeaf(leaf); bytes32 root = tree.computeRoot(); - vm.prank(STATE_TRANSITIONER); + vm.prank(ROLLUP_CONTRACT); outbox.insert(1, root, DEFAULT_TREE_HEIGHT); (bytes32[] memory path,) = tree.computeSiblingPath(0); @@ -234,7 +234,7 @@ contract NewOutboxTest is Test { vm.expectEmit(true, true, true, true, address(outbox)); emit INewOutbox.RootAdded(_blockNumber, root, bigTreeHeight); - vm.prank(STATE_TRANSITIONER); + vm.prank(ROLLUP_CONTRACT); outbox.insert(_blockNumber, root, bigTreeHeight); for (uint256 i = 0; i < numberOfMessages; i++) { From b8ddbf8896cda46fcbcc7858f22b39cd0b8fa990 Mon Sep 17 00:00:00 2001 From: Esau Date: Wed, 13 Mar 2024 23:47:37 +0100 Subject: [PATCH 45/62] renames --- l1-contracts/test/merkle/Naive.t.sol | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/l1-contracts/test/merkle/Naive.t.sol b/l1-contracts/test/merkle/Naive.t.sol index 91a7f9c01ca..01881bbc16f 100644 --- a/l1-contracts/test/merkle/Naive.t.sol +++ b/l1-contracts/test/merkle/Naive.t.sol @@ -6,15 +6,15 @@ import {Test} from "forge-std/Test.sol"; import {NaiveMerkle} from "./Naive.sol"; -contract MerkleTest is Test { +contract NaiveTest is Test { function setUp() public {} function testComputeSiblingPathManuallyLeftChild() public { /// Creates a merkle tree with depth 3 and size 8, with leafs from 1 - 8 - NaiveMerkle manualTree = new NaiveMerkle(3); + NaiveMerkle tree = new NaiveMerkle(3); for (uint256 i = 1; i <= 8; i++) { bytes32 generatedLeaf = bytes32(abi.encode(i)); - manualTree.insertLeaf(generatedLeaf); + tree.insertLeaf(generatedLeaf); } /** @@ -38,7 +38,7 @@ contract MerkleTest is Test { ]; /// We then compute the sibling path using the tree and expect that our manual calculation should equal the computed one - (bytes32[] memory path, bytes32 leaf) = manualTree.computeSiblingPath(0); + (bytes32[] memory path, bytes32 leaf) = tree.computeSiblingPath(0); assertEq(leaf, bytes32(abi.encode(1))); assertEq(path[0], expectedPath[0]); assertEq(path[1], expectedPath[1]); @@ -47,10 +47,10 @@ contract MerkleTest is Test { function testComputeSiblingPathManuallyRightChild() public { /// Creates a merkle tree with depth 3 and size 8, with leafs from 1 - 8 - NaiveMerkle manualTree = new NaiveMerkle(3); + NaiveMerkle tree = new NaiveMerkle(3); for (uint256 i = 1; i <= 8; i++) { bytes32 generatedLeaf = bytes32(abi.encode(i)); - manualTree.insertLeaf(generatedLeaf); + tree.insertLeaf(generatedLeaf); } /** @@ -74,7 +74,7 @@ contract MerkleTest is Test { ]; /// We then compute the sibling path using the tree and expect that our manual calculation should equal the computed one - (bytes32[] memory path, bytes32 leaf) = manualTree.computeSiblingPath(7); + (bytes32[] memory path, bytes32 leaf) = tree.computeSiblingPath(7); assertEq(leaf, bytes32(abi.encode(8))); assertEq(path[0], expectedPath[0]); assertEq(path[1], expectedPath[1]); From 75b3ed1482b8651be9f34201f5efb639d4149d4b Mon Sep 17 00:00:00 2001 From: Esau Date: Wed, 13 Mar 2024 23:47:52 +0100 Subject: [PATCH 46/62] slith --- l1-contracts/slither_output.md | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/l1-contracts/slither_output.md b/l1-contracts/slither_output.md index 65947f33e5f..4d0cacff45a 100644 --- a/l1-contracts/slither_output.md +++ b/l1-contracts/slither_output.md @@ -11,7 +11,7 @@ Summary - [dead-code](#dead-code) (5 results) (Informational) - [solc-version](#solc-version) (1 results) (Informational) - [low-level-calls](#low-level-calls) (1 results) (Informational) - - [similar-names](#similar-names) (4 results) (Informational) + - [similar-names](#similar-names) (3 results) (Informational) - [constable-states](#constable-states) (1 results) (Optimization) - [pess-multiple-storage-read](#pess-multiple-storage-read) (6 results) (Optimization) ## pess-unprotected-setter @@ -138,7 +138,7 @@ src/core/messagebridge/NewInbox.sol#L41 - [ ] ID-13 [NewOutbox.constructor(address)._stateTransitioner](src/core/messagebridge/NewOutbox.sol#L30) lacks a zero-check on : - - [STATE_TRANSITIONER = _stateTransitioner](src/core/messagebridge/NewOutbox.sol#L31) + - [ROLLUP_CONTRACT = _stateTransitioner](src/core/messagebridge/NewOutbox.sol#L31) src/core/messagebridge/NewOutbox.sol#L30 @@ -341,12 +341,6 @@ src/core/libraries/ConstantsGen.sol#L112 - [ ] ID-38 -Variable [NewOutbox.STATE_TRANSITIONER](src/core/messagebridge/NewOutbox.sol#L27) is too similar to [NewOutbox.constructor(address)._stateTransitioner](src/core/messagebridge/NewOutbox.sol#L30) - -src/core/messagebridge/NewOutbox.sol#L27 - - - - [ ] ID-39 Variable [Rollup.AVAILABILITY_ORACLE](src/core/Rollup.sol#L33) is too similar to [Rollup.constructor(IRegistry,IAvailabilityOracle)._availabilityOracle](src/core/Rollup.sol#L43) src/core/Rollup.sol#L33 @@ -355,7 +349,7 @@ src/core/Rollup.sol#L33 ## constable-states Impact: Optimization Confidence: High - - [ ] ID-40 + - [ ] ID-39 [Rollup.lastWarpedBlockTs](src/core/Rollup.sol#L41) should be constant src/core/Rollup.sol#L41 @@ -364,37 +358,37 @@ src/core/Rollup.sol#L41 ## pess-multiple-storage-read Impact: Optimization Confidence: High - - [ ] ID-41 + - [ ] ID-40 In a function [NewOutbox.insert(uint256,bytes32,uint256)](src/core/messagebridge/NewOutbox.sol#L43-L63) variable [NewOutbox.roots](src/core/messagebridge/NewOutbox.sol#L28) is read multiple times src/core/messagebridge/NewOutbox.sol#L43-L63 - - [ ] ID-42 + - [ ] ID-41 In a function [NewInbox.sendL2Message(DataStructures.L2Actor,bytes32,bytes32)](src/core/messagebridge/NewInbox.sol#L62-L99) variable [NewInbox.inProgress](src/core/messagebridge/NewInbox.sol#L37) is read multiple times src/core/messagebridge/NewInbox.sol#L62-L99 - - [ ] ID-43 + - [ ] ID-42 In a function [FrontierMerkle.root()](src/core/messagebridge/frontier_tree/Frontier.sol#L43-L76) variable [FrontierMerkle.HEIGHT](src/core/messagebridge/frontier_tree/Frontier.sol#L8) is read multiple times src/core/messagebridge/frontier_tree/Frontier.sol#L43-L76 - - [ ] ID-44 + - [ ] ID-43 In a function [NewInbox.consume()](src/core/messagebridge/NewInbox.sol#L108-L127) variable [NewInbox.inProgress](src/core/messagebridge/NewInbox.sol#L37) is read multiple times src/core/messagebridge/NewInbox.sol#L108-L127 - - [ ] ID-45 + - [ ] ID-44 In a function [NewInbox.consume()](src/core/messagebridge/NewInbox.sol#L108-L127) variable [NewInbox.toConsume](src/core/messagebridge/NewInbox.sol#L35) is read multiple times src/core/messagebridge/NewInbox.sol#L108-L127 - - [ ] ID-46 + - [ ] ID-45 In a function [FrontierMerkle.root()](src/core/messagebridge/frontier_tree/Frontier.sol#L43-L76) variable [FrontierMerkle.frontier](src/core/messagebridge/frontier_tree/Frontier.sol#L13) is read multiple times src/core/messagebridge/frontier_tree/Frontier.sol#L43-L76 From 5c32f4fa14509f63993437515dfb721af9e74d6f Mon Sep 17 00:00:00 2001 From: Esau Date: Thu, 14 Mar 2024 14:53:26 +0100 Subject: [PATCH 47/62] Version update --- l1-contracts/test/NewOutbox.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l1-contracts/test/NewOutbox.t.sol b/l1-contracts/test/NewOutbox.t.sol index f5b10ecd850..6031d146731 100644 --- a/l1-contracts/test/NewOutbox.t.sol +++ b/l1-contracts/test/NewOutbox.t.sol @@ -18,7 +18,7 @@ contract NewOutboxTest is Test { address internal constant NOT_ROLLUP_CONTRACT = address(0x69); address internal constant NOT_RECIPIENT = address(0x420); uint256 internal constant DEFAULT_TREE_HEIGHT = 2; - uint256 internal constant AZTEC_VERSION = 0; + uint256 internal constant AZTEC_VERSION = 1; NewOutbox internal outbox; NaiveMerkle internal zeroedTree; From 8d5865625822ce04e7c5ab9374a4f09694afada1 Mon Sep 17 00:00:00 2001 From: Esau Date: Thu, 14 Mar 2024 15:09:54 +0100 Subject: [PATCH 48/62] rename --- l1-contracts/test/NewOutbox.t.sol | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/l1-contracts/test/NewOutbox.t.sol b/l1-contracts/test/NewOutbox.t.sol index 6031d146731..40e6521c1db 100644 --- a/l1-contracts/test/NewOutbox.t.sol +++ b/l1-contracts/test/NewOutbox.t.sol @@ -65,8 +65,8 @@ contract NewOutboxTest is Test { // L2 to L1 message tree, expect for the correct event to be emitted, and then query for the root in the contract—making sure the roots, as well as the // the tree height (which is also the length of the sibling path) match function testInsertVariedLeafs(bytes32[] calldata _messageLeafs) public { - uint256 bigTreeHeight = merkleLibTest.calculateTreeHeightFromSize(_messageLeafs.length); - NaiveMerkle tree = new NaiveMerkle(bigTreeHeight); + uint256 treeHeight = merkleLibTest.calculateTreeHeightFromSize(_messageLeafs.length); + NaiveMerkle tree = new NaiveMerkle(treeHeight); for (uint256 i = 0; i < _messageLeafs.length; i++) { vm.assume(_messageLeafs[i] != bytes32(0)); @@ -76,13 +76,13 @@ contract NewOutboxTest is Test { bytes32 root = tree.computeRoot(); vm.expectEmit(true, true, true, true, address(outbox)); - emit INewOutbox.RootAdded(1, root, bigTreeHeight); + emit INewOutbox.RootAdded(1, root, treeHeight); vm.prank(ROLLUP_CONTRACT); - outbox.insert(1, root, bigTreeHeight); + outbox.insert(1, root, treeHeight); (bytes32 actualRoot, uint256 actualHeight) = outbox.roots(1); assertEq(root, actualRoot); - assertEq(bigTreeHeight, actualHeight); + assertEq(treeHeight, actualHeight); } function testRevertIfConsumingMessageBelongingToOther() public { @@ -219,8 +219,8 @@ contract NewOutboxTest is Test { uint256 numberOfMessages = bound(_size, 1, _recipients.length); DataStructures.L2ToL1Msg[] memory messages = new DataStructures.L2ToL1Msg[](numberOfMessages); - uint256 bigTreeHeight = merkleLibTest.calculateTreeHeightFromSize(numberOfMessages); - NaiveMerkle tree = new NaiveMerkle(bigTreeHeight); + uint256 treeHeight = merkleLibTest.calculateTreeHeightFromSize(numberOfMessages); + NaiveMerkle tree = new NaiveMerkle(treeHeight); for (uint256 i = 0; i < numberOfMessages; i++) { DataStructures.L2ToL1Msg memory fakeMessage = _fakeMessage(_recipients[i]); @@ -233,9 +233,9 @@ contract NewOutboxTest is Test { bytes32 root = tree.computeRoot(); vm.expectEmit(true, true, true, true, address(outbox)); - emit INewOutbox.RootAdded(_blockNumber, root, bigTreeHeight); + emit INewOutbox.RootAdded(_blockNumber, root, treeHeight); vm.prank(ROLLUP_CONTRACT); - outbox.insert(_blockNumber, root, bigTreeHeight); + outbox.insert(_blockNumber, root, treeHeight); for (uint256 i = 0; i < numberOfMessages; i++) { (bytes32[] memory path, bytes32 leaf) = tree.computeSiblingPath(i); From b94bb511c44e08616a1d4e0c299f6db2cfc64d95 Mon Sep 17 00:00:00 2001 From: Esau Date: Thu, 14 Mar 2024 18:28:13 +0100 Subject: [PATCH 49/62] comments --- l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol | 4 ++-- l1-contracts/src/core/messagebridge/NewOutbox.sol | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol b/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol index 05430d535b4..a0532bbd634 100644 --- a/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol +++ b/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol @@ -51,9 +51,9 @@ interface INewOutbox { /** * @notice Checks to see if an index of the L2 to L1 message tree for a specific block has been consumed - * @dev - This message does not throw, and out-of-bounds access is considered valid, but will always return false + * @dev - This function does not throw. Out-of-bounds access is considered valid, but will always return false * @param _l2BlockNumber - The block number specifying the block that contains the index of the message we want to check - * @param _leafIndex - The index inside the merkle tree where the message is located + * @param _leafIndex - The index of the message inside the merkle tree */ function hasMessageBeenConsumedAtBlockAndIndex(uint256 _l2BlockNumber, uint256 _leafIndex) external diff --git a/l1-contracts/src/core/messagebridge/NewOutbox.sol b/l1-contracts/src/core/messagebridge/NewOutbox.sol index cd284ef0ddf..204a1289f9a 100644 --- a/l1-contracts/src/core/messagebridge/NewOutbox.sol +++ b/l1-contracts/src/core/messagebridge/NewOutbox.sol @@ -116,9 +116,9 @@ contract NewOutbox is INewOutbox { /** * @notice Checks to see if an index of the L2 to L1 message tree for a specific block has been consumed - * @dev - This message does not throw, and out-of-bounds access is considered valid, but will always return false + * @dev - This function does not throw. Out-of-bounds access is considered valid, but will always return false * @param _l2BlockNumber - The block number specifying the block that contains the index of the message we want to check - * @param _leafIndex - The index inside the merkle tree where the message is located + * @param _leafIndex - The index of the message inside the merkle tree */ function hasMessageBeenConsumedAtBlockAndIndex(uint256 _l2BlockNumber, uint256 _leafIndex) external From 3913aa92d749e7366bfaca6b752c9916d4801b4c Mon Sep 17 00:00:00 2001 From: Esau Date: Thu, 14 Mar 2024 18:32:35 +0100 Subject: [PATCH 50/62] comments --- l1-contracts/src/core/libraries/MerkleLib.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/l1-contracts/src/core/libraries/MerkleLib.sol b/l1-contracts/src/core/libraries/MerkleLib.sol index 85fbd247ad9..bd7c5fbb8bd 100644 --- a/l1-contracts/src/core/libraries/MerkleLib.sol +++ b/l1-contracts/src/core/libraries/MerkleLib.sol @@ -18,11 +18,11 @@ library MerkleLib { * @param _index - The index of the message inside the L2 to L1 message tree * @param _expectedRoot - The expected root to check the validity of the message and sibling path with. * @notice - - * E.g. A sibling path for a leaf at index 3 in a tree of depth 3 (between 5 and 8 leafs) consists of the 3 elements denoted as *'s + * E.g. A sibling path for a leaf at index 3 (L) in a tree of depth 3 (between 5 and 8 leafs) consists of the 3 elements denoted as *'s * d0: [ root ] * d1: [ ] [*] * d2: [*] [ ] [ ] [ ] - * d3: [ ] [ ] [*] [ ] [ ] [ ] [ ] [ ]. + * d3: [ ] [ ] [*] [L] [ ] [ ] [ ] [ ]. * And the elements would be ordered as: [ d3_index_2, d2_index_0, d1_index_1 ]. */ function verifyMembership( From bdc0396c4cd47d23d1ccf77fcfa95a4da2c62e98 Mon Sep 17 00:00:00 2001 From: esau <152162806+sklppy88@users.noreply.github.com> Date: Thu, 14 Mar 2024 20:27:36 -0500 Subject: [PATCH 51/62] Update INewOutbox.sol --- l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol b/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol index a0532bbd634..3a7c9e03af0 100644 --- a/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol +++ b/l1-contracts/src/core/interfaces/messagebridge/INewOutbox.sol @@ -45,8 +45,8 @@ interface INewOutbox { function consume( uint256 _l2BlockNumber, uint256 _leafIndex, - DataStructures.L2ToL1Msg memory _message, - bytes32[] memory _path + DataStructures.L2ToL1Msg calldata _message, + bytes32[] calldata _path ) external; /** From 7883ab665bc8c05bdc0f0ccd74a5492927f4b6c7 Mon Sep 17 00:00:00 2001 From: esau <152162806+sklppy88@users.noreply.github.com> Date: Fri, 15 Mar 2024 06:19:46 -0500 Subject: [PATCH 52/62] Update l1-contracts/src/core/messagebridge/NewOutbox.sol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jan Beneš --- l1-contracts/src/core/messagebridge/NewOutbox.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l1-contracts/src/core/messagebridge/NewOutbox.sol b/l1-contracts/src/core/messagebridge/NewOutbox.sol index 204a1289f9a..1c7352d6ddf 100644 --- a/l1-contracts/src/core/messagebridge/NewOutbox.sol +++ b/l1-contracts/src/core/messagebridge/NewOutbox.sol @@ -27,7 +27,7 @@ contract NewOutbox is INewOutbox { address public immutable ROLLUP_CONTRACT; mapping(uint256 l2BlockNumber => RootData) public roots; - constructor(address _stateTransitioner) { + constructor(address _rollup) { ROLLUP_CONTRACT = _stateTransitioner; } From 8103b96feed7643759b31ea2869497ea98195998 Mon Sep 17 00:00:00 2001 From: esau <152162806+sklppy88@users.noreply.github.com> Date: Fri, 15 Mar 2024 06:20:00 -0500 Subject: [PATCH 53/62] Update l1-contracts/src/core/messagebridge/NewOutbox.sol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jan Beneš --- l1-contracts/src/core/messagebridge/NewOutbox.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l1-contracts/src/core/messagebridge/NewOutbox.sol b/l1-contracts/src/core/messagebridge/NewOutbox.sol index 1c7352d6ddf..b172904e5d0 100644 --- a/l1-contracts/src/core/messagebridge/NewOutbox.sol +++ b/l1-contracts/src/core/messagebridge/NewOutbox.sol @@ -28,7 +28,7 @@ contract NewOutbox is INewOutbox { mapping(uint256 l2BlockNumber => RootData) public roots; constructor(address _rollup) { - ROLLUP_CONTRACT = _stateTransitioner; + ROLLUP_CONTRACT = _rollup; } /** From ba536ed47b01ea757b16e767e37061f839efd519 Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Thu, 14 Mar 2024 15:35:49 -0300 Subject: [PATCH 54/62] fix: Point docs links to current tag if available (#5219) Instead of pointing to master, load the current tag if it's available on the environment and use it for redirecting to github. --- docs/Dockerfile | 4 ++++ docs/src/preprocess/include_code.js | 13 ++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/docs/Dockerfile b/docs/Dockerfile index e9d9f58beec..f82f8187700 100644 --- a/docs/Dockerfile +++ b/docs/Dockerfile @@ -29,6 +29,10 @@ ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true WORKDIR /usr/src/yarn-project RUN ./bootstrap.sh +# Make COMMIT_TAG visible to build scripts +ARG COMMIT_TAG="" +ENV COMMIT_TAG=$COMMIT_TAG + WORKDIR /usr/src/docs RUN yarn && yarn build diff --git a/docs/src/preprocess/include_code.js b/docs/src/preprocess/include_code.js index ff2d97c30f4..01a37e44ce1 100644 --- a/docs/src/preprocess/include_code.js +++ b/docs/src/preprocess/include_code.js @@ -234,11 +234,14 @@ async function preprocessIncludeCode(markdownContent, filePath, rootDir) { filePath ); - const relativeCodeFilePath = path.resolve(rootDir, codeFilePath); - - let urlText = `${relativeCodeFilePath}#L${startLine}-L${endLine}`; - const tag = "master"; - const url = `https://github.com/AztecProtocol/aztec-packages/blob/${tag}/${relativeCodeFilePath}#L${startLine}-L${endLine}`; + const relativeCodeFilePath = path + .resolve(rootDir, codeFilePath) + .replace(/^\//, ""); + const urlText = `${relativeCodeFilePath}#L${startLine}-L${endLine}`; + const tag = process.env.COMMIT_TAG + ? `aztec-packages-${process.env.COMMIT_TAG}` + : "master"; + const url = `https://github.com/AztecProtocol/aztec-packages/blob/${tag}/${urlText}`; const title = noTitle ? "" : `title="${identifier}"`; const lineNumbers = noLineNumbers ? "" : "showLineNumbers"; From 11700813aacb98b10189fb135746c47da9f5d27d Mon Sep 17 00:00:00 2001 From: josh crites Date: Thu, 14 Mar 2024 15:25:20 -0400 Subject: [PATCH 55/62] chore(docs): Add details to getting started contract deployment (#5220) Add some feedback based on office hours today. --- .../getting_started/aztecnr-getting-started.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/docs/developers/getting_started/aztecnr-getting-started.md b/docs/docs/developers/getting_started/aztecnr-getting-started.md index 25b9bd1ddb0..08b091f943d 100644 --- a/docs/docs/developers/getting_started/aztecnr-getting-started.md +++ b/docs/docs/developers/getting_started/aztecnr-getting-started.md @@ -186,13 +186,25 @@ Use one of these `address`es as the `owner`. You can either copy it or export. To deploy the counter contract, [ensure the sandbox is running](../sandbox/references/sandbox-reference.md) and run this in the root of your Noir project: ```bash -aztec-cli deploy contracts/counter/target/counter-Counter.json --args 100 0x2fd4503a9b855a852272945df53d7173297c1469cceda31048b85118364b09a3 +aztec-cli deploy contracts/counter/target/counter-Counter.json --args 100 0x0a0ab6320e2981cc543fedb9ad0f524c0a750397ca3372508d14af5b3c3c7cf0 --private-key 0x2153536ff6628eee01cf4024889ff977a18d9fa61d0e414422f7681cf085c281 ``` You can also test the functions by applying what you learned in the [quickstart](./quickstart.md). Congratulations, you have now written, compiled, and deployed your first Aztec.nr smart contract! +Deploying your contract via the CLI will not register the deployed contract with the [PXE](../../learn/concepts/pxe/main.md). To do so, use `aztec-cli add-contract`. + +```bash +aztec-cli add-contract --contract-artifact contracts/counter/target/counter-Counter.json --contract-address +``` + +:::note + +You can also deploy contracts using Aztec.js. See [the next page](./aztecjs-getting-started.md) for details. + +::: + ## Install Noir LSP (recommended) Install the [Noir Language Support extension](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir) to get syntax highlighting, syntax error detection and go-to definitions for your Aztec contracts. From afa0555b2ddcae4eba3ca1356cc7e6a9e033374c Mon Sep 17 00:00:00 2001 From: Aztec Bot <49558828+AztecBot@users.noreply.github.com> Date: Thu, 14 Mar 2024 15:34:03 -0400 Subject: [PATCH 56/62] chore(master): Release 0.28.0 (#5192) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit :robot: I have created a release *beep* *boop* ---
aztec-package: 0.28.0 ## [0.28.0](https://github.com/AztecProtocol/aztec-packages/compare/aztec-package-v0.27.2...aztec-package-v0.28.0) (2024-03-14) ### ⚠ BREAKING CHANGES * Support contracts with no constructor ([#5175](https://github.com/AztecProtocol/aztec-packages/issues/5175)) ### Features * Support contracts with no constructor ([#5175](https://github.com/AztecProtocol/aztec-packages/issues/5175)) ([df7fa32](https://github.com/AztecProtocol/aztec-packages/commit/df7fa32f34e790231e091c38a4a6e84be5407763))
barretenberg.js: 0.28.0 ## [0.28.0](https://github.com/AztecProtocol/aztec-packages/compare/barretenberg.js-v0.27.2...barretenberg.js-v0.28.0) (2024-03-14) ### Miscellaneous * **barretenberg.js:** Synchronize aztec-packages versions
aztec-cli: 0.28.0 ## [0.28.0](https://github.com/AztecProtocol/aztec-packages/compare/aztec-cli-v0.27.2...aztec-cli-v0.28.0) (2024-03-14) ### ⚠ BREAKING CHANGES * Support contracts with no constructor ([#5175](https://github.com/AztecProtocol/aztec-packages/issues/5175)) ### Features * Support contracts with no constructor ([#5175](https://github.com/AztecProtocol/aztec-packages/issues/5175)) ([df7fa32](https://github.com/AztecProtocol/aztec-packages/commit/df7fa32f34e790231e091c38a4a6e84be5407763))
aztec-packages: 0.28.0 ## [0.28.0](https://github.com/AztecProtocol/aztec-packages/compare/aztec-packages-v0.27.2...aztec-packages-v0.28.0) (2024-03-14) ### ⚠ BREAKING CHANGES * Support contracts with no constructor ([#5175](https://github.com/AztecProtocol/aztec-packages/issues/5175)) ### Features * **avm-simulator:** Euclidean and field div ([#5181](https://github.com/AztecProtocol/aztec-packages/issues/5181)) ([037a38f](https://github.com/AztecProtocol/aztec-packages/commit/037a38f498ee7f9d9c530a4b3b236e9c377b377d)) * Isolate Plonk dependencies ([#5068](https://github.com/AztecProtocol/aztec-packages/issues/5068)) ([5cbbd7d](https://github.com/AztecProtocol/aztec-packages/commit/5cbbd7da89488f6f662f96d0a3532921534755b4)) * New brillig field operations and refactor of binary operations ([#5208](https://github.com/AztecProtocol/aztec-packages/issues/5208)) ([eb69504](https://github.com/AztecProtocol/aztec-packages/commit/eb6950462b1ab2a0c8f50722791c7b0b9f1daf83)) * Parallelize linearly dependent contribution in PG ([#4742](https://github.com/AztecProtocol/aztec-packages/issues/4742)) ([d1799ae](https://github.com/AztecProtocol/aztec-packages/commit/d1799aeccb328582fabed25811e756bf0453216c)) * Parity circuits ([#5082](https://github.com/AztecProtocol/aztec-packages/issues/5082)) ([335c46e](https://github.com/AztecProtocol/aztec-packages/commit/335c46e7b7eddc0396190e6dae7eb2255e3caa9e)) * Support contracts with no constructor ([#5175](https://github.com/AztecProtocol/aztec-packages/issues/5175)) ([df7fa32](https://github.com/AztecProtocol/aztec-packages/commit/df7fa32f34e790231e091c38a4a6e84be5407763)) * Track side effects in public ([#5129](https://github.com/AztecProtocol/aztec-packages/issues/5129)) ([d666f6f](https://github.com/AztecProtocol/aztec-packages/commit/d666f6f1a0a67fd95694bb5f42b3e7af19a0abea)), closes [#5185](https://github.com/AztecProtocol/aztec-packages/issues/5185) * Update SMT Circuit class and add gate relaxation functionality ([#5176](https://github.com/AztecProtocol/aztec-packages/issues/5176)) ([5948996](https://github.com/AztecProtocol/aztec-packages/commit/5948996c0bab8ee99c4686352b8475da38604f28)) ### Bug Fixes * **avm-transpiler:** FDIV and U128 test case ([#5200](https://github.com/AztecProtocol/aztec-packages/issues/5200)) ([6977e81](https://github.com/AztecProtocol/aztec-packages/commit/6977e8166b5c27685458a6e04e840b45a77d4765)) * Barretenberg-acir-tests-bb.js thru version bump ([#5216](https://github.com/AztecProtocol/aztec-packages/issues/5216)) ([9298f93](https://github.com/AztecProtocol/aztec-packages/commit/9298f932b2d22aa5a4c87dab90d5e72614f222da)) * Do not release docs on every commit to master ([#5214](https://github.com/AztecProtocol/aztec-packages/issues/5214)) ([c34a299](https://github.com/AztecProtocol/aztec-packages/commit/c34a299e354847e3e4e253b41921814e86b38645)) * Fail transaction if we revert in setup or teardown ([#5093](https://github.com/AztecProtocol/aztec-packages/issues/5093)) ([db9a960](https://github.com/AztecProtocol/aztec-packages/commit/db9a960a99db663a328b261e08917ce5f1dd4e69)) * Intermittent invert 0 in Goblin ([#5189](https://github.com/AztecProtocol/aztec-packages/issues/5189)) ([6c70624](https://github.com/AztecProtocol/aztec-packages/commit/6c7062443ae23cc75ac06b7ac1492d12f803d0e5)) * Point docs links to current tag if available ([#5219](https://github.com/AztecProtocol/aztec-packages/issues/5219)) ([0e9c7c7](https://github.com/AztecProtocol/aztec-packages/commit/0e9c7c757ed5501d01bb20a57f22e857cf50b93d)) * Remove embedded srs ([#5173](https://github.com/AztecProtocol/aztec-packages/issues/5173)) ([cfd673d](https://github.com/AztecProtocol/aztec-packages/commit/cfd673d6224e95a7b09eaa51e1f6535b277b2827)) * Split setup/teardown functions when there's no public app logic ([#5156](https://github.com/AztecProtocol/aztec-packages/issues/5156)) ([2ee13b3](https://github.com/AztecProtocol/aztec-packages/commit/2ee13b3e9d17d4715ec72c738cf74e75e3c1581f)) * Validate EthAddress size in aztec-nr ([#5198](https://github.com/AztecProtocol/aztec-packages/issues/5198)) ([201c5e1](https://github.com/AztecProtocol/aztec-packages/commit/201c5e1cf94b448f4f75f460a9838b526903f3ce)) ### Miscellaneous * Add dependency instructions to bberg README ([#5187](https://github.com/AztecProtocol/aztec-packages/issues/5187)) ([850febc](https://github.com/AztecProtocol/aztec-packages/commit/850febc31400b0f5ca2064d91833a847adc5df31)) * **avm-simulator:** Make sure we support Map storage ([#5207](https://github.com/AztecProtocol/aztec-packages/issues/5207)) ([08835f9](https://github.com/AztecProtocol/aztec-packages/commit/08835f99e11c479cb498b411b15a16305695039f)) * **avm-simulator:** Restructure contract storage tests ([#5194](https://github.com/AztecProtocol/aztec-packages/issues/5194)) ([fcdd1cc](https://github.com/AztecProtocol/aztec-packages/commit/fcdd1cc260c1faf14eb5fe719d5c7f5306699b1e)) * **docs:** Add details to getting started contract deployment ([#5220](https://github.com/AztecProtocol/aztec-packages/issues/5220)) ([5c267ae](https://github.com/AztecProtocol/aztec-packages/commit/5c267ae50561c36eb02b84e5f8f7043b929e906c)) * Moving wit comms and witness and comm labels from instance to oink ([#5199](https://github.com/AztecProtocol/aztec-packages/issues/5199)) ([19eb7f9](https://github.com/AztecProtocol/aztec-packages/commit/19eb7f9bd48f1f5fb8d9e9a2e172c8f0c2c9445b)) * Oink ([#5210](https://github.com/AztecProtocol/aztec-packages/issues/5210)) ([321f149](https://github.com/AztecProtocol/aztec-packages/commit/321f149dd720f2e74d3b4118bf75c910b466d0ed)) * Pull noir ([#5193](https://github.com/AztecProtocol/aztec-packages/issues/5193)) ([aa90f6e](https://github.com/AztecProtocol/aztec-packages/commit/aa90f6ed7bfae06bdf6990816d154bbd24993689)) * Trying to fix intermitent ci failure for boxes ([#5182](https://github.com/AztecProtocol/aztec-packages/issues/5182)) ([f988cb8](https://github.com/AztecProtocol/aztec-packages/commit/f988cb85a35fbc16690c81071d8153bd76c51185)) ### Documentation * **yellow-paper:** Add pseudocode for verifying broadcasted functions in contract deployment ([#4431](https://github.com/AztecProtocol/aztec-packages/issues/4431)) ([8bdb921](https://github.com/AztecProtocol/aztec-packages/commit/8bdb9213ff2560a83aadd7cc4af062e08e98bd22))
barretenberg: 0.28.0 ## [0.28.0](https://github.com/AztecProtocol/aztec-packages/compare/barretenberg-v0.27.2...barretenberg-v0.28.0) (2024-03-14) ### Features * **avm-simulator:** Euclidean and field div ([#5181](https://github.com/AztecProtocol/aztec-packages/issues/5181)) ([037a38f](https://github.com/AztecProtocol/aztec-packages/commit/037a38f498ee7f9d9c530a4b3b236e9c377b377d)) * Isolate Plonk dependencies ([#5068](https://github.com/AztecProtocol/aztec-packages/issues/5068)) ([5cbbd7d](https://github.com/AztecProtocol/aztec-packages/commit/5cbbd7da89488f6f662f96d0a3532921534755b4)) * New brillig field operations and refactor of binary operations ([#5208](https://github.com/AztecProtocol/aztec-packages/issues/5208)) ([eb69504](https://github.com/AztecProtocol/aztec-packages/commit/eb6950462b1ab2a0c8f50722791c7b0b9f1daf83)) * Parallelize linearly dependent contribution in PG ([#4742](https://github.com/AztecProtocol/aztec-packages/issues/4742)) ([d1799ae](https://github.com/AztecProtocol/aztec-packages/commit/d1799aeccb328582fabed25811e756bf0453216c)) * Update SMT Circuit class and add gate relaxation functionality ([#5176](https://github.com/AztecProtocol/aztec-packages/issues/5176)) ([5948996](https://github.com/AztecProtocol/aztec-packages/commit/5948996c0bab8ee99c4686352b8475da38604f28)) ### Bug Fixes * Barretenberg-acir-tests-bb.js thru version bump ([#5216](https://github.com/AztecProtocol/aztec-packages/issues/5216)) ([9298f93](https://github.com/AztecProtocol/aztec-packages/commit/9298f932b2d22aa5a4c87dab90d5e72614f222da)) * Intermittent invert 0 in Goblin ([#5189](https://github.com/AztecProtocol/aztec-packages/issues/5189)) ([6c70624](https://github.com/AztecProtocol/aztec-packages/commit/6c7062443ae23cc75ac06b7ac1492d12f803d0e5)) * Remove embedded srs ([#5173](https://github.com/AztecProtocol/aztec-packages/issues/5173)) ([cfd673d](https://github.com/AztecProtocol/aztec-packages/commit/cfd673d6224e95a7b09eaa51e1f6535b277b2827)) ### Miscellaneous * Add dependency instructions to bberg README ([#5187](https://github.com/AztecProtocol/aztec-packages/issues/5187)) ([850febc](https://github.com/AztecProtocol/aztec-packages/commit/850febc31400b0f5ca2064d91833a847adc5df31)) * Moving wit comms and witness and comm labels from instance to oink ([#5199](https://github.com/AztecProtocol/aztec-packages/issues/5199)) ([19eb7f9](https://github.com/AztecProtocol/aztec-packages/commit/19eb7f9bd48f1f5fb8d9e9a2e172c8f0c2c9445b)) * Oink ([#5210](https://github.com/AztecProtocol/aztec-packages/issues/5210)) ([321f149](https://github.com/AztecProtocol/aztec-packages/commit/321f149dd720f2e74d3b4118bf75c910b466d0ed))
--- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --- .release-please-manifest.json | 10 +++---- CHANGELOG.md | 48 +++++++++++++++++++++++++++++++++ barretenberg/CHANGELOG.md | 25 +++++++++++++++++ barretenberg/cpp/CMakeLists.txt | 2 +- barretenberg/ts/CHANGELOG.md | 7 +++++ barretenberg/ts/package.json | 2 +- yarn-project/aztec/CHANGELOG.md | 11 ++++++++ yarn-project/aztec/package.json | 2 +- yarn-project/cli/CHANGELOG.md | 11 ++++++++ yarn-project/cli/package.json | 2 +- 10 files changed, 111 insertions(+), 9 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 2fd41c1ede4..6300fa8e909 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,7 +1,7 @@ { - ".": "0.27.2", - "yarn-project/cli": "0.27.2", - "yarn-project/aztec": "0.27.2", - "barretenberg": "0.27.2", - "barretenberg/ts": "0.27.2" + ".": "0.28.0", + "yarn-project/cli": "0.28.0", + "yarn-project/aztec": "0.28.0", + "barretenberg": "0.28.0", + "barretenberg/ts": "0.28.0" } diff --git a/CHANGELOG.md b/CHANGELOG.md index d17f49c55b2..a2f5f4afb21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,53 @@ # Changelog +## [0.28.0](https://github.com/AztecProtocol/aztec-packages/compare/aztec-packages-v0.27.2...aztec-packages-v0.28.0) (2024-03-14) + + +### ⚠ BREAKING CHANGES + +* Support contracts with no constructor ([#5175](https://github.com/AztecProtocol/aztec-packages/issues/5175)) + +### Features + +* **avm-simulator:** Euclidean and field div ([#5181](https://github.com/AztecProtocol/aztec-packages/issues/5181)) ([037a38f](https://github.com/AztecProtocol/aztec-packages/commit/037a38f498ee7f9d9c530a4b3b236e9c377b377d)) +* Isolate Plonk dependencies ([#5068](https://github.com/AztecProtocol/aztec-packages/issues/5068)) ([5cbbd7d](https://github.com/AztecProtocol/aztec-packages/commit/5cbbd7da89488f6f662f96d0a3532921534755b4)) +* New brillig field operations and refactor of binary operations ([#5208](https://github.com/AztecProtocol/aztec-packages/issues/5208)) ([eb69504](https://github.com/AztecProtocol/aztec-packages/commit/eb6950462b1ab2a0c8f50722791c7b0b9f1daf83)) +* Parallelize linearly dependent contribution in PG ([#4742](https://github.com/AztecProtocol/aztec-packages/issues/4742)) ([d1799ae](https://github.com/AztecProtocol/aztec-packages/commit/d1799aeccb328582fabed25811e756bf0453216c)) +* Parity circuits ([#5082](https://github.com/AztecProtocol/aztec-packages/issues/5082)) ([335c46e](https://github.com/AztecProtocol/aztec-packages/commit/335c46e7b7eddc0396190e6dae7eb2255e3caa9e)) +* Support contracts with no constructor ([#5175](https://github.com/AztecProtocol/aztec-packages/issues/5175)) ([df7fa32](https://github.com/AztecProtocol/aztec-packages/commit/df7fa32f34e790231e091c38a4a6e84be5407763)) +* Track side effects in public ([#5129](https://github.com/AztecProtocol/aztec-packages/issues/5129)) ([d666f6f](https://github.com/AztecProtocol/aztec-packages/commit/d666f6f1a0a67fd95694bb5f42b3e7af19a0abea)), closes [#5185](https://github.com/AztecProtocol/aztec-packages/issues/5185) +* Update SMT Circuit class and add gate relaxation functionality ([#5176](https://github.com/AztecProtocol/aztec-packages/issues/5176)) ([5948996](https://github.com/AztecProtocol/aztec-packages/commit/5948996c0bab8ee99c4686352b8475da38604f28)) + + +### Bug Fixes + +* **avm-transpiler:** FDIV and U128 test case ([#5200](https://github.com/AztecProtocol/aztec-packages/issues/5200)) ([6977e81](https://github.com/AztecProtocol/aztec-packages/commit/6977e8166b5c27685458a6e04e840b45a77d4765)) +* Barretenberg-acir-tests-bb.js thru version bump ([#5216](https://github.com/AztecProtocol/aztec-packages/issues/5216)) ([9298f93](https://github.com/AztecProtocol/aztec-packages/commit/9298f932b2d22aa5a4c87dab90d5e72614f222da)) +* Do not release docs on every commit to master ([#5214](https://github.com/AztecProtocol/aztec-packages/issues/5214)) ([c34a299](https://github.com/AztecProtocol/aztec-packages/commit/c34a299e354847e3e4e253b41921814e86b38645)) +* Fail transaction if we revert in setup or teardown ([#5093](https://github.com/AztecProtocol/aztec-packages/issues/5093)) ([db9a960](https://github.com/AztecProtocol/aztec-packages/commit/db9a960a99db663a328b261e08917ce5f1dd4e69)) +* Intermittent invert 0 in Goblin ([#5189](https://github.com/AztecProtocol/aztec-packages/issues/5189)) ([6c70624](https://github.com/AztecProtocol/aztec-packages/commit/6c7062443ae23cc75ac06b7ac1492d12f803d0e5)) +* Point docs links to current tag if available ([#5219](https://github.com/AztecProtocol/aztec-packages/issues/5219)) ([0e9c7c7](https://github.com/AztecProtocol/aztec-packages/commit/0e9c7c757ed5501d01bb20a57f22e857cf50b93d)) +* Remove embedded srs ([#5173](https://github.com/AztecProtocol/aztec-packages/issues/5173)) ([cfd673d](https://github.com/AztecProtocol/aztec-packages/commit/cfd673d6224e95a7b09eaa51e1f6535b277b2827)) +* Split setup/teardown functions when there's no public app logic ([#5156](https://github.com/AztecProtocol/aztec-packages/issues/5156)) ([2ee13b3](https://github.com/AztecProtocol/aztec-packages/commit/2ee13b3e9d17d4715ec72c738cf74e75e3c1581f)) +* Validate EthAddress size in aztec-nr ([#5198](https://github.com/AztecProtocol/aztec-packages/issues/5198)) ([201c5e1](https://github.com/AztecProtocol/aztec-packages/commit/201c5e1cf94b448f4f75f460a9838b526903f3ce)) + + +### Miscellaneous + +* Add dependency instructions to bberg README ([#5187](https://github.com/AztecProtocol/aztec-packages/issues/5187)) ([850febc](https://github.com/AztecProtocol/aztec-packages/commit/850febc31400b0f5ca2064d91833a847adc5df31)) +* **avm-simulator:** Make sure we support Map storage ([#5207](https://github.com/AztecProtocol/aztec-packages/issues/5207)) ([08835f9](https://github.com/AztecProtocol/aztec-packages/commit/08835f99e11c479cb498b411b15a16305695039f)) +* **avm-simulator:** Restructure contract storage tests ([#5194](https://github.com/AztecProtocol/aztec-packages/issues/5194)) ([fcdd1cc](https://github.com/AztecProtocol/aztec-packages/commit/fcdd1cc260c1faf14eb5fe719d5c7f5306699b1e)) +* **docs:** Add details to getting started contract deployment ([#5220](https://github.com/AztecProtocol/aztec-packages/issues/5220)) ([5c267ae](https://github.com/AztecProtocol/aztec-packages/commit/5c267ae50561c36eb02b84e5f8f7043b929e906c)) +* Moving wit comms and witness and comm labels from instance to oink ([#5199](https://github.com/AztecProtocol/aztec-packages/issues/5199)) ([19eb7f9](https://github.com/AztecProtocol/aztec-packages/commit/19eb7f9bd48f1f5fb8d9e9a2e172c8f0c2c9445b)) +* Oink ([#5210](https://github.com/AztecProtocol/aztec-packages/issues/5210)) ([321f149](https://github.com/AztecProtocol/aztec-packages/commit/321f149dd720f2e74d3b4118bf75c910b466d0ed)) +* Pull noir ([#5193](https://github.com/AztecProtocol/aztec-packages/issues/5193)) ([aa90f6e](https://github.com/AztecProtocol/aztec-packages/commit/aa90f6ed7bfae06bdf6990816d154bbd24993689)) +* Trying to fix intermitent ci failure for boxes ([#5182](https://github.com/AztecProtocol/aztec-packages/issues/5182)) ([f988cb8](https://github.com/AztecProtocol/aztec-packages/commit/f988cb85a35fbc16690c81071d8153bd76c51185)) + + +### Documentation + +* **yellow-paper:** Add pseudocode for verifying broadcasted functions in contract deployment ([#4431](https://github.com/AztecProtocol/aztec-packages/issues/4431)) ([8bdb921](https://github.com/AztecProtocol/aztec-packages/commit/8bdb9213ff2560a83aadd7cc4af062e08e98bd22)) + ## [0.27.2](https://github.com/AztecProtocol/aztec-packages/compare/aztec-packages-v0.27.1...aztec-packages-v0.27.2) (2024-03-13) diff --git a/barretenberg/CHANGELOG.md b/barretenberg/CHANGELOG.md index f02fdeb2b31..e7de0a347d8 100644 --- a/barretenberg/CHANGELOG.md +++ b/barretenberg/CHANGELOG.md @@ -1,5 +1,30 @@ # Changelog +## [0.28.0](https://github.com/AztecProtocol/aztec-packages/compare/barretenberg-v0.27.2...barretenberg-v0.28.0) (2024-03-14) + + +### Features + +* **avm-simulator:** Euclidean and field div ([#5181](https://github.com/AztecProtocol/aztec-packages/issues/5181)) ([037a38f](https://github.com/AztecProtocol/aztec-packages/commit/037a38f498ee7f9d9c530a4b3b236e9c377b377d)) +* Isolate Plonk dependencies ([#5068](https://github.com/AztecProtocol/aztec-packages/issues/5068)) ([5cbbd7d](https://github.com/AztecProtocol/aztec-packages/commit/5cbbd7da89488f6f662f96d0a3532921534755b4)) +* New brillig field operations and refactor of binary operations ([#5208](https://github.com/AztecProtocol/aztec-packages/issues/5208)) ([eb69504](https://github.com/AztecProtocol/aztec-packages/commit/eb6950462b1ab2a0c8f50722791c7b0b9f1daf83)) +* Parallelize linearly dependent contribution in PG ([#4742](https://github.com/AztecProtocol/aztec-packages/issues/4742)) ([d1799ae](https://github.com/AztecProtocol/aztec-packages/commit/d1799aeccb328582fabed25811e756bf0453216c)) +* Update SMT Circuit class and add gate relaxation functionality ([#5176](https://github.com/AztecProtocol/aztec-packages/issues/5176)) ([5948996](https://github.com/AztecProtocol/aztec-packages/commit/5948996c0bab8ee99c4686352b8475da38604f28)) + + +### Bug Fixes + +* Barretenberg-acir-tests-bb.js thru version bump ([#5216](https://github.com/AztecProtocol/aztec-packages/issues/5216)) ([9298f93](https://github.com/AztecProtocol/aztec-packages/commit/9298f932b2d22aa5a4c87dab90d5e72614f222da)) +* Intermittent invert 0 in Goblin ([#5189](https://github.com/AztecProtocol/aztec-packages/issues/5189)) ([6c70624](https://github.com/AztecProtocol/aztec-packages/commit/6c7062443ae23cc75ac06b7ac1492d12f803d0e5)) +* Remove embedded srs ([#5173](https://github.com/AztecProtocol/aztec-packages/issues/5173)) ([cfd673d](https://github.com/AztecProtocol/aztec-packages/commit/cfd673d6224e95a7b09eaa51e1f6535b277b2827)) + + +### Miscellaneous + +* Add dependency instructions to bberg README ([#5187](https://github.com/AztecProtocol/aztec-packages/issues/5187)) ([850febc](https://github.com/AztecProtocol/aztec-packages/commit/850febc31400b0f5ca2064d91833a847adc5df31)) +* Moving wit comms and witness and comm labels from instance to oink ([#5199](https://github.com/AztecProtocol/aztec-packages/issues/5199)) ([19eb7f9](https://github.com/AztecProtocol/aztec-packages/commit/19eb7f9bd48f1f5fb8d9e9a2e172c8f0c2c9445b)) +* Oink ([#5210](https://github.com/AztecProtocol/aztec-packages/issues/5210)) ([321f149](https://github.com/AztecProtocol/aztec-packages/commit/321f149dd720f2e74d3b4118bf75c910b466d0ed)) + ## [0.27.2](https://github.com/AztecProtocol/aztec-packages/compare/barretenberg-v0.27.1...barretenberg-v0.27.2) (2024-03-13) diff --git a/barretenberg/cpp/CMakeLists.txt b/barretenberg/cpp/CMakeLists.txt index 323acb4f343..a21c4c2e4b5 100644 --- a/barretenberg/cpp/CMakeLists.txt +++ b/barretenberg/cpp/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.24 FATAL_ERROR) project( Barretenberg DESCRIPTION "BN254 elliptic curve library, and PLONK SNARK prover" - VERSION 0.27.2 # x-release-please-version + VERSION 0.28.0 # x-release-please-version LANGUAGES CXX C ) # Insert version into `bb` config file diff --git a/barretenberg/ts/CHANGELOG.md b/barretenberg/ts/CHANGELOG.md index f22f0b61ce5..b593f1e3c6d 100644 --- a/barretenberg/ts/CHANGELOG.md +++ b/barretenberg/ts/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [0.28.0](https://github.com/AztecProtocol/aztec-packages/compare/barretenberg.js-v0.27.2...barretenberg.js-v0.28.0) (2024-03-14) + + +### Miscellaneous + +* **barretenberg.js:** Synchronize aztec-packages versions + ## [0.27.2](https://github.com/AztecProtocol/aztec-packages/compare/barretenberg.js-v0.27.1...barretenberg.js-v0.27.2) (2024-03-13) diff --git a/barretenberg/ts/package.json b/barretenberg/ts/package.json index 2b5cde5c39c..8168b1f5fb8 100644 --- a/barretenberg/ts/package.json +++ b/barretenberg/ts/package.json @@ -1,6 +1,6 @@ { "name": "@aztec/bb.js", - "version": "0.27.2", + "version": "0.28.0", "homepage": "https://github.com/AztecProtocol/aztec-packages/tree/master/barretenberg/ts", "license": "MIT", "type": "module", diff --git a/yarn-project/aztec/CHANGELOG.md b/yarn-project/aztec/CHANGELOG.md index 03132b2c4a0..cf185d55a70 100644 --- a/yarn-project/aztec/CHANGELOG.md +++ b/yarn-project/aztec/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## [0.28.0](https://github.com/AztecProtocol/aztec-packages/compare/aztec-package-v0.27.2...aztec-package-v0.28.0) (2024-03-14) + + +### ⚠ BREAKING CHANGES + +* Support contracts with no constructor ([#5175](https://github.com/AztecProtocol/aztec-packages/issues/5175)) + +### Features + +* Support contracts with no constructor ([#5175](https://github.com/AztecProtocol/aztec-packages/issues/5175)) ([df7fa32](https://github.com/AztecProtocol/aztec-packages/commit/df7fa32f34e790231e091c38a4a6e84be5407763)) + ## [0.27.2](https://github.com/AztecProtocol/aztec-packages/compare/aztec-package-v0.27.1...aztec-package-v0.27.2) (2024-03-13) diff --git a/yarn-project/aztec/package.json b/yarn-project/aztec/package.json index bd83e85d1fd..bcf705383d1 100644 --- a/yarn-project/aztec/package.json +++ b/yarn-project/aztec/package.json @@ -1,6 +1,6 @@ { "name": "@aztec/aztec", - "version": "0.27.2", + "version": "0.28.0", "type": "module", "exports": { ".": "./dest/index.js" diff --git a/yarn-project/cli/CHANGELOG.md b/yarn-project/cli/CHANGELOG.md index 3decfa4b598..6fe01867bf3 100644 --- a/yarn-project/cli/CHANGELOG.md +++ b/yarn-project/cli/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## [0.28.0](https://github.com/AztecProtocol/aztec-packages/compare/aztec-cli-v0.27.2...aztec-cli-v0.28.0) (2024-03-14) + + +### ⚠ BREAKING CHANGES + +* Support contracts with no constructor ([#5175](https://github.com/AztecProtocol/aztec-packages/issues/5175)) + +### Features + +* Support contracts with no constructor ([#5175](https://github.com/AztecProtocol/aztec-packages/issues/5175)) ([df7fa32](https://github.com/AztecProtocol/aztec-packages/commit/df7fa32f34e790231e091c38a4a6e84be5407763)) + ## [0.27.2](https://github.com/AztecProtocol/aztec-packages/compare/aztec-cli-v0.27.1...aztec-cli-v0.27.2) (2024-03-13) diff --git a/yarn-project/cli/package.json b/yarn-project/cli/package.json index 12a2f38195e..07ff1dafc7d 100644 --- a/yarn-project/cli/package.json +++ b/yarn-project/cli/package.json @@ -1,6 +1,6 @@ { "name": "@aztec/cli", - "version": "0.27.2", + "version": "0.28.0", "type": "module", "main": "./dest/index.js", "bin": { From 1256848cb2c63cb3dea762a3abcc40a1d668ff40 Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Thu, 14 Mar 2024 17:18:42 -0300 Subject: [PATCH 57/62] fix: Docs links URL missing a letter (#5223) Seems like COMMIT_TAG strips the leading `v` as well. This seems to contradict what we're seeing here, but oh well. https://github.com/AztecProtocol/aztec-packages/blob/37bdc1820f92ad2cc545e6843a0dd90273c21e0e/build-system/scripts/setup_env#L17 --- docs/src/preprocess/include_code.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/preprocess/include_code.js b/docs/src/preprocess/include_code.js index 01a37e44ce1..08758311b53 100644 --- a/docs/src/preprocess/include_code.js +++ b/docs/src/preprocess/include_code.js @@ -239,7 +239,7 @@ async function preprocessIncludeCode(markdownContent, filePath, rootDir) { .replace(/^\//, ""); const urlText = `${relativeCodeFilePath}#L${startLine}-L${endLine}`; const tag = process.env.COMMIT_TAG - ? `aztec-packages-${process.env.COMMIT_TAG}` + ? `aztec-packages-v${process.env.COMMIT_TAG}` : "master"; const url = `https://github.com/AztecProtocol/aztec-packages/blob/${tag}/${urlText}`; From 84c24ec10c866cc3bc4b1ee37c6af59ffdd9a6de Mon Sep 17 00:00:00 2001 From: josh crites Date: Thu, 14 Mar 2024 16:42:24 -0400 Subject: [PATCH 58/62] chore(docs): add note on new initializer (#5224) updates the getting started guide to discuss initializer instead of constructor --- docs/docs/developers/getting_started/aztecnr-getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/developers/getting_started/aztecnr-getting-started.md b/docs/docs/developers/getting_started/aztecnr-getting-started.md index 08b091f943d..ae8b1e83e23 100644 --- a/docs/docs/developers/getting_started/aztecnr-getting-started.md +++ b/docs/docs/developers/getting_started/aztecnr-getting-started.md @@ -102,7 +102,7 @@ Add this below the imports. It declares the storage variables for our contract. Now we’ve got a mechanism for storing our private state, we can start using it to ensure the privacy of balances. -Let’s create a `constructor` method to run on deployment that assigns an initial supply of tokens to a specified owner. In the constructor we created in the first step, write this: +Let’s create a constructor method to run on deployment that assigns an initial supply of tokens to a specified owner. This function is called `initialize`, but behaves like a constructor. It is the `#[aztec(initializer)]` decorator that specifies that this function behaves like a constructor. Write this: #include_code constructor /noir-projects/noir-contracts/contracts/counter_contract/src/main.nr rust From d0dc528bfc3eb587659b0b841ce6a8e470846afc Mon Sep 17 00:00:00 2001 From: josh crites Date: Thu, 14 Mar 2024 17:26:50 -0400 Subject: [PATCH 59/62] fix(docs): Update other constructor refs in docs to use initializer (#5227) Please read [contributing guidelines](CONTRIBUTING.md) and remove this line. --------- Co-authored-by: Santiago Palladino --- .../functions/initializers.md | 32 ++++++++++++++++ .../writing_contracts/functions/main.md | 16 +++----- .../functions/write_constructor.md | 37 ------------------- docs/docs/developers/limitations/main.md | 2 +- docs/sidebars.js | 4 +- 5 files changed, 41 insertions(+), 50 deletions(-) create mode 100644 docs/docs/developers/contracts/writing_contracts/functions/initializers.md delete mode 100644 docs/docs/developers/contracts/writing_contracts/functions/write_constructor.md diff --git a/docs/docs/developers/contracts/writing_contracts/functions/initializers.md b/docs/docs/developers/contracts/writing_contracts/functions/initializers.md new file mode 100644 index 00000000000..a673264a4b3 --- /dev/null +++ b/docs/docs/developers/contracts/writing_contracts/functions/initializers.md @@ -0,0 +1,32 @@ +--- +title: How to write an initializer function +--- + +This page explains how to write an initializer function. + +To learn more about initializers, read [this](./main.md#initializer-functions). + +Initializers are regular functions that set an "initialized" flag (a nullifier) for the contract. A contract can only be initialized once, and contract functions can only be called after the contract has been initialized, much like a constructor. However, if a contract defines no initializers, it can be called at any time. Additionally, you can define as many initializer functions in a contract as you want, both private and public. + +## Annotate with `#[aztec(private)]` and `#[aztec(initializer)]` + + +Define your initiaizer like so: + +```rust +#[aztec(private)] +#[aztec(initializer)] +fn constructor(){ + // function logic here +} +``` + +## Initializer with logic + +Initializers are commonly used to set an admin, such as this example: + +#include_code constructor /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust + +Here, the initializer is calling a public function. It can also call a private function. Learn more about calling functions from functions [here](../functions/call_functions.md). + +To see constructors in action, check out the [Aztec.nr getting started guide](../../../getting_started/aztecnr-getting-started.md). diff --git a/docs/docs/developers/contracts/writing_contracts/functions/main.md b/docs/docs/developers/contracts/writing_contracts/functions/main.md index 91b0fea0ca6..acd3b2c1e1e 100644 --- a/docs/docs/developers/contracts/writing_contracts/functions/main.md +++ b/docs/docs/developers/contracts/writing_contracts/functions/main.md @@ -6,17 +6,13 @@ Functions serve as the building blocks of smart contracts. Functions can be eith For a more practical guide of using multiple types of functions, follow the [token tutorial](../../../tutorials/writing_token_contract.md). -Currently, any function is "mutable" in the sense that it might alter state. However, we also support support static calls, similarly to EVM. A static call is essentially a call that does not alter state (it keeps state static). +Currently, any function is "mutable" in the sense that it might alter state. However, we also support support static calls, similarly to EVM. A static call is essentially a call that does not alter state (it keeps state static). -## Constructors +## Initializer functions -Every smart contract has a private `constructor` function which is called when the contract is deployed. +Smart contracts may have one, or many, initializer functions which are called when the contract is deployed. -A special constructor function must be declared within a contract's scope. - -A constructor doesn't have a name, because its purpose is clear: to initialize contract state. -In Aztec terminology, a constructor is always a 'private function' (i.e. it cannot be a public function). -A constructor behaves almost identically to any other function. It is just important for Aztec to be able to identify this function as special: it may only be called once, and will not be deployed as part of the contract. +Initializers are regular functions that set an "initialized" flag (a nullifier) for the contract. A contract can only be initialized once, and contract functions can only be called after the contract has been initialized, much like a constructor. However, if a contract defines no initializers, it can be called at any time. Additionally, you can define as many initializer functions in a contract as you want, both private and public. ## Oracles @@ -26,7 +22,7 @@ Explore this section to learn: - [How function visibility works in Aztec](./visibility.md) - [Public, private, and unconstrained functions](./public_private_unconstrained.md), and how to write them -- How to write a [constructor](./write_constructor.md) +- How to write an [initializer function](./initializers.md) - [Calling functions from within the same smart contract and from different contracts](./call_functions.md), including calling private functions from private functions, public from public, and even private from public - [Oracles](../oracles/main.md) and how Aztec smart contracts might use them -- [How functions work under the hood](./inner_workings.md) \ No newline at end of file +- [How functions work under the hood](./inner_workings.md) diff --git a/docs/docs/developers/contracts/writing_contracts/functions/write_constructor.md b/docs/docs/developers/contracts/writing_contracts/functions/write_constructor.md deleted file mode 100644 index 618f5ff1863..00000000000 --- a/docs/docs/developers/contracts/writing_contracts/functions/write_constructor.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -title: How to write a constructor ---- - -This page explains how to write a constructor function. - -To learn more about constructors, read [this](./main.md#constructors). - -## Annotate with `#[aztec(private)]` - -Currently, all constructors in Aztec must be private. - -Define your constructor like so: - -```rust -#[aztec(private)] -fn constructor() -``` - -## Option 1: Empty constructor - -Your constructor does not need to do anything; you can leave it blank like so: - -```rust -#[aztec(private)] -fn constructor() {} -``` - -## Option 2: Constructor with logic - -Constructors are commonly used to set an admin, such as this example: - -#include_code constructor /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust - -Here, the constructor is calling a public function. It can also call a private function. Learn more about calling functions from functions [here](../functions/call_functions.md). - -To see constructors in action, check out the [Aztec.nr getting started guide](../../../getting_started/aztecnr-getting-started.md). diff --git a/docs/docs/developers/limitations/main.md b/docs/docs/developers/limitations/main.md index baf3e719021..03a43d4d65d 100644 --- a/docs/docs/developers/limitations/main.md +++ b/docs/docs/developers/limitations/main.md @@ -29,7 +29,7 @@ Help shape and define: - It is a testing environment, it is insecure, unaudited and does not generate any proofs, its only for testing purposes; - Constructors can not call nor alter public state - - The constructor is executed exclusively in private domain, WITHOUT the ability to call public functions or alter public state. This means to set initial storage values, you need to follow a pattern similar to [proxies in Ethereum](https://blog.openzeppelin.com/proxy-patterns), where you `initialize` the contract with values after it have been deployed, see [constructor](../contracts/writing_contracts/functions/write_constructor.md). + - The constructor is executed exclusively in private domain, WITHOUT the ability to call public functions or alter public state. This means to set initial storage values, you need to follow a pattern similar to [proxies in Ethereum](https://blog.openzeppelin.com/proxy-patterns), where you `initialize` the contract with values after it have been deployed, see [initializer functions](../contracts/writing_contracts/functions/initializers.md). - Beware that what you think of as a `view` could alter state ATM! Notably the account could alter state or re-enter whenever the account contract's `is_valid` function is called. - `msg_sender` is currently leaking when doing private -> public calls - The `msg_sender` will always be set, if you call a public function from the private world, the `msg_sender` will be set to the private caller's address. See [function context](../contracts/writing_contracts/functions/context.md). diff --git a/docs/sidebars.js b/docs/sidebars.js index 1fb6ee6910f..3e23ca68acd 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -338,7 +338,7 @@ const sidebars = { "developers/contracts/writing_contracts/layout", "developers/contracts/writing_contracts/example_contract", { - label: "Functions and Constructors", + label: "Functions and Initializers", type: "category", link: { type: "doc", @@ -349,7 +349,7 @@ const sidebars = { "developers/contracts/writing_contracts/functions/public_private_unconstrained", "developers/contracts/writing_contracts/functions/visibility", "developers/contracts/writing_contracts/functions/call_functions", - "developers/contracts/writing_contracts/functions/write_constructor", + "developers/contracts/writing_contracts/functions/initializers", "developers/contracts/writing_contracts/functions/compute_note_hash_and_nullifier", "developers/contracts/writing_contracts/functions/inner_workings", ], From 9698d4460c5258efcaf214ad0c41c55d1d71bf07 Mon Sep 17 00:00:00 2001 From: Esau Date: Fri, 15 Mar 2024 12:39:58 +0100 Subject: [PATCH 60/62] move to util --- l1-contracts/test/merkle/TestUtil.sol | 49 +++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 l1-contracts/test/merkle/TestUtil.sol diff --git a/l1-contracts/test/merkle/TestUtil.sol b/l1-contracts/test/merkle/TestUtil.sol new file mode 100644 index 00000000000..4ca20511fcd --- /dev/null +++ b/l1-contracts/test/merkle/TestUtil.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 Aztec Labs. +pragma solidity >=0.8.18; + +import {Test} from "forge-std/Test.sol"; + +contract MerkleTestUtil is Test { + /* + * @notice Calculates a tree height from the amount of elements in the tree + * @param _size - The amount of elements in the tree + */ + function calculateTreeHeightFromSize(uint256 _size) public pure returns (uint256) { + /// The code / formula that works below has one edge case at _size = 1, which we handle here + if (_size == 1) { + return 1; + } + + /// We need to store the original numer to check at the end if we are a power of two + uint256 originalNumber = _size; + + /// We need the height of the tree that will contain all of our leaves, + /// hence the next highest power of two from the amount of leaves - Math.ceil(Math.log2(x)) + uint256 height = 0; + + /// While size > 1, we divide by two, and count how many times we do this; producing a rudimentary way of calculating Math.Floor(Math.log2(x)) + while (_size > 1) { + _size >>= 1; + height++; + } + + /// @notice - We check if 2 ** height does not equal our original number. If so, this means that our size is not a power of two, + /// and hence we've rounded down (Math.floor) and have obtained the next lowest power of two instead of rounding up (Math.ceil) to obtain the next highest power of two and therefore we need to increment height before returning it. + /// If 2 ** height equals our original number, it means that we have a perfect power of two and Math.floor(Math.log2(x)) = Math.ceil(Math.log2(x)) and we can return height as-is + return (2 ** height) != originalNumber ? ++height : height; + } + + function testCalculateTreeHeightFromSize() external { + assertEq(calculateTreeHeightFromSize(0), 1); + assertEq(calculateTreeHeightFromSize(1), 1); + assertEq(calculateTreeHeightFromSize(2), 1); + assertEq(calculateTreeHeightFromSize(3), 2); + assertEq(calculateTreeHeightFromSize(4), 2); + assertEq(calculateTreeHeightFromSize(5), 3); + assertEq(calculateTreeHeightFromSize(6), 3); + assertEq(calculateTreeHeightFromSize(7), 3); + assertEq(calculateTreeHeightFromSize(8), 3); + assertEq(calculateTreeHeightFromSize(9), 4); + } +} From 1735d7550931cd28063ef3be57a1ff2c979a1f27 Mon Sep 17 00:00:00 2001 From: Esau Date: Fri, 15 Mar 2024 12:40:14 +0100 Subject: [PATCH 61/62] test --- l1-contracts/test/NewOutbox.t.sol | 10 +++--- l1-contracts/test/merkle/MerkleLib.t.sol | 44 +----------------------- l1-contracts/test/merkle/Naive.sol | 2 +- l1-contracts/test/merkle/Naive.t.sol | 2 +- 4 files changed, 8 insertions(+), 50 deletions(-) diff --git a/l1-contracts/test/NewOutbox.t.sol b/l1-contracts/test/NewOutbox.t.sol index 40e6521c1db..2a22f78115d 100644 --- a/l1-contracts/test/NewOutbox.t.sol +++ b/l1-contracts/test/NewOutbox.t.sol @@ -9,7 +9,7 @@ import {Errors} from "../src/core/libraries/Errors.sol"; import {DataStructures} from "../src/core/libraries/DataStructures.sol"; import {Hash} from "../src/core/libraries/Hash.sol"; import {NaiveMerkle} from "./merkle/Naive.sol"; -import {MerkleLibTest} from "./merkle/MerkleLib.t.sol"; +import {MerkleTestUtil} from "./merkle/TestUtil.sol"; contract NewOutboxTest is Test { using Hash for DataStructures.L2ToL1Msg; @@ -22,12 +22,12 @@ contract NewOutboxTest is Test { NewOutbox internal outbox; NaiveMerkle internal zeroedTree; - MerkleLibTest internal merkleLibTest; + MerkleTestUtil internal merkleTestUtil; function setUp() public { outbox = new NewOutbox(ROLLUP_CONTRACT); zeroedTree = new NaiveMerkle(DEFAULT_TREE_HEIGHT); - merkleLibTest = new MerkleLibTest(); + merkleTestUtil = new MerkleTestUtil(); } function _fakeMessage(address _recipient) internal view returns (DataStructures.L2ToL1Msg memory) { @@ -65,7 +65,7 @@ contract NewOutboxTest is Test { // L2 to L1 message tree, expect for the correct event to be emitted, and then query for the root in the contract—making sure the roots, as well as the // the tree height (which is also the length of the sibling path) match function testInsertVariedLeafs(bytes32[] calldata _messageLeafs) public { - uint256 treeHeight = merkleLibTest.calculateTreeHeightFromSize(_messageLeafs.length); + uint256 treeHeight = merkleTestUtil.calculateTreeHeightFromSize(_messageLeafs.length); NaiveMerkle tree = new NaiveMerkle(treeHeight); for (uint256 i = 0; i < _messageLeafs.length; i++) { @@ -219,7 +219,7 @@ contract NewOutboxTest is Test { uint256 numberOfMessages = bound(_size, 1, _recipients.length); DataStructures.L2ToL1Msg[] memory messages = new DataStructures.L2ToL1Msg[](numberOfMessages); - uint256 treeHeight = merkleLibTest.calculateTreeHeightFromSize(numberOfMessages); + uint256 treeHeight = merkleTestUtil.calculateTreeHeightFromSize(numberOfMessages); NaiveMerkle tree = new NaiveMerkle(treeHeight); for (uint256 i = 0; i < numberOfMessages; i++) { diff --git a/l1-contracts/test/merkle/MerkleLib.t.sol b/l1-contracts/test/merkle/MerkleLib.t.sol index c1c86aad591..b296fae8947 100644 --- a/l1-contracts/test/merkle/MerkleLib.t.sol +++ b/l1-contracts/test/merkle/MerkleLib.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright 2023 Aztec Labs. +// Copyright 2024 Aztec Labs. pragma solidity >=0.8.18; import {Test} from "forge-std/Test.sol"; @@ -78,46 +78,4 @@ contract MerkleLibTest is Test { vm.expectRevert(); merkleLibHelper.verifyMembership(siblingPath, leaf, leafIndex, expectedRoot); } - - function testCalculateTreeHeightFromSize() external { - assertEq(calculateTreeHeightFromSize(0), 1); - assertEq(calculateTreeHeightFromSize(1), 1); - assertEq(calculateTreeHeightFromSize(2), 1); - assertEq(calculateTreeHeightFromSize(3), 2); - assertEq(calculateTreeHeightFromSize(4), 2); - assertEq(calculateTreeHeightFromSize(5), 3); - assertEq(calculateTreeHeightFromSize(6), 3); - assertEq(calculateTreeHeightFromSize(7), 3); - assertEq(calculateTreeHeightFromSize(8), 3); - assertEq(calculateTreeHeightFromSize(9), 4); - } - - /* - * @notice Calculates a tree height from the amount of elements in the tree - * @param _size - The amount of elements in the tree - */ - function calculateTreeHeightFromSize(uint256 _size) public pure returns (uint256) { - /// The code / formula that works below has one edge case at _size = 1, which we handle here - if (_size == 1) { - return 1; - } - - /// We need to store the original numer to check at the end if we are a power of two - uint256 originalNumber = _size; - - /// We need the height of the tree that will contain all of our leaves, - /// hence the next highest power of two from the amount of leaves - Math.ceil(Math.log2(x)) - uint256 height = 0; - - /// While size > 1, we divide by two, and count how many times we do this; producing a rudimentary way of calculating Math.Floor(Math.log2(x)) - while (_size > 1) { - _size >>= 1; - height++; - } - - /// @notice - We check if 2 ** height does not equal our original number. If so, this means that our size is not a power of two, - /// and hence we've rounded down (Math.floor) and have obtained the next lowest power of two instead of rounding up (Math.ceil) to obtain the next highest power of two and therefore we need to increment height before returning it. - /// If 2 ** height equals our original number, it means that we have a perfect power of two and Math.floor(Math.log2(x)) = Math.ceil(Math.log2(x)) and we can return height as-is - return (2 ** height) != originalNumber ? ++height : height; - } } diff --git a/l1-contracts/test/merkle/Naive.sol b/l1-contracts/test/merkle/Naive.sol index e564c2d6db6..6ab6c896a48 100644 --- a/l1-contracts/test/merkle/Naive.sol +++ b/l1-contracts/test/merkle/Naive.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright 2023 Aztec Labs. +// Copyright 2024 Aztec Labs. pragma solidity >=0.8.18; contract NaiveMerkle { diff --git a/l1-contracts/test/merkle/Naive.t.sol b/l1-contracts/test/merkle/Naive.t.sol index 01881bbc16f..7b6dee2130e 100644 --- a/l1-contracts/test/merkle/Naive.t.sol +++ b/l1-contracts/test/merkle/Naive.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright 2023 Aztec Labs. +// Copyright 2024 Aztec Labs. pragma solidity >=0.8.18; import {Test} from "forge-std/Test.sol"; From aa2e3bcf90ba06ce96873de24a867f71ef8a55da Mon Sep 17 00:00:00 2001 From: Esau Date: Fri, 15 Mar 2024 12:40:51 +0100 Subject: [PATCH 62/62] slither --- l1-contracts/slither_output.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/l1-contracts/slither_output.md b/l1-contracts/slither_output.md index 4d0cacff45a..32bf9f3cf12 100644 --- a/l1-contracts/slither_output.md +++ b/l1-contracts/slither_output.md @@ -137,8 +137,8 @@ src/core/messagebridge/NewInbox.sol#L41 - [ ] ID-13 -[NewOutbox.constructor(address)._stateTransitioner](src/core/messagebridge/NewOutbox.sol#L30) lacks a zero-check on : - - [ROLLUP_CONTRACT = _stateTransitioner](src/core/messagebridge/NewOutbox.sol#L31) +[NewOutbox.constructor(address)._rollup](src/core/messagebridge/NewOutbox.sol#L30) lacks a zero-check on : + - [ROLLUP_CONTRACT = _rollup](src/core/messagebridge/NewOutbox.sol#L31) src/core/messagebridge/NewOutbox.sol#L30