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..1a2d70f9e944 100644 --- a/l1-contracts/src/core/libraries/rollup/ProposeLib.sol +++ b/l1-contracts/src/core/libraries/rollup/ProposeLib.sol @@ -9,6 +9,7 @@ import {IInbox, MAX_MSGS_PER_BUCKET} from "@aztec/core/interfaces/messagebridge/ 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"; @@ -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); }