-
Notifications
You must be signed in to change notification settings - Fork 614
feat: New Outbox Contract #4768 #5090
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 12 commits
06ca528
8c14998
f6dd799
66b4fdd
9ddf5fe
76091ea
d411a65
813a592
ec90ee8
bd7c58e
6131ecf
44ff77b
9899a93
980c711
1c1b87e
a1b471c
02f6fcd
e964b7d
6657587
622375c
486dbeb
cd89926
2163906
b960106
97d3497
10ded0e
3ee4dad
8393d6a
98e9779
2580dce
1e1cfe4
1d9f120
69046ac
e74ba1d
7dbafcf
0a19afa
8d6dbb3
a08f272
6e114dc
de503db
cde4943
a1dad25
1e81b50
c19828a
2b8748f
ed6398f
7bd699d
03b4676
eecf80b
3bc409b
bd639ae
b8ddbf8
75b3ed1
5c32f4f
8d58656
b94bb51
3913aa9
4d3369d
bdc0396
7883ab6
8103b96
ba536ed
1170081
afa0555
1256848
84c24ec
d0dc528
9698d44
1735d75
aa2e3bc
1fcd93d
9e7fc6b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 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; | ||
|
|
||
| /* | ||
| * @notice Consumes an entry from the Outbox | ||
| * @dev Only meaningfully callable by portals / recipients of messages | ||
|
sklppy88 marked this conversation as resolved.
Outdated
|
||
| * @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 | ||
|
sklppy88 marked this conversation as resolved.
Outdated
|
||
| */ | ||
| function consume( | ||
| uint256 _l2BlockNumber, | ||
| uint256 _leafIndex, | ||
| DataStructures.L2ToL1Msg memory _message, | ||
| bytes32[] memory _path | ||
| ) external; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,144 @@ | ||
| // 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 {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 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Everywhere else in solidity we call state transitioner a Rollup so would prefer to stick to that naming. I see Rollup as an implementation of the abstract "state transitioner" from yellow paper. |
||
| * and will be consumed by the portal contracts. | ||
| */ | ||
| contract NewOutbox is INewOutbox { | ||
| using Hash for DataStructures.L2ToL1Msg; | ||
|
|
||
| struct RootData { | ||
| bytes32 root; | ||
| uint256 height; | ||
| mapping(uint256 => bool) nullified; | ||
| } | ||
|
|
||
| address public immutable STATE_TRANSITIONER; | ||
| mapping(uint256 l2BlockNumber => RootData) public roots; | ||
|
|
||
| constructor(address _stateTransitioner) { | ||
|
sklppy88 marked this conversation as resolved.
Outdated
|
||
| 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) | ||
| { | ||
| if (msg.sender != STATE_TRANSITIONER) { | ||
| revert Errors.Outbox__Unauthorized(); | ||
| } | ||
|
|
||
| if (roots[_l2BlockNumber].root != bytes32(0)) { | ||
| revert Errors.Outbox__RootAlreadySet(_l2BlockNumber); | ||
| } | ||
|
sklppy88 marked this conversation as resolved.
|
||
|
|
||
| roots[_l2BlockNumber].root = _root; | ||
| roots[_l2BlockNumber].height = _height; | ||
|
|
||
| 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, | ||
| DataStructures.L2ToL1Msg memory _message, | ||
| bytes32[] memory _path | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems to me that it would be better to use calldata for _message and _path given that you don't need to modify the values and calldata is cheaper.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (do benchmark and compare if you do this though)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I do actually see a speedup over many runs, it may be just the test environment, but very interesting nonetheless
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is not a speedup that you can just see, it is evaluating the gas, you can add
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| ) public override(INewOutbox) { | ||
|
sklppy88 marked this conversation as resolved.
Outdated
|
||
| 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(); | ||
| } | ||
|
|
||
| bytes32 expectedRoot = roots[_l2BlockNumber].root; | ||
|
sklppy88 marked this conversation as resolved.
Outdated
|
||
|
|
||
| if (expectedRoot == 0) { | ||
| revert Errors.Outbox__NothingToConsumeAtBlock(_l2BlockNumber); | ||
| } | ||
|
|
||
| if (roots[_l2BlockNumber].nullified[_leafIndex]) { | ||
| revert Errors.Outbox__AlreadyNullified(_l2BlockNumber, _leafIndex); | ||
| } | ||
|
|
||
| uint256 expectedHeight = roots[_l2BlockNumber].height; | ||
|
|
||
| if (expectedHeight != _path.length) { | ||
| revert Errors.Outbox__InvalidPathLength(expectedHeight, _path.length); | ||
| } | ||
|
|
||
| bytes32 messageHash = _verifyMembership(_path, _message, _leafIndex, expectedRoot); | ||
|
sklppy88 marked this conversation as resolved.
Outdated
|
||
|
|
||
| roots[_l2BlockNumber].nullified[_leafIndex] = true; | ||
|
|
||
| emit MessageConsumed(_l2BlockNumber, expectedRoot, messageHash); | ||
|
sklppy88 marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| /* | ||
| * @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( | ||
|
sklppy88 marked this conversation as resolved.
Outdated
|
||
| bytes32[] memory _path, | ||
| DataStructures.L2ToL1Msg memory _message, | ||
| uint256 _index, | ||
| bytes32 _expectedRoot | ||
| ) internal pure returns (bytes32) { | ||
| bytes32 leaf = _message.sha256ToField(); | ||
|
|
||
| bytes32 root; | ||
| uint256 index = _index; | ||
|
sklppy88 marked this conversation as resolved.
Outdated
|
||
|
|
||
| for (uint256 i = 0; i < _path.length; i++) { | ||
| bool isRight = (index & 1) == 1; | ||
|
|
||
| root = isRight | ||
| ? sha256(bytes.concat(_path[i], i == 0 ? leaf : root)) | ||
|
sklppy88 marked this conversation as resolved.
Outdated
|
||
| : sha256(bytes.concat(i == 0 ? leaf : root, _path[i])); | ||
|
|
||
| index /= 2; | ||
| } | ||
|
|
||
| if (root != _expectedRoot) { | ||
| revert Errors.Outbox__InvalidRoot(_expectedRoot, root); | ||
| } | ||
|
|
||
| return leaf; | ||
| } | ||
| } | ||


Uh oh!
There was an error while loading. Please reload this page.