Skip to content

Commit a7651be

Browse files
committed
chore(fast-inbox): delete legacy L1 inbox path (A-1386)
Remove the frontier-tree machinery, legacy consume(), the LAG/inboxLag plumbing and the dead MessageSent event field now that propose enforces streaming-inbox consumption (AZIP-22 Fast Inbox). Inbox / IInbox: - Drop the frontier tree forest (trees mapping, forest, HEIGHT/SIZE/EMPTY_ROOT), the per-message tree insert, getRoot(), and consume(). - Drop the LAG immutable and the _lag/_height constructor args; the constructor now takes only (rollup, feeAsset, version, bucketRingSize). - Source the compact message index from the current bucket's running total instead of a parallel counter; drop InboxState.inProgress and getInProgress(). - Drop the meaningless checkpointNumber (former inProgress) from MessageSent. - Keep the 128-bit rolling hash, InboxState.{rollingHash,totalMessagesInserted} and getState()/getTotalMessagesInserted(): the node still consumes them for message sync and L1-reorg detection until that path is retired. Rollup / config / errors: - Remove RollupConfigInput.inboxLag and the RollupCore constructor pass-through; drop Constants.L1_TO_L2_MSG_SUBTREE_HEIGHT from the Inbox construction. - Sweep orphaned errors Inbox__Unauthorized, Inbox__MustBuildBeforeConsume and Rollup__InvalidInHash; refresh the now-stale ProposeLib NatSpec. Tests: delete the frontier/consume suites and fuzz, re-home the bucket/rolling-hash coverage, update the propose-path fixtures (inHash is an unconstrained hint now), and fix the flip-stranded fee-portal/token-portal index and event expectations. FrontierLib is kept: it still backs the base-parity and merkle Frontier tests.
1 parent e896f31 commit a7651be

19 files changed

Lines changed: 68 additions & 498 deletions

File tree

l1-contracts/script/deploy/RollupConfiguration.sol

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,6 @@ contract RollupConfiguration is IRollupConfiguration, Test {
110110
config.targetCommitteeSize = vm.envUint("AZTEC_TARGET_COMMITTEE_SIZE");
111111
config.lagInEpochsForValidatorSet = vm.envUint("AZTEC_LAG_IN_EPOCHS_FOR_VALIDATOR_SET");
112112
config.lagInEpochsForRandao = vm.envUint("AZTEC_LAG_IN_EPOCHS_FOR_RANDAO");
113-
config.inboxLag = vm.envUint("AZTEC_INBOX_LAG");
114113
config.aztecProofSubmissionEpochs = vm.envUint("AZTEC_PROOF_SUBMISSION_EPOCHS");
115114
config.localEjectionThreshold = vm.envUint("AZTEC_LOCAL_EJECTION_THRESHOLD");
116115
config.slashingQuorum = vm.envOr("AZTEC_SLASHING_QUORUM", slashingRoundSize / 2 + 1);

l1-contracts/src/core/RollupCore.sol

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import {IStakingCore} from "@aztec/core/interfaces/IStaking.sol";
1515
import {IValidatorSelectionCore} from "@aztec/core/interfaces/IValidatorSelection.sol";
1616
import {IInbox} from "@aztec/core/interfaces/messagebridge/IInbox.sol";
1717
import {IOutbox} from "@aztec/core/interfaces/messagebridge/IOutbox.sol";
18-
import {Constants} from "@aztec/core/libraries/ConstantsGen.sol";
1918
import {CommitteeAttestations} from "@aztec/core/libraries/rollup/AttestationLib.sol";
2019
import {Errors} from "@aztec/core/libraries/Errors.sol";
2120
import {RollupOperationsExtLib} from "@aztec/core/libraries/rollup/RollupOperationsExtLib.sol";
@@ -619,18 +618,7 @@ contract RollupCore is EIP712("Aztec Rollup", "1"), Ownable, IStakingCore, IVali
619618
rollupStore.config.epochProofVerifier = _epochProofVerifier;
620619
rollupStore.config.version = _config.version;
621620

622-
IInbox inbox = IInbox(
623-
address(
624-
new Inbox(
625-
address(this),
626-
_feeAsset,
627-
_config.version,
628-
Constants.L1_TO_L2_MSG_SUBTREE_HEIGHT,
629-
_config.inboxLag,
630-
INBOX_BUCKET_RING_SIZE
631-
)
632-
)
633-
);
621+
IInbox inbox = IInbox(address(new Inbox(address(this), _feeAsset, _config.version, INBOX_BUCKET_RING_SIZE)));
634622

635623
rollupStore.config.inbox = inbox;
636624
rollupStore.config.outbox = IOutbox(address(new Outbox(address(this), _config.version)));

l1-contracts/src/core/interfaces/IRollup.sol

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@ struct RollupConfigInput {
8484
RewardBoostConfig rewardBoostConfig;
8585
StakingQueueConfig stakingQueueConfig;
8686
uint256 localEjectionThreshold;
87-
uint256 inboxLag;
8887
}
8988

9089
struct RollupConfig {

l1-contracts/src/core/interfaces/messagebridge/IInbox.sol

Lines changed: 10 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,14 @@ import {DataStructures} from "../../libraries/DataStructures.sol";
1111
*/
1212
interface IInbox {
1313
struct InboxState {
14-
// Rolling hash of all messages inserted into the inbox.
15-
// Used by clients to check for consistency.
16-
// TODO: remove once the streaming inbox (AZIP-22 Fast Inbox) flips on and clients rely on the
17-
// consensus rolling hash tracked in the buckets instead.
14+
// Legacy 128-bit keccak rolling hash of all messages inserted into the inbox. Consumed only by the
15+
// node for message sync and L1-reorg detection.
16+
// TODO: remove once the node relies on the full-width consensus rolling hash tracked in the buckets
17+
// instead (AZIP-22 Fast Inbox).
1818
bytes16 rollingHash;
19-
// This value is not used much by the contract, but it is useful for synching the node faster
20-
// as it can more easily figure out if it can just skip looking for events for a time period.
19+
// Cumulative number of messages inserted into the inbox. Useful for synching the node faster as it can
20+
// more easily figure out if it can just skip looking for events for a time period.
2121
uint64 totalMessagesInserted;
22-
// Number of a tree which is currently being filled
23-
uint64 inProgress;
2422
}
2523

2624
/**
@@ -45,20 +43,14 @@ interface IInbox {
4543

4644
/**
4745
* @notice Emitted when a message is sent
48-
* @param checkpointNumber - The checkpoint number in which the message is included
49-
* @param index - The index of the message in the L1 to L2 messages tree
46+
* @param index - The compact cumulative index of the message in the Inbox insertion order
5047
* @param hash - The hash of the message
51-
* @param rollingHash - The rolling hash of all messages inserted into the inbox
48+
* @param rollingHash - The legacy 128-bit rolling hash of all messages inserted into the inbox
5249
* @param inboxRollingHash - The consensus rolling hash (truncated sha256 chain) after this message
5350
* @param bucketSeq - The sequence number of the bucket this message was absorbed into
5451
*/
5552
event MessageSent(
56-
uint256 indexed checkpointNumber,
57-
uint256 index,
58-
bytes32 indexed hash,
59-
bytes16 rollingHash,
60-
bytes32 inboxRollingHash,
61-
uint256 bucketSeq
53+
uint256 index, bytes32 indexed hash, bytes16 rollingHash, bytes32 inboxRollingHash, uint256 bucketSeq
6254
);
6355

6456
// docs:start:send_l1_to_l2_message
@@ -69,37 +61,19 @@ interface IInbox {
6961
* @param _content - The content of the message (application specific)
7062
* @param _secretHash - The secret hash of the message (make it possible to hide when a specific message is consumed
7163
* on L2)
72-
* @return The key of the message in the set and its leaf index in the tree
64+
* @return The key of the message in the set and its compact cumulative index
7365
*/
7466
function sendL2Message(DataStructures.L2Actor memory _recipient, bytes32 _content, bytes32 _secretHash)
7567
external
7668
returns (bytes32, uint256);
7769
// docs:end:send_l1_to_l2_message
7870

79-
// docs:start:consume
80-
/**
81-
* @notice Consumes the current tree, and starts a new one if needed
82-
* @dev Only callable by the rollup contract
83-
* @dev In the first iteration we return empty tree root because first checkpoint's messages tree is always
84-
* empty because there has to be a 1 checkpoint lag to prevent sequencer DOS attacks
85-
*
86-
* @param _toConsume - The checkpoint number to consume
87-
*
88-
* @return The root of the consumed tree
89-
*/
90-
function consume(uint256 _toConsume) external returns (bytes32);
91-
// docs:end:consume
92-
9371
function getFeeAssetPortal() external view returns (address);
9472

95-
function getRoot(uint256 _checkpointNumber) external view returns (bytes32);
96-
9773
function getState() external view returns (InboxState memory);
9874

9975
function getTotalMessagesInserted() external view returns (uint64);
10076

101-
function getInProgress() external view returns (uint64);
102-
10377
/**
10478
* @notice Returns the sequence number of the bucket currently accumulating messages
10579
* @return The current bucket sequence number

l1-contracts/src/core/libraries/Errors.sol

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,10 @@ library Errors {
2222
error DevNet__InvalidProposer(address expected, address actual); // 0x11e6e6f7
2323

2424
// Inbox
25-
error Inbox__Unauthorized(); // 0xe5336a6b
2625
error Inbox__ActorTooLarge(bytes32 actor); // 0xa776a06e
2726
error Inbox__VersionMismatch(uint256 expected, uint256 actual); // 0x47452014
2827
error Inbox__ContentTooLarge(bytes32 content); // 0x47452014
2928
error Inbox__SecretHashTooLarge(bytes32 secretHash); // 0xecde7e2c
30-
error Inbox__MustBuildBeforeConsume(); // 0xc4901999
3129
error Inbox__BucketOutOfWindow(uint256 seq, uint256 current); // 0xfee255b7
3230

3331
// Outbox
@@ -58,7 +56,6 @@ library Errors {
5856
error Rollup__InvalidCheckpointHeader(bytes32 expected, bytes32 actual);
5957
error Rollup__InvalidCheckpointHeaderCount(uint256 expected, uint256 actual);
6058
error Rollup__InvalidCheckpointNumber(uint256 expected, uint256 actual); // 0xd1ba9bfa
61-
error Rollup__InvalidInHash(bytes32 expected, bytes32 actual); // 0xcd6f4233
6259
error Rollup__InvalidInboxRollingHash(bytes32 expected, bytes32 actual); // 0xed1f7bb5
6360
error Rollup__InvalidPreviousInboxRollingHash(bytes32 expected, bytes32 actual); // 0x2fe7cae5
6461
error Rollup__UnconsumedInboxMessages(uint256 nextBucketSeq); // 0x2bd4bf10

l1-contracts/src/core/libraries/rollup/ProposeLib.sol

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ library ProposeLib {
142142
* - Checkpoint header validations (see validateHeader function for details)
143143
* - Proposer signature is valid for designated slot proposer:
144144
* Errors.ValidatorSelection__MissingProposerSignature
145-
* - Inbox hash matches expected value: Errors.Rollup__InvalidInHash
145+
* - Streaming Inbox consumption is valid: Errors.Rollup__InvalidInboxRollingHash
146146
* - Archive root is within the scalar field: Errors.Rollup__FieldElementOutOfRange
147147
*
148148
* Validations NOT performed:
@@ -154,7 +154,7 @@ library ProposeLib {
154154
* - Store archive root for the new checkpoint number
155155
* - Store checkpoint metadata in circular storage (TempCheckpointLog)
156156
* - Update L1 gas fee oracle
157-
* - Consume inbox messages
157+
* - Validate streaming Inbox consumption against the parent checkpoint
158158
* - Setup epoch for validator selection (first block of the epoch)
159159
*
160160
* @param _args - The arguments to propose the checkpoint
@@ -401,8 +401,8 @@ library ProposeLib {
401401

402402
/**
403403
* @notice Validates a checkpoint's Inbox consumption against the streaming inbox buckets and returns how
404-
* far consumption has reached. Not yet called from propose(): the legacy `consume()`/`inHash` flow
405-
* remains the enforced path until the streaming inbox (AZIP-22 Fast Inbox) flips on.
404+
* far consumption has reached. Called from propose() as the enforced consumption path (AZIP-22 Fast
405+
* Inbox).
406406
*
407407
* @dev Read-only; performs no Inbox write. Checks, in order:
408408
* 1. The checkpoint header's `inboxRollingHash` must equal the rolling hash snapshotted in the Inbox

l1-contracts/src/core/messagebridge/Inbox.sol

Lines changed: 22 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ pragma solidity >=0.8.27;
55
import {IRollup} from "@aztec/core/interfaces/IRollup.sol";
66
import {IInbox} from "@aztec/core/interfaces/messagebridge/IInbox.sol";
77
import {Constants} from "@aztec/core/libraries/ConstantsGen.sol";
8-
import {FrontierLib} from "@aztec/core/libraries/crypto/FrontierLib.sol";
98
import {Hash} from "@aztec/core/libraries/crypto/Hash.sol";
109
import {DataStructures} from "@aztec/core/libraries/DataStructures.sol";
1110
import {Errors} from "@aztec/core/libraries/Errors.sol";
@@ -31,71 +30,41 @@ uint256 constant MIN_BUCKET_RING_SIZE = 512;
3130
*/
3231
contract 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

Comments
 (0)