Skip to content

Commit de34a37

Browse files
committed
Aggregate out hashes and set on L1 per epoch.
1 parent 08935f7 commit de34a37

File tree

127 files changed

+10420
-9268
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

127 files changed

+10420
-9268
lines changed

docs/versioned_docs/version-v3.0.0-devnet.4/developers/migration_notes.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,35 @@ Aztec is in full-speed development. Literally every version breaks compatibility
99

1010
## 3.0.0-devnet.4
1111

12+
### L2-to-L1 messages are now grouped by epoch.
13+
14+
L2-to-L1 messages are now aggregated and organized per epoch rather than per block. This change affects how you compute membership witnesses for consuming messages on L1. You now need to know the epoch number in which the message was emitted to retrieve and consume the message.
15+
16+
**Note**: This is only an API change. The protocol behavior remains the same - messages can still only be consumed once an epoch is proven as before.
17+
18+
#### What changed
19+
20+
Previously, you might have computed the membership witness without explicitly needing the epoch:
21+
22+
```typescript
23+
const witness = await computeL2ToL1MembershipWitness(
24+
node,
25+
l2TxReceipt.blockNumber,
26+
l2ToL1Message
27+
);
28+
```
29+
30+
Now, you should provide the epoch number:
31+
32+
```typescript
33+
const epoch = await rollup.getEpochNumberForBlock(l2TxReceipt.blockNumber);
34+
const witness = await computeL2ToL1MembershipWitness(
35+
node,
36+
epoch,
37+
l2ToL1Message
38+
);
39+
```
40+
1241
## [aztec.js] Removal of barrel export
1342

1443
`aztec.js` is now divided into granular exports, which improves loading performance in node.js and also makes the job of web bundlers easier:

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {IERC20} from "@oz/token/ERC20/IERC20.sol";
2626
struct PublicInputArgs {
2727
bytes32 previousArchive;
2828
bytes32 endArchive;
29+
bytes32 outHash;
2930
address proverId;
3031
}
3132

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

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
pragma solidity >=0.8.27;
44

55
import {DataStructures} from "../../libraries/DataStructures.sol";
6+
import {Epoch} from "../../libraries/TimeLib.sol";
67

78
/**
89
* @title IOutbox
@@ -11,21 +12,18 @@ import {DataStructures} from "../../libraries/DataStructures.sol";
1112
* and will be consumed by the portal contracts.
1213
*/
1314
interface IOutbox {
14-
event RootAdded(uint256 indexed l2BlockNumber, bytes32 indexed root);
15-
event MessageConsumed(
16-
uint256 indexed l2BlockNumber, bytes32 indexed root, bytes32 indexed messageHash, uint256 leafId
17-
);
15+
event RootAdded(Epoch indexed epoch, bytes32 indexed root);
16+
event MessageConsumed(Epoch indexed epoch, bytes32 indexed root, bytes32 indexed messageHash, uint256 leafId);
1817

1918
// docs:start:outbox_insert
2019
/**
21-
* @notice Inserts the root of a merkle tree containing all of the L2 to L1 messages in
22-
* a block specified by _l2BlockNumber.
20+
* @notice Inserts the root of a merkle tree containing all of the L2 to L1 messages in an epoch specified by _epoch.
2321
* @dev Only callable by the rollup contract
2422
* @dev Emits `RootAdded` upon inserting the root successfully
25-
* @param _l2BlockNumber - The L2 Block Number in which the L2 to L1 messages reside
23+
* @param _epoch - The epoch in which the L2 to L1 messages reside
2624
* @param _root - The merkle root of the tree where all the L2 to L1 messages are leaves
2725
*/
28-
function insert(uint256 _l2BlockNumber, bytes32 _root) external;
26+
function insert(Epoch _epoch, bytes32 _root) external;
2927
// docs:end:outbox_insert
3028

3129
// docs:start:outbox_consume
@@ -34,37 +32,36 @@ interface IOutbox {
3432
* @dev Only useable by portals / recipients of messages
3533
* @dev Emits `MessageConsumed` when consuming messages
3634
* @param _message - The L2 to L1 message
37-
* @param _l2BlockNumber - The block number specifying the block that contains the message we want to consume
38-
* @param _leafIndex - The index inside the merkle tree where the message is located
39-
* @param _path - The sibling path used to prove inclusion of the message, the _path length directly depends
40-
* 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
41-
* L1 to L2 message tree.
35+
* @param _epoch - The epoch that contains the message we want to consume
36+
* @param _leafIndex - The index at the level in the wonky tree where the message is located
37+
* @param _path - The sibling path used to prove inclusion of the message, the _path length depends
38+
* on the location of the L2 to L1 message in the wonky tree.
4239
*/
4340
function consume(
4441
DataStructures.L2ToL1Msg calldata _message,
45-
uint256 _l2BlockNumber,
42+
Epoch _epoch,
4643
uint256 _leafIndex,
4744
bytes32[] calldata _path
4845
) external;
4946
// docs:end:outbox_consume
5047

5148
// docs:start:outbox_has_message_been_consumed_at_block_and_index
5249
/**
53-
* @notice Checks to see if an L2 to L1 message in a specific block has been consumed
50+
* @notice Checks to see if an L2 to L1 message in a specific epoch has been consumed
5451
* @dev - This function does not throw. Out-of-bounds access is considered valid, but will always return false
55-
* @param _l2BlockNumber - The block number specifying the block that contains the message we want to check
52+
* @param _epoch - The epoch that contains the message we want to check
5653
* @param _leafId - The unique id of the message leaf
5754
*/
58-
function hasMessageBeenConsumedAtBlock(uint256 _l2BlockNumber, uint256 _leafId) external view returns (bool);
55+
function hasMessageBeenConsumedAtEpoch(Epoch _epoch, uint256 _leafId) external view returns (bool);
5956
// docs:end:outbox_has_message_been_consumed_at_block_and_index
6057

6158
/**
62-
* @notice Fetch the root data for a given block number
63-
* Returns (0, 0) if the block is not proven
59+
* @notice Fetch the root data for a given epoch
60+
* Returns (0, 0) if the epoch is not proven
6461
*
65-
* @param _l2BlockNumber - The block number to fetch the root data for
62+
* @param _epoch - The epoch to fetch the root data for
6663
*
6764
* @return bytes32 - The root of the merkle tree containing the L2 to L1 messages
6865
*/
69-
function getRootData(uint256 _l2BlockNumber) external view returns (bytes32);
66+
function getRootData(Epoch _epoch) external view returns (bytes32);
7067
}

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ library Constants {
2424
14_269_942_583_723_164_841_365_114_274_712_143_548_835_546_030_057_296_325_580_016_468_921_911_294_613;
2525
uint256 internal constant FEE_JUICE_ADDRESS = 5;
2626
uint256 internal constant BLS12_POINT_COMPRESSED_BYTES = 48;
27-
uint256 internal constant PROPOSED_BLOCK_HEADER_LENGTH_BYTES = 284;
28-
uint256 internal constant ROOT_ROLLUP_PUBLIC_INPUTS_LENGTH = 158;
27+
uint256 internal constant ROOT_ROLLUP_PUBLIC_INPUTS_LENGTH = 159;
2928
uint256 internal constant NUM_MSGS_PER_BASE_PARITY = 256;
3029
uint256 internal constant NUM_BASE_PARITY_PER_ROOT_PARITY = 4;
3130
}

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

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,9 @@ library Errors {
4343
uint32 storedDeadline,
4444
uint32 deadlinePassed
4545
); // 0x5e789f34
46-
error Outbox__RootAlreadySetAtBlock(uint256 l2BlockNumber); // 0x3eccfd3e
4746
error Outbox__InvalidRecipient(address expected, address actual); // 0x57aad581
48-
error Outbox__AlreadyNullified(uint256 l2BlockNumber, uint256 leafIndex); // 0xfd71c2d4
49-
error Outbox__NothingToConsumeAtBlock(uint256 l2BlockNumber); // 0xa4508f22
50-
error Outbox__BlockNotProven(uint256 l2BlockNumber); // 0x0e194a6d
51-
error Outbox__BlockAlreadyProven(uint256 l2BlockNumber);
47+
error Outbox__AlreadyNullified(Epoch epoch, uint256 leafIndex); // 0xfd71c2d4
48+
error Outbox__NothingToConsumeAtEpoch(Epoch epoch); // 0xa4508f22
5249
error Outbox__PathTooLong();
5350
error Outbox__LeafIndexOutOfBounds(uint256 leafIndex, uint256 pathLength);
5451

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

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,18 @@ library EpochProofLib {
116116
require(verifyEpochRootProof(_args), Errors.Rollup__InvalidProof());
117117

118118
RollupStore storage rollupStore = STFLib.getStorage();
119-
rollupStore.tips =
120-
rollupStore.tips.updateProvenBlockNumber(Math.max(rollupStore.tips.getProvenBlockNumber(), _args.end));
119+
120+
// Advance the proven block number and insert the out hash if the chain is extended.
121+
if (_args.end > rollupStore.tips.getProvenBlockNumber()) {
122+
rollupStore.tips =
123+
rollupStore.tips.updateProvenBlockNumber(Math.max(rollupStore.tips.getProvenBlockNumber(), _args.end));
124+
125+
// Handle L2->L1 message processing:
126+
if (_args.args.outHash != bytes32(0)) {
127+
// Insert L2->L1 messages root into outbox for consumption.
128+
rollupStore.config.outbox.insert(endEpoch, _args.args.outHash);
129+
}
130+
}
121131

122132
RewardLib.handleRewardsAndFees(_args, endEpoch);
123133

@@ -171,30 +181,33 @@ library EpochProofLib {
171181
// struct RootRollupPublicInputs {
172182
// previous_archive_root: Field,
173183
// end_archive_root: Field,
184+
// out_hash: Field,
174185
// proposedBlockHeaderHashes: [Field; Constants.AZTEC_MAX_EPOCH_DURATION],
175186
// fees: [FeeRecipient; Constants.AZTEC_MAX_EPOCH_DURATION],
176187
// chain_id: Field,
177188
// version: Field,
178189
// vk_tree_root: Field,
179190
// protocol_contracts_hash: Field,
180191
// prover_id: Field,
181-
// blob_public_inputs: FinalBlobAccumulatorPublicInputs,
192+
// blob_public_inputs: FinalBlobAccumulator,
182193
// }
183194
{
184195
// previous_archive.root: the previous archive tree root
185196
publicInputs[0] = _args.previousArchive;
186197

187198
// end_archive.root: the new archive tree root
188199
publicInputs[1] = _args.endArchive;
200+
201+
publicInputs[2] = _args.outHash;
189202
}
190203

191204
uint256 numBlocks = _end - _start + 1;
192205

193206
for (uint256 i = 0; i < numBlocks; i++) {
194-
publicInputs[2 + i] = STFLib.getHeaderHash(_start + i);
207+
publicInputs[3 + i] = STFLib.getHeaderHash(_start + i);
195208
}
196209

197-
uint256 offset = 2 + Constants.AZTEC_MAX_EPOCH_DURATION;
210+
uint256 offset = 3 + Constants.AZTEC_MAX_EPOCH_DURATION;
198211

199212
uint256 feesLength = Constants.AZTEC_MAX_EPOCH_DURATION * 2;
200213
// fees[2n to 2n + 1]: a fee element, which contains of a recipient and a value
@@ -249,7 +262,6 @@ library EpochProofLib {
249262
publicInputs[offset] = bytes32(uint256(uint248(bytes31((_blobPublicInputs[96:127])))));
250263
// c[1]
251264
publicInputs[offset + 1] = bytes32(uint256(uint136(bytes17((_blobPublicInputs[127:144])))));
252-
offset += 2;
253265

254266
return publicInputs;
255267
}

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

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ struct ValidateHeaderArgs {
7676
* - Validator selection and proposer verification
7777
* - Fee calculation and mana consumption tracking
7878
* - State transitions and archive updates
79-
* - Message processing between L1 and L2 via the Inbox and Outbox contracts
79+
* - L1 to L2 message processing via the Inbox
8080
*
8181
* The proposal flow operates within Aztec's time-based model where:
8282
* - Each slot has a designated proposer selected from the validator set
@@ -123,7 +123,7 @@ library ProposeLib {
123123
* Note that some validations and processes are disabled if the chain is configured to run without
124124
* transactions, such as during ignition phase:
125125
* - No fee header computation or L1 gas fee oracle update
126-
* - No inbox message consumption or outbox message insertion
126+
* - No inbox message consumption
127127
*
128128
* Validations performed:
129129
* - Blob commitments against provided blob data: Errors.Rollup__InvalidBlobHash,
@@ -143,7 +143,6 @@ library ProposeLib {
143143
* - Store block metadata in circular storage (TempBlockLog)
144144
* - Update L1 gas fee oracle (when txs enabled)
145145
* - Consume inbox messages (when txs enabled)
146-
* - Insert outbox messages (when txs enabled)
147146
* - Setup epoch for validator selection (first block of the epoch)
148147
*
149148
* @param _args - The arguments to propose the block
@@ -294,22 +293,16 @@ library ProposeLib {
294293
})
295294
);
296295

297-
// Handle L1<->L2 message processing (only when transactions are enabled)
296+
// Handle L1->L2 message processing (only when transactions are enabled)
298297
if (v.isTxsEnabled) {
299-
// Since ignition will have no transactions there will be no method to consume or output message.
298+
// Since ignition will have no transactions there will be no method to consume messages.
300299
// Therefore we can ignore it as long as mana target is zero.
301300
// Since the inbox is async, it must enforce its own check to not try to insert if ignition.
302301

303302
// Consume pending L1->L2 messages and validate against header commitment
304303
// @note The block number here will always be >=1 as the genesis block is at 0
305304
v.inHash = rollupStore.config.inbox.consume(blockNumber);
306-
require(
307-
v.header.contentCommitment.inHash == v.inHash,
308-
Errors.Rollup__InvalidInHash(v.inHash, v.header.contentCommitment.inHash)
309-
);
310-
311-
// Insert L2->L1 messages into outbox for later consumption
312-
rollupStore.config.outbox.insert(blockNumber, v.header.contentCommitment.outHash);
305+
require(v.header.inHash == v.inHash, Errors.Rollup__InvalidInHash(v.inHash, v.header.inHash));
313306
}
314307

315308
// Emit event for external listeners. Nodes rely on this event to update their state.
@@ -363,8 +356,8 @@ library ProposeLib {
363356
require(timestamp <= currentTime, Errors.Rollup__TimestampInFuture(currentTime, timestamp));
364357

365358
require(
366-
_args.flags.ignoreDA || _args.header.contentCommitment.blobsHash == _args.blobsHashesCommitment,
367-
Errors.Rollup__UnavailableTxs(_args.header.contentCommitment.blobsHash)
359+
_args.flags.ignoreDA || _args.header.blobsHash == _args.blobsHashesCommitment,
360+
Errors.Rollup__UnavailableTxs(_args.header.blobsHash)
368361
);
369362

370363
require(_args.header.gasFees.feePerDaGas == 0, Errors.Rollup__NonZeroDaFee());

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

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,11 @@ struct GasFees {
2929
uint128 feePerL2Gas;
3030
}
3131

32-
struct ContentCommitment {
32+
struct ProposedHeader {
33+
bytes32 lastArchiveRoot;
3334
bytes32 blobsHash;
3435
bytes32 inHash;
3536
bytes32 outHash;
36-
}
37-
38-
struct ProposedHeader {
39-
bytes32 lastArchiveRoot;
40-
ContentCommitment contentCommitment;
4137
Slot slotNumber;
4238
Timestamp timestamp;
4339
address coinbase;
@@ -67,9 +63,9 @@ library ProposedHeaderLib {
6763
return Hash.sha256ToField(
6864
abi.encodePacked(
6965
_header.lastArchiveRoot,
70-
_header.contentCommitment.blobsHash,
71-
_header.contentCommitment.inHash,
72-
_header.contentCommitment.outHash,
66+
_header.blobsHash,
67+
_header.inHash,
68+
_header.outHash,
7369
_header.slotNumber,
7470
Timestamp.unwrap(_header.timestamp).toUint64(),
7571
_header.coinbase,

0 commit comments

Comments
 (0)