@@ -5,7 +5,6 @@ pragma solidity >=0.8.27;
55import {IRollup} from "@aztec/core/interfaces/IRollup.sol " ;
66import {IInbox} from "@aztec/core/interfaces/messagebridge/IInbox.sol " ;
77import {Constants} from "@aztec/core/libraries/ConstantsGen.sol " ;
8- import {FrontierLib} from "@aztec/core/libraries/crypto/FrontierLib.sol " ;
98import {Hash} from "@aztec/core/libraries/crypto/Hash.sol " ;
109import {DataStructures} from "@aztec/core/libraries/DataStructures.sol " ;
1110import {Errors} from "@aztec/core/libraries/Errors.sol " ;
@@ -31,71 +30,41 @@ uint256 constant MIN_BUCKET_RING_SIZE = 512;
3130 */
3231contract Inbox is IInbox {
3332 using Hash for DataStructures.L1ToL2Msg;
34- using FrontierLib for FrontierLib.Forest;
35- using FrontierLib for FrontierLib.Tree;
3633
3734 // Maximum number of messages a single bucket can hold before further messages in the same L1 block
3835 // spill over into the next bucket. Matches the number of L1 to L2 messages a single L2 block can
39- // insert once the streaming inbox is live , so any one bucket is always consumable by one block.
36+ // insert, so any one bucket is always consumable by one block.
4037 uint256 public constant MAX_MSGS_PER_BUCKET = 256 ;
4138
4239 address public immutable ROLLUP;
4340 uint256 public immutable VERSION;
4441 address public immutable FEE_ASSET_PORTAL;
4542
46- uint256 public immutable LAG;
47-
4843 uint256 public immutable BUCKET_RING_SIZE;
4944
50- uint256 internal immutable HEIGHT;
51- uint256 internal immutable SIZE;
52- bytes32 internal immutable EMPTY_ROOT; // The root of an empty frontier tree
53-
54- // Practically immutable value as we only set it in the constructor.
55- FrontierLib.Forest internal forest;
56-
57- mapping (uint256 checkpointNumber = > FrontierLib.Tree tree ) public trees;
45+ // Legacy 128-bit keccak rolling hash over every inserted message leaf. Consumed only by the node's
46+ // message sync and L1-reorg detection; retained until those switch to the full-width consensus rolling
47+ // hash tracked in the buckets (AZIP-22 Fast Inbox).
48+ bytes16 internal messagesRollingHash;
5849
59- InboxState internal state;
60-
61- // Ring of rolling-hash buckets, keyed by `bucketSeq % BUCKET_RING_SIZE`. Inert for the legacy
62- // frontier-tree flow; consumed by the streaming inbox checks at `propose` (AZIP-22 Fast Inbox).
50+ // Ring of rolling-hash buckets, keyed by `bucketSeq % BUCKET_RING_SIZE`. Consumed by the streaming inbox
51+ // checks at `propose` (AZIP-22 Fast Inbox).
6352 mapping (uint256 ringIndex = > InboxBucket bucket ) internal buckets;
6453
6554 uint64 internal currentBucketSeq;
6655
67- constructor (
68- address _rollup ,
69- IERC20 _feeAsset ,
70- uint256 _version ,
71- uint256 _height ,
72- uint256 _lag ,
73- uint256 _bucketRingSize
74- ) {
56+ constructor (address _rollup , IERC20 _feeAsset , uint256 _version , uint256 _bucketRingSize ) {
7557 ROLLUP = _rollup;
7658 VERSION = _version;
7759
78- HEIGHT = _height;
79- SIZE = 2 ** _height;
80-
81- require (_lag > 0 , "LAG TOO SMALL " );
82- LAG = _lag;
83-
8460 require (_bucketRingSize >= MIN_BUCKET_RING_SIZE, "BUCKET RING TOO SMALL " );
8561 BUCKET_RING_SIZE = _bucketRingSize;
8662
87- state = InboxState ({
88- rollingHash: 0 , totalMessagesInserted: 0 , inProgress: SafeCast.toUint64 (Constants.INITIAL_CHECKPOINT_NUMBER + LAG)
89- });
90-
9163 // Genesis bucket: a checkpoint consuming no messages references the same bucket as its parent, so the
9264 // first checkpoint against an empty Inbox references this one and no base case leaks into `propose`.
9365 buckets[0 ] =
9466 InboxBucket ({rollingHash: 0 , totalMsgCount: 0 , timestamp: SafeCast.toUint64 (block .timestamp ), msgCount: 0 });
9567
96- forest.initialize (_height);
97- EMPTY_ROOT = trees[type (uint256 ).max].root (forest, HEIGHT, SIZE);
98-
9968 FEE_ASSET_PORTAL = address (new FeeJuicePortal (IRollup (_rollup), _feeAsset, IInbox (this ), VERSION));
10069 }
10170
@@ -109,7 +78,7 @@ contract Inbox is IInbox {
10978 * @param _secretHash - The secret hash of the message (make it possible to hide when a specific message is consumed
11079 * on L2)
11180 *
112- * @return Hash of the sent message and its leaf index in the tree .
81+ * @return Hash of the sent message and its compact cumulative index .
11382 */
11483 function sendL2Message (DataStructures.L2Actor memory _recipient , bytes32 _content , bytes32 _secretHash )
11584 external
@@ -121,24 +90,11 @@ contract Inbox is IInbox {
12190 require (uint256 (_content) <= Constants.MAX_FIELD_VALUE, Errors.Inbox__ContentTooLarge (_content));
12291 require (uint256 (_secretHash) <= Constants.MAX_FIELD_VALUE, Errors.Inbox__SecretHashTooLarge (_secretHash));
12392
124- // Is this the best way to read a packed struct into local variables in a single SLOAD
125- // without having to use assembly and manual unpacking?
126- InboxState memory _state = state;
127- bytes16 rollingHash = _state.rollingHash;
128- uint64 totalMessagesInserted = _state.totalMessagesInserted;
129- uint64 inProgress = _state.inProgress;
130-
131- FrontierLib.Tree storage currentTree = trees[inProgress];
132-
133- if (currentTree.isFull (SIZE)) {
134- inProgress += 1 ;
135- currentTree = trees[inProgress];
136- }
137-
13893 // Compact cumulative message index (AZIP-22 Fast Inbox): the zero-based position of this message in the Inbox's
139- // insertion order, equal to the number of messages inserted before it. It is embedded in the leaf preimage and
140- // matches the streaming L1-to-L2 tree's leaf count, so consumers do not need per-checkpoint tree geometry.
141- uint256 index = totalMessagesInserted;
94+ // insertion order, equal to the number of messages inserted before it. It matches the streaming L1-to-L2 tree's
95+ // leaf count, so consumers do not need per-checkpoint tree geometry. Sourced from the current bucket's running
96+ // total, which the absorb below then advances (a rollover carries the total forward unchanged).
97+ uint256 index = _totalMessagesInserted ();
14298
14399 // If the sender is the fee asset portal, we use a magic address to simpler have it initialized at genesis.
144100 // We assume that no-one will know the private key for this address and that the precompile won't change to
@@ -154,16 +110,12 @@ contract Inbox is IInbox {
154110 });
155111
156112 bytes32 leaf = message.sha256ToField ();
157- currentTree.insertLeaf (leaf);
158113
159- bytes16 updatedRollingHash = bytes16 (keccak256 (abi.encodePacked (rollingHash, leaf)));
160- state = InboxState ({
161- rollingHash: updatedRollingHash, totalMessagesInserted: totalMessagesInserted + 1 , inProgress: inProgress
162- });
114+ messagesRollingHash = bytes16 (keccak256 (abi.encodePacked (messagesRollingHash, leaf)));
163115
164116 (uint64 bucketSeq , bytes32 inboxRollingHash ) = _absorbIntoBucket (leaf);
165117
166- emit MessageSent (inProgress, index, leaf, updatedRollingHash , inboxRollingHash, bucketSeq);
118+ emit MessageSent (index, leaf, messagesRollingHash , inboxRollingHash, bucketSeq);
167119
168120 return (leaf, index);
169121 }
@@ -209,55 +161,16 @@ contract Inbox is IInbox {
209161 return (bucketSeq, bucket.rollingHash);
210162 }
211163
212- /**
213- * @notice Consumes the current tree, and starts a new one if needed
214- *
215- * @dev Only callable by the rollup contract
216- * @dev In the first iteration we return empty tree root because first checkpoint's messages tree is always
217- * empty because there has to be a 1 checkpoint lag to prevent sequencer DOS attacks
218- *
219- * @param _toConsume - The checkpoint number to consume
220- *
221- * @return The root of the consumed tree
222- */
223- function consume (uint256 _toConsume ) external override (IInbox) returns (bytes32 ) {
224- require (msg .sender == ROLLUP, Errors.Inbox__Unauthorized ());
225-
226- uint64 inProgress = state.inProgress;
227- require (_toConsume < inProgress, Errors.Inbox__MustBuildBeforeConsume ());
228-
229- bytes32 root = EMPTY_ROOT;
230- if (_toConsume > Constants.INITIAL_CHECKPOINT_NUMBER) {
231- root = trees[_toConsume].root (forest, HEIGHT, SIZE);
232- }
233-
234- // Once consumption reaches the current lag boundary, open the next checkpoint
235- // so new inserts keep a full LAG-sized buffer ahead of the rollup.
236- if (_toConsume + LAG == inProgress) {
237- state.inProgress = inProgress + 1 ;
238- }
239-
240- return root;
241- }
242-
243164 function getFeeAssetPortal () external view override (IInbox) returns (address ) {
244165 return FEE_ASSET_PORTAL;
245166 }
246167
247- function getRoot (uint256 _checkpointNumber ) external view override (IInbox) returns (bytes32 ) {
248- return trees[_checkpointNumber].root (forest, HEIGHT, SIZE);
249- }
250-
251168 function getState () external view override (IInbox) returns (InboxState memory ) {
252- return state ;
169+ return InboxState ({rollingHash: messagesRollingHash, totalMessagesInserted: _totalMessagesInserted ()}) ;
253170 }
254171
255172 function getTotalMessagesInserted () external view override (IInbox) returns (uint64 ) {
256- return state.totalMessagesInserted;
257- }
258-
259- function getInProgress () external view override (IInbox) returns (uint64 ) {
260- return state.inProgress;
173+ return _totalMessagesInserted ();
261174 }
262175
263176 function getCurrentBucketSeq () external view override (IInbox) returns (uint64 ) {
@@ -269,4 +182,9 @@ contract Inbox is IInbox {
269182 require (_seq <= current && current - _seq < BUCKET_RING_SIZE, Errors.Inbox__BucketOutOfWindow (_seq, current));
270183 return buckets[_seq % BUCKET_RING_SIZE];
271184 }
185+
186+ // Cumulative number of messages inserted into the Inbox, tracked by the current bucket's running total.
187+ function _totalMessagesInserted () internal view returns (uint64 ) {
188+ return buckets[currentBucketSeq % BUCKET_RING_SIZE].totalMsgCount;
189+ }
272190}
0 commit comments