From c273112cc56ff14d3a0d1a10c179a3114f6d92c7 Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Sat, 18 Jul 2026 20:20:55 -0300 Subject: [PATCH 1/2] chore(fast-inbox): emit inbox lag and checkpoint cap from the constants generator (A-1434) Add INBOX_LAG_SECONDS and MAX_L1_TO_L2_MSGS_PER_CHECKPOINT to the Solidity constants allowlist so the generator emits them into ConstantsGen.sol, matching the values already present in constants.gen.ts. ProposeLib and its consumption test now read the generated Constants library instead of the hand-declared file-scope copies, giving L1 and TS a single source of truth. --- .../scripts/constants-codegen/solidity.json | 2 ++ .../src/core/libraries/rollup/ProposeLib.sol | 15 ++++-------- .../test/rollup/ProposeInboxConsumption.t.sol | 23 +++++++++---------- 3 files changed, 17 insertions(+), 23 deletions(-) diff --git a/l1-contracts/scripts/constants-codegen/solidity.json b/l1-contracts/scripts/constants-codegen/solidity.json index 2fcd7cc212c4..147c0c25975b 100644 --- a/l1-contracts/scripts/constants-codegen/solidity.json +++ b/l1-contracts/scripts/constants-codegen/solidity.json @@ -1,6 +1,8 @@ [ "MAX_FIELD_VALUE", "L1_TO_L2_MSG_SUBTREE_HEIGHT", + "MAX_L1_TO_L2_MSGS_PER_CHECKPOINT", + "INBOX_LAG_SECONDS", "MAX_L2_TO_L1_MSGS_PER_TX", "INITIAL_CHECKPOINT_NUMBER", "MAX_CHECKPOINTS_PER_EPOCH", diff --git a/l1-contracts/src/core/libraries/rollup/ProposeLib.sol b/l1-contracts/src/core/libraries/rollup/ProposeLib.sol index efb031bcbe9b..4c2006597640 100644 --- a/l1-contracts/src/core/libraries/rollup/ProposeLib.sol +++ b/l1-contracts/src/core/libraries/rollup/ProposeLib.sol @@ -3,6 +3,7 @@ pragma solidity >=0.8.27; import {BlobLib} from "@aztec-blob-lib/BlobLib.sol"; +import {Constants} from "@aztec/core/libraries/ConstantsGen.sol"; import {IEscapeHatch} from "@aztec/core/interfaces/IEscapeHatch.sol"; import {RollupStore, IRollupCore, CheckpointHeaderValidationFlags} from "@aztec/core/interfaces/IRollup.sol"; import {IInbox, MAX_MSGS_PER_BUCKET} from "@aztec/core/interfaces/messagebridge/IInbox.sol"; @@ -22,14 +23,6 @@ import {SafeCast} from "@oz/utils/math/SafeCast.sol"; import {ProposedHeader, ProposedHeaderLib} from "./ProposedHeaderLib.sol"; import {STFLib} from "./STFLib.sol"; -// Streaming-inbox protocol constants (AZIP-22 Fast Inbox). These mirror the protocol circuit constants and -// should move into the generated Constants library once the Solidity emitter includes them. -// Minimum bucket age, in seconds, at the start of a checkpoint's build frame for its consumption to be -// mandatory. One L1 slot: validators cannot be required to act on buckets they may not have seen. -uint256 constant INBOX_LAG_SECONDS = 12; -// Maximum number of L1 to L2 messages a single checkpoint can insert. -uint256 constant MAX_L1_TO_L2_MSGS_PER_CHECKPOINT = 1024; - struct ProposeArgs { bytes32 archive; OracleInput oracleInput; @@ -465,17 +458,17 @@ library ProposeLib { ); require( - bucket.totalMsgCount - _parentTotalMsgCount <= MAX_L1_TO_L2_MSGS_PER_CHECKPOINT, + bucket.totalMsgCount - _parentTotalMsgCount <= Constants.MAX_L1_TO_L2_MSGS_PER_CHECKPOINT, Errors.Rollup__TooManyInboxMessagesConsumed(bucket.totalMsgCount - _parentTotalMsgCount) ); if (_bucketHint < _inbox.getCurrentBucketSeq()) { IInbox.InboxBucket memory next = _inbox.getBucket(_bucketHint + 1); Timestamp buildFrameStart = TimeLib.toTimestamp(_slotNumber - Slot.wrap(1)); - Timestamp cutoff = buildFrameStart - Timestamp.wrap(INBOX_LAG_SECONDS); + Timestamp cutoff = buildFrameStart - Timestamp.wrap(Constants.INBOX_LAG_SECONDS); require( next.timestamp > Timestamp.unwrap(cutoff) - || next.totalMsgCount - _parentTotalMsgCount > MAX_L1_TO_L2_MSGS_PER_CHECKPOINT, + || next.totalMsgCount - _parentTotalMsgCount > Constants.MAX_L1_TO_L2_MSGS_PER_CHECKPOINT, Errors.Rollup__UnconsumedInboxMessages(_bucketHint + 1) ); } diff --git a/l1-contracts/test/rollup/ProposeInboxConsumption.t.sol b/l1-contracts/test/rollup/ProposeInboxConsumption.t.sol index c0539cc09118..f3749c8ac3b2 100644 --- a/l1-contracts/test/rollup/ProposeInboxConsumption.t.sol +++ b/l1-contracts/test/rollup/ProposeInboxConsumption.t.sol @@ -6,11 +6,8 @@ import {Test} from "forge-std/Test.sol"; import {TestERC20} from "src/mock/TestERC20.sol"; import {IERC20} from "@oz/token/ERC20/IERC20.sol"; import {IInbox, MAX_MSGS_PER_BUCKET} from "@aztec/core/interfaces/messagebridge/IInbox.sol"; -import { - ProposeLib, - INBOX_LAG_SECONDS, - MAX_L1_TO_L2_MSGS_PER_CHECKPOINT -} from "@aztec/core/libraries/rollup/ProposeLib.sol"; +import {ProposeLib} from "@aztec/core/libraries/rollup/ProposeLib.sol"; +import {Constants} from "@aztec/core/libraries/ConstantsGen.sol"; import {TimeLib, Slot, Timestamp} from "@aztec/core/libraries/TimeLib.sol"; import {Errors} from "@aztec/core/libraries/Errors.sol"; import {DataStructures} from "@aztec/core/libraries/DataStructures.sol"; @@ -50,7 +47,7 @@ contract ProposeInboxConsumptionTest is Test { // Start of the build frame for a checkpoint proposed in SLOT: it is built during the previous slot. uint256 internal buildFrameStart = GENESIS_TIME + (Slot.unwrap(SLOT) - 1) * SLOT_DURATION; // Buckets at or before the cutoff must be consumed by the checkpoint. - uint256 internal cutoff = buildFrameStart - INBOX_LAG_SECONDS; + uint256 internal cutoff = buildFrameStart - Constants.INBOX_LAG_SECONDS; function setUp() public { vm.warp(GENESIS_TIME); @@ -125,7 +122,7 @@ contract ProposeInboxConsumptionTest is Test { // One more message than the checkpoint cap, all before the cutoff. They spill over into buckets // 1..4 of 256 (the per-bucket cap) plus bucket 5 with the single excess message. vm.warp(cutoff - 100); - _sendMany(MAX_L1_TO_L2_MSGS_PER_CHECKPOINT + 1); + _sendMany(Constants.MAX_L1_TO_L2_MSGS_PER_CHECKPOINT + 1); assertEq(inbox.getCurrentBucketSeq(), 5, "expected five buckets"); vm.warp(GENESIS_TIME + Slot.unwrap(SLOT) * SLOT_DURATION); @@ -134,12 +131,12 @@ contract ProposeInboxConsumptionTest is Test { // even though it is old. bytes32 endHash = inbox.getBucket(4).rollingHash; uint256 consumed = rollup.validateInboxConsumption(inbox, endHash, 4, SLOT, 0); - assertEq(consumed, MAX_L1_TO_L2_MSGS_PER_CHECKPOINT, "consumed the full cap"); + assertEq(consumed, Constants.MAX_L1_TO_L2_MSGS_PER_CHECKPOINT, "consumed the full cap"); } function testNoCapEscapeAtExactCap() public { vm.warp(cutoff - 100); - _sendMany(MAX_L1_TO_L2_MSGS_PER_CHECKPOINT + 1); + _sendMany(Constants.MAX_L1_TO_L2_MSGS_PER_CHECKPOINT + 1); vm.warp(GENESIS_TIME + Slot.unwrap(SLOT) * SLOT_DURATION); @@ -154,7 +151,7 @@ contract ProposeInboxConsumptionTest is Test { // Same layout as testCapEscape, but the parent checkpoint had already consumed one message: // buckets 2..5 then hold cap messages total, which fit in one checkpoint, so no escape from bucket 1. vm.warp(cutoff - 100); - _sendMany(MAX_L1_TO_L2_MSGS_PER_CHECKPOINT + 1); + _sendMany(Constants.MAX_L1_TO_L2_MSGS_PER_CHECKPOINT + 1); vm.warp(GENESIS_TIME + Slot.unwrap(SLOT) * SLOT_DURATION); @@ -288,14 +285,16 @@ contract ProposeInboxConsumptionTest is Test { // One more message than the checkpoint cap, all before the cutoff, referenced in a single proposal from // a fresh parent: the consumed delta exceeds what the circuits can insert. vm.warp(cutoff - 100); - _sendMany(MAX_L1_TO_L2_MSGS_PER_CHECKPOINT + 1); + _sendMany(Constants.MAX_L1_TO_L2_MSGS_PER_CHECKPOINT + 1); assertEq(inbox.getCurrentBucketSeq(), 5, "expected five buckets"); bytes32 endHash = inbox.getBucket(5).rollingHash; vm.warp(GENESIS_TIME + Slot.unwrap(SLOT) * SLOT_DURATION); vm.expectRevert( - abi.encodeWithSelector(Errors.Rollup__TooManyInboxMessagesConsumed.selector, MAX_L1_TO_L2_MSGS_PER_CHECKPOINT + 1) + abi.encodeWithSelector( + Errors.Rollup__TooManyInboxMessagesConsumed.selector, Constants.MAX_L1_TO_L2_MSGS_PER_CHECKPOINT + 1 + ) ); rollup.validateInboxConsumption(inbox, endHash, 5, SLOT, 0); } From 138638f61c20a34d4ad93e9fb331058ca5f89b9a Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Sun, 19 Jul 2026 14:08:35 -0300 Subject: [PATCH 2/2] chore(fast-inbox): fix solhint import order for ConstantsGen in ProposeLib (A-1434) --- l1-contracts/src/core/libraries/rollup/ProposeLib.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l1-contracts/src/core/libraries/rollup/ProposeLib.sol b/l1-contracts/src/core/libraries/rollup/ProposeLib.sol index 4c2006597640..1a2d70f9e944 100644 --- a/l1-contracts/src/core/libraries/rollup/ProposeLib.sol +++ b/l1-contracts/src/core/libraries/rollup/ProposeLib.sol @@ -3,13 +3,13 @@ pragma solidity >=0.8.27; import {BlobLib} from "@aztec-blob-lib/BlobLib.sol"; -import {Constants} from "@aztec/core/libraries/ConstantsGen.sol"; import {IEscapeHatch} from "@aztec/core/interfaces/IEscapeHatch.sol"; import {RollupStore, IRollupCore, CheckpointHeaderValidationFlags} from "@aztec/core/interfaces/IRollup.sol"; import {IInbox, MAX_MSGS_PER_BUCKET} from "@aztec/core/interfaces/messagebridge/IInbox.sol"; import {TempCheckpointLog} from "@aztec/core/libraries/compressed-data/CheckpointLog.sol"; import {FeeHeader} from "@aztec/core/libraries/compressed-data/fees/FeeStructs.sol"; import {ChainTipsLib, CompressedChainTips} from "@aztec/core/libraries/compressed-data/Tips.sol"; +import {Constants} from "@aztec/core/libraries/ConstantsGen.sol"; import {Errors} from "@aztec/core/libraries/Errors.sol"; import {CommitteeAttestations} from "@aztec/core/libraries/rollup/AttestationLib.sol"; import {CoordinationSignatureLib} from "@aztec/core/libraries/rollup/CoordinationSignatureLib.sol";