diff --git a/l1-contracts/src/core/Rollup.sol b/l1-contracts/src/core/Rollup.sol index 9d4cb1a17f10..3abd82078875 100644 --- a/l1-contracts/src/core/Rollup.sol +++ b/l1-contracts/src/core/Rollup.sol @@ -41,6 +41,7 @@ import { Epoch, Timestamp, CommitteeAttestations, + EpochProofExtLib, RollupOperationsExtLib, ValidatorOperationsExtLib, EthValue, @@ -300,7 +301,7 @@ contract Rollup is IStaking, IValidatorSelection, IRollup, RollupCore { ProposedHeader[] calldata _headers, bytes calldata _blobPublicInputs ) external view override(IRollup) returns (bytes32[] memory) { - return RollupOperationsExtLib.getEpochProofPublicInputs(_start, _end, _args, _headers, _blobPublicInputs); + return EpochProofExtLib.getEpochProofPublicInputs(_start, _end, _args, _headers, _blobPublicInputs); } /** diff --git a/l1-contracts/src/core/RollupCore.sol b/l1-contracts/src/core/RollupCore.sol index cc66c96895e2..ac0a89dfa0d5 100644 --- a/l1-contracts/src/core/RollupCore.sol +++ b/l1-contracts/src/core/RollupCore.sol @@ -18,6 +18,7 @@ import {IOutbox} from "@aztec/core/interfaces/messagebridge/IOutbox.sol"; import {Constants} from "@aztec/core/libraries/ConstantsGen.sol"; import {CommitteeAttestations} from "@aztec/core/libraries/rollup/AttestationLib.sol"; import {Errors} from "@aztec/core/libraries/Errors.sol"; +import {EpochProofExtLib} from "@aztec/core/libraries/rollup/EpochProofExtLib.sol"; import {RollupOperationsExtLib} from "@aztec/core/libraries/rollup/RollupOperationsExtLib.sol"; import {ValidatorOperationsExtLib} from "@aztec/core/libraries/rollup/ValidatorOperationsExtLib.sol"; import {SlasherDeploymentExtLib} from "@aztec/core/libraries/rollup/SlasherDeploymentExtLib.sol"; @@ -466,7 +467,7 @@ contract RollupCore is EIP712("Aztec Rollup", "1"), Ownable, IStakingCore, IVali * @param _args Contains the epoch range, public inputs, fees, attestations, and the ZK proof */ function submitEpochRootProof(SubmitEpochRootProofArgs calldata _args) external override(IRollupCore) { - RollupOperationsExtLib.submitEpochRootProof(_args); + EpochProofExtLib.submitEpochRootProof(_args); } /** diff --git a/l1-contracts/src/core/libraries/Errors.sol b/l1-contracts/src/core/libraries/Errors.sol index db0ea9ed37bd..83227e372e55 100644 --- a/l1-contracts/src/core/libraries/Errors.sol +++ b/l1-contracts/src/core/libraries/Errors.sol @@ -60,6 +60,8 @@ library Errors { error Rollup__InvalidCheckpointNumber(uint256 expected, uint256 actual); // 0xd1ba9bfa error Rollup__InvalidInHash(bytes32 expected, bytes32 actual); // 0xcd6f4233 error Rollup__InvalidInboxRollingHash(bytes32 expected, bytes32 actual); // 0xed1f7bb5 + error Rollup__InvalidPreviousInboxRollingHash(bytes32 expected, bytes32 actual); // 0x2fe7cae5 + error Rollup__InvalidEndInboxRollingHash(bytes32 expected, bytes32 actual); // 0x4a9cdb72 error Rollup__InboxBucketStillMutable(uint256 bucketSeq); // 0x7254b980 error Rollup__UnconsumedInboxMessages(uint256 nextBucketSeq); // 0x2bd4bf10 error Rollup__InboxConsumptionBehindParent(uint256 expected, uint256 actual); // 0x54e0c025 diff --git a/l1-contracts/src/core/libraries/compressed-data/CheckpointLog.sol b/l1-contracts/src/core/libraries/compressed-data/CheckpointLog.sol index 486aba1d2bae..96af3d3ac257 100644 --- a/l1-contracts/src/core/libraries/compressed-data/CheckpointLog.sol +++ b/l1-contracts/src/core/libraries/compressed-data/CheckpointLog.sol @@ -34,7 +34,16 @@ struct TempCheckpointLog { bytes32 attestationsHash; bytes32 payloadDigest; Slot slotNumber; + // Streaming Inbox consumption counts. `inboxMsgTotal` is the cumulative Inbox message count + // consumed as of this checkpoint (the child's parent-total origin); `inboxConsumedBucket` is the bucket sequence + // number the header's rolling hash corresponds to. Declared next to the slot number so the three share one storage + // slot (4 + 8 + 8 of 32 bytes): propose writes and reads that slot for the slot-progression check anyway. + uint64 inboxMsgTotal; + uint64 inboxConsumedBucket; FeeHeader feeHeader; + // The consensus Inbox rolling hash the checkpoint header committed to, in a slot of its own: epoch proofs anchor + // both ends of their consumed chain segment against it. + bytes32 inboxRollingHash; } struct CompressedTempCheckpointLog { @@ -44,7 +53,10 @@ struct CompressedTempCheckpointLog { bytes32 attestationsHash; bytes32 payloadDigest; CompressedSlot slotNumber; + uint64 inboxMsgTotal; + uint64 inboxConsumedBucket; CompressedFeeHeader feeHeader; + bytes32 inboxRollingHash; } library CompressedTempCheckpointLogLib { @@ -61,7 +73,10 @@ library CompressedTempCheckpointLogLib { attestationsHash: _checkpoint.attestationsHash, payloadDigest: _checkpoint.payloadDigest, slotNumber: _checkpoint.slotNumber.compress(), - feeHeader: _checkpoint.feeHeader.compress() + inboxMsgTotal: _checkpoint.inboxMsgTotal, + inboxConsumedBucket: _checkpoint.inboxConsumedBucket, + feeHeader: _checkpoint.feeHeader.compress(), + inboxRollingHash: _checkpoint.inboxRollingHash }); } @@ -77,7 +92,10 @@ library CompressedTempCheckpointLogLib { attestationsHash: _compressedCheckpoint.attestationsHash, payloadDigest: _compressedCheckpoint.payloadDigest, slotNumber: _compressedCheckpoint.slotNumber.decompress(), - feeHeader: _compressedCheckpoint.feeHeader.decompress() + inboxMsgTotal: _compressedCheckpoint.inboxMsgTotal, + inboxConsumedBucket: _compressedCheckpoint.inboxConsumedBucket, + feeHeader: _compressedCheckpoint.feeHeader.decompress(), + inboxRollingHash: _compressedCheckpoint.inboxRollingHash }); } } diff --git a/l1-contracts/src/core/libraries/rollup/EpochProofExtLib.sol b/l1-contracts/src/core/libraries/rollup/EpochProofExtLib.sol new file mode 100644 index 000000000000..a71251c19a7b --- /dev/null +++ b/l1-contracts/src/core/libraries/rollup/EpochProofExtLib.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 Aztec Labs. +pragma solidity >=0.8.27; + +import {SubmitEpochRootProofArgs, PublicInputArgs} from "@aztec/core/interfaces/IRollup.sol"; +import {ProposedHeader} from "@aztec/core/libraries/rollup/ProposedHeaderLib.sol"; +import {EpochProofLib} from "./EpochProofLib.sol"; + +/** + * @title EpochProofExtLib - External Rollup Library (Epoch Proof Functions) + * @author Aztec Labs + * @notice External library containing epoch-proof functions for the Rollup contract to avoid exceeding max + * contract size. + * + * @dev This library serves as an external library for the Rollup contract, splitting off the epoch proof + * submission and public-input computation from RollupOperationsExtLib, which the streaming inbox + * validation pushed over the deployable size limit. The library contains external functions primarily + * focused on: + * - Epoch proof submission and verification + * - Epoch proof public input computation + */ +library EpochProofExtLib { + function submitEpochRootProof(SubmitEpochRootProofArgs calldata _args) external { + EpochProofLib.submitEpochRootProof(_args); + } + + function getEpochProofPublicInputs( + uint256 _start, + uint256 _end, + PublicInputArgs calldata _args, + ProposedHeader[] calldata _headers, + bytes calldata _blobPublicInputs + ) external view returns (bytes32[] memory) { + return EpochProofLib.getEpochProofPublicInputs(_start, _end, _args, _headers, _blobPublicInputs); + } +} diff --git a/l1-contracts/src/core/libraries/rollup/EpochProofLib.sol b/l1-contracts/src/core/libraries/rollup/EpochProofLib.sol index 06aa075932cf..e2d90ef1f16d 100644 --- a/l1-contracts/src/core/libraries/rollup/EpochProofLib.sol +++ b/l1-contracts/src/core/libraries/rollup/EpochProofLib.sol @@ -289,6 +289,32 @@ library EpochProofLib { expectedEndArchive == _args.endArchive, Errors.Rollup__InvalidArchive(expectedEndArchive, _args.endArchive) ); } + + // Boundary anchoring for the Inbox rolling-hash chain, mirroring previousArchive/endArchive: + // both ends of the claimed chain segment must match the rolling hashes recorded at propose for checkpoints + // _start - 1 and _end. The start needs this to be sound - the previous checkpoint's header is not among this + // proof's public inputs, so nothing else pins where the segment begins. The end is already pinned transitively + // (the checkpoint root binds end_inbox_rolling_hash to the last checkpoint header, whose hash verifyHeaders ties + // to storage), so checking it here only trades a bare proof-verification failure for a specific error. + // The end check is therefore a deliberate gas-for-diagnostics trade: it costs one cold SLOAD (2,100 gas per + // submission) and can be deleted if the submit path ever needs trimming. + { + bytes32 expectedPreviousInboxRollingHash = STFLib.getInboxRollingHash(_start - 1); + require( + expectedPreviousInboxRollingHash == _args.previousInboxRollingHash, + Errors.Rollup__InvalidPreviousInboxRollingHash( + expectedPreviousInboxRollingHash, _args.previousInboxRollingHash + ) + ); + } + + { + bytes32 expectedEndInboxRollingHash = STFLib.getInboxRollingHash(_end); + require( + expectedEndInboxRollingHash == _args.endInboxRollingHash, + Errors.Rollup__InvalidEndInboxRollingHash(expectedEndInboxRollingHash, _args.endInboxRollingHash) + ); + } } bytes32[] memory publicInputs = new bytes32[](Constants.ROOT_ROLLUP_PUBLIC_INPUTS_LENGTH); @@ -319,9 +345,9 @@ library EpochProofLib { publicInputs[2] = _args.outHash; - // Inbox rolling-hash chain segment consumed across the epoch (AZIP-22 Fast Inbox). Deliberately UNVALIDATED - // until the Fast Inbox flip, when they get checked against per-checkpoint records written at propose; for now - // they are only passed through to the proof's public inputs. + // Inbox rolling-hash chain segment consumed across the epoch. The start is validated above + // against the record written at propose for checkpoint _start - 1; the end is pinned transitively through the + // stored checkpoint header hashes (see the anchoring block in assertAcceptable). publicInputs[3] = _args.previousInboxRollingHash; publicInputs[4] = _args.endInboxRollingHash; } diff --git a/l1-contracts/src/core/libraries/rollup/ProposeLib.sol b/l1-contracts/src/core/libraries/rollup/ProposeLib.sol index 8b4022175636..23dd2aeb7d20 100644 --- a/l1-contracts/src/core/libraries/rollup/ProposeLib.sol +++ b/l1-contracts/src/core/libraries/rollup/ProposeLib.sol @@ -18,6 +18,7 @@ import {ValidatorSelectionLib} from "@aztec/core/libraries/rollup/ValidatorSelec import {Timestamp, Slot, Epoch, TimeLib} from "@aztec/core/libraries/TimeLib.sol"; import {CompressedSlot, CompressedTimeMath} from "@aztec/shared/libraries/CompressedTimeMath.sol"; import {Signature} from "@aztec/shared/libraries/SignatureLib.sol"; +import {SafeCast} from "@oz/utils/math/SafeCast.sol"; import {ProposedHeader, ProposedHeaderLib} from "./ProposedHeaderLib.sol"; import {STFLib} from "./STFLib.sol"; @@ -33,6 +34,10 @@ struct ProposeArgs { bytes32 archive; OracleInput oracleInput; ProposedHeader header; + // Sequence number of the Inbox bucket the header's `inboxRollingHash` corresponds to. + // Unsigned lookup aid kept out of the attested payload digest: a wrong hint can only revert, never change what is + // accepted, since integrity comes from the rolling-hash equality check against the committee-signed header. + uint256 bucketHint; } struct ProposePayload { @@ -46,7 +51,9 @@ struct InterimProposeValues { bytes32[] blobHashes; bytes32 blobsHashesCommitment; bytes[] blobCommitments; - bytes32 inHash; + bytes32 blobCommitmentsHash; + FeeHeader feeHeader; + uint256 consumedInboxMsgTotal; bytes32 headerHash; bytes32 attestationsHash; bytes32 payloadDigest; @@ -121,6 +128,7 @@ library ProposeLib { using TimeLib for Epoch; using CompressedTimeMath for CompressedSlot; using ChainTipsLib for CompressedChainTips; + using SafeCast for uint256; /** * @notice Publishes a new checkpoint to the pending chain. @@ -261,17 +269,31 @@ library ProposeLib { uint256 checkpointNumber = tips.getPending() + 1; tips = tips.updatePending(checkpointNumber); + // Validate the streaming Inbox consumption against the parent checkpoint's consumed position. + // The parent is checkpointNumber - 1, always available: checkpoint 0 carries the {0,0,0} genesis base + // case written at initialization. rollupStore.tips is not committed until below, so the parent read still sees + // the parent as the pending tip. The returned cumulative total is stored in this checkpoint's record so its + // child validates against it and, since temp-log records rewind with the pending chain on a prune, the record + // stays prune-consistent. + v.consumedInboxMsgTotal = validateInboxConsumption( + rollupStore.config.inbox, + v.header.inboxRollingHash, + _args.bucketHint, + v.header.slotNumber, + STFLib.getInboxMsgTotal(checkpointNumber - 1) + ); + // Calculate accumulated blob commitments hash for this checkpoint // Blob commitments are collected and proven per root rollup proof (per epoch), // so we need to know whether we are at the epoch start: v.isFirstCheckpointOfEpoch = v.currentEpoch > STFLib.getEpochForCheckpoint(checkpointNumber - 1) || checkpointNumber == 1; - bytes32 blobCommitmentsHash = BlobLib.calculateBlobCommitmentsHash( + v.blobCommitmentsHash = BlobLib.calculateBlobCommitmentsHash( STFLib.getBlobCommitmentsHash(checkpointNumber - 1), v.blobCommitments, v.isFirstCheckpointOfEpoch ); // Compute fee header for checkpoint metadata - FeeHeader memory feeHeader = FeeLib.computeFeeHeader( + v.feeHeader = FeeLib.computeFeeHeader( checkpointNumber, _args.oracleInput.feeAssetPriceModifier, v.header.totalManaUsed, @@ -289,20 +311,18 @@ library ProposeLib { STFLib.addTempCheckpointLog( TempCheckpointLog({ headerHash: v.headerHash, - blobCommitmentsHash: blobCommitmentsHash, + blobCommitmentsHash: v.blobCommitmentsHash, outHash: v.header.outHash, attestationsHash: v.attestationsHash, payloadDigest: v.payloadDigest, slotNumber: v.header.slotNumber, - feeHeader: feeHeader + feeHeader: v.feeHeader, + inboxRollingHash: v.header.inboxRollingHash, + inboxMsgTotal: v.consumedInboxMsgTotal.toUint64(), + inboxConsumedBucket: _args.bucketHint.toUint64() }) ); - // Consume pending L1->L2 messages and validate against header commitment - // @note The checkpoint number here will always be >=1 as the genesis checkpoint is at 0 - v.inHash = rollupStore.config.inbox.consume(checkpointNumber); - require(v.header.inHash == v.inHash, Errors.Rollup__InvalidInHash(v.inHash, v.header.inHash)); - { bytes32 archive = _args.archive; if (v.isEscapeHatch) { diff --git a/l1-contracts/src/core/libraries/rollup/RollupOperationsExtLib.sol b/l1-contracts/src/core/libraries/rollup/RollupOperationsExtLib.sol index 5d40cf46832b..8d3a5a5e93b2 100644 --- a/l1-contracts/src/core/libraries/rollup/RollupOperationsExtLib.sol +++ b/l1-contracts/src/core/libraries/rollup/RollupOperationsExtLib.sol @@ -4,12 +4,9 @@ pragma solidity >=0.8.27; import {Errors} from "@aztec/core/libraries/Errors.sol"; -import {SubmitEpochRootProofArgs, PublicInputArgs} from "@aztec/core/interfaces/IRollup.sol"; import {STFLib} from "@aztec/core/libraries/rollup/STFLib.sol"; import {Timestamp, TimeLib, Slot, Epoch} from "@aztec/core/libraries/TimeLib.sol"; import {BlobLib} from "@aztec-blob-lib/BlobLib.sol"; -import {EpochProofLib} from "./EpochProofLib.sol"; -import {ProposedHeader} from "@aztec/core/libraries/rollup/ProposedHeaderLib.sol"; import {AttestationLib} from "@aztec/core/libraries/rollup/AttestationLib.sol"; import { ProposeLib, @@ -21,16 +18,16 @@ import { import {Signature} from "@aztec/shared/libraries/SignatureLib.sol"; /** - * @title RollupOperationsExtLib - External Rollup Library (Proposal and Proof Verification Functions) + * @title RollupOperationsExtLib - External Rollup Library (Proposal Functions) * @author Aztec Labs * @notice External library containing proposal-related functions for the Rollup contract to avoid exceeding max * contract size. * * @dev This library serves as an external library for the Rollup contract, splitting off proposal-related - * functionality to keep the main contract within the maximum contract size limit. The library contains - * external functions primarily focused on: + * functionality to keep the main contract within the maximum contract size limit. Epoch-proof functions + * live in EpochProofExtLib to keep this library itself deployable. The library contains external + * functions primarily focused on: * - Checkpoint proposal submission and validation - * - Epoch proof submission and verification * - Blob validation and commitment management * - Chain pruning operations */ @@ -39,10 +36,6 @@ library RollupOperationsExtLib { using TimeLib for Slot; using AttestationLib for CommitteeAttestations; - function submitEpochRootProof(SubmitEpochRootProofArgs calldata _args) external { - EpochProofLib.submitEpochRootProof(_args); - } - function validateHeaderWithAttestations( ValidateHeaderArgs calldata _args, CommitteeAttestations calldata _attestations, @@ -78,16 +71,6 @@ library RollupOperationsExtLib { STFLib.prune(); } - function getEpochProofPublicInputs( - uint256 _start, - uint256 _end, - PublicInputArgs calldata _args, - ProposedHeader[] calldata _headers, - bytes calldata _blobPublicInputs - ) external view returns (bytes32[] memory) { - return EpochProofLib.getEpochProofPublicInputs(_start, _end, _args, _headers, _blobPublicInputs); - } - function validateBlobs(bytes calldata _blobsInput, bool _checkBlob) external view diff --git a/l1-contracts/src/core/libraries/rollup/STFLib.sol b/l1-contracts/src/core/libraries/rollup/STFLib.sol index d776e634157b..eca3e15e7ade 100644 --- a/l1-contracts/src/core/libraries/rollup/STFLib.sol +++ b/l1-contracts/src/core/libraries/rollup/STFLib.sol @@ -132,7 +132,12 @@ library STFLib { slotNumber: Slot.wrap(0), feeHeader: FeeHeader({ excessMana: 0, manaUsed: 0, ethPerFeeAsset: _initialEthPerFeeAsset, congestionCost: 0, proverCost: 0 - }) + }), + // Genesis Inbox consumption base case, matching the Inbox's genesis bucket-0 sentinel {0, 0, 0}, so + // checkpoint 1 validates its consumption against it. + inboxRollingHash: bytes32(0), + inboxMsgTotal: 0, + inboxConsumedBucket: 0 }).compress(); } @@ -305,6 +310,26 @@ library STFLib { return getStorageTempCheckpointLog(_checkpointNumber).slotNumber.decompress(); } + /** + * @notice Retrieves the cumulative Inbox message count consumed as of a checkpoint + * @dev Gas-efficient accessor reading only the streaming-inbox consumed total. Reverts if the checkpoint is stale. + * @param _checkpointNumber The checkpoint number to get the consumed total for + * @return The cumulative Inbox message count consumed as of the checkpoint + */ + function getInboxMsgTotal(uint256 _checkpointNumber) internal view returns (uint64) { + return getStorageTempCheckpointLog(_checkpointNumber).inboxMsgTotal; + } + + /** + * @notice Retrieves the Inbox rolling hash a checkpoint committed to + * @dev Gas-efficient accessor reading only the streaming-inbox rolling hash. Reverts if the checkpoint is stale. + * @param _checkpointNumber The checkpoint number to get the rolling hash for + * @return The consensus Inbox rolling hash recorded for the checkpoint + */ + function getInboxRollingHash(uint256 _checkpointNumber) internal view returns (bytes32) { + return getStorageTempCheckpointLog(_checkpointNumber).inboxRollingHash; + } + /** * @notice Gets the effective pending checkpoint number based on pruning eligibility * @dev Returns either the pending checkpoint number or proven checkpoint number depending on diff --git a/l1-contracts/src/core/messagebridge/Inbox.sol b/l1-contracts/src/core/messagebridge/Inbox.sol index c0782bc87909..4258afb2cc64 100644 --- a/l1-contracts/src/core/messagebridge/Inbox.sol +++ b/l1-contracts/src/core/messagebridge/Inbox.sol @@ -130,10 +130,10 @@ contract Inbox is IInbox { currentTree = trees[inProgress]; } - // this is the global leaf index and not index in the checkpoint subtree - // such that users can simply use it and don't need access to a node if they are to consume it in public. - // trees are constant size so global index = tree number * size + subtree index - uint256 index = (inProgress - Constants.INITIAL_CHECKPOINT_NUMBER) * SIZE + currentTree.nextIndex; + // Compact cumulative message index: the zero-based position of this message in the Inbox's + // insertion order, equal to the number of messages inserted before it. It is embedded in the leaf preimage and + // matches the streaming L1-to-L2 tree's leaf count, so consumers do not need per-checkpoint tree geometry. + uint256 index = totalMessagesInserted; // If the sender is the fee asset portal, we use a magic address to simpler have it initialized at genesis. // We assume that no-one will know the private key for this address and that the precompile won't change to diff --git a/l1-contracts/test/Inbox.t.sol b/l1-contracts/test/Inbox.t.sol index 68cccd760645..7f9e36d4c1e4 100644 --- a/l1-contracts/test/Inbox.t.sol +++ b/l1-contracts/test/Inbox.t.sol @@ -93,7 +93,8 @@ contract InboxTest is Test { function testFuzzInsert(DataStructures.L1ToL2Msg memory _message) public checkInvariant { Inbox.InboxState memory stateBefore = inbox.getState(); - uint256 globalLeafIndex = (FIRST_REAL_TREE_NUM - 1) * SIZE; + // Compact cumulative index: the message's index is the count inserted before it. + uint256 globalLeafIndex = stateBefore.totalMessagesInserted; DataStructures.L1ToL2Msg memory message = _boundMessage(_message, globalLeafIndex); bytes32 leaf = message.sha256ToField(); diff --git a/l1-contracts/test/InboxBuckets.t.sol b/l1-contracts/test/InboxBuckets.t.sol index 574621bcad1b..5f23173c2416 100644 --- a/l1-contracts/test/InboxBuckets.t.sol +++ b/l1-contracts/test/InboxBuckets.t.sol @@ -132,7 +132,8 @@ contract InboxBucketsTest is Test { recipient: recipient, content: content, secretHash: secretHash, - index: (FIRST_REAL_TREE_NUM - 1) * (2 ** HEIGHT) + // Compact cumulative index: the first message against a fresh Inbox has index 0. + index: inbox.getState().totalMessagesInserted }); bytes32 leaf = Hash.sha256ToField(message); bytes16 legacyHash = bytes16(keccak256(abi.encodePacked(inbox.getState().rollingHash, leaf))); diff --git a/l1-contracts/test/Rollup.t.sol b/l1-contracts/test/Rollup.t.sol index 2456474939b7..830e62d33c9f 100644 --- a/l1-contracts/test/Rollup.t.sol +++ b/l1-contracts/test/Rollup.t.sol @@ -173,7 +173,9 @@ contract RollupTest is RollupBase { assertEq( inbox.getInProgress(), - Constants.INITIAL_CHECKPOINT_NUMBER + TestConstants.AZTEC_INBOX_LAG + 1, + // Post-flip the Inbox `consume()` is no longer called at propose, so the frontier in-progress tree no longer + // advances per checkpoint; it stays at its initial value until a tree fills (AZIP-22 Fast Inbox). + Constants.INITIAL_CHECKPOINT_NUMBER + TestConstants.AZTEC_INBOX_LAG, "Invalid in progress" ); @@ -193,7 +195,9 @@ contract RollupTest is RollupBase { rollup.prune(); assertEq( inbox.getInProgress(), - Constants.INITIAL_CHECKPOINT_NUMBER + TestConstants.AZTEC_INBOX_LAG + 1, + // Post-flip the Inbox `consume()` is no longer called at propose, so the frontier in-progress tree no longer + // advances per checkpoint; it stays at its initial value until a tree fills (AZIP-22 Fast Inbox). + Constants.INITIAL_CHECKPOINT_NUMBER + TestConstants.AZTEC_INBOX_LAG, "Invalid in progress" ); assertEq(rollup.getPendingCheckpointNumber(), 0, "Invalid pending checkpoint number"); @@ -209,7 +213,9 @@ contract RollupTest is RollupBase { assertEq( inbox.getInProgress(), - Constants.INITIAL_CHECKPOINT_NUMBER + TestConstants.AZTEC_INBOX_LAG + 1, + // Post-flip the Inbox `consume()` is no longer called at propose, so the frontier in-progress tree no longer + // advances per checkpoint; it stays at its initial value until a tree fills (AZIP-22 Fast Inbox). + Constants.INITIAL_CHECKPOINT_NUMBER + TestConstants.AZTEC_INBOX_LAG, "Invalid in progress" ); assertEq(inbox.getRoot(2), inboxRoot2, "Invalid inbox root"); @@ -237,7 +243,8 @@ contract RollupTest is RollupBase { bytes32[] memory blobHashes = new bytes32[](1); blobHashes[0] = bytes32(uint256(1)); vm.blobhashes(blobHashes); - ProposeArgs memory args = ProposeArgs({header: data.header, archive: data.archive, oracleInput: OracleInput(0)}); + ProposeArgs memory args = + ProposeArgs({header: data.header, archive: data.archive, oracleInput: OracleInput(0), bucketHint: 0}); bytes32 realBlobHash = this.getBlobHashes(data.blobCommitments)[0]; vm.expectRevert(abi.encodeWithSelector(Errors.Rollup__InvalidBlobHash.selector, blobHashes[0], realBlobHash)); rollup.propose( @@ -326,7 +333,8 @@ contract RollupTest is RollupBase { skipBlobCheck(address(rollup)); vm.expectRevert(abi.encodeWithSelector(Errors.Rollup__NonZeroDaFee.selector)); - ProposeArgs memory args = ProposeArgs({header: header, archive: data.archive, oracleInput: OracleInput(0)}); + ProposeArgs memory args = + ProposeArgs({header: header, archive: data.archive, oracleInput: OracleInput(0), bucketHint: 0}); rollup.propose( args, AttestationLibHelper.packAttestations(attestations), @@ -353,7 +361,8 @@ contract RollupTest is RollupBase { // When not canonical, we expect the fee to be 0 vm.expectRevert(abi.encodeWithSelector(Errors.Rollup__InvalidManaMinFee.selector, expectedFee, 1)); - ProposeArgs memory args = ProposeArgs({header: header, archive: data.archive, oracleInput: OracleInput(0)}); + ProposeArgs memory args = + ProposeArgs({header: header, archive: data.archive, oracleInput: OracleInput(0), bucketHint: 0}); rollup.propose( args, AttestationLibHelper.packAttestations(attestations), @@ -461,8 +470,12 @@ contract RollupTest is RollupBase { interim.feeAmount = interim.manaUsed * interim.minFee + interim.portalBalance; header.accumulatedFees = interim.feeAmount; + // Streaming Inbox: nothing is seeded here, so reference the genesis bucket (hash 0). + header.inboxRollingHash = bytes32(0); + // Assert that balance have NOT been increased by proposing the checkpoint - ProposeArgs memory args = ProposeArgs({header: header, archive: data.archive, oracleInput: OracleInput(0)}); + ProposeArgs memory args = + ProposeArgs({header: header, archive: data.archive, oracleInput: OracleInput(0), bucketHint: 0}); rollup.propose( args, AttestationLibHelper.packAttestations(attestations), @@ -668,7 +681,8 @@ contract RollupTest is RollupBase { skipBlobCheck(address(rollup)); vm.expectRevert(abi.encodeWithSelector(Errors.Rollup__InvalidTimestamp.selector, realTs, badTs)); - ProposeArgs memory args = ProposeArgs({header: header, archive: archive, oracleInput: OracleInput(0)}); + ProposeArgs memory args = + ProposeArgs({header: header, archive: archive, oracleInput: OracleInput(0), bucketHint: 0}); rollup.propose( args, AttestationLibHelper.packAttestations(attestations), @@ -694,7 +708,8 @@ contract RollupTest is RollupBase { vm.blobhashes(blobHashes); skipBlobCheck(address(rollup)); vm.expectRevert(abi.encodeWithSelector(Errors.Rollup__InvalidCoinbase.selector)); - ProposeArgs memory args = ProposeArgs({header: header, archive: archive, oracleInput: OracleInput(0)}); + ProposeArgs memory args = + ProposeArgs({header: header, archive: archive, oracleInput: OracleInput(0), bucketHint: 0}); rollup.propose( args, AttestationLibHelper.packAttestations(attestations), @@ -770,7 +785,8 @@ contract RollupTest is RollupBase { header.gasFees.feePerL2Gas = uint128(rollup.getManaMinFeeAt(Timestamp.wrap(block.timestamp), true)); vm.expectRevert(abi.encodeWithSelector(Errors.Rollup__NoBlobsInCheckpoint.selector)); - ProposeArgs memory args = ProposeArgs({header: header, archive: archive, oracleInput: OracleInput(0)}); + ProposeArgs memory args = + ProposeArgs({header: header, archive: archive, oracleInput: OracleInput(0), bucketHint: 0}); rollup.propose( args, AttestationLibHelper.packAttestations(attestations), @@ -889,7 +905,7 @@ contract RollupTest is RollupBase { endArchive: data.archive, outHash: data.header.outHash, previousInboxRollingHash: 0, - endInboxRollingHash: data.header.inboxRollingHash, + endInboxRollingHash: proposedHeaders[1].inboxRollingHash, proverId: address(0) }); @@ -920,6 +936,61 @@ contract RollupTest is RollupBase { rollup.getEpochProofPublicInputs(1, 1, args, headers, data.batchedBlobInputs); } + // The epoch-proof anchoring pins the rolling-hash chain start to the record written at propose for checkpoint + // start - 1, mirroring previousArchive. A wrong previousInboxRollingHash must be rejected. + function testGetEpochProofPublicInputsRejectsWrongPreviousInboxRollingHash() public setUpFor("empty_checkpoint_1") { + _proposeCheckpoint("empty_checkpoint_1", 1); + + DecoderBase.Data memory data = load("empty_checkpoint_1").checkpoint; + CheckpointLog memory checkpoint = rollup.getCheckpoint(0); + + ProposedHeader[] memory headers = new ProposedHeader[](1); + headers[0] = proposedHeaders[1]; + + // Checkpoint 0 is genesis with a zero rolling hash, so any non-zero chain start is wrong. + bytes32 wrongPrevious = bytes32(uint256(1)); + PublicInputArgs memory args = PublicInputArgs({ + previousArchive: checkpoint.archive, + endArchive: data.archive, + outHash: data.header.outHash, + previousInboxRollingHash: wrongPrevious, + endInboxRollingHash: proposedHeaders[1].inboxRollingHash, + proverId: address(0) + }); + + vm.expectRevert( + abi.encodeWithSelector(Errors.Rollup__InvalidPreviousInboxRollingHash.selector, bytes32(0), wrongPrevious) + ); + rollup.getEpochProofPublicInputs(1, 1, args, headers, data.batchedBlobInputs); + } + + // The end of the rolling-hash chain segment is pinned to the hash recorded at propose for the epoch's last + // checkpoint, mirroring endArchive. A wrong endInboxRollingHash must be rejected here rather + // than surfacing as a generic proof-verification failure. + function testGetEpochProofPublicInputsRejectsWrongEndInboxRollingHash() public setUpFor("empty_checkpoint_1") { + _proposeCheckpoint("empty_checkpoint_1", 1); + + DecoderBase.Data memory data = load("empty_checkpoint_1").checkpoint; + CheckpointLog memory checkpoint = rollup.getCheckpoint(0); + + ProposedHeader[] memory headers = new ProposedHeader[](1); + headers[0] = proposedHeaders[1]; + + bytes32 expectedEnd = proposedHeaders[1].inboxRollingHash; + bytes32 wrongEnd = bytes32(uint256(expectedEnd) + 1); + PublicInputArgs memory args = PublicInputArgs({ + previousArchive: checkpoint.archive, + endArchive: data.archive, + outHash: data.header.outHash, + previousInboxRollingHash: 0, + endInboxRollingHash: wrongEnd, + proverId: address(0) + }); + + vm.expectRevert(abi.encodeWithSelector(Errors.Rollup__InvalidEndInboxRollingHash.selector, expectedEnd, wrongEnd)); + rollup.getEpochProofPublicInputs(1, 1, args, headers, data.batchedBlobInputs); + } + function _submitEpochProof( uint256 _start, uint256 _end, @@ -945,7 +1016,7 @@ contract RollupTest is RollupBase { endArchive: _archive, outHash: _outHash, previousInboxRollingHash: 0, - endInboxRollingHash: 0, + endInboxRollingHash: proposedHeaders[_end].inboxRollingHash, proverId: _prover }); diff --git a/l1-contracts/test/RollupFieldRange.t.sol b/l1-contracts/test/RollupFieldRange.t.sol index d10c187e9f75..3a910ac3e9d6 100644 --- a/l1-contracts/test/RollupFieldRange.t.sol +++ b/l1-contracts/test/RollupFieldRange.t.sol @@ -175,7 +175,11 @@ contract RollupFieldRangeTest is RollupBase { vm.blobhashes(this.getBlobHashes(full.checkpoint.blobCommitments)); - ProposeArgs memory args = ProposeArgs({header: header, archive: bytes32(FIELD_MAX), oracleInput: OracleInput(0)}); + // Streaming Inbox: nothing is seeded here, so reference the genesis bucket (hash 0). + header.inboxRollingHash = bytes32(0); + + ProposeArgs memory args = + ProposeArgs({header: header, archive: bytes32(FIELD_MAX), oracleInput: OracleInput(0), bucketHint: 0}); rollup.propose( args, @@ -202,7 +206,8 @@ contract RollupFieldRangeTest is RollupBase { skipBlobCheck(address(rollup)); bytes32 archive = _useFixtureArchive ? full.checkpoint.archive : bytes32(_archive); - return ProposeArgs({header: header, archive: archive, oracleInput: OracleInput(0)}); + // Boundary tests revert on a field-range check before Inbox consumption; the genesis bucket hint suffices. + return ProposeArgs({header: header, archive: archive, oracleInput: OracleInput(0), bucketHint: 0}); } function _expectFieldOutOfRange(ProposeArgs memory _args, bytes32 _value) internal { diff --git a/l1-contracts/test/base/RollupBase.sol b/l1-contracts/test/base/RollupBase.sol index f75b9875d013..4f50e6b3c4eb 100644 --- a/l1-contracts/test/base/RollupBase.sol +++ b/l1-contracts/test/base/RollupBase.sol @@ -78,8 +78,10 @@ contract RollupBase is DecoderBase { previousArchive: parentCheckpointLog.archive, endArchive: endFull.checkpoint.archive, outHash: endFull.checkpoint.header.outHash, - previousInboxRollingHash: 0, - endInboxRollingHash: 0, + // Anchor the rolling-hash chain start to the record written at propose for checkpoint start - 1. + // The end value is unchecked on L1 but supplied for completeness. + previousInboxRollingHash: proposedHeaders[startCheckpointNumber - 1].inboxRollingHash, + endInboxRollingHash: proposedHeaders[endCheckpointNumber].inboxRollingHash, proverId: _prover }); @@ -163,11 +165,19 @@ contract RollupBase is DecoderBase { checkpointFees[full.checkpoint.checkpointNumber] = _manaUsed * minFee; - // We jump to the time of the block. (unless it is in the past) - vm.warp(max(block.timestamp, Timestamp.unwrap(full.checkpoint.header.timestamp))); - + // Seed the Inbox before jumping to the checkpoint's L1 block: propose rejects a bucket that is still + // accumulating, and a bucket keeps accumulating for the whole L1 block that opened it. _populateInbox(full.populate.sender, full.populate.recipient, full.populate.l1ToL2Content); + + // We jump to the time of the block, always past the L1 block the messages above landed in. + vm.warp(max(block.timestamp + 1, Timestamp.unwrap(full.checkpoint.header.timestamp))); + // Legacy frontier root for the header's inHash field. Unchecked at propose post-flip, but kept because the + // fixtures were generated with it as part of the header hash. full.checkpoint.header.inHash = rollup.getInbox().getRoot(full.checkpoint.checkpointNumber); + // Streaming Inbox: reference the newest bucket so the checkpoint consumes all messages + // seeded above and the mandatory-consumption assert is trivially satisfied (a wrong ref could only revert). + uint256 bucketHint = rollup.getInbox().getCurrentBucketSeq(); + full.checkpoint.header.inboxRollingHash = rollup.getInbox().getBucket(bucketHint).rollingHash; { bytes32[] memory blobHashes; @@ -197,8 +207,12 @@ contract RollupBase is DecoderBase { proposedHeaders[full.checkpoint.checkpointNumber] = full.checkpoint.header; - ProposeArgs memory args = - ProposeArgs({header: full.checkpoint.header, archive: full.checkpoint.archive, oracleInput: OracleInput(0)}); + ProposeArgs memory args = ProposeArgs({ + header: full.checkpoint.header, + archive: full.checkpoint.archive, + oracleInput: OracleInput(0), + bucketHint: bucketHint + }); if (_revertMsg.length > 0) { vm.expectRevert(_revertMsg); diff --git a/l1-contracts/test/benchmark/happy.t.sol b/l1-contracts/test/benchmark/happy.t.sol index f5f0a900b888..7cbf18d9f0da 100644 --- a/l1-contracts/test/benchmark/happy.t.sol +++ b/l1-contracts/test/benchmark/happy.t.sol @@ -274,10 +274,15 @@ contract BenchmarkRollupTest is FeeModelTestPoints, DecoderBase { header.totalManaUsed = manaSpent; header.accumulatedFees = uint256(manaMinFee) * manaSpent; + // Streaming Inbox: reference the newest bucket (nothing seeded here, so the genesis bucket). + uint256 bucketHint = rollup.getInbox().getCurrentBucketSeq(); + header.inboxRollingHash = rollup.getInbox().getBucket(bucketHint).rollingHash; + ProposeArgs memory proposeArgs = ProposeArgs({ header: header, archive: archiveRoot, - oracleInput: OracleInput({feeAssetPriceModifier: point.oracle_input.fee_asset_price_modifier}) + oracleInput: OracleInput({feeAssetPriceModifier: point.oracle_input.fee_asset_price_modifier}), + bucketHint: bucketHint }); CommitteeAttestation[] memory attestations; diff --git a/l1-contracts/test/compression/PreHeating.t.sol b/l1-contracts/test/compression/PreHeating.t.sol index 7cf07a0c9298..7bce6f907c2f 100644 --- a/l1-contracts/test/compression/PreHeating.t.sol +++ b/l1-contracts/test/compression/PreHeating.t.sol @@ -327,10 +327,15 @@ contract PreHeatingTest is FeeModelTestPoints, DecoderBase { header.totalManaUsed = manaSpent; header.accumulatedFees = uint256(manaMinFee) * manaSpent; + // Streaming Inbox: reference the newest bucket (nothing seeded here, so the genesis bucket). + uint256 bucketHint = rollup.getInbox().getCurrentBucketSeq(); + header.inboxRollingHash = rollup.getInbox().getBucket(bucketHint).rollingHash; + ProposeArgs memory proposeArgs = ProposeArgs({ header: header, archive: archiveRoot, - oracleInput: OracleInput({feeAssetPriceModifier: point.oracle_input.fee_asset_price_modifier}) + oracleInput: OracleInput({feeAssetPriceModifier: point.oracle_input.fee_asset_price_modifier}), + bucketHint: bucketHint }); CommitteeAttestation[] memory attestations; diff --git a/l1-contracts/test/escape-hatch/integration/EscapeHatchIntegrationBase.sol b/l1-contracts/test/escape-hatch/integration/EscapeHatchIntegrationBase.sol index 7b2e16301bbb..5e6cd5fea89e 100644 --- a/l1-contracts/test/escape-hatch/integration/EscapeHatchIntegrationBase.sol +++ b/l1-contracts/test/escape-hatch/integration/EscapeHatchIntegrationBase.sol @@ -155,7 +155,13 @@ abstract contract EscapeHatchIntegrationBase is ValidatorSelectionTestBase { accumulatedFees: 0 }); - args = ProposeArgs({header: header, archive: archive, oracleInput: OracleInput({feeAssetPriceModifier: 0})}); + // Streaming Inbox: reference the newest bucket (genesis here; nothing is seeded). + uint256 bucketHint = rollup.getInbox().getCurrentBucketSeq(); + header.inboxRollingHash = rollup.getInbox().getBucket(bucketHint).rollingHash; + + args = ProposeArgs({ + header: header, archive: archive, oracleInput: OracleInput({feeAssetPriceModifier: 0}), bucketHint: bucketHint + }); blobs = full.checkpoint.blobCommitments; } @@ -205,8 +211,13 @@ abstract contract EscapeHatchIntegrationBase is ValidatorSelectionTestBase { header.gasFees.feePerL2Gas = manaMinFee; } - ProposeArgs memory proposeArgs = - ProposeArgs({header: header, archive: full.checkpoint.archive, oracleInput: OracleInput(0)}); + // Streaming Inbox: reference the newest bucket (genesis here; nothing is seeded). + uint256 bucketHint = rollup.getInbox().getCurrentBucketSeq(); + header.inboxRollingHash = rollup.getInbox().getBucket(bucketHint).rollingHash; + + ProposeArgs memory proposeArgs = ProposeArgs({ + header: header, archive: full.checkpoint.archive, oracleInput: OracleInput(0), bucketHint: bucketHint + }); skipBlobCheck(address(rollup)); diff --git a/l1-contracts/test/escape-hatch/integration/invalidate.t.sol b/l1-contracts/test/escape-hatch/integration/invalidate.t.sol index f4709a59eabf..de6808f2a11e 100644 --- a/l1-contracts/test/escape-hatch/integration/invalidate.t.sol +++ b/l1-contracts/test/escape-hatch/integration/invalidate.t.sol @@ -139,8 +139,13 @@ contract invalidateTest is EscapeHatchIntegrationBase { header.gasFees.feePerL2Gas = manaMinFee; } - ProposeArgs memory proposeArgs = - ProposeArgs({header: header, archive: full.checkpoint.archive, oracleInput: OracleInput(0)}); + // Streaming Inbox: reference the newest bucket (genesis here; nothing is seeded). + uint256 bucketHint = rollup.getInbox().getCurrentBucketSeq(); + header.inboxRollingHash = rollup.getInbox().getBucket(bucketHint).rollingHash; + + ProposeArgs memory proposeArgs = ProposeArgs({ + header: header, archive: full.checkpoint.archive, oracleInput: OracleInput(0), bucketHint: bucketHint + }); skipBlobCheck(address(rollup)); diff --git a/l1-contracts/test/fee_portal/depositToAztecPublic.t.sol b/l1-contracts/test/fee_portal/depositToAztecPublic.t.sol index 61a5ff73a90f..bdda37eb7990 100644 --- a/l1-contracts/test/fee_portal/depositToAztecPublic.t.sol +++ b/l1-contracts/test/fee_portal/depositToAztecPublic.t.sol @@ -82,12 +82,9 @@ contract DepositToAztecPublic is Test { uint256 amount = 100 ether; Inbox inbox = Inbox(address(Rollup(address(registry.getCanonicalRollup())).getInbox())); - // The first message goes into tree (INITIAL_CHECKPOINT_NUMBER + LAG) at index 0 - // Global index = (inProgress - INITIAL_CHECKPOINT_NUMBER) * SIZE + 0 - // = ((INITIAL_CHECKPOINT_NUMBER + LAG) - INITIAL_CHECKPOINT_NUMBER) * SIZE - // = LAG * SIZE - uint256 SIZE = 2 ** Constants.L1_TO_L2_MSG_SUBTREE_HEIGHT; - uint256 expectedIndex = TestConstants.AZTEC_INBOX_LAG * SIZE; + // Compact cumulative index (AZIP-22 Fast Inbox): the first message against a fresh Inbox has index 0 + // (equal to the number of messages inserted before it), regardless of the inbox lag. + uint256 expectedIndex = 0; // The purpose of including the function selector is to make the message unique to that specific call. Note that // it has nothing to do with calling the function. @@ -156,8 +153,10 @@ contract DepositToAztecPublic is Test { "Initial inProgress should be INITIAL_CHECKPOINT_NUMBER + lag" ); state.initialInProgress = state.testInbox.getInProgress(); - state.expectedIndex1 = state.lag * state.SIZE; - state.expectedIndex2 = state.lag * state.SIZE + 1; + // Compact cumulative index (AZIP-22 Fast Inbox): messages are indexed by insertion order from 0, + // independent of the lag-based tree geometry. + state.expectedIndex1 = 0; + state.expectedIndex2 = 1; vm.prank(state.testToken.owner()); state.testToken.mint(address(this), state.amount * 2); @@ -165,7 +164,7 @@ contract DepositToAztecPublic is Test { // Send first message (, uint256 index1) = state.testFeeJuicePortal.depositToAztecPublic(state.to, state.amount, state.secretHash1); - assertEq(index1, state.expectedIndex1, "First message index should be lag * SIZE"); + assertEq(index1, state.expectedIndex1, "First message index should be 0 (compact cumulative)"); assertEq( state.testInbox.getInProgress(), state.expectedInProgress, @@ -175,7 +174,7 @@ contract DepositToAztecPublic is Test { // Send second message (, uint256 index2) = state.testFeeJuicePortal.depositToAztecPublic(state.to, state.amount, state.secretHash2); - assertEq(index2, state.expectedIndex2, "Second message index should be lag * SIZE + 1"); + assertEq(index2, state.expectedIndex2, "Second message index should be 1 (compact cumulative)"); assertEq( state.testInbox.getInProgress(), state.expectedInProgress, diff --git a/l1-contracts/test/fees/FeeHeaderOverflow.t.sol b/l1-contracts/test/fees/FeeHeaderOverflow.t.sol index ed8a7e208798..4d5057bc1112 100644 --- a/l1-contracts/test/fees/FeeHeaderOverflow.t.sol +++ b/l1-contracts/test/fees/FeeHeaderOverflow.t.sol @@ -98,11 +98,20 @@ contract FeeHeaderOverflowTest is DecoderBase { header.gasFees.feePerDaGas = 0; header.totalManaUsed = 0; + // Streaming Inbox: reference the newest bucket (genesis here; nothing is seeded). + uint256 bucketHint = _rollup.getInbox().getCurrentBucketSeq(); + header.inboxRollingHash = _rollup.getInbox().getBucket(bucketHint).rollingHash; + CommitteeAttestation[] memory attestations = new CommitteeAttestation[](0); address[] memory signers = new address[](0); return ( - ProposeArgs({header: header, archive: archiveRoot, oracleInput: OracleInput({feeAssetPriceModifier: 0})}), + ProposeArgs({ + header: header, + archive: archiveRoot, + oracleInput: OracleInput({feeAssetPriceModifier: 0}), + bucketHint: bucketHint + }), AttestationLibHelper.packAttestations(attestations), signers ); diff --git a/l1-contracts/test/fees/FeeRollup.t.sol b/l1-contracts/test/fees/FeeRollup.t.sol index 26d858ee7e71..52219fd3e200 100644 --- a/l1-contracts/test/fees/FeeRollup.t.sol +++ b/l1-contracts/test/fees/FeeRollup.t.sol @@ -269,13 +269,17 @@ contract FeeRollupTest is FeeModelTestPoints, DecoderBase { if (rollup.getCurrentSlot() == nextSlot) { TestPoint memory point = points[Slot.unwrap(nextSlot) - 1]; Checkpoint memory b = getCheckpoint(); + // Streaming Inbox: reference the newest bucket (genesis here; nothing is seeded). + uint256 bucketHint = rollup.getInbox().getCurrentBucketSeq(); + b.header.inboxRollingHash = rollup.getInbox().getBucket(bucketHint).rollingHash; skipBlobCheck(address(rollup)); checkpointHeaders[rollup.getPendingCheckpointNumber() + 1] = b.header; rollup.propose( ProposeArgs({ header: b.header, archive: b.archive, - oracleInput: OracleInput({feeAssetPriceModifier: point.oracle_input.fee_asset_price_modifier}) + oracleInput: OracleInput({feeAssetPriceModifier: point.oracle_input.fee_asset_price_modifier}), + bucketHint: bucketHint }), AttestationLibHelper.packAttestations(b.attestations), b.signers, @@ -361,13 +365,17 @@ contract FeeRollupTest is FeeModelTestPoints, DecoderBase { Checkpoint memory b = getCheckpoint(); + // Streaming Inbox: reference the newest bucket (genesis here; nothing is seeded). + uint256 bucketHint = rollup.getInbox().getCurrentBucketSeq(); + b.header.inboxRollingHash = rollup.getInbox().getBucket(bucketHint).rollingHash; skipBlobCheck(address(rollup)); checkpointHeaders[rollup.getPendingCheckpointNumber() + 1] = b.header; rollup.propose( ProposeArgs({ header: b.header, archive: b.archive, - oracleInput: OracleInput({feeAssetPriceModifier: point.oracle_input.fee_asset_price_modifier}) + oracleInput: OracleInput({feeAssetPriceModifier: point.oracle_input.fee_asset_price_modifier}), + bucketHint: bucketHint }), AttestationLibHelper.packAttestations(b.attestations), b.signers, diff --git a/l1-contracts/test/fees/MinimalFeeModel.sol b/l1-contracts/test/fees/MinimalFeeModel.sol index f37d6dd4fd7f..24d433406390 100644 --- a/l1-contracts/test/fees/MinimalFeeModel.sol +++ b/l1-contracts/test/fees/MinimalFeeModel.sol @@ -126,7 +126,10 @@ contract MinimalFeeModel { attestationsHash: bytes32(0), payloadDigest: bytes32(0), slotNumber: Slot.wrap(0), - feeHeader: FeeLib.computeFeeHeader(checkpointNumber, _oracleInput.feeAssetPriceModifier, _manaUsed, 0, 0) + feeHeader: FeeLib.computeFeeHeader(checkpointNumber, _oracleInput.feeAssetPriceModifier, _manaUsed, 0, 0), + inboxRollingHash: bytes32(0), + inboxMsgTotal: 0, + inboxConsumedBucket: 0 }) ); // FeeLib.writeFeeHeader(++populatedThrough, _oracleInput.feeAssetPriceModifier, _manaUsed, 0, 0); diff --git a/l1-contracts/test/harnesses/InboxHarness.sol b/l1-contracts/test/harnesses/InboxHarness.sol index 1ba2b3b446ca..b7489a0d4a9f 100644 --- a/l1-contracts/test/harnesses/InboxHarness.sol +++ b/l1-contracts/test/harnesses/InboxHarness.sol @@ -40,8 +40,7 @@ contract InboxHarness is Inbox { } function getNextMessageIndex() external view returns (uint256) { - FrontierLib.Tree storage currentTree = trees[state.inProgress]; - uint256 index = (state.inProgress - Constants.INITIAL_CHECKPOINT_NUMBER) * SIZE + currentTree.nextIndex; - return index; + // Compact cumulative index: the next message's index is the count inserted so far. + return state.totalMessagesInserted; } } diff --git a/l1-contracts/test/portals/TokenPortal.t.sol b/l1-contracts/test/portals/TokenPortal.t.sol index cb6bf77e3bd6..ef51998626b8 100644 --- a/l1-contracts/test/portals/TokenPortal.t.sol +++ b/l1-contracts/test/portals/TokenPortal.t.sol @@ -118,8 +118,9 @@ contract TokenPortalTest is Test { testERC20.mint(address(this), mintAmount); testERC20.approve(address(tokenPortal), mintAmount); - // Check for the expected message - uint256 expectedIndex = (FIRST_REAL_TREE_NUM - 1) * L1_TO_L2_MSG_SUBTREE_SIZE; + // Check for the expected message. + // Compact cumulative index: the first message against a fresh Inbox has index 0. + uint256 expectedIndex = 0; DataStructures.L1ToL2Msg memory expectedMessage = _createExpectedMintPrivateL1ToL2Message(expectedIndex); bytes32 expectedLeaf = expectedMessage.sha256ToField(); @@ -146,8 +147,9 @@ contract TokenPortalTest is Test { testERC20.mint(address(this), mintAmount); testERC20.approve(address(tokenPortal), mintAmount); - // Check for the expected message - uint256 expectedIndex = (FIRST_REAL_TREE_NUM - 1) * L1_TO_L2_MSG_SUBTREE_SIZE; + // Check for the expected message. + // Compact cumulative index: the first message against a fresh Inbox has index 0. + uint256 expectedIndex = 0; DataStructures.L1ToL2Msg memory expectedMessage = _createExpectedMintPublicL1ToL2Message(expectedIndex); bytes32 expectedLeaf = expectedMessage.sha256ToField(); bytes16 expectedHash = bytes16(keccak256(abi.encodePacked(inbox.getState().rollingHash, expectedLeaf))); diff --git a/l1-contracts/test/rollup/libraries/rewardlib/RewardLibWrapper.sol b/l1-contracts/test/rollup/libraries/rewardlib/RewardLibWrapper.sol index dc37e84e3757..4aa2a583739e 100644 --- a/l1-contracts/test/rollup/libraries/rewardlib/RewardLibWrapper.sol +++ b/l1-contracts/test/rollup/libraries/rewardlib/RewardLibWrapper.sol @@ -116,7 +116,10 @@ contract RewardLibWrapper { attestationsHash: bytes32(0), payloadDigest: bytes32(0), slotNumber: Slot.wrap(0), - feeHeader: _feeHeader + feeHeader: _feeHeader, + inboxRollingHash: bytes32(0), + inboxMsgTotal: 0, + inboxConsumedBucket: 0 }) ); } diff --git a/l1-contracts/test/tmnt419.t.sol b/l1-contracts/test/tmnt419.t.sol index 525df4ff47f2..2cd12d72c9d5 100644 --- a/l1-contracts/test/tmnt419.t.sol +++ b/l1-contracts/test/tmnt419.t.sol @@ -173,8 +173,13 @@ contract Tmnt419Test is RollupBase { header.gasFees.feePerL2Gas = SafeCast.toUint128(rollup.getManaMinFeeAt(Timestamp.wrap(block.timestamp), true)); header.totalManaUsed = MANA_TARGET; - ProposeArgs memory proposeArgs = - ProposeArgs({header: header, archive: archiveRoot, oracleInput: OracleInput({feeAssetPriceModifier: 0})}); + // Streaming Inbox: reference the newest bucket so any seeded messages are consumed. + uint256 bucketHint = rollup.getInbox().getCurrentBucketSeq(); + header.inboxRollingHash = rollup.getInbox().getBucket(bucketHint).rollingHash; + + ProposeArgs memory proposeArgs = ProposeArgs({ + header: header, archive: archiveRoot, oracleInput: OracleInput({feeAssetPriceModifier: 0}), bucketHint: bucketHint + }); CommitteeAttestation[] memory attestations = new CommitteeAttestation[](0); address[] memory signers = new address[](0); diff --git a/l1-contracts/test/validator-selection/ValidatorSelection.t.sol b/l1-contracts/test/validator-selection/ValidatorSelection.t.sol index 722e97dc320a..19da2d10ca76 100644 --- a/l1-contracts/test/validator-selection/ValidatorSelection.t.sol +++ b/l1-contracts/test/validator-selection/ValidatorSelection.t.sol @@ -531,11 +531,13 @@ contract ValidatorSelectionTest is ValidatorSelectionTestBase { DecoderBase.Full memory full = load(_name); ProposedHeader memory header = full.checkpoint.header; - // We jump to the time of the block. (unless it is in the past) - vm.warp(max(block.timestamp, Timestamp.unwrap(full.checkpoint.header.timestamp))); - + // Seed the Inbox before jumping to the checkpoint's L1 block: propose rejects a bucket that is still + // accumulating, and a bucket keeps accumulating for the whole L1 block that opened it. _populateInbox(full.populate.sender, full.populate.recipient, full.populate.l1ToL2Content); + // We jump to the time of the block, always past the L1 block the messages above landed in. + vm.warp(max(block.timestamp + 1, Timestamp.unwrap(full.checkpoint.header.timestamp))); + rollup.setupEpoch(); ree.proposer = rollup.getCurrentProposer(); @@ -549,7 +551,13 @@ contract ValidatorSelectionTest is ValidatorSelectionTestBase { header.gasFees.feePerL2Gas = manaMinFee; } - ree.proposeArgs = ProposeArgs({header: header, archive: full.checkpoint.archive, oracleInput: OracleInput(0)}); + // Streaming Inbox: reference the newest bucket, consuming the messages seeded above. + uint256 bucketHint = inbox.getCurrentBucketSeq(); + header.inboxRollingHash = inbox.getBucket(bucketHint).rollingHash; + + ree.proposeArgs = ProposeArgs({ + header: header, archive: full.checkpoint.archive, oracleInput: OracleInput(0), bucketHint: bucketHint + }); skipBlobCheck(address(rollup)); @@ -746,7 +754,7 @@ contract ValidatorSelectionTest is ValidatorSelectionTestBase { endArchive: endFull.checkpoint.archive, outHash: endFull.checkpoint.header.outHash, previousInboxRollingHash: 0, - endInboxRollingHash: 0, + endInboxRollingHash: proposedHeaders[endCheckpointNumber].inboxRollingHash, proverId: prover }); diff --git a/l1-contracts/test/validator-selection/tmnt207.t.sol b/l1-contracts/test/validator-selection/tmnt207.t.sol index 85a34b8f84a1..f01f9b6440a2 100644 --- a/l1-contracts/test/validator-selection/tmnt207.t.sol +++ b/l1-contracts/test/validator-selection/tmnt207.t.sol @@ -263,8 +263,13 @@ contract Tmnt207Test is RollupBase { header.gasFees.feePerL2Gas = SafeCast.toUint128(rollup.getManaMinFeeAt(Timestamp.wrap(block.timestamp), true)); header.totalManaUsed = MANA_TARGET; - ProposeArgs memory proposeArgs = - ProposeArgs({header: header, archive: archiveRoot, oracleInput: OracleInput({feeAssetPriceModifier: 0})}); + // Streaming Inbox: reference the newest bucket (genesis here; nothing is seeded). + uint256 bucketHint = rollup.getInbox().getCurrentBucketSeq(); + header.inboxRollingHash = rollup.getInbox().getBucket(bucketHint).rollingHash; + + ProposeArgs memory proposeArgs = ProposeArgs({ + header: header, archive: archiveRoot, oracleInput: OracleInput({feeAssetPriceModifier: 0}), bucketHint: bucketHint + }); CommitteeAttestation[] memory attestations; address[] memory signers; diff --git a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-block-merge/Prover.toml b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-block-merge/Prover.toml index ae1219326b3c..665c89c2f99f 100644 --- a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-block-merge/Prover.toml +++ b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-block-merge/Prover.toml @@ -484,7 +484,7 @@ proof = [ [inputs.previous_rollups.public_inputs] timestamp = "0x0000000000000000000000000000000000000000000000000000000000000186" - block_headers_hash = "0x039c20f9a005e10660c5c4cfb680e4fd5ed1f5dd1f54a7ef2e6e43c4df4e4880" + block_headers_hash = "0x06defb29e0cc83c263924a02dbfacdaf24e07f985d6e331324980497f5ab009d" out_hash = "0x006bd7618b0cf7b40e3f107022eee2d411bcc5850fbb774dacc46a15957659c4" accumulated_fees = "0x0000000000000000000000000000000000000000000000000000000000000000" accumulated_mana_used = "0x000000000000000000000000000000000000000000000000000000000006b6c0" @@ -492,7 +492,7 @@ proof = [ [inputs.previous_rollups.public_inputs.constants] chain_id = "0x0000000000000000000000000000000000000000000000000000000000000000" version = "0x0000000000000000000000000000000000000000000000000000000000000000" - vk_tree_root = "0x16b7eeebd9ec7b6b28187878be4fef52c87331b0ebf2e5719fd203a0474402b1" + vk_tree_root = "0x121ed84a634911bbb8a5e986c40e120a016a1713140a6a5ea4f8187a5e07e547" protocol_contracts_hash = "0x08d0026a4983c49638d0910859942c6648d5ea0a3a152c8a376e4983d0a003d9" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" slot_number = "0x000000000000000000000000000000000000000000000000000000000000000f" @@ -512,7 +512,7 @@ proof = [ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000001" [inputs.previous_rollups.public_inputs.new_archive] - root = "0x17ed0d01fc0f9551271cfba4270e351858fcba108e95d76959c62156d0d5f6d9" + root = "0x0a02b885838fb80d52ac8d0d5c2955fd2298804ed9cea5b239e7e4c0bf5c99cb" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000002" [inputs.previous_rollups.public_inputs.start_state.l1_to_l2_message_tree] @@ -532,8 +532,8 @@ root = "0x1a90881964e28a92a419f1d8361c14ac147b6f9175c04fdf57dadf0d7ba781c9" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000080" [inputs.previous_rollups.public_inputs.end_state.l1_to_l2_message_tree] -root = "0x18d6aae3ab4a271abd590cb98267825b70de1e9e5c339356e94ea5fc7feba64b" -next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000400" +root = "0x0c123047b777079e8db5f669ddaf1ffa1c7780d56b21e29f35e4772ac0f6280c" +next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000100" [inputs.previous_rollups.public_inputs.end_state.partial.note_hash_tree] root = "0x01612d24a146efc2df9d815a2f733c17486304424577ffb4232fe4cb0c94e1e7" @@ -572,13 +572,13 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 cache = [ "0x2d9141720c810b831246b0e50c0ad9d4b935418ba2ae8a06c395bb80340ee684", "0x1a90881964e28a92a419f1d8361c14ac147b6f9175c04fdf57dadf0d7ba781c9", - "0x18d6aae3ab4a271abd590cb98267825b70de1e9e5c339356e94ea5fc7feba64b" + "0x0c123047b777079e8db5f669ddaf1ffa1c7780d56b21e29f35e4772ac0f6280c" ] state = [ - "0x12132d945b134d2010f2e6482a0e5b895ce5c78e526754bb443002ce03b1ec5c", - "0x2ca971fe0179c466bcdbccaa9bb6332af8969a84e14be7d6950fb2f9b75aa144", - "0x23483232a044f97de696c766aab451c6128a8c0a9106b94e19570dc746451248", - "0x21fa5aa13e18141fa66be96d12361f696ab46301ad3a1850278526700567d702" + "0x10ba05722cbf8114df72299dc735360c577d1aa3d85f72d876102b0968e882c3", + "0x0e3514f9b6714e76e842b5a1a865de2ccbab5a421e8a45b8ce3e30b1d73123ac", + "0x19b221f0840f66d184dd2e539c7ca141d8102697616c32f57702745f85ced6c8", + "0x06dc4cfe7e4179e221b1cc2e2319b103452bdd12b27990aabb1abe3191e596e6" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000003" squeeze_mode = false @@ -602,19 +602,19 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 squeeze_mode = false [inputs.previous_rollups.public_inputs.end_msg_sponge] - num_absorbed = "0x0000000000000000000000000000000000000000000000000000000000000400" + num_absorbed = "0x0000000000000000000000000000000000000000000000000000000000000100" [inputs.previous_rollups.public_inputs.end_msg_sponge.sponge] cache = [ - "0x00000000000000000000000000000000000000000000000000000000000009db", - "0x00000000000000000000000000000000000000000000000000000000000009d9", - "0x00000000000000000000000000000000000000000000000000000000000009da" + "0x00000000000000000000000000000000000000000000000000000000000006db", + "0x00000000000000000000000000000000000000000000000000000000000006d9", + "0x00000000000000000000000000000000000000000000000000000000000006da" ] state = [ - "0x13a801138d16230fb1088f7fd847ded1c4972fb74bd6e7bc356881f054400aa6", - "0x182a8954baa5425098a60fdbd090ff8918a2ea34cd0f7f52b65422bf666ebfcf", - "0x11e3f79645edb7132868adb47ea5361ff65f7b612d23ebd31b2d2e16f8570918", - "0x2f22e68da640d535dbaa64cc7c81e2b36ada1e9bcb891b371f90220e74493ae0" + "0x0a0aa9eae5277bc3a8414be4a1a279d69f89d765ed17a0123ac83a2547a9cf99", + "0x22e19d5ba8ed0525429b7ac6b29abd221ed25181a9a2085507125e57ad7c3514", + "0x29f22a3e1d331fa7d7d47f6f0f139d765279266c338b405db43ecd66b5ef328c", + "0x1235e52a8b15fc46edb612d48c23715f76bba8eb563b9e29e2519f16612f0faa" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000001" squeeze_mode = false @@ -623,7 +623,7 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 leaf_index = "0x000000000000000000000000000000000000000000000000000000000000000e" sibling_path = [ "0x09b01173f91d75d916624a8f5af30b582c910e76997f09fe0eac95e50fd4a299", - "0x0c8de1eecfa40a64855d7f483a9efec89cab69da7d828685354b7759018eb4fd", + "0x0beaba6d817ffeb92671c729be0bbb0e0f952849aa3de039d2febbcc3982bd18", "0x1e16cf81772d4ffd1a84e0c458ae6fe2b66031a749e0a28cb82ec1fc5b5c5900", "0x20f1c701d84b280c9f80a517272153688a9e1b92166d1000cfe7c829b7c25f69", "0x25f9ac8d47a2dbcfb13b88ea474667fd2854bf5c901f2f698266864ffe7c21ad", @@ -636,46 +636,46 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x0000000000000000000000000000000000000000000000000000000000000014", "0x0000000000000000000000000000000000000000000000000000000000000059", "0x0000000000000000000000000000000000000000000000000000000000000005", - "0x00000000000000000000000000000000055335bed07ce39bb2852497353c2e43", - "0x000000000000000000000000000000000029ccc3e08d82b63e3f609ca9a11b21", - "0x000000000000000000000000000000b6fb93450f00b33fe9a1532ee939065bdd", - "0x00000000000000000000000000000000001ca3bea5acf7a213c69e9cedea6063", - "0x000000000000000000000000000000eed6bcfab8f52cd915236147191c33d9b0", - "0x000000000000000000000000000000000020010ed194eacbbce2f4cc9a4a1ea8", - "0x0000000000000000000000000000000c9fe2fc7c4c171230241ef90039872ede", - "0x00000000000000000000000000000000001b6bf93d470ea3cb747e6b8b36aebb", - "0x000000000000000000000000000000ec45ad3b5e6a2873a37dd839f1bbc0e22f", - "0x00000000000000000000000000000000002be9abe61c83327b46d987c1225e1e", - "0x000000000000000000000000000000ffa720c152631593ad6f33c591f5995295", - "0x00000000000000000000000000000000002d41ac0e21dc073238de7daed4dc99", - "0x000000000000000000000000000000b7fc524b1e5246871367220028b45bc277", - "0x00000000000000000000000000000000000f932bf2afaa1d3c4e7bb0e1c0828f", - "0x0000000000000000000000000000005e45b1d94eb6cc61bede9a23665a86287c", - "0x00000000000000000000000000000000000787a9166206d88529ddbbf1dbba6f", - "0x0000000000000000000000000000004ec736da57d0b6d1ed7b90d115f237c92d", - "0x000000000000000000000000000000000000ddaa6be0ceaf2254e8a6f38c4f2b", - "0x000000000000000000000000000000d041b41dee927c7f5b0d5401df413fe468", - "0x000000000000000000000000000000000027cb1182770226911e2ad5585582ec", - "0x000000000000000000000000000000691fe2534ea5b5524ef026f67f860c2bf7", - "0x00000000000000000000000000000000001763103d1a0b2b06e4cfa6c7386217", - "0x000000000000000000000000000000ba24793560e14dfca1fabb4feb09c423aa", - "0x00000000000000000000000000000000000858092262b6649c56c1f7b57c73c9", - "0x0000000000000000000000000000009b21bb7092213c986c17d80a93a2ccd460", - "0x000000000000000000000000000000000008bdb75681aa1130ce8ab0caab0e75", - "0x000000000000000000000000000000d697a01332830f4841b2894bdd18f1f12d", - "0x000000000000000000000000000000000020479ca77ea7517efbf1ec8236224c", - "0x000000000000000000000000000000bdfc44bc64d9e59ba36e6b74acb33c2a34", - "0x000000000000000000000000000000000009f91be000bc87e44e14085581e9f7", - "0x0000000000000000000000000000007bb37d4d77b1845cc82419821967a74cb3", - "0x00000000000000000000000000000000001af3534851f732afd2a52faf09e650", + "0x000000000000000000000000000000e6fa6cc2b4ce900a4cca530592eb33623e", + "0x00000000000000000000000000000000002ba5f421db47ec4860964149d30f8a", + "0x000000000000000000000000000000f954f71b9f2643cd378d72c60f5eeaa933", + "0x00000000000000000000000000000000002052585c2021ec6814b55df2314771", + "0x000000000000000000000000000000b7cca2121e5ab4fe39b7287d1a68571533", + "0x00000000000000000000000000000000001c655d150e21dd44fe7a7b3a47a639", + "0x000000000000000000000000000000e1bef232bc072bdbb53f5a96060294bb68", + "0x00000000000000000000000000000000002c4a204b8bd2c29b08dfd2332df7f9", + "0x0000000000000000000000000000009e4364a35f98d726b67986bf8ffa820e8e", + "0x0000000000000000000000000000000000021c78ace5103acf668ca6081f7ae4", + "0x000000000000000000000000000000f4539caee41e955fdb2e3d6bc5b04504ad", + "0x00000000000000000000000000000000002874dbb96ffb6a494691bd8a496d6f", + "0x0000000000000000000000000000006d80d6976c5f1f0cc41dffbc6767d99928", + "0x000000000000000000000000000000000024d81e29c6068295fe388b1c0b4dee", + "0x00000000000000000000000000000098459b08c7998799df9262459fbf614bab", + "0x00000000000000000000000000000000000c8d209746347b6e9f838f3f9faa8b", + "0x000000000000000000000000000000762800cbff560787a5451da271d033228c", + "0x00000000000000000000000000000000000f510f68ef4666bb0d13fbec65570e", + "0x000000000000000000000000000000140d935449cac890de10b41d7cb1b86bb2", + "0x000000000000000000000000000000000016b0aa857c348c2b85d08ab773699e", + "0x000000000000000000000000000000a1cb5ea33b073c0b3f5a6033495c972bdf", + "0x00000000000000000000000000000000001e291100e271652598ac96e475d52f", + "0x000000000000000000000000000000142253e2c36348104292d4ebcc24b915f9", + "0x00000000000000000000000000000000001b506cbf7b95ec31520e6e39b7b0bc", + "0x0000000000000000000000000000006fbe3f33a2ba9070bcb218d69ccfd047af", + "0x00000000000000000000000000000000001340c2ff0b6aa51d863ec9a632930c", + "0x000000000000000000000000000000647f41f1d6bdc8031bce4b0ce44920ab3d", + "0x00000000000000000000000000000000001854cbe04065342c35ac2fc904ea6a", + "0x000000000000000000000000000000e90c5598e0e7b46ef4d408543f53dd44df", + "0x00000000000000000000000000000000001e71ca00a26e68d0596304c04e6303", + "0x0000000000000000000000000000001db69e25f567374396c685e1fe1356142d", + "0x000000000000000000000000000000000020596d23c75bcfb6fde5a30e81fe53", "0x0000000000000000000000000000001eee81b23a887f299049b14c11e98460d6", "0x00000000000000000000000000000000002a56ce41f6b0be13b9c26747621b82", "0x000000000000000000000000000000d5827d6338c78656c0d12ca1aea6ef2c7c", "0x00000000000000000000000000000000001aa98f2de3ddda547d8f6de4e725de", - "0x000000000000000000000000000000b2377e9da1200c2558886606832f23d44a", - "0x00000000000000000000000000000000000e9f3ac5795f58502a89bd667f2a89", - "0x00000000000000000000000000000045fd2523f98dc58b742de998c1933a6fe6", - "0x000000000000000000000000000000000010e3df2ff71b2f689d35326c3127e9", + "0x000000000000000000000000000000c1fc6bab9776c90d2d9a274a3d3aa63986", + "0x0000000000000000000000000000000000071c5e2591ebba51a2e0b82b7d039a", + "0x000000000000000000000000000000d16df194d44e261e75467cf08d420e3877", + "0x00000000000000000000000000000000001d4b970e13000b149abae107561ff3", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", @@ -696,60 +696,60 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x000000000000000000000000000000e628b5eaa91be82614173f2257a6018d1a", - "0x0000000000000000000000000000000000203b4ead4f592b58d91fb310c748e3", - "0x000000000000000000000000000000178601dbb37eab4ec8ab23b6dc2b946051", - "0x000000000000000000000000000000000012d8b3171c16a2e6eba78e0baa877d", - "0x0000000000000000000000000000001cc1e904bd59e45bf3ec74fe790cd7c705", - "0x00000000000000000000000000000000000f3bd76e92a6cbbbd4c89a79217510", - "0x000000000000000000000000000000263356978193c38d4493d9a1987aaf4d71", - "0x00000000000000000000000000000000002254e425d9c316f98accf10da23563", - "0x0000000000000000000000000000000ba0ea7990ee3606678d39c5f351442b7a", - "0x000000000000000000000000000000000001d7c5e7076db7bb0d090c4fbb6fbc", - "0x0000000000000000000000000000000d34404703ca13b13d99e7d6b9dc8089e3", - "0x00000000000000000000000000000000001cdd65b0fffb9b88ff50d4d5003247", - "0x000000000000000000000000000000289cfc71d9cf1c696c41efafd897c82a3a", - "0x000000000000000000000000000000000010be80d1fbb634a959aabc2342adf5", - "0x000000000000000000000000000000c3633db95f156adcbc622aa9711d669d9e", - "0x000000000000000000000000000000000029e779becd0ca3c975ab04ebc01657", - "0x0000000000000000000000000000002baeb2e26bae966e331142778d98b1b2f8", - "0x000000000000000000000000000000000027633b9da357677c069a172b15e6a5", - "0x000000000000000000000000000000256a4b1212a7bcd54a30577ade9a8da327", - "0x00000000000000000000000000000000001171df6d52478245528b59d4e4440e", - "0x0000000000000000000000000000005f744815910d2f12f4d34b61cd85dcc027", - "0x00000000000000000000000000000000002fdbad335d646c942e641ce72bddec", - "0x000000000000000000000000000000adf30adcac70bc45bec04af7c4e5e474f2", - "0x0000000000000000000000000000000000079785403cbf80dbcea12d8a06a06a", - "0x000000000000000000000000000000408649668bf251852a5262c6fac4ee62ec", - "0x0000000000000000000000000000000000088ced21c90c247772ab30b7eefb7d", - "0x0000000000000000000000000000005cd1adcfbfe3c58479bc30a27711141bfb", - "0x0000000000000000000000000000000000060091d1a0a170cc2bee77460a362a", - "0x00000000000000000000000000000082e16497cffbe3bcf84da485af7da9ec9e", - "0x000000000000000000000000000000000013a60aba91a03bf1f20c50a65a0c88", - "0x000000000000000000000000000000f106a142f1b718c9fa5c314dcc7de0c51d", - "0x000000000000000000000000000000000011f5b1f786f12c37faebd7dcdbda1c", - "0x000000000000000000000000000000fc3b2328300cb91129b21d9c4db048a4a3", - "0x00000000000000000000000000000000001e9946865275a0d309e2b1322ed412", - "0x000000000000000000000000000000221cf0459a26c211a9c6213ddcc0c94242", - "0x00000000000000000000000000000000002913083583d90997f84efc4ea4392c", - "0x000000000000000000000000000000f9d69c226a1a305aa66086fef4521bf6b4", - "0x00000000000000000000000000000000000ebb1b848a2a8ac0339d4b75b68499", - "0x000000000000000000000000000000e8580bf114db2142474bc6c30dbed4021d", - "0x0000000000000000000000000000000000271d9f91c3f794f595488e3d0ad26d", - "0x000000000000000000000000000000518174aabb50077b68a5fdc0083edeedd3", - "0x000000000000000000000000000000000017ef8124abc878c573ab407e72c228", - "0x0000000000000000000000000000005eb34d8f6177d57e9ff3a862cfcf45716a", - "0x00000000000000000000000000000000001b050888d38f68885af4ba9bc1f580", - "0x00000000000000000000000000000043ebdf59e671a1a45b700fc06f9d0d9a8f", - "0x00000000000000000000000000000000002d4fa03da9c379db7453843e5ec0f4", - "0x00000000000000000000000000000081adf66e0638b7fe69497366c642770021", - "0x00000000000000000000000000000000000971ff41a9f70a97836def36037298", - "0x000000000000000000000000000000711e5d0db718b86d164064cf225a73e955", - "0x00000000000000000000000000000000000509072de771f04b34b1ef812c5131", - "0x0000000000000000000000000000009cc841d26329d4d25dbe90c777ebff2d27", - "0x00000000000000000000000000000000002763275f03558c4df29e509d1b6927" + "0x0000000000000000000000000000005528e34bbc9008f235eaa9b1fb3e492bb6", + "0x00000000000000000000000000000000001e96dfcdf5c833fe582d284cfb1127", + "0x00000000000000000000000000000020efbec39b10bf1c21b008567a7a17f193", + "0x00000000000000000000000000000000002dd00623040edd90a0f905df1ee8b1", + "0x000000000000000000000000000000d64164c5e4df1345be48974245e69fb0e8", + "0x0000000000000000000000000000000000202daa8b7646d9d48cf3092c1d40a4", + "0x000000000000000000000000000000083e603de933d2705824c999b7938273ce", + "0x000000000000000000000000000000000024f167e27f5353e1d72d86e8afc12a", + "0x0000000000000000000000000000002d05d4d2469c9e9adeea9f848ca7f77368", + "0x00000000000000000000000000000000002a208b36d2c05e17e021e82518f236", + "0x000000000000000000000000000000c594696fec6a6663c1c617eb7e9252cc04", + "0x0000000000000000000000000000000000279437df02d8c75d8c333ca6c55446", + "0x000000000000000000000000000000939fdb55c142d3e40cb4776cf103ff5bb8", + "0x0000000000000000000000000000000000124b5475a92494c6c4614571c6f84a", + "0x0000000000000000000000000000003374c665b6d450a1a292770583a04e53bc", + "0x0000000000000000000000000000000000243aa67f9a569907838154cd732034", + "0x0000000000000000000000000000001f894d69bbb0ef28f3a87b7a83bf283217", + "0x000000000000000000000000000000000020f6038f38643d71e786c4e9afb789", + "0x0000000000000000000000000000003a2409099e43152b484fb30468412ad1e6", + "0x00000000000000000000000000000000002decfb60557edde6b0896b12515a9d", + "0x0000000000000000000000000000005129b856813afb7862f5da8ea76596259b", + "0x000000000000000000000000000000000013b69dbb43385e826988b16abd9c2d", + "0x0000000000000000000000000000005f5d1483ae12bf604b1f98ccab7327b8c8", + "0x000000000000000000000000000000000011007e3f754dbe876ce960400dbc0e", + "0x000000000000000000000000000000bf8f5fd66fbd0389ce92b19d18d97660c9", + "0x0000000000000000000000000000000000111dac45f3f4c15283ac50638c549f", + "0x000000000000000000000000000000e932d917fd760479237c07685fd070be17", + "0x0000000000000000000000000000000000170a2458ec6958f23a1b1879ce5888", + "0x00000000000000000000000000000052c59a55f865d8de2f11671201203d4fcd", + "0x0000000000000000000000000000000000056938ee424410ee76882439f71d85", + "0x000000000000000000000000000000a18ab0a8506a1ae70534aa6c34b07587f4", + "0x0000000000000000000000000000000000164a49247f146467af05ad3cf2fa51", + "0x00000000000000000000000000000007e3ae149237fca52d55fe1b2955b74578", + "0x000000000000000000000000000000000003720a8ca3e549ea64019ffdec959c", + "0x000000000000000000000000000000cb7121e2fcf5de366d98198905237e2df8", + "0x00000000000000000000000000000000002dd6f395a9e42cf4c4b8d8f9bca46c", + "0x000000000000000000000000000000991d750e8b9cbef6fa84b16df9c7a4d4a3", + "0x00000000000000000000000000000000001c432157e139787ccea464e841706a", + "0x00000000000000000000000000000085d593e8d302515ac699b015b9c6f5e4aa", + "0x00000000000000000000000000000000001a22275587f67f1e9d32014f904fba", + "0x000000000000000000000000000000529a6e562d4ed309fc6541966ee4afc972", + "0x00000000000000000000000000000000001d87693a9f4663b02938060e510977", + "0x00000000000000000000000000000039fe4f2d5de006cd3813c2b7732ed7941e", + "0x00000000000000000000000000000000001375d7a4bcf3944a2245f53d6d63dc", + "0x000000000000000000000000000000b1513695eb651129bbfef3f4a7d8a97e1e", + "0x00000000000000000000000000000000000857becf651251725a3495fa64158f", + "0x000000000000000000000000000000ad7b5e3da01371c4d1d2c686714dd5b715", + "0x000000000000000000000000000000000013fa8c7518f259a681d8218dd176fa", + "0x0000000000000000000000000000001d791ffd19e3b0f7bfe2f5a96f080faa09", + "0x00000000000000000000000000000000002a8467a433dd8940023eb9c32cbf9c", + "0x000000000000000000000000000000289adc8d569087d1e36b38fac16b3fb00f", + "0x0000000000000000000000000000000000128a5ac3e8f32638360a6f0dd0aed9" ] - hash = "0x26f357b4b0e05ad1536d8d7e0708cd0c2d85542ff14dc630f0491f96c435d8ff" + hash = "0x0ac540177ae086f42456dd77c554a20d08529e5e7a66b74f048136d0e778da92" [[inputs.previous_rollups]] proof = [ @@ -1237,7 +1237,7 @@ proof = [ [inputs.previous_rollups.public_inputs] timestamp = "0x0000000000000000000000000000000000000000000000000000000000000186" - block_headers_hash = "0x0ec78db0bd1ee2e51ba79311ec805bddda64ee26f10ef86745b7f515f7a473dc" + block_headers_hash = "0x0bd67553a90b6d8dcbd607bee791721e1be4aec0076a699c959b9f6abb889c80" out_hash = "0x00abb50b8989a7f19fd4526d43e15a1ab5d2a43af413cc8ca91e82a3c8828625" accumulated_fees = "0x0000000000000000000000000000000000000000000000000000000000000000" accumulated_mana_used = "0x0000000000000000000000000000000000000000000000000000000000000000" @@ -1245,7 +1245,7 @@ proof = [ [inputs.previous_rollups.public_inputs.constants] chain_id = "0x0000000000000000000000000000000000000000000000000000000000000000" version = "0x0000000000000000000000000000000000000000000000000000000000000000" - vk_tree_root = "0x16b7eeebd9ec7b6b28187878be4fef52c87331b0ebf2e5719fd203a0474402b1" + vk_tree_root = "0x121ed84a634911bbb8a5e986c40e120a016a1713140a6a5ea4f8187a5e07e547" protocol_contracts_hash = "0x08d0026a4983c49638d0910859942c6648d5ea0a3a152c8a376e4983d0a003d9" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" slot_number = "0x000000000000000000000000000000000000000000000000000000000000000f" @@ -1261,16 +1261,16 @@ proof = [ fee_per_l2_gas = "0x0000000000000000000000000000000000000000000000000000000000000000" [inputs.previous_rollups.public_inputs.previous_archive] - root = "0x17ed0d01fc0f9551271cfba4270e351858fcba108e95d76959c62156d0d5f6d9" + root = "0x0a02b885838fb80d52ac8d0d5c2955fd2298804ed9cea5b239e7e4c0bf5c99cb" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000002" [inputs.previous_rollups.public_inputs.new_archive] - root = "0x14be32b2805c0b11d6fcbbecad4ed13d3be1d16b88d4cd068b20c0fc2dce5435" + root = "0x2d52f50f113807342bb8703e7e4bb4eb201f862dc894b0335a90239aedc6d9cf" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000003" [inputs.previous_rollups.public_inputs.start_state.l1_to_l2_message_tree] -root = "0x18d6aae3ab4a271abd590cb98267825b70de1e9e5c339356e94ea5fc7feba64b" -next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000400" +root = "0x0c123047b777079e8db5f669ddaf1ffa1c7780d56b21e29f35e4772ac0f6280c" +next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000100" [inputs.previous_rollups.public_inputs.start_state.partial.note_hash_tree] root = "0x01612d24a146efc2df9d815a2f733c17486304424577ffb4232fe4cb0c94e1e7" @@ -1285,8 +1285,8 @@ root = "0x1a90881964e28a92a419f1d8361c14ac147b6f9175c04fdf57dadf0d7ba781c9" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000080" [inputs.previous_rollups.public_inputs.end_state.l1_to_l2_message_tree] -root = "0x18d6aae3ab4a271abd590cb98267825b70de1e9e5c339356e94ea5fc7feba64b" -next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000400" +root = "0x0c123047b777079e8db5f669ddaf1ffa1c7780d56b21e29f35e4772ac0f6280c" +next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000100" [inputs.previous_rollups.public_inputs.end_state.partial.note_hash_tree] root = "0x2326bf220c6839c1856478f0c082f0c5883b2baed0bc222a1fa5e1244184c82b" @@ -1307,67 +1307,67 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 cache = [ "0x2d9141720c810b831246b0e50c0ad9d4b935418ba2ae8a06c395bb80340ee684", "0x1a90881964e28a92a419f1d8361c14ac147b6f9175c04fdf57dadf0d7ba781c9", - "0x18d6aae3ab4a271abd590cb98267825b70de1e9e5c339356e94ea5fc7feba64b" + "0x0c123047b777079e8db5f669ddaf1ffa1c7780d56b21e29f35e4772ac0f6280c" ] state = [ - "0x12132d945b134d2010f2e6482a0e5b895ce5c78e526754bb443002ce03b1ec5c", - "0x2ca971fe0179c466bcdbccaa9bb6332af8969a84e14be7d6950fb2f9b75aa144", - "0x23483232a044f97de696c766aab451c6128a8c0a9106b94e19570dc746451248", - "0x21fa5aa13e18141fa66be96d12361f696ab46301ad3a1850278526700567d702" + "0x10ba05722cbf8114df72299dc735360c577d1aa3d85f72d876102b0968e882c3", + "0x0e3514f9b6714e76e842b5a1a865de2ccbab5a421e8a45b8ce3e30b1d73123ac", + "0x19b221f0840f66d184dd2e539c7ca141d8102697616c32f57702745f85ced6c8", + "0x06dc4cfe7e4179e221b1cc2e2319b103452bdd12b27990aabb1abe3191e596e6" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000003" squeeze_mode = false [inputs.previous_rollups.public_inputs.end_sponge_blob] - num_absorbed_fields = "0x0000000000000000000000000000000000000000000000000000000000000a25" + num_absorbed_fields = "0x0000000000000000000000000000000000000000000000000000000000000a26" [inputs.previous_rollups.public_inputs.end_sponge_blob.sponge] cache = [ "0x1e1c597744057b88e39a9780ed087c39b1fc42864e05ef03a59ebd9e96b70b00", "0x2306af8b455a9cbf87331182183be8c0759fd5e1a4f606cea8a9e24efa461759", - "0x2326bf220c6839c1856478f0c082f0c5883b2baed0bc222a1fa5e1244184c82b" + "0x0c123047b777079e8db5f669ddaf1ffa1c7780d56b21e29f35e4772ac0f6280c" ] state = [ - "0x04d8a59ea98cbd4eb7f425b8f0bca26a6026565519068ea04e36bae686fd3824", - "0x019fb4519f358bdd14fb5000ad6e6132827a6aa547d4fc6020a02c00671ececc", - "0x1785dae4b5f56f54408796dca53112f34d8adc61cfca80987ce46f3a229993a4", - "0x1158bc703fe8088f5df444cc08d998ae4259b259a85fdae3e2195a82730b1c8e" + "0x18cc50c72512a32c7e9d03e3c75a6c231c64bb8b956e130edd92f0372cde5557", + "0x023a8d485733b7cf37de7afcb7b2ba2cecf58cc222cd6be8f423b5788aa711c4", + "0x0c8ae0e23734ad05a2db5cc7095e1762c2cab0f6cdcd5e8487d9b0c8f74b74f0", + "0x1bd89e098756374d4b55698f5fc2d7c41c3bdf340ce474b99b9b58a7e118affe" ] - cache_size = "0x0000000000000000000000000000000000000000000000000000000000000002" + cache_size = "0x0000000000000000000000000000000000000000000000000000000000000003" squeeze_mode = false [inputs.previous_rollups.public_inputs.start_msg_sponge] - num_absorbed = "0x0000000000000000000000000000000000000000000000000000000000000400" + num_absorbed = "0x0000000000000000000000000000000000000000000000000000000000000100" [inputs.previous_rollups.public_inputs.start_msg_sponge.sponge] cache = [ - "0x00000000000000000000000000000000000000000000000000000000000009db", - "0x00000000000000000000000000000000000000000000000000000000000009d9", - "0x00000000000000000000000000000000000000000000000000000000000009da" + "0x00000000000000000000000000000000000000000000000000000000000006db", + "0x00000000000000000000000000000000000000000000000000000000000006d9", + "0x00000000000000000000000000000000000000000000000000000000000006da" ] state = [ - "0x13a801138d16230fb1088f7fd847ded1c4972fb74bd6e7bc356881f054400aa6", - "0x182a8954baa5425098a60fdbd090ff8918a2ea34cd0f7f52b65422bf666ebfcf", - "0x11e3f79645edb7132868adb47ea5361ff65f7b612d23ebd31b2d2e16f8570918", - "0x2f22e68da640d535dbaa64cc7c81e2b36ada1e9bcb891b371f90220e74493ae0" + "0x0a0aa9eae5277bc3a8414be4a1a279d69f89d765ed17a0123ac83a2547a9cf99", + "0x22e19d5ba8ed0525429b7ac6b29abd221ed25181a9a2085507125e57ad7c3514", + "0x29f22a3e1d331fa7d7d47f6f0f139d765279266c338b405db43ecd66b5ef328c", + "0x1235e52a8b15fc46edb612d48c23715f76bba8eb563b9e29e2519f16612f0faa" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000001" squeeze_mode = false [inputs.previous_rollups.public_inputs.end_msg_sponge] - num_absorbed = "0x0000000000000000000000000000000000000000000000000000000000000400" + num_absorbed = "0x0000000000000000000000000000000000000000000000000000000000000100" [inputs.previous_rollups.public_inputs.end_msg_sponge.sponge] cache = [ - "0x00000000000000000000000000000000000000000000000000000000000009db", - "0x00000000000000000000000000000000000000000000000000000000000009d9", - "0x00000000000000000000000000000000000000000000000000000000000009da" + "0x00000000000000000000000000000000000000000000000000000000000006db", + "0x00000000000000000000000000000000000000000000000000000000000006d9", + "0x00000000000000000000000000000000000000000000000000000000000006da" ] state = [ - "0x13a801138d16230fb1088f7fd847ded1c4972fb74bd6e7bc356881f054400aa6", - "0x182a8954baa5425098a60fdbd090ff8918a2ea34cd0f7f52b65422bf666ebfcf", - "0x11e3f79645edb7132868adb47ea5361ff65f7b612d23ebd31b2d2e16f8570918", - "0x2f22e68da640d535dbaa64cc7c81e2b36ada1e9bcb891b371f90220e74493ae0" + "0x0a0aa9eae5277bc3a8414be4a1a279d69f89d765ed17a0123ac83a2547a9cf99", + "0x22e19d5ba8ed0525429b7ac6b29abd221ed25181a9a2085507125e57ad7c3514", + "0x29f22a3e1d331fa7d7d47f6f0f139d765279266c338b405db43ecd66b5ef328c", + "0x1235e52a8b15fc46edb612d48c23715f76bba8eb563b9e29e2519f16612f0faa" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000001" squeeze_mode = false @@ -1376,7 +1376,7 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 leaf_index = "0x000000000000000000000000000000000000000000000000000000000000000e" sibling_path = [ "0x09b01173f91d75d916624a8f5af30b582c910e76997f09fe0eac95e50fd4a299", - "0x0c8de1eecfa40a64855d7f483a9efec89cab69da7d828685354b7759018eb4fd", + "0x0beaba6d817ffeb92671c729be0bbb0e0f952849aa3de039d2febbcc3982bd18", "0x1e16cf81772d4ffd1a84e0c458ae6fe2b66031a749e0a28cb82ec1fc5b5c5900", "0x20f1c701d84b280c9f80a517272153688a9e1b92166d1000cfe7c829b7c25f69", "0x25f9ac8d47a2dbcfb13b88ea474667fd2854bf5c901f2f698266864ffe7c21ad", @@ -1389,46 +1389,46 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x0000000000000000000000000000000000000000000000000000000000000014", "0x0000000000000000000000000000000000000000000000000000000000000059", "0x0000000000000000000000000000000000000000000000000000000000000005", - "0x00000000000000000000000000000000055335bed07ce39bb2852497353c2e43", - "0x000000000000000000000000000000000029ccc3e08d82b63e3f609ca9a11b21", - "0x000000000000000000000000000000b6fb93450f00b33fe9a1532ee939065bdd", - "0x00000000000000000000000000000000001ca3bea5acf7a213c69e9cedea6063", - "0x000000000000000000000000000000eed6bcfab8f52cd915236147191c33d9b0", - "0x000000000000000000000000000000000020010ed194eacbbce2f4cc9a4a1ea8", - "0x0000000000000000000000000000000c9fe2fc7c4c171230241ef90039872ede", - "0x00000000000000000000000000000000001b6bf93d470ea3cb747e6b8b36aebb", - "0x000000000000000000000000000000ec45ad3b5e6a2873a37dd839f1bbc0e22f", - "0x00000000000000000000000000000000002be9abe61c83327b46d987c1225e1e", - "0x000000000000000000000000000000ffa720c152631593ad6f33c591f5995295", - "0x00000000000000000000000000000000002d41ac0e21dc073238de7daed4dc99", - "0x000000000000000000000000000000b7fc524b1e5246871367220028b45bc277", - "0x00000000000000000000000000000000000f932bf2afaa1d3c4e7bb0e1c0828f", - "0x0000000000000000000000000000005e45b1d94eb6cc61bede9a23665a86287c", - "0x00000000000000000000000000000000000787a9166206d88529ddbbf1dbba6f", - "0x0000000000000000000000000000004ec736da57d0b6d1ed7b90d115f237c92d", - "0x000000000000000000000000000000000000ddaa6be0ceaf2254e8a6f38c4f2b", - "0x000000000000000000000000000000d041b41dee927c7f5b0d5401df413fe468", - "0x000000000000000000000000000000000027cb1182770226911e2ad5585582ec", - "0x000000000000000000000000000000691fe2534ea5b5524ef026f67f860c2bf7", - "0x00000000000000000000000000000000001763103d1a0b2b06e4cfa6c7386217", - "0x000000000000000000000000000000ba24793560e14dfca1fabb4feb09c423aa", - "0x00000000000000000000000000000000000858092262b6649c56c1f7b57c73c9", - "0x0000000000000000000000000000009b21bb7092213c986c17d80a93a2ccd460", - "0x000000000000000000000000000000000008bdb75681aa1130ce8ab0caab0e75", - "0x000000000000000000000000000000d697a01332830f4841b2894bdd18f1f12d", - "0x000000000000000000000000000000000020479ca77ea7517efbf1ec8236224c", - "0x000000000000000000000000000000bdfc44bc64d9e59ba36e6b74acb33c2a34", - "0x000000000000000000000000000000000009f91be000bc87e44e14085581e9f7", - "0x0000000000000000000000000000007bb37d4d77b1845cc82419821967a74cb3", - "0x00000000000000000000000000000000001af3534851f732afd2a52faf09e650", + "0x000000000000000000000000000000e6fa6cc2b4ce900a4cca530592eb33623e", + "0x00000000000000000000000000000000002ba5f421db47ec4860964149d30f8a", + "0x000000000000000000000000000000f954f71b9f2643cd378d72c60f5eeaa933", + "0x00000000000000000000000000000000002052585c2021ec6814b55df2314771", + "0x000000000000000000000000000000b7cca2121e5ab4fe39b7287d1a68571533", + "0x00000000000000000000000000000000001c655d150e21dd44fe7a7b3a47a639", + "0x000000000000000000000000000000e1bef232bc072bdbb53f5a96060294bb68", + "0x00000000000000000000000000000000002c4a204b8bd2c29b08dfd2332df7f9", + "0x0000000000000000000000000000009e4364a35f98d726b67986bf8ffa820e8e", + "0x0000000000000000000000000000000000021c78ace5103acf668ca6081f7ae4", + "0x000000000000000000000000000000f4539caee41e955fdb2e3d6bc5b04504ad", + "0x00000000000000000000000000000000002874dbb96ffb6a494691bd8a496d6f", + "0x0000000000000000000000000000006d80d6976c5f1f0cc41dffbc6767d99928", + "0x000000000000000000000000000000000024d81e29c6068295fe388b1c0b4dee", + "0x00000000000000000000000000000098459b08c7998799df9262459fbf614bab", + "0x00000000000000000000000000000000000c8d209746347b6e9f838f3f9faa8b", + "0x000000000000000000000000000000762800cbff560787a5451da271d033228c", + "0x00000000000000000000000000000000000f510f68ef4666bb0d13fbec65570e", + "0x000000000000000000000000000000140d935449cac890de10b41d7cb1b86bb2", + "0x000000000000000000000000000000000016b0aa857c348c2b85d08ab773699e", + "0x000000000000000000000000000000a1cb5ea33b073c0b3f5a6033495c972bdf", + "0x00000000000000000000000000000000001e291100e271652598ac96e475d52f", + "0x000000000000000000000000000000142253e2c36348104292d4ebcc24b915f9", + "0x00000000000000000000000000000000001b506cbf7b95ec31520e6e39b7b0bc", + "0x0000000000000000000000000000006fbe3f33a2ba9070bcb218d69ccfd047af", + "0x00000000000000000000000000000000001340c2ff0b6aa51d863ec9a632930c", + "0x000000000000000000000000000000647f41f1d6bdc8031bce4b0ce44920ab3d", + "0x00000000000000000000000000000000001854cbe04065342c35ac2fc904ea6a", + "0x000000000000000000000000000000e90c5598e0e7b46ef4d408543f53dd44df", + "0x00000000000000000000000000000000001e71ca00a26e68d0596304c04e6303", + "0x0000000000000000000000000000001db69e25f567374396c685e1fe1356142d", + "0x000000000000000000000000000000000020596d23c75bcfb6fde5a30e81fe53", "0x0000000000000000000000000000001eee81b23a887f299049b14c11e98460d6", "0x00000000000000000000000000000000002a56ce41f6b0be13b9c26747621b82", "0x000000000000000000000000000000d5827d6338c78656c0d12ca1aea6ef2c7c", "0x00000000000000000000000000000000001aa98f2de3ddda547d8f6de4e725de", - "0x000000000000000000000000000000b2377e9da1200c2558886606832f23d44a", - "0x00000000000000000000000000000000000e9f3ac5795f58502a89bd667f2a89", - "0x00000000000000000000000000000045fd2523f98dc58b742de998c1933a6fe6", - "0x000000000000000000000000000000000010e3df2ff71b2f689d35326c3127e9", + "0x000000000000000000000000000000c1fc6bab9776c90d2d9a274a3d3aa63986", + "0x0000000000000000000000000000000000071c5e2591ebba51a2e0b82b7d039a", + "0x000000000000000000000000000000d16df194d44e261e75467cf08d420e3877", + "0x00000000000000000000000000000000001d4b970e13000b149abae107561ff3", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", @@ -1449,57 +1449,57 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x000000000000000000000000000000e628b5eaa91be82614173f2257a6018d1a", - "0x0000000000000000000000000000000000203b4ead4f592b58d91fb310c748e3", - "0x000000000000000000000000000000178601dbb37eab4ec8ab23b6dc2b946051", - "0x000000000000000000000000000000000012d8b3171c16a2e6eba78e0baa877d", - "0x0000000000000000000000000000001cc1e904bd59e45bf3ec74fe790cd7c705", - "0x00000000000000000000000000000000000f3bd76e92a6cbbbd4c89a79217510", - "0x000000000000000000000000000000263356978193c38d4493d9a1987aaf4d71", - "0x00000000000000000000000000000000002254e425d9c316f98accf10da23563", - "0x0000000000000000000000000000000ba0ea7990ee3606678d39c5f351442b7a", - "0x000000000000000000000000000000000001d7c5e7076db7bb0d090c4fbb6fbc", - "0x0000000000000000000000000000000d34404703ca13b13d99e7d6b9dc8089e3", - "0x00000000000000000000000000000000001cdd65b0fffb9b88ff50d4d5003247", - "0x000000000000000000000000000000289cfc71d9cf1c696c41efafd897c82a3a", - "0x000000000000000000000000000000000010be80d1fbb634a959aabc2342adf5", - "0x000000000000000000000000000000c3633db95f156adcbc622aa9711d669d9e", - "0x000000000000000000000000000000000029e779becd0ca3c975ab04ebc01657", - "0x0000000000000000000000000000002baeb2e26bae966e331142778d98b1b2f8", - "0x000000000000000000000000000000000027633b9da357677c069a172b15e6a5", - "0x000000000000000000000000000000256a4b1212a7bcd54a30577ade9a8da327", - "0x00000000000000000000000000000000001171df6d52478245528b59d4e4440e", - "0x0000000000000000000000000000005f744815910d2f12f4d34b61cd85dcc027", - "0x00000000000000000000000000000000002fdbad335d646c942e641ce72bddec", - "0x000000000000000000000000000000adf30adcac70bc45bec04af7c4e5e474f2", - "0x0000000000000000000000000000000000079785403cbf80dbcea12d8a06a06a", - "0x000000000000000000000000000000408649668bf251852a5262c6fac4ee62ec", - "0x0000000000000000000000000000000000088ced21c90c247772ab30b7eefb7d", - "0x0000000000000000000000000000005cd1adcfbfe3c58479bc30a27711141bfb", - "0x0000000000000000000000000000000000060091d1a0a170cc2bee77460a362a", - "0x00000000000000000000000000000082e16497cffbe3bcf84da485af7da9ec9e", - "0x000000000000000000000000000000000013a60aba91a03bf1f20c50a65a0c88", - "0x000000000000000000000000000000f106a142f1b718c9fa5c314dcc7de0c51d", - "0x000000000000000000000000000000000011f5b1f786f12c37faebd7dcdbda1c", - "0x000000000000000000000000000000fc3b2328300cb91129b21d9c4db048a4a3", - "0x00000000000000000000000000000000001e9946865275a0d309e2b1322ed412", - "0x000000000000000000000000000000221cf0459a26c211a9c6213ddcc0c94242", - "0x00000000000000000000000000000000002913083583d90997f84efc4ea4392c", - "0x000000000000000000000000000000f9d69c226a1a305aa66086fef4521bf6b4", - "0x00000000000000000000000000000000000ebb1b848a2a8ac0339d4b75b68499", - "0x000000000000000000000000000000e8580bf114db2142474bc6c30dbed4021d", - "0x0000000000000000000000000000000000271d9f91c3f794f595488e3d0ad26d", - "0x000000000000000000000000000000518174aabb50077b68a5fdc0083edeedd3", - "0x000000000000000000000000000000000017ef8124abc878c573ab407e72c228", - "0x0000000000000000000000000000005eb34d8f6177d57e9ff3a862cfcf45716a", - "0x00000000000000000000000000000000001b050888d38f68885af4ba9bc1f580", - "0x00000000000000000000000000000043ebdf59e671a1a45b700fc06f9d0d9a8f", - "0x00000000000000000000000000000000002d4fa03da9c379db7453843e5ec0f4", - "0x00000000000000000000000000000081adf66e0638b7fe69497366c642770021", - "0x00000000000000000000000000000000000971ff41a9f70a97836def36037298", - "0x000000000000000000000000000000711e5d0db718b86d164064cf225a73e955", - "0x00000000000000000000000000000000000509072de771f04b34b1ef812c5131", - "0x0000000000000000000000000000009cc841d26329d4d25dbe90c777ebff2d27", - "0x00000000000000000000000000000000002763275f03558c4df29e509d1b6927" + "0x0000000000000000000000000000005528e34bbc9008f235eaa9b1fb3e492bb6", + "0x00000000000000000000000000000000001e96dfcdf5c833fe582d284cfb1127", + "0x00000000000000000000000000000020efbec39b10bf1c21b008567a7a17f193", + "0x00000000000000000000000000000000002dd00623040edd90a0f905df1ee8b1", + "0x000000000000000000000000000000d64164c5e4df1345be48974245e69fb0e8", + "0x0000000000000000000000000000000000202daa8b7646d9d48cf3092c1d40a4", + "0x000000000000000000000000000000083e603de933d2705824c999b7938273ce", + "0x000000000000000000000000000000000024f167e27f5353e1d72d86e8afc12a", + "0x0000000000000000000000000000002d05d4d2469c9e9adeea9f848ca7f77368", + "0x00000000000000000000000000000000002a208b36d2c05e17e021e82518f236", + "0x000000000000000000000000000000c594696fec6a6663c1c617eb7e9252cc04", + "0x0000000000000000000000000000000000279437df02d8c75d8c333ca6c55446", + "0x000000000000000000000000000000939fdb55c142d3e40cb4776cf103ff5bb8", + "0x0000000000000000000000000000000000124b5475a92494c6c4614571c6f84a", + "0x0000000000000000000000000000003374c665b6d450a1a292770583a04e53bc", + "0x0000000000000000000000000000000000243aa67f9a569907838154cd732034", + "0x0000000000000000000000000000001f894d69bbb0ef28f3a87b7a83bf283217", + "0x000000000000000000000000000000000020f6038f38643d71e786c4e9afb789", + "0x0000000000000000000000000000003a2409099e43152b484fb30468412ad1e6", + "0x00000000000000000000000000000000002decfb60557edde6b0896b12515a9d", + "0x0000000000000000000000000000005129b856813afb7862f5da8ea76596259b", + "0x000000000000000000000000000000000013b69dbb43385e826988b16abd9c2d", + "0x0000000000000000000000000000005f5d1483ae12bf604b1f98ccab7327b8c8", + "0x000000000000000000000000000000000011007e3f754dbe876ce960400dbc0e", + "0x000000000000000000000000000000bf8f5fd66fbd0389ce92b19d18d97660c9", + "0x0000000000000000000000000000000000111dac45f3f4c15283ac50638c549f", + "0x000000000000000000000000000000e932d917fd760479237c07685fd070be17", + "0x0000000000000000000000000000000000170a2458ec6958f23a1b1879ce5888", + "0x00000000000000000000000000000052c59a55f865d8de2f11671201203d4fcd", + "0x0000000000000000000000000000000000056938ee424410ee76882439f71d85", + "0x000000000000000000000000000000a18ab0a8506a1ae70534aa6c34b07587f4", + "0x0000000000000000000000000000000000164a49247f146467af05ad3cf2fa51", + "0x00000000000000000000000000000007e3ae149237fca52d55fe1b2955b74578", + "0x000000000000000000000000000000000003720a8ca3e549ea64019ffdec959c", + "0x000000000000000000000000000000cb7121e2fcf5de366d98198905237e2df8", + "0x00000000000000000000000000000000002dd6f395a9e42cf4c4b8d8f9bca46c", + "0x000000000000000000000000000000991d750e8b9cbef6fa84b16df9c7a4d4a3", + "0x00000000000000000000000000000000001c432157e139787ccea464e841706a", + "0x00000000000000000000000000000085d593e8d302515ac699b015b9c6f5e4aa", + "0x00000000000000000000000000000000001a22275587f67f1e9d32014f904fba", + "0x000000000000000000000000000000529a6e562d4ed309fc6541966ee4afc972", + "0x00000000000000000000000000000000001d87693a9f4663b02938060e510977", + "0x00000000000000000000000000000039fe4f2d5de006cd3813c2b7732ed7941e", + "0x00000000000000000000000000000000001375d7a4bcf3944a2245f53d6d63dc", + "0x000000000000000000000000000000b1513695eb651129bbfef3f4a7d8a97e1e", + "0x00000000000000000000000000000000000857becf651251725a3495fa64158f", + "0x000000000000000000000000000000ad7b5e3da01371c4d1d2c686714dd5b715", + "0x000000000000000000000000000000000013fa8c7518f259a681d8218dd176fa", + "0x0000000000000000000000000000001d791ffd19e3b0f7bfe2f5a96f080faa09", + "0x00000000000000000000000000000000002a8467a433dd8940023eb9c32cbf9c", + "0x000000000000000000000000000000289adc8d569087d1e36b38fac16b3fb00f", + "0x0000000000000000000000000000000000128a5ac3e8f32638360a6f0dd0aed9" ] - hash = "0x26f357b4b0e05ad1536d8d7e0708cd0c2d85542ff14dc630f0491f96c435d8ff" + hash = "0x0ac540177ae086f42456dd77c554a20d08529e5e7a66b74f048136d0e778da92" diff --git a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-block-root-no-txs/Prover.toml b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-block-root-no-txs/Prover.toml index 2530084b39d6..00e88daab5d6 100644 --- a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-block-root-no-txs/Prover.toml +++ b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-block-root-no-txs/Prover.toml @@ -2,15 +2,15 @@ timestamp = "0x0000000000000000000000000000000000000000000000000000000000000186" l1_to_l2_message_frontier_hint = [ "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x19f1a0c09db4cd026f686e9c8fb45501a9fefb4eb1b4c6c328a51343a0094eeb", + "0x14e4b977b2203b70e6ee1c2456eb7114d090fe4b907f631eecd0919fed432e7d", + "0x30105bad22ddcc508b739b7c9ad87a561c569ff5cb0098a853c1c4ac21b7a037", + "0x1e20ad4181460cbfdc74ca773502c59b890f184efe300ebad895956d318422da", + "0x1434e6e2d5db1053ab8a3be58704509c799ee17e109c77f441f7bf1755400249", + "0x119f56a2e8423a7feaab49b9b5dcbadec0648dfa4096b61b6774ea33ae29dc7f", + "0x221cf368938c74e4fced9dfb2a8e37cd8a6c57d21385c249f0b5c2412341287f", + "0x2c5214dfc4d70d2619fce2a7e02ddcf380576dca42b66c9215c7d8d1ec154116", + "0x13abc9bba431e6930c169f5daeb60aedbb27d7618c7ff88b3b4ec1c6de1d6bb8", "0x0d04c63f36bd168215c9b09a227c7e8d3ad48e2f11b8202fd07c524bd30ee88f", "0x042c72d0ca208f0631ed947050258333518c26059f0a2ef041e933b1b2a6d8ad", "0x00c21235cdc5d4241fab782680421cdd99c088a3b48a740d8289d0e67b2ee5da", @@ -94,7 +94,7 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 [inputs.constants] chain_id = "0x0000000000000000000000000000000000000000000000000000000000000000" version = "0x0000000000000000000000000000000000000000000000000000000000000000" - vk_tree_root = "0x16b7eeebd9ec7b6b28187878be4fef52c87331b0ebf2e5719fd203a0474402b1" + vk_tree_root = "0x121ed84a634911bbb8a5e986c40e120a016a1713140a6a5ea4f8187a5e07e547" protocol_contracts_hash = "0x08d0026a4983c49638d0910859942c6648d5ea0a3a152c8a376e4983d0a003d9" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" slot_number = "0x000000000000000000000000000000000000000000000000000000000000000f" @@ -402,775 +402,6 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x00000000000000000000000000000000000000000000000000000000000006d8", "0x00000000000000000000000000000000000000000000000000000000000006d9", "0x00000000000000000000000000000000000000000000000000000000000006da", - "0x00000000000000000000000000000000000000000000000000000000000006db", - "0x00000000000000000000000000000000000000000000000000000000000006dc", - "0x00000000000000000000000000000000000000000000000000000000000006dd", - "0x00000000000000000000000000000000000000000000000000000000000006de", - "0x00000000000000000000000000000000000000000000000000000000000006df", - "0x00000000000000000000000000000000000000000000000000000000000006e0", - "0x00000000000000000000000000000000000000000000000000000000000006e1", - "0x00000000000000000000000000000000000000000000000000000000000006e2", - "0x00000000000000000000000000000000000000000000000000000000000006e3", - "0x00000000000000000000000000000000000000000000000000000000000006e4", - "0x00000000000000000000000000000000000000000000000000000000000006e5", - "0x00000000000000000000000000000000000000000000000000000000000006e6", - "0x00000000000000000000000000000000000000000000000000000000000006e7", - "0x00000000000000000000000000000000000000000000000000000000000006e8", - "0x00000000000000000000000000000000000000000000000000000000000006e9", - "0x00000000000000000000000000000000000000000000000000000000000006ea", - "0x00000000000000000000000000000000000000000000000000000000000006eb", - "0x00000000000000000000000000000000000000000000000000000000000006ec", - "0x00000000000000000000000000000000000000000000000000000000000006ed", - "0x00000000000000000000000000000000000000000000000000000000000006ee", - "0x00000000000000000000000000000000000000000000000000000000000006ef", - "0x00000000000000000000000000000000000000000000000000000000000006f0", - "0x00000000000000000000000000000000000000000000000000000000000006f1", - "0x00000000000000000000000000000000000000000000000000000000000006f2", - "0x00000000000000000000000000000000000000000000000000000000000006f3", - "0x00000000000000000000000000000000000000000000000000000000000006f4", - "0x00000000000000000000000000000000000000000000000000000000000006f5", - "0x00000000000000000000000000000000000000000000000000000000000006f6", - "0x00000000000000000000000000000000000000000000000000000000000006f7", - "0x00000000000000000000000000000000000000000000000000000000000006f8", - "0x00000000000000000000000000000000000000000000000000000000000006f9", - "0x00000000000000000000000000000000000000000000000000000000000006fa", - "0x00000000000000000000000000000000000000000000000000000000000006fb", - "0x00000000000000000000000000000000000000000000000000000000000006fc", - "0x00000000000000000000000000000000000000000000000000000000000006fd", - "0x00000000000000000000000000000000000000000000000000000000000006fe", - "0x00000000000000000000000000000000000000000000000000000000000006ff", - "0x0000000000000000000000000000000000000000000000000000000000000700", - "0x0000000000000000000000000000000000000000000000000000000000000701", - "0x0000000000000000000000000000000000000000000000000000000000000702", - "0x0000000000000000000000000000000000000000000000000000000000000703", - "0x0000000000000000000000000000000000000000000000000000000000000704", - "0x0000000000000000000000000000000000000000000000000000000000000705", - "0x0000000000000000000000000000000000000000000000000000000000000706", - "0x0000000000000000000000000000000000000000000000000000000000000707", - "0x0000000000000000000000000000000000000000000000000000000000000708", - "0x0000000000000000000000000000000000000000000000000000000000000709", - "0x000000000000000000000000000000000000000000000000000000000000070a", - "0x000000000000000000000000000000000000000000000000000000000000070b", - "0x000000000000000000000000000000000000000000000000000000000000070c", - "0x000000000000000000000000000000000000000000000000000000000000070d", - "0x000000000000000000000000000000000000000000000000000000000000070e", - "0x000000000000000000000000000000000000000000000000000000000000070f", - "0x0000000000000000000000000000000000000000000000000000000000000710", - "0x0000000000000000000000000000000000000000000000000000000000000711", - "0x0000000000000000000000000000000000000000000000000000000000000712", - "0x0000000000000000000000000000000000000000000000000000000000000713", - "0x0000000000000000000000000000000000000000000000000000000000000714", - "0x0000000000000000000000000000000000000000000000000000000000000715", - "0x0000000000000000000000000000000000000000000000000000000000000716", - "0x0000000000000000000000000000000000000000000000000000000000000717", - "0x0000000000000000000000000000000000000000000000000000000000000718", - "0x0000000000000000000000000000000000000000000000000000000000000719", - "0x000000000000000000000000000000000000000000000000000000000000071a", - "0x000000000000000000000000000000000000000000000000000000000000071b", - "0x000000000000000000000000000000000000000000000000000000000000071c", - "0x000000000000000000000000000000000000000000000000000000000000071d", - "0x000000000000000000000000000000000000000000000000000000000000071e", - "0x000000000000000000000000000000000000000000000000000000000000071f", - "0x0000000000000000000000000000000000000000000000000000000000000720", - "0x0000000000000000000000000000000000000000000000000000000000000721", - "0x0000000000000000000000000000000000000000000000000000000000000722", - "0x0000000000000000000000000000000000000000000000000000000000000723", - "0x0000000000000000000000000000000000000000000000000000000000000724", - "0x0000000000000000000000000000000000000000000000000000000000000725", - "0x0000000000000000000000000000000000000000000000000000000000000726", - "0x0000000000000000000000000000000000000000000000000000000000000727", - "0x0000000000000000000000000000000000000000000000000000000000000728", - "0x0000000000000000000000000000000000000000000000000000000000000729", - "0x000000000000000000000000000000000000000000000000000000000000072a", - "0x000000000000000000000000000000000000000000000000000000000000072b", - "0x000000000000000000000000000000000000000000000000000000000000072c", - "0x000000000000000000000000000000000000000000000000000000000000072d", - "0x000000000000000000000000000000000000000000000000000000000000072e", - "0x000000000000000000000000000000000000000000000000000000000000072f", - "0x0000000000000000000000000000000000000000000000000000000000000730", - "0x0000000000000000000000000000000000000000000000000000000000000731", - "0x0000000000000000000000000000000000000000000000000000000000000732", - "0x0000000000000000000000000000000000000000000000000000000000000733", - "0x0000000000000000000000000000000000000000000000000000000000000734", - "0x0000000000000000000000000000000000000000000000000000000000000735", - "0x0000000000000000000000000000000000000000000000000000000000000736", - "0x0000000000000000000000000000000000000000000000000000000000000737", - "0x0000000000000000000000000000000000000000000000000000000000000738", - "0x0000000000000000000000000000000000000000000000000000000000000739", - "0x000000000000000000000000000000000000000000000000000000000000073a", - "0x000000000000000000000000000000000000000000000000000000000000073b", - "0x000000000000000000000000000000000000000000000000000000000000073c", - "0x000000000000000000000000000000000000000000000000000000000000073d", - "0x000000000000000000000000000000000000000000000000000000000000073e", - "0x000000000000000000000000000000000000000000000000000000000000073f", - "0x0000000000000000000000000000000000000000000000000000000000000740", - "0x0000000000000000000000000000000000000000000000000000000000000741", - "0x0000000000000000000000000000000000000000000000000000000000000742", - "0x0000000000000000000000000000000000000000000000000000000000000743", - "0x0000000000000000000000000000000000000000000000000000000000000744", - "0x0000000000000000000000000000000000000000000000000000000000000745", - "0x0000000000000000000000000000000000000000000000000000000000000746", - "0x0000000000000000000000000000000000000000000000000000000000000747", - "0x0000000000000000000000000000000000000000000000000000000000000748", - "0x0000000000000000000000000000000000000000000000000000000000000749", - "0x000000000000000000000000000000000000000000000000000000000000074a", - "0x000000000000000000000000000000000000000000000000000000000000074b", - "0x000000000000000000000000000000000000000000000000000000000000074c", - "0x000000000000000000000000000000000000000000000000000000000000074d", - "0x000000000000000000000000000000000000000000000000000000000000074e", - "0x000000000000000000000000000000000000000000000000000000000000074f", - "0x0000000000000000000000000000000000000000000000000000000000000750", - "0x0000000000000000000000000000000000000000000000000000000000000751", - "0x0000000000000000000000000000000000000000000000000000000000000752", - "0x0000000000000000000000000000000000000000000000000000000000000753", - "0x0000000000000000000000000000000000000000000000000000000000000754", - "0x0000000000000000000000000000000000000000000000000000000000000755", - "0x0000000000000000000000000000000000000000000000000000000000000756", - "0x0000000000000000000000000000000000000000000000000000000000000757", - "0x0000000000000000000000000000000000000000000000000000000000000758", - "0x0000000000000000000000000000000000000000000000000000000000000759", - "0x000000000000000000000000000000000000000000000000000000000000075a", - "0x000000000000000000000000000000000000000000000000000000000000075b", - "0x000000000000000000000000000000000000000000000000000000000000075c", - "0x000000000000000000000000000000000000000000000000000000000000075d", - "0x000000000000000000000000000000000000000000000000000000000000075e", - "0x000000000000000000000000000000000000000000000000000000000000075f", - "0x0000000000000000000000000000000000000000000000000000000000000760", - "0x0000000000000000000000000000000000000000000000000000000000000761", - "0x0000000000000000000000000000000000000000000000000000000000000762", - "0x0000000000000000000000000000000000000000000000000000000000000763", - "0x0000000000000000000000000000000000000000000000000000000000000764", - "0x0000000000000000000000000000000000000000000000000000000000000765", - "0x0000000000000000000000000000000000000000000000000000000000000766", - "0x0000000000000000000000000000000000000000000000000000000000000767", - "0x0000000000000000000000000000000000000000000000000000000000000768", - "0x0000000000000000000000000000000000000000000000000000000000000769", - "0x000000000000000000000000000000000000000000000000000000000000076a", - "0x000000000000000000000000000000000000000000000000000000000000076b", - "0x000000000000000000000000000000000000000000000000000000000000076c", - "0x000000000000000000000000000000000000000000000000000000000000076d", - "0x000000000000000000000000000000000000000000000000000000000000076e", - "0x000000000000000000000000000000000000000000000000000000000000076f", - "0x0000000000000000000000000000000000000000000000000000000000000770", - "0x0000000000000000000000000000000000000000000000000000000000000771", - "0x0000000000000000000000000000000000000000000000000000000000000772", - "0x0000000000000000000000000000000000000000000000000000000000000773", - "0x0000000000000000000000000000000000000000000000000000000000000774", - "0x0000000000000000000000000000000000000000000000000000000000000775", - "0x0000000000000000000000000000000000000000000000000000000000000776", - "0x0000000000000000000000000000000000000000000000000000000000000777", - "0x0000000000000000000000000000000000000000000000000000000000000778", - "0x0000000000000000000000000000000000000000000000000000000000000779", - "0x000000000000000000000000000000000000000000000000000000000000077a", - "0x000000000000000000000000000000000000000000000000000000000000077b", - "0x000000000000000000000000000000000000000000000000000000000000077c", - "0x000000000000000000000000000000000000000000000000000000000000077d", - "0x000000000000000000000000000000000000000000000000000000000000077e", - "0x000000000000000000000000000000000000000000000000000000000000077f", - "0x0000000000000000000000000000000000000000000000000000000000000780", - "0x0000000000000000000000000000000000000000000000000000000000000781", - "0x0000000000000000000000000000000000000000000000000000000000000782", - "0x0000000000000000000000000000000000000000000000000000000000000783", - "0x0000000000000000000000000000000000000000000000000000000000000784", - "0x0000000000000000000000000000000000000000000000000000000000000785", - "0x0000000000000000000000000000000000000000000000000000000000000786", - "0x0000000000000000000000000000000000000000000000000000000000000787", - "0x0000000000000000000000000000000000000000000000000000000000000788", - "0x0000000000000000000000000000000000000000000000000000000000000789", - "0x000000000000000000000000000000000000000000000000000000000000078a", - "0x000000000000000000000000000000000000000000000000000000000000078b", - "0x000000000000000000000000000000000000000000000000000000000000078c", - "0x000000000000000000000000000000000000000000000000000000000000078d", - "0x000000000000000000000000000000000000000000000000000000000000078e", - "0x000000000000000000000000000000000000000000000000000000000000078f", - "0x0000000000000000000000000000000000000000000000000000000000000790", - "0x0000000000000000000000000000000000000000000000000000000000000791", - "0x0000000000000000000000000000000000000000000000000000000000000792", - "0x0000000000000000000000000000000000000000000000000000000000000793", - "0x0000000000000000000000000000000000000000000000000000000000000794", - "0x0000000000000000000000000000000000000000000000000000000000000795", - "0x0000000000000000000000000000000000000000000000000000000000000796", - "0x0000000000000000000000000000000000000000000000000000000000000797", - "0x0000000000000000000000000000000000000000000000000000000000000798", - "0x0000000000000000000000000000000000000000000000000000000000000799", - "0x000000000000000000000000000000000000000000000000000000000000079a", - "0x000000000000000000000000000000000000000000000000000000000000079b", - "0x000000000000000000000000000000000000000000000000000000000000079c", - "0x000000000000000000000000000000000000000000000000000000000000079d", - "0x000000000000000000000000000000000000000000000000000000000000079e", - "0x000000000000000000000000000000000000000000000000000000000000079f", - "0x00000000000000000000000000000000000000000000000000000000000007a0", - "0x00000000000000000000000000000000000000000000000000000000000007a1", - "0x00000000000000000000000000000000000000000000000000000000000007a2", - "0x00000000000000000000000000000000000000000000000000000000000007a3", - "0x00000000000000000000000000000000000000000000000000000000000007a4", - "0x00000000000000000000000000000000000000000000000000000000000007a5", - "0x00000000000000000000000000000000000000000000000000000000000007a6", - "0x00000000000000000000000000000000000000000000000000000000000007a7", - "0x00000000000000000000000000000000000000000000000000000000000007a8", - "0x00000000000000000000000000000000000000000000000000000000000007a9", - "0x00000000000000000000000000000000000000000000000000000000000007aa", - "0x00000000000000000000000000000000000000000000000000000000000007ab", - "0x00000000000000000000000000000000000000000000000000000000000007ac", - "0x00000000000000000000000000000000000000000000000000000000000007ad", - "0x00000000000000000000000000000000000000000000000000000000000007ae", - "0x00000000000000000000000000000000000000000000000000000000000007af", - "0x00000000000000000000000000000000000000000000000000000000000007b0", - "0x00000000000000000000000000000000000000000000000000000000000007b1", - "0x00000000000000000000000000000000000000000000000000000000000007b2", - "0x00000000000000000000000000000000000000000000000000000000000007b3", - "0x00000000000000000000000000000000000000000000000000000000000007b4", - "0x00000000000000000000000000000000000000000000000000000000000007b5", - "0x00000000000000000000000000000000000000000000000000000000000007b6", - "0x00000000000000000000000000000000000000000000000000000000000007b7", - "0x00000000000000000000000000000000000000000000000000000000000007b8", - "0x00000000000000000000000000000000000000000000000000000000000007b9", - "0x00000000000000000000000000000000000000000000000000000000000007ba", - "0x00000000000000000000000000000000000000000000000000000000000007bb", - "0x00000000000000000000000000000000000000000000000000000000000007bc", - "0x00000000000000000000000000000000000000000000000000000000000007bd", - "0x00000000000000000000000000000000000000000000000000000000000007be", - "0x00000000000000000000000000000000000000000000000000000000000007bf", - "0x00000000000000000000000000000000000000000000000000000000000007c0", - "0x00000000000000000000000000000000000000000000000000000000000007c1", - "0x00000000000000000000000000000000000000000000000000000000000007c2", - "0x00000000000000000000000000000000000000000000000000000000000007c3", - "0x00000000000000000000000000000000000000000000000000000000000007c4", - "0x00000000000000000000000000000000000000000000000000000000000007c5", - "0x00000000000000000000000000000000000000000000000000000000000007c6", - "0x00000000000000000000000000000000000000000000000000000000000007c7", - "0x00000000000000000000000000000000000000000000000000000000000007c8", - "0x00000000000000000000000000000000000000000000000000000000000007c9", - "0x00000000000000000000000000000000000000000000000000000000000007ca", - "0x00000000000000000000000000000000000000000000000000000000000007cb", - "0x00000000000000000000000000000000000000000000000000000000000007cc", - "0x00000000000000000000000000000000000000000000000000000000000007cd", - "0x00000000000000000000000000000000000000000000000000000000000007ce", - "0x00000000000000000000000000000000000000000000000000000000000007cf", - "0x00000000000000000000000000000000000000000000000000000000000007d0", - "0x00000000000000000000000000000000000000000000000000000000000007d1", - "0x00000000000000000000000000000000000000000000000000000000000007d2", - "0x00000000000000000000000000000000000000000000000000000000000007d3", - "0x00000000000000000000000000000000000000000000000000000000000007d4", - "0x00000000000000000000000000000000000000000000000000000000000007d5", - "0x00000000000000000000000000000000000000000000000000000000000007d6", - "0x00000000000000000000000000000000000000000000000000000000000007d7", - "0x00000000000000000000000000000000000000000000000000000000000007d8", - "0x00000000000000000000000000000000000000000000000000000000000007d9", - "0x00000000000000000000000000000000000000000000000000000000000007da", - "0x00000000000000000000000000000000000000000000000000000000000007db", - "0x00000000000000000000000000000000000000000000000000000000000007dc", - "0x00000000000000000000000000000000000000000000000000000000000007dd", - "0x00000000000000000000000000000000000000000000000000000000000007de", - "0x00000000000000000000000000000000000000000000000000000000000007df", - "0x00000000000000000000000000000000000000000000000000000000000007e0", - "0x00000000000000000000000000000000000000000000000000000000000007e1", - "0x00000000000000000000000000000000000000000000000000000000000007e2", - "0x00000000000000000000000000000000000000000000000000000000000007e3", - "0x00000000000000000000000000000000000000000000000000000000000007e4", - "0x00000000000000000000000000000000000000000000000000000000000007e5", - "0x00000000000000000000000000000000000000000000000000000000000007e6", - "0x00000000000000000000000000000000000000000000000000000000000007e7", - "0x00000000000000000000000000000000000000000000000000000000000007e8", - "0x00000000000000000000000000000000000000000000000000000000000007e9", - "0x00000000000000000000000000000000000000000000000000000000000007ea", - "0x00000000000000000000000000000000000000000000000000000000000007eb", - "0x00000000000000000000000000000000000000000000000000000000000007ec", - "0x00000000000000000000000000000000000000000000000000000000000007ed", - "0x00000000000000000000000000000000000000000000000000000000000007ee", - "0x00000000000000000000000000000000000000000000000000000000000007ef", - "0x00000000000000000000000000000000000000000000000000000000000007f0", - "0x00000000000000000000000000000000000000000000000000000000000007f1", - "0x00000000000000000000000000000000000000000000000000000000000007f2", - "0x00000000000000000000000000000000000000000000000000000000000007f3", - "0x00000000000000000000000000000000000000000000000000000000000007f4", - "0x00000000000000000000000000000000000000000000000000000000000007f5", - "0x00000000000000000000000000000000000000000000000000000000000007f6", - "0x00000000000000000000000000000000000000000000000000000000000007f7", - "0x00000000000000000000000000000000000000000000000000000000000007f8", - "0x00000000000000000000000000000000000000000000000000000000000007f9", - "0x00000000000000000000000000000000000000000000000000000000000007fa", - "0x00000000000000000000000000000000000000000000000000000000000007fb", - "0x00000000000000000000000000000000000000000000000000000000000007fc", - "0x00000000000000000000000000000000000000000000000000000000000007fd", - "0x00000000000000000000000000000000000000000000000000000000000007fe", - "0x00000000000000000000000000000000000000000000000000000000000007ff", - "0x0000000000000000000000000000000000000000000000000000000000000800", - "0x0000000000000000000000000000000000000000000000000000000000000801", - "0x0000000000000000000000000000000000000000000000000000000000000802", - "0x0000000000000000000000000000000000000000000000000000000000000803", - "0x0000000000000000000000000000000000000000000000000000000000000804", - "0x0000000000000000000000000000000000000000000000000000000000000805", - "0x0000000000000000000000000000000000000000000000000000000000000806", - "0x0000000000000000000000000000000000000000000000000000000000000807", - "0x0000000000000000000000000000000000000000000000000000000000000808", - "0x0000000000000000000000000000000000000000000000000000000000000809", - "0x000000000000000000000000000000000000000000000000000000000000080a", - "0x000000000000000000000000000000000000000000000000000000000000080b", - "0x000000000000000000000000000000000000000000000000000000000000080c", - "0x000000000000000000000000000000000000000000000000000000000000080d", - "0x000000000000000000000000000000000000000000000000000000000000080e", - "0x000000000000000000000000000000000000000000000000000000000000080f", - "0x0000000000000000000000000000000000000000000000000000000000000810", - "0x0000000000000000000000000000000000000000000000000000000000000811", - "0x0000000000000000000000000000000000000000000000000000000000000812", - "0x0000000000000000000000000000000000000000000000000000000000000813", - "0x0000000000000000000000000000000000000000000000000000000000000814", - "0x0000000000000000000000000000000000000000000000000000000000000815", - "0x0000000000000000000000000000000000000000000000000000000000000816", - "0x0000000000000000000000000000000000000000000000000000000000000817", - "0x0000000000000000000000000000000000000000000000000000000000000818", - "0x0000000000000000000000000000000000000000000000000000000000000819", - "0x000000000000000000000000000000000000000000000000000000000000081a", - "0x000000000000000000000000000000000000000000000000000000000000081b", - "0x000000000000000000000000000000000000000000000000000000000000081c", - "0x000000000000000000000000000000000000000000000000000000000000081d", - "0x000000000000000000000000000000000000000000000000000000000000081e", - "0x000000000000000000000000000000000000000000000000000000000000081f", - "0x0000000000000000000000000000000000000000000000000000000000000820", - "0x0000000000000000000000000000000000000000000000000000000000000821", - "0x0000000000000000000000000000000000000000000000000000000000000822", - "0x0000000000000000000000000000000000000000000000000000000000000823", - "0x0000000000000000000000000000000000000000000000000000000000000824", - "0x0000000000000000000000000000000000000000000000000000000000000825", - "0x0000000000000000000000000000000000000000000000000000000000000826", - "0x0000000000000000000000000000000000000000000000000000000000000827", - "0x0000000000000000000000000000000000000000000000000000000000000828", - "0x0000000000000000000000000000000000000000000000000000000000000829", - "0x000000000000000000000000000000000000000000000000000000000000082a", - "0x000000000000000000000000000000000000000000000000000000000000082b", - "0x000000000000000000000000000000000000000000000000000000000000082c", - "0x000000000000000000000000000000000000000000000000000000000000082d", - "0x000000000000000000000000000000000000000000000000000000000000082e", - "0x000000000000000000000000000000000000000000000000000000000000082f", - "0x0000000000000000000000000000000000000000000000000000000000000830", - "0x0000000000000000000000000000000000000000000000000000000000000831", - "0x0000000000000000000000000000000000000000000000000000000000000832", - "0x0000000000000000000000000000000000000000000000000000000000000833", - "0x0000000000000000000000000000000000000000000000000000000000000834", - "0x0000000000000000000000000000000000000000000000000000000000000835", - "0x0000000000000000000000000000000000000000000000000000000000000836", - "0x0000000000000000000000000000000000000000000000000000000000000837", - "0x0000000000000000000000000000000000000000000000000000000000000838", - "0x0000000000000000000000000000000000000000000000000000000000000839", - "0x000000000000000000000000000000000000000000000000000000000000083a", - "0x000000000000000000000000000000000000000000000000000000000000083b", - "0x000000000000000000000000000000000000000000000000000000000000083c", - "0x000000000000000000000000000000000000000000000000000000000000083d", - "0x000000000000000000000000000000000000000000000000000000000000083e", - "0x000000000000000000000000000000000000000000000000000000000000083f", - "0x0000000000000000000000000000000000000000000000000000000000000840", - "0x0000000000000000000000000000000000000000000000000000000000000841", - "0x0000000000000000000000000000000000000000000000000000000000000842", - "0x0000000000000000000000000000000000000000000000000000000000000843", - "0x0000000000000000000000000000000000000000000000000000000000000844", - "0x0000000000000000000000000000000000000000000000000000000000000845", - "0x0000000000000000000000000000000000000000000000000000000000000846", - "0x0000000000000000000000000000000000000000000000000000000000000847", - "0x0000000000000000000000000000000000000000000000000000000000000848", - "0x0000000000000000000000000000000000000000000000000000000000000849", - "0x000000000000000000000000000000000000000000000000000000000000084a", - "0x000000000000000000000000000000000000000000000000000000000000084b", - "0x000000000000000000000000000000000000000000000000000000000000084c", - "0x000000000000000000000000000000000000000000000000000000000000084d", - "0x000000000000000000000000000000000000000000000000000000000000084e", - "0x000000000000000000000000000000000000000000000000000000000000084f", - "0x0000000000000000000000000000000000000000000000000000000000000850", - "0x0000000000000000000000000000000000000000000000000000000000000851", - "0x0000000000000000000000000000000000000000000000000000000000000852", - "0x0000000000000000000000000000000000000000000000000000000000000853", - "0x0000000000000000000000000000000000000000000000000000000000000854", - "0x0000000000000000000000000000000000000000000000000000000000000855", - "0x0000000000000000000000000000000000000000000000000000000000000856", - "0x0000000000000000000000000000000000000000000000000000000000000857", - "0x0000000000000000000000000000000000000000000000000000000000000858", - "0x0000000000000000000000000000000000000000000000000000000000000859", - "0x000000000000000000000000000000000000000000000000000000000000085a", - "0x000000000000000000000000000000000000000000000000000000000000085b", - "0x000000000000000000000000000000000000000000000000000000000000085c", - "0x000000000000000000000000000000000000000000000000000000000000085d", - "0x000000000000000000000000000000000000000000000000000000000000085e", - "0x000000000000000000000000000000000000000000000000000000000000085f", - "0x0000000000000000000000000000000000000000000000000000000000000860", - "0x0000000000000000000000000000000000000000000000000000000000000861", - "0x0000000000000000000000000000000000000000000000000000000000000862", - "0x0000000000000000000000000000000000000000000000000000000000000863", - "0x0000000000000000000000000000000000000000000000000000000000000864", - "0x0000000000000000000000000000000000000000000000000000000000000865", - "0x0000000000000000000000000000000000000000000000000000000000000866", - "0x0000000000000000000000000000000000000000000000000000000000000867", - "0x0000000000000000000000000000000000000000000000000000000000000868", - "0x0000000000000000000000000000000000000000000000000000000000000869", - "0x000000000000000000000000000000000000000000000000000000000000086a", - "0x000000000000000000000000000000000000000000000000000000000000086b", - "0x000000000000000000000000000000000000000000000000000000000000086c", - "0x000000000000000000000000000000000000000000000000000000000000086d", - "0x000000000000000000000000000000000000000000000000000000000000086e", - "0x000000000000000000000000000000000000000000000000000000000000086f", - "0x0000000000000000000000000000000000000000000000000000000000000870", - "0x0000000000000000000000000000000000000000000000000000000000000871", - "0x0000000000000000000000000000000000000000000000000000000000000872", - "0x0000000000000000000000000000000000000000000000000000000000000873", - "0x0000000000000000000000000000000000000000000000000000000000000874", - "0x0000000000000000000000000000000000000000000000000000000000000875", - "0x0000000000000000000000000000000000000000000000000000000000000876", - "0x0000000000000000000000000000000000000000000000000000000000000877", - "0x0000000000000000000000000000000000000000000000000000000000000878", - "0x0000000000000000000000000000000000000000000000000000000000000879", - "0x000000000000000000000000000000000000000000000000000000000000087a", - "0x000000000000000000000000000000000000000000000000000000000000087b", - "0x000000000000000000000000000000000000000000000000000000000000087c", - "0x000000000000000000000000000000000000000000000000000000000000087d", - "0x000000000000000000000000000000000000000000000000000000000000087e", - "0x000000000000000000000000000000000000000000000000000000000000087f", - "0x0000000000000000000000000000000000000000000000000000000000000880", - "0x0000000000000000000000000000000000000000000000000000000000000881", - "0x0000000000000000000000000000000000000000000000000000000000000882", - "0x0000000000000000000000000000000000000000000000000000000000000883", - "0x0000000000000000000000000000000000000000000000000000000000000884", - "0x0000000000000000000000000000000000000000000000000000000000000885", - "0x0000000000000000000000000000000000000000000000000000000000000886", - "0x0000000000000000000000000000000000000000000000000000000000000887", - "0x0000000000000000000000000000000000000000000000000000000000000888", - "0x0000000000000000000000000000000000000000000000000000000000000889", - "0x000000000000000000000000000000000000000000000000000000000000088a", - "0x000000000000000000000000000000000000000000000000000000000000088b", - "0x000000000000000000000000000000000000000000000000000000000000088c", - "0x000000000000000000000000000000000000000000000000000000000000088d", - "0x000000000000000000000000000000000000000000000000000000000000088e", - "0x000000000000000000000000000000000000000000000000000000000000088f", - "0x0000000000000000000000000000000000000000000000000000000000000890", - "0x0000000000000000000000000000000000000000000000000000000000000891", - "0x0000000000000000000000000000000000000000000000000000000000000892", - "0x0000000000000000000000000000000000000000000000000000000000000893", - "0x0000000000000000000000000000000000000000000000000000000000000894", - "0x0000000000000000000000000000000000000000000000000000000000000895", - "0x0000000000000000000000000000000000000000000000000000000000000896", - "0x0000000000000000000000000000000000000000000000000000000000000897", - "0x0000000000000000000000000000000000000000000000000000000000000898", - "0x0000000000000000000000000000000000000000000000000000000000000899", - "0x000000000000000000000000000000000000000000000000000000000000089a", - "0x000000000000000000000000000000000000000000000000000000000000089b", - "0x000000000000000000000000000000000000000000000000000000000000089c", - "0x000000000000000000000000000000000000000000000000000000000000089d", - "0x000000000000000000000000000000000000000000000000000000000000089e", - "0x000000000000000000000000000000000000000000000000000000000000089f", - "0x00000000000000000000000000000000000000000000000000000000000008a0", - "0x00000000000000000000000000000000000000000000000000000000000008a1", - "0x00000000000000000000000000000000000000000000000000000000000008a2", - "0x00000000000000000000000000000000000000000000000000000000000008a3", - "0x00000000000000000000000000000000000000000000000000000000000008a4", - "0x00000000000000000000000000000000000000000000000000000000000008a5", - "0x00000000000000000000000000000000000000000000000000000000000008a6", - "0x00000000000000000000000000000000000000000000000000000000000008a7", - "0x00000000000000000000000000000000000000000000000000000000000008a8", - "0x00000000000000000000000000000000000000000000000000000000000008a9", - "0x00000000000000000000000000000000000000000000000000000000000008aa", - "0x00000000000000000000000000000000000000000000000000000000000008ab", - "0x00000000000000000000000000000000000000000000000000000000000008ac", - "0x00000000000000000000000000000000000000000000000000000000000008ad", - "0x00000000000000000000000000000000000000000000000000000000000008ae", - "0x00000000000000000000000000000000000000000000000000000000000008af", - "0x00000000000000000000000000000000000000000000000000000000000008b0", - "0x00000000000000000000000000000000000000000000000000000000000008b1", - "0x00000000000000000000000000000000000000000000000000000000000008b2", - "0x00000000000000000000000000000000000000000000000000000000000008b3", - "0x00000000000000000000000000000000000000000000000000000000000008b4", - "0x00000000000000000000000000000000000000000000000000000000000008b5", - "0x00000000000000000000000000000000000000000000000000000000000008b6", - "0x00000000000000000000000000000000000000000000000000000000000008b7", - "0x00000000000000000000000000000000000000000000000000000000000008b8", - "0x00000000000000000000000000000000000000000000000000000000000008b9", - "0x00000000000000000000000000000000000000000000000000000000000008ba", - "0x00000000000000000000000000000000000000000000000000000000000008bb", - "0x00000000000000000000000000000000000000000000000000000000000008bc", - "0x00000000000000000000000000000000000000000000000000000000000008bd", - "0x00000000000000000000000000000000000000000000000000000000000008be", - "0x00000000000000000000000000000000000000000000000000000000000008bf", - "0x00000000000000000000000000000000000000000000000000000000000008c0", - "0x00000000000000000000000000000000000000000000000000000000000008c1", - "0x00000000000000000000000000000000000000000000000000000000000008c2", - "0x00000000000000000000000000000000000000000000000000000000000008c3", - "0x00000000000000000000000000000000000000000000000000000000000008c4", - "0x00000000000000000000000000000000000000000000000000000000000008c5", - "0x00000000000000000000000000000000000000000000000000000000000008c6", - "0x00000000000000000000000000000000000000000000000000000000000008c7", - "0x00000000000000000000000000000000000000000000000000000000000008c8", - "0x00000000000000000000000000000000000000000000000000000000000008c9", - "0x00000000000000000000000000000000000000000000000000000000000008ca", - "0x00000000000000000000000000000000000000000000000000000000000008cb", - "0x00000000000000000000000000000000000000000000000000000000000008cc", - "0x00000000000000000000000000000000000000000000000000000000000008cd", - "0x00000000000000000000000000000000000000000000000000000000000008ce", - "0x00000000000000000000000000000000000000000000000000000000000008cf", - "0x00000000000000000000000000000000000000000000000000000000000008d0", - "0x00000000000000000000000000000000000000000000000000000000000008d1", - "0x00000000000000000000000000000000000000000000000000000000000008d2", - "0x00000000000000000000000000000000000000000000000000000000000008d3", - "0x00000000000000000000000000000000000000000000000000000000000008d4", - "0x00000000000000000000000000000000000000000000000000000000000008d5", - "0x00000000000000000000000000000000000000000000000000000000000008d6", - "0x00000000000000000000000000000000000000000000000000000000000008d7", - "0x00000000000000000000000000000000000000000000000000000000000008d8", - "0x00000000000000000000000000000000000000000000000000000000000008d9", - "0x00000000000000000000000000000000000000000000000000000000000008da", - "0x00000000000000000000000000000000000000000000000000000000000008db", - "0x00000000000000000000000000000000000000000000000000000000000008dc", - "0x00000000000000000000000000000000000000000000000000000000000008dd", - "0x00000000000000000000000000000000000000000000000000000000000008de", - "0x00000000000000000000000000000000000000000000000000000000000008df", - "0x00000000000000000000000000000000000000000000000000000000000008e0", - "0x00000000000000000000000000000000000000000000000000000000000008e1", - "0x00000000000000000000000000000000000000000000000000000000000008e2", - "0x00000000000000000000000000000000000000000000000000000000000008e3", - "0x00000000000000000000000000000000000000000000000000000000000008e4", - "0x00000000000000000000000000000000000000000000000000000000000008e5", - "0x00000000000000000000000000000000000000000000000000000000000008e6", - "0x00000000000000000000000000000000000000000000000000000000000008e7", - "0x00000000000000000000000000000000000000000000000000000000000008e8", - "0x00000000000000000000000000000000000000000000000000000000000008e9", - "0x00000000000000000000000000000000000000000000000000000000000008ea", - "0x00000000000000000000000000000000000000000000000000000000000008eb", - "0x00000000000000000000000000000000000000000000000000000000000008ec", - "0x00000000000000000000000000000000000000000000000000000000000008ed", - "0x00000000000000000000000000000000000000000000000000000000000008ee", - "0x00000000000000000000000000000000000000000000000000000000000008ef", - "0x00000000000000000000000000000000000000000000000000000000000008f0", - "0x00000000000000000000000000000000000000000000000000000000000008f1", - "0x00000000000000000000000000000000000000000000000000000000000008f2", - "0x00000000000000000000000000000000000000000000000000000000000008f3", - "0x00000000000000000000000000000000000000000000000000000000000008f4", - "0x00000000000000000000000000000000000000000000000000000000000008f5", - "0x00000000000000000000000000000000000000000000000000000000000008f6", - "0x00000000000000000000000000000000000000000000000000000000000008f7", - "0x00000000000000000000000000000000000000000000000000000000000008f8", - "0x00000000000000000000000000000000000000000000000000000000000008f9", - "0x00000000000000000000000000000000000000000000000000000000000008fa", - "0x00000000000000000000000000000000000000000000000000000000000008fb", - "0x00000000000000000000000000000000000000000000000000000000000008fc", - "0x00000000000000000000000000000000000000000000000000000000000008fd", - "0x00000000000000000000000000000000000000000000000000000000000008fe", - "0x00000000000000000000000000000000000000000000000000000000000008ff", - "0x0000000000000000000000000000000000000000000000000000000000000900", - "0x0000000000000000000000000000000000000000000000000000000000000901", - "0x0000000000000000000000000000000000000000000000000000000000000902", - "0x0000000000000000000000000000000000000000000000000000000000000903", - "0x0000000000000000000000000000000000000000000000000000000000000904", - "0x0000000000000000000000000000000000000000000000000000000000000905", - "0x0000000000000000000000000000000000000000000000000000000000000906", - "0x0000000000000000000000000000000000000000000000000000000000000907", - "0x0000000000000000000000000000000000000000000000000000000000000908", - "0x0000000000000000000000000000000000000000000000000000000000000909", - "0x000000000000000000000000000000000000000000000000000000000000090a", - "0x000000000000000000000000000000000000000000000000000000000000090b", - "0x000000000000000000000000000000000000000000000000000000000000090c", - "0x000000000000000000000000000000000000000000000000000000000000090d", - "0x000000000000000000000000000000000000000000000000000000000000090e", - "0x000000000000000000000000000000000000000000000000000000000000090f", - "0x0000000000000000000000000000000000000000000000000000000000000910", - "0x0000000000000000000000000000000000000000000000000000000000000911", - "0x0000000000000000000000000000000000000000000000000000000000000912", - "0x0000000000000000000000000000000000000000000000000000000000000913", - "0x0000000000000000000000000000000000000000000000000000000000000914", - "0x0000000000000000000000000000000000000000000000000000000000000915", - "0x0000000000000000000000000000000000000000000000000000000000000916", - "0x0000000000000000000000000000000000000000000000000000000000000917", - "0x0000000000000000000000000000000000000000000000000000000000000918", - "0x0000000000000000000000000000000000000000000000000000000000000919", - "0x000000000000000000000000000000000000000000000000000000000000091a", - "0x000000000000000000000000000000000000000000000000000000000000091b", - "0x000000000000000000000000000000000000000000000000000000000000091c", - "0x000000000000000000000000000000000000000000000000000000000000091d", - "0x000000000000000000000000000000000000000000000000000000000000091e", - "0x000000000000000000000000000000000000000000000000000000000000091f", - "0x0000000000000000000000000000000000000000000000000000000000000920", - "0x0000000000000000000000000000000000000000000000000000000000000921", - "0x0000000000000000000000000000000000000000000000000000000000000922", - "0x0000000000000000000000000000000000000000000000000000000000000923", - "0x0000000000000000000000000000000000000000000000000000000000000924", - "0x0000000000000000000000000000000000000000000000000000000000000925", - "0x0000000000000000000000000000000000000000000000000000000000000926", - "0x0000000000000000000000000000000000000000000000000000000000000927", - "0x0000000000000000000000000000000000000000000000000000000000000928", - "0x0000000000000000000000000000000000000000000000000000000000000929", - "0x000000000000000000000000000000000000000000000000000000000000092a", - "0x000000000000000000000000000000000000000000000000000000000000092b", - "0x000000000000000000000000000000000000000000000000000000000000092c", - "0x000000000000000000000000000000000000000000000000000000000000092d", - "0x000000000000000000000000000000000000000000000000000000000000092e", - "0x000000000000000000000000000000000000000000000000000000000000092f", - "0x0000000000000000000000000000000000000000000000000000000000000930", - "0x0000000000000000000000000000000000000000000000000000000000000931", - "0x0000000000000000000000000000000000000000000000000000000000000932", - "0x0000000000000000000000000000000000000000000000000000000000000933", - "0x0000000000000000000000000000000000000000000000000000000000000934", - "0x0000000000000000000000000000000000000000000000000000000000000935", - "0x0000000000000000000000000000000000000000000000000000000000000936", - "0x0000000000000000000000000000000000000000000000000000000000000937", - "0x0000000000000000000000000000000000000000000000000000000000000938", - "0x0000000000000000000000000000000000000000000000000000000000000939", - "0x000000000000000000000000000000000000000000000000000000000000093a", - "0x000000000000000000000000000000000000000000000000000000000000093b", - "0x000000000000000000000000000000000000000000000000000000000000093c", - "0x000000000000000000000000000000000000000000000000000000000000093d", - "0x000000000000000000000000000000000000000000000000000000000000093e", - "0x000000000000000000000000000000000000000000000000000000000000093f", - "0x0000000000000000000000000000000000000000000000000000000000000940", - "0x0000000000000000000000000000000000000000000000000000000000000941", - "0x0000000000000000000000000000000000000000000000000000000000000942", - "0x0000000000000000000000000000000000000000000000000000000000000943", - "0x0000000000000000000000000000000000000000000000000000000000000944", - "0x0000000000000000000000000000000000000000000000000000000000000945", - "0x0000000000000000000000000000000000000000000000000000000000000946", - "0x0000000000000000000000000000000000000000000000000000000000000947", - "0x0000000000000000000000000000000000000000000000000000000000000948", - "0x0000000000000000000000000000000000000000000000000000000000000949", - "0x000000000000000000000000000000000000000000000000000000000000094a", - "0x000000000000000000000000000000000000000000000000000000000000094b", - "0x000000000000000000000000000000000000000000000000000000000000094c", - "0x000000000000000000000000000000000000000000000000000000000000094d", - "0x000000000000000000000000000000000000000000000000000000000000094e", - "0x000000000000000000000000000000000000000000000000000000000000094f", - "0x0000000000000000000000000000000000000000000000000000000000000950", - "0x0000000000000000000000000000000000000000000000000000000000000951", - "0x0000000000000000000000000000000000000000000000000000000000000952", - "0x0000000000000000000000000000000000000000000000000000000000000953", - "0x0000000000000000000000000000000000000000000000000000000000000954", - "0x0000000000000000000000000000000000000000000000000000000000000955", - "0x0000000000000000000000000000000000000000000000000000000000000956", - "0x0000000000000000000000000000000000000000000000000000000000000957", - "0x0000000000000000000000000000000000000000000000000000000000000958", - "0x0000000000000000000000000000000000000000000000000000000000000959", - "0x000000000000000000000000000000000000000000000000000000000000095a", - "0x000000000000000000000000000000000000000000000000000000000000095b", - "0x000000000000000000000000000000000000000000000000000000000000095c", - "0x000000000000000000000000000000000000000000000000000000000000095d", - "0x000000000000000000000000000000000000000000000000000000000000095e", - "0x000000000000000000000000000000000000000000000000000000000000095f", - "0x0000000000000000000000000000000000000000000000000000000000000960", - "0x0000000000000000000000000000000000000000000000000000000000000961", - "0x0000000000000000000000000000000000000000000000000000000000000962", - "0x0000000000000000000000000000000000000000000000000000000000000963", - "0x0000000000000000000000000000000000000000000000000000000000000964", - "0x0000000000000000000000000000000000000000000000000000000000000965", - "0x0000000000000000000000000000000000000000000000000000000000000966", - "0x0000000000000000000000000000000000000000000000000000000000000967", - "0x0000000000000000000000000000000000000000000000000000000000000968", - "0x0000000000000000000000000000000000000000000000000000000000000969", - "0x000000000000000000000000000000000000000000000000000000000000096a", - "0x000000000000000000000000000000000000000000000000000000000000096b", - "0x000000000000000000000000000000000000000000000000000000000000096c", - "0x000000000000000000000000000000000000000000000000000000000000096d", - "0x000000000000000000000000000000000000000000000000000000000000096e", - "0x000000000000000000000000000000000000000000000000000000000000096f", - "0x0000000000000000000000000000000000000000000000000000000000000970", - "0x0000000000000000000000000000000000000000000000000000000000000971", - "0x0000000000000000000000000000000000000000000000000000000000000972", - "0x0000000000000000000000000000000000000000000000000000000000000973", - "0x0000000000000000000000000000000000000000000000000000000000000974", - "0x0000000000000000000000000000000000000000000000000000000000000975", - "0x0000000000000000000000000000000000000000000000000000000000000976", - "0x0000000000000000000000000000000000000000000000000000000000000977", - "0x0000000000000000000000000000000000000000000000000000000000000978", - "0x0000000000000000000000000000000000000000000000000000000000000979", - "0x000000000000000000000000000000000000000000000000000000000000097a", - "0x000000000000000000000000000000000000000000000000000000000000097b", - "0x000000000000000000000000000000000000000000000000000000000000097c", - "0x000000000000000000000000000000000000000000000000000000000000097d", - "0x000000000000000000000000000000000000000000000000000000000000097e", - "0x000000000000000000000000000000000000000000000000000000000000097f", - "0x0000000000000000000000000000000000000000000000000000000000000980", - "0x0000000000000000000000000000000000000000000000000000000000000981", - "0x0000000000000000000000000000000000000000000000000000000000000982", - "0x0000000000000000000000000000000000000000000000000000000000000983", - "0x0000000000000000000000000000000000000000000000000000000000000984", - "0x0000000000000000000000000000000000000000000000000000000000000985", - "0x0000000000000000000000000000000000000000000000000000000000000986", - "0x0000000000000000000000000000000000000000000000000000000000000987", - "0x0000000000000000000000000000000000000000000000000000000000000988", - "0x0000000000000000000000000000000000000000000000000000000000000989", - "0x000000000000000000000000000000000000000000000000000000000000098a", - "0x000000000000000000000000000000000000000000000000000000000000098b", - "0x000000000000000000000000000000000000000000000000000000000000098c", - "0x000000000000000000000000000000000000000000000000000000000000098d", - "0x000000000000000000000000000000000000000000000000000000000000098e", - "0x000000000000000000000000000000000000000000000000000000000000098f", - "0x0000000000000000000000000000000000000000000000000000000000000990", - "0x0000000000000000000000000000000000000000000000000000000000000991", - "0x0000000000000000000000000000000000000000000000000000000000000992", - "0x0000000000000000000000000000000000000000000000000000000000000993", - "0x0000000000000000000000000000000000000000000000000000000000000994", - "0x0000000000000000000000000000000000000000000000000000000000000995", - "0x0000000000000000000000000000000000000000000000000000000000000996", - "0x0000000000000000000000000000000000000000000000000000000000000997", - "0x0000000000000000000000000000000000000000000000000000000000000998", - "0x0000000000000000000000000000000000000000000000000000000000000999", - "0x000000000000000000000000000000000000000000000000000000000000099a", - "0x000000000000000000000000000000000000000000000000000000000000099b", - "0x000000000000000000000000000000000000000000000000000000000000099c", - "0x000000000000000000000000000000000000000000000000000000000000099d", - "0x000000000000000000000000000000000000000000000000000000000000099e", - "0x000000000000000000000000000000000000000000000000000000000000099f", - "0x00000000000000000000000000000000000000000000000000000000000009a0", - "0x00000000000000000000000000000000000000000000000000000000000009a1", - "0x00000000000000000000000000000000000000000000000000000000000009a2", - "0x00000000000000000000000000000000000000000000000000000000000009a3", - "0x00000000000000000000000000000000000000000000000000000000000009a4", - "0x00000000000000000000000000000000000000000000000000000000000009a5", - "0x00000000000000000000000000000000000000000000000000000000000009a6", - "0x00000000000000000000000000000000000000000000000000000000000009a7", - "0x00000000000000000000000000000000000000000000000000000000000009a8", - "0x00000000000000000000000000000000000000000000000000000000000009a9", - "0x00000000000000000000000000000000000000000000000000000000000009aa", - "0x00000000000000000000000000000000000000000000000000000000000009ab", - "0x00000000000000000000000000000000000000000000000000000000000009ac", - "0x00000000000000000000000000000000000000000000000000000000000009ad", - "0x00000000000000000000000000000000000000000000000000000000000009ae", - "0x00000000000000000000000000000000000000000000000000000000000009af", - "0x00000000000000000000000000000000000000000000000000000000000009b0", - "0x00000000000000000000000000000000000000000000000000000000000009b1", - "0x00000000000000000000000000000000000000000000000000000000000009b2", - "0x00000000000000000000000000000000000000000000000000000000000009b3", - "0x00000000000000000000000000000000000000000000000000000000000009b4", - "0x00000000000000000000000000000000000000000000000000000000000009b5", - "0x00000000000000000000000000000000000000000000000000000000000009b6", - "0x00000000000000000000000000000000000000000000000000000000000009b7", - "0x00000000000000000000000000000000000000000000000000000000000009b8", - "0x00000000000000000000000000000000000000000000000000000000000009b9", - "0x00000000000000000000000000000000000000000000000000000000000009ba", - "0x00000000000000000000000000000000000000000000000000000000000009bb", - "0x00000000000000000000000000000000000000000000000000000000000009bc", - "0x00000000000000000000000000000000000000000000000000000000000009bd", - "0x00000000000000000000000000000000000000000000000000000000000009be", - "0x00000000000000000000000000000000000000000000000000000000000009bf", - "0x00000000000000000000000000000000000000000000000000000000000009c0", - "0x00000000000000000000000000000000000000000000000000000000000009c1", - "0x00000000000000000000000000000000000000000000000000000000000009c2", - "0x00000000000000000000000000000000000000000000000000000000000009c3", - "0x00000000000000000000000000000000000000000000000000000000000009c4", - "0x00000000000000000000000000000000000000000000000000000000000009c5", - "0x00000000000000000000000000000000000000000000000000000000000009c6", - "0x00000000000000000000000000000000000000000000000000000000000009c7", - "0x00000000000000000000000000000000000000000000000000000000000009c8", - "0x00000000000000000000000000000000000000000000000000000000000009c9", - "0x00000000000000000000000000000000000000000000000000000000000009ca", - "0x00000000000000000000000000000000000000000000000000000000000009cb", - "0x00000000000000000000000000000000000000000000000000000000000009cc", - "0x00000000000000000000000000000000000000000000000000000000000009cd", - "0x00000000000000000000000000000000000000000000000000000000000009ce", - "0x00000000000000000000000000000000000000000000000000000000000009cf", - "0x00000000000000000000000000000000000000000000000000000000000009d0", - "0x00000000000000000000000000000000000000000000000000000000000009d1", - "0x00000000000000000000000000000000000000000000000000000000000009d2", - "0x00000000000000000000000000000000000000000000000000000000000009d3", - "0x00000000000000000000000000000000000000000000000000000000000009d4", - "0x00000000000000000000000000000000000000000000000000000000000009d5", - "0x00000000000000000000000000000000000000000000000000000000000009d6", - "0x00000000000000000000000000000000000000000000000000000000000009d7", - "0x00000000000000000000000000000000000000000000000000000000000009d8", - "0x00000000000000000000000000000000000000000000000000000000000009d9", - "0x00000000000000000000000000000000000000000000000000000000000009da", - "0x00000000000000000000000000000000000000000000000000000000000009db" + "0x00000000000000000000000000000000000000000000000000000000000006db" ] - num_msgs = "0x0000000000000000000000000000000000000000000000000000000000000400" - num_real_msgs = "0x0000000000000000000000000000000000000000000000000000000000000400" + num_msgs = "0x0000000000000000000000000000000000000000000000000000000000000100" diff --git a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-block-root-single-tx/Prover.toml b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-block-root-single-tx/Prover.toml index abe9492115a4..debd6744d176 100644 --- a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-block-root-single-tx/Prover.toml +++ b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-block-root-single-tx/Prover.toml @@ -1,15 +1,15 @@ [inputs] l1_to_l2_message_frontier_hint = [ "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x19f1a0c09db4cd026f686e9c8fb45501a9fefb4eb1b4c6c328a51343a0094eeb", + "0x14e4b977b2203b70e6ee1c2456eb7114d090fe4b907f631eecd0919fed432e7d", + "0x30105bad22ddcc508b739b7c9ad87a561c569ff5cb0098a853c1c4ac21b7a037", + "0x1e20ad4181460cbfdc74ca773502c59b890f184efe300ebad895956d318422da", + "0x1434e6e2d5db1053ab8a3be58704509c799ee17e109c77f441f7bf1755400249", + "0x119f56a2e8423a7feaab49b9b5dcbadec0648dfa4096b61b6774ea33ae29dc7f", + "0x221cf368938c74e4fced9dfb2a8e37cd8a6c57d21385c249f0b5c2412341287f", + "0x2c5214dfc4d70d2619fce2a7e02ddcf380576dca42b66c9215c7d8d1ec154116", + "0x13abc9bba431e6930c169f5daeb60aedbb27d7618c7ff88b3b4ec1c6de1d6bb8", "0x0d04c63f36bd168215c9b09a227c7e8d3ad48e2f11b8202fd07c524bd30ee88f", "0x042c72d0ca208f0631ed947050258333518c26059f0a2ef041e933b1b2a6d8ad", "0x00c21235cdc5d4241fab782680421cdd99c088a3b48a740d8289d0e67b2ee5da", @@ -561,7 +561,7 @@ new_archive_sibling_path = [ accumulated_mana_used = "0x000000000000000000000000000000000000000000000000000000000006b6c0" [inputs.previous_rollup.public_inputs.constants] - vk_tree_root = "0x16b7eeebd9ec7b6b28187878be4fef52c87331b0ebf2e5719fd203a0474402b1" + vk_tree_root = "0x121ed84a634911bbb8a5e986c40e120a016a1713140a6a5ea4f8187a5e07e547" protocol_contracts_hash = "0x08d0026a4983c49638d0910859942c6648d5ea0a3a152c8a376e4983d0a003d9" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" @@ -570,8 +570,8 @@ new_archive_sibling_path = [ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000001" [inputs.previous_rollup.public_inputs.constants.l1_to_l2_tree_snapshot] - root = "0x18d6aae3ab4a271abd590cb98267825b70de1e9e5c339356e94ea5fc7feba64b" - next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000400" + root = "0x0c123047b777079e8db5f669ddaf1ffa1c7780d56b21e29f35e4772ac0f6280c" + next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000100" [inputs.previous_rollup.public_inputs.constants.global_variables] chain_id = "0x0000000000000000000000000000000000000000000000000000000000000000" @@ -642,10 +642,10 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x00000000000000000000000000000000000000000000000000000000b7d1b44c" ] state = [ - "0x0094718410fde55bb517af3681f091074cfb1ad4b887f787cb6c8c0da282a4f4", - "0x1dd771ecb8f2a7dd3a85308ac20c04186ca23839a7a6e72ac8f808b54ef61529", - "0x28a6fe33cf32cd2116271658e6139a4ec79c810940a66734e169b78012d4d0f5", - "0x28bc1311501123c189212d28152f83902b6f0ec4bcca0c20d1afd2ada5b5dee4" + "0x288af51abe89c10666a769ec6df9758af5abdad8e88d044f5d37766385bcac53", + "0x27cdbbfad8887920e1a0c5e720bb2aa5e71edec420fcb1bf82823eccc86729d9", + "0x2fe17ffc52db3d9ffe075dfb1e56f274253851ca37da207ed1b7a304c4627978", + "0x0efdadcf08efcd8f0cc01195504fd280d970551dbc0a717bdb28e5f5a0556373" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000002" squeeze_mode = false @@ -656,7 +656,7 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x1fc39d0a428c8536dbca551ea79848acf67d89d090fd8c643c7d90f2e8f32340", "0x12ce5a49a1ceca53ada7bee003f929bbd65abaa74e8072a81f304c2c96c44e31", "0x276f60617ee2c3b406af56bfe9216a7d99be56ad316ff557cee0446f08e14135", - "0x14f9430ecc9a29b27e8af3154b4cbb75bf0f995c16737ed125019dea77ab3f62", + "0x1800b90c40fa738a64bbc71d11bdc768c7c43a4e8876d6e5bf203355830af7e2", "0x25f9ac8d47a2dbcfb13b88ea474667fd2854bf5c901f2f698266864ffe7c21ad", "0x0db3ab63c7556f8724d86819156701855018cc79140a052893d67fb67c99c1f1", "0x2a5c50782880ea145b4f04af31f7df9ef0cde48fab69c958aef179c68e836f62" @@ -1039,778 +1039,9 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x00000000000000000000000000000000000000000000000000000000000006d8", "0x00000000000000000000000000000000000000000000000000000000000006d9", "0x00000000000000000000000000000000000000000000000000000000000006da", - "0x00000000000000000000000000000000000000000000000000000000000006db", - "0x00000000000000000000000000000000000000000000000000000000000006dc", - "0x00000000000000000000000000000000000000000000000000000000000006dd", - "0x00000000000000000000000000000000000000000000000000000000000006de", - "0x00000000000000000000000000000000000000000000000000000000000006df", - "0x00000000000000000000000000000000000000000000000000000000000006e0", - "0x00000000000000000000000000000000000000000000000000000000000006e1", - "0x00000000000000000000000000000000000000000000000000000000000006e2", - "0x00000000000000000000000000000000000000000000000000000000000006e3", - "0x00000000000000000000000000000000000000000000000000000000000006e4", - "0x00000000000000000000000000000000000000000000000000000000000006e5", - "0x00000000000000000000000000000000000000000000000000000000000006e6", - "0x00000000000000000000000000000000000000000000000000000000000006e7", - "0x00000000000000000000000000000000000000000000000000000000000006e8", - "0x00000000000000000000000000000000000000000000000000000000000006e9", - "0x00000000000000000000000000000000000000000000000000000000000006ea", - "0x00000000000000000000000000000000000000000000000000000000000006eb", - "0x00000000000000000000000000000000000000000000000000000000000006ec", - "0x00000000000000000000000000000000000000000000000000000000000006ed", - "0x00000000000000000000000000000000000000000000000000000000000006ee", - "0x00000000000000000000000000000000000000000000000000000000000006ef", - "0x00000000000000000000000000000000000000000000000000000000000006f0", - "0x00000000000000000000000000000000000000000000000000000000000006f1", - "0x00000000000000000000000000000000000000000000000000000000000006f2", - "0x00000000000000000000000000000000000000000000000000000000000006f3", - "0x00000000000000000000000000000000000000000000000000000000000006f4", - "0x00000000000000000000000000000000000000000000000000000000000006f5", - "0x00000000000000000000000000000000000000000000000000000000000006f6", - "0x00000000000000000000000000000000000000000000000000000000000006f7", - "0x00000000000000000000000000000000000000000000000000000000000006f8", - "0x00000000000000000000000000000000000000000000000000000000000006f9", - "0x00000000000000000000000000000000000000000000000000000000000006fa", - "0x00000000000000000000000000000000000000000000000000000000000006fb", - "0x00000000000000000000000000000000000000000000000000000000000006fc", - "0x00000000000000000000000000000000000000000000000000000000000006fd", - "0x00000000000000000000000000000000000000000000000000000000000006fe", - "0x00000000000000000000000000000000000000000000000000000000000006ff", - "0x0000000000000000000000000000000000000000000000000000000000000700", - "0x0000000000000000000000000000000000000000000000000000000000000701", - "0x0000000000000000000000000000000000000000000000000000000000000702", - "0x0000000000000000000000000000000000000000000000000000000000000703", - "0x0000000000000000000000000000000000000000000000000000000000000704", - "0x0000000000000000000000000000000000000000000000000000000000000705", - "0x0000000000000000000000000000000000000000000000000000000000000706", - "0x0000000000000000000000000000000000000000000000000000000000000707", - "0x0000000000000000000000000000000000000000000000000000000000000708", - "0x0000000000000000000000000000000000000000000000000000000000000709", - "0x000000000000000000000000000000000000000000000000000000000000070a", - "0x000000000000000000000000000000000000000000000000000000000000070b", - "0x000000000000000000000000000000000000000000000000000000000000070c", - "0x000000000000000000000000000000000000000000000000000000000000070d", - "0x000000000000000000000000000000000000000000000000000000000000070e", - "0x000000000000000000000000000000000000000000000000000000000000070f", - "0x0000000000000000000000000000000000000000000000000000000000000710", - "0x0000000000000000000000000000000000000000000000000000000000000711", - "0x0000000000000000000000000000000000000000000000000000000000000712", - "0x0000000000000000000000000000000000000000000000000000000000000713", - "0x0000000000000000000000000000000000000000000000000000000000000714", - "0x0000000000000000000000000000000000000000000000000000000000000715", - "0x0000000000000000000000000000000000000000000000000000000000000716", - "0x0000000000000000000000000000000000000000000000000000000000000717", - "0x0000000000000000000000000000000000000000000000000000000000000718", - "0x0000000000000000000000000000000000000000000000000000000000000719", - "0x000000000000000000000000000000000000000000000000000000000000071a", - "0x000000000000000000000000000000000000000000000000000000000000071b", - "0x000000000000000000000000000000000000000000000000000000000000071c", - "0x000000000000000000000000000000000000000000000000000000000000071d", - "0x000000000000000000000000000000000000000000000000000000000000071e", - "0x000000000000000000000000000000000000000000000000000000000000071f", - "0x0000000000000000000000000000000000000000000000000000000000000720", - "0x0000000000000000000000000000000000000000000000000000000000000721", - "0x0000000000000000000000000000000000000000000000000000000000000722", - "0x0000000000000000000000000000000000000000000000000000000000000723", - "0x0000000000000000000000000000000000000000000000000000000000000724", - "0x0000000000000000000000000000000000000000000000000000000000000725", - "0x0000000000000000000000000000000000000000000000000000000000000726", - "0x0000000000000000000000000000000000000000000000000000000000000727", - "0x0000000000000000000000000000000000000000000000000000000000000728", - "0x0000000000000000000000000000000000000000000000000000000000000729", - "0x000000000000000000000000000000000000000000000000000000000000072a", - "0x000000000000000000000000000000000000000000000000000000000000072b", - "0x000000000000000000000000000000000000000000000000000000000000072c", - "0x000000000000000000000000000000000000000000000000000000000000072d", - "0x000000000000000000000000000000000000000000000000000000000000072e", - "0x000000000000000000000000000000000000000000000000000000000000072f", - "0x0000000000000000000000000000000000000000000000000000000000000730", - "0x0000000000000000000000000000000000000000000000000000000000000731", - "0x0000000000000000000000000000000000000000000000000000000000000732", - "0x0000000000000000000000000000000000000000000000000000000000000733", - "0x0000000000000000000000000000000000000000000000000000000000000734", - "0x0000000000000000000000000000000000000000000000000000000000000735", - "0x0000000000000000000000000000000000000000000000000000000000000736", - "0x0000000000000000000000000000000000000000000000000000000000000737", - "0x0000000000000000000000000000000000000000000000000000000000000738", - "0x0000000000000000000000000000000000000000000000000000000000000739", - "0x000000000000000000000000000000000000000000000000000000000000073a", - "0x000000000000000000000000000000000000000000000000000000000000073b", - "0x000000000000000000000000000000000000000000000000000000000000073c", - "0x000000000000000000000000000000000000000000000000000000000000073d", - "0x000000000000000000000000000000000000000000000000000000000000073e", - "0x000000000000000000000000000000000000000000000000000000000000073f", - "0x0000000000000000000000000000000000000000000000000000000000000740", - "0x0000000000000000000000000000000000000000000000000000000000000741", - "0x0000000000000000000000000000000000000000000000000000000000000742", - "0x0000000000000000000000000000000000000000000000000000000000000743", - "0x0000000000000000000000000000000000000000000000000000000000000744", - "0x0000000000000000000000000000000000000000000000000000000000000745", - "0x0000000000000000000000000000000000000000000000000000000000000746", - "0x0000000000000000000000000000000000000000000000000000000000000747", - "0x0000000000000000000000000000000000000000000000000000000000000748", - "0x0000000000000000000000000000000000000000000000000000000000000749", - "0x000000000000000000000000000000000000000000000000000000000000074a", - "0x000000000000000000000000000000000000000000000000000000000000074b", - "0x000000000000000000000000000000000000000000000000000000000000074c", - "0x000000000000000000000000000000000000000000000000000000000000074d", - "0x000000000000000000000000000000000000000000000000000000000000074e", - "0x000000000000000000000000000000000000000000000000000000000000074f", - "0x0000000000000000000000000000000000000000000000000000000000000750", - "0x0000000000000000000000000000000000000000000000000000000000000751", - "0x0000000000000000000000000000000000000000000000000000000000000752", - "0x0000000000000000000000000000000000000000000000000000000000000753", - "0x0000000000000000000000000000000000000000000000000000000000000754", - "0x0000000000000000000000000000000000000000000000000000000000000755", - "0x0000000000000000000000000000000000000000000000000000000000000756", - "0x0000000000000000000000000000000000000000000000000000000000000757", - "0x0000000000000000000000000000000000000000000000000000000000000758", - "0x0000000000000000000000000000000000000000000000000000000000000759", - "0x000000000000000000000000000000000000000000000000000000000000075a", - "0x000000000000000000000000000000000000000000000000000000000000075b", - "0x000000000000000000000000000000000000000000000000000000000000075c", - "0x000000000000000000000000000000000000000000000000000000000000075d", - "0x000000000000000000000000000000000000000000000000000000000000075e", - "0x000000000000000000000000000000000000000000000000000000000000075f", - "0x0000000000000000000000000000000000000000000000000000000000000760", - "0x0000000000000000000000000000000000000000000000000000000000000761", - "0x0000000000000000000000000000000000000000000000000000000000000762", - "0x0000000000000000000000000000000000000000000000000000000000000763", - "0x0000000000000000000000000000000000000000000000000000000000000764", - "0x0000000000000000000000000000000000000000000000000000000000000765", - "0x0000000000000000000000000000000000000000000000000000000000000766", - "0x0000000000000000000000000000000000000000000000000000000000000767", - "0x0000000000000000000000000000000000000000000000000000000000000768", - "0x0000000000000000000000000000000000000000000000000000000000000769", - "0x000000000000000000000000000000000000000000000000000000000000076a", - "0x000000000000000000000000000000000000000000000000000000000000076b", - "0x000000000000000000000000000000000000000000000000000000000000076c", - "0x000000000000000000000000000000000000000000000000000000000000076d", - "0x000000000000000000000000000000000000000000000000000000000000076e", - "0x000000000000000000000000000000000000000000000000000000000000076f", - "0x0000000000000000000000000000000000000000000000000000000000000770", - "0x0000000000000000000000000000000000000000000000000000000000000771", - "0x0000000000000000000000000000000000000000000000000000000000000772", - "0x0000000000000000000000000000000000000000000000000000000000000773", - "0x0000000000000000000000000000000000000000000000000000000000000774", - "0x0000000000000000000000000000000000000000000000000000000000000775", - "0x0000000000000000000000000000000000000000000000000000000000000776", - "0x0000000000000000000000000000000000000000000000000000000000000777", - "0x0000000000000000000000000000000000000000000000000000000000000778", - "0x0000000000000000000000000000000000000000000000000000000000000779", - "0x000000000000000000000000000000000000000000000000000000000000077a", - "0x000000000000000000000000000000000000000000000000000000000000077b", - "0x000000000000000000000000000000000000000000000000000000000000077c", - "0x000000000000000000000000000000000000000000000000000000000000077d", - "0x000000000000000000000000000000000000000000000000000000000000077e", - "0x000000000000000000000000000000000000000000000000000000000000077f", - "0x0000000000000000000000000000000000000000000000000000000000000780", - "0x0000000000000000000000000000000000000000000000000000000000000781", - "0x0000000000000000000000000000000000000000000000000000000000000782", - "0x0000000000000000000000000000000000000000000000000000000000000783", - "0x0000000000000000000000000000000000000000000000000000000000000784", - "0x0000000000000000000000000000000000000000000000000000000000000785", - "0x0000000000000000000000000000000000000000000000000000000000000786", - "0x0000000000000000000000000000000000000000000000000000000000000787", - "0x0000000000000000000000000000000000000000000000000000000000000788", - "0x0000000000000000000000000000000000000000000000000000000000000789", - "0x000000000000000000000000000000000000000000000000000000000000078a", - "0x000000000000000000000000000000000000000000000000000000000000078b", - "0x000000000000000000000000000000000000000000000000000000000000078c", - "0x000000000000000000000000000000000000000000000000000000000000078d", - "0x000000000000000000000000000000000000000000000000000000000000078e", - "0x000000000000000000000000000000000000000000000000000000000000078f", - "0x0000000000000000000000000000000000000000000000000000000000000790", - "0x0000000000000000000000000000000000000000000000000000000000000791", - "0x0000000000000000000000000000000000000000000000000000000000000792", - "0x0000000000000000000000000000000000000000000000000000000000000793", - "0x0000000000000000000000000000000000000000000000000000000000000794", - "0x0000000000000000000000000000000000000000000000000000000000000795", - "0x0000000000000000000000000000000000000000000000000000000000000796", - "0x0000000000000000000000000000000000000000000000000000000000000797", - "0x0000000000000000000000000000000000000000000000000000000000000798", - "0x0000000000000000000000000000000000000000000000000000000000000799", - "0x000000000000000000000000000000000000000000000000000000000000079a", - "0x000000000000000000000000000000000000000000000000000000000000079b", - "0x000000000000000000000000000000000000000000000000000000000000079c", - "0x000000000000000000000000000000000000000000000000000000000000079d", - "0x000000000000000000000000000000000000000000000000000000000000079e", - "0x000000000000000000000000000000000000000000000000000000000000079f", - "0x00000000000000000000000000000000000000000000000000000000000007a0", - "0x00000000000000000000000000000000000000000000000000000000000007a1", - "0x00000000000000000000000000000000000000000000000000000000000007a2", - "0x00000000000000000000000000000000000000000000000000000000000007a3", - "0x00000000000000000000000000000000000000000000000000000000000007a4", - "0x00000000000000000000000000000000000000000000000000000000000007a5", - "0x00000000000000000000000000000000000000000000000000000000000007a6", - "0x00000000000000000000000000000000000000000000000000000000000007a7", - "0x00000000000000000000000000000000000000000000000000000000000007a8", - "0x00000000000000000000000000000000000000000000000000000000000007a9", - "0x00000000000000000000000000000000000000000000000000000000000007aa", - "0x00000000000000000000000000000000000000000000000000000000000007ab", - "0x00000000000000000000000000000000000000000000000000000000000007ac", - "0x00000000000000000000000000000000000000000000000000000000000007ad", - "0x00000000000000000000000000000000000000000000000000000000000007ae", - "0x00000000000000000000000000000000000000000000000000000000000007af", - "0x00000000000000000000000000000000000000000000000000000000000007b0", - "0x00000000000000000000000000000000000000000000000000000000000007b1", - "0x00000000000000000000000000000000000000000000000000000000000007b2", - "0x00000000000000000000000000000000000000000000000000000000000007b3", - "0x00000000000000000000000000000000000000000000000000000000000007b4", - "0x00000000000000000000000000000000000000000000000000000000000007b5", - "0x00000000000000000000000000000000000000000000000000000000000007b6", - "0x00000000000000000000000000000000000000000000000000000000000007b7", - "0x00000000000000000000000000000000000000000000000000000000000007b8", - "0x00000000000000000000000000000000000000000000000000000000000007b9", - "0x00000000000000000000000000000000000000000000000000000000000007ba", - "0x00000000000000000000000000000000000000000000000000000000000007bb", - "0x00000000000000000000000000000000000000000000000000000000000007bc", - "0x00000000000000000000000000000000000000000000000000000000000007bd", - "0x00000000000000000000000000000000000000000000000000000000000007be", - "0x00000000000000000000000000000000000000000000000000000000000007bf", - "0x00000000000000000000000000000000000000000000000000000000000007c0", - "0x00000000000000000000000000000000000000000000000000000000000007c1", - "0x00000000000000000000000000000000000000000000000000000000000007c2", - "0x00000000000000000000000000000000000000000000000000000000000007c3", - "0x00000000000000000000000000000000000000000000000000000000000007c4", - "0x00000000000000000000000000000000000000000000000000000000000007c5", - "0x00000000000000000000000000000000000000000000000000000000000007c6", - "0x00000000000000000000000000000000000000000000000000000000000007c7", - "0x00000000000000000000000000000000000000000000000000000000000007c8", - "0x00000000000000000000000000000000000000000000000000000000000007c9", - "0x00000000000000000000000000000000000000000000000000000000000007ca", - "0x00000000000000000000000000000000000000000000000000000000000007cb", - "0x00000000000000000000000000000000000000000000000000000000000007cc", - "0x00000000000000000000000000000000000000000000000000000000000007cd", - "0x00000000000000000000000000000000000000000000000000000000000007ce", - "0x00000000000000000000000000000000000000000000000000000000000007cf", - "0x00000000000000000000000000000000000000000000000000000000000007d0", - "0x00000000000000000000000000000000000000000000000000000000000007d1", - "0x00000000000000000000000000000000000000000000000000000000000007d2", - "0x00000000000000000000000000000000000000000000000000000000000007d3", - "0x00000000000000000000000000000000000000000000000000000000000007d4", - "0x00000000000000000000000000000000000000000000000000000000000007d5", - "0x00000000000000000000000000000000000000000000000000000000000007d6", - "0x00000000000000000000000000000000000000000000000000000000000007d7", - "0x00000000000000000000000000000000000000000000000000000000000007d8", - "0x00000000000000000000000000000000000000000000000000000000000007d9", - "0x00000000000000000000000000000000000000000000000000000000000007da", - "0x00000000000000000000000000000000000000000000000000000000000007db", - "0x00000000000000000000000000000000000000000000000000000000000007dc", - "0x00000000000000000000000000000000000000000000000000000000000007dd", - "0x00000000000000000000000000000000000000000000000000000000000007de", - "0x00000000000000000000000000000000000000000000000000000000000007df", - "0x00000000000000000000000000000000000000000000000000000000000007e0", - "0x00000000000000000000000000000000000000000000000000000000000007e1", - "0x00000000000000000000000000000000000000000000000000000000000007e2", - "0x00000000000000000000000000000000000000000000000000000000000007e3", - "0x00000000000000000000000000000000000000000000000000000000000007e4", - "0x00000000000000000000000000000000000000000000000000000000000007e5", - "0x00000000000000000000000000000000000000000000000000000000000007e6", - "0x00000000000000000000000000000000000000000000000000000000000007e7", - "0x00000000000000000000000000000000000000000000000000000000000007e8", - "0x00000000000000000000000000000000000000000000000000000000000007e9", - "0x00000000000000000000000000000000000000000000000000000000000007ea", - "0x00000000000000000000000000000000000000000000000000000000000007eb", - "0x00000000000000000000000000000000000000000000000000000000000007ec", - "0x00000000000000000000000000000000000000000000000000000000000007ed", - "0x00000000000000000000000000000000000000000000000000000000000007ee", - "0x00000000000000000000000000000000000000000000000000000000000007ef", - "0x00000000000000000000000000000000000000000000000000000000000007f0", - "0x00000000000000000000000000000000000000000000000000000000000007f1", - "0x00000000000000000000000000000000000000000000000000000000000007f2", - "0x00000000000000000000000000000000000000000000000000000000000007f3", - "0x00000000000000000000000000000000000000000000000000000000000007f4", - "0x00000000000000000000000000000000000000000000000000000000000007f5", - "0x00000000000000000000000000000000000000000000000000000000000007f6", - "0x00000000000000000000000000000000000000000000000000000000000007f7", - "0x00000000000000000000000000000000000000000000000000000000000007f8", - "0x00000000000000000000000000000000000000000000000000000000000007f9", - "0x00000000000000000000000000000000000000000000000000000000000007fa", - "0x00000000000000000000000000000000000000000000000000000000000007fb", - "0x00000000000000000000000000000000000000000000000000000000000007fc", - "0x00000000000000000000000000000000000000000000000000000000000007fd", - "0x00000000000000000000000000000000000000000000000000000000000007fe", - "0x00000000000000000000000000000000000000000000000000000000000007ff", - "0x0000000000000000000000000000000000000000000000000000000000000800", - "0x0000000000000000000000000000000000000000000000000000000000000801", - "0x0000000000000000000000000000000000000000000000000000000000000802", - "0x0000000000000000000000000000000000000000000000000000000000000803", - "0x0000000000000000000000000000000000000000000000000000000000000804", - "0x0000000000000000000000000000000000000000000000000000000000000805", - "0x0000000000000000000000000000000000000000000000000000000000000806", - "0x0000000000000000000000000000000000000000000000000000000000000807", - "0x0000000000000000000000000000000000000000000000000000000000000808", - "0x0000000000000000000000000000000000000000000000000000000000000809", - "0x000000000000000000000000000000000000000000000000000000000000080a", - "0x000000000000000000000000000000000000000000000000000000000000080b", - "0x000000000000000000000000000000000000000000000000000000000000080c", - "0x000000000000000000000000000000000000000000000000000000000000080d", - "0x000000000000000000000000000000000000000000000000000000000000080e", - "0x000000000000000000000000000000000000000000000000000000000000080f", - "0x0000000000000000000000000000000000000000000000000000000000000810", - "0x0000000000000000000000000000000000000000000000000000000000000811", - "0x0000000000000000000000000000000000000000000000000000000000000812", - "0x0000000000000000000000000000000000000000000000000000000000000813", - "0x0000000000000000000000000000000000000000000000000000000000000814", - "0x0000000000000000000000000000000000000000000000000000000000000815", - "0x0000000000000000000000000000000000000000000000000000000000000816", - "0x0000000000000000000000000000000000000000000000000000000000000817", - "0x0000000000000000000000000000000000000000000000000000000000000818", - "0x0000000000000000000000000000000000000000000000000000000000000819", - "0x000000000000000000000000000000000000000000000000000000000000081a", - "0x000000000000000000000000000000000000000000000000000000000000081b", - "0x000000000000000000000000000000000000000000000000000000000000081c", - "0x000000000000000000000000000000000000000000000000000000000000081d", - "0x000000000000000000000000000000000000000000000000000000000000081e", - "0x000000000000000000000000000000000000000000000000000000000000081f", - "0x0000000000000000000000000000000000000000000000000000000000000820", - "0x0000000000000000000000000000000000000000000000000000000000000821", - "0x0000000000000000000000000000000000000000000000000000000000000822", - "0x0000000000000000000000000000000000000000000000000000000000000823", - "0x0000000000000000000000000000000000000000000000000000000000000824", - "0x0000000000000000000000000000000000000000000000000000000000000825", - "0x0000000000000000000000000000000000000000000000000000000000000826", - "0x0000000000000000000000000000000000000000000000000000000000000827", - "0x0000000000000000000000000000000000000000000000000000000000000828", - "0x0000000000000000000000000000000000000000000000000000000000000829", - "0x000000000000000000000000000000000000000000000000000000000000082a", - "0x000000000000000000000000000000000000000000000000000000000000082b", - "0x000000000000000000000000000000000000000000000000000000000000082c", - "0x000000000000000000000000000000000000000000000000000000000000082d", - "0x000000000000000000000000000000000000000000000000000000000000082e", - "0x000000000000000000000000000000000000000000000000000000000000082f", - "0x0000000000000000000000000000000000000000000000000000000000000830", - "0x0000000000000000000000000000000000000000000000000000000000000831", - "0x0000000000000000000000000000000000000000000000000000000000000832", - "0x0000000000000000000000000000000000000000000000000000000000000833", - "0x0000000000000000000000000000000000000000000000000000000000000834", - "0x0000000000000000000000000000000000000000000000000000000000000835", - "0x0000000000000000000000000000000000000000000000000000000000000836", - "0x0000000000000000000000000000000000000000000000000000000000000837", - "0x0000000000000000000000000000000000000000000000000000000000000838", - "0x0000000000000000000000000000000000000000000000000000000000000839", - "0x000000000000000000000000000000000000000000000000000000000000083a", - "0x000000000000000000000000000000000000000000000000000000000000083b", - "0x000000000000000000000000000000000000000000000000000000000000083c", - "0x000000000000000000000000000000000000000000000000000000000000083d", - "0x000000000000000000000000000000000000000000000000000000000000083e", - "0x000000000000000000000000000000000000000000000000000000000000083f", - "0x0000000000000000000000000000000000000000000000000000000000000840", - "0x0000000000000000000000000000000000000000000000000000000000000841", - "0x0000000000000000000000000000000000000000000000000000000000000842", - "0x0000000000000000000000000000000000000000000000000000000000000843", - "0x0000000000000000000000000000000000000000000000000000000000000844", - "0x0000000000000000000000000000000000000000000000000000000000000845", - "0x0000000000000000000000000000000000000000000000000000000000000846", - "0x0000000000000000000000000000000000000000000000000000000000000847", - "0x0000000000000000000000000000000000000000000000000000000000000848", - "0x0000000000000000000000000000000000000000000000000000000000000849", - "0x000000000000000000000000000000000000000000000000000000000000084a", - "0x000000000000000000000000000000000000000000000000000000000000084b", - "0x000000000000000000000000000000000000000000000000000000000000084c", - "0x000000000000000000000000000000000000000000000000000000000000084d", - "0x000000000000000000000000000000000000000000000000000000000000084e", - "0x000000000000000000000000000000000000000000000000000000000000084f", - "0x0000000000000000000000000000000000000000000000000000000000000850", - "0x0000000000000000000000000000000000000000000000000000000000000851", - "0x0000000000000000000000000000000000000000000000000000000000000852", - "0x0000000000000000000000000000000000000000000000000000000000000853", - "0x0000000000000000000000000000000000000000000000000000000000000854", - "0x0000000000000000000000000000000000000000000000000000000000000855", - "0x0000000000000000000000000000000000000000000000000000000000000856", - "0x0000000000000000000000000000000000000000000000000000000000000857", - "0x0000000000000000000000000000000000000000000000000000000000000858", - "0x0000000000000000000000000000000000000000000000000000000000000859", - "0x000000000000000000000000000000000000000000000000000000000000085a", - "0x000000000000000000000000000000000000000000000000000000000000085b", - "0x000000000000000000000000000000000000000000000000000000000000085c", - "0x000000000000000000000000000000000000000000000000000000000000085d", - "0x000000000000000000000000000000000000000000000000000000000000085e", - "0x000000000000000000000000000000000000000000000000000000000000085f", - "0x0000000000000000000000000000000000000000000000000000000000000860", - "0x0000000000000000000000000000000000000000000000000000000000000861", - "0x0000000000000000000000000000000000000000000000000000000000000862", - "0x0000000000000000000000000000000000000000000000000000000000000863", - "0x0000000000000000000000000000000000000000000000000000000000000864", - "0x0000000000000000000000000000000000000000000000000000000000000865", - "0x0000000000000000000000000000000000000000000000000000000000000866", - "0x0000000000000000000000000000000000000000000000000000000000000867", - "0x0000000000000000000000000000000000000000000000000000000000000868", - "0x0000000000000000000000000000000000000000000000000000000000000869", - "0x000000000000000000000000000000000000000000000000000000000000086a", - "0x000000000000000000000000000000000000000000000000000000000000086b", - "0x000000000000000000000000000000000000000000000000000000000000086c", - "0x000000000000000000000000000000000000000000000000000000000000086d", - "0x000000000000000000000000000000000000000000000000000000000000086e", - "0x000000000000000000000000000000000000000000000000000000000000086f", - "0x0000000000000000000000000000000000000000000000000000000000000870", - "0x0000000000000000000000000000000000000000000000000000000000000871", - "0x0000000000000000000000000000000000000000000000000000000000000872", - "0x0000000000000000000000000000000000000000000000000000000000000873", - "0x0000000000000000000000000000000000000000000000000000000000000874", - "0x0000000000000000000000000000000000000000000000000000000000000875", - "0x0000000000000000000000000000000000000000000000000000000000000876", - "0x0000000000000000000000000000000000000000000000000000000000000877", - "0x0000000000000000000000000000000000000000000000000000000000000878", - "0x0000000000000000000000000000000000000000000000000000000000000879", - "0x000000000000000000000000000000000000000000000000000000000000087a", - "0x000000000000000000000000000000000000000000000000000000000000087b", - "0x000000000000000000000000000000000000000000000000000000000000087c", - "0x000000000000000000000000000000000000000000000000000000000000087d", - "0x000000000000000000000000000000000000000000000000000000000000087e", - "0x000000000000000000000000000000000000000000000000000000000000087f", - "0x0000000000000000000000000000000000000000000000000000000000000880", - "0x0000000000000000000000000000000000000000000000000000000000000881", - "0x0000000000000000000000000000000000000000000000000000000000000882", - "0x0000000000000000000000000000000000000000000000000000000000000883", - "0x0000000000000000000000000000000000000000000000000000000000000884", - "0x0000000000000000000000000000000000000000000000000000000000000885", - "0x0000000000000000000000000000000000000000000000000000000000000886", - "0x0000000000000000000000000000000000000000000000000000000000000887", - "0x0000000000000000000000000000000000000000000000000000000000000888", - "0x0000000000000000000000000000000000000000000000000000000000000889", - "0x000000000000000000000000000000000000000000000000000000000000088a", - "0x000000000000000000000000000000000000000000000000000000000000088b", - "0x000000000000000000000000000000000000000000000000000000000000088c", - "0x000000000000000000000000000000000000000000000000000000000000088d", - "0x000000000000000000000000000000000000000000000000000000000000088e", - "0x000000000000000000000000000000000000000000000000000000000000088f", - "0x0000000000000000000000000000000000000000000000000000000000000890", - "0x0000000000000000000000000000000000000000000000000000000000000891", - "0x0000000000000000000000000000000000000000000000000000000000000892", - "0x0000000000000000000000000000000000000000000000000000000000000893", - "0x0000000000000000000000000000000000000000000000000000000000000894", - "0x0000000000000000000000000000000000000000000000000000000000000895", - "0x0000000000000000000000000000000000000000000000000000000000000896", - "0x0000000000000000000000000000000000000000000000000000000000000897", - "0x0000000000000000000000000000000000000000000000000000000000000898", - "0x0000000000000000000000000000000000000000000000000000000000000899", - "0x000000000000000000000000000000000000000000000000000000000000089a", - "0x000000000000000000000000000000000000000000000000000000000000089b", - "0x000000000000000000000000000000000000000000000000000000000000089c", - "0x000000000000000000000000000000000000000000000000000000000000089d", - "0x000000000000000000000000000000000000000000000000000000000000089e", - "0x000000000000000000000000000000000000000000000000000000000000089f", - "0x00000000000000000000000000000000000000000000000000000000000008a0", - "0x00000000000000000000000000000000000000000000000000000000000008a1", - "0x00000000000000000000000000000000000000000000000000000000000008a2", - "0x00000000000000000000000000000000000000000000000000000000000008a3", - "0x00000000000000000000000000000000000000000000000000000000000008a4", - "0x00000000000000000000000000000000000000000000000000000000000008a5", - "0x00000000000000000000000000000000000000000000000000000000000008a6", - "0x00000000000000000000000000000000000000000000000000000000000008a7", - "0x00000000000000000000000000000000000000000000000000000000000008a8", - "0x00000000000000000000000000000000000000000000000000000000000008a9", - "0x00000000000000000000000000000000000000000000000000000000000008aa", - "0x00000000000000000000000000000000000000000000000000000000000008ab", - "0x00000000000000000000000000000000000000000000000000000000000008ac", - "0x00000000000000000000000000000000000000000000000000000000000008ad", - "0x00000000000000000000000000000000000000000000000000000000000008ae", - "0x00000000000000000000000000000000000000000000000000000000000008af", - "0x00000000000000000000000000000000000000000000000000000000000008b0", - "0x00000000000000000000000000000000000000000000000000000000000008b1", - "0x00000000000000000000000000000000000000000000000000000000000008b2", - "0x00000000000000000000000000000000000000000000000000000000000008b3", - "0x00000000000000000000000000000000000000000000000000000000000008b4", - "0x00000000000000000000000000000000000000000000000000000000000008b5", - "0x00000000000000000000000000000000000000000000000000000000000008b6", - "0x00000000000000000000000000000000000000000000000000000000000008b7", - "0x00000000000000000000000000000000000000000000000000000000000008b8", - "0x00000000000000000000000000000000000000000000000000000000000008b9", - "0x00000000000000000000000000000000000000000000000000000000000008ba", - "0x00000000000000000000000000000000000000000000000000000000000008bb", - "0x00000000000000000000000000000000000000000000000000000000000008bc", - "0x00000000000000000000000000000000000000000000000000000000000008bd", - "0x00000000000000000000000000000000000000000000000000000000000008be", - "0x00000000000000000000000000000000000000000000000000000000000008bf", - "0x00000000000000000000000000000000000000000000000000000000000008c0", - "0x00000000000000000000000000000000000000000000000000000000000008c1", - "0x00000000000000000000000000000000000000000000000000000000000008c2", - "0x00000000000000000000000000000000000000000000000000000000000008c3", - "0x00000000000000000000000000000000000000000000000000000000000008c4", - "0x00000000000000000000000000000000000000000000000000000000000008c5", - "0x00000000000000000000000000000000000000000000000000000000000008c6", - "0x00000000000000000000000000000000000000000000000000000000000008c7", - "0x00000000000000000000000000000000000000000000000000000000000008c8", - "0x00000000000000000000000000000000000000000000000000000000000008c9", - "0x00000000000000000000000000000000000000000000000000000000000008ca", - "0x00000000000000000000000000000000000000000000000000000000000008cb", - "0x00000000000000000000000000000000000000000000000000000000000008cc", - "0x00000000000000000000000000000000000000000000000000000000000008cd", - "0x00000000000000000000000000000000000000000000000000000000000008ce", - "0x00000000000000000000000000000000000000000000000000000000000008cf", - "0x00000000000000000000000000000000000000000000000000000000000008d0", - "0x00000000000000000000000000000000000000000000000000000000000008d1", - "0x00000000000000000000000000000000000000000000000000000000000008d2", - "0x00000000000000000000000000000000000000000000000000000000000008d3", - "0x00000000000000000000000000000000000000000000000000000000000008d4", - "0x00000000000000000000000000000000000000000000000000000000000008d5", - "0x00000000000000000000000000000000000000000000000000000000000008d6", - "0x00000000000000000000000000000000000000000000000000000000000008d7", - "0x00000000000000000000000000000000000000000000000000000000000008d8", - "0x00000000000000000000000000000000000000000000000000000000000008d9", - "0x00000000000000000000000000000000000000000000000000000000000008da", - "0x00000000000000000000000000000000000000000000000000000000000008db", - "0x00000000000000000000000000000000000000000000000000000000000008dc", - "0x00000000000000000000000000000000000000000000000000000000000008dd", - "0x00000000000000000000000000000000000000000000000000000000000008de", - "0x00000000000000000000000000000000000000000000000000000000000008df", - "0x00000000000000000000000000000000000000000000000000000000000008e0", - "0x00000000000000000000000000000000000000000000000000000000000008e1", - "0x00000000000000000000000000000000000000000000000000000000000008e2", - "0x00000000000000000000000000000000000000000000000000000000000008e3", - "0x00000000000000000000000000000000000000000000000000000000000008e4", - "0x00000000000000000000000000000000000000000000000000000000000008e5", - "0x00000000000000000000000000000000000000000000000000000000000008e6", - "0x00000000000000000000000000000000000000000000000000000000000008e7", - "0x00000000000000000000000000000000000000000000000000000000000008e8", - "0x00000000000000000000000000000000000000000000000000000000000008e9", - "0x00000000000000000000000000000000000000000000000000000000000008ea", - "0x00000000000000000000000000000000000000000000000000000000000008eb", - "0x00000000000000000000000000000000000000000000000000000000000008ec", - "0x00000000000000000000000000000000000000000000000000000000000008ed", - "0x00000000000000000000000000000000000000000000000000000000000008ee", - "0x00000000000000000000000000000000000000000000000000000000000008ef", - "0x00000000000000000000000000000000000000000000000000000000000008f0", - "0x00000000000000000000000000000000000000000000000000000000000008f1", - "0x00000000000000000000000000000000000000000000000000000000000008f2", - "0x00000000000000000000000000000000000000000000000000000000000008f3", - "0x00000000000000000000000000000000000000000000000000000000000008f4", - "0x00000000000000000000000000000000000000000000000000000000000008f5", - "0x00000000000000000000000000000000000000000000000000000000000008f6", - "0x00000000000000000000000000000000000000000000000000000000000008f7", - "0x00000000000000000000000000000000000000000000000000000000000008f8", - "0x00000000000000000000000000000000000000000000000000000000000008f9", - "0x00000000000000000000000000000000000000000000000000000000000008fa", - "0x00000000000000000000000000000000000000000000000000000000000008fb", - "0x00000000000000000000000000000000000000000000000000000000000008fc", - "0x00000000000000000000000000000000000000000000000000000000000008fd", - "0x00000000000000000000000000000000000000000000000000000000000008fe", - "0x00000000000000000000000000000000000000000000000000000000000008ff", - "0x0000000000000000000000000000000000000000000000000000000000000900", - "0x0000000000000000000000000000000000000000000000000000000000000901", - "0x0000000000000000000000000000000000000000000000000000000000000902", - "0x0000000000000000000000000000000000000000000000000000000000000903", - "0x0000000000000000000000000000000000000000000000000000000000000904", - "0x0000000000000000000000000000000000000000000000000000000000000905", - "0x0000000000000000000000000000000000000000000000000000000000000906", - "0x0000000000000000000000000000000000000000000000000000000000000907", - "0x0000000000000000000000000000000000000000000000000000000000000908", - "0x0000000000000000000000000000000000000000000000000000000000000909", - "0x000000000000000000000000000000000000000000000000000000000000090a", - "0x000000000000000000000000000000000000000000000000000000000000090b", - "0x000000000000000000000000000000000000000000000000000000000000090c", - "0x000000000000000000000000000000000000000000000000000000000000090d", - "0x000000000000000000000000000000000000000000000000000000000000090e", - "0x000000000000000000000000000000000000000000000000000000000000090f", - "0x0000000000000000000000000000000000000000000000000000000000000910", - "0x0000000000000000000000000000000000000000000000000000000000000911", - "0x0000000000000000000000000000000000000000000000000000000000000912", - "0x0000000000000000000000000000000000000000000000000000000000000913", - "0x0000000000000000000000000000000000000000000000000000000000000914", - "0x0000000000000000000000000000000000000000000000000000000000000915", - "0x0000000000000000000000000000000000000000000000000000000000000916", - "0x0000000000000000000000000000000000000000000000000000000000000917", - "0x0000000000000000000000000000000000000000000000000000000000000918", - "0x0000000000000000000000000000000000000000000000000000000000000919", - "0x000000000000000000000000000000000000000000000000000000000000091a", - "0x000000000000000000000000000000000000000000000000000000000000091b", - "0x000000000000000000000000000000000000000000000000000000000000091c", - "0x000000000000000000000000000000000000000000000000000000000000091d", - "0x000000000000000000000000000000000000000000000000000000000000091e", - "0x000000000000000000000000000000000000000000000000000000000000091f", - "0x0000000000000000000000000000000000000000000000000000000000000920", - "0x0000000000000000000000000000000000000000000000000000000000000921", - "0x0000000000000000000000000000000000000000000000000000000000000922", - "0x0000000000000000000000000000000000000000000000000000000000000923", - "0x0000000000000000000000000000000000000000000000000000000000000924", - "0x0000000000000000000000000000000000000000000000000000000000000925", - "0x0000000000000000000000000000000000000000000000000000000000000926", - "0x0000000000000000000000000000000000000000000000000000000000000927", - "0x0000000000000000000000000000000000000000000000000000000000000928", - "0x0000000000000000000000000000000000000000000000000000000000000929", - "0x000000000000000000000000000000000000000000000000000000000000092a", - "0x000000000000000000000000000000000000000000000000000000000000092b", - "0x000000000000000000000000000000000000000000000000000000000000092c", - "0x000000000000000000000000000000000000000000000000000000000000092d", - "0x000000000000000000000000000000000000000000000000000000000000092e", - "0x000000000000000000000000000000000000000000000000000000000000092f", - "0x0000000000000000000000000000000000000000000000000000000000000930", - "0x0000000000000000000000000000000000000000000000000000000000000931", - "0x0000000000000000000000000000000000000000000000000000000000000932", - "0x0000000000000000000000000000000000000000000000000000000000000933", - "0x0000000000000000000000000000000000000000000000000000000000000934", - "0x0000000000000000000000000000000000000000000000000000000000000935", - "0x0000000000000000000000000000000000000000000000000000000000000936", - "0x0000000000000000000000000000000000000000000000000000000000000937", - "0x0000000000000000000000000000000000000000000000000000000000000938", - "0x0000000000000000000000000000000000000000000000000000000000000939", - "0x000000000000000000000000000000000000000000000000000000000000093a", - "0x000000000000000000000000000000000000000000000000000000000000093b", - "0x000000000000000000000000000000000000000000000000000000000000093c", - "0x000000000000000000000000000000000000000000000000000000000000093d", - "0x000000000000000000000000000000000000000000000000000000000000093e", - "0x000000000000000000000000000000000000000000000000000000000000093f", - "0x0000000000000000000000000000000000000000000000000000000000000940", - "0x0000000000000000000000000000000000000000000000000000000000000941", - "0x0000000000000000000000000000000000000000000000000000000000000942", - "0x0000000000000000000000000000000000000000000000000000000000000943", - "0x0000000000000000000000000000000000000000000000000000000000000944", - "0x0000000000000000000000000000000000000000000000000000000000000945", - "0x0000000000000000000000000000000000000000000000000000000000000946", - "0x0000000000000000000000000000000000000000000000000000000000000947", - "0x0000000000000000000000000000000000000000000000000000000000000948", - "0x0000000000000000000000000000000000000000000000000000000000000949", - "0x000000000000000000000000000000000000000000000000000000000000094a", - "0x000000000000000000000000000000000000000000000000000000000000094b", - "0x000000000000000000000000000000000000000000000000000000000000094c", - "0x000000000000000000000000000000000000000000000000000000000000094d", - "0x000000000000000000000000000000000000000000000000000000000000094e", - "0x000000000000000000000000000000000000000000000000000000000000094f", - "0x0000000000000000000000000000000000000000000000000000000000000950", - "0x0000000000000000000000000000000000000000000000000000000000000951", - "0x0000000000000000000000000000000000000000000000000000000000000952", - "0x0000000000000000000000000000000000000000000000000000000000000953", - "0x0000000000000000000000000000000000000000000000000000000000000954", - "0x0000000000000000000000000000000000000000000000000000000000000955", - "0x0000000000000000000000000000000000000000000000000000000000000956", - "0x0000000000000000000000000000000000000000000000000000000000000957", - "0x0000000000000000000000000000000000000000000000000000000000000958", - "0x0000000000000000000000000000000000000000000000000000000000000959", - "0x000000000000000000000000000000000000000000000000000000000000095a", - "0x000000000000000000000000000000000000000000000000000000000000095b", - "0x000000000000000000000000000000000000000000000000000000000000095c", - "0x000000000000000000000000000000000000000000000000000000000000095d", - "0x000000000000000000000000000000000000000000000000000000000000095e", - "0x000000000000000000000000000000000000000000000000000000000000095f", - "0x0000000000000000000000000000000000000000000000000000000000000960", - "0x0000000000000000000000000000000000000000000000000000000000000961", - "0x0000000000000000000000000000000000000000000000000000000000000962", - "0x0000000000000000000000000000000000000000000000000000000000000963", - "0x0000000000000000000000000000000000000000000000000000000000000964", - "0x0000000000000000000000000000000000000000000000000000000000000965", - "0x0000000000000000000000000000000000000000000000000000000000000966", - "0x0000000000000000000000000000000000000000000000000000000000000967", - "0x0000000000000000000000000000000000000000000000000000000000000968", - "0x0000000000000000000000000000000000000000000000000000000000000969", - "0x000000000000000000000000000000000000000000000000000000000000096a", - "0x000000000000000000000000000000000000000000000000000000000000096b", - "0x000000000000000000000000000000000000000000000000000000000000096c", - "0x000000000000000000000000000000000000000000000000000000000000096d", - "0x000000000000000000000000000000000000000000000000000000000000096e", - "0x000000000000000000000000000000000000000000000000000000000000096f", - "0x0000000000000000000000000000000000000000000000000000000000000970", - "0x0000000000000000000000000000000000000000000000000000000000000971", - "0x0000000000000000000000000000000000000000000000000000000000000972", - "0x0000000000000000000000000000000000000000000000000000000000000973", - "0x0000000000000000000000000000000000000000000000000000000000000974", - "0x0000000000000000000000000000000000000000000000000000000000000975", - "0x0000000000000000000000000000000000000000000000000000000000000976", - "0x0000000000000000000000000000000000000000000000000000000000000977", - "0x0000000000000000000000000000000000000000000000000000000000000978", - "0x0000000000000000000000000000000000000000000000000000000000000979", - "0x000000000000000000000000000000000000000000000000000000000000097a", - "0x000000000000000000000000000000000000000000000000000000000000097b", - "0x000000000000000000000000000000000000000000000000000000000000097c", - "0x000000000000000000000000000000000000000000000000000000000000097d", - "0x000000000000000000000000000000000000000000000000000000000000097e", - "0x000000000000000000000000000000000000000000000000000000000000097f", - "0x0000000000000000000000000000000000000000000000000000000000000980", - "0x0000000000000000000000000000000000000000000000000000000000000981", - "0x0000000000000000000000000000000000000000000000000000000000000982", - "0x0000000000000000000000000000000000000000000000000000000000000983", - "0x0000000000000000000000000000000000000000000000000000000000000984", - "0x0000000000000000000000000000000000000000000000000000000000000985", - "0x0000000000000000000000000000000000000000000000000000000000000986", - "0x0000000000000000000000000000000000000000000000000000000000000987", - "0x0000000000000000000000000000000000000000000000000000000000000988", - "0x0000000000000000000000000000000000000000000000000000000000000989", - "0x000000000000000000000000000000000000000000000000000000000000098a", - "0x000000000000000000000000000000000000000000000000000000000000098b", - "0x000000000000000000000000000000000000000000000000000000000000098c", - "0x000000000000000000000000000000000000000000000000000000000000098d", - "0x000000000000000000000000000000000000000000000000000000000000098e", - "0x000000000000000000000000000000000000000000000000000000000000098f", - "0x0000000000000000000000000000000000000000000000000000000000000990", - "0x0000000000000000000000000000000000000000000000000000000000000991", - "0x0000000000000000000000000000000000000000000000000000000000000992", - "0x0000000000000000000000000000000000000000000000000000000000000993", - "0x0000000000000000000000000000000000000000000000000000000000000994", - "0x0000000000000000000000000000000000000000000000000000000000000995", - "0x0000000000000000000000000000000000000000000000000000000000000996", - "0x0000000000000000000000000000000000000000000000000000000000000997", - "0x0000000000000000000000000000000000000000000000000000000000000998", - "0x0000000000000000000000000000000000000000000000000000000000000999", - "0x000000000000000000000000000000000000000000000000000000000000099a", - "0x000000000000000000000000000000000000000000000000000000000000099b", - "0x000000000000000000000000000000000000000000000000000000000000099c", - "0x000000000000000000000000000000000000000000000000000000000000099d", - "0x000000000000000000000000000000000000000000000000000000000000099e", - "0x000000000000000000000000000000000000000000000000000000000000099f", - "0x00000000000000000000000000000000000000000000000000000000000009a0", - "0x00000000000000000000000000000000000000000000000000000000000009a1", - "0x00000000000000000000000000000000000000000000000000000000000009a2", - "0x00000000000000000000000000000000000000000000000000000000000009a3", - "0x00000000000000000000000000000000000000000000000000000000000009a4", - "0x00000000000000000000000000000000000000000000000000000000000009a5", - "0x00000000000000000000000000000000000000000000000000000000000009a6", - "0x00000000000000000000000000000000000000000000000000000000000009a7", - "0x00000000000000000000000000000000000000000000000000000000000009a8", - "0x00000000000000000000000000000000000000000000000000000000000009a9", - "0x00000000000000000000000000000000000000000000000000000000000009aa", - "0x00000000000000000000000000000000000000000000000000000000000009ab", - "0x00000000000000000000000000000000000000000000000000000000000009ac", - "0x00000000000000000000000000000000000000000000000000000000000009ad", - "0x00000000000000000000000000000000000000000000000000000000000009ae", - "0x00000000000000000000000000000000000000000000000000000000000009af", - "0x00000000000000000000000000000000000000000000000000000000000009b0", - "0x00000000000000000000000000000000000000000000000000000000000009b1", - "0x00000000000000000000000000000000000000000000000000000000000009b2", - "0x00000000000000000000000000000000000000000000000000000000000009b3", - "0x00000000000000000000000000000000000000000000000000000000000009b4", - "0x00000000000000000000000000000000000000000000000000000000000009b5", - "0x00000000000000000000000000000000000000000000000000000000000009b6", - "0x00000000000000000000000000000000000000000000000000000000000009b7", - "0x00000000000000000000000000000000000000000000000000000000000009b8", - "0x00000000000000000000000000000000000000000000000000000000000009b9", - "0x00000000000000000000000000000000000000000000000000000000000009ba", - "0x00000000000000000000000000000000000000000000000000000000000009bb", - "0x00000000000000000000000000000000000000000000000000000000000009bc", - "0x00000000000000000000000000000000000000000000000000000000000009bd", - "0x00000000000000000000000000000000000000000000000000000000000009be", - "0x00000000000000000000000000000000000000000000000000000000000009bf", - "0x00000000000000000000000000000000000000000000000000000000000009c0", - "0x00000000000000000000000000000000000000000000000000000000000009c1", - "0x00000000000000000000000000000000000000000000000000000000000009c2", - "0x00000000000000000000000000000000000000000000000000000000000009c3", - "0x00000000000000000000000000000000000000000000000000000000000009c4", - "0x00000000000000000000000000000000000000000000000000000000000009c5", - "0x00000000000000000000000000000000000000000000000000000000000009c6", - "0x00000000000000000000000000000000000000000000000000000000000009c7", - "0x00000000000000000000000000000000000000000000000000000000000009c8", - "0x00000000000000000000000000000000000000000000000000000000000009c9", - "0x00000000000000000000000000000000000000000000000000000000000009ca", - "0x00000000000000000000000000000000000000000000000000000000000009cb", - "0x00000000000000000000000000000000000000000000000000000000000009cc", - "0x00000000000000000000000000000000000000000000000000000000000009cd", - "0x00000000000000000000000000000000000000000000000000000000000009ce", - "0x00000000000000000000000000000000000000000000000000000000000009cf", - "0x00000000000000000000000000000000000000000000000000000000000009d0", - "0x00000000000000000000000000000000000000000000000000000000000009d1", - "0x00000000000000000000000000000000000000000000000000000000000009d2", - "0x00000000000000000000000000000000000000000000000000000000000009d3", - "0x00000000000000000000000000000000000000000000000000000000000009d4", - "0x00000000000000000000000000000000000000000000000000000000000009d5", - "0x00000000000000000000000000000000000000000000000000000000000009d6", - "0x00000000000000000000000000000000000000000000000000000000000009d7", - "0x00000000000000000000000000000000000000000000000000000000000009d8", - "0x00000000000000000000000000000000000000000000000000000000000009d9", - "0x00000000000000000000000000000000000000000000000000000000000009da", - "0x00000000000000000000000000000000000000000000000000000000000009db" + "0x00000000000000000000000000000000000000000000000000000000000006db" ] - num_msgs = "0x0000000000000000000000000000000000000000000000000000000000000400" - num_real_msgs = "0x0000000000000000000000000000000000000000000000000000000000000400" + num_msgs = "0x0000000000000000000000000000000000000000000000000000000000000100" [inputs.previous_l1_to_l2] root = "0x0fef6d80d31109ddb56d6b3f607cbc9c0af0bff3ea0d43e8f278983c64c11f7a" diff --git a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-block-root/Prover.toml b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-block-root/Prover.toml index 6178b85674ae..150e950e8143 100644 --- a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-block-root/Prover.toml +++ b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-block-root/Prover.toml @@ -1,15 +1,15 @@ [inputs] l1_to_l2_message_frontier_hint = [ "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x19f1a0c09db4cd026f686e9c8fb45501a9fefb4eb1b4c6c328a51343a0094eeb", + "0x14e4b977b2203b70e6ee1c2456eb7114d090fe4b907f631eecd0919fed432e7d", + "0x30105bad22ddcc508b739b7c9ad87a561c569ff5cb0098a853c1c4ac21b7a037", + "0x1e20ad4181460cbfdc74ca773502c59b890f184efe300ebad895956d318422da", + "0x1434e6e2d5db1053ab8a3be58704509c799ee17e109c77f441f7bf1755400249", + "0x119f56a2e8423a7feaab49b9b5dcbadec0648dfa4096b61b6774ea33ae29dc7f", + "0x221cf368938c74e4fced9dfb2a8e37cd8a6c57d21385c249f0b5c2412341287f", + "0x2c5214dfc4d70d2619fce2a7e02ddcf380576dca42b66c9215c7d8d1ec154116", + "0x13abc9bba431e6930c169f5daeb60aedbb27d7618c7ff88b3b4ec1c6de1d6bb8", "0x0d04c63f36bd168215c9b09a227c7e8d3ad48e2f11b8202fd07c524bd30ee88f", "0x042c72d0ca208f0631ed947050258333518c26059f0a2ef041e933b1b2a6d8ad", "0x00c21235cdc5d4241fab782680421cdd99c088a3b48a740d8289d0e67b2ee5da", @@ -561,7 +561,7 @@ new_archive_sibling_path = [ accumulated_mana_used = "0x000000000000000000000000000000000000000000000000000000000006b6c0" [inputs.previous_rollups.public_inputs.constants] - vk_tree_root = "0x16b7eeebd9ec7b6b28187878be4fef52c87331b0ebf2e5719fd203a0474402b1" + vk_tree_root = "0x121ed84a634911bbb8a5e986c40e120a016a1713140a6a5ea4f8187a5e07e547" protocol_contracts_hash = "0x08d0026a4983c49638d0910859942c6648d5ea0a3a152c8a376e4983d0a003d9" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" @@ -570,8 +570,8 @@ new_archive_sibling_path = [ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000001" [inputs.previous_rollups.public_inputs.constants.l1_to_l2_tree_snapshot] - root = "0x18d6aae3ab4a271abd590cb98267825b70de1e9e5c339356e94ea5fc7feba64b" - next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000400" + root = "0x0c123047b777079e8db5f669ddaf1ffa1c7780d56b21e29f35e4772ac0f6280c" + next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000100" [inputs.previous_rollups.public_inputs.constants.global_variables] chain_id = "0x0000000000000000000000000000000000000000000000000000000000000000" @@ -642,10 +642,10 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x00000000000000000000000000000000000000000000000000000000b7d1b44c" ] state = [ - "0x0b28717d2159da0d34de0b7f4a09706445a956f0a617301bf71e71589ecab768", - "0x053ce4bbd9549525eecffc435aedb278d17f949999c5b478fadf5d4e8ea2cab3", - "0x1aa47fa350d60e62db30dda2c263dda039f207a072c63dc7175e4830875d0a6b", - "0x2f11a2454b729aea70a6e8d844bcb77e8089ac57707bfcc4417379e7e82f11d2" + "0x088fb5e466f2db1970586659b8221b4b6937610c6721f0fc77fee62ddc0e108a", + "0x0f7e718e17954d1b062a7c75da12ab807115e00a0916158e7744ad48e4b1bec7", + "0x10af0e6f8af267e6dc03ada1f5e07556be6fc336af4741127ae2226e3ea248c2", + "0x19d19153f83049828fdab971c49c1d96de5a2667d12509f8d7df3d53b7e22f3d" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000002" squeeze_mode = false @@ -656,7 +656,7 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x1fc39d0a428c8536dbca551ea79848acf67d89d090fd8c643c7d90f2e8f32340", "0x12ce5a49a1ceca53ada7bee003f929bbd65abaa74e8072a81f304c2c96c44e31", "0x276f60617ee2c3b406af56bfe9216a7d99be56ad316ff557cee0446f08e14135", - "0x14f9430ecc9a29b27e8af3154b4cbb75bf0f995c16737ed125019dea77ab3f62", + "0x1800b90c40fa738a64bbc71d11bdc768c7c43a4e8876d6e5bf203355830af7e2", "0x25f9ac8d47a2dbcfb13b88ea474667fd2854bf5c901f2f698266864ffe7c21ad", "0x0db3ab63c7556f8724d86819156701855018cc79140a052893d67fb67c99c1f1", "0x2a5c50782880ea145b4f04af31f7df9ef0cde48fab69c958aef179c68e836f62" @@ -1273,7 +1273,7 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 accumulated_mana_used = "0x0000000000000000000000000000000000000000000000000000000000000000" [inputs.previous_rollups.public_inputs.constants] - vk_tree_root = "0x16b7eeebd9ec7b6b28187878be4fef52c87331b0ebf2e5719fd203a0474402b1" + vk_tree_root = "0x121ed84a634911bbb8a5e986c40e120a016a1713140a6a5ea4f8187a5e07e547" protocol_contracts_hash = "0x08d0026a4983c49638d0910859942c6648d5ea0a3a152c8a376e4983d0a003d9" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" @@ -1282,8 +1282,8 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000001" [inputs.previous_rollups.public_inputs.constants.l1_to_l2_tree_snapshot] - root = "0x18d6aae3ab4a271abd590cb98267825b70de1e9e5c339356e94ea5fc7feba64b" - next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000400" + root = "0x0c123047b777079e8db5f669ddaf1ffa1c7780d56b21e29f35e4772ac0f6280c" + next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000100" [inputs.previous_rollups.public_inputs.constants.global_variables] chain_id = "0x0000000000000000000000000000000000000000000000000000000000000000" @@ -1336,10 +1336,10 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x00000000000000000000000000000000000000000000000000000000b7d1b44c" ] state = [ - "0x0b28717d2159da0d34de0b7f4a09706445a956f0a617301bf71e71589ecab768", - "0x053ce4bbd9549525eecffc435aedb278d17f949999c5b478fadf5d4e8ea2cab3", - "0x1aa47fa350d60e62db30dda2c263dda039f207a072c63dc7175e4830875d0a6b", - "0x2f11a2454b729aea70a6e8d844bcb77e8089ac57707bfcc4417379e7e82f11d2" + "0x088fb5e466f2db1970586659b8221b4b6937610c6721f0fc77fee62ddc0e108a", + "0x0f7e718e17954d1b062a7c75da12ab807115e00a0916158e7744ad48e4b1bec7", + "0x10af0e6f8af267e6dc03ada1f5e07556be6fc336af4741127ae2226e3ea248c2", + "0x19d19153f83049828fdab971c49c1d96de5a2667d12509f8d7df3d53b7e22f3d" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000002" squeeze_mode = false @@ -1354,10 +1354,10 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x00000000000000000000000000000000000000000000000000000000b7e5c34d" ] state = [ - "0x2233552bb5afadfc9f03a2defae5feac8aa0a9cdb8f9cb1d0efa87cd7c6e47f4", - "0x063a047a72fac3a9805c058ffbe7867fc12df19c22787d42fb4eb0f450488752", - "0x256c89e495065074acde8c5d2bf95efd43265acc6189401c1d5863f2d4d8bbee", - "0x21d85c62f2997e0675683939a3eb7953d8e20181432e6495624c5523e4a8f558" + "0x20c03c5adcca732cdaeb6841c82e0c2d54035c0d29993e0847ab34afef3b2f6f", + "0x18809eda7d2f09edd946e0e493064df97cab4fca9b18f4c20f253159cbbf3d19", + "0x14fd3812b040f1f2f48384660dd40376845bbf04492b68238272899f8834dbab", + "0x2589b5e3cc7741b45ea207db604c552b4a35c6013079ed98d3a59409c4b88572" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000001" squeeze_mode = false @@ -1367,7 +1367,7 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 sibling_path = [ "0x10b6730f1d1e9c6bf8d7c4b42b64b40d2603e3ae6ddbd464c3d8fcfb9e06e6d4", "0x19f1a0c09db4cd026f686e9c8fb45501a9fefb4eb1b4c6c328a51343a0094eeb", - "0x07d1c896dc593088cd3771dbfe5c285e01b304923f1dd76afa5b3421ee691a95", + "0x1711a59f08607dbf1f5750a75220be77ca425b214c4b09c3b6a89dba28f7d71e", "0x20f1c701d84b280c9f80a517272153688a9e1b92166d1000cfe7c829b7c25f69", "0x25f9ac8d47a2dbcfb13b88ea474667fd2854bf5c901f2f698266864ffe7c21ad", "0x0db3ab63c7556f8724d86819156701855018cc79140a052893d67fb67c99c1f1", @@ -1751,778 +1751,9 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x00000000000000000000000000000000000000000000000000000000000006d8", "0x00000000000000000000000000000000000000000000000000000000000006d9", "0x00000000000000000000000000000000000000000000000000000000000006da", - "0x00000000000000000000000000000000000000000000000000000000000006db", - "0x00000000000000000000000000000000000000000000000000000000000006dc", - "0x00000000000000000000000000000000000000000000000000000000000006dd", - "0x00000000000000000000000000000000000000000000000000000000000006de", - "0x00000000000000000000000000000000000000000000000000000000000006df", - "0x00000000000000000000000000000000000000000000000000000000000006e0", - "0x00000000000000000000000000000000000000000000000000000000000006e1", - "0x00000000000000000000000000000000000000000000000000000000000006e2", - "0x00000000000000000000000000000000000000000000000000000000000006e3", - "0x00000000000000000000000000000000000000000000000000000000000006e4", - "0x00000000000000000000000000000000000000000000000000000000000006e5", - "0x00000000000000000000000000000000000000000000000000000000000006e6", - "0x00000000000000000000000000000000000000000000000000000000000006e7", - "0x00000000000000000000000000000000000000000000000000000000000006e8", - "0x00000000000000000000000000000000000000000000000000000000000006e9", - "0x00000000000000000000000000000000000000000000000000000000000006ea", - "0x00000000000000000000000000000000000000000000000000000000000006eb", - "0x00000000000000000000000000000000000000000000000000000000000006ec", - "0x00000000000000000000000000000000000000000000000000000000000006ed", - "0x00000000000000000000000000000000000000000000000000000000000006ee", - "0x00000000000000000000000000000000000000000000000000000000000006ef", - "0x00000000000000000000000000000000000000000000000000000000000006f0", - "0x00000000000000000000000000000000000000000000000000000000000006f1", - "0x00000000000000000000000000000000000000000000000000000000000006f2", - "0x00000000000000000000000000000000000000000000000000000000000006f3", - "0x00000000000000000000000000000000000000000000000000000000000006f4", - "0x00000000000000000000000000000000000000000000000000000000000006f5", - "0x00000000000000000000000000000000000000000000000000000000000006f6", - "0x00000000000000000000000000000000000000000000000000000000000006f7", - "0x00000000000000000000000000000000000000000000000000000000000006f8", - "0x00000000000000000000000000000000000000000000000000000000000006f9", - "0x00000000000000000000000000000000000000000000000000000000000006fa", - "0x00000000000000000000000000000000000000000000000000000000000006fb", - "0x00000000000000000000000000000000000000000000000000000000000006fc", - "0x00000000000000000000000000000000000000000000000000000000000006fd", - "0x00000000000000000000000000000000000000000000000000000000000006fe", - "0x00000000000000000000000000000000000000000000000000000000000006ff", - "0x0000000000000000000000000000000000000000000000000000000000000700", - "0x0000000000000000000000000000000000000000000000000000000000000701", - "0x0000000000000000000000000000000000000000000000000000000000000702", - "0x0000000000000000000000000000000000000000000000000000000000000703", - "0x0000000000000000000000000000000000000000000000000000000000000704", - "0x0000000000000000000000000000000000000000000000000000000000000705", - "0x0000000000000000000000000000000000000000000000000000000000000706", - "0x0000000000000000000000000000000000000000000000000000000000000707", - "0x0000000000000000000000000000000000000000000000000000000000000708", - "0x0000000000000000000000000000000000000000000000000000000000000709", - "0x000000000000000000000000000000000000000000000000000000000000070a", - "0x000000000000000000000000000000000000000000000000000000000000070b", - "0x000000000000000000000000000000000000000000000000000000000000070c", - "0x000000000000000000000000000000000000000000000000000000000000070d", - "0x000000000000000000000000000000000000000000000000000000000000070e", - "0x000000000000000000000000000000000000000000000000000000000000070f", - "0x0000000000000000000000000000000000000000000000000000000000000710", - "0x0000000000000000000000000000000000000000000000000000000000000711", - "0x0000000000000000000000000000000000000000000000000000000000000712", - "0x0000000000000000000000000000000000000000000000000000000000000713", - "0x0000000000000000000000000000000000000000000000000000000000000714", - "0x0000000000000000000000000000000000000000000000000000000000000715", - "0x0000000000000000000000000000000000000000000000000000000000000716", - "0x0000000000000000000000000000000000000000000000000000000000000717", - "0x0000000000000000000000000000000000000000000000000000000000000718", - "0x0000000000000000000000000000000000000000000000000000000000000719", - "0x000000000000000000000000000000000000000000000000000000000000071a", - "0x000000000000000000000000000000000000000000000000000000000000071b", - "0x000000000000000000000000000000000000000000000000000000000000071c", - "0x000000000000000000000000000000000000000000000000000000000000071d", - "0x000000000000000000000000000000000000000000000000000000000000071e", - "0x000000000000000000000000000000000000000000000000000000000000071f", - "0x0000000000000000000000000000000000000000000000000000000000000720", - "0x0000000000000000000000000000000000000000000000000000000000000721", - "0x0000000000000000000000000000000000000000000000000000000000000722", - "0x0000000000000000000000000000000000000000000000000000000000000723", - "0x0000000000000000000000000000000000000000000000000000000000000724", - "0x0000000000000000000000000000000000000000000000000000000000000725", - "0x0000000000000000000000000000000000000000000000000000000000000726", - "0x0000000000000000000000000000000000000000000000000000000000000727", - "0x0000000000000000000000000000000000000000000000000000000000000728", - "0x0000000000000000000000000000000000000000000000000000000000000729", - "0x000000000000000000000000000000000000000000000000000000000000072a", - "0x000000000000000000000000000000000000000000000000000000000000072b", - "0x000000000000000000000000000000000000000000000000000000000000072c", - "0x000000000000000000000000000000000000000000000000000000000000072d", - "0x000000000000000000000000000000000000000000000000000000000000072e", - "0x000000000000000000000000000000000000000000000000000000000000072f", - "0x0000000000000000000000000000000000000000000000000000000000000730", - "0x0000000000000000000000000000000000000000000000000000000000000731", - "0x0000000000000000000000000000000000000000000000000000000000000732", - "0x0000000000000000000000000000000000000000000000000000000000000733", - "0x0000000000000000000000000000000000000000000000000000000000000734", - "0x0000000000000000000000000000000000000000000000000000000000000735", - "0x0000000000000000000000000000000000000000000000000000000000000736", - "0x0000000000000000000000000000000000000000000000000000000000000737", - "0x0000000000000000000000000000000000000000000000000000000000000738", - "0x0000000000000000000000000000000000000000000000000000000000000739", - "0x000000000000000000000000000000000000000000000000000000000000073a", - "0x000000000000000000000000000000000000000000000000000000000000073b", - "0x000000000000000000000000000000000000000000000000000000000000073c", - "0x000000000000000000000000000000000000000000000000000000000000073d", - "0x000000000000000000000000000000000000000000000000000000000000073e", - "0x000000000000000000000000000000000000000000000000000000000000073f", - "0x0000000000000000000000000000000000000000000000000000000000000740", - "0x0000000000000000000000000000000000000000000000000000000000000741", - "0x0000000000000000000000000000000000000000000000000000000000000742", - "0x0000000000000000000000000000000000000000000000000000000000000743", - "0x0000000000000000000000000000000000000000000000000000000000000744", - "0x0000000000000000000000000000000000000000000000000000000000000745", - "0x0000000000000000000000000000000000000000000000000000000000000746", - "0x0000000000000000000000000000000000000000000000000000000000000747", - "0x0000000000000000000000000000000000000000000000000000000000000748", - "0x0000000000000000000000000000000000000000000000000000000000000749", - "0x000000000000000000000000000000000000000000000000000000000000074a", - "0x000000000000000000000000000000000000000000000000000000000000074b", - "0x000000000000000000000000000000000000000000000000000000000000074c", - "0x000000000000000000000000000000000000000000000000000000000000074d", - "0x000000000000000000000000000000000000000000000000000000000000074e", - "0x000000000000000000000000000000000000000000000000000000000000074f", - "0x0000000000000000000000000000000000000000000000000000000000000750", - "0x0000000000000000000000000000000000000000000000000000000000000751", - "0x0000000000000000000000000000000000000000000000000000000000000752", - "0x0000000000000000000000000000000000000000000000000000000000000753", - "0x0000000000000000000000000000000000000000000000000000000000000754", - "0x0000000000000000000000000000000000000000000000000000000000000755", - "0x0000000000000000000000000000000000000000000000000000000000000756", - "0x0000000000000000000000000000000000000000000000000000000000000757", - "0x0000000000000000000000000000000000000000000000000000000000000758", - "0x0000000000000000000000000000000000000000000000000000000000000759", - "0x000000000000000000000000000000000000000000000000000000000000075a", - "0x000000000000000000000000000000000000000000000000000000000000075b", - "0x000000000000000000000000000000000000000000000000000000000000075c", - "0x000000000000000000000000000000000000000000000000000000000000075d", - "0x000000000000000000000000000000000000000000000000000000000000075e", - "0x000000000000000000000000000000000000000000000000000000000000075f", - "0x0000000000000000000000000000000000000000000000000000000000000760", - "0x0000000000000000000000000000000000000000000000000000000000000761", - "0x0000000000000000000000000000000000000000000000000000000000000762", - "0x0000000000000000000000000000000000000000000000000000000000000763", - "0x0000000000000000000000000000000000000000000000000000000000000764", - "0x0000000000000000000000000000000000000000000000000000000000000765", - "0x0000000000000000000000000000000000000000000000000000000000000766", - "0x0000000000000000000000000000000000000000000000000000000000000767", - "0x0000000000000000000000000000000000000000000000000000000000000768", - "0x0000000000000000000000000000000000000000000000000000000000000769", - "0x000000000000000000000000000000000000000000000000000000000000076a", - "0x000000000000000000000000000000000000000000000000000000000000076b", - "0x000000000000000000000000000000000000000000000000000000000000076c", - "0x000000000000000000000000000000000000000000000000000000000000076d", - "0x000000000000000000000000000000000000000000000000000000000000076e", - "0x000000000000000000000000000000000000000000000000000000000000076f", - "0x0000000000000000000000000000000000000000000000000000000000000770", - "0x0000000000000000000000000000000000000000000000000000000000000771", - "0x0000000000000000000000000000000000000000000000000000000000000772", - "0x0000000000000000000000000000000000000000000000000000000000000773", - "0x0000000000000000000000000000000000000000000000000000000000000774", - "0x0000000000000000000000000000000000000000000000000000000000000775", - "0x0000000000000000000000000000000000000000000000000000000000000776", - "0x0000000000000000000000000000000000000000000000000000000000000777", - "0x0000000000000000000000000000000000000000000000000000000000000778", - "0x0000000000000000000000000000000000000000000000000000000000000779", - "0x000000000000000000000000000000000000000000000000000000000000077a", - "0x000000000000000000000000000000000000000000000000000000000000077b", - "0x000000000000000000000000000000000000000000000000000000000000077c", - "0x000000000000000000000000000000000000000000000000000000000000077d", - "0x000000000000000000000000000000000000000000000000000000000000077e", - "0x000000000000000000000000000000000000000000000000000000000000077f", - "0x0000000000000000000000000000000000000000000000000000000000000780", - "0x0000000000000000000000000000000000000000000000000000000000000781", - "0x0000000000000000000000000000000000000000000000000000000000000782", - "0x0000000000000000000000000000000000000000000000000000000000000783", - "0x0000000000000000000000000000000000000000000000000000000000000784", - "0x0000000000000000000000000000000000000000000000000000000000000785", - "0x0000000000000000000000000000000000000000000000000000000000000786", - "0x0000000000000000000000000000000000000000000000000000000000000787", - "0x0000000000000000000000000000000000000000000000000000000000000788", - "0x0000000000000000000000000000000000000000000000000000000000000789", - "0x000000000000000000000000000000000000000000000000000000000000078a", - "0x000000000000000000000000000000000000000000000000000000000000078b", - "0x000000000000000000000000000000000000000000000000000000000000078c", - "0x000000000000000000000000000000000000000000000000000000000000078d", - "0x000000000000000000000000000000000000000000000000000000000000078e", - "0x000000000000000000000000000000000000000000000000000000000000078f", - "0x0000000000000000000000000000000000000000000000000000000000000790", - "0x0000000000000000000000000000000000000000000000000000000000000791", - "0x0000000000000000000000000000000000000000000000000000000000000792", - "0x0000000000000000000000000000000000000000000000000000000000000793", - "0x0000000000000000000000000000000000000000000000000000000000000794", - "0x0000000000000000000000000000000000000000000000000000000000000795", - "0x0000000000000000000000000000000000000000000000000000000000000796", - "0x0000000000000000000000000000000000000000000000000000000000000797", - "0x0000000000000000000000000000000000000000000000000000000000000798", - "0x0000000000000000000000000000000000000000000000000000000000000799", - "0x000000000000000000000000000000000000000000000000000000000000079a", - "0x000000000000000000000000000000000000000000000000000000000000079b", - "0x000000000000000000000000000000000000000000000000000000000000079c", - "0x000000000000000000000000000000000000000000000000000000000000079d", - "0x000000000000000000000000000000000000000000000000000000000000079e", - "0x000000000000000000000000000000000000000000000000000000000000079f", - "0x00000000000000000000000000000000000000000000000000000000000007a0", - "0x00000000000000000000000000000000000000000000000000000000000007a1", - "0x00000000000000000000000000000000000000000000000000000000000007a2", - "0x00000000000000000000000000000000000000000000000000000000000007a3", - "0x00000000000000000000000000000000000000000000000000000000000007a4", - "0x00000000000000000000000000000000000000000000000000000000000007a5", - "0x00000000000000000000000000000000000000000000000000000000000007a6", - "0x00000000000000000000000000000000000000000000000000000000000007a7", - "0x00000000000000000000000000000000000000000000000000000000000007a8", - "0x00000000000000000000000000000000000000000000000000000000000007a9", - "0x00000000000000000000000000000000000000000000000000000000000007aa", - "0x00000000000000000000000000000000000000000000000000000000000007ab", - "0x00000000000000000000000000000000000000000000000000000000000007ac", - "0x00000000000000000000000000000000000000000000000000000000000007ad", - "0x00000000000000000000000000000000000000000000000000000000000007ae", - "0x00000000000000000000000000000000000000000000000000000000000007af", - "0x00000000000000000000000000000000000000000000000000000000000007b0", - "0x00000000000000000000000000000000000000000000000000000000000007b1", - "0x00000000000000000000000000000000000000000000000000000000000007b2", - "0x00000000000000000000000000000000000000000000000000000000000007b3", - "0x00000000000000000000000000000000000000000000000000000000000007b4", - "0x00000000000000000000000000000000000000000000000000000000000007b5", - "0x00000000000000000000000000000000000000000000000000000000000007b6", - "0x00000000000000000000000000000000000000000000000000000000000007b7", - "0x00000000000000000000000000000000000000000000000000000000000007b8", - "0x00000000000000000000000000000000000000000000000000000000000007b9", - "0x00000000000000000000000000000000000000000000000000000000000007ba", - "0x00000000000000000000000000000000000000000000000000000000000007bb", - "0x00000000000000000000000000000000000000000000000000000000000007bc", - "0x00000000000000000000000000000000000000000000000000000000000007bd", - "0x00000000000000000000000000000000000000000000000000000000000007be", - "0x00000000000000000000000000000000000000000000000000000000000007bf", - "0x00000000000000000000000000000000000000000000000000000000000007c0", - "0x00000000000000000000000000000000000000000000000000000000000007c1", - "0x00000000000000000000000000000000000000000000000000000000000007c2", - "0x00000000000000000000000000000000000000000000000000000000000007c3", - "0x00000000000000000000000000000000000000000000000000000000000007c4", - "0x00000000000000000000000000000000000000000000000000000000000007c5", - "0x00000000000000000000000000000000000000000000000000000000000007c6", - "0x00000000000000000000000000000000000000000000000000000000000007c7", - "0x00000000000000000000000000000000000000000000000000000000000007c8", - "0x00000000000000000000000000000000000000000000000000000000000007c9", - "0x00000000000000000000000000000000000000000000000000000000000007ca", - "0x00000000000000000000000000000000000000000000000000000000000007cb", - "0x00000000000000000000000000000000000000000000000000000000000007cc", - "0x00000000000000000000000000000000000000000000000000000000000007cd", - "0x00000000000000000000000000000000000000000000000000000000000007ce", - "0x00000000000000000000000000000000000000000000000000000000000007cf", - "0x00000000000000000000000000000000000000000000000000000000000007d0", - "0x00000000000000000000000000000000000000000000000000000000000007d1", - "0x00000000000000000000000000000000000000000000000000000000000007d2", - "0x00000000000000000000000000000000000000000000000000000000000007d3", - "0x00000000000000000000000000000000000000000000000000000000000007d4", - "0x00000000000000000000000000000000000000000000000000000000000007d5", - "0x00000000000000000000000000000000000000000000000000000000000007d6", - "0x00000000000000000000000000000000000000000000000000000000000007d7", - "0x00000000000000000000000000000000000000000000000000000000000007d8", - "0x00000000000000000000000000000000000000000000000000000000000007d9", - "0x00000000000000000000000000000000000000000000000000000000000007da", - "0x00000000000000000000000000000000000000000000000000000000000007db", - "0x00000000000000000000000000000000000000000000000000000000000007dc", - "0x00000000000000000000000000000000000000000000000000000000000007dd", - "0x00000000000000000000000000000000000000000000000000000000000007de", - "0x00000000000000000000000000000000000000000000000000000000000007df", - "0x00000000000000000000000000000000000000000000000000000000000007e0", - "0x00000000000000000000000000000000000000000000000000000000000007e1", - "0x00000000000000000000000000000000000000000000000000000000000007e2", - "0x00000000000000000000000000000000000000000000000000000000000007e3", - "0x00000000000000000000000000000000000000000000000000000000000007e4", - "0x00000000000000000000000000000000000000000000000000000000000007e5", - "0x00000000000000000000000000000000000000000000000000000000000007e6", - "0x00000000000000000000000000000000000000000000000000000000000007e7", - "0x00000000000000000000000000000000000000000000000000000000000007e8", - "0x00000000000000000000000000000000000000000000000000000000000007e9", - "0x00000000000000000000000000000000000000000000000000000000000007ea", - "0x00000000000000000000000000000000000000000000000000000000000007eb", - "0x00000000000000000000000000000000000000000000000000000000000007ec", - "0x00000000000000000000000000000000000000000000000000000000000007ed", - "0x00000000000000000000000000000000000000000000000000000000000007ee", - "0x00000000000000000000000000000000000000000000000000000000000007ef", - "0x00000000000000000000000000000000000000000000000000000000000007f0", - "0x00000000000000000000000000000000000000000000000000000000000007f1", - "0x00000000000000000000000000000000000000000000000000000000000007f2", - "0x00000000000000000000000000000000000000000000000000000000000007f3", - "0x00000000000000000000000000000000000000000000000000000000000007f4", - "0x00000000000000000000000000000000000000000000000000000000000007f5", - "0x00000000000000000000000000000000000000000000000000000000000007f6", - "0x00000000000000000000000000000000000000000000000000000000000007f7", - "0x00000000000000000000000000000000000000000000000000000000000007f8", - "0x00000000000000000000000000000000000000000000000000000000000007f9", - "0x00000000000000000000000000000000000000000000000000000000000007fa", - "0x00000000000000000000000000000000000000000000000000000000000007fb", - "0x00000000000000000000000000000000000000000000000000000000000007fc", - "0x00000000000000000000000000000000000000000000000000000000000007fd", - "0x00000000000000000000000000000000000000000000000000000000000007fe", - "0x00000000000000000000000000000000000000000000000000000000000007ff", - "0x0000000000000000000000000000000000000000000000000000000000000800", - "0x0000000000000000000000000000000000000000000000000000000000000801", - "0x0000000000000000000000000000000000000000000000000000000000000802", - "0x0000000000000000000000000000000000000000000000000000000000000803", - "0x0000000000000000000000000000000000000000000000000000000000000804", - "0x0000000000000000000000000000000000000000000000000000000000000805", - "0x0000000000000000000000000000000000000000000000000000000000000806", - "0x0000000000000000000000000000000000000000000000000000000000000807", - "0x0000000000000000000000000000000000000000000000000000000000000808", - "0x0000000000000000000000000000000000000000000000000000000000000809", - "0x000000000000000000000000000000000000000000000000000000000000080a", - "0x000000000000000000000000000000000000000000000000000000000000080b", - "0x000000000000000000000000000000000000000000000000000000000000080c", - "0x000000000000000000000000000000000000000000000000000000000000080d", - "0x000000000000000000000000000000000000000000000000000000000000080e", - "0x000000000000000000000000000000000000000000000000000000000000080f", - "0x0000000000000000000000000000000000000000000000000000000000000810", - "0x0000000000000000000000000000000000000000000000000000000000000811", - "0x0000000000000000000000000000000000000000000000000000000000000812", - "0x0000000000000000000000000000000000000000000000000000000000000813", - "0x0000000000000000000000000000000000000000000000000000000000000814", - "0x0000000000000000000000000000000000000000000000000000000000000815", - "0x0000000000000000000000000000000000000000000000000000000000000816", - "0x0000000000000000000000000000000000000000000000000000000000000817", - "0x0000000000000000000000000000000000000000000000000000000000000818", - "0x0000000000000000000000000000000000000000000000000000000000000819", - "0x000000000000000000000000000000000000000000000000000000000000081a", - "0x000000000000000000000000000000000000000000000000000000000000081b", - "0x000000000000000000000000000000000000000000000000000000000000081c", - "0x000000000000000000000000000000000000000000000000000000000000081d", - "0x000000000000000000000000000000000000000000000000000000000000081e", - "0x000000000000000000000000000000000000000000000000000000000000081f", - "0x0000000000000000000000000000000000000000000000000000000000000820", - "0x0000000000000000000000000000000000000000000000000000000000000821", - "0x0000000000000000000000000000000000000000000000000000000000000822", - "0x0000000000000000000000000000000000000000000000000000000000000823", - "0x0000000000000000000000000000000000000000000000000000000000000824", - "0x0000000000000000000000000000000000000000000000000000000000000825", - "0x0000000000000000000000000000000000000000000000000000000000000826", - "0x0000000000000000000000000000000000000000000000000000000000000827", - "0x0000000000000000000000000000000000000000000000000000000000000828", - "0x0000000000000000000000000000000000000000000000000000000000000829", - "0x000000000000000000000000000000000000000000000000000000000000082a", - "0x000000000000000000000000000000000000000000000000000000000000082b", - "0x000000000000000000000000000000000000000000000000000000000000082c", - "0x000000000000000000000000000000000000000000000000000000000000082d", - "0x000000000000000000000000000000000000000000000000000000000000082e", - "0x000000000000000000000000000000000000000000000000000000000000082f", - "0x0000000000000000000000000000000000000000000000000000000000000830", - "0x0000000000000000000000000000000000000000000000000000000000000831", - "0x0000000000000000000000000000000000000000000000000000000000000832", - "0x0000000000000000000000000000000000000000000000000000000000000833", - "0x0000000000000000000000000000000000000000000000000000000000000834", - "0x0000000000000000000000000000000000000000000000000000000000000835", - "0x0000000000000000000000000000000000000000000000000000000000000836", - "0x0000000000000000000000000000000000000000000000000000000000000837", - "0x0000000000000000000000000000000000000000000000000000000000000838", - "0x0000000000000000000000000000000000000000000000000000000000000839", - "0x000000000000000000000000000000000000000000000000000000000000083a", - "0x000000000000000000000000000000000000000000000000000000000000083b", - "0x000000000000000000000000000000000000000000000000000000000000083c", - "0x000000000000000000000000000000000000000000000000000000000000083d", - "0x000000000000000000000000000000000000000000000000000000000000083e", - "0x000000000000000000000000000000000000000000000000000000000000083f", - "0x0000000000000000000000000000000000000000000000000000000000000840", - "0x0000000000000000000000000000000000000000000000000000000000000841", - "0x0000000000000000000000000000000000000000000000000000000000000842", - "0x0000000000000000000000000000000000000000000000000000000000000843", - "0x0000000000000000000000000000000000000000000000000000000000000844", - "0x0000000000000000000000000000000000000000000000000000000000000845", - "0x0000000000000000000000000000000000000000000000000000000000000846", - "0x0000000000000000000000000000000000000000000000000000000000000847", - "0x0000000000000000000000000000000000000000000000000000000000000848", - "0x0000000000000000000000000000000000000000000000000000000000000849", - "0x000000000000000000000000000000000000000000000000000000000000084a", - "0x000000000000000000000000000000000000000000000000000000000000084b", - "0x000000000000000000000000000000000000000000000000000000000000084c", - "0x000000000000000000000000000000000000000000000000000000000000084d", - "0x000000000000000000000000000000000000000000000000000000000000084e", - "0x000000000000000000000000000000000000000000000000000000000000084f", - "0x0000000000000000000000000000000000000000000000000000000000000850", - "0x0000000000000000000000000000000000000000000000000000000000000851", - "0x0000000000000000000000000000000000000000000000000000000000000852", - "0x0000000000000000000000000000000000000000000000000000000000000853", - "0x0000000000000000000000000000000000000000000000000000000000000854", - "0x0000000000000000000000000000000000000000000000000000000000000855", - "0x0000000000000000000000000000000000000000000000000000000000000856", - "0x0000000000000000000000000000000000000000000000000000000000000857", - "0x0000000000000000000000000000000000000000000000000000000000000858", - "0x0000000000000000000000000000000000000000000000000000000000000859", - "0x000000000000000000000000000000000000000000000000000000000000085a", - "0x000000000000000000000000000000000000000000000000000000000000085b", - "0x000000000000000000000000000000000000000000000000000000000000085c", - "0x000000000000000000000000000000000000000000000000000000000000085d", - "0x000000000000000000000000000000000000000000000000000000000000085e", - "0x000000000000000000000000000000000000000000000000000000000000085f", - "0x0000000000000000000000000000000000000000000000000000000000000860", - "0x0000000000000000000000000000000000000000000000000000000000000861", - "0x0000000000000000000000000000000000000000000000000000000000000862", - "0x0000000000000000000000000000000000000000000000000000000000000863", - "0x0000000000000000000000000000000000000000000000000000000000000864", - "0x0000000000000000000000000000000000000000000000000000000000000865", - "0x0000000000000000000000000000000000000000000000000000000000000866", - "0x0000000000000000000000000000000000000000000000000000000000000867", - "0x0000000000000000000000000000000000000000000000000000000000000868", - "0x0000000000000000000000000000000000000000000000000000000000000869", - "0x000000000000000000000000000000000000000000000000000000000000086a", - "0x000000000000000000000000000000000000000000000000000000000000086b", - "0x000000000000000000000000000000000000000000000000000000000000086c", - "0x000000000000000000000000000000000000000000000000000000000000086d", - "0x000000000000000000000000000000000000000000000000000000000000086e", - "0x000000000000000000000000000000000000000000000000000000000000086f", - "0x0000000000000000000000000000000000000000000000000000000000000870", - "0x0000000000000000000000000000000000000000000000000000000000000871", - "0x0000000000000000000000000000000000000000000000000000000000000872", - "0x0000000000000000000000000000000000000000000000000000000000000873", - "0x0000000000000000000000000000000000000000000000000000000000000874", - "0x0000000000000000000000000000000000000000000000000000000000000875", - "0x0000000000000000000000000000000000000000000000000000000000000876", - "0x0000000000000000000000000000000000000000000000000000000000000877", - "0x0000000000000000000000000000000000000000000000000000000000000878", - "0x0000000000000000000000000000000000000000000000000000000000000879", - "0x000000000000000000000000000000000000000000000000000000000000087a", - "0x000000000000000000000000000000000000000000000000000000000000087b", - "0x000000000000000000000000000000000000000000000000000000000000087c", - "0x000000000000000000000000000000000000000000000000000000000000087d", - "0x000000000000000000000000000000000000000000000000000000000000087e", - "0x000000000000000000000000000000000000000000000000000000000000087f", - "0x0000000000000000000000000000000000000000000000000000000000000880", - "0x0000000000000000000000000000000000000000000000000000000000000881", - "0x0000000000000000000000000000000000000000000000000000000000000882", - "0x0000000000000000000000000000000000000000000000000000000000000883", - "0x0000000000000000000000000000000000000000000000000000000000000884", - "0x0000000000000000000000000000000000000000000000000000000000000885", - "0x0000000000000000000000000000000000000000000000000000000000000886", - "0x0000000000000000000000000000000000000000000000000000000000000887", - "0x0000000000000000000000000000000000000000000000000000000000000888", - "0x0000000000000000000000000000000000000000000000000000000000000889", - "0x000000000000000000000000000000000000000000000000000000000000088a", - "0x000000000000000000000000000000000000000000000000000000000000088b", - "0x000000000000000000000000000000000000000000000000000000000000088c", - "0x000000000000000000000000000000000000000000000000000000000000088d", - "0x000000000000000000000000000000000000000000000000000000000000088e", - "0x000000000000000000000000000000000000000000000000000000000000088f", - "0x0000000000000000000000000000000000000000000000000000000000000890", - "0x0000000000000000000000000000000000000000000000000000000000000891", - "0x0000000000000000000000000000000000000000000000000000000000000892", - "0x0000000000000000000000000000000000000000000000000000000000000893", - "0x0000000000000000000000000000000000000000000000000000000000000894", - "0x0000000000000000000000000000000000000000000000000000000000000895", - "0x0000000000000000000000000000000000000000000000000000000000000896", - "0x0000000000000000000000000000000000000000000000000000000000000897", - "0x0000000000000000000000000000000000000000000000000000000000000898", - "0x0000000000000000000000000000000000000000000000000000000000000899", - "0x000000000000000000000000000000000000000000000000000000000000089a", - "0x000000000000000000000000000000000000000000000000000000000000089b", - "0x000000000000000000000000000000000000000000000000000000000000089c", - "0x000000000000000000000000000000000000000000000000000000000000089d", - "0x000000000000000000000000000000000000000000000000000000000000089e", - "0x000000000000000000000000000000000000000000000000000000000000089f", - "0x00000000000000000000000000000000000000000000000000000000000008a0", - "0x00000000000000000000000000000000000000000000000000000000000008a1", - "0x00000000000000000000000000000000000000000000000000000000000008a2", - "0x00000000000000000000000000000000000000000000000000000000000008a3", - "0x00000000000000000000000000000000000000000000000000000000000008a4", - "0x00000000000000000000000000000000000000000000000000000000000008a5", - "0x00000000000000000000000000000000000000000000000000000000000008a6", - "0x00000000000000000000000000000000000000000000000000000000000008a7", - "0x00000000000000000000000000000000000000000000000000000000000008a8", - "0x00000000000000000000000000000000000000000000000000000000000008a9", - "0x00000000000000000000000000000000000000000000000000000000000008aa", - "0x00000000000000000000000000000000000000000000000000000000000008ab", - "0x00000000000000000000000000000000000000000000000000000000000008ac", - "0x00000000000000000000000000000000000000000000000000000000000008ad", - "0x00000000000000000000000000000000000000000000000000000000000008ae", - "0x00000000000000000000000000000000000000000000000000000000000008af", - "0x00000000000000000000000000000000000000000000000000000000000008b0", - "0x00000000000000000000000000000000000000000000000000000000000008b1", - "0x00000000000000000000000000000000000000000000000000000000000008b2", - "0x00000000000000000000000000000000000000000000000000000000000008b3", - "0x00000000000000000000000000000000000000000000000000000000000008b4", - "0x00000000000000000000000000000000000000000000000000000000000008b5", - "0x00000000000000000000000000000000000000000000000000000000000008b6", - "0x00000000000000000000000000000000000000000000000000000000000008b7", - "0x00000000000000000000000000000000000000000000000000000000000008b8", - "0x00000000000000000000000000000000000000000000000000000000000008b9", - "0x00000000000000000000000000000000000000000000000000000000000008ba", - "0x00000000000000000000000000000000000000000000000000000000000008bb", - "0x00000000000000000000000000000000000000000000000000000000000008bc", - "0x00000000000000000000000000000000000000000000000000000000000008bd", - "0x00000000000000000000000000000000000000000000000000000000000008be", - "0x00000000000000000000000000000000000000000000000000000000000008bf", - "0x00000000000000000000000000000000000000000000000000000000000008c0", - "0x00000000000000000000000000000000000000000000000000000000000008c1", - "0x00000000000000000000000000000000000000000000000000000000000008c2", - "0x00000000000000000000000000000000000000000000000000000000000008c3", - "0x00000000000000000000000000000000000000000000000000000000000008c4", - "0x00000000000000000000000000000000000000000000000000000000000008c5", - "0x00000000000000000000000000000000000000000000000000000000000008c6", - "0x00000000000000000000000000000000000000000000000000000000000008c7", - "0x00000000000000000000000000000000000000000000000000000000000008c8", - "0x00000000000000000000000000000000000000000000000000000000000008c9", - "0x00000000000000000000000000000000000000000000000000000000000008ca", - "0x00000000000000000000000000000000000000000000000000000000000008cb", - "0x00000000000000000000000000000000000000000000000000000000000008cc", - "0x00000000000000000000000000000000000000000000000000000000000008cd", - "0x00000000000000000000000000000000000000000000000000000000000008ce", - "0x00000000000000000000000000000000000000000000000000000000000008cf", - "0x00000000000000000000000000000000000000000000000000000000000008d0", - "0x00000000000000000000000000000000000000000000000000000000000008d1", - "0x00000000000000000000000000000000000000000000000000000000000008d2", - "0x00000000000000000000000000000000000000000000000000000000000008d3", - "0x00000000000000000000000000000000000000000000000000000000000008d4", - "0x00000000000000000000000000000000000000000000000000000000000008d5", - "0x00000000000000000000000000000000000000000000000000000000000008d6", - "0x00000000000000000000000000000000000000000000000000000000000008d7", - "0x00000000000000000000000000000000000000000000000000000000000008d8", - "0x00000000000000000000000000000000000000000000000000000000000008d9", - "0x00000000000000000000000000000000000000000000000000000000000008da", - "0x00000000000000000000000000000000000000000000000000000000000008db", - "0x00000000000000000000000000000000000000000000000000000000000008dc", - "0x00000000000000000000000000000000000000000000000000000000000008dd", - "0x00000000000000000000000000000000000000000000000000000000000008de", - "0x00000000000000000000000000000000000000000000000000000000000008df", - "0x00000000000000000000000000000000000000000000000000000000000008e0", - "0x00000000000000000000000000000000000000000000000000000000000008e1", - "0x00000000000000000000000000000000000000000000000000000000000008e2", - "0x00000000000000000000000000000000000000000000000000000000000008e3", - "0x00000000000000000000000000000000000000000000000000000000000008e4", - "0x00000000000000000000000000000000000000000000000000000000000008e5", - "0x00000000000000000000000000000000000000000000000000000000000008e6", - "0x00000000000000000000000000000000000000000000000000000000000008e7", - "0x00000000000000000000000000000000000000000000000000000000000008e8", - "0x00000000000000000000000000000000000000000000000000000000000008e9", - "0x00000000000000000000000000000000000000000000000000000000000008ea", - "0x00000000000000000000000000000000000000000000000000000000000008eb", - "0x00000000000000000000000000000000000000000000000000000000000008ec", - "0x00000000000000000000000000000000000000000000000000000000000008ed", - "0x00000000000000000000000000000000000000000000000000000000000008ee", - "0x00000000000000000000000000000000000000000000000000000000000008ef", - "0x00000000000000000000000000000000000000000000000000000000000008f0", - "0x00000000000000000000000000000000000000000000000000000000000008f1", - "0x00000000000000000000000000000000000000000000000000000000000008f2", - "0x00000000000000000000000000000000000000000000000000000000000008f3", - "0x00000000000000000000000000000000000000000000000000000000000008f4", - "0x00000000000000000000000000000000000000000000000000000000000008f5", - "0x00000000000000000000000000000000000000000000000000000000000008f6", - "0x00000000000000000000000000000000000000000000000000000000000008f7", - "0x00000000000000000000000000000000000000000000000000000000000008f8", - "0x00000000000000000000000000000000000000000000000000000000000008f9", - "0x00000000000000000000000000000000000000000000000000000000000008fa", - "0x00000000000000000000000000000000000000000000000000000000000008fb", - "0x00000000000000000000000000000000000000000000000000000000000008fc", - "0x00000000000000000000000000000000000000000000000000000000000008fd", - "0x00000000000000000000000000000000000000000000000000000000000008fe", - "0x00000000000000000000000000000000000000000000000000000000000008ff", - "0x0000000000000000000000000000000000000000000000000000000000000900", - "0x0000000000000000000000000000000000000000000000000000000000000901", - "0x0000000000000000000000000000000000000000000000000000000000000902", - "0x0000000000000000000000000000000000000000000000000000000000000903", - "0x0000000000000000000000000000000000000000000000000000000000000904", - "0x0000000000000000000000000000000000000000000000000000000000000905", - "0x0000000000000000000000000000000000000000000000000000000000000906", - "0x0000000000000000000000000000000000000000000000000000000000000907", - "0x0000000000000000000000000000000000000000000000000000000000000908", - "0x0000000000000000000000000000000000000000000000000000000000000909", - "0x000000000000000000000000000000000000000000000000000000000000090a", - "0x000000000000000000000000000000000000000000000000000000000000090b", - "0x000000000000000000000000000000000000000000000000000000000000090c", - "0x000000000000000000000000000000000000000000000000000000000000090d", - "0x000000000000000000000000000000000000000000000000000000000000090e", - "0x000000000000000000000000000000000000000000000000000000000000090f", - "0x0000000000000000000000000000000000000000000000000000000000000910", - "0x0000000000000000000000000000000000000000000000000000000000000911", - "0x0000000000000000000000000000000000000000000000000000000000000912", - "0x0000000000000000000000000000000000000000000000000000000000000913", - "0x0000000000000000000000000000000000000000000000000000000000000914", - "0x0000000000000000000000000000000000000000000000000000000000000915", - "0x0000000000000000000000000000000000000000000000000000000000000916", - "0x0000000000000000000000000000000000000000000000000000000000000917", - "0x0000000000000000000000000000000000000000000000000000000000000918", - "0x0000000000000000000000000000000000000000000000000000000000000919", - "0x000000000000000000000000000000000000000000000000000000000000091a", - "0x000000000000000000000000000000000000000000000000000000000000091b", - "0x000000000000000000000000000000000000000000000000000000000000091c", - "0x000000000000000000000000000000000000000000000000000000000000091d", - "0x000000000000000000000000000000000000000000000000000000000000091e", - "0x000000000000000000000000000000000000000000000000000000000000091f", - "0x0000000000000000000000000000000000000000000000000000000000000920", - "0x0000000000000000000000000000000000000000000000000000000000000921", - "0x0000000000000000000000000000000000000000000000000000000000000922", - "0x0000000000000000000000000000000000000000000000000000000000000923", - "0x0000000000000000000000000000000000000000000000000000000000000924", - "0x0000000000000000000000000000000000000000000000000000000000000925", - "0x0000000000000000000000000000000000000000000000000000000000000926", - "0x0000000000000000000000000000000000000000000000000000000000000927", - "0x0000000000000000000000000000000000000000000000000000000000000928", - "0x0000000000000000000000000000000000000000000000000000000000000929", - "0x000000000000000000000000000000000000000000000000000000000000092a", - "0x000000000000000000000000000000000000000000000000000000000000092b", - "0x000000000000000000000000000000000000000000000000000000000000092c", - "0x000000000000000000000000000000000000000000000000000000000000092d", - "0x000000000000000000000000000000000000000000000000000000000000092e", - "0x000000000000000000000000000000000000000000000000000000000000092f", - "0x0000000000000000000000000000000000000000000000000000000000000930", - "0x0000000000000000000000000000000000000000000000000000000000000931", - "0x0000000000000000000000000000000000000000000000000000000000000932", - "0x0000000000000000000000000000000000000000000000000000000000000933", - "0x0000000000000000000000000000000000000000000000000000000000000934", - "0x0000000000000000000000000000000000000000000000000000000000000935", - "0x0000000000000000000000000000000000000000000000000000000000000936", - "0x0000000000000000000000000000000000000000000000000000000000000937", - "0x0000000000000000000000000000000000000000000000000000000000000938", - "0x0000000000000000000000000000000000000000000000000000000000000939", - "0x000000000000000000000000000000000000000000000000000000000000093a", - "0x000000000000000000000000000000000000000000000000000000000000093b", - "0x000000000000000000000000000000000000000000000000000000000000093c", - "0x000000000000000000000000000000000000000000000000000000000000093d", - "0x000000000000000000000000000000000000000000000000000000000000093e", - "0x000000000000000000000000000000000000000000000000000000000000093f", - "0x0000000000000000000000000000000000000000000000000000000000000940", - "0x0000000000000000000000000000000000000000000000000000000000000941", - "0x0000000000000000000000000000000000000000000000000000000000000942", - "0x0000000000000000000000000000000000000000000000000000000000000943", - "0x0000000000000000000000000000000000000000000000000000000000000944", - "0x0000000000000000000000000000000000000000000000000000000000000945", - "0x0000000000000000000000000000000000000000000000000000000000000946", - "0x0000000000000000000000000000000000000000000000000000000000000947", - "0x0000000000000000000000000000000000000000000000000000000000000948", - "0x0000000000000000000000000000000000000000000000000000000000000949", - "0x000000000000000000000000000000000000000000000000000000000000094a", - "0x000000000000000000000000000000000000000000000000000000000000094b", - "0x000000000000000000000000000000000000000000000000000000000000094c", - "0x000000000000000000000000000000000000000000000000000000000000094d", - "0x000000000000000000000000000000000000000000000000000000000000094e", - "0x000000000000000000000000000000000000000000000000000000000000094f", - "0x0000000000000000000000000000000000000000000000000000000000000950", - "0x0000000000000000000000000000000000000000000000000000000000000951", - "0x0000000000000000000000000000000000000000000000000000000000000952", - "0x0000000000000000000000000000000000000000000000000000000000000953", - "0x0000000000000000000000000000000000000000000000000000000000000954", - "0x0000000000000000000000000000000000000000000000000000000000000955", - "0x0000000000000000000000000000000000000000000000000000000000000956", - "0x0000000000000000000000000000000000000000000000000000000000000957", - "0x0000000000000000000000000000000000000000000000000000000000000958", - "0x0000000000000000000000000000000000000000000000000000000000000959", - "0x000000000000000000000000000000000000000000000000000000000000095a", - "0x000000000000000000000000000000000000000000000000000000000000095b", - "0x000000000000000000000000000000000000000000000000000000000000095c", - "0x000000000000000000000000000000000000000000000000000000000000095d", - "0x000000000000000000000000000000000000000000000000000000000000095e", - "0x000000000000000000000000000000000000000000000000000000000000095f", - "0x0000000000000000000000000000000000000000000000000000000000000960", - "0x0000000000000000000000000000000000000000000000000000000000000961", - "0x0000000000000000000000000000000000000000000000000000000000000962", - "0x0000000000000000000000000000000000000000000000000000000000000963", - "0x0000000000000000000000000000000000000000000000000000000000000964", - "0x0000000000000000000000000000000000000000000000000000000000000965", - "0x0000000000000000000000000000000000000000000000000000000000000966", - "0x0000000000000000000000000000000000000000000000000000000000000967", - "0x0000000000000000000000000000000000000000000000000000000000000968", - "0x0000000000000000000000000000000000000000000000000000000000000969", - "0x000000000000000000000000000000000000000000000000000000000000096a", - "0x000000000000000000000000000000000000000000000000000000000000096b", - "0x000000000000000000000000000000000000000000000000000000000000096c", - "0x000000000000000000000000000000000000000000000000000000000000096d", - "0x000000000000000000000000000000000000000000000000000000000000096e", - "0x000000000000000000000000000000000000000000000000000000000000096f", - "0x0000000000000000000000000000000000000000000000000000000000000970", - "0x0000000000000000000000000000000000000000000000000000000000000971", - "0x0000000000000000000000000000000000000000000000000000000000000972", - "0x0000000000000000000000000000000000000000000000000000000000000973", - "0x0000000000000000000000000000000000000000000000000000000000000974", - "0x0000000000000000000000000000000000000000000000000000000000000975", - "0x0000000000000000000000000000000000000000000000000000000000000976", - "0x0000000000000000000000000000000000000000000000000000000000000977", - "0x0000000000000000000000000000000000000000000000000000000000000978", - "0x0000000000000000000000000000000000000000000000000000000000000979", - "0x000000000000000000000000000000000000000000000000000000000000097a", - "0x000000000000000000000000000000000000000000000000000000000000097b", - "0x000000000000000000000000000000000000000000000000000000000000097c", - "0x000000000000000000000000000000000000000000000000000000000000097d", - "0x000000000000000000000000000000000000000000000000000000000000097e", - "0x000000000000000000000000000000000000000000000000000000000000097f", - "0x0000000000000000000000000000000000000000000000000000000000000980", - "0x0000000000000000000000000000000000000000000000000000000000000981", - "0x0000000000000000000000000000000000000000000000000000000000000982", - "0x0000000000000000000000000000000000000000000000000000000000000983", - "0x0000000000000000000000000000000000000000000000000000000000000984", - "0x0000000000000000000000000000000000000000000000000000000000000985", - "0x0000000000000000000000000000000000000000000000000000000000000986", - "0x0000000000000000000000000000000000000000000000000000000000000987", - "0x0000000000000000000000000000000000000000000000000000000000000988", - "0x0000000000000000000000000000000000000000000000000000000000000989", - "0x000000000000000000000000000000000000000000000000000000000000098a", - "0x000000000000000000000000000000000000000000000000000000000000098b", - "0x000000000000000000000000000000000000000000000000000000000000098c", - "0x000000000000000000000000000000000000000000000000000000000000098d", - "0x000000000000000000000000000000000000000000000000000000000000098e", - "0x000000000000000000000000000000000000000000000000000000000000098f", - "0x0000000000000000000000000000000000000000000000000000000000000990", - "0x0000000000000000000000000000000000000000000000000000000000000991", - "0x0000000000000000000000000000000000000000000000000000000000000992", - "0x0000000000000000000000000000000000000000000000000000000000000993", - "0x0000000000000000000000000000000000000000000000000000000000000994", - "0x0000000000000000000000000000000000000000000000000000000000000995", - "0x0000000000000000000000000000000000000000000000000000000000000996", - "0x0000000000000000000000000000000000000000000000000000000000000997", - "0x0000000000000000000000000000000000000000000000000000000000000998", - "0x0000000000000000000000000000000000000000000000000000000000000999", - "0x000000000000000000000000000000000000000000000000000000000000099a", - "0x000000000000000000000000000000000000000000000000000000000000099b", - "0x000000000000000000000000000000000000000000000000000000000000099c", - "0x000000000000000000000000000000000000000000000000000000000000099d", - "0x000000000000000000000000000000000000000000000000000000000000099e", - "0x000000000000000000000000000000000000000000000000000000000000099f", - "0x00000000000000000000000000000000000000000000000000000000000009a0", - "0x00000000000000000000000000000000000000000000000000000000000009a1", - "0x00000000000000000000000000000000000000000000000000000000000009a2", - "0x00000000000000000000000000000000000000000000000000000000000009a3", - "0x00000000000000000000000000000000000000000000000000000000000009a4", - "0x00000000000000000000000000000000000000000000000000000000000009a5", - "0x00000000000000000000000000000000000000000000000000000000000009a6", - "0x00000000000000000000000000000000000000000000000000000000000009a7", - "0x00000000000000000000000000000000000000000000000000000000000009a8", - "0x00000000000000000000000000000000000000000000000000000000000009a9", - "0x00000000000000000000000000000000000000000000000000000000000009aa", - "0x00000000000000000000000000000000000000000000000000000000000009ab", - "0x00000000000000000000000000000000000000000000000000000000000009ac", - "0x00000000000000000000000000000000000000000000000000000000000009ad", - "0x00000000000000000000000000000000000000000000000000000000000009ae", - "0x00000000000000000000000000000000000000000000000000000000000009af", - "0x00000000000000000000000000000000000000000000000000000000000009b0", - "0x00000000000000000000000000000000000000000000000000000000000009b1", - "0x00000000000000000000000000000000000000000000000000000000000009b2", - "0x00000000000000000000000000000000000000000000000000000000000009b3", - "0x00000000000000000000000000000000000000000000000000000000000009b4", - "0x00000000000000000000000000000000000000000000000000000000000009b5", - "0x00000000000000000000000000000000000000000000000000000000000009b6", - "0x00000000000000000000000000000000000000000000000000000000000009b7", - "0x00000000000000000000000000000000000000000000000000000000000009b8", - "0x00000000000000000000000000000000000000000000000000000000000009b9", - "0x00000000000000000000000000000000000000000000000000000000000009ba", - "0x00000000000000000000000000000000000000000000000000000000000009bb", - "0x00000000000000000000000000000000000000000000000000000000000009bc", - "0x00000000000000000000000000000000000000000000000000000000000009bd", - "0x00000000000000000000000000000000000000000000000000000000000009be", - "0x00000000000000000000000000000000000000000000000000000000000009bf", - "0x00000000000000000000000000000000000000000000000000000000000009c0", - "0x00000000000000000000000000000000000000000000000000000000000009c1", - "0x00000000000000000000000000000000000000000000000000000000000009c2", - "0x00000000000000000000000000000000000000000000000000000000000009c3", - "0x00000000000000000000000000000000000000000000000000000000000009c4", - "0x00000000000000000000000000000000000000000000000000000000000009c5", - "0x00000000000000000000000000000000000000000000000000000000000009c6", - "0x00000000000000000000000000000000000000000000000000000000000009c7", - "0x00000000000000000000000000000000000000000000000000000000000009c8", - "0x00000000000000000000000000000000000000000000000000000000000009c9", - "0x00000000000000000000000000000000000000000000000000000000000009ca", - "0x00000000000000000000000000000000000000000000000000000000000009cb", - "0x00000000000000000000000000000000000000000000000000000000000009cc", - "0x00000000000000000000000000000000000000000000000000000000000009cd", - "0x00000000000000000000000000000000000000000000000000000000000009ce", - "0x00000000000000000000000000000000000000000000000000000000000009cf", - "0x00000000000000000000000000000000000000000000000000000000000009d0", - "0x00000000000000000000000000000000000000000000000000000000000009d1", - "0x00000000000000000000000000000000000000000000000000000000000009d2", - "0x00000000000000000000000000000000000000000000000000000000000009d3", - "0x00000000000000000000000000000000000000000000000000000000000009d4", - "0x00000000000000000000000000000000000000000000000000000000000009d5", - "0x00000000000000000000000000000000000000000000000000000000000009d6", - "0x00000000000000000000000000000000000000000000000000000000000009d7", - "0x00000000000000000000000000000000000000000000000000000000000009d8", - "0x00000000000000000000000000000000000000000000000000000000000009d9", - "0x00000000000000000000000000000000000000000000000000000000000009da", - "0x00000000000000000000000000000000000000000000000000000000000009db" + "0x00000000000000000000000000000000000000000000000000000000000006db" ] - num_msgs = "0x0000000000000000000000000000000000000000000000000000000000000400" - num_real_msgs = "0x0000000000000000000000000000000000000000000000000000000000000400" + num_msgs = "0x0000000000000000000000000000000000000000000000000000000000000100" [inputs.previous_l1_to_l2] root = "0x0fef6d80d31109ddb56d6b3f607cbc9c0af0bff3ea0d43e8f278983c64c11f7a" diff --git a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-checkpoint-merge/Prover.toml b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-checkpoint-merge/Prover.toml index e7cc8ec2d1bf..5ed545c4cf44 100644 --- a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-checkpoint-merge/Prover.toml +++ b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-checkpoint-merge/Prover.toml @@ -486,7 +486,7 @@ proof = [ start_inbox_rolling_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" end_inbox_rolling_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" checkpoint_header_hashes = [ - "0x00f4ff0ca8ebcc3044514a51f8293cba64255dc2890c9a71c9f91ac7ff5fdb61", + "0x00d7202546d0405929e8a53e4cc2ad596c364cee3cae655ae60dea12fde500f5", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", @@ -523,7 +523,7 @@ proof = [ [inputs.previous_rollups.public_inputs.constants] chain_id = "0x0000000000000000000000000000000000000000000000000000000000000000" version = "0x0000000000000000000000000000000000000000000000000000000000000000" - vk_tree_root = "0x16b7eeebd9ec7b6b28187878be4fef52c87331b0ebf2e5719fd203a0474402b1" + vk_tree_root = "0x121ed84a634911bbb8a5e986c40e120a016a1713140a6a5ea4f8187a5e07e547" protocol_contracts_hash = "0x08d0026a4983c49638d0910859942c6648d5ea0a3a152c8a376e4983d0a003d9" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" @@ -532,7 +532,7 @@ proof = [ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000001" [inputs.previous_rollups.public_inputs.new_archive] - root = "0x020f8ecf81935872f66e546c75705f42ea472db6c17ba17c21b37123ac3370ef" + root = "0x1325a9020cc7fb52af7690c0fddd605de0754b0acaf2b78ae98fecc768d2874b" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000002" [inputs.previous_rollups.public_inputs.previous_out_hash] @@ -774,15 +774,15 @@ proof = [ ] [inputs.previous_rollups.public_inputs.end_blob_accumulator] - blob_commitments_hash_acc = "0x00d233140ef9fae4208e220352f2415e1d246046fe63f7c509328f8e7103a8fc" - z_acc = "0x1d819eab16959d185398b917877c1c16c10522124436d14d42548e57923963db" - gamma_acc = "0x2e62d68f38af2a2167c0e5a3f218a3fd62c399dcd2018edb576f9d706ac7d174" + blob_commitments_hash_acc = "0x003bfd9630d12ef136e6723e604eb1a993ecd429c15379422f3316e4d24b152d" + z_acc = "0x0661acfc7736f8385a237de88f03ad51072109bf2316f163566376b184f2706b" + gamma_acc = "0x2da0c63c0d32a0a0d10487e6794731689febb165f0c49cf72c9c1cdf0002de91" [inputs.previous_rollups.public_inputs.end_blob_accumulator.y_acc] limbs = [ - "0xf01d475b6366048d07a0f5965a5ee0", - "0x54cc08730507b05cbd6b911469f40b", - "0x66b2" + "0x2f7c14eecb4ae03244855e01d4ef0f", + "0x87aa3de39e3e33518180416bf14487", + "0x5bf9" ] [inputs.previous_rollups.public_inputs.end_blob_accumulator.c_acc] @@ -790,35 +790,35 @@ proof = [ [inputs.previous_rollups.public_inputs.end_blob_accumulator.c_acc.x] limbs = [ - "0xf8b00e5dd11ec77298863595bce9c7", - "0xdeef2cad345af154480df3667618a1", - "0xb5b852748ea1cd7725d665a19d0b06", - "0x0f39eb" + "0x8256b3f41cb60af8b1efecb138ba7e", + "0xfaedd946e1c6b5aca2f75072e23c68", + "0x7d8b4dcf4cc92f8d088b34d3d62111", + "0x0bf17c" ] [inputs.previous_rollups.public_inputs.end_blob_accumulator.c_acc.y] limbs = [ - "0xa4add5202babc769f5d1a26180fbb0", - "0x7ac91b08e1e7a23a02ba0f5414adc6", - "0x75f4c94ac794e5ccb7364ffddd27b8", - "0x125dc4" + "0x09ceedf50d8c297ba50fbd6d30246a", + "0x1b83805c2039bb09b6b2a445ffa085", + "0xc5511e27691b2d75b7e2c0aa988960", + "0x11ea21" ] [inputs.previous_rollups.public_inputs.end_blob_accumulator.gamma_pow_acc] limbs = [ - "0xc122779321e7668e5d7410d8ccfb56", - "0x2e937c495f28a164530b7bdad6504a", - "0x20c4" + "0xf8146db7557c7c9e4b53c50bb45266", + "0x25ff1ba1fed8334a468c5f2900adac", + "0x2a8c" ] [inputs.previous_rollups.public_inputs.final_blob_challenges] - z = "0x301443e8af936ff991979c0b872a8a39bbf64e6e85abb08060e8b9bf4f9b22a7" + z = "0x23735272a95801d901e7fa12bb1d935d00dd43798205d09a5aaa1d1415f80cbf" [inputs.previous_rollups.public_inputs.final_blob_challenges.gamma] limbs = [ - "0xc122779321e7668e5d7410d8ccfb56", - "0x2e937c495f28a164530b7bdad6504a", - "0x20c4" + "0xf8146db7557c7c9e4b53c50bb45266", + "0x25ff1ba1fed8334a468c5f2900adac", + "0x2a8c" ] [inputs.previous_rollups.vk_data] @@ -828,7 +828,7 @@ proof = [ "0x151ec971d9b291d78b1d405b001fc9a60d1bc918252466d6a4774554135802ac", "0x29c4f2a137ff5c5b54eb911f6add38eb5ec9d139f54d95a7db432abbae472256", "0x02dfab3acbc7708b1b3654912e946fe846568b8cb0e8af8da6a1845278664e55", - "0x03c532012747965bffe5821f5c98d482a745316ad4854045ea812b17962feb68", + "0x29ef5024937c71b580f6cfb649bd06b4a62241ec399bd579b0d0b2bec78c4cae", "0x0db3ab63c7556f8724d86819156701855018cc79140a052893d67fb67c99c1f1", "0x2a5c50782880ea145b4f04af31f7df9ef0cde48fab69c958aef179c68e836f62" ] @@ -1441,7 +1441,7 @@ proof = [ start_inbox_rolling_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" end_inbox_rolling_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" checkpoint_header_hashes = [ - "0x000d09e1989d10f075437e3b06a8cef9a434aab1404f96a21401e711c5422417", + "0x007a32ad48f386ee6d70f2ce416ec10421438ccc14c501f4eecc88fddeacff1f", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", @@ -1478,16 +1478,16 @@ proof = [ [inputs.previous_rollups.public_inputs.constants] chain_id = "0x0000000000000000000000000000000000000000000000000000000000000000" version = "0x0000000000000000000000000000000000000000000000000000000000000000" - vk_tree_root = "0x16b7eeebd9ec7b6b28187878be4fef52c87331b0ebf2e5719fd203a0474402b1" + vk_tree_root = "0x121ed84a634911bbb8a5e986c40e120a016a1713140a6a5ea4f8187a5e07e547" protocol_contracts_hash = "0x08d0026a4983c49638d0910859942c6648d5ea0a3a152c8a376e4983d0a003d9" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" [inputs.previous_rollups.public_inputs.previous_archive] - root = "0x020f8ecf81935872f66e546c75705f42ea472db6c17ba17c21b37123ac3370ef" + root = "0x1325a9020cc7fb52af7690c0fddd605de0754b0acaf2b78ae98fecc768d2874b" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000002" [inputs.previous_rollups.public_inputs.new_archive] - root = "0x070145bd40ecfc4f0837cf98656e6dd4bd9d164dbd8fc77a127127ab62c16826" + root = "0x23e4b2d15041dada68899f950607202dd78115495c75c4143124aa92d663576e" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000003" [inputs.previous_rollups.public_inputs.previous_out_hash] @@ -1691,15 +1691,15 @@ proof = [ inner = "0x0000000000000000000000000000000000000000000000000000000000000000" [inputs.previous_rollups.public_inputs.start_blob_accumulator] - blob_commitments_hash_acc = "0x00d233140ef9fae4208e220352f2415e1d246046fe63f7c509328f8e7103a8fc" - z_acc = "0x1d819eab16959d185398b917877c1c16c10522124436d14d42548e57923963db" - gamma_acc = "0x2e62d68f38af2a2167c0e5a3f218a3fd62c399dcd2018edb576f9d706ac7d174" + blob_commitments_hash_acc = "0x003bfd9630d12ef136e6723e604eb1a993ecd429c15379422f3316e4d24b152d" + z_acc = "0x0661acfc7736f8385a237de88f03ad51072109bf2316f163566376b184f2706b" + gamma_acc = "0x2da0c63c0d32a0a0d10487e6794731689febb165f0c49cf72c9c1cdf0002de91" [inputs.previous_rollups.public_inputs.start_blob_accumulator.y_acc] limbs = [ - "0xf01d475b6366048d07a0f5965a5ee0", - "0x54cc08730507b05cbd6b911469f40b", - "0x66b2" + "0x2f7c14eecb4ae03244855e01d4ef0f", + "0x87aa3de39e3e33518180416bf14487", + "0x5bf9" ] [inputs.previous_rollups.public_inputs.start_blob_accumulator.c_acc] @@ -1707,37 +1707,37 @@ proof = [ [inputs.previous_rollups.public_inputs.start_blob_accumulator.c_acc.x] limbs = [ - "0xf8b00e5dd11ec77298863595bce9c7", - "0xdeef2cad345af154480df3667618a1", - "0xb5b852748ea1cd7725d665a19d0b06", - "0x0f39eb" + "0x8256b3f41cb60af8b1efecb138ba7e", + "0xfaedd946e1c6b5aca2f75072e23c68", + "0x7d8b4dcf4cc92f8d088b34d3d62111", + "0x0bf17c" ] [inputs.previous_rollups.public_inputs.start_blob_accumulator.c_acc.y] limbs = [ - "0xa4add5202babc769f5d1a26180fbb0", - "0x7ac91b08e1e7a23a02ba0f5414adc6", - "0x75f4c94ac794e5ccb7364ffddd27b8", - "0x125dc4" + "0x09ceedf50d8c297ba50fbd6d30246a", + "0x1b83805c2039bb09b6b2a445ffa085", + "0xc5511e27691b2d75b7e2c0aa988960", + "0x11ea21" ] [inputs.previous_rollups.public_inputs.start_blob_accumulator.gamma_pow_acc] limbs = [ - "0xc122779321e7668e5d7410d8ccfb56", - "0x2e937c495f28a164530b7bdad6504a", - "0x20c4" + "0xf8146db7557c7c9e4b53c50bb45266", + "0x25ff1ba1fed8334a468c5f2900adac", + "0x2a8c" ] [inputs.previous_rollups.public_inputs.end_blob_accumulator] - blob_commitments_hash_acc = "0x0051d2aa74654163ad884448987023584e69031d88f7f1292319166a971ee204" - z_acc = "0x13ea812d9c84a80a4d89be0f769b12836cbf85130c6a41738cb7265d41163b1a" - gamma_acc = "0x206c4631238f5fceedc3bcf59ba2d068f0bd3bc86bff8a319472041bd071f6f9" + blob_commitments_hash_acc = "0x0014eaaa6a7be36e8cdff06ac233b9bb62fb27d5e65997ab1576bd02f042f210" + z_acc = "0x0b6beca87f91374302faffb1c9218b55eac9d6d1dc12388201821b677233340b" + gamma_acc = "0x14604adad7eeba0d6be170c55827099369b698723b9b0b3b3f0ddbc6a2401c40" [inputs.previous_rollups.public_inputs.end_blob_accumulator.y_acc] limbs = [ - "0xa4ed7e9e21718043511d6bf1d55fd9", - "0x5cd953cd05d1cdce238877cc50b736", - "0x4f47" + "0xdd72e8a18a802677f92178d973f637", + "0x5ec4f748f461c118c2863a56ca1f8d", + "0x542f" ] [inputs.previous_rollups.public_inputs.end_blob_accumulator.c_acc] @@ -1745,35 +1745,35 @@ proof = [ [inputs.previous_rollups.public_inputs.end_blob_accumulator.c_acc.x] limbs = [ - "0xad16ede16ffd3960c392840e772061", - "0xec11574492f92bebe8a31c1f1c31d5", - "0xd20ca247c7ec7dee5c3c22e92d188b", - "0x008a44" + "0xa704e08e62ecddc07bb7cdd3eee4b3", + "0x76ed68987d23ee41981f426721ad1c", + "0xff2e5af1834ae472f3c5be672d05be", + "0x0456dc" ] [inputs.previous_rollups.public_inputs.end_blob_accumulator.c_acc.y] limbs = [ - "0xb14c1e22322df0882981263f698688", - "0x8589485707ff41784cfba5b6b6b2c0", - "0x99aa489b03ad45ec3649a57bd3d159", - "0x025726" + "0x75ac39983211b9a537b4a515aef3fb", + "0xb5335efaf1ce0a0279e6e5a877f145", + "0xe726ec473bb079fa2905c1e7ad4f46", + "0x1357c9" ] [inputs.previous_rollups.public_inputs.end_blob_accumulator.gamma_pow_acc] limbs = [ - "0x39a1f02b475f3867c0c84e68e98f5f", - "0x9ebdc34d725dfcb104cd2fb325be0d", - "0x6d5a" + "0x6a1756dfbffb46aec3ae4e18a778d0", + "0x4e8089dfc70fdffda2f6bb3064c24e", + "0x7365" ] [inputs.previous_rollups.public_inputs.final_blob_challenges] - z = "0x301443e8af936ff991979c0b872a8a39bbf64e6e85abb08060e8b9bf4f9b22a7" + z = "0x23735272a95801d901e7fa12bb1d935d00dd43798205d09a5aaa1d1415f80cbf" [inputs.previous_rollups.public_inputs.final_blob_challenges.gamma] limbs = [ - "0xc122779321e7668e5d7410d8ccfb56", - "0x2e937c495f28a164530b7bdad6504a", - "0x20c4" + "0xf8146db7557c7c9e4b53c50bb45266", + "0x25ff1ba1fed8334a468c5f2900adac", + "0x2a8c" ] [inputs.previous_rollups.vk_data] @@ -1783,7 +1783,7 @@ proof = [ "0x151ec971d9b291d78b1d405b001fc9a60d1bc918252466d6a4774554135802ac", "0x29c4f2a137ff5c5b54eb911f6add38eb5ec9d139f54d95a7db432abbae472256", "0x02dfab3acbc7708b1b3654912e946fe846568b8cb0e8af8da6a1845278664e55", - "0x03c532012747965bffe5821f5c98d482a745316ad4854045ea812b17962feb68", + "0x29ef5024937c71b580f6cfb649bd06b4a62241ec399bd579b0d0b2bec78c4cae", "0x0db3ab63c7556f8724d86819156701855018cc79140a052893d67fb67c99c1f1", "0x2a5c50782880ea145b4f04af31f7df9ef0cde48fab69c958aef179c68e836f62" ] diff --git a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-checkpoint-root-single-block/Prover.toml b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-checkpoint-root-single-block/Prover.toml index f3a1cd32d6e1..f30ed9f1dc82 100644 --- a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-checkpoint-root-single-block/Prover.toml +++ b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-checkpoint-root-single-block/Prover.toml @@ -484,7 +484,7 @@ proof = [ [inputs.previous_rollup.public_inputs] timestamp = "0x0000000000000000000000000000000000000000000000000000000000000186" - block_headers_hash = "0x11e04a9ffaa75e50324688e2be52568545ffc62d5cd442b877e4415288af7f1e" + block_headers_hash = "0x00a7dc1b5c6a540e6a0e288a0cc25438c2b91f7737e3e545399a722047f130e0" out_hash = "0x00746f2611b7b24448263e846ba73bf1861fc6e68dbc605414405a520957a902" accumulated_fees = "0x0000000000000000000000000000000000000000000000000000000000000000" accumulated_mana_used = "0x000000000000000000000000000000000000000000000000000000000006b6c0" @@ -492,7 +492,7 @@ proof = [ [inputs.previous_rollup.public_inputs.constants] chain_id = "0x0000000000000000000000000000000000000000000000000000000000000000" version = "0x0000000000000000000000000000000000000000000000000000000000000000" - vk_tree_root = "0x16b7eeebd9ec7b6b28187878be4fef52c87331b0ebf2e5719fd203a0474402b1" + vk_tree_root = "0x121ed84a634911bbb8a5e986c40e120a016a1713140a6a5ea4f8187a5e07e547" protocol_contracts_hash = "0x08d0026a4983c49638d0910859942c6648d5ea0a3a152c8a376e4983d0a003d9" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" slot_number = "0x000000000000000000000000000000000000000000000000000000000000000f" @@ -512,7 +512,7 @@ proof = [ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000001" [inputs.previous_rollup.public_inputs.new_archive] - root = "0x057166efca0bc897cfcff75777dd91241f55438e6126342a9221ceb78a9b37f7" + root = "0x27f857ebe6a425096be1cac6e8c23c3f0b27e476062f17d60e52d98612b0c741" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000002" [inputs.previous_rollup.public_inputs.start_state.l1_to_l2_message_tree] @@ -532,8 +532,8 @@ root = "0x1a90881964e28a92a419f1d8361c14ac147b6f9175c04fdf57dadf0d7ba781c9" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000080" [inputs.previous_rollup.public_inputs.end_state.l1_to_l2_message_tree] -root = "0x18d6aae3ab4a271abd590cb98267825b70de1e9e5c339356e94ea5fc7feba64b" -next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000400" +root = "0x0c123047b777079e8db5f669ddaf1ffa1c7780d56b21e29f35e4772ac0f6280c" +next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000100" [inputs.previous_rollup.public_inputs.end_state.partial.note_hash_tree] root = "0x2326bf220c6839c1856478f0c082f0c5883b2baed0bc222a1fa5e1244184c82b" @@ -571,14 +571,14 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 [inputs.previous_rollup.public_inputs.end_sponge_blob.sponge] cache = [ "0x2306af8b455a9cbf87331182183be8c0759fd5e1a4f606cea8a9e24efa461759", - "0x18d6aae3ab4a271abd590cb98267825b70de1e9e5c339356e94ea5fc7feba64b", + "0x0c123047b777079e8db5f669ddaf1ffa1c7780d56b21e29f35e4772ac0f6280c", "0x1e1c597744057b88e39a9780ed087c39b1fc42864e05ef03a59ebd9e96b70b00" ] state = [ - "0x10d28befe8e56d1b63058daa1caac47f5dcda3cf42d48949c70789a3b1c0e378", - "0x0c9dd1fd73fa835196f82c14c9512931f01bbe84eaeadf7d05306f3fa8feca0c", - "0x1a774a31bcd77902c537e015880eac751adc0bf10223cf13f524e6780ce3bc6f", - "0x03f03faaa2776caed30b381e350e90df60171cbc35d5c868718016e5171382bd" + "0x0ea18153f489a674826c5efa162bd5694e9f98127737860d24bdd3893f8d96aa", + "0x063aeec2272e4bb354e2e13f6d08b508c3da92986b18074d4c1ca78cf9dda802", + "0x02b71cf952d8db56ab1f468efa4d5ce5ff7781699c65b00c2925f16112d4c641", + "0x19681f29662ce10887c2bb85e574c45931bf4ef764169d0903522ea34c130114" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000002" squeeze_mode = false @@ -602,19 +602,19 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 squeeze_mode = false [inputs.previous_rollup.public_inputs.end_msg_sponge] - num_absorbed = "0x0000000000000000000000000000000000000000000000000000000000000400" + num_absorbed = "0x0000000000000000000000000000000000000000000000000000000000000100" [inputs.previous_rollup.public_inputs.end_msg_sponge.sponge] cache = [ - "0x00000000000000000000000000000000000000000000000000000000000009db", - "0x00000000000000000000000000000000000000000000000000000000000009d9", - "0x00000000000000000000000000000000000000000000000000000000000009da" + "0x00000000000000000000000000000000000000000000000000000000000006db", + "0x00000000000000000000000000000000000000000000000000000000000006d9", + "0x00000000000000000000000000000000000000000000000000000000000006da" ] state = [ - "0x13a801138d16230fb1088f7fd847ded1c4972fb74bd6e7bc356881f054400aa6", - "0x182a8954baa5425098a60fdbd090ff8918a2ea34cd0f7f52b65422bf666ebfcf", - "0x11e3f79645edb7132868adb47ea5361ff65f7b612d23ebd31b2d2e16f8570918", - "0x2f22e68da640d535dbaa64cc7c81e2b36ada1e9bcb891b371f90220e74493ae0" + "0x0a0aa9eae5277bc3a8414be4a1a279d69f89d765ed17a0123ac83a2547a9cf99", + "0x22e19d5ba8ed0525429b7ac6b29abd221ed25181a9a2085507125e57ad7c3514", + "0x29f22a3e1d331fa7d7d47f6f0f139d765279266c338b405db43ecd66b5ef328c", + "0x1235e52a8b15fc46edb612d48c23715f76bba8eb563b9e29e2519f16612f0faa" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000001" squeeze_mode = false @@ -622,8 +622,8 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 [inputs.previous_rollup.vk_data] leaf_index = "0x000000000000000000000000000000000000000000000000000000000000000d" sibling_path = [ - "0x257d3aec9bbae403732a1fa21e88549d9fd583904cef1290d07ff6ef71b43545", - "0x1eb99c78b615be643bacfbde76ca90b576cfcfebf3f72b65b7112cbcf8f3a12a", + "0x2ad878732a73a8d4f16ad251551287067673f5e856efd44a826fb1642d7afd44", + "0x2f62b974527628413584650394d69b6f3b33f96c79535de642f0257cca300d49", "0x1e16cf81772d4ffd1a84e0c458ae6fe2b66031a749e0a28cb82ec1fc5b5c5900", "0x20f1c701d84b280c9f80a517272153688a9e1b92166d1000cfe7c829b7c25f69", "0x25f9ac8d47a2dbcfb13b88ea474667fd2854bf5c901f2f698266864ffe7c21ad", @@ -636,46 +636,46 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x0000000000000000000000000000000000000000000000000000000000000015", "0x0000000000000000000000000000000000000000000000000000000000000059", "0x0000000000000000000000000000000000000000000000000000000000000005", - "0x000000000000000000000000000000ff364ba83eb627e449a005a1c467182a05", - "0x00000000000000000000000000000000001f0bd5bca0c3cba410bd1d5aeee971", - "0x0000000000000000000000000000001702f2cc12a0d59fc72cd8d4d5cf1297ef", - "0x00000000000000000000000000000000001e027284d71b9591621019234910d6", - "0x000000000000000000000000000000435c75bf59cdf86f8e4ec4535ea216b6e3", - "0x00000000000000000000000000000000000acf027325e713f54528e6bb38a8c5", - "0x000000000000000000000000000000688b502fa3681325590523dfbf8269b21d", - "0x00000000000000000000000000000000001b7575dbbdf1ea4677832c2cd600cd", - "0x00000000000000000000000000000053e169acf78a8a6f22077d4c5e97169a8c", - "0x000000000000000000000000000000000002a1a34b35f1f6538608b15845fc67", - "0x0000000000000000000000000000003784c107a6867eab031c73df85ea54f161", - "0x000000000000000000000000000000000012380b6e1baa21da21cab11c0c6007", - "0x000000000000000000000000000000e86fabdcd43570fb9d0ffe81d3649ec6d2", - "0x0000000000000000000000000000000000222fa2f87c6fe0744f5fbe6b541e60", - "0x0000000000000000000000000000009249e7cfd36b4d1d83ecda24dafa101ebb", - "0x00000000000000000000000000000000000ea031907be007377ff0b4660a6331", - "0x00000000000000000000000000000001adcd8190c108a0ffd7b63b1180a8c54c", - "0x00000000000000000000000000000000000f32de52203c87b4574c1c40cba238", - "0x000000000000000000000000000000a042720ae44f8c8c4bd8f3c833adbe4902", - "0x000000000000000000000000000000000011edfcd573c3cf435f7c8dbd1bb55c", - "0x00000000000000000000000000000010aa8db9ec9cee5c883e4a71bca9979b50", - "0x00000000000000000000000000000000000a945d11cf55ee084890287bd96546", - "0x000000000000000000000000000000b1a3bbe697279ce48f385b30c9d84933f5", - "0x00000000000000000000000000000000000d00386ba6d0ca2701eb0687ea8ad2", - "0x0000000000000000000000000000005eb09bdce1a9036525ab79d66a8dc741ca", - "0x00000000000000000000000000000000001efeffaabc4b696de3d4b196d3444a", - "0x0000000000000000000000000000009cbb3ba94d08e820430d65b25cc90f1040", - "0x000000000000000000000000000000000001394b107c126a3808d8b767c273d3", - "0x000000000000000000000000000000c04645da4f191299b434ae7417ea9f5909", - "0x00000000000000000000000000000000000d3198452735fd012957fe6d432f71", - "0x000000000000000000000000000000230b137b5450d25e5afb07140885f20fe7", - "0x00000000000000000000000000000000000cccf07870ff2013e5d423441ccd76", + "0x00000000000000000000000000000077282541bd6fd9d6bfc1f25166c7429e6a", + "0x000000000000000000000000000000000021b1b69bc7372bd8bff5ff53eebfef", + "0x000000000000000000000000000000a1421a4bb0d664e0f0eb1e76e84782b28e", + "0x0000000000000000000000000000000000086c4fdd0ae664f385c026c87937dd", + "0x00000000000000000000000000000084c1a7ff885d80dba73df7900501f5c326", + "0x00000000000000000000000000000000000f06e01a48da3d3b9346ae460b1879", + "0x000000000000000000000000000000582c412c2d9c803f476b4006e97b0c02b4", + "0x0000000000000000000000000000000000062a2d0ba8f876e287f146169d7eff", + "0x000000000000000000000000000000f6286b2159c5806d297e1557605c862f11", + "0x00000000000000000000000000000000000a127b60d46d2049c54815802a57c6", + "0x0000000000000000000000000000001038afa7a23bd50daee7099d2ceb2e02b8", + "0x000000000000000000000000000000000014090521caeb2ea4470366d49c09c6", + "0x00000000000000000000000000000042e4e518cc8ba00133558637e802c15a22", + "0x000000000000000000000000000000000025244ef6851423a1ae14c8f5256401", + "0x00000000000000000000000000000075d1248adf239e62192536a99158d8f3c0", + "0x000000000000000000000000000000000014c60fe86425fc729c9fdb12d0ea33", + "0x0000000000000000000000000000001615437ff572eac350be35ea0c85f09cb7", + "0x00000000000000000000000000000000001587d193ec1986231fe8e923cd31fc", + "0x000000000000000000000000000000c188aa6c855728ae8111aa7d67d86a8456", + "0x00000000000000000000000000000000002b35185fc925aca2681dfb66c5715a", + "0x00000000000000000000000000000039844af71f2531391123f92398e90317bb", + "0x000000000000000000000000000000000002173f19e450c66402230dfea5215a", + "0x00000000000000000000000000000062b85fad58f8b7d7c37ddcf4552cae7368", + "0x00000000000000000000000000000000000be6dc9fe72590405ae312e70ad123", + "0x000000000000000000000000000000d5439860914df8e89ef2246bdec1d4b035", + "0x0000000000000000000000000000000000304bf0c92a739423b5faf7566fe54a", + "0x00000000000000000000000000000000836bdc9b471debddde1043c1db9afb16", + "0x0000000000000000000000000000000000029a9544c5bd71b5a799e3dd05dcaa", + "0x00000000000000000000000000000016c864da94bb3f5632d8bdeceb613bd7a4", + "0x00000000000000000000000000000000002d05e0ce6beb306608814321dc09e6", + "0x000000000000000000000000000000c27c9a8ec5f270e5895b414913f8d25c62", + "0x000000000000000000000000000000000029915a58475606d7fe11dd20273c66", "0x0000000000000000000000000000001eee81b23a887f299049b14c11e98460d6", "0x00000000000000000000000000000000002a56ce41f6b0be13b9c26747621b82", "0x000000000000000000000000000000d5827d6338c78656c0d12ca1aea6ef2c7c", "0x00000000000000000000000000000000001aa98f2de3ddda547d8f6de4e725de", - "0x000000000000000000000000000000cf8b9ffe98d3b5a03326ff0ff46c571a15", - "0x00000000000000000000000000000000002022182acb367120f3ab9d5f25b75d", - "0x000000000000000000000000000000232c4d60abc17da18e4a73764002438e4a", - "0x000000000000000000000000000000000026fe38367ddb62c640d7cd1ac9311a", + "0x00000000000000000000000000000091c519bd3f9ea8a6456beed311d0622c87", + "0x0000000000000000000000000000000000294a64c06eeb935b28faff12a38cd4", + "0x00000000000000000000000000000042153704f2093c9c8dc90ce2aade7c3a12", + "0x000000000000000000000000000000000008516334a2a692925a1eed6bd51b17", "0x0000000000000000000000000000008c74e076dc7aa753701853e54da054f3b5", "0x00000000000000000000000000000000001181a90851f0643c8b31071db5b02a", "0x0000000000000000000000000000009d279b3b46218954fc4e0ace7203890a14", @@ -696,60 +696,60 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x00000000000000000000000000000000001c11e3c428004298033b34b3c708a1", "0x000000000000000000000000000000e620c5127fb60d387bebeed1ea01366f9d", "0x00000000000000000000000000000000002928d84e92dc8687c3f39e98cab437", - "0x00000000000000000000000000000046734e6c365916353416a09d3fb7423638", - "0x00000000000000000000000000000000002bc174261fc5551c0bec26b6dd3c2d", - "0x00000000000000000000000000000064a55ea29a136e65a90a01d039cfb19929", - "0x000000000000000000000000000000000017436510ca25eedfe60a2f9d164ac3", - "0x000000000000000000000000000000a932f59f973b03be2497533adf1043a1dd", - "0x00000000000000000000000000000000002d93f6ff9f4aec685b5403800d084e", - "0x0000000000000000000000000000004043dd2f7e2d7a2a045c91a64e7c6392ca", - "0x00000000000000000000000000000000001dc3789f3d6ac07668ee49bfbb7113", - "0x0000000000000000000000000000007bd39f0ceb060dab3b6dc2f6257ca95db4", - "0x00000000000000000000000000000000002cfbf6faff3e12278309577d2f1255", - "0x0000000000000000000000000000006ae7eaa59e26c06b1b1a7cb7a7d5d3df7d", - "0x0000000000000000000000000000000000074563b522b1ca0e9cfb994eedbe72", - "0x000000000000000000000000000000068ab427abe5f5d48b40c7b36fdcdc02e4", - "0x0000000000000000000000000000000000138e89d24a9c01093c1b1c15ce82e0", - "0x000000000000000000000000000000a273fef9dc9790d71301c1878d8c481e85", - "0x00000000000000000000000000000000002c60e4a68fb4592a28dcdcd8acd3ff", - "0x000000000000000000000000000000b51fd500a011bc00685a9063c8eece1a2e", - "0x00000000000000000000000000000000002d4ed0ad596bf4b0ad5a9139a9f733", - "0x0000000000000000000000000000006eeaf571a581efc13196baab7b69f35842", - "0x000000000000000000000000000000000027b4af638f50eb73516e16977c0109", - "0x000000000000000000000000000000a60addb5cd61509bebfbbdb5886333fdcd", - "0x000000000000000000000000000000000017fcac16e25dc04387f1527eff36f4", - "0x000000000000000000000000000000c90c582bc47bc3005c32f2a6c92404758a", - "0x000000000000000000000000000000000001cde45f545150d7b02ca496eced87", - "0x000000000000000000000000000000de05c4bfdf0721425a61c6e21f4fee00b8", - "0x00000000000000000000000000000000002e2a5c40d197d5c37627cab196beea", - "0x000000000000000000000000000000cddd96500b2b258e515cf2043a258f38a9", - "0x00000000000000000000000000000000000dea76437fd2e7be6320e016095dc8", - "0x0000000000000000000000000000007180c768da10d6b18ace1c03268d6558f0", - "0x0000000000000000000000000000000000071d8c7111fb5fcc01bf7225c07272", - "0x00000000000000000000000000000092a3fdda037c232edd3d3bd9baf81b4aa7", - "0x00000000000000000000000000000000000f64da4ed7fae7121466ee75dde24b", - "0x000000000000000000000000000000dc89685442975638cd8be3805c3f40c9a0", - "0x0000000000000000000000000000000000123dd9b2d733764c71393ddfdd5dcb", - "0x0000000000000000000000000000001c013cd996c358b8d9b84883ec81c0c832", - "0x0000000000000000000000000000000000146f32456a1777406cabfe5a204ba2", - "0x000000000000000000000000000000adc342075c79c9e9ced5d0015e41be6617", - "0x0000000000000000000000000000000000207bd54bdda9c2e9ffad0dba21aec1", - "0x000000000000000000000000000000a2c3cc211dd21cb95d92b4ef46684f6312", - "0x0000000000000000000000000000000000068d526bda7832695853a37c3cc85b", - "0x0000000000000000000000000000005fc7f7da52294ba16f174d9fcdf4493631", - "0x00000000000000000000000000000000002cf843d29cfdfc5ccf4b15fae04736", - "0x000000000000000000000000000000202f2bf101adb2b47b05f7fc3762d89d7d", - "0x000000000000000000000000000000000008e130d6c0804622e9ab85f7bde49f", - "0x00000000000000000000000000000085b282ceaca6bec4102a81fb95ef3e052a", - "0x00000000000000000000000000000000001941220b0189973c9cdb8a21defdae", - "0x000000000000000000000000000000efb87b6bc9a0e24368ea536fdc2f032026", - "0x00000000000000000000000000000000000a8f0a1044bf7e05b9ae92915218b3", - "0x000000000000000000000000000000c22215c39ee117d08329a06a22e9aaacd4", - "0x00000000000000000000000000000000001911e5595b8d3add78263844f1115b", - "0x0000000000000000000000000000003fb580eae72521cb2051058d45ff4f5723", - "0x00000000000000000000000000000000002c6b7be33924c0aff5624490756dc0" + "0x0000000000000000000000000000002e817322c081ea26c1c0a64de7e8367e0e", + "0x0000000000000000000000000000000000118158162cf91a423978fdaa5a45b8", + "0x00000000000000000000000000000009397bb309f41e63a95fe38069d764c04c", + "0x00000000000000000000000000000000000146b96e9017c90dec3646b5f84b39", + "0x000000000000000000000000000000f15a61d3a92b4e9d4118cdb11e178e3942", + "0x000000000000000000000000000000000001c112866f13be1efa2907ac96e5a8", + "0x000000000000000000000000000000c9444ada521a65a91c7ae8759603c3a9f8", + "0x00000000000000000000000000000000000dfdaad1550b12fa2bd44edb707186", + "0x00000000000000000000000000000025b0540dffa6ee3699aa2f700f111997f5", + "0x00000000000000000000000000000000000dc67e2a5944b060ea3c815360d8a0", + "0x000000000000000000000000000000989bff55ad48dd8ef89d18754514fa3265", + "0x0000000000000000000000000000000000021d0869faf9d5a2855b2372fc0bcb", + "0x000000000000000000000000000000abb1983d4d348ec7a076453b247144e6da", + "0x00000000000000000000000000000000000d7b5046dfe3d777e23e532bca5906", + "0x00000000000000000000000000000079ac389fcc55ded831b4cd85196021f2d8", + "0x00000000000000000000000000000000002c67976317863d5704aaed1904d968", + "0x000000000000000000000000000000e10facb05860832e504103165f52eb54ab", + "0x00000000000000000000000000000000002a21154b190555dbaa44b3fe61e616", + "0x0000000000000000000000000000004b56960d095cc95cd4e80b27959da18f13", + "0x000000000000000000000000000000000025da441820bceafc17abf32c07f271", + "0x000000000000000000000000000000c29760193d6774b7f4ffdc509dcc0c6ee3", + "0x00000000000000000000000000000000002f4d5face9b329a9aa16aed324064c", + "0x000000000000000000000000000000c014e943dd12b0ccaca8c27603e2456647", + "0x000000000000000000000000000000000005ac6b234a7d890ce4332e51c1a909", + "0x000000000000000000000000000000aa690a688ed70e14dceecc3b12c8045bf2", + "0x0000000000000000000000000000000000224732733dfac995d192446d198c5b", + "0x00000000000000000000000000000072bb2f3526cbeb18c3e2729c2065ecac9d", + "0x000000000000000000000000000000000013303d33d15c2d6904c7de18ae2822", + "0x000000000000000000000000000000d0f385950771ec288df81649a39007ec71", + "0x00000000000000000000000000000000002fd7af7f94a03fa18a2dcc66ed285e", + "0x000000000000000000000000000000b78ac98e6d2fca14c5accbfbf39411aecc", + "0x0000000000000000000000000000000000249e89097357f16051a3d9bfbeb9e2", + "0x00000000000000000000000000000054598f08e2afefa43e77fb2d0c9dd777c5", + "0x00000000000000000000000000000000000f25a74f71f08b540c89a1f3999d44", + "0x0000000000000000000000000000004f6df0f70892359116cc4f412330e481da", + "0x0000000000000000000000000000000000086d7cdc73d9b2289397c881a373cb", + "0x000000000000000000000000000000cd8c4dca38b6022319702c0dd77211ae1e", + "0x00000000000000000000000000000000000a1525dffaaf5a2ed380db6a01ac47", + "0x000000000000000000000000000000433ac0e21e7b0235027979f479baacf996", + "0x00000000000000000000000000000000000cd7fcfd8a1fbdd6450d4ff77ccc98", + "0x000000000000000000000000000000cbd43ee6ae7df2893288cf28b9a4f162fe", + "0x00000000000000000000000000000000000c6f30155cbc3d770bf018ab735bc3", + "0x000000000000000000000000000000f1cd6ec0fa8b6d380e568b82fdbca0636a", + "0x0000000000000000000000000000000000219d5cfef5057a9dab54703488fdac", + "0x000000000000000000000000000000a2cf9ac556f0fe0b40d437ad8e42607007", + "0x000000000000000000000000000000000008cbcb6e78d1b05ef0139b1eeaf5cc", + "0x000000000000000000000000000000777b36cca4b2ec47527ee5a2a616f7b319", + "0x000000000000000000000000000000000020c6e94c77c1d5da3c95d66d60059c", + "0x000000000000000000000000000000a20049994901e0a0fed66e891320a7fbbd", + "0x00000000000000000000000000000000000ce92ac8185c1ec16428565349f491", + "0x00000000000000000000000000000057d502cf443d291025fdfd6d69ee0f474c", + "0x00000000000000000000000000000000002af154a8f06e7c58d933b0b82a93e5" ] - hash = "0x049b51ea962465dcb60820b06348a1e00782e4ccfaa2c06635c205671d24a5a2" + hash = "0x1873ec71806c4bab179d6fa07827a23ed7fa8e918d0a9d8b9dfac918209a6d5b" [inputs.inbox_parity] proof = [ @@ -1166,92 +1166,92 @@ proof = [ ] [inputs.inbox_parity.public_inputs] - in_hash = "0x00aa91330eafec1db9b1ca2e1733b213a28bfde0499aca2506acc8c00aae7ba3" + in_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" start_rolling_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" - end_rolling_hash = "0x005b0c15d0f641e148adfec120a12eadbf8343e009d350aa593b6d78dbae9568" - num_msgs = "0x0000000000000000000000000000000000000000000000000000000000000400" - vk_tree_root = "0x16b7eeebd9ec7b6b28187878be4fef52c87331b0ebf2e5719fd203a0474402b1" + end_rolling_hash = "0x00a48b67f417042d5231ac634d609fe03a3884c4757a2afe457a7d1c6ca063ba" + num_msgs = "0x0000000000000000000000000000000000000000000000000000000000000100" + vk_tree_root = "0x121ed84a634911bbb8a5e986c40e120a016a1713140a6a5ea4f8187a5e07e547" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" [inputs.inbox_parity.public_inputs.end_sponge] - num_absorbed = "0x0000000000000000000000000000000000000000000000000000000000000400" + num_absorbed = "0x0000000000000000000000000000000000000000000000000000000000000100" [inputs.inbox_parity.public_inputs.end_sponge.sponge] cache = [ - "0x00000000000000000000000000000000000000000000000000000000000009db", - "0x00000000000000000000000000000000000000000000000000000000000009d9", - "0x00000000000000000000000000000000000000000000000000000000000009da" + "0x00000000000000000000000000000000000000000000000000000000000006db", + "0x00000000000000000000000000000000000000000000000000000000000006d9", + "0x00000000000000000000000000000000000000000000000000000000000006da" ] state = [ - "0x13a801138d16230fb1088f7fd847ded1c4972fb74bd6e7bc356881f054400aa6", - "0x182a8954baa5425098a60fdbd090ff8918a2ea34cd0f7f52b65422bf666ebfcf", - "0x11e3f79645edb7132868adb47ea5361ff65f7b612d23ebd31b2d2e16f8570918", - "0x2f22e68da640d535dbaa64cc7c81e2b36ada1e9bcb891b371f90220e74493ae0" + "0x0a0aa9eae5277bc3a8414be4a1a279d69f89d765ed17a0123ac83a2547a9cf99", + "0x22e19d5ba8ed0525429b7ac6b29abd221ed25181a9a2085507125e57ad7c3514", + "0x29f22a3e1d331fa7d7d47f6f0f139d765279266c338b405db43ecd66b5ef328c", + "0x1235e52a8b15fc46edb612d48c23715f76bba8eb563b9e29e2519f16612f0faa" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000001" squeeze_mode = false [inputs.inbox_parity.vk_data] - leaf_index = "0x000000000000000000000000000000000000000000000000000000000000004c" + leaf_index = "0x000000000000000000000000000000000000000000000000000000000000004b" sibling_path = [ - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x19f1a0c09db4cd026f686e9c8fb45501a9fefb4eb1b4c6c328a51343a0094eeb", - "0x06dec47368fa76ba945eee4191c7ad19adacfc7f6caeca5a6490826064124503", + "0x23b7f87d31bcf8101da7e60a2ee1f304b7bc37bb270a910be379c041a2807ccf", + "0x1d438b6650ee2b4a994c0eec98be4e2fd6f843225ad6fb72d292d652cfe1aa2b", + "0x245dbc47d6efe6f6761074906fb89ede81b85145a02ce07d32ddc39723b6bb28", "0x21ed90cd61698dc1e3dfc970eefcedc837dfedf46c2c4e594ce076ee8fba9386", "0x1e20ad4181460cbfdc74ca773502c59b890f184efe300ebad895956d318422da", "0x1434e6e2d5db1053ab8a3be58704509c799ee17e109c77f441f7bf1755400249", - "0x2e599507c72b52b5337c34a023c60717a53366b20ca669172a3021ec1abd8864" + "0x233f4d8335f80ad820a004f575aa5acb4ed91b8e598bd77a024098baa78ce50d" ] [inputs.inbox_parity.vk_data.vk] key = [ - "0x0000000000000000000000000000000000000000000000000000000000000018", + "0x0000000000000000000000000000000000000000000000000000000000000016", "0x0000000000000000000000000000000000000000000000000000000000000018", "0x0000000000000000000000000000000000000000000000000000000000000005", - "0x00000000000000000000000000000013e27e8ee7ae87b078e0e8a84dfb6d03ae", - "0x00000000000000000000000000000000002c74885cce2f6170022d2782036e20", - "0x0000000000000000000000000000000692577d50a6a7de2fb6ea1f98bb873d12", - "0x00000000000000000000000000000000002d70bc332855138017034c10cd8430", - "0x000000000000000000000000000000e81d2f144589cd473a1f91b40233d31701", - "0x0000000000000000000000000000000000146c41acc0d5344668872928402a6b", - "0x000000000000000000000000000000a0d36ca94b988fd4988f1b960fa14efa65", - "0x00000000000000000000000000000000000266004c450687e951cc66d3535d91", - "0x00000000000000000000000000000051af42269b89eea9e2ef9d5a985a31796a", - "0x0000000000000000000000000000000000135277662b72a9817181a3abbab756", - "0x0000000000000000000000000000007a7f1bec18169d119658ddc940ebdf2a0c", - "0x00000000000000000000000000000000001bfaa40105cf97387bc0b39706a119", - "0x000000000000000000000000000000de48af8ae9bfc71db33bc45b4bd57aa030", - "0x0000000000000000000000000000000000029816bdef484c620a15198c4eac09", - "0x000000000000000000000000000000887216af11a9cc803aace28926493d8f0b", - "0x0000000000000000000000000000000000141bba0115846cf35a3fc53dfeb036", - "0x000000000000000000000000000000e4f5ab585fc19d90660c2a90e734db18cd", - "0x000000000000000000000000000000000013fa21bebb58188f81dc206c6ec649", - "0x000000000000000000000000000000a67b3c059ce2374ad92c22ca68ae3305f7", - "0x000000000000000000000000000000000020fe7cd7b7e9e3c0dd0429fe17b23d", - "0x000000000000000000000000000000cfeb80c561ff7e8911d52ec9bab198fb82", - "0x00000000000000000000000000000000002e6c00e9c4e9cf4b960f760149296f", - "0x00000000000000000000000000000030f987ee91930fd32f90c1c4ffae02eba0", - "0x00000000000000000000000000000000002e7565ee181a20320746d6563fee23", - "0x0000000000000000000000000000005122de2df8de92502233de3b7f4d2beadc", - "0x00000000000000000000000000000000002bc5b7b4a2893e3ba2956380cc732b", - "0x000000000000000000000000000000439afd0e31207ed98521bd53f472254c6d", - "0x00000000000000000000000000000000000b75c19eb8201ae71340c527b64e14", - "0x000000000000000000000000000000de089b6bd03e2b6fba51dbb5ab10a57301", - "0x0000000000000000000000000000000000050ee900cb8cdd87217e53c24cf30e", - "0x000000000000000000000000000000ac323aa8e9594d55f1dbb83286db086f67", - "0x00000000000000000000000000000000002c7515131fa378dbf0b8f7c3d34bd2", + "0x000000000000000000000000000000dae09d98202a22c6ac15261b4f252d6cbe", + "0x0000000000000000000000000000000000240dcd2511fcf65ee83534a8135f60", + "0x000000000000000000000000000000302ce523364a28ebc6ad0ec16bfec27e21", + "0x00000000000000000000000000000000000bc5966879a70e0a9eb113501c6726", + "0x00000000000000000000000000000016c5041a6b2d62884e241c5b165f59e7d7", + "0x00000000000000000000000000000000001e5602a431a4a6dcb0e9b3a5792685", + "0x00000000000000000000000000000077c443a58d6674cf4d0611911536d64b4f", + "0x00000000000000000000000000000000002bc3ba27abe0113b96e9dfc96b52d8", + "0x00000000000000000000000000000012d2e28f1542d8db84bc45c6048f6d1a19", + "0x00000000000000000000000000000000002c77da6f3d448fa9d4b6b9ca65b08a", + "0x000000000000000000000000000000e8db81d3dadf826307d4f32cf036b2e8cf", + "0x000000000000000000000000000000000007cd425cfb2b99f21ecf7168075b85", + "0x000000000000000000000000000000ce73196c354127662893883f0fdfacb3d1", + "0x00000000000000000000000000000000001aedd98fbd7e65b081f4701dc0450e", + "0x000000000000000000000000000000d29ace52d594a343b643422e8298f07e2f", + "0x00000000000000000000000000000000000f1cfa4be8a4cb6f5aefbeb0df0c7b", + "0x000000000000000000000000000000c96a4f395943376b11aa391daaa8edf663", + "0x00000000000000000000000000000000000ac0260e687c759f0b13dfcfee3bfc", + "0x0000000000000000000000000000007f895348a081e9fcfce9286c94db5d1521", + "0x00000000000000000000000000000000002d4f1a03f8fcd1583f9e129266246b", + "0x0000000000000000000000000000007a4131bf4eb9c0db3da33eda029e892b13", + "0x0000000000000000000000000000000000076aa7f44082099313ccaa9846ff8f", + "0x000000000000000000000000000000ee1f5ca62037c8b8899104b078ea2077b0", + "0x000000000000000000000000000000000007b885b2ac8c0368ba5b7e4741603f", + "0x0000000000000000000000000000006c5267dd1f95070973a140b3f057813bc0", + "0x0000000000000000000000000000000000055f6b1ec5456ec1621d94632ccdb3", + "0x0000000000000000000000000000001eb20e9b969496970af54378f350e4919f", + "0x00000000000000000000000000000000002f0246ac684f2ac92acba58a5dc773", + "0x00000000000000000000000000000062471b29b5c9f7c5c8d17efc4ad90fd523", + "0x0000000000000000000000000000000000234b08c34fe4311510b6cf295edb2b", + "0x000000000000000000000000000000eaf8cb39542ad42d14ecf1802fd9d9c355", + "0x00000000000000000000000000000000000b0f83076962b5659cbdce6fbeb251", "0x0000000000000000000000000000001eee81b23a887f299049b14c11e98460d6", "0x00000000000000000000000000000000002a56ce41f6b0be13b9c26747621b82", "0x000000000000000000000000000000d5827d6338c78656c0d12ca1aea6ef2c7c", "0x00000000000000000000000000000000001aa98f2de3ddda547d8f6de4e725de", - "0x0000000000000000000000000000000c50ce31e59e68b8dc9dd274400e8e0ee9", - "0x00000000000000000000000000000000001315191c92d1fb007b10e8d441a566", - "0x00000000000000000000000000000034f837d7bd4004456531be2379b9e95e49", - "0x00000000000000000000000000000000002108e529c51f15d70075d78abd23e8", - "0x000000000000000000000000000000b35c952e777e933293105c4a17565f04b9", - "0x0000000000000000000000000000000000178760dda7490ee7b71feb4a0eea59", - "0x0000000000000000000000000000001b196882d64e838e302abef2d5827bf622", - "0x00000000000000000000000000000000000505566c9fb34a0b96addd4941786b", + "0x000000000000000000000000000000ee963a9acacfe2ab5b48db9af317882ad4", + "0x0000000000000000000000000000000000042a9670e4e41ed527d1be6644f0e4", + "0x00000000000000000000000000000046dc668b76fbac5f163faf2aabd8b458d7", + "0x000000000000000000000000000000000017351eba93572036da206797c69aa1", + "0x0000000000000000000000000000007439344271b5f8c01d662783ced69a92ef", + "0x00000000000000000000000000000000001eb26568e7dd11be5fae8b3e89513f", + "0x0000000000000000000000000000005b8b260669dba29d5623fc60bda423470a", + "0x0000000000000000000000000000000000111205e8e77ea2cd11bfb786f2d266", "0x0000000000000000000000000000001f2c696a036d939c1c6030b6865756faab", "0x00000000000000000000000000000000002571340ad307cfa8a523f6f8914ea7", "0x0000000000000000000000000000007bc1893b89ad56380159cd60f6fa00b992", @@ -1268,60 +1268,60 @@ proof = [ "0x000000000000000000000000000000000008f6999fc49f20dc6deda265a9c248", "0x0000000000000000000000000000001c6634543018006fa8da47114274528b7e", "0x0000000000000000000000000000000000245bc7cfa90e6b8a98a064db058e7d", - "0x0000000000000000000000000000007f57d25276da88d9b2299b82ca2110a85a", - "0x00000000000000000000000000000000001592a6098919d0d82181b41bceb4b8", - "0x000000000000000000000000000000225dfd2449ca643ca558292aebea034c2b", - "0x000000000000000000000000000000000020699876518bed469bda6fbe5c99a8", - "0x0000000000000000000000000000001f0718e9b2dab2a06af6bba5f991b1aeff", - "0x000000000000000000000000000000000029e7dceab9fd3733a4a2713b49fd11", - "0x0000000000000000000000000000004a512111969cb0e314e551322e4099e157", - "0x000000000000000000000000000000000028f3abcfc1e677ddad9ffa8fe7e0a6", - "0x000000000000000000000000000000e4406844c7446977df592d9ad61d1b56f2", - "0x000000000000000000000000000000000002ebd233103c5acd9f1359e1665478", - "0x000000000000000000000000000000b7af55335650e157acffbcc0ed3519ea30", - "0x00000000000000000000000000000000000ecb72e0d4d237416e75c9fd273607", - "0x000000000000000000000000000000ee9cb81f66d70a51afa22dacdaac78962e", - "0x00000000000000000000000000000000002f91af48a647f7fad43ae7996ae4ca", - "0x000000000000000000000000000000c9f2a045318dd5f1fc6ac44968aec340cc", - "0x000000000000000000000000000000000019f643a87c01adfa986a51d37207ca", - "0x0000000000000000000000000000003dc591787b7963d6b1c497903331df5359", - "0x000000000000000000000000000000000001fdcad4552407a181cb9c6532a3f3", - "0x000000000000000000000000000000579f9d0b0dfc60181c9167588d2250a730", - "0x00000000000000000000000000000000000113952c5a14d3883c2deb9806af0d", - "0x000000000000000000000000000000e83a62b64386e2b31aff8d5a69e2f6da0e", - "0x00000000000000000000000000000000001dc82f7a3dfb603a136816bc1f7348", - "0x0000000000000000000000000000008f826f2ed8ed4bfe496815e1c0a72963db", - "0x00000000000000000000000000000000001df6ef06f7f92452ba6b25de232ee8", - "0x0000000000000000000000000000008e6d47a2f0473350f67eed42d663c052fd", - "0x00000000000000000000000000000000000c68ccfc2049c43893739edfaf6af4", - "0x00000000000000000000000000000032e80451bb54640385b8214748a47482d5", - "0x00000000000000000000000000000000000775cc9791f3f3aaa54c4229a8a3cd", - "0x000000000000000000000000000000291f55c9fa3fe4c3cb589c098968a07ba1", - "0x00000000000000000000000000000000001e888cbcc392dbddebea5a471bc3b1", - "0x0000000000000000000000000000009d9dba2e68259dc75569b3565af201d671", - "0x0000000000000000000000000000000000001b6c80929f8089db2588a078886d", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000007723278fd44d9137fd7f73943181fec927", - "0x00000000000000000000000000000000002f4289239983dee23c85caa81f6b7a", - "0x0000000000000000000000000000004fd879e7160788dfea92771fc093fa6ed3", - "0x00000000000000000000000000000000000e53ff20a38a32db810c0e38953849", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x000000000000000000000000000000799e70d12366d4e94ee4d09b507035ce0b", - "0x000000000000000000000000000000000022221e8995f2d5b244fcc1d27b928a", - "0x0000000000000000000000000000007364373df859bfcd1e8310aedc638bafd1", - "0x00000000000000000000000000000000001bc0064388a0c02636fc3297afbe95", - "0x0000000000000000000000000000005a17ea5f4d1be4cb71561b98ded77f3390", - "0x0000000000000000000000000000000000159e2d5660ec3bac3bc3cf2bc93307", - "0x00000000000000000000000000000013b14a2afcb8517986ed10bfeb7f1b2d0a", - "0x00000000000000000000000000000000001b1591e2a64bdc4410582d2f4d5dfc" + "0x000000000000000000000000000000bc53950cdece1378f71426956b09b8c715", + "0x00000000000000000000000000000000001666f474e305c6c725bbddc3d43b45", + "0x000000000000000000000000000000a3f60a21f6002502be611b6a2d43909c0a", + "0x00000000000000000000000000000000000f6ba0625fdf62bf0d82078f25b268", + "0x0000000000000000000000000000001c31e32b6b97fc804ffa233232d7d6152b", + "0x00000000000000000000000000000000000e186393d569195b20f0680817822d", + "0x000000000000000000000000000000791c999533f9a6b63e377b283f7f43f996", + "0x000000000000000000000000000000000029813a9a42076eacbcac2286e88533", + "0x0000000000000000000000000000001ce5500d435749132ce24e82c8b85bb103", + "0x00000000000000000000000000000000002e85fd181eb14317e77b5a2e420fec", + "0x000000000000000000000000000000ac83b50241c7a1a4b44fa2c5c9e97e9dda", + "0x000000000000000000000000000000000009d4ba5a4ba58befdf8d4742cf2eb6", + "0x000000000000000000000000000000e2f574ecf3980ed3b67dcd1d7ed9685c92", + "0x000000000000000000000000000000000024c60f17d585a20430c31dd20f9342", + "0x000000000000000000000000000000a8f3a498e8d170091222549f91addb05b6", + "0x000000000000000000000000000000000027a3ec67a6a191c31d3786df7008a6", + "0x0000000000000000000000000000003fa2e22424dfc8d0054ec6bbd134a2b0c8", + "0x0000000000000000000000000000000000059b3ede255d172aa6c311f5b6ccd5", + "0x0000000000000000000000000000001594960df1a79583ac30bec748c7485853", + "0x0000000000000000000000000000000000027abcebe3b69452a9b9c04633c92d", + "0x0000000000000000000000000000006533248587d69bfc7a30d589c6c48635a1", + "0x00000000000000000000000000000000000cec6d65fc6a996b7cd53d5fae94e0", + "0x000000000000000000000000000000ee0310864efdd6fb69a49443d0fe9a3dfa", + "0x00000000000000000000000000000000002d9044697e7e3aa54e694cdf844035", + "0x000000000000000000000000000000a9251a3b8a7231f992190feb90388c8006", + "0x00000000000000000000000000000000001fe160490e82d3ec52b80e48305a2b", + "0x000000000000000000000000000000452b50c9d1b328206aae1c02bce63fd63f", + "0x0000000000000000000000000000000000132a166f36321c6fa4af24d94d7caa", + "0x000000000000000000000000000000a1ed7394f2f3b2fc24cec375a5b94bebdd", + "0x00000000000000000000000000000000000050a9c522b47cabe8a093b8f3cfc8", + "0x000000000000000000000000000000ee8d22e30dedfa9bb0b4b88b553c29f472", + "0x000000000000000000000000000000000010e374b47f6c05dca0f2e6a8089622", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000009225b0768a5f2b2fca802133a236607d93", + "0x000000000000000000000000000000000010a37ebb9ed1ec7fe541db60eedc04", + "0x000000000000000000000000000000433926efbed74978d125bc3887beada7a9", + "0x000000000000000000000000000000000014b329c7843d26db335b388c47dd59", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000000000db6509fde8c38e05d282929864c44561f9", + "0x00000000000000000000000000000000001fb147b4cc39f0ecf514c31d6ddb0d", + "0x00000000000000000000000000000018e1b42c0c0cd40dbcb952e052d8772bb3", + "0x00000000000000000000000000000000001cfd429eb121d2e6008aa798fc8f15", + "0x0000000000000000000000000000000634ee754d3c52d0653ecae52cfe9efaaf", + "0x0000000000000000000000000000000000221440cca686314c9860eb30f2014e", + "0x000000000000000000000000000000467162c2853a0c28162a3cf09b81c1bf2d", + "0x0000000000000000000000000000000000141542ce6959cd779b56b3acba5f13" ] - hash = "0x2222faec2be27b40c5637d56b620859be5b4b9bf21ca587ba9cf332dd8ca1f39" + hash = "0x145cd27f3c0b97d766b693debe76f88e076cb3a4ba18f64395b5d27c82738df1" [inputs.hints] previous_archive_sibling_path = [ @@ -1365,7 +1365,7 @@ new_out_hash_sibling_path = [ ] blobs_fields = [ "0x00000000009c70751800400040000800010040040000000000000000000004cd", - "0x1865c7f4c9ad4163b0ce1997c41d5684accf3ff83aabac7038c9516fb110b4b6", + "0x0c48efdea7f0a7924d7769c75e5701478f6468c91e6909a28a346640e12a455c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x00000000000000000000000000000000000000000000000000000000b7d1b100", "0x00000000000000000000000000000000000000000000000000000000b7d1b101", @@ -2594,7 +2594,7 @@ blobs_fields = [ "0x00000000000000000000000000000000000000000000000000000000b7d1b44d", "0x00000000000000000000000000000000000000000000000000000000b7d1b44e", "0x00000000009c707518004000400008004000400400000000000000000000054b", - "0x071b69e21ffc747c3a3a7293d62aa779c6721b5f8b53b85a0f19bf703ceff3e5", + "0x2425aa5ed2ff588221c7e04be2714739b497fbf4b127a902d64e3d3373123070", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x00000000000000000000000000000000000000000000000000000000b7e5c000", "0x00000000000000000000000000000000000000000000000000000000b7e5c001", @@ -3949,12 +3949,12 @@ blobs_fields = [ "0x00000000000000000000000000000000000000000000000000000000b7e5c34d", "0x00000000000000000000000000000000000000000000000000000000b7e5c34e", "0x0000000000000000000000000000eb8dcdbf0000000000000186000000010002", - "0x00000000000000000040000000000200000000010000000000bf00000006b6c0", + "0x00000000000000000010000000000200000000010000000000bf00000006b6c0", "0x0fb2945d3438d906d88a216364dbfe9760e96001343468610e01d18182d493d0", "0x2326bf220c6839c1856478f0c082f0c5883b2baed0bc222a1fa5e1244184c82b", "0x1e1c597744057b88e39a9780ed087c39b1fc42864e05ef03a59ebd9e96b70b00", "0x2306af8b455a9cbf87331182183be8c0759fd5e1a4f606cea8a9e24efa461759", - "0x18d6aae3ab4a271abd590cb98267825b70de1e9e5c339356e94ea5fc7feba64b", + "0x0c123047b777079e8db5f669ddaf1ffa1c7780d56b21e29f35e4772ac0f6280c", "0x0000000000000000000000000000000000000000000000008c63744300000a20", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", @@ -25941,7 +25941,7 @@ blobs_fields = [ "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000" ] -blobs_hash = "0x00d5e0aefc76388c97ade33001a452a4118563f871c8677c77bf3f4d919a31d8" +blobs_hash = "0x0086689ab3853177e02ef6c9c173fd54165d0531699c67ccd905a34c49e03e07" [inputs.hints.previous_block_header] sponge_blob_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" @@ -26028,13 +26028,13 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 ] [inputs.hints.final_blob_challenges] - z = "0x0a343b62be2fa62c441c838e0c6fe61d06540f6804a27804c2745a9ae3b5873f" + z = "0x2788c4f66ff0c6b019e32b5af747d327c03b46d7a20eb8421c411f398bf7b053" [inputs.hints.final_blob_challenges.gamma] limbs = [ - "0x192ece5c73191885510555f7d1a955", - "0xa1086360fd4a5d4ab060f2639a5c6c", - "0x0ee9" + "0xf1878edc99069747ad48ef4ec47b03", + "0x54c3ca2583cdc30ca1041ec254b306", + "0x0c0e" ] [[inputs.hints.blob_commitments]] @@ -26042,18 +26042,18 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 [inputs.hints.blob_commitments.x] limbs = [ - "0xdadacade6372672375edadfbf03b01", - "0xc1a60cb6e176187d955a7642b70626", - "0x20085df04e76d8b20d03874ff0fe10", - "0x05fd42" + "0x1a654508f70fe5d24da85bbec49a0e", + "0xf4f772ed01aafa935827ca219ca42f", + "0x2beebf0b2bab0dd26807cb5a8a3a79", + "0x1326f1" ] [inputs.hints.blob_commitments.y] limbs = [ - "0xf5c30044f83d9d2eb693f6ae4edee3", - "0xd5a9a37b09ad2ed272c2dd052084ab", - "0xfd7551e2b626d746dccaa24bc828e9", - "0x19819a" + "0xf5c05c8ba34058a2a3838cd5156df7", + "0x2e8d57e86505f439e2489474494027", + "0x81a3cc8342d1eb49d5ef8e3b5b83f4", + "0x009a2e" ] [[inputs.hints.blob_commitments]] diff --git a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-checkpoint-root/Prover.toml b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-checkpoint-root/Prover.toml index 8f445e982fd6..044103d6ab6e 100644 --- a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-checkpoint-root/Prover.toml +++ b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-checkpoint-root/Prover.toml @@ -484,7 +484,7 @@ proof = [ [inputs.previous_rollups.public_inputs] timestamp = "0x0000000000000000000000000000000000000000000000000000000000000186" - block_headers_hash = "0x2dc80c55273e1db1eee524268287b9823c805d24e3a87c0d0623cc4730f36b96" + block_headers_hash = "0x19da69d556647ecb91a3be5ec6cab57a0a051a823ee35e7b6cc9367ffae7579f" out_hash = "0x00746f2611b7b24448263e846ba73bf1861fc6e68dbc605414405a520957a902" accumulated_fees = "0x0000000000000000000000000000000000000000000000000000000000000000" accumulated_mana_used = "0x000000000000000000000000000000000000000000000000000000000006b6c0" @@ -492,7 +492,7 @@ proof = [ [inputs.previous_rollups.public_inputs.constants] chain_id = "0x0000000000000000000000000000000000000000000000000000000000000000" version = "0x0000000000000000000000000000000000000000000000000000000000000000" - vk_tree_root = "0x16b7eeebd9ec7b6b28187878be4fef52c87331b0ebf2e5719fd203a0474402b1" + vk_tree_root = "0x121ed84a634911bbb8a5e986c40e120a016a1713140a6a5ea4f8187a5e07e547" protocol_contracts_hash = "0x08d0026a4983c49638d0910859942c6648d5ea0a3a152c8a376e4983d0a003d9" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" slot_number = "0x000000000000000000000000000000000000000000000000000000000000000f" @@ -512,7 +512,7 @@ proof = [ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000001" [inputs.previous_rollups.public_inputs.new_archive] - root = "0x14be32b2805c0b11d6fcbbecad4ed13d3be1d16b88d4cd068b20c0fc2dce5435" + root = "0x2d52f50f113807342bb8703e7e4bb4eb201f862dc894b0335a90239aedc6d9cf" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000003" [inputs.previous_rollups.public_inputs.start_state.l1_to_l2_message_tree] @@ -532,8 +532,8 @@ root = "0x1a90881964e28a92a419f1d8361c14ac147b6f9175c04fdf57dadf0d7ba781c9" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000080" [inputs.previous_rollups.public_inputs.end_state.l1_to_l2_message_tree] -root = "0x18d6aae3ab4a271abd590cb98267825b70de1e9e5c339356e94ea5fc7feba64b" -next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000400" +root = "0x0c123047b777079e8db5f669ddaf1ffa1c7780d56b21e29f35e4772ac0f6280c" +next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000100" [inputs.previous_rollups.public_inputs.end_state.partial.note_hash_tree] root = "0x2326bf220c6839c1856478f0c082f0c5883b2baed0bc222a1fa5e1244184c82b" @@ -566,21 +566,21 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 squeeze_mode = false [inputs.previous_rollups.public_inputs.end_sponge_blob] - num_absorbed_fields = "0x0000000000000000000000000000000000000000000000000000000000000a25" + num_absorbed_fields = "0x0000000000000000000000000000000000000000000000000000000000000a26" [inputs.previous_rollups.public_inputs.end_sponge_blob.sponge] cache = [ "0x1e1c597744057b88e39a9780ed087c39b1fc42864e05ef03a59ebd9e96b70b00", "0x2306af8b455a9cbf87331182183be8c0759fd5e1a4f606cea8a9e24efa461759", - "0x2326bf220c6839c1856478f0c082f0c5883b2baed0bc222a1fa5e1244184c82b" + "0x0c123047b777079e8db5f669ddaf1ffa1c7780d56b21e29f35e4772ac0f6280c" ] state = [ - "0x04d8a59ea98cbd4eb7f425b8f0bca26a6026565519068ea04e36bae686fd3824", - "0x019fb4519f358bdd14fb5000ad6e6132827a6aa547d4fc6020a02c00671ececc", - "0x1785dae4b5f56f54408796dca53112f34d8adc61cfca80987ce46f3a229993a4", - "0x1158bc703fe8088f5df444cc08d998ae4259b259a85fdae3e2195a82730b1c8e" + "0x18cc50c72512a32c7e9d03e3c75a6c231c64bb8b956e130edd92f0372cde5557", + "0x023a8d485733b7cf37de7afcb7b2ba2cecf58cc222cd6be8f423b5788aa711c4", + "0x0c8ae0e23734ad05a2db5cc7095e1762c2cab0f6cdcd5e8487d9b0c8f74b74f0", + "0x1bd89e098756374d4b55698f5fc2d7c41c3bdf340ce474b99b9b58a7e118affe" ] - cache_size = "0x0000000000000000000000000000000000000000000000000000000000000002" + cache_size = "0x0000000000000000000000000000000000000000000000000000000000000003" squeeze_mode = false [inputs.previous_rollups.public_inputs.start_msg_sponge] @@ -602,19 +602,19 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 squeeze_mode = false [inputs.previous_rollups.public_inputs.end_msg_sponge] - num_absorbed = "0x0000000000000000000000000000000000000000000000000000000000000400" + num_absorbed = "0x0000000000000000000000000000000000000000000000000000000000000100" [inputs.previous_rollups.public_inputs.end_msg_sponge.sponge] cache = [ - "0x00000000000000000000000000000000000000000000000000000000000009db", - "0x00000000000000000000000000000000000000000000000000000000000009d9", - "0x00000000000000000000000000000000000000000000000000000000000009da" + "0x00000000000000000000000000000000000000000000000000000000000006db", + "0x00000000000000000000000000000000000000000000000000000000000006d9", + "0x00000000000000000000000000000000000000000000000000000000000006da" ] state = [ - "0x13a801138d16230fb1088f7fd847ded1c4972fb74bd6e7bc356881f054400aa6", - "0x182a8954baa5425098a60fdbd090ff8918a2ea34cd0f7f52b65422bf666ebfcf", - "0x11e3f79645edb7132868adb47ea5361ff65f7b612d23ebd31b2d2e16f8570918", - "0x2f22e68da640d535dbaa64cc7c81e2b36ada1e9bcb891b371f90220e74493ae0" + "0x0a0aa9eae5277bc3a8414be4a1a279d69f89d765ed17a0123ac83a2547a9cf99", + "0x22e19d5ba8ed0525429b7ac6b29abd221ed25181a9a2085507125e57ad7c3514", + "0x29f22a3e1d331fa7d7d47f6f0f139d765279266c338b405db43ecd66b5ef328c", + "0x1235e52a8b15fc46edb612d48c23715f76bba8eb563b9e29e2519f16612f0faa" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000001" squeeze_mode = false @@ -622,8 +622,8 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 [inputs.previous_rollups.vk_data] leaf_index = "0x000000000000000000000000000000000000000000000000000000000000000f" sibling_path = [ - "0x26f357b4b0e05ad1536d8d7e0708cd0c2d85542ff14dc630f0491f96c435d8ff", - "0x0c8de1eecfa40a64855d7f483a9efec89cab69da7d828685354b7759018eb4fd", + "0x0ac540177ae086f42456dd77c554a20d08529e5e7a66b74f048136d0e778da92", + "0x0beaba6d817ffeb92671c729be0bbb0e0f952849aa3de039d2febbcc3982bd18", "0x1e16cf81772d4ffd1a84e0c458ae6fe2b66031a749e0a28cb82ec1fc5b5c5900", "0x20f1c701d84b280c9f80a517272153688a9e1b92166d1000cfe7c829b7c25f69", "0x25f9ac8d47a2dbcfb13b88ea474667fd2854bf5c901f2f698266864ffe7c21ad", @@ -1237,7 +1237,7 @@ proof = [ [inputs.previous_rollups.public_inputs] timestamp = "0x0000000000000000000000000000000000000000000000000000000000000186" - block_headers_hash = "0x163a07cf637c28ef388c8f88cd7d041cc068b502bbca95dd5e725f567fb8e12e" + block_headers_hash = "0x0684304b86a48a79d3e857a93689fc9a751c49cf0cf9dc2dee05f376e503359e" out_hash = "0x00fab7a43a18caf54d1e3dd82cf6d3def175265507c701576b015603f4dd1b44" accumulated_fees = "0x0000000000000000000000000000000000000000000000000000000000000000" accumulated_mana_used = "0x000000000000000000000000000000000000000000000000000000000006b6c0" @@ -1245,7 +1245,7 @@ proof = [ [inputs.previous_rollups.public_inputs.constants] chain_id = "0x0000000000000000000000000000000000000000000000000000000000000000" version = "0x0000000000000000000000000000000000000000000000000000000000000000" - vk_tree_root = "0x16b7eeebd9ec7b6b28187878be4fef52c87331b0ebf2e5719fd203a0474402b1" + vk_tree_root = "0x121ed84a634911bbb8a5e986c40e120a016a1713140a6a5ea4f8187a5e07e547" protocol_contracts_hash = "0x08d0026a4983c49638d0910859942c6648d5ea0a3a152c8a376e4983d0a003d9" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" slot_number = "0x000000000000000000000000000000000000000000000000000000000000000f" @@ -1261,16 +1261,16 @@ proof = [ fee_per_l2_gas = "0x0000000000000000000000000000000000000000000000000000000000000000" [inputs.previous_rollups.public_inputs.previous_archive] - root = "0x14be32b2805c0b11d6fcbbecad4ed13d3be1d16b88d4cd068b20c0fc2dce5435" + root = "0x2d52f50f113807342bb8703e7e4bb4eb201f862dc894b0335a90239aedc6d9cf" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000003" [inputs.previous_rollups.public_inputs.new_archive] - root = "0x14620b6370c6dd8fefdc1efce06a974389636f92f96017329efa456f2685757a" + root = "0x1b52e2dfc3c009866bdfd89317e4e3e5f323cc9de925781494c1b0c825d90031" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000004" [inputs.previous_rollups.public_inputs.start_state.l1_to_l2_message_tree] -root = "0x18d6aae3ab4a271abd590cb98267825b70de1e9e5c339356e94ea5fc7feba64b" -next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000400" +root = "0x0c123047b777079e8db5f669ddaf1ffa1c7780d56b21e29f35e4772ac0f6280c" +next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000100" [inputs.previous_rollups.public_inputs.start_state.partial.note_hash_tree] root = "0x2326bf220c6839c1856478f0c082f0c5883b2baed0bc222a1fa5e1244184c82b" @@ -1285,8 +1285,8 @@ root = "0x2306af8b455a9cbf87331182183be8c0759fd5e1a4f606cea8a9e24efa461759" next_available_leaf_index = "0x00000000000000000000000000000000000000000000000000000000000000bf" [inputs.previous_rollups.public_inputs.end_state.l1_to_l2_message_tree] -root = "0x18d6aae3ab4a271abd590cb98267825b70de1e9e5c339356e94ea5fc7feba64b" -next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000400" +root = "0x0c123047b777079e8db5f669ddaf1ffa1c7780d56b21e29f35e4772ac0f6280c" +next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000100" [inputs.previous_rollups.public_inputs.end_state.partial.note_hash_tree] root = "0x144f9224dee4aac6eddc5d988e7c6965528d2e08db91cf58989655a68fbfcc52" @@ -1301,73 +1301,73 @@ root = "0x2306af8b455a9cbf87331182183be8c0759fd5e1a4f606cea8a9e24efa461759" next_available_leaf_index = "0x00000000000000000000000000000000000000000000000000000000000000bf" [inputs.previous_rollups.public_inputs.start_sponge_blob] - num_absorbed_fields = "0x0000000000000000000000000000000000000000000000000000000000000a25" + num_absorbed_fields = "0x0000000000000000000000000000000000000000000000000000000000000a26" [inputs.previous_rollups.public_inputs.start_sponge_blob.sponge] cache = [ "0x1e1c597744057b88e39a9780ed087c39b1fc42864e05ef03a59ebd9e96b70b00", "0x2306af8b455a9cbf87331182183be8c0759fd5e1a4f606cea8a9e24efa461759", - "0x2326bf220c6839c1856478f0c082f0c5883b2baed0bc222a1fa5e1244184c82b" + "0x0c123047b777079e8db5f669ddaf1ffa1c7780d56b21e29f35e4772ac0f6280c" ] state = [ - "0x04d8a59ea98cbd4eb7f425b8f0bca26a6026565519068ea04e36bae686fd3824", - "0x019fb4519f358bdd14fb5000ad6e6132827a6aa547d4fc6020a02c00671ececc", - "0x1785dae4b5f56f54408796dca53112f34d8adc61cfca80987ce46f3a229993a4", - "0x1158bc703fe8088f5df444cc08d998ae4259b259a85fdae3e2195a82730b1c8e" + "0x18cc50c72512a32c7e9d03e3c75a6c231c64bb8b956e130edd92f0372cde5557", + "0x023a8d485733b7cf37de7afcb7b2ba2cecf58cc222cd6be8f423b5788aa711c4", + "0x0c8ae0e23734ad05a2db5cc7095e1762c2cab0f6cdcd5e8487d9b0c8f74b74f0", + "0x1bd89e098756374d4b55698f5fc2d7c41c3bdf340ce474b99b9b58a7e118affe" ] - cache_size = "0x0000000000000000000000000000000000000000000000000000000000000002" + cache_size = "0x0000000000000000000000000000000000000000000000000000000000000003" squeeze_mode = false [inputs.previous_rollups.public_inputs.end_sponge_blob] - num_absorbed_fields = "0x0000000000000000000000000000000000000000000000000000000000000ef8" + num_absorbed_fields = "0x0000000000000000000000000000000000000000000000000000000000000efa" [inputs.previous_rollups.public_inputs.end_sponge_blob.sponge] cache = [ + "0x191d19a6ad2b7bba03d122035938544f5e65de24aeaa436cd5e4d977bd014505", "0x2306af8b455a9cbf87331182183be8c0759fd5e1a4f606cea8a9e24efa461759", - "0x144f9224dee4aac6eddc5d988e7c6965528d2e08db91cf58989655a68fbfcc52", - "0x191d19a6ad2b7bba03d122035938544f5e65de24aeaa436cd5e4d977bd014505" + "0x0c123047b777079e8db5f669ddaf1ffa1c7780d56b21e29f35e4772ac0f6280c" ] state = [ - "0x1483731db3869f591442838bdc60333f58f6e38272ae03bfa7570513707cfcaa", - "0x1cf08aac89b287b29cf64e8b1d1a5b96952c1350b74bfd0ee01f3f32f36fb937", - "0x2c26bcd3a97707c1c273f37b02d53302710d5beb3711be10963756a986522c2f", - "0x154b3c690a49988a7ebe98e6c5694d8746970da1db8dcc654ae9ea9f75d6acac" + "0x295b7b0aaf5e2ae00c09ec027a175dd38627dcce25f3d8046aa4c798a3356d45", + "0x2fd41e732c6c6d93451bdef3f4f0db3c86ade4350d95642c04f133cacb669d09", + "0x17e99c7379a9eff2128fbc1db2ac83667b15cf2ccf2fc3eed7d5593af9261e28", + "0x086a8f5c8bb66efbaacb7d012d0a153167394b3366f5402f0502b8387cb90103" ] - cache_size = "0x0000000000000000000000000000000000000000000000000000000000000001" + cache_size = "0x0000000000000000000000000000000000000000000000000000000000000003" squeeze_mode = false [inputs.previous_rollups.public_inputs.start_msg_sponge] - num_absorbed = "0x0000000000000000000000000000000000000000000000000000000000000400" + num_absorbed = "0x0000000000000000000000000000000000000000000000000000000000000100" [inputs.previous_rollups.public_inputs.start_msg_sponge.sponge] cache = [ - "0x00000000000000000000000000000000000000000000000000000000000009db", - "0x00000000000000000000000000000000000000000000000000000000000009d9", - "0x00000000000000000000000000000000000000000000000000000000000009da" + "0x00000000000000000000000000000000000000000000000000000000000006db", + "0x00000000000000000000000000000000000000000000000000000000000006d9", + "0x00000000000000000000000000000000000000000000000000000000000006da" ] state = [ - "0x13a801138d16230fb1088f7fd847ded1c4972fb74bd6e7bc356881f054400aa6", - "0x182a8954baa5425098a60fdbd090ff8918a2ea34cd0f7f52b65422bf666ebfcf", - "0x11e3f79645edb7132868adb47ea5361ff65f7b612d23ebd31b2d2e16f8570918", - "0x2f22e68da640d535dbaa64cc7c81e2b36ada1e9bcb891b371f90220e74493ae0" + "0x0a0aa9eae5277bc3a8414be4a1a279d69f89d765ed17a0123ac83a2547a9cf99", + "0x22e19d5ba8ed0525429b7ac6b29abd221ed25181a9a2085507125e57ad7c3514", + "0x29f22a3e1d331fa7d7d47f6f0f139d765279266c338b405db43ecd66b5ef328c", + "0x1235e52a8b15fc46edb612d48c23715f76bba8eb563b9e29e2519f16612f0faa" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000001" squeeze_mode = false [inputs.previous_rollups.public_inputs.end_msg_sponge] - num_absorbed = "0x0000000000000000000000000000000000000000000000000000000000000400" + num_absorbed = "0x0000000000000000000000000000000000000000000000000000000000000100" [inputs.previous_rollups.public_inputs.end_msg_sponge.sponge] cache = [ - "0x00000000000000000000000000000000000000000000000000000000000009db", - "0x00000000000000000000000000000000000000000000000000000000000009d9", - "0x00000000000000000000000000000000000000000000000000000000000009da" + "0x00000000000000000000000000000000000000000000000000000000000006db", + "0x00000000000000000000000000000000000000000000000000000000000006d9", + "0x00000000000000000000000000000000000000000000000000000000000006da" ] state = [ - "0x13a801138d16230fb1088f7fd847ded1c4972fb74bd6e7bc356881f054400aa6", - "0x182a8954baa5425098a60fdbd090ff8918a2ea34cd0f7f52b65422bf666ebfcf", - "0x11e3f79645edb7132868adb47ea5361ff65f7b612d23ebd31b2d2e16f8570918", - "0x2f22e68da640d535dbaa64cc7c81e2b36ada1e9bcb891b371f90220e74493ae0" + "0x0a0aa9eae5277bc3a8414be4a1a279d69f89d765ed17a0123ac83a2547a9cf99", + "0x22e19d5ba8ed0525429b7ac6b29abd221ed25181a9a2085507125e57ad7c3514", + "0x29f22a3e1d331fa7d7d47f6f0f139d765279266c338b405db43ecd66b5ef328c", + "0x1235e52a8b15fc46edb612d48c23715f76bba8eb563b9e29e2519f16612f0faa" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000001" squeeze_mode = false @@ -1376,7 +1376,7 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 leaf_index = "0x000000000000000000000000000000000000000000000000000000000000000e" sibling_path = [ "0x09b01173f91d75d916624a8f5af30b582c910e76997f09fe0eac95e50fd4a299", - "0x0c8de1eecfa40a64855d7f483a9efec89cab69da7d828685354b7759018eb4fd", + "0x0beaba6d817ffeb92671c729be0bbb0e0f952849aa3de039d2febbcc3982bd18", "0x1e16cf81772d4ffd1a84e0c458ae6fe2b66031a749e0a28cb82ec1fc5b5c5900", "0x20f1c701d84b280c9f80a517272153688a9e1b92166d1000cfe7c829b7c25f69", "0x25f9ac8d47a2dbcfb13b88ea474667fd2854bf5c901f2f698266864ffe7c21ad", @@ -1389,46 +1389,46 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x0000000000000000000000000000000000000000000000000000000000000014", "0x0000000000000000000000000000000000000000000000000000000000000059", "0x0000000000000000000000000000000000000000000000000000000000000005", - "0x00000000000000000000000000000000055335bed07ce39bb2852497353c2e43", - "0x000000000000000000000000000000000029ccc3e08d82b63e3f609ca9a11b21", - "0x000000000000000000000000000000b6fb93450f00b33fe9a1532ee939065bdd", - "0x00000000000000000000000000000000001ca3bea5acf7a213c69e9cedea6063", - "0x000000000000000000000000000000eed6bcfab8f52cd915236147191c33d9b0", - "0x000000000000000000000000000000000020010ed194eacbbce2f4cc9a4a1ea8", - "0x0000000000000000000000000000000c9fe2fc7c4c171230241ef90039872ede", - "0x00000000000000000000000000000000001b6bf93d470ea3cb747e6b8b36aebb", - "0x000000000000000000000000000000ec45ad3b5e6a2873a37dd839f1bbc0e22f", - "0x00000000000000000000000000000000002be9abe61c83327b46d987c1225e1e", - "0x000000000000000000000000000000ffa720c152631593ad6f33c591f5995295", - "0x00000000000000000000000000000000002d41ac0e21dc073238de7daed4dc99", - "0x000000000000000000000000000000b7fc524b1e5246871367220028b45bc277", - "0x00000000000000000000000000000000000f932bf2afaa1d3c4e7bb0e1c0828f", - "0x0000000000000000000000000000005e45b1d94eb6cc61bede9a23665a86287c", - "0x00000000000000000000000000000000000787a9166206d88529ddbbf1dbba6f", - "0x0000000000000000000000000000004ec736da57d0b6d1ed7b90d115f237c92d", - "0x000000000000000000000000000000000000ddaa6be0ceaf2254e8a6f38c4f2b", - "0x000000000000000000000000000000d041b41dee927c7f5b0d5401df413fe468", - "0x000000000000000000000000000000000027cb1182770226911e2ad5585582ec", - "0x000000000000000000000000000000691fe2534ea5b5524ef026f67f860c2bf7", - "0x00000000000000000000000000000000001763103d1a0b2b06e4cfa6c7386217", - "0x000000000000000000000000000000ba24793560e14dfca1fabb4feb09c423aa", - "0x00000000000000000000000000000000000858092262b6649c56c1f7b57c73c9", - "0x0000000000000000000000000000009b21bb7092213c986c17d80a93a2ccd460", - "0x000000000000000000000000000000000008bdb75681aa1130ce8ab0caab0e75", - "0x000000000000000000000000000000d697a01332830f4841b2894bdd18f1f12d", - "0x000000000000000000000000000000000020479ca77ea7517efbf1ec8236224c", - "0x000000000000000000000000000000bdfc44bc64d9e59ba36e6b74acb33c2a34", - "0x000000000000000000000000000000000009f91be000bc87e44e14085581e9f7", - "0x0000000000000000000000000000007bb37d4d77b1845cc82419821967a74cb3", - "0x00000000000000000000000000000000001af3534851f732afd2a52faf09e650", + "0x000000000000000000000000000000e6fa6cc2b4ce900a4cca530592eb33623e", + "0x00000000000000000000000000000000002ba5f421db47ec4860964149d30f8a", + "0x000000000000000000000000000000f954f71b9f2643cd378d72c60f5eeaa933", + "0x00000000000000000000000000000000002052585c2021ec6814b55df2314771", + "0x000000000000000000000000000000b7cca2121e5ab4fe39b7287d1a68571533", + "0x00000000000000000000000000000000001c655d150e21dd44fe7a7b3a47a639", + "0x000000000000000000000000000000e1bef232bc072bdbb53f5a96060294bb68", + "0x00000000000000000000000000000000002c4a204b8bd2c29b08dfd2332df7f9", + "0x0000000000000000000000000000009e4364a35f98d726b67986bf8ffa820e8e", + "0x0000000000000000000000000000000000021c78ace5103acf668ca6081f7ae4", + "0x000000000000000000000000000000f4539caee41e955fdb2e3d6bc5b04504ad", + "0x00000000000000000000000000000000002874dbb96ffb6a494691bd8a496d6f", + "0x0000000000000000000000000000006d80d6976c5f1f0cc41dffbc6767d99928", + "0x000000000000000000000000000000000024d81e29c6068295fe388b1c0b4dee", + "0x00000000000000000000000000000098459b08c7998799df9262459fbf614bab", + "0x00000000000000000000000000000000000c8d209746347b6e9f838f3f9faa8b", + "0x000000000000000000000000000000762800cbff560787a5451da271d033228c", + "0x00000000000000000000000000000000000f510f68ef4666bb0d13fbec65570e", + "0x000000000000000000000000000000140d935449cac890de10b41d7cb1b86bb2", + "0x000000000000000000000000000000000016b0aa857c348c2b85d08ab773699e", + "0x000000000000000000000000000000a1cb5ea33b073c0b3f5a6033495c972bdf", + "0x00000000000000000000000000000000001e291100e271652598ac96e475d52f", + "0x000000000000000000000000000000142253e2c36348104292d4ebcc24b915f9", + "0x00000000000000000000000000000000001b506cbf7b95ec31520e6e39b7b0bc", + "0x0000000000000000000000000000006fbe3f33a2ba9070bcb218d69ccfd047af", + "0x00000000000000000000000000000000001340c2ff0b6aa51d863ec9a632930c", + "0x000000000000000000000000000000647f41f1d6bdc8031bce4b0ce44920ab3d", + "0x00000000000000000000000000000000001854cbe04065342c35ac2fc904ea6a", + "0x000000000000000000000000000000e90c5598e0e7b46ef4d408543f53dd44df", + "0x00000000000000000000000000000000001e71ca00a26e68d0596304c04e6303", + "0x0000000000000000000000000000001db69e25f567374396c685e1fe1356142d", + "0x000000000000000000000000000000000020596d23c75bcfb6fde5a30e81fe53", "0x0000000000000000000000000000001eee81b23a887f299049b14c11e98460d6", "0x00000000000000000000000000000000002a56ce41f6b0be13b9c26747621b82", "0x000000000000000000000000000000d5827d6338c78656c0d12ca1aea6ef2c7c", "0x00000000000000000000000000000000001aa98f2de3ddda547d8f6de4e725de", - "0x000000000000000000000000000000b2377e9da1200c2558886606832f23d44a", - "0x00000000000000000000000000000000000e9f3ac5795f58502a89bd667f2a89", - "0x00000000000000000000000000000045fd2523f98dc58b742de998c1933a6fe6", - "0x000000000000000000000000000000000010e3df2ff71b2f689d35326c3127e9", + "0x000000000000000000000000000000c1fc6bab9776c90d2d9a274a3d3aa63986", + "0x0000000000000000000000000000000000071c5e2591ebba51a2e0b82b7d039a", + "0x000000000000000000000000000000d16df194d44e261e75467cf08d420e3877", + "0x00000000000000000000000000000000001d4b970e13000b149abae107561ff3", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", @@ -1449,60 +1449,60 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x000000000000000000000000000000e628b5eaa91be82614173f2257a6018d1a", - "0x0000000000000000000000000000000000203b4ead4f592b58d91fb310c748e3", - "0x000000000000000000000000000000178601dbb37eab4ec8ab23b6dc2b946051", - "0x000000000000000000000000000000000012d8b3171c16a2e6eba78e0baa877d", - "0x0000000000000000000000000000001cc1e904bd59e45bf3ec74fe790cd7c705", - "0x00000000000000000000000000000000000f3bd76e92a6cbbbd4c89a79217510", - "0x000000000000000000000000000000263356978193c38d4493d9a1987aaf4d71", - "0x00000000000000000000000000000000002254e425d9c316f98accf10da23563", - "0x0000000000000000000000000000000ba0ea7990ee3606678d39c5f351442b7a", - "0x000000000000000000000000000000000001d7c5e7076db7bb0d090c4fbb6fbc", - "0x0000000000000000000000000000000d34404703ca13b13d99e7d6b9dc8089e3", - "0x00000000000000000000000000000000001cdd65b0fffb9b88ff50d4d5003247", - "0x000000000000000000000000000000289cfc71d9cf1c696c41efafd897c82a3a", - "0x000000000000000000000000000000000010be80d1fbb634a959aabc2342adf5", - "0x000000000000000000000000000000c3633db95f156adcbc622aa9711d669d9e", - "0x000000000000000000000000000000000029e779becd0ca3c975ab04ebc01657", - "0x0000000000000000000000000000002baeb2e26bae966e331142778d98b1b2f8", - "0x000000000000000000000000000000000027633b9da357677c069a172b15e6a5", - "0x000000000000000000000000000000256a4b1212a7bcd54a30577ade9a8da327", - "0x00000000000000000000000000000000001171df6d52478245528b59d4e4440e", - "0x0000000000000000000000000000005f744815910d2f12f4d34b61cd85dcc027", - "0x00000000000000000000000000000000002fdbad335d646c942e641ce72bddec", - "0x000000000000000000000000000000adf30adcac70bc45bec04af7c4e5e474f2", - "0x0000000000000000000000000000000000079785403cbf80dbcea12d8a06a06a", - "0x000000000000000000000000000000408649668bf251852a5262c6fac4ee62ec", - "0x0000000000000000000000000000000000088ced21c90c247772ab30b7eefb7d", - "0x0000000000000000000000000000005cd1adcfbfe3c58479bc30a27711141bfb", - "0x0000000000000000000000000000000000060091d1a0a170cc2bee77460a362a", - "0x00000000000000000000000000000082e16497cffbe3bcf84da485af7da9ec9e", - "0x000000000000000000000000000000000013a60aba91a03bf1f20c50a65a0c88", - "0x000000000000000000000000000000f106a142f1b718c9fa5c314dcc7de0c51d", - "0x000000000000000000000000000000000011f5b1f786f12c37faebd7dcdbda1c", - "0x000000000000000000000000000000fc3b2328300cb91129b21d9c4db048a4a3", - "0x00000000000000000000000000000000001e9946865275a0d309e2b1322ed412", - "0x000000000000000000000000000000221cf0459a26c211a9c6213ddcc0c94242", - "0x00000000000000000000000000000000002913083583d90997f84efc4ea4392c", - "0x000000000000000000000000000000f9d69c226a1a305aa66086fef4521bf6b4", - "0x00000000000000000000000000000000000ebb1b848a2a8ac0339d4b75b68499", - "0x000000000000000000000000000000e8580bf114db2142474bc6c30dbed4021d", - "0x0000000000000000000000000000000000271d9f91c3f794f595488e3d0ad26d", - "0x000000000000000000000000000000518174aabb50077b68a5fdc0083edeedd3", - "0x000000000000000000000000000000000017ef8124abc878c573ab407e72c228", - "0x0000000000000000000000000000005eb34d8f6177d57e9ff3a862cfcf45716a", - "0x00000000000000000000000000000000001b050888d38f68885af4ba9bc1f580", - "0x00000000000000000000000000000043ebdf59e671a1a45b700fc06f9d0d9a8f", - "0x00000000000000000000000000000000002d4fa03da9c379db7453843e5ec0f4", - "0x00000000000000000000000000000081adf66e0638b7fe69497366c642770021", - "0x00000000000000000000000000000000000971ff41a9f70a97836def36037298", - "0x000000000000000000000000000000711e5d0db718b86d164064cf225a73e955", - "0x00000000000000000000000000000000000509072de771f04b34b1ef812c5131", - "0x0000000000000000000000000000009cc841d26329d4d25dbe90c777ebff2d27", - "0x00000000000000000000000000000000002763275f03558c4df29e509d1b6927" + "0x0000000000000000000000000000005528e34bbc9008f235eaa9b1fb3e492bb6", + "0x00000000000000000000000000000000001e96dfcdf5c833fe582d284cfb1127", + "0x00000000000000000000000000000020efbec39b10bf1c21b008567a7a17f193", + "0x00000000000000000000000000000000002dd00623040edd90a0f905df1ee8b1", + "0x000000000000000000000000000000d64164c5e4df1345be48974245e69fb0e8", + "0x0000000000000000000000000000000000202daa8b7646d9d48cf3092c1d40a4", + "0x000000000000000000000000000000083e603de933d2705824c999b7938273ce", + "0x000000000000000000000000000000000024f167e27f5353e1d72d86e8afc12a", + "0x0000000000000000000000000000002d05d4d2469c9e9adeea9f848ca7f77368", + "0x00000000000000000000000000000000002a208b36d2c05e17e021e82518f236", + "0x000000000000000000000000000000c594696fec6a6663c1c617eb7e9252cc04", + "0x0000000000000000000000000000000000279437df02d8c75d8c333ca6c55446", + "0x000000000000000000000000000000939fdb55c142d3e40cb4776cf103ff5bb8", + "0x0000000000000000000000000000000000124b5475a92494c6c4614571c6f84a", + "0x0000000000000000000000000000003374c665b6d450a1a292770583a04e53bc", + "0x0000000000000000000000000000000000243aa67f9a569907838154cd732034", + "0x0000000000000000000000000000001f894d69bbb0ef28f3a87b7a83bf283217", + "0x000000000000000000000000000000000020f6038f38643d71e786c4e9afb789", + "0x0000000000000000000000000000003a2409099e43152b484fb30468412ad1e6", + "0x00000000000000000000000000000000002decfb60557edde6b0896b12515a9d", + "0x0000000000000000000000000000005129b856813afb7862f5da8ea76596259b", + "0x000000000000000000000000000000000013b69dbb43385e826988b16abd9c2d", + "0x0000000000000000000000000000005f5d1483ae12bf604b1f98ccab7327b8c8", + "0x000000000000000000000000000000000011007e3f754dbe876ce960400dbc0e", + "0x000000000000000000000000000000bf8f5fd66fbd0389ce92b19d18d97660c9", + "0x0000000000000000000000000000000000111dac45f3f4c15283ac50638c549f", + "0x000000000000000000000000000000e932d917fd760479237c07685fd070be17", + "0x0000000000000000000000000000000000170a2458ec6958f23a1b1879ce5888", + "0x00000000000000000000000000000052c59a55f865d8de2f11671201203d4fcd", + "0x0000000000000000000000000000000000056938ee424410ee76882439f71d85", + "0x000000000000000000000000000000a18ab0a8506a1ae70534aa6c34b07587f4", + "0x0000000000000000000000000000000000164a49247f146467af05ad3cf2fa51", + "0x00000000000000000000000000000007e3ae149237fca52d55fe1b2955b74578", + "0x000000000000000000000000000000000003720a8ca3e549ea64019ffdec959c", + "0x000000000000000000000000000000cb7121e2fcf5de366d98198905237e2df8", + "0x00000000000000000000000000000000002dd6f395a9e42cf4c4b8d8f9bca46c", + "0x000000000000000000000000000000991d750e8b9cbef6fa84b16df9c7a4d4a3", + "0x00000000000000000000000000000000001c432157e139787ccea464e841706a", + "0x00000000000000000000000000000085d593e8d302515ac699b015b9c6f5e4aa", + "0x00000000000000000000000000000000001a22275587f67f1e9d32014f904fba", + "0x000000000000000000000000000000529a6e562d4ed309fc6541966ee4afc972", + "0x00000000000000000000000000000000001d87693a9f4663b02938060e510977", + "0x00000000000000000000000000000039fe4f2d5de006cd3813c2b7732ed7941e", + "0x00000000000000000000000000000000001375d7a4bcf3944a2245f53d6d63dc", + "0x000000000000000000000000000000b1513695eb651129bbfef3f4a7d8a97e1e", + "0x00000000000000000000000000000000000857becf651251725a3495fa64158f", + "0x000000000000000000000000000000ad7b5e3da01371c4d1d2c686714dd5b715", + "0x000000000000000000000000000000000013fa8c7518f259a681d8218dd176fa", + "0x0000000000000000000000000000001d791ffd19e3b0f7bfe2f5a96f080faa09", + "0x00000000000000000000000000000000002a8467a433dd8940023eb9c32cbf9c", + "0x000000000000000000000000000000289adc8d569087d1e36b38fac16b3fb00f", + "0x0000000000000000000000000000000000128a5ac3e8f32638360a6f0dd0aed9" ] - hash = "0x26f357b4b0e05ad1536d8d7e0708cd0c2d85542ff14dc630f0491f96c435d8ff" + hash = "0x0ac540177ae086f42456dd77c554a20d08529e5e7a66b74f048136d0e778da92" [inputs.inbox_parity] proof = [ @@ -1919,92 +1919,92 @@ proof = [ ] [inputs.inbox_parity.public_inputs] - in_hash = "0x00aa91330eafec1db9b1ca2e1733b213a28bfde0499aca2506acc8c00aae7ba3" + in_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" start_rolling_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" - end_rolling_hash = "0x005b0c15d0f641e148adfec120a12eadbf8343e009d350aa593b6d78dbae9568" - num_msgs = "0x0000000000000000000000000000000000000000000000000000000000000400" - vk_tree_root = "0x16b7eeebd9ec7b6b28187878be4fef52c87331b0ebf2e5719fd203a0474402b1" + end_rolling_hash = "0x00a48b67f417042d5231ac634d609fe03a3884c4757a2afe457a7d1c6ca063ba" + num_msgs = "0x0000000000000000000000000000000000000000000000000000000000000100" + vk_tree_root = "0x121ed84a634911bbb8a5e986c40e120a016a1713140a6a5ea4f8187a5e07e547" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" [inputs.inbox_parity.public_inputs.end_sponge] - num_absorbed = "0x0000000000000000000000000000000000000000000000000000000000000400" + num_absorbed = "0x0000000000000000000000000000000000000000000000000000000000000100" [inputs.inbox_parity.public_inputs.end_sponge.sponge] cache = [ - "0x00000000000000000000000000000000000000000000000000000000000009db", - "0x00000000000000000000000000000000000000000000000000000000000009d9", - "0x00000000000000000000000000000000000000000000000000000000000009da" + "0x00000000000000000000000000000000000000000000000000000000000006db", + "0x00000000000000000000000000000000000000000000000000000000000006d9", + "0x00000000000000000000000000000000000000000000000000000000000006da" ] state = [ - "0x13a801138d16230fb1088f7fd847ded1c4972fb74bd6e7bc356881f054400aa6", - "0x182a8954baa5425098a60fdbd090ff8918a2ea34cd0f7f52b65422bf666ebfcf", - "0x11e3f79645edb7132868adb47ea5361ff65f7b612d23ebd31b2d2e16f8570918", - "0x2f22e68da640d535dbaa64cc7c81e2b36ada1e9bcb891b371f90220e74493ae0" + "0x0a0aa9eae5277bc3a8414be4a1a279d69f89d765ed17a0123ac83a2547a9cf99", + "0x22e19d5ba8ed0525429b7ac6b29abd221ed25181a9a2085507125e57ad7c3514", + "0x29f22a3e1d331fa7d7d47f6f0f139d765279266c338b405db43ecd66b5ef328c", + "0x1235e52a8b15fc46edb612d48c23715f76bba8eb563b9e29e2519f16612f0faa" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000001" squeeze_mode = false [inputs.inbox_parity.vk_data] - leaf_index = "0x000000000000000000000000000000000000000000000000000000000000004c" + leaf_index = "0x000000000000000000000000000000000000000000000000000000000000004b" sibling_path = [ - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x19f1a0c09db4cd026f686e9c8fb45501a9fefb4eb1b4c6c328a51343a0094eeb", - "0x06dec47368fa76ba945eee4191c7ad19adacfc7f6caeca5a6490826064124503", + "0x23b7f87d31bcf8101da7e60a2ee1f304b7bc37bb270a910be379c041a2807ccf", + "0x1d438b6650ee2b4a994c0eec98be4e2fd6f843225ad6fb72d292d652cfe1aa2b", + "0x245dbc47d6efe6f6761074906fb89ede81b85145a02ce07d32ddc39723b6bb28", "0x21ed90cd61698dc1e3dfc970eefcedc837dfedf46c2c4e594ce076ee8fba9386", "0x1e20ad4181460cbfdc74ca773502c59b890f184efe300ebad895956d318422da", "0x1434e6e2d5db1053ab8a3be58704509c799ee17e109c77f441f7bf1755400249", - "0x2e599507c72b52b5337c34a023c60717a53366b20ca669172a3021ec1abd8864" + "0x233f4d8335f80ad820a004f575aa5acb4ed91b8e598bd77a024098baa78ce50d" ] [inputs.inbox_parity.vk_data.vk] key = [ - "0x0000000000000000000000000000000000000000000000000000000000000018", + "0x0000000000000000000000000000000000000000000000000000000000000016", "0x0000000000000000000000000000000000000000000000000000000000000018", "0x0000000000000000000000000000000000000000000000000000000000000005", - "0x00000000000000000000000000000013e27e8ee7ae87b078e0e8a84dfb6d03ae", - "0x00000000000000000000000000000000002c74885cce2f6170022d2782036e20", - "0x0000000000000000000000000000000692577d50a6a7de2fb6ea1f98bb873d12", - "0x00000000000000000000000000000000002d70bc332855138017034c10cd8430", - "0x000000000000000000000000000000e81d2f144589cd473a1f91b40233d31701", - "0x0000000000000000000000000000000000146c41acc0d5344668872928402a6b", - "0x000000000000000000000000000000a0d36ca94b988fd4988f1b960fa14efa65", - "0x00000000000000000000000000000000000266004c450687e951cc66d3535d91", - "0x00000000000000000000000000000051af42269b89eea9e2ef9d5a985a31796a", - "0x0000000000000000000000000000000000135277662b72a9817181a3abbab756", - "0x0000000000000000000000000000007a7f1bec18169d119658ddc940ebdf2a0c", - "0x00000000000000000000000000000000001bfaa40105cf97387bc0b39706a119", - "0x000000000000000000000000000000de48af8ae9bfc71db33bc45b4bd57aa030", - "0x0000000000000000000000000000000000029816bdef484c620a15198c4eac09", - "0x000000000000000000000000000000887216af11a9cc803aace28926493d8f0b", - "0x0000000000000000000000000000000000141bba0115846cf35a3fc53dfeb036", - "0x000000000000000000000000000000e4f5ab585fc19d90660c2a90e734db18cd", - "0x000000000000000000000000000000000013fa21bebb58188f81dc206c6ec649", - "0x000000000000000000000000000000a67b3c059ce2374ad92c22ca68ae3305f7", - "0x000000000000000000000000000000000020fe7cd7b7e9e3c0dd0429fe17b23d", - "0x000000000000000000000000000000cfeb80c561ff7e8911d52ec9bab198fb82", - "0x00000000000000000000000000000000002e6c00e9c4e9cf4b960f760149296f", - "0x00000000000000000000000000000030f987ee91930fd32f90c1c4ffae02eba0", - "0x00000000000000000000000000000000002e7565ee181a20320746d6563fee23", - "0x0000000000000000000000000000005122de2df8de92502233de3b7f4d2beadc", - "0x00000000000000000000000000000000002bc5b7b4a2893e3ba2956380cc732b", - "0x000000000000000000000000000000439afd0e31207ed98521bd53f472254c6d", - "0x00000000000000000000000000000000000b75c19eb8201ae71340c527b64e14", - "0x000000000000000000000000000000de089b6bd03e2b6fba51dbb5ab10a57301", - "0x0000000000000000000000000000000000050ee900cb8cdd87217e53c24cf30e", - "0x000000000000000000000000000000ac323aa8e9594d55f1dbb83286db086f67", - "0x00000000000000000000000000000000002c7515131fa378dbf0b8f7c3d34bd2", + "0x000000000000000000000000000000dae09d98202a22c6ac15261b4f252d6cbe", + "0x0000000000000000000000000000000000240dcd2511fcf65ee83534a8135f60", + "0x000000000000000000000000000000302ce523364a28ebc6ad0ec16bfec27e21", + "0x00000000000000000000000000000000000bc5966879a70e0a9eb113501c6726", + "0x00000000000000000000000000000016c5041a6b2d62884e241c5b165f59e7d7", + "0x00000000000000000000000000000000001e5602a431a4a6dcb0e9b3a5792685", + "0x00000000000000000000000000000077c443a58d6674cf4d0611911536d64b4f", + "0x00000000000000000000000000000000002bc3ba27abe0113b96e9dfc96b52d8", + "0x00000000000000000000000000000012d2e28f1542d8db84bc45c6048f6d1a19", + "0x00000000000000000000000000000000002c77da6f3d448fa9d4b6b9ca65b08a", + "0x000000000000000000000000000000e8db81d3dadf826307d4f32cf036b2e8cf", + "0x000000000000000000000000000000000007cd425cfb2b99f21ecf7168075b85", + "0x000000000000000000000000000000ce73196c354127662893883f0fdfacb3d1", + "0x00000000000000000000000000000000001aedd98fbd7e65b081f4701dc0450e", + "0x000000000000000000000000000000d29ace52d594a343b643422e8298f07e2f", + "0x00000000000000000000000000000000000f1cfa4be8a4cb6f5aefbeb0df0c7b", + "0x000000000000000000000000000000c96a4f395943376b11aa391daaa8edf663", + "0x00000000000000000000000000000000000ac0260e687c759f0b13dfcfee3bfc", + "0x0000000000000000000000000000007f895348a081e9fcfce9286c94db5d1521", + "0x00000000000000000000000000000000002d4f1a03f8fcd1583f9e129266246b", + "0x0000000000000000000000000000007a4131bf4eb9c0db3da33eda029e892b13", + "0x0000000000000000000000000000000000076aa7f44082099313ccaa9846ff8f", + "0x000000000000000000000000000000ee1f5ca62037c8b8899104b078ea2077b0", + "0x000000000000000000000000000000000007b885b2ac8c0368ba5b7e4741603f", + "0x0000000000000000000000000000006c5267dd1f95070973a140b3f057813bc0", + "0x0000000000000000000000000000000000055f6b1ec5456ec1621d94632ccdb3", + "0x0000000000000000000000000000001eb20e9b969496970af54378f350e4919f", + "0x00000000000000000000000000000000002f0246ac684f2ac92acba58a5dc773", + "0x00000000000000000000000000000062471b29b5c9f7c5c8d17efc4ad90fd523", + "0x0000000000000000000000000000000000234b08c34fe4311510b6cf295edb2b", + "0x000000000000000000000000000000eaf8cb39542ad42d14ecf1802fd9d9c355", + "0x00000000000000000000000000000000000b0f83076962b5659cbdce6fbeb251", "0x0000000000000000000000000000001eee81b23a887f299049b14c11e98460d6", "0x00000000000000000000000000000000002a56ce41f6b0be13b9c26747621b82", "0x000000000000000000000000000000d5827d6338c78656c0d12ca1aea6ef2c7c", "0x00000000000000000000000000000000001aa98f2de3ddda547d8f6de4e725de", - "0x0000000000000000000000000000000c50ce31e59e68b8dc9dd274400e8e0ee9", - "0x00000000000000000000000000000000001315191c92d1fb007b10e8d441a566", - "0x00000000000000000000000000000034f837d7bd4004456531be2379b9e95e49", - "0x00000000000000000000000000000000002108e529c51f15d70075d78abd23e8", - "0x000000000000000000000000000000b35c952e777e933293105c4a17565f04b9", - "0x0000000000000000000000000000000000178760dda7490ee7b71feb4a0eea59", - "0x0000000000000000000000000000001b196882d64e838e302abef2d5827bf622", - "0x00000000000000000000000000000000000505566c9fb34a0b96addd4941786b", + "0x000000000000000000000000000000ee963a9acacfe2ab5b48db9af317882ad4", + "0x0000000000000000000000000000000000042a9670e4e41ed527d1be6644f0e4", + "0x00000000000000000000000000000046dc668b76fbac5f163faf2aabd8b458d7", + "0x000000000000000000000000000000000017351eba93572036da206797c69aa1", + "0x0000000000000000000000000000007439344271b5f8c01d662783ced69a92ef", + "0x00000000000000000000000000000000001eb26568e7dd11be5fae8b3e89513f", + "0x0000000000000000000000000000005b8b260669dba29d5623fc60bda423470a", + "0x0000000000000000000000000000000000111205e8e77ea2cd11bfb786f2d266", "0x0000000000000000000000000000001f2c696a036d939c1c6030b6865756faab", "0x00000000000000000000000000000000002571340ad307cfa8a523f6f8914ea7", "0x0000000000000000000000000000007bc1893b89ad56380159cd60f6fa00b992", @@ -2021,60 +2021,60 @@ proof = [ "0x000000000000000000000000000000000008f6999fc49f20dc6deda265a9c248", "0x0000000000000000000000000000001c6634543018006fa8da47114274528b7e", "0x0000000000000000000000000000000000245bc7cfa90e6b8a98a064db058e7d", - "0x0000000000000000000000000000007f57d25276da88d9b2299b82ca2110a85a", - "0x00000000000000000000000000000000001592a6098919d0d82181b41bceb4b8", - "0x000000000000000000000000000000225dfd2449ca643ca558292aebea034c2b", - "0x000000000000000000000000000000000020699876518bed469bda6fbe5c99a8", - "0x0000000000000000000000000000001f0718e9b2dab2a06af6bba5f991b1aeff", - "0x000000000000000000000000000000000029e7dceab9fd3733a4a2713b49fd11", - "0x0000000000000000000000000000004a512111969cb0e314e551322e4099e157", - "0x000000000000000000000000000000000028f3abcfc1e677ddad9ffa8fe7e0a6", - "0x000000000000000000000000000000e4406844c7446977df592d9ad61d1b56f2", - "0x000000000000000000000000000000000002ebd233103c5acd9f1359e1665478", - "0x000000000000000000000000000000b7af55335650e157acffbcc0ed3519ea30", - "0x00000000000000000000000000000000000ecb72e0d4d237416e75c9fd273607", - "0x000000000000000000000000000000ee9cb81f66d70a51afa22dacdaac78962e", - "0x00000000000000000000000000000000002f91af48a647f7fad43ae7996ae4ca", - "0x000000000000000000000000000000c9f2a045318dd5f1fc6ac44968aec340cc", - "0x000000000000000000000000000000000019f643a87c01adfa986a51d37207ca", - "0x0000000000000000000000000000003dc591787b7963d6b1c497903331df5359", - "0x000000000000000000000000000000000001fdcad4552407a181cb9c6532a3f3", - "0x000000000000000000000000000000579f9d0b0dfc60181c9167588d2250a730", - "0x00000000000000000000000000000000000113952c5a14d3883c2deb9806af0d", - "0x000000000000000000000000000000e83a62b64386e2b31aff8d5a69e2f6da0e", - "0x00000000000000000000000000000000001dc82f7a3dfb603a136816bc1f7348", - "0x0000000000000000000000000000008f826f2ed8ed4bfe496815e1c0a72963db", - "0x00000000000000000000000000000000001df6ef06f7f92452ba6b25de232ee8", - "0x0000000000000000000000000000008e6d47a2f0473350f67eed42d663c052fd", - "0x00000000000000000000000000000000000c68ccfc2049c43893739edfaf6af4", - "0x00000000000000000000000000000032e80451bb54640385b8214748a47482d5", - "0x00000000000000000000000000000000000775cc9791f3f3aaa54c4229a8a3cd", - "0x000000000000000000000000000000291f55c9fa3fe4c3cb589c098968a07ba1", - "0x00000000000000000000000000000000001e888cbcc392dbddebea5a471bc3b1", - "0x0000000000000000000000000000009d9dba2e68259dc75569b3565af201d671", - "0x0000000000000000000000000000000000001b6c80929f8089db2588a078886d", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000007723278fd44d9137fd7f73943181fec927", - "0x00000000000000000000000000000000002f4289239983dee23c85caa81f6b7a", - "0x0000000000000000000000000000004fd879e7160788dfea92771fc093fa6ed3", - "0x00000000000000000000000000000000000e53ff20a38a32db810c0e38953849", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x000000000000000000000000000000799e70d12366d4e94ee4d09b507035ce0b", - "0x000000000000000000000000000000000022221e8995f2d5b244fcc1d27b928a", - "0x0000000000000000000000000000007364373df859bfcd1e8310aedc638bafd1", - "0x00000000000000000000000000000000001bc0064388a0c02636fc3297afbe95", - "0x0000000000000000000000000000005a17ea5f4d1be4cb71561b98ded77f3390", - "0x0000000000000000000000000000000000159e2d5660ec3bac3bc3cf2bc93307", - "0x00000000000000000000000000000013b14a2afcb8517986ed10bfeb7f1b2d0a", - "0x00000000000000000000000000000000001b1591e2a64bdc4410582d2f4d5dfc" + "0x000000000000000000000000000000bc53950cdece1378f71426956b09b8c715", + "0x00000000000000000000000000000000001666f474e305c6c725bbddc3d43b45", + "0x000000000000000000000000000000a3f60a21f6002502be611b6a2d43909c0a", + "0x00000000000000000000000000000000000f6ba0625fdf62bf0d82078f25b268", + "0x0000000000000000000000000000001c31e32b6b97fc804ffa233232d7d6152b", + "0x00000000000000000000000000000000000e186393d569195b20f0680817822d", + "0x000000000000000000000000000000791c999533f9a6b63e377b283f7f43f996", + "0x000000000000000000000000000000000029813a9a42076eacbcac2286e88533", + "0x0000000000000000000000000000001ce5500d435749132ce24e82c8b85bb103", + "0x00000000000000000000000000000000002e85fd181eb14317e77b5a2e420fec", + "0x000000000000000000000000000000ac83b50241c7a1a4b44fa2c5c9e97e9dda", + "0x000000000000000000000000000000000009d4ba5a4ba58befdf8d4742cf2eb6", + "0x000000000000000000000000000000e2f574ecf3980ed3b67dcd1d7ed9685c92", + "0x000000000000000000000000000000000024c60f17d585a20430c31dd20f9342", + "0x000000000000000000000000000000a8f3a498e8d170091222549f91addb05b6", + "0x000000000000000000000000000000000027a3ec67a6a191c31d3786df7008a6", + "0x0000000000000000000000000000003fa2e22424dfc8d0054ec6bbd134a2b0c8", + "0x0000000000000000000000000000000000059b3ede255d172aa6c311f5b6ccd5", + "0x0000000000000000000000000000001594960df1a79583ac30bec748c7485853", + "0x0000000000000000000000000000000000027abcebe3b69452a9b9c04633c92d", + "0x0000000000000000000000000000006533248587d69bfc7a30d589c6c48635a1", + "0x00000000000000000000000000000000000cec6d65fc6a996b7cd53d5fae94e0", + "0x000000000000000000000000000000ee0310864efdd6fb69a49443d0fe9a3dfa", + "0x00000000000000000000000000000000002d9044697e7e3aa54e694cdf844035", + "0x000000000000000000000000000000a9251a3b8a7231f992190feb90388c8006", + "0x00000000000000000000000000000000001fe160490e82d3ec52b80e48305a2b", + "0x000000000000000000000000000000452b50c9d1b328206aae1c02bce63fd63f", + "0x0000000000000000000000000000000000132a166f36321c6fa4af24d94d7caa", + "0x000000000000000000000000000000a1ed7394f2f3b2fc24cec375a5b94bebdd", + "0x00000000000000000000000000000000000050a9c522b47cabe8a093b8f3cfc8", + "0x000000000000000000000000000000ee8d22e30dedfa9bb0b4b88b553c29f472", + "0x000000000000000000000000000000000010e374b47f6c05dca0f2e6a8089622", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000009225b0768a5f2b2fca802133a236607d93", + "0x000000000000000000000000000000000010a37ebb9ed1ec7fe541db60eedc04", + "0x000000000000000000000000000000433926efbed74978d125bc3887beada7a9", + "0x000000000000000000000000000000000014b329c7843d26db335b388c47dd59", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000000000db6509fde8c38e05d282929864c44561f9", + "0x00000000000000000000000000000000001fb147b4cc39f0ecf514c31d6ddb0d", + "0x00000000000000000000000000000018e1b42c0c0cd40dbcb952e052d8772bb3", + "0x00000000000000000000000000000000001cfd429eb121d2e6008aa798fc8f15", + "0x0000000000000000000000000000000634ee754d3c52d0653ecae52cfe9efaaf", + "0x0000000000000000000000000000000000221440cca686314c9860eb30f2014e", + "0x000000000000000000000000000000467162c2853a0c28162a3cf09b81c1bf2d", + "0x0000000000000000000000000000000000141542ce6959cd779b56b3acba5f13" ] - hash = "0x2222faec2be27b40c5637d56b620859be5b4b9bf21ca587ba9cf332dd8ca1f39" + hash = "0x145cd27f3c0b97d766b693debe76f88e076cb3a4ba18f64395b5d27c82738df1" [inputs.hints] previous_archive_sibling_path = [ @@ -2118,7 +2118,7 @@ new_out_hash_sibling_path = [ ] blobs_fields = [ "0x00000000009c70751800400040000800010040040000000000000000000004cd", - "0x1f720dbf696e133efe8c9b90e5d581f1d4b803c2712561dc41dcd2cb4b030214", + "0x1b34b153d269e9e6b542df751be74e4fa80d77d8cbba4058d88b053609f0cbb4", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x00000000000000000000000000000000000000000000000000000000b7d1b100", "0x00000000000000000000000000000000000000000000000000000000b7d1b101", @@ -3347,14 +3347,14 @@ blobs_fields = [ "0x00000000000000000000000000000000000000000000000000000000b7d1b44d", "0x00000000000000000000000000000000000000000000000000000000b7d1b44e", "0x0000000000000000000000000000eb8dcdbf0000000000000186000000010001", - "0x0000000000000000004000000000010000000000c0000000008000000006b6c0", + "0x0000000000000000001000000000010000000000c0000000008000000006b6c0", "0x0fb2945d3438d906d88a216364dbfe9760e96001343468610e01d18182d493d0", "0x01612d24a146efc2df9d815a2f733c17486304424577ffb4232fe4cb0c94e1e7", "0x2d9141720c810b831246b0e50c0ad9d4b935418ba2ae8a06c395bb80340ee684", "0x1a90881964e28a92a419f1d8361c14ac147b6f9175c04fdf57dadf0d7ba781c9", - "0x18d6aae3ab4a271abd590cb98267825b70de1e9e5c339356e94ea5fc7feba64b", + "0x0c123047b777079e8db5f669ddaf1ffa1c7780d56b21e29f35e4772ac0f6280c", "0x00000000009c707518004000400008004000400400000000000000000000054b", - "0x12e286f9b1600e72fc263d79ba0e82cfb97d468636dcd3b910a28a5cec241085", + "0x2a04b83b492c532bcd59ee0817a51e209401af1b72e533f189ad63b13f06af7a", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x00000000000000000000000000000000000000000000000000000000b7e5c000", "0x00000000000000000000000000000000000000000000000000000000b7e5c001", @@ -4709,13 +4709,14 @@ blobs_fields = [ "0x00000000000000000000000000000000000000000000000000000000b7e5c34d", "0x00000000000000000000000000000000000000000000000000000000b7e5c34e", "0x0000000000000000000000000000eb8dcdbf0000000000000186000000020001", - "0x00000000000000000040000000000200000000010000000000bf000000000000", - "0x17ed0d01fc0f9551271cfba4270e351858fcba108e95d76959c62156d0d5f6d9", + "0x00000000000000000010000000000200000000010000000000bf000000000000", + "0x0a02b885838fb80d52ac8d0d5c2955fd2298804ed9cea5b239e7e4c0bf5c99cb", "0x2326bf220c6839c1856478f0c082f0c5883b2baed0bc222a1fa5e1244184c82b", "0x1e1c597744057b88e39a9780ed087c39b1fc42864e05ef03a59ebd9e96b70b00", "0x2306af8b455a9cbf87331182183be8c0759fd5e1a4f606cea8a9e24efa461759", + "0x0c123047b777079e8db5f669ddaf1ffa1c7780d56b21e29f35e4772ac0f6280c", "0x00000000009c70751800400040000800010040040000000000000000000004cd", - "0x0c244fa70d229004d8965fbe703d2acd940021070a1e22d477e22b5e04d4ad53", + "0x1285641ab95c19ca786e8a69d97964cf0ffbbb3f8815cb20756357306641f858", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x00000000000000000000000000000000000000000000000000000000b7f9d100", "0x00000000000000000000000000000000000000000000000000000000b7f9d101", @@ -5944,14 +5945,13 @@ blobs_fields = [ "0x00000000000000000000000000000000000000000000000000000000b7f9d44d", "0x00000000000000000000000000000000000000000000000000000000b7f9d44e", "0x0000000000000000000000000000eb8dcdbf0000000000000186000000030001", - "0x00000000000000000040000000000300000000014000000000bf00000006b6c0", - "0x14be32b2805c0b11d6fcbbecad4ed13d3be1d16b88d4cd068b20c0fc2dce5435", + "0x00000000000000000010000000000300000000014000000000bf00000006b6c0", + "0x2d52f50f113807342bb8703e7e4bb4eb201f862dc894b0335a90239aedc6d9cf", "0x144f9224dee4aac6eddc5d988e7c6965528d2e08db91cf58989655a68fbfcc52", "0x191d19a6ad2b7bba03d122035938544f5e65de24aeaa436cd5e4d977bd014505", "0x2306af8b455a9cbf87331182183be8c0759fd5e1a4f606cea8a9e24efa461759", - "0x0000000000000000000000000000000000000000000000008c63744300000ef9", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0c123047b777079e8db5f669ddaf1ffa1c7780d56b21e29f35e4772ac0f6280c", + "0x0000000000000000000000000000000000000000000000008c63744300000efb", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", @@ -26694,7 +26694,7 @@ blobs_fields = [ "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000" ] -blobs_hash = "0x002084e26b915eae596138a59988e45b4708861f60a770a08a1c2d438cb525a9" +blobs_hash = "0x000ea734125275500b91c41f0a6403ef420ab6f8205aced7603aa3c115ba5f50" [inputs.hints.previous_block_header] sponge_blob_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" @@ -26781,13 +26781,13 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 ] [inputs.hints.final_blob_challenges] - z = "0x13657d03aae60cfb6e175e255982b306081ca6450ea900e636adc5d75ca734ec" + z = "0x1badfff9a395f1cbba160a23b9a2da4b460db7aface6f061a5d975176c4ae635" [inputs.hints.final_blob_challenges.gamma] limbs = [ - "0x1d375d45008ed9a71e3c352115e765", - "0x8ec6ae8cb9843aed84c26e812004b7", - "0x2271" + "0xf45a2af2fb6975cec6e8a90ef44c59", + "0xad99e61b0d7286dbac775e4732ceec", + "0x2c38" ] [[inputs.hints.blob_commitments]] @@ -26795,18 +26795,18 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 [inputs.hints.blob_commitments.x] limbs = [ - "0xbdb44c90e79aaea8a89cd0e427a0b8", - "0x18c1f22c4a9cdd4bb89481af15ff89", - "0x7128fdb39068ecbba9288e46360cf7", - "0x053a6f" + "0x19db349658e8929c8f7c9e126d84e5", + "0x8c31a4bd62d4e8c794c595a129cad0", + "0xceeeb3336f5ec6305d1b3dc64c4fbc", + "0x18f110" ] [inputs.hints.blob_commitments.y] limbs = [ - "0x2cf4a144f3721e1bb75ab379ce0507", - "0xb2c829587006c67b4242f7d042c205", - "0x6ea5dd20cae0ed4d51d53ca057486f", - "0x19696c" + "0xf5630b05d4cda607ebc2c0cd1e1e58", + "0xedeeb31855060ba54bbd3e95e7d151", + "0xb523c6412ebf431ea8e91ab4c6e8b0", + "0x0c17bb" ] [[inputs.hints.blob_commitments]] diff --git a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/abis/l1_to_l2_message_bundle.nr b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/abis/l1_to_l2_message_bundle.nr index 24b206855625..dda5fc4593c7 100644 --- a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/abis/l1_to_l2_message_bundle.nr +++ b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/abis/l1_to_l2_message_bundle.nr @@ -4,26 +4,20 @@ use types::{ traits::{Deserialize, Empty, Serialize}, }; -/// A block's L1-to-L2 message bundle: the leaves it inserts into the L1-to-L2 message tree, plus the counts a block -/// root needs to append them and absorb them into the message sponge. +/// A block's L1-to-L2 message bundle: the real message leaves it inserts into the L1-to-L2 message tree, and the +/// count a block root needs to append them and absorb them into the message sponge. /// -/// It carries two counts on purpose. Transitionally the L1-to-L2 tree insert is a fixed padded subtree while the -/// message sponge (and the checkpoint's `InboxParity` proof) work at the real message count, so the two differ: -/// - `num_msgs` — leaves appended to the tree (the padded subtree size for a message-bearing block, 0 for an empty -/// one). -/// - `num_real_msgs` — real (non-padding) messages absorbed into the message sponge. -/// -/// Once the L1-to-L2 tree insert becomes real-count too (the Fast Inbox flip), `num_msgs == num_real_msgs` and -/// `num_real_msgs` is dropped, leaving this struct otherwise unchanged. +/// The `num_msgs` real messages occupy the leading lanes; the same count drives both the compact (unpadded) tree +/// append and the message-sponge absorb, matching the checkpoint's variable-size `InboxParity` proof. Lanes past +/// `num_msgs` are zero padding. #[derive(Deserialize, Eq, Serialize)] pub struct L1ToL2MessageBundle { pub messages: [Field; MAX_L1_TO_L2_MSGS_PER_BLOCK], pub num_msgs: u32, - pub num_real_msgs: u32, } impl Empty for L1ToL2MessageBundle { fn empty() -> Self { - L1ToL2MessageBundle { messages: [0; MAX_L1_TO_L2_MSGS_PER_BLOCK], num_msgs: 0, num_real_msgs: 0 } + L1ToL2MessageBundle { messages: [0; MAX_L1_TO_L2_MSGS_PER_BLOCK], num_msgs: 0 } } } diff --git a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/block_root/components/block_rollup_public_inputs_composer.nr b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/block_root/components/block_rollup_public_inputs_composer.nr index 0863cb925115..a9287b22efbb 100644 --- a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/block_root/components/block_rollup_public_inputs_composer.nr +++ b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/block_root/components/block_rollup_public_inputs_composer.nr @@ -125,10 +125,9 @@ impl BlockRollupPublicInputsComposer { /// tx constants carry the resulting post-bundle snapshot. `start_msg_sponge` is the sponge inherited from the /// previous block (empty for the first block). /// - /// The tree append and the sponge absorb use different counts transitionally (see `L1ToL2MessageBundle`): the tree - /// inserts `bundle.num_msgs` leaves (a full padded subtree for a message-bearing block), while the sponge absorbs - /// only `bundle.num_real_msgs` real leaves so it matches the checkpoint's variable-size `InboxParity` proof. The - /// padding lanes past `num_real_msgs` must be zero. + /// A single `bundle.num_msgs` count drives both the compact (unpadded) tree append and the message-sponge absorb, + /// so the sponge matches the checkpoint's variable-size `InboxParity` proof. The lanes past + /// `num_msgs` must be zero. pub fn with_message_bundle( &mut self, previous_l1_to_l2: AppendOnlyTreeSnapshot, @@ -139,20 +138,10 @@ impl BlockRollupPublicInputsComposer { self.previous_l1_to_l2 = previous_l1_to_l2; self.bundle_applied = true; - // Real messages occupy the leading lanes; everything past `num_real_msgs` is zero padding, including the - // padding that fills out the tree's fixed subtree insert. - assert_trailing_zeros(bundle.messages, bundle.num_real_msgs); + // Real messages occupy the leading lanes; everything past `num_msgs` is zero padding. + assert_trailing_zeros(bundle.messages, bundle.num_msgs); - // The sponge absorbs a prefix of what the tree inserts, so the real count can never exceed the insert count. - assert(bundle.num_real_msgs <= bundle.num_msgs, "more real messages than tree leaves"); - - // NOTE: `num_msgs` (the tree-insert count) is not otherwise pinned in-circuit — nothing here forces it to the - // padded subtree size. On the honest path the orchestrator sets it to `MAX_L1_TO_L2_MSGS_PER_BLOCK` for the - // first block and 0 otherwise, and the resulting `new_l1_to_l2` snapshot is what binds it (first-block variants - // check it against the tx-constants snapshot, ultimately the L1 pending-chain header hash). Like the - // unconstrained `in_hash`, a fully trustless binding is deferred to the Fast Inbox flip. - - // Append the bundle to the l1-to-l2 message tree at its current (arbitrary) next-available index. + // Append the real leaves to the l1-to-l2 message tree at its current (compact, unaligned) next-available index. self.new_l1_to_l2 = append_only_tree::append_leaves_to_snapshot::( previous_l1_to_l2, bundle.messages, @@ -160,10 +149,10 @@ impl BlockRollupPublicInputsComposer { frontier_hint, ); - // Absorb the real leaves into the message sponge, threading it from the previous block. + // Absorb the same real leaves into the message sponge, threading it from the previous block. self.start_msg_sponge = start_msg_sponge; let mut end_msg_sponge = start_msg_sponge; - end_msg_sponge.absorb(bundle.messages, bundle.num_real_msgs); + end_msg_sponge.absorb(bundle.messages, bundle.num_msgs); self.end_msg_sponge = end_msg_sponge; *self @@ -258,23 +247,17 @@ impl BlockRollupPublicInputsComposer { gas_fees: self.constants.gas_fees, }; - // Absorb data for this block into the end sponge blob. + // Absorb data for this block into the end sponge blob. The l1-to-l2 message tree root is absorbed for every + // block: any block can insert its own bundle, so the root is per-block, not + // checkpoint-constant. let mut block_end_sponge_blob = self.end_sponge_blob; - // The l1-to-l2 message tree root is absorbed only for the first block of a checkpoint (the value is shared by - // all blocks in the checkpoint). The first block is exactly the block whose start sponge blob is still at its - // initial state: every block absorbs its block-end fields here, so a block that follows another one inside the - // checkpoint always inherits a sponge blob with fields already absorbed. Since `start_sponge_blob` is a public - // input pinned by the checkpoint root (leftmost must equal `SpongeBlob::init()`) and by the block merge - // (`right.start_sponge_blob == left.end_sponge_blob`), this derivation cannot be steered by the prover. - let is_first_block_in_checkpoint = self.start_sponge_blob.num_absorbed_fields == 0; block_end_sponge_blob.absorb_block_end_data( global_variables, last_archive, state, self.num_txs, self.accumulated_mana_used, - is_first_block_in_checkpoint, ); // Duplicate the `block_end_sponge_blob` so that we can squeeze it. diff --git a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/block_root/tests/mod.nr b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/block_root/tests/mod.nr index d0bd9941008e..ab2fc9b35b80 100644 --- a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/block_root/tests/mod.nr +++ b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/block_root/tests/mod.nr @@ -250,15 +250,10 @@ impl TestBuilder { let _ = self.execute(); } - // The block's message bundle. The unit-test fixture inserts exactly `num_msgs` real leaves into the tree, so the - // tree-insert count and the sponge real-count are equal here; the transitional gap between them (padded tree - // insert vs real-count sponge) is exercised by the orchestrator and full-rollup tests. + // The block's message bundle: exactly `num_msgs` real leaves, appended compactly to the tree and absorbed into the + // message sponge. fn message_bundle(self) -> L1ToL2MessageBundle { - L1ToL2MessageBundle { - messages: self.l1_to_l2_messages, - num_msgs: self.num_msgs, - num_real_msgs: self.num_msgs, - } + L1ToL2MessageBundle { messages: self.l1_to_l2_messages, num_msgs: self.num_msgs } } pub fn assert_expected_public_inputs(self, pi: BlockRollupPublicInputs) { @@ -308,7 +303,6 @@ impl TestBuilder { pi.end_state, self.num_left_txs + self.num_right_txs, pi.accumulated_mana_used, - self.is_first_block_root, ); assert_eq(pi.end_sponge_blob, expected_end_sponge_blob); diff --git a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/block_root/tests/no_txs_tests.nr b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/block_root/tests/no_txs_tests.nr index 7added78a52d..9871f54355c4 100644 --- a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/block_root/tests/no_txs_tests.nr +++ b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/block_root/tests/no_txs_tests.nr @@ -100,7 +100,6 @@ impl TestBuilder { message_bundle: L1ToL2MessageBundle { messages: self.l1_to_l2_messages, num_msgs: self.num_msgs, - num_real_msgs: self.num_msgs, }, l1_to_l2_message_frontier_hint: self.l1_to_l2_message_frontier_hint, new_archive_sibling_path: self.new_archive_sibling_path, @@ -119,8 +118,7 @@ impl TestBuilder { assert_eq(pi.num_blocks(), 1); assert_eq(pi.constants, self.constants); - // --- Sponge blobs: threaded from the provided start, with the block end data absorbed. The l1-to-l2 tree - // root is part of that data only for the checkpoint's first block. --- + // --- Sponge blobs: threaded from the provided start, with the block end data absorbed. --- assert_eq(pi.start_sponge_blob, self.start_sponge_blob); let mut expected_end_sponge_blob = self.start_sponge_blob; expected_end_sponge_blob.absorb_block_end_data( @@ -129,7 +127,6 @@ impl TestBuilder { pi.end_state, 0, // num_txs 0, // total_mana_used - self.is_first_block, ); assert_eq(pi.end_sponge_blob, expected_end_sponge_blob); @@ -219,19 +216,3 @@ fn no_txs_block_root_accepts_empty_bundle() { assert_eq(pi.end_state.l1_to_l2_message_tree, pi.start_state.l1_to_l2_message_tree); assert_eq(pi.end_msg_sponge, pi.start_msg_sponge); } - -// The first block of a checkpoint additionally absorbs the l1-to-l2 message tree root into its blob data, which is -// what distinguishes its blob layout from a later block's. -#[test] -fn first_no_txs_block_absorbs_one_more_blob_field() { - let first_pi = TestBuilder::new_first().execute(); - let later_pi = TestBuilder::new().execute(); - - assert_eq( - first_pi.end_sponge_blob.num_absorbed_fields - - first_pi.start_sponge_blob.num_absorbed_fields, - later_pi.end_sponge_blob.num_absorbed_fields - - later_pi.start_sponge_blob.num_absorbed_fields - + 1, - ); -} diff --git a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/parity/tests/inbox_parity_tests.nr b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/parity/tests/inbox_parity_tests.nr index 87873656e896..1b84dcd5242f 100644 --- a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/parity/tests/inbox_parity_tests.nr +++ b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/parity/tests/inbox_parity_tests.nr @@ -58,7 +58,7 @@ fn sponge_matches_block_root_real_count_absorb() { }; let public_inputs = inbox_parity::execute(private_inputs); - // A block root absorbing the same two real leaves out of its 1024-wide bundle reaches the same sponge. + // A block root absorbing the same two real leaves out of its wider bundle reaches the same sponge. let mut block_sponge = L1ToL2MessageSponge::empty(); block_sponge.absorb([101, 202, 0, 0, 0], 2); assert_eq(public_inputs.end_sponge, block_sponge); diff --git a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-root/Prover.toml b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-root/Prover.toml index c341c1723868..851a93e1c184 100644 --- a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-root/Prover.toml +++ b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-root/Prover.toml @@ -484,9 +484,9 @@ proof = [ [inputs.previous_rollups.public_inputs] start_inbox_rolling_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" - end_inbox_rolling_hash = "0x005b0c15d0f641e148adfec120a12eadbf8343e009d350aa593b6d78dbae9568" + end_inbox_rolling_hash = "0x00a48b67f417042d5231ac634d609fe03a3884c4757a2afe457a7d1c6ca063ba" checkpoint_header_hashes = [ - "0x00da86a7b8a6981c2d9f8c036fd01c628cfacb68bae56be49746a9b793dd82f2", + "0x001fb0c9f41af72e766207c25d9e5f9b697c6eba96e9ba22c7be8e9ffc91083c", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", @@ -523,7 +523,7 @@ proof = [ [inputs.previous_rollups.public_inputs.constants] chain_id = "0x0000000000000000000000000000000000000000000000000000000000000000" version = "0x0000000000000000000000000000000000000000000000000000000000000000" - vk_tree_root = "0x16b7eeebd9ec7b6b28187878be4fef52c87331b0ebf2e5719fd203a0474402b1" + vk_tree_root = "0x121ed84a634911bbb8a5e986c40e120a016a1713140a6a5ea4f8187a5e07e547" protocol_contracts_hash = "0x08d0026a4983c49638d0910859942c6648d5ea0a3a152c8a376e4983d0a003d9" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" @@ -532,7 +532,7 @@ proof = [ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000001" [inputs.previous_rollups.public_inputs.new_archive] - root = "0x057166efca0bc897cfcff75777dd91241f55438e6126342a9221ceb78a9b37f7" + root = "0x27f857ebe6a425096be1cac6e8c23c3f0b27e476062f17d60e52d98612b0c741" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000002" [inputs.previous_rollups.public_inputs.previous_out_hash] @@ -774,15 +774,15 @@ proof = [ ] [inputs.previous_rollups.public_inputs.end_blob_accumulator] - blob_commitments_hash_acc = "0x004774e27332d1b84dd9559cbe2675763a9bf3530cec7e53213465b58242766e" - z_acc = "0x0a343b62be2fa62c441c838e0c6fe61d06540f6804a27804c2745a9ae3b5873f" - gamma_acc = "0x12de24584922d4751f9a740cdda665bf58b7e12070c364c0773516903f609b77" + blob_commitments_hash_acc = "0x00af07b982656fe441bc88560461768f6ab4a776d3e1239ae0c18b7ae1e585b8" + z_acc = "0x2788c4f66ff0c6b019e32b5af747d327c03b46d7a20eb8421c411f398bf7b053" + gamma_acc = "0x04a16ee45a2d57279cdeab611a710c3c1c27ec0eb731fbe14147337a4d883f32" [inputs.previous_rollups.public_inputs.end_blob_accumulator.y_acc] limbs = [ - "0x2cd5355fbfb09280d993572c74398f", - "0xa294ebdb15a35d5c8cc3922cf1a624", - "0x02b8" + "0xb4dc6eb0f4d256c0ce6248d379c888", + "0x555aff4fcfa7f080cd9a282fb8c07c", + "0x40a1" ] [inputs.previous_rollups.public_inputs.end_blob_accumulator.c_acc] @@ -790,35 +790,35 @@ proof = [ [inputs.previous_rollups.public_inputs.end_blob_accumulator.c_acc.x] limbs = [ - "0xdadacade6372672375edadfbf03b01", - "0xc1a60cb6e176187d955a7642b70626", - "0x20085df04e76d8b20d03874ff0fe10", - "0x05fd42" + "0x1a654508f70fe5d24da85bbec49a0e", + "0xf4f772ed01aafa935827ca219ca42f", + "0x2beebf0b2bab0dd26807cb5a8a3a79", + "0x1326f1" ] [inputs.previous_rollups.public_inputs.end_blob_accumulator.c_acc.y] limbs = [ - "0xf5c30044f83d9d2eb693f6ae4edee3", - "0xd5a9a37b09ad2ed272c2dd052084ab", - "0xfd7551e2b626d746dccaa24bc828e9", - "0x19819a" + "0xf5c05c8ba34058a2a3838cd5156df7", + "0x2e8d57e86505f439e2489474494027", + "0x81a3cc8342d1eb49d5ef8e3b5b83f4", + "0x009a2e" ] [inputs.previous_rollups.public_inputs.end_blob_accumulator.gamma_pow_acc] limbs = [ - "0x192ece5c73191885510555f7d1a955", - "0xa1086360fd4a5d4ab060f2639a5c6c", - "0x0ee9" + "0xf1878edc99069747ad48ef4ec47b03", + "0x54c3ca2583cdc30ca1041ec254b306", + "0x0c0e" ] [inputs.previous_rollups.public_inputs.final_blob_challenges] - z = "0x0a343b62be2fa62c441c838e0c6fe61d06540f6804a27804c2745a9ae3b5873f" + z = "0x2788c4f66ff0c6b019e32b5af747d327c03b46d7a20eb8421c411f398bf7b053" [inputs.previous_rollups.public_inputs.final_blob_challenges.gamma] limbs = [ - "0x192ece5c73191885510555f7d1a955", - "0xa1086360fd4a5d4ab060f2639a5c6c", - "0x0ee9" + "0xf1878edc99069747ad48ef4ec47b03", + "0x54c3ca2583cdc30ca1041ec254b306", + "0x0c0e" ] [inputs.previous_rollups.vk_data] @@ -828,7 +828,7 @@ proof = [ "0x151ec971d9b291d78b1d405b001fc9a60d1bc918252466d6a4774554135802ac", "0x29c4f2a137ff5c5b54eb911f6add38eb5ec9d139f54d95a7db432abbae472256", "0x02dfab3acbc7708b1b3654912e946fe846568b8cb0e8af8da6a1845278664e55", - "0x03c532012747965bffe5821f5c98d482a745316ad4854045ea812b17962feb68", + "0x29ef5024937c71b580f6cfb649bd06b4a62241ec399bd579b0d0b2bec78c4cae", "0x0db3ab63c7556f8724d86819156701855018cc79140a052893d67fb67c99c1f1", "0x2a5c50782880ea145b4f04af31f7df9ef0cde48fab69c958aef179c68e836f62" ] @@ -1783,7 +1783,7 @@ proof = [ "0x01e26c7b9970671cded813139e591c3c13ac7832a84a51be4ea0a28843f6f526", "0x29c4f2a137ff5c5b54eb911f6add38eb5ec9d139f54d95a7db432abbae472256", "0x02dfab3acbc7708b1b3654912e946fe846568b8cb0e8af8da6a1845278664e55", - "0x03c532012747965bffe5821f5c98d482a745316ad4854045ea812b17962feb68", + "0x29ef5024937c71b580f6cfb649bd06b4a62241ec399bd579b0d0b2bec78c4cae", "0x0db3ab63c7556f8724d86819156701855018cc79140a052893d67fb67c99c1f1", "0x2a5c50782880ea145b4f04af31f7df9ef0cde48fab69c958aef179c68e836f62" ] diff --git a/noir-projects/fnd/noir-protocol-circuits/crates/types/src/blob_data/sponge_blob.nr b/noir-projects/fnd/noir-protocol-circuits/crates/types/src/blob_data/sponge_blob.nr index 9467ca4bd1eb..98dbb9c5b265 100644 --- a/noir-projects/fnd/noir-protocol-circuits/crates/types/src/blob_data/sponge_blob.nr +++ b/noir-projects/fnd/noir-protocol-circuits/crates/types/src/blob_data/sponge_blob.nr @@ -127,7 +127,6 @@ impl SpongeBlob { state: StateReference, num_txs: u16, total_mana_used: Field, - is_first_block_in_checkpoint: bool, ) { let blob_data = create_block_end_blob_data( global_variables, @@ -137,15 +136,10 @@ impl SpongeBlob { total_mana_used, ); - // Include the last field (the l1-to-l2 message tree root) only for the first block, since this value is the - // same for all blocks in the checkpoint. - let num_blob_data_to_absorb = if is_first_block_in_checkpoint { - blob_data.len() - } else { - blob_data.len() - 1 - }; - - self.absorb(blob_data, num_blob_data_to_absorb); + // The l1-to-l2 message tree root (the last field) is absorbed for every block: any block + // can now insert its own bundle, so the root differs per block and blob-syncing nodes reconstruct each block's + // message-tree root from the blob alone. + self.absorb(blob_data, blob_data.len()); } /// |----------|---------------------------------------------|---------| diff --git a/noir-projects/fnd/noir-protocol-circuits/crates/types/src/constants.nr b/noir-projects/fnd/noir-protocol-circuits/crates/types/src/constants.nr index 689d1dd6a067..24009923249f 100644 --- a/noir-projects/fnd/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/fnd/noir-protocol-circuits/crates/types/src/constants.nr @@ -63,12 +63,16 @@ pub global NULLIFIER_SUBTREE_ROOT_SIBLING_PATH_LENGTH: u32 = NULLIFIER_TREE_HEIGHT - NULLIFIER_SUBTREE_HEIGHT; pub global L1_TO_L2_MSG_SUBTREE_ROOT_SIBLING_PATH_LENGTH: u32 = L1_TO_L2_MSG_TREE_HEIGHT - L1_TO_L2_MSG_SUBTREE_HEIGHT; -// Cap on L1-to-L2 messages bundled into a single L2 block. Transitional value equal to the per-checkpoint cap: the -// constant is deliberately oversized so the first block's bundle can carry a whole checkpoint's padded messages during -// the transition, and drops to 256 at the flip. -pub global MAX_L1_TO_L2_MSGS_PER_BLOCK: u32 = 1024; +// Cap on L1-to-L2 messages bundled into a single L2 block. One quarter of the per-checkpoint +// cap: a checkpoint drains its Inbox consumption across up to four message-bearing blocks. +pub global MAX_L1_TO_L2_MSGS_PER_BLOCK: u32 = 256; // Cap on L1-to-L2 messages consumed by a single checkpoint. Semantically today's NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP. pub global MAX_L1_TO_L2_MSGS_PER_CHECKPOINT: u32 = 1024; +// Minimum bucket age, in seconds, at the start of a checkpoint's build frame for its consumption to be mandatory under +// the streaming Inbox censorship cutoff. One L1 slot: validators cannot be required to act on +// buckets they may not yet have seen. L2 consensus policy consumed by the sequencer and validator (and mirrored by +// ProposeLib on L1); the protocol circuits do not read it. +pub global INBOX_LAG_SECONDS: u32 = 12; // Maximum number of subtrees a L2ToL1Msg unbalanced tree can have. Used when calculating the out hash of a tx. pub global MAX_L2_TO_L1_MSG_SUBTREES_PER_TX: u32 = 3; // ceil(log2(MAX_L2_TO_L1_MSGS_PER_TX)) diff --git a/yarn-project/archiver/src/archiver-store.test.ts b/yarn-project/archiver/src/archiver-store.test.ts index d6f41c6925a8..b79ade3f86c9 100644 --- a/yarn-project/archiver/src/archiver-store.test.ts +++ b/yarn-project/archiver/src/archiver-store.test.ts @@ -462,6 +462,7 @@ describe('Archiver Store', () => { totalManaUsed: 100n, feeAssetPriceModifier: 0n, }); + return block; } it('returns the latest proposed entry when called with no args', async () => { @@ -477,6 +478,13 @@ describe('Archiver Store', () => { expect(result).toBeUndefined(); }); + it('reports the consumed Inbox total of the last block in the checkpoint', async () => { + const block = await addProposedCheckpoint(CheckpointNumber(1), SlotNumber(3), BlockNumber(1)); + + const result = await archiver.getProposedCheckpointData(); + expect(result!.inboxMsgTotal).toBe(BigInt(block.header.state.l1ToL2MessageTree.nextAvailableLeafIndex)); + }); + it('returns the latest proposed entry for tag=proposed', async () => { await addProposedCheckpoint(CheckpointNumber(1), SlotNumber(5), BlockNumber(1)); diff --git a/yarn-project/archiver/src/archiver-sync.test.ts b/yarn-project/archiver/src/archiver-sync.test.ts index c8595cfe9e43..3cfffde67ee8 100644 --- a/yarn-project/archiver/src/archiver-sync.test.ts +++ b/yarn-project/archiver/src/archiver-sync.test.ts @@ -16,6 +16,7 @@ import { sum, times } from '@aztec/foundation/collection'; import { Secp256k1Signer } from '@aztec/foundation/crypto/secp256k1-signer'; import { Fr } from '@aztec/foundation/curves/bn254'; import { EthAddress } from '@aztec/foundation/eth-address'; +import { toArray } from '@aztec/foundation/iterable'; import { type Logger, createLogger } from '@aztec/foundation/log'; import { retryFastUntil } from '@aztec/foundation/retry'; import { TestDateProvider } from '@aztec/foundation/timer'; @@ -23,7 +24,6 @@ import { openTmpStore } from '@aztec/kv-store/lmdb-v2'; import { GENESIS_BLOCK_HEADER_HASH, L2BlockSourceEvents, type L2BlockSourceUpdatedEvent } from '@aztec/stdlib/block'; import type { ProposedCheckpointInput } from '@aztec/stdlib/checkpoint'; import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers'; -import { computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging'; import { CheckpointHeader } from '@aztec/stdlib/rollup'; import { mockCheckpointAndMessages } from '@aztec/stdlib/testing'; import { ConsensusTimetable } from '@aztec/stdlib/timetable'; @@ -37,7 +37,7 @@ import { type MockProxy, mock } from 'jest-mock-extended'; import type { GetBlockReturnType } from 'viem'; import { Archiver, type ArchiverEmitter } from './archiver.js'; -import { BlockOrCheckpointSlotExpiredError, L1ToL2MessagesNotReadyError } from './errors.js'; +import { BlockOrCheckpointSlotExpiredError } from './errors.js'; import type { ArchiverInstrumentation } from './modules/instrumentation.js'; import { ArchiverL1Synchronizer } from './modules/l1_synchronizer.js'; import { type ArchiverDataStores, createArchiverDataStores } from './store/data_stores.js'; @@ -187,6 +187,11 @@ describe('Archiver Sync', () => { await archiver?.stop(); }); + // Returns every stored L1-to-L2 message leaf (as hex), in insertion order (compact indexing). + const getStoredLeaves = async () => + (await toArray(archiverStore.messages.iterateL1ToL2Messages())).map(m => m.leaf.toString()); + const asHex = (leaves: Fr[]) => leaves.map(l => l.toString()); + describe('basic sync', () => { it('syncs l1 to l2 messages and checkpoints', async () => { expect(await archiver.getCheckpointNumber()).toEqual(CheckpointNumber(0)); @@ -221,7 +226,7 @@ describe('Archiver Sync', () => { expect(await archiver.getCheckpointNumber()).toEqual(CheckpointNumber(1)); // Verify messages for checkpoint 1 - expect(await archiver.getL1ToL2Messages(CheckpointNumber(1))).toEqual(msgs1); + expect(await getStoredLeaves()).toEqual(asHex(msgs1)); // Mark checkpoint 1 as proven fake.markCheckpointAsProven(CheckpointNumber(1)); @@ -234,11 +239,8 @@ describe('Archiver Sync', () => { await archiver.syncImmediate(); expect(await archiver.getCheckpointNumber()).toEqual(CheckpointNumber(3)); - // Verify messages for all checkpoints - expect(await archiver.getL1ToL2Messages(CheckpointNumber(1))).toEqual(msgs1); - expect(await archiver.getL1ToL2Messages(CheckpointNumber(2))).toEqual(msgs2); - expect(await archiver.getL1ToL2Messages(CheckpointNumber(3))).toEqual(msgs3); - await expect(archiver.getL1ToL2Messages(CheckpointNumber(4))).rejects.toThrow(L1ToL2MessagesNotReadyError); + // Verify messages for all checkpoints, stored contiguously in insertion order. + expect(await getStoredLeaves()).toEqual(asHex([...msgs1, ...msgs2, ...msgs3])); // Verify private logs are surfaced through the block body. for (const checkpoint of [cp1, cp2, cp3]) { @@ -259,6 +261,34 @@ describe('Archiver Sync', () => { expect( (await archiver.getCheckpoints({ from: CheckpointNumber(1), limit: 100 })).map(b => b.checkpoint.number), ).toEqual([1, 2, 3]); + + // Inbox buckets: each of the three L1 message blocks opened its own bucket, in insertion order. + const t1 = fake.getTimestampAtL1Block(98n); + const t2 = fake.getTimestampAtL1Block(2504n); + const t3 = fake.getTimestampAtL1Block(2511n); + + expect(await archiver.getInboxBucket(1n)).toMatchObject({ + seq: 1n, + msgCount: 3, + totalMsgCount: 3n, + timestamp: t1, + }); + expect(await archiver.getInboxBucket(3n)).toMatchObject({ + seq: 3n, + msgCount: 3, + totalMsgCount: 9n, + timestamp: t3, + }); + + // At-or-before lookups resolve the latest bucket not opened after the given timestamp. + expect((await archiver.getLatestInboxBucketAtOrBefore(t3))!.seq).toEqual(3n); + expect((await archiver.getLatestInboxBucketAtOrBefore(t2))!.seq).toEqual(2n); + expect(await archiver.getLatestInboxBucketAtOrBefore(t1 - 1n)).toBeUndefined(); + + // Messages between buckets, in insertion order. + expect(await archiver.getL1ToL2MessagesBetweenBuckets(0n, 3n)).toEqual([...msgs1, ...msgs2, ...msgs3]); + expect(await archiver.getL1ToL2MessagesBetweenBuckets(1n, 2n)).toEqual(msgs2); + expect(await archiver.getL1ToL2MessagesBetweenBuckets(2n, 3n)).toEqual(msgs3); }, 30_000); it('ignores checkpoint 3 because it has been pruned', async () => { @@ -308,53 +338,6 @@ describe('Archiver Sync', () => { ); }); - it('stop processing if one of the checkpoints has a mismatch inHash', async () => { - expect(await archiver.getCheckpointNumber()).toEqual(CheckpointNumber(0)); - - // Add checkpoint 1 and 2 with all messages visible - await fake.addCheckpoint(CheckpointNumber(1), { - l1BlockNumber: 70n, - messagesL1BlockNumber: 50n, - numL1ToL2Messages: 3, - }); - - await fake.addCheckpoint(CheckpointNumber(2), { - l1BlockNumber: 80n, - messagesL1BlockNumber: 60n, - numL1ToL2Messages: 3, - }); - - // Add checkpoint 3 with 3 messages at L1 block 100n - const { checkpoint: cp3, messages: msgs3 } = await fake.addCheckpoint(CheckpointNumber(3), { - l1BlockNumber: 90n, - messagesL1BlockNumber: 100n, - numL1ToL2Messages: 3, - }); - - // Move last 2 messages of checkpoint 3 to L1 block 103n (beyond current L1 block) - // This simulates partial message visibility - const totalMessages = 3 + 3 + 3; // 9 messages total - fake.moveMessageAtIndexToL1Block(totalMessages - 1, 103n); // Move last message - fake.moveMessageAtIndexToL1Block(totalMessages - 2, 103n); // Move second to last - - // Set current L1 block to 102n - only 1 message from checkpoint 3 will be visible - fake.setL1BlockNumber(102n); - - // The archiver will compute inHash from only the first message, - // which won't match the checkpoint's inHash (computed from all 3 messages) - const visibleMessages = msgs3.slice(0, 1); - const computedInHash = computeInHashFromL1ToL2Messages(visibleMessages); - - // Run archiver (expect failure) - await expect(() => archiver.syncImmediate()).rejects.toThrow( - new RegExp(`mismatch inHash for checkpoint 3.*${computedInHash}.*${cp3.header.inHash}`, 'i'), - ); - - // Should still be at checkpoint 0 since the error prevents checkpoint processing - // (checkpoints 1 and 2 also fail because they're in the same batch) - expect(await archiver.getCheckpointNumber()).toEqual(CheckpointNumber(0)); - }, 10_000); - it('skip event search if no changes found', async () => { const loggerSpy = jest.spyOn(syncLogger, 'debug'); @@ -478,9 +461,9 @@ describe('Archiver Sync', () => { }); it('does not fetch messages when local and remote state both have zero messages', async () => { - // When there are no messages on L1, the remote inbox state has messagesRollingHash = Buffer16.ZERO - // and totalMessagesInserted = 0. The local store also returns 0 messages and undefined lastMessage. - // The fallback for the local rolling hash must use Buffer16.ZERO (not Buffer32.ZERO) to match. + // When there are no messages on L1, the remote Inbox current bucket is genesis (rolling hash Fr.ZERO, + // total 0). The local store also returns 0 messages and undefined lastMessage, whose rolling-hash fallback + // is Fr.ZERO — so local and remote state match and no message fetch is attempted. fake.setL1BlockNumber(100n); // Add a checkpoint with zero messages so the sync has something to process @@ -1112,10 +1095,7 @@ describe('Archiver Sync', () => { // Sync await archiver.syncImmediate(); - expect(await archiver.getL1ToL2Messages(CheckpointNumber(1))).toHaveLength(2); - expect(await archiver.getL1ToL2Messages(CheckpointNumber(2))).toHaveLength(0); - expect(await archiver.getL1ToL2Messages(CheckpointNumber(3))).toHaveLength(4); - await expect(archiver.getL1ToL2Messages(CheckpointNumber(4))).rejects.toThrow(L1ToL2MessagesNotReadyError); + expect(await getStoredLeaves()).toEqual(asHex([...msgs1, ...msgs3])); // Simulate L1 reorg: remove last 2 messages from checkpoint 3, add new messages for checkpoints 4 and 5 logger.warn('Reorging L1 to L2 messages'); @@ -1132,18 +1112,8 @@ describe('Archiver Sync', () => { fake.setL1BlockNumber(111n); await archiver.syncImmediate(); - expect(await archiver.getL1ToL2Messages(CheckpointNumber(1))).toHaveLength(2); - expect(await archiver.getL1ToL2Messages(CheckpointNumber(2))).toHaveLength(0); - expect(await archiver.getL1ToL2Messages(CheckpointNumber(3))).toHaveLength(2); // Reduced from 4 to 2 - expect(await archiver.getL1ToL2Messages(CheckpointNumber(4))).toHaveLength(1); - expect(await archiver.getL1ToL2Messages(CheckpointNumber(5))).toHaveLength(2); - - expect((await archiver.getL1ToL2Messages(CheckpointNumber(4))).map(leaf => leaf.toString())).toEqual( - [msg40].map(leaf => leaf.toString()), - ); - expect((await archiver.getL1ToL2Messages(CheckpointNumber(5))).map(leaf => leaf.toString())).toEqual( - [msg50, msg51].map(leaf => leaf.toString()), - ); + // The reorg kept the first 4 messages (2 from CP1, 2 from CP3) and appended the new ones. + expect(await getStoredLeaves()).toEqual(asHex([msgs1[0], msgs1[1], msgs3[0], msgs3[1], msg40, msg50, msg51])); }); it('short-circuits rollback at the finalized L1 block', async () => { @@ -1159,8 +1129,7 @@ describe('Archiver Sync', () => { fake.setL1BlockNumber(110n); await archiver.syncImmediate(); - expect(await archiver.getL1ToL2Messages(CheckpointNumber(1))).toHaveLength(2); - expect(await archiver.getL1ToL2Messages(CheckpointNumber(3))).toHaveLength(4); + expect(await getStoredLeaves()).toEqual(asHex([...msgs1, ...msgs3])); // Simulate L1 reorg: remove the last 2 messages from checkpoint 3 and add new ones. fake.removeMessagesAfter(4); @@ -1181,8 +1150,7 @@ describe('Archiver Sync', () => { ); expect(callsAtFinalizedOrBelow).toHaveLength(0); - expect(await archiver.getL1ToL2Messages(CheckpointNumber(1))).toHaveLength(2); - expect(await archiver.getL1ToL2Messages(CheckpointNumber(4))).toHaveLength(1); + expect(await getStoredLeaves()).toEqual(asHex([msgs1[0], msgs1[1], msgs3[0], msgs3[1], msg40])); }); it('falls back to per-message log queries when finalized block is undefined', async () => { @@ -1211,8 +1179,7 @@ describe('Archiver Sync', () => { // 2 messages mismatch on remote (msgs3[2], msgs3[3]) and one matches (msgs3[1]) before we break. expect(eventByHashSpy).toHaveBeenCalledTimes(3); - expect(await archiver.getL1ToL2Messages(CheckpointNumber(1))).toHaveLength(2); - expect(await archiver.getL1ToL2Messages(CheckpointNumber(4))).toHaveLength(1); + expect(await getStoredLeaves()).toEqual(asHex([msgs1[0], msgs1[1], msgs3[0], msgs3[1], msg40])); }); it('persists the finalized L1 block monotonically after message sync', async () => { diff --git a/yarn-project/archiver/src/errors.ts b/yarn-project/archiver/src/errors.ts index db3be133c0b0..29f4de4e84b6 100644 --- a/yarn-project/archiver/src/errors.ts +++ b/yarn-project/archiver/src/errors.ts @@ -116,6 +116,28 @@ export class L1ToL2MessagesNotReadyError extends Error { } } +/** + * Thrown when a query names an Inbox bucket this archiver has not synced. Distinguishes "not synced yet, retry once + * L1 sync catches up" from a genuinely empty result. + */ +export class InboxBucketNotSyncedError extends Error { + constructor(public readonly bucketSeq: bigint) { + super(`Inbox bucket ${bucketSeq} has not been synced`); + this.name = 'InboxBucketNotSyncedError'; + } +} + +/** + * Thrown when a cumulative Inbox message count does not resolve to a bucket boundary this archiver has synced, either + * because the count sits inside a bucket or because the bucket is not synced yet. + */ +export class InboxBucketBoundaryNotSyncedError extends Error { + constructor(public readonly totalMsgCount: bigint) { + super(`No synced Inbox bucket ends at cumulative message count ${totalMsgCount}`); + this.name = 'InboxBucketBoundaryNotSyncedError'; + } +} + /** Thrown when a proposed checkpoint number is stale (already processed). */ export class ProposedCheckpointStaleError extends Error { constructor( diff --git a/yarn-project/archiver/src/l1/calldata_retriever.test.ts b/yarn-project/archiver/src/l1/calldata_retriever.test.ts index 716c3cb23b22..10a2103cc253 100644 --- a/yarn-project/archiver/src/l1/calldata_retriever.test.ts +++ b/yarn-project/archiver/src/l1/calldata_retriever.test.ts @@ -140,6 +140,7 @@ describe('CalldataRetriever', () => { archive, oracleInput: { feeAssetPriceModifier: BigInt(0) }, header: viemHeader, + bucketHint: 0n, }, attestations, signers, @@ -368,6 +369,7 @@ describe('CalldataRetriever', () => { archive, oracleInput: { feeAssetPriceModifier }, header, + bucketHint: 0n, }, attestations, [], // signers diff --git a/yarn-project/archiver/src/l1/data_retrieval.test.ts b/yarn-project/archiver/src/l1/data_retrieval.test.ts index 341d6b8b7fc6..93eed1186c01 100644 --- a/yarn-project/archiver/src/l1/data_retrieval.test.ts +++ b/yarn-project/archiver/src/l1/data_retrieval.test.ts @@ -16,9 +16,9 @@ describe('data_retrieval', () => { const body3 = await Body.random({ txsPerBlock: 2 }); // Convert to BlockBlobData - const block1BlobData = makeBlockBlobDataFromBody(body1, BlockNumber(1), true, 1000); - const block2BlobData = makeBlockBlobDataFromBody(body2, BlockNumber(2), false, 2000); - const block3BlobData = makeBlockBlobDataFromBody(body3, BlockNumber(3), false, 3000); + const block1BlobData = makeBlockBlobDataFromBody(body1, BlockNumber(1), 1000); + const block2BlobData = makeBlockBlobDataFromBody(body2, BlockNumber(2), 2000); + const block3BlobData = makeBlockBlobDataFromBody(body3, BlockNumber(3), 3000); // Calculate total blob fields for checkpoint end marker const numBlobFields = 100; // Approximate, doesn't need to be exact for this test @@ -79,11 +79,24 @@ describe('data_retrieval', () => { expect(reconstructedBlock1.body.txEffects.map(tx => tx.txHash.toString())).not.toEqual( reconstructedBlock3.body.txEffects.map(tx => tx.txHash.toString()), ); + + // Each block's L1-to-L2 message tree root must be reconstructed from its own blob data, not the + // checkpoint's first block. Any block can insert messages, so + // intra-checkpoint blocks carry distinct roots; using the first block's root forks follower nodes. + expect(reconstructedBlock1.header.state.l1ToL2MessageTree.root.toString()).toEqual( + block1BlobData.l1ToL2MessageRoot.toString(), + ); + expect(reconstructedBlock2.header.state.l1ToL2MessageTree.root.toString()).toEqual( + block2BlobData.l1ToL2MessageRoot.toString(), + ); + expect(reconstructedBlock3.header.state.l1ToL2MessageTree.root.toString()).toEqual( + block3BlobData.l1ToL2MessageRoot.toString(), + ); }); it('handles single-block checkpoint', async () => { const body1 = await Body.random({ txsPerBlock: 3 }); - const block1BlobData = makeBlockBlobDataFromBody(body1, BlockNumber(1), true, 5000); + const block1BlobData = makeBlockBlobDataFromBody(body1, BlockNumber(1), 5000); const checkpointBlobData: CheckpointBlobData = { blocks: [block1BlobData], @@ -121,15 +134,9 @@ describe('data_retrieval', () => { * Helper to create a BlockBlobData from a Body. This ensures the blob data is compatible * with Body.fromTxBlobData. */ -function makeBlockBlobDataFromBody( - body: Body, - blockNumber: BlockNumber, - isFirstBlock: boolean, - seed: number, -): BlockBlobData { +function makeBlockBlobDataFromBody(body: Body, blockNumber: BlockNumber, seed: number): BlockBlobData { const blockEndBlobData = makeBlockEndBlobData({ seed, - isFirstBlock, blockEndMarker: { numTxs: body.txEffects.length, blockNumber, diff --git a/yarn-project/archiver/src/l1/data_retrieval.ts b/yarn-project/archiver/src/l1/data_retrieval.ts index 18282920db60..c6f7c8b8941b 100644 --- a/yarn-project/archiver/src/l1/data_retrieval.ts +++ b/yarn-project/archiver/src/l1/data_retrieval.ts @@ -83,16 +83,22 @@ export async function retrievedToPublishedCheckpoint({ .slice(1) .concat([archiveRoot]); - // An error will be thrown from `decodeCheckpointBlobDataFromBlobs` if it can't read a field for the - // `l1ToL2MessageRoot` of the first block. So below we can safely assume it exists: - const l1toL2MessageTreeRoot = blocksBlobData[0].l1ToL2MessageRoot!; - const spongeBlob = SpongeBlob.init(); const l2Blocks: L2Block[] = []; for (let i = 0; i < blocksBlobData.length; i++) { const blockBlobData = blocksBlobData[i]; - const { blockEndMarker, blockEndStateField, lastArchiveRoot, noteHashRoot, nullifierRoot, publicDataRoot } = - blockBlobData; + // The blob carries a per-block L1-to-L2 message tree root: any block + // within a checkpoint can insert messages, so reconstruction must use each block's own root rather than + // the checkpoint's first block. + const { + blockEndMarker, + blockEndStateField, + lastArchiveRoot, + noteHashRoot, + nullifierRoot, + publicDataRoot, + l1ToL2MessageRoot, + } = blockBlobData; const l2BlockNumber = blockEndMarker.blockNumber; @@ -109,7 +115,7 @@ export async function retrievedToPublishedCheckpoint({ const state = StateReference.from({ l1ToL2MessageTree: new AppendOnlyTreeSnapshot( - l1toL2MessageTreeRoot, + l1ToL2MessageRoot, blockEndStateField.l1ToL2MessageNextAvailableLeafIndex, ), partial: PartialStateReference.from({ @@ -387,6 +393,9 @@ function mapLogInboxMessage(log: MessageSentLog): InboxMessage { l1BlockHash: log.l1BlockHash, checkpointNumber: log.args.checkpointNumber, rollingHash: log.args.rollingHash, + inboxRollingHash: log.args.inboxRollingHash, + bucketSeq: log.args.bucketSeq, + bucketTimestamp: log.l1BlockTimestamp, }; } diff --git a/yarn-project/archiver/src/modules/data_source_base.ts b/yarn-project/archiver/src/modules/data_source_base.ts index 6cde2b44b0d8..9d2256c7baa6 100644 --- a/yarn-project/archiver/src/modules/data_source_base.ts +++ b/yarn-project/archiver/src/modules/data_source_base.ts @@ -40,7 +40,7 @@ import { } from '@aztec/stdlib/epoch-helpers'; import type { L2LogsSource } from '@aztec/stdlib/interfaces/server'; import type { LogResult, PrivateLogsQuery, PublicLogsQuery } from '@aztec/stdlib/logs'; -import type { L1ToL2MessageSource, L2ToL1MembershipWitness } from '@aztec/stdlib/messaging'; +import type { InboxBucket, L1ToL2MessageSource, L2ToL1MembershipWitness } from '@aztec/stdlib/messaging'; import { AppendOnlyTreeSnapshot } from '@aztec/stdlib/trees'; import type { BlockHeader, IndexedTxEffect, TxHash } from '@aztec/stdlib/tx'; import type { UInt64 } from '@aztec/stdlib/types'; @@ -324,6 +324,26 @@ export abstract class ArchiverDataSourceBase return this.stores.messages.getL1ToL2MessageIndex(l1ToL2Message); } + public getLatestInboxBucketAtOrBefore(timestamp: bigint): Promise { + return this.stores.messages.getLatestInboxBucketAtOrBefore(timestamp); + } + + public getInboxBucket(seq: bigint): Promise { + return this.stores.messages.getInboxBucket(seq); + } + + public getInboxBucketByTotalMsgCount(totalMsgCount: bigint): Promise { + return this.stores.messages.getInboxBucketByTotalMsgCount(totalMsgCount); + } + + public getL1ToL2MessagesBetweenBuckets(fromExclusive: bigint, toInclusive: bigint): Promise { + return this.stores.messages.getL1ToL2MessagesBetweenBuckets(fromExclusive, toInclusive); + } + + public getL1ToL2MessagesBetweenLeafCounts(startLeafCount: bigint, endLeafCount: bigint): Promise { + return this.stores.messages.getL1ToL2MessagesBetweenLeafCounts(startLeafCount, endLeafCount); + } + private async getPublishedCheckpointFromCheckpointData(checkpoint: CheckpointData): Promise { const blocksForCheckpoint = await this.stores.blocks.getBlocksForCheckpoint(checkpoint.checkpointNumber); if (!blocksForCheckpoint) { diff --git a/yarn-project/archiver/src/modules/l1_synchronizer.ts b/yarn-project/archiver/src/modules/l1_synchronizer.ts index 1739dffccde2..ba5876f77500 100644 --- a/yarn-project/archiver/src/modules/l1_synchronizer.ts +++ b/yarn-project/archiver/src/modules/l1_synchronizer.ts @@ -30,7 +30,6 @@ import { PublishedCheckpoint, } from '@aztec/stdlib/checkpoint'; import { type L1RollupConstants, getEpochAtSlot, getSlotAtNextL1Block } from '@aztec/stdlib/epoch-helpers'; -import { computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging'; import type { CoordinationSignatureContext } from '@aztec/stdlib/p2p'; import { type Traceable, type Tracer, execInSpan, trackSpan } from '@aztec/telemetry-client'; @@ -495,7 +494,10 @@ export class ArchiverL1Synchronizer implements Traceable { ); } - /** Retrieves L1 to L2 messages from L1 in batches and stores them. */ + /** + * Retrieves L1 to L2 messages from L1 in batches and stores them. Batches must span whole L1 blocks so that every + * message of an Inbox bucket is stored in a single call, which the message store requires. + */ private async retrieveAndStoreMessages(fromL1Block: bigint, toL1Block: bigint): Promise { let searchStartBlock: bigint = 0n; let searchEndBlock: bigint = fromL1Block; @@ -508,7 +510,7 @@ export class ArchiverL1Synchronizer implements Traceable { this.log.trace(`Retrieving L1 to L2 messages in L1 blocks ${searchStartBlock}-${searchEndBlock}`); const messages = await retrieveL1ToL2Messages(this.inbox, searchStartBlock, searchEndBlock); const timer = new Timer(); - await this.stores.messages.addL1ToL2Messages(messages); + await this.stores.messages.addL1ToL2MessageBuckets(messages); const perMsg = timer.ms() / messages.length; this.instrumentation.processNewMessages(messages.length, perMsg); for (const msg of messages) { @@ -1013,25 +1015,6 @@ export class ArchiverL1Synchronizer implements Traceable { for (const calldataCheckpoint of checkpointsToIngest) { const published = publishedByNumber.get(calldataCheckpoint.checkpointNumber)!; - // Check the inHash of the checkpoint against the l1->l2 messages. - // The messages should've been synced up to the currentL1BlockNumber and must be available for the published - // checkpoints we just retrieved. - const l1ToL2Messages = await this.stores.messages.getL1ToL2Messages(published.checkpoint.number); - const computedInHash = computeInHashFromL1ToL2Messages(l1ToL2Messages); - const publishedInHash = published.checkpoint.header.inHash; - if (!computedInHash.equals(publishedInHash)) { - this.log.fatal(`Mismatch inHash for checkpoint ${published.checkpoint.number}`, { - checkpointHash: published.checkpoint.hash(), - l1BlockNumber: published.l1.blockNumber, - computedInHash, - publishedInHash, - }); - // Throwing an error since this is most likely caused by a bug. - throw new Error( - `Mismatch inHash for checkpoint ${published.checkpoint.number}. Expected ${computedInHash} but got ${publishedInHash}`, - ); - } - validCheckpoints.push(published); this.log.debug( `Ingesting new checkpoint ${published.checkpoint.number} with ${published.checkpoint.blocks.length} blocks`, diff --git a/yarn-project/archiver/src/store/block_store.ts b/yarn-project/archiver/src/store/block_store.ts index 1e887246d0f3..51f09ef746dd 100644 --- a/yarn-project/archiver/src/store/block_store.ts +++ b/yarn-project/archiver/src/store/block_store.ts @@ -125,6 +125,7 @@ type CheckpointStorage = CommonCheckpointStorage & { type ProposedCheckpointStorage = CommonCheckpointStorage & { totalManaUsed: string; feeAssetPriceModifier: string; + inboxMsgTotal: string; }; export type RemoveCheckpointsResult = { blocksRemoved: L2Block[] | undefined }; @@ -976,6 +977,7 @@ export class BlockStore { blockCount: stored.blockCount, totalManaUsed: BigInt(stored.totalManaUsed), feeAssetPriceModifier: BigInt(stored.feeAssetPriceModifier), + inboxMsgTotal: BigInt(stored.inboxMsgTotal), }; } @@ -1407,7 +1409,7 @@ export class BlockStore { * Adds a proposed checkpoint to the pending queue. * Accepts proposed.checkpointNumber === latestTip + 1, where latestTip is the highest of * confirmed and the highest pending checkpoint number. - * Computes archive and checkpointOutHash from the stored blocks. + * Computes archive, checkpointOutHash and the consumed Inbox message total from the stored blocks. */ async addProposedCheckpoint(proposed: ProposedCheckpointInput) { return await this.db.transactionAsync(async () => { @@ -1433,8 +1435,12 @@ export class BlockStore { } this.validateCheckpointBlocks(blocks, previousBlock); - const archive = blocks[blocks.length - 1].archive; + const lastBlock = blocks[blocks.length - 1]; + const archive = lastBlock.archive; const checkpointOutHash = Checkpoint.getCheckpointOutHash(blocks); + // The last block's L1-to-L2 leaf count is the checkpoint's cumulative consumed Inbox total under compact + // indexing, which is what `propose` records on L1 for it. + const inboxMsgTotal = BigInt(lastBlock.header.state.l1ToL2MessageTree.nextAvailableLeafIndex); await this.#proposedCheckpoints.set(proposed.checkpointNumber, { header: proposed.header.toBuffer(), @@ -1445,6 +1451,7 @@ export class BlockStore { blockCount: proposed.blockCount, totalManaUsed: proposed.totalManaUsed.toString(), feeAssetPriceModifier: proposed.feeAssetPriceModifier.toString(), + inboxMsgTotal: inboxMsgTotal.toString(), }); }); } diff --git a/yarn-project/archiver/src/store/data_stores.ts b/yarn-project/archiver/src/store/data_stores.ts index d34d9497b152..f463e06735ba 100644 --- a/yarn-project/archiver/src/store/data_stores.ts +++ b/yarn-project/archiver/src/store/data_stores.ts @@ -13,7 +13,7 @@ import { FunctionNamesCache } from './function_names_cache.js'; import { LogStore } from './log_store.js'; import { MessageStore } from './message_store.js'; -export const ARCHIVER_DB_VERSION = 7; +export const ARCHIVER_DB_VERSION = 8; /** * Represents the latest L1 block processed by the archiver for various objects in L2. diff --git a/yarn-project/archiver/src/store/message_store.test.ts b/yarn-project/archiver/src/store/message_store.test.ts index ef2ca57b9013..440a8866a268 100644 --- a/yarn-project/archiver/src/store/message_store.test.ts +++ b/yarn-project/archiver/src/store/message_store.test.ts @@ -1,13 +1,14 @@ -import { NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants'; import { CheckpointNumber } from '@aztec/foundation/branded-types'; import { Buffer16, Buffer32 } from '@aztec/foundation/buffer'; +import { Fr } from '@aztec/foundation/curves/bn254'; import { toArray } from '@aztec/foundation/iterable'; import { openTmpStore } from '@aztec/kv-store/lmdb-v2'; import { Checkpoint, type PublishedCheckpoint } from '@aztec/stdlib/checkpoint'; +import { updateInboxRollingHash } from '@aztec/stdlib/messaging'; import '@aztec/stdlib/testing/jest'; -import { L1ToL2MessagesNotReadyError } from '../errors.js'; -import type { InboxMessage } from '../structs/inbox_message.js'; +import { InboxBucketBoundaryNotSyncedError, InboxBucketNotSyncedError } from '../errors.js'; +import { type InboxMessage, updateRollingHash } from '../structs/inbox_message.js'; import { makeInboxMessage, makeInboxMessages, @@ -78,7 +79,7 @@ describe('MessageStore', () => { const l1BlockHash = Buffer32.random(); const l1BlockNumber = 10n; await messageStore.setMessageSyncState({ l1BlockNumber, l1BlockHash }, 1n); - await messageStore.addL1ToL2Messages([ + await messageStore.addL1ToL2MessageBuckets([ makeInboxMessage(Buffer16.ZERO, { l1BlockNumber: 5n, l1BlockHash: Buffer32.random() }), ]); await expect(getSynchPoint(blockStore, messageStore)).resolves.toEqual({ @@ -99,93 +100,58 @@ describe('MessageStore', () => { it('stores first message ever', async () => { const msg = makeInboxMessage(Buffer16.ZERO, { index: 0n, checkpointNumber: CheckpointNumber(1) }); - await messageStore.addL1ToL2Messages([msg]); + await messageStore.addL1ToL2MessageBuckets([msg]); await checkMessages([msg]); - expect(await messageStore.getL1ToL2Messages(CheckpointNumber(1))).toEqual([msg.leaf]); }); it('stores single message', async () => { const msg = makeInboxMessage(Buffer16.ZERO, { checkpointNumber: CheckpointNumber(2) }); - await messageStore.addL1ToL2Messages([msg]); + await messageStore.addL1ToL2MessageBuckets([msg]); await checkMessages([msg]); - expect(await messageStore.getL1ToL2Messages(CheckpointNumber(2))).toEqual([msg.leaf]); }); - it('stores and returns messages across different blocks', async () => { + it('stores messages across different blocks', async () => { const msgs = makeInboxMessages(5, { initialCheckpointNumber }); - await messageStore.addL1ToL2Messages(msgs); + await messageStore.addL1ToL2MessageBuckets(msgs); await checkMessages(msgs); - expect(await messageStore.getL1ToL2Messages(CheckpointNumber(initialCheckpointNumber + 2))).toEqual( - [msgs[2]].map(m => m.leaf), - ); }); it('stores the same messages again', async () => { const msgs = makeInboxMessages(5, { initialCheckpointNumber }); - await messageStore.addL1ToL2Messages(msgs); - await messageStore.addL1ToL2Messages(msgs.slice(2)); + await messageStore.addL1ToL2MessageBuckets(msgs); + await messageStore.addL1ToL2MessageBuckets(msgs.slice(2)); await checkMessages(msgs); }); - it('stores and returns messages across different blocks with gaps', async () => { - const msgs1 = makeInboxMessages(3, { initialCheckpointNumber: CheckpointNumber(1) }); - const msgs2 = makeInboxMessages(3, { - initialCheckpointNumber: CheckpointNumber(20), - initialHash: msgs1.at(-1)!.rollingHash, - }); - - await messageStore.addL1ToL2Messages(msgs1); - await messageStore.addL1ToL2Messages(msgs2); - - await checkMessages([...msgs1, ...msgs2]); - - expect(await messageStore.getL1ToL2Messages(CheckpointNumber(1))).toEqual([msgs1[0].leaf]); - expect(await messageStore.getL1ToL2Messages(CheckpointNumber(4))).toEqual([]); - expect(await messageStore.getL1ToL2Messages(CheckpointNumber(20))).toEqual([msgs2[0].leaf]); - expect(await messageStore.getL1ToL2Messages(CheckpointNumber(24))).toEqual([]); - }); - - it('stores and returns messages with block numbers larger than a byte', async () => { + it('stores messages with block numbers larger than a byte', async () => { const msgs = makeInboxMessages(5, { initialCheckpointNumber: CheckpointNumber(1000) }); - await messageStore.addL1ToL2Messages(msgs); + await messageStore.addL1ToL2MessageBuckets(msgs); await checkMessages(msgs); - expect(await messageStore.getL1ToL2Messages(CheckpointNumber(1002))).toEqual([msgs[2]].map(m => m.leaf)); }); - it('stores and returns multiple messages per block', async () => { + it('stores multiple messages per block', async () => { const msgs = makeInboxMessagesWithFullBlocks(4); - await messageStore.addL1ToL2Messages(msgs); + await messageStore.addL1ToL2MessageBuckets(msgs); await checkMessages(msgs); - const blockMessages = await messageStore.getL1ToL2Messages(CheckpointNumber(initialCheckpointNumber + 1)); - expect(blockMessages).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP); - expect(blockMessages).toEqual( - msgs.slice(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP * 2).map(m => m.leaf), - ); }); it('stores messages in multiple operations', async () => { const msgs = makeInboxMessages(20, { initialCheckpointNumber }); - await messageStore.addL1ToL2Messages(msgs.slice(0, 10)); - await messageStore.addL1ToL2Messages(msgs.slice(10, 20)); + await messageStore.addL1ToL2MessageBuckets(msgs.slice(0, 10)); + await messageStore.addL1ToL2MessageBuckets(msgs.slice(10, 20)); - expect(await messageStore.getL1ToL2Messages(CheckpointNumber(initialCheckpointNumber + 2))).toEqual( - [msgs[2]].map(m => m.leaf), - ); - expect(await messageStore.getL1ToL2Messages(CheckpointNumber(initialCheckpointNumber + 12))).toEqual( - [msgs[12]].map(m => m.leaf), - ); await checkMessages(msgs); }); it('iterates over messages from start index', async () => { const msgs = makeInboxMessages(10, { initialCheckpointNumber }); - await messageStore.addL1ToL2Messages(msgs); + await messageStore.addL1ToL2MessageBuckets(msgs); const iterated = await toArray(messageStore.iterateL1ToL2Messages({ start: msgs[3].index })); expect(iterated).toEqual(msgs.slice(3)); @@ -193,7 +159,7 @@ describe('MessageStore', () => { it('iterates over messages in reverse', async () => { const msgs = makeInboxMessages(10, { initialCheckpointNumber }); - await messageStore.addL1ToL2Messages(msgs); + await messageStore.addL1ToL2MessageBuckets(msgs); const iterated = await toArray(messageStore.iterateL1ToL2Messages({ reverse: true, end: msgs[3].index })); expect(iterated).toEqual(msgs.slice(0, 4).reverse()); @@ -201,130 +167,338 @@ describe('MessageStore', () => { it('throws if messages are added out of order', async () => { const msgs = makeInboxMessages(5, { overrideFn: (msg, i) => ({ ...msg, index: BigInt(10 - i) }) }); - await expect(messageStore.addL1ToL2Messages(msgs)).rejects.toThrow(MessageStoreError); - }); - - it('throws if block number for the first message is out of order', async () => { - const msgs = makeInboxMessages(4, { initialCheckpointNumber }); - msgs[2].checkpointNumber = CheckpointNumber(initialCheckpointNumber - 1); - await messageStore.addL1ToL2Messages(msgs.slice(0, 2)); - await expect(messageStore.addL1ToL2Messages(msgs.slice(2, 4))).rejects.toThrow(MessageStoreError); + await expect(messageStore.addL1ToL2MessageBuckets(msgs)).rejects.toThrow(MessageStoreError); }); it('throws if rolling hash is not correct', async () => { const msgs = makeInboxMessages(5); msgs[1].rollingHash = Buffer16.random(); - await expect(messageStore.addL1ToL2Messages(msgs)).rejects.toThrow(MessageStoreError); + await expect(messageStore.addL1ToL2MessageBuckets(msgs)).rejects.toThrow(MessageStoreError); }); it('throws if rolling hash for first message is not correct', async () => { const msgs = makeInboxMessages(4); msgs[2].rollingHash = Buffer16.random(); - await messageStore.addL1ToL2Messages(msgs.slice(0, CheckpointNumber(2))); - await expect(messageStore.addL1ToL2Messages(msgs.slice(2, 4))).rejects.toThrow(MessageStoreError); + await messageStore.addL1ToL2MessageBuckets(msgs.slice(0, CheckpointNumber(2))); + await expect(messageStore.addL1ToL2MessageBuckets(msgs.slice(2, 4))).rejects.toThrow(MessageStoreError); }); - it('throws if index is not in the correct range', async () => { + it('throws if index skips ahead', async () => { const msgs = makeInboxMessages(5, { initialCheckpointNumber }); msgs.at(-1)!.index += 100n; - await expect(messageStore.addL1ToL2Messages(msgs)).rejects.toThrow(MessageStoreError); + await expect(messageStore.addL1ToL2MessageBuckets(msgs)).rejects.toThrow(MessageStoreError); }); - it('throws if first index in block has gaps', async () => { + it('throws if index does not follow previous one', async () => { const msgs = makeInboxMessages(4, { initialCheckpointNumber }); msgs[2].index++; - await expect(messageStore.addL1ToL2Messages(msgs)).rejects.toThrow(MessageStoreError); + await expect(messageStore.addL1ToL2MessageBuckets(msgs)).rejects.toThrow(MessageStoreError); }); - it('throws if index does not follow previous one', async () => { - const msgs = makeInboxMessages(2, { - initialCheckpointNumber, - overrideFn: (msg, i) => ({ - ...msg, - checkpointNumber: CheckpointNumber(2), - index: BigInt(i + NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP * 2), - }), + it('removes messages starting with the given index', async () => { + const msgs = makeInboxMessagesWithFullBlocks(4, { initialCheckpointNumber: CheckpointNumber(1) }); + await messageStore.addL1ToL2MessageBuckets(msgs); + + await messageStore.removeL1ToL2Messages(msgs[13].index); + await checkMessages(msgs.slice(0, 13)); + }); + }); + + describe('Inbox buckets', () => { + // Builds `count` consecutive valid messages in a single checkpoint, then reassigns their bucket sequence and + // timestamp per the given per-message spec so we can exercise multi-message and rollover buckets. + const makeBucketedMessages = (spec: { seq: bigint; timestamp: bigint }[]): InboxMessage[] => { + const msgs = makeInboxMessages(spec.length, { + initialCheckpointNumber: CheckpointNumber(1), + messagesPerCheckpoint: spec.length, + }); + msgs.forEach((msg, i) => { + msg.bucketSeq = spec[i].seq; + msg.bucketTimestamp = spec[i].timestamp; }); - msgs[1].index++; - await expect(messageStore.addL1ToL2Messages(msgs)).rejects.toThrow(MessageStoreError); + return msgs; + }; + + // Builds a valid message continuing the chain after `previous`, absorbed into the given bucket. + const makeNextMessage = (previous: InboxMessage, bucket: { seq: bigint; timestamp: bigint }): InboxMessage => { + const leaf = Fr.random(); + return { + ...previous, + leaf, + index: previous.index + 1n, + rollingHash: updateRollingHash(previous.rollingHash, leaf), + inboxRollingHash: updateInboxRollingHash(previous.inboxRollingHash, leaf), + bucketSeq: bucket.seq, + bucketTimestamp: bucket.timestamp, + }; + }; + + // Three buckets over six messages: bucket 1 = [0,1,2], bucket 2 = [3,4], bucket 3 = [5]. + const threeBucketSpec = [ + { seq: 1n, timestamp: 100n }, + { seq: 1n, timestamp: 100n }, + { seq: 1n, timestamp: 100n }, + { seq: 2n, timestamp: 200n }, + { seq: 2n, timestamp: 200n }, + { seq: 3n, timestamp: 300n }, + ]; + + it('snapshots buckets as messages are inserted', async () => { + const msgs = makeBucketedMessages(threeBucketSpec); + await messageStore.addL1ToL2MessageBuckets(msgs); + + expect(await messageStore.getInboxBucket(1n)).toEqual({ + seq: 1n, + inboxRollingHash: msgs[2].inboxRollingHash, + totalMsgCount: 3n, + timestamp: 100n, + msgCount: 3, + lastMessageIndex: msgs[2].index, + }); + expect(await messageStore.getInboxBucket(2n)).toEqual({ + seq: 2n, + inboxRollingHash: msgs[4].inboxRollingHash, + totalMsgCount: 5n, + timestamp: 200n, + msgCount: 2, + lastMessageIndex: msgs[4].index, + }); + expect(await messageStore.getInboxBucket(3n)).toEqual({ + seq: 3n, + inboxRollingHash: msgs[5].inboxRollingHash, + totalMsgCount: 6n, + timestamp: 300n, + msgCount: 1, + lastMessageIndex: msgs[5].index, + }); + expect(await messageStore.getInboxBucket(4n)).toBeUndefined(); }); - it('removes messages up to the given block number', async () => { - const msgs = makeInboxMessagesWithFullBlocks(4, { initialCheckpointNumber: CheckpointNumber(1) }); + it('resolves a bucket by its cumulative message total', async () => { + const msgs = makeBucketedMessages(threeBucketSpec); + await messageStore.addL1ToL2MessageBuckets(msgs); + + // Each bucket boundary (cumulative totals 3, 5, 6) resolves to its bucket. + expect((await messageStore.getInboxBucketByTotalMsgCount(3n))?.seq).toEqual(1n); + expect((await messageStore.getInboxBucketByTotalMsgCount(5n))?.seq).toEqual(2n); + expect((await messageStore.getInboxBucketByTotalMsgCount(6n))?.seq).toEqual(3n); + // A total inside a bucket (not on a boundary) does not resolve. + expect(await messageStore.getInboxBucketByTotalMsgCount(4n)).toBeUndefined(); + // A total past the last synced bucket does not resolve. + expect(await messageStore.getInboxBucketByTotalMsgCount(7n)).toBeUndefined(); + }); - await messageStore.addL1ToL2Messages(msgs); - await checkMessages(msgs); + it('synthesizes the genesis sentinel bucket (sequence 0, total 0) which is never ingested', async () => { + // With real messages present but no ingested sequence-0 snapshot, both lookups still resolve genesis. + await messageStore.addL1ToL2MessageBuckets(makeBucketedMessages(threeBucketSpec)); - expect(await messageStore.getL1ToL2Messages(CheckpointNumber(1))).toHaveLength( - NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, - ); - expect(await messageStore.getL1ToL2Messages(CheckpointNumber(2))).toHaveLength( - NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, - ); - expect(await messageStore.getL1ToL2Messages(CheckpointNumber(3))).toHaveLength( - NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, - ); - expect(await messageStore.getL1ToL2Messages(CheckpointNumber(4))).toHaveLength( - NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, - ); + expect(await messageStore.getInboxBucket(0n)).toMatchObject({ seq: 0n, totalMsgCount: 0n, msgCount: 0 }); + expect(await messageStore.getInboxBucketByTotalMsgCount(0n)).toMatchObject({ seq: 0n, totalMsgCount: 0n }); + }); - await messageStore.rollbackL1ToL2MessagesToCheckpoint(CheckpointNumber(2)); + it('rejects a bucket delivered without the messages already stored for it', async () => { + const msgs = makeBucketedMessages(threeBucketSpec); + await messageStore.addL1ToL2MessageBuckets(msgs.slice(0, 2)); - expect(await messageStore.getL1ToL2Messages(CheckpointNumber(1))).toHaveLength( - NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, - ); - expect(await messageStore.getL1ToL2Messages(CheckpointNumber(2))).toHaveLength( - NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, - ); - expect(await messageStore.getL1ToL2Messages(CheckpointNumber(3))).toHaveLength(0); - expect(await messageStore.getL1ToL2Messages(CheckpointNumber(4))).toHaveLength(0); + // The second batch continues bucket 1 from its third message, so its snapshot would undercount the bucket. + await expect(messageStore.addL1ToL2MessageBuckets(msgs.slice(2))).rejects.toThrow(/Incomplete Inbox bucket 1/); + }); - await checkMessages(msgs.slice(0, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP * 2)); + it('rebuilds a stored bucket re-delivered in full with a replaced tail', async () => { + const msgs = makeBucketedMessages(threeBucketSpec); + await messageStore.addL1ToL2MessageBuckets(msgs.slice(0, 3)); + // An L1 reorg drops bucket 1's last message; the re-sync replays the whole L1 block it lives in. + await messageStore.removeL1ToL2Messages(msgs[2].index); + const replacement = makeNextMessage(msgs[1], { seq: 1n, timestamp: 100n }); + await messageStore.addL1ToL2MessageBuckets([msgs[0], msgs[1], replacement]); + + expect(await messageStore.getInboxBucket(1n)).toEqual({ + seq: 1n, + inboxRollingHash: replacement.inboxRollingHash, + totalMsgCount: 3n, + timestamp: 100n, + msgCount: 3, + lastMessageIndex: replacement.index, + }); + expect(await messageStore.getTotalL1ToL2MessageCount()).toEqual(3n); }); - it('removes messages starting with the given index', async () => { - const msgs = makeInboxMessagesWithFullBlocks(4, { initialCheckpointNumber: CheckpointNumber(1) }); - await messageStore.addL1ToL2Messages(msgs); + it('rejects a message opening a bucket older than the newest stored one', async () => { + const msgs = makeBucketedMessages([ + { seq: 1n, timestamp: 100n }, + { seq: 2n, timestamp: 200n }, + { seq: 4n, timestamp: 400n }, + ]); + await messageStore.addL1ToL2MessageBuckets(msgs); - await messageStore.removeL1ToL2Messages(msgs[13].index); - await checkMessages(msgs.slice(0, 13)); + const stale = makeNextMessage(msgs[2], { seq: 3n, timestamp: 300n }); + await expect(messageStore.addL1ToL2MessageBuckets([stale])).rejects.toThrow(/Cannot open Inbox bucket 3/); }); - describe('inbox tree in progress guard', () => { - it('throws when checkpointNumber >= treeInProgress', async () => { - const msgs = makeInboxMessages(3, { initialCheckpointNumber: CheckpointNumber(5) }); - await messageStore.addL1ToL2Messages(msgs); + it('throws if the consensus rolling hash is not correct', async () => { + const msgs = makeInboxMessages(5); + msgs[1].inboxRollingHash = Fr.random(); + await expect(messageStore.addL1ToL2MessageBuckets(msgs)).rejects.toThrow(MessageStoreError); + }); - // Set treeInProgress to 7, meaning checkpoints 5 and 6 are sealed, 7+ are not - await messageStore.setMessageSyncState({ l1BlockNumber: 1n, l1BlockHash: Buffer32.random() }, 7n); + it('resolves the latest bucket at or before a timestamp', async () => { + await messageStore.addL1ToL2MessageBuckets(makeBucketedMessages(threeBucketSpec)); - // Sealed checkpoint should succeed - await expect(messageStore.getL1ToL2Messages(CheckpointNumber(5))).resolves.toEqual([msgs[0].leaf]); + expect((await messageStore.getLatestInboxBucketAtOrBefore(100n))!.seq).toEqual(1n); + expect((await messageStore.getLatestInboxBucketAtOrBefore(150n))!.seq).toEqual(1n); + expect((await messageStore.getLatestInboxBucketAtOrBefore(300n))!.seq).toEqual(3n); + expect((await messageStore.getLatestInboxBucketAtOrBefore(10_000n))!.seq).toEqual(3n); + expect(await messageStore.getLatestInboxBucketAtOrBefore(99n)).toBeUndefined(); + }); - // Unsealed checkpoint (== treeInProgress) should throw - await expect(messageStore.getL1ToL2Messages(CheckpointNumber(7))).rejects.toThrow(L1ToL2MessagesNotReadyError); + it('resolves rollover buckets that share a timestamp to the highest sequence', async () => { + // Buckets 2 and 3 share timestamp 200 (a full bucket rolling over within the same L1 block). + const msgs = makeBucketedMessages([ + { seq: 1n, timestamp: 100n }, + { seq: 2n, timestamp: 200n }, + { seq: 3n, timestamp: 200n }, + ]); + await messageStore.addL1ToL2MessageBuckets(msgs); - // Future checkpoint should also throw - await expect(messageStore.getL1ToL2Messages(CheckpointNumber(8))).rejects.toThrow(L1ToL2MessagesNotReadyError); - }); + expect((await messageStore.getLatestInboxBucketAtOrBefore(200n))!.seq).toEqual(3n); + }); - it('returns messages when checkpointNumber < treeInProgress', async () => { - const msgs = makeInboxMessages(3, { initialCheckpointNumber: CheckpointNumber(10) }); - await messageStore.addL1ToL2Messages(msgs); + it('returns messages between buckets in insertion order', async () => { + const msgs = makeBucketedMessages(threeBucketSpec); + await messageStore.addL1ToL2MessageBuckets(msgs); + const leaves = msgs.map(m => m.leaf); - await messageStore.setMessageSyncState({ l1BlockNumber: 1n, l1BlockHash: Buffer32.random() }, 13n); + expect(await messageStore.getL1ToL2MessagesBetweenBuckets(0n, 3n)).toEqual(leaves); + expect(await messageStore.getL1ToL2MessagesBetweenBuckets(1n, 2n)).toEqual(leaves.slice(3, 5)); + expect(await messageStore.getL1ToL2MessagesBetweenBuckets(2n, 3n)).toEqual(leaves.slice(5)); + // An empty (fromExclusive, toInclusive] range yields no messages. + expect(await messageStore.getL1ToL2MessagesBetweenBuckets(3n, 3n)).toEqual([]); + }); - await expect(messageStore.getL1ToL2Messages(CheckpointNumber(10))).resolves.toEqual([msgs[0].leaf]); - await expect(messageStore.getL1ToL2Messages(CheckpointNumber(11))).resolves.toEqual([msgs[1].leaf]); - }); + it('throws when a bucket bound has not been synced', async () => { + const msgs = makeBucketedMessages(threeBucketSpec); + await messageStore.addL1ToL2MessageBuckets(msgs); + + // An unsynced bound is reported rather than collapsing into an empty range. + await expect(messageStore.getL1ToL2MessagesBetweenBuckets(0n, 9n)).rejects.toThrow(InboxBucketNotSyncedError); + await expect(messageStore.getL1ToL2MessagesBetweenBuckets(9n, 12n)).rejects.toThrow(InboxBucketNotSyncedError); + // A nonzero lower bound is never treated as genesis. + await expect(messageStore.getL1ToL2MessagesBetweenBuckets(4n, 5n)).rejects.toThrow(InboxBucketNotSyncedError); + await expect(messageStore.getL1ToL2MessagesBetweenBuckets(4n, 3n)).rejects.toThrow(/Invalid Inbox bucket range/); + }); - it('skips guard when treeInProgress is not set', async () => { - const msgs = makeInboxMessages(2, { initialCheckpointNumber: CheckpointNumber(1) }); - await messageStore.addL1ToL2Messages(msgs); + it('returns messages between cumulative leaf counts', async () => { + const msgs = makeBucketedMessages(threeBucketSpec); + await messageStore.addL1ToL2MessageBuckets(msgs); + const leaves = msgs.map(m => m.leaf); + + // Bucket boundaries sit at cumulative counts 0 (genesis), 3, 5 and 6. + expect(await messageStore.getL1ToL2MessagesBetweenLeafCounts(0n, 6n)).toEqual(leaves); + expect(await messageStore.getL1ToL2MessagesBetweenLeafCounts(0n, 3n)).toEqual(leaves.slice(0, 3)); + expect(await messageStore.getL1ToL2MessagesBetweenLeafCounts(3n, 5n)).toEqual(leaves.slice(3, 5)); + expect(await messageStore.getL1ToL2MessagesBetweenLeafCounts(5n, 6n)).toEqual(leaves.slice(5)); + // An empty range consumes nothing, at a bucket boundary or at genesis. + expect(await messageStore.getL1ToL2MessagesBetweenLeafCounts(5n, 5n)).toEqual([]); + expect(await messageStore.getL1ToL2MessagesBetweenLeafCounts(0n, 0n)).toEqual([]); + }); + + it('throws when a leaf count does not land on a synced bucket boundary', async () => { + const msgs = makeBucketedMessages(threeBucketSpec); + await messageStore.addL1ToL2MessageBuckets(msgs); - // No setMessageSyncState call — guard should be permissive - await expect(messageStore.getL1ToL2Messages(CheckpointNumber(1))).resolves.toEqual([msgs[0].leaf]); + // Counts inside a bucket and past the last synced bucket both fail rather than returning a partial range. + await expect(messageStore.getL1ToL2MessagesBetweenLeafCounts(0n, 4n)).rejects.toThrow( + InboxBucketBoundaryNotSyncedError, + ); + await expect(messageStore.getL1ToL2MessagesBetweenLeafCounts(4n, 6n)).rejects.toThrow( + InboxBucketBoundaryNotSyncedError, + ); + await expect(messageStore.getL1ToL2MessagesBetweenLeafCounts(3n, 9n)).rejects.toThrow( + InboxBucketBoundaryNotSyncedError, + ); + await expect(messageStore.getL1ToL2MessagesBetweenLeafCounts(5n, 3n)).rejects.toThrow( + /Invalid Inbox leaf count range/, + ); + }); + + it('rewinds buckets when messages are removed', async () => { + const msgs = makeBucketedMessages(threeBucketSpec); + await messageStore.addL1ToL2MessageBuckets(msgs); + + // Remove the last two messages (msgs[4] in bucket 2, msgs[5] in bucket 3), splitting bucket 2. + await messageStore.removeL1ToL2Messages(msgs[4].index); + + expect(await messageStore.getInboxBucket(3n)).toBeUndefined(); + expect(await messageStore.getInboxBucket(2n)).toEqual({ + seq: 2n, + inboxRollingHash: msgs[3].inboxRollingHash, + totalMsgCount: 4n, + timestamp: 200n, + msgCount: 1, + lastMessageIndex: msgs[3].index, }); + expect(await messageStore.getInboxBucket(1n)).toMatchObject({ msgCount: 3, totalMsgCount: 3n }); + + // Bucket 3's timestamp index entry is gone, so an at-or-before lookup falls back to bucket 2. + expect((await messageStore.getLatestInboxBucketAtOrBefore(300n))!.seq).toEqual(2n); + }); + + it('rewinds a rollover bucket sharing a timestamp with the surviving boundary', async () => { + const msgs = makeBucketedMessages([ + { seq: 1n, timestamp: 100n }, + { seq: 2n, timestamp: 200n }, + { seq: 3n, timestamp: 200n }, + ]); + await messageStore.addL1ToL2MessageBuckets(msgs); + + // Removing the last message deletes bucket 3, whose timestamp (200) is shared with the surviving bucket 2. + await messageStore.removeL1ToL2Messages(msgs[2].index); + + expect(await messageStore.getInboxBucket(3n)).toBeUndefined(); + expect((await messageStore.getLatestInboxBucketAtOrBefore(200n))!.seq).toEqual(2n); + }); + + it('keeps rollover siblings indexed when a bucket sharing their timestamp is removed', async () => { + // Three buckets rolling over within one L1 block, so all three share timestamp 100. + const msgs = makeBucketedMessages([ + { seq: 1n, timestamp: 100n }, + { seq: 2n, timestamp: 100n }, + { seq: 3n, timestamp: 100n }, + ]); + await messageStore.addL1ToL2MessageBuckets(msgs); + + await messageStore.removeL1ToL2Messages(msgs[2].index); + + expect(await messageStore.getInboxBucket(3n)).toBeUndefined(); + expect(await messageStore.getInboxBucket(1n)).toMatchObject({ msgCount: 1, totalMsgCount: 1n }); + expect((await messageStore.getLatestInboxBucketAtOrBefore(100n))!.seq).toEqual(2n); + }); + + it('reindexes a bucket re-delivered from an L1 block with a different timestamp', async () => { + const msgs = makeBucketedMessages(threeBucketSpec); + await messageStore.addL1ToL2MessageBuckets(msgs.slice(0, 3)); + await messageStore.removeL1ToL2Messages(msgs[2].index); + + // The reorged L1 block holding bucket 1 was re-mined at a later timestamp. + const replayed = [msgs[0], msgs[1], makeNextMessage(msgs[1], { seq: 1n, timestamp: 100n })].map(msg => ({ + ...msg, + bucketTimestamp: 150n, + })); + await messageStore.addL1ToL2MessageBuckets(replayed); + + expect(await messageStore.getInboxBucket(1n)).toMatchObject({ timestamp: 150n, msgCount: 3 }); + expect(await messageStore.getLatestInboxBucketAtOrBefore(100n)).toBeUndefined(); + expect((await messageStore.getLatestInboxBucketAtOrBefore(150n))!.seq).toEqual(1n); + }); + + it('clears all buckets when every message is removed', async () => { + const msgs = makeBucketedMessages(threeBucketSpec); + await messageStore.addL1ToL2MessageBuckets(msgs); + + await messageStore.removeL1ToL2Messages(msgs[0].index); + + expect(await messageStore.getInboxBucket(1n)).toBeUndefined(); + expect(await messageStore.getLatestInboxBucketAtOrBefore(300n)).toBeUndefined(); }); }); }); diff --git a/yarn-project/archiver/src/store/message_store.ts b/yarn-project/archiver/src/store/message_store.ts index 5aaf949ee3b0..da9351708e4d 100644 --- a/yarn-project/archiver/src/store/message_store.ts +++ b/yarn-project/archiver/src/store/message_store.ts @@ -4,17 +4,22 @@ import { Buffer16, Buffer32 } from '@aztec/foundation/buffer'; import { Fr } from '@aztec/foundation/curves/bn254'; import { toArray } from '@aztec/foundation/iterable'; import { createLogger } from '@aztec/foundation/log'; -import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; +import { BufferReader, bigintToUInt64BE, numToUInt32BE, serializeToBuffer } from '@aztec/foundation/serialize'; import { type AztecAsyncKVStore, type AztecAsyncMap, + type AztecAsyncMultiMap, type AztecAsyncSingleton, type CustomRange, mapRange, } from '@aztec/kv-store'; -import { InboxLeaf } from '@aztec/stdlib/messaging'; +import { type InboxBucket, InboxLeaf, updateInboxRollingHash } from '@aztec/stdlib/messaging'; -import { L1ToL2MessagesNotReadyError } from '../errors.js'; +import { + InboxBucketBoundaryNotSyncedError, + InboxBucketNotSyncedError, + L1ToL2MessagesNotReadyError, +} from '../errors.js'; import { type InboxMessage, deserializeInboxMessage, @@ -22,6 +27,81 @@ import { updateRollingHash, } from '../structs/inbox_message.js'; +/** + * Persisted snapshot of an Inbox rolling-hash bucket. Mirrors the fields the on-chain Inbox tracks per bucket, plus + * the L1 block the bucket was opened in and the index span of its messages, so rollbacks and range queries can work + * off bucket records alone without scanning messages. + */ +type BucketSnapshot = { + inboxRollingHash: Fr; + totalMsgCount: bigint; + timestamp: bigint; + l1BlockNumber: bigint; + msgCount: number; + firstMessageIndex: bigint; + lastMessageIndex: bigint; +}; + +function serializeBucketSnapshot(snapshot: BucketSnapshot): Buffer { + return serializeToBuffer([ + snapshot.inboxRollingHash, + bigintToUInt64BE(snapshot.totalMsgCount), + bigintToUInt64BE(snapshot.timestamp), + bigintToUInt64BE(snapshot.l1BlockNumber), + numToUInt32BE(snapshot.msgCount), + bigintToUInt64BE(snapshot.firstMessageIndex), + bigintToUInt64BE(snapshot.lastMessageIndex), + ]); +} + +function deserializeBucketSnapshot(buffer: Buffer): BucketSnapshot { + const reader = BufferReader.asReader(buffer); + const inboxRollingHash = reader.readObject(Fr); + const totalMsgCount = reader.readUInt64(); + const timestamp = reader.readUInt64(); + const l1BlockNumber = reader.readUInt64(); + const msgCount = reader.readNumber(); + const firstMessageIndex = reader.readUInt64(); + const lastMessageIndex = reader.readUInt64(); + return { inboxRollingHash, totalMsgCount, timestamp, l1BlockNumber, msgCount, firstMessageIndex, lastMessageIndex }; +} + +/** The messages of a single Inbox bucket within an incoming batch, in insertion order. */ +type IncomingBucket = { + seq: bigint; + messages: InboxMessage[]; +}; + +/** + * Splits an incoming batch of messages into per-bucket groups, in delivery order. Messages arrive ordered by index + * and a bucket's messages are contiguous within that order, so a group ends as soon as the bucket sequence changes. + */ +function groupMessagesByBucket(messages: InboxMessage[]): IncomingBucket[] { + const buckets: IncomingBucket[] = []; + for (const message of messages) { + const current = buckets.at(-1); + if (current !== undefined && current.seq === message.bucketSeq) { + current.messages.push(message); + } else { + buckets.push({ seq: message.bucketSeq, messages: [message] }); + } + } + return buckets; +} + +// The genesis sentinel bucket: sequence 0 with a zero rolling hash and no messages, mirroring the +// on-chain Inbox's base case. The archiver never ingests a snapshot for it (no message is absorbed into sequence 0), so +// it is synthesized on read. Consumers use its sequence number and zero total; its deploy-time timestamp is not tracked +// here and is unused. +const GENESIS_INBOX_BUCKET: InboxBucket = { + seq: 0n, + inboxRollingHash: Fr.ZERO, + totalMsgCount: 0n, + timestamp: 0n, + msgCount: 0, + lastMessageIndex: 0n, +}; + export class MessageStoreError extends Error { constructor( message: string, @@ -45,6 +125,14 @@ export class MessageStore { #inboxTreeInProgress: AztecAsyncSingleton; /** Stores the L1 finalized block as of the last successful message sync. */ #messagesFinalizedL1Block: AztecAsyncSingleton; + /** Maps from Inbox bucket sequence number to its serialized snapshot. */ + #inboxBuckets: AztecAsyncMap; + /** + * Maps from a bucket's L1 timestamp (key) to the sequence numbers of the buckets opened at that timestamp. Holds + * several sequences per timestamp when a full bucket rolls over within one L1 block, so that deleting one bucket + * leaves its rollover siblings indexed. + */ + #bucketTimestampToSeq: AztecAsyncMultiMap; #log = createLogger('archiver:message_store'); @@ -55,6 +143,8 @@ export class MessageStore { this.#totalMessageCount = db.openSingleton('archiver_l1_to_l2_message_count'); this.#inboxTreeInProgress = db.openSingleton('archiver_inbox_tree_in_progress'); this.#messagesFinalizedL1Block = db.openSingleton('archiver_messages_finalized_l1_block'); + this.#inboxBuckets = db.openMap('archiver_inbox_buckets'); + this.#bucketTimestampToSeq = db.openMultiMap('archiver_inbox_bucket_timestamps'); } public async getTotalL1ToL2MessageCount(): Promise { @@ -99,11 +189,20 @@ export class MessageStore { } /** - * Append L1 to L2 messages to the store. - * Requires new messages to be in order and strictly after the last message added. - * Throws if out of order messages are added or if the rolling hash is invalid. + * Appends L1 to L2 messages to the store, one whole Inbox bucket at a time. + * + * A bucket is opened and closed within a single L1 block, and callers retrieve Inbox logs in whole-L1-block ranges, + * so every message of a bucket reaches this method in the same call — including the rollover buckets a full block + * spills into. The bucket snapshots are derived from that: the batch is split per bucket sequence and each bucket + * gets a single snapshot built from its complete message set. Delivering only part of a bucket already held in the + * store is rejected, since the snapshot would then undercount the bucket. Delivering a stored bucket again from its + * first message is allowed: an L1 reorg can replace a bucket's tail, and the re-sync that follows replays the whole + * L1 block it lives in. + * + * Requires messages to be ordered by index and to continue the stored chain. Throws a `MessageStoreError` if + * messages arrive out of order, if the rolling hash chain breaks, or if a bucket arrives incomplete. */ - public addL1ToL2Messages(messages: InboxMessage[]): Promise { + public addL1ToL2MessageBuckets(messages: InboxMessage[]): Promise { if (messages.length === 0) { return Promise.resolve(); } @@ -112,6 +211,9 @@ export class MessageStore { let lastMessage = await this.getLastMessage(); let messageCount = 0; + const incomingBuckets = groupMessagesByBucket(messages); + await this.assertIncomingBucketsAreComplete(incomingBuckets); + for (const message of messages) { // Check messages are inserted in increasing order, but allow reinserting messages. if (lastMessage && message.index <= lastMessage.index) { @@ -141,37 +243,28 @@ export class MessageStore { ); } - // Check index corresponds to the checkpoint number. - const [expectedStart, expectedEnd] = InboxLeaf.indexRangeForCheckpoint(message.checkpointNumber); - if (message.index < expectedStart || message.index >= expectedEnd) { - throw new MessageStoreError( - `Invalid index ${message.index} for incoming L1 to L2 message ${message.leaf.toString()} ` + - `at checkpoint ${message.checkpointNumber} (expected value in range [${expectedStart}, ${expectedEnd}))`, - message, - ); - } - - // Check there are no gaps in the indices within the same checkpoint. - if ( - lastMessage && - message.checkpointNumber === lastMessage.checkpointNumber && - message.index !== lastMessage.index + 1n - ) { + // Check the full-width consensus rolling hash is valid (AZIP-22 Fast Inbox). Runs alongside the legacy + // 128-bit check above until the streaming inbox flips on and the legacy hash is removed. + const previousInboxRollingHash = lastMessage?.inboxRollingHash ?? Fr.ZERO; + const expectedInboxRollingHash = updateInboxRollingHash(previousInboxRollingHash, message.leaf); + if (!expectedInboxRollingHash.equals(message.inboxRollingHash)) { throw new MessageStoreError( - `Missing prior message for incoming L1 to L2 message ${message.leaf.toString()} ` + - `with index ${message.index}`, + `Invalid inbox rolling hash for incoming L1 to L2 message ${message.leaf.toString()} ` + + `with index ${message.index} ` + + `(expected ${expectedInboxRollingHash.toString()} from previous hash ${previousInboxRollingHash.toString()} ` + + `but got ${message.inboxRollingHash.toString()})`, message, ); } - // Check the first message in a checkpoint has the correct index. - if ( - (!lastMessage || message.checkpointNumber > lastMessage.checkpointNumber) && - message.index !== expectedStart - ) { + // Check the compact-indexed messages arrive contiguously (AZIP-22 Fast Inbox): the global insertion index of + // each message is exactly one past the previous one, independent of the checkpoint it landed in. The flipped + // Inbox emits this compact totalMessagesInserted index, so the legacy per-checkpoint range no longer applies. + const expectedIndex = lastMessage === undefined ? 0n : lastMessage.index + 1n; + if (message.index !== expectedIndex) { throw new MessageStoreError( - `Message ${message.leaf.toString()} for checkpoint ${message.checkpointNumber} has wrong index ` + - `${message.index} (expected ${expectedStart})`, + `Invalid index ${message.index} for incoming L1 to L2 message ${message.leaf.toString()} ` + + `(expected ${expectedIndex})`, message, ); } @@ -180,15 +273,75 @@ export class MessageStore { await this.#l1ToL2Messages.set(this.indexToKey(message.index), serializeInboxMessage(message)); await this.#l1ToL2MessageIndices.set(this.leafToIndexKey(message.leaf), message.index); messageCount++; + this.#log.trace(`Inserted L1 to L2 message ${message.leaf} with index ${message.index} into the store`); lastMessage = message; } + await this.writeIncomingBucketSnapshots(incomingBuckets); + // Update total message count with the number of inserted messages. await this.increaseTotalMessageCount(messageCount); }); } + /** + * Rejects a batch that delivers an Inbox bucket the store already holds without replaying it from its first message, + * or that opens a bucket older than the newest one stored. Either would produce a snapshot that disagrees with the + * messages it covers, since each snapshot is derived from the batch's messages for that bucket alone. + */ + private async assertIncomingBucketsAreComplete(incomingBuckets: IncomingBucket[]): Promise { + const newestStoredSeq = await this.getNewestBucketSeq(); + let previousSeq: bigint | undefined; + for (const bucket of incomingBuckets) { + if (previousSeq !== undefined && bucket.seq <= previousSeq) { + throw new MessageStoreError( + `Inbox bucket ${bucket.seq} arrives after bucket ${previousSeq} in the same batch`, + bucket.messages[0], + ); + } + previousSeq = bucket.seq; + + const stored = await this.getBucketSnapshotBySeq(bucket.seq); + if (stored === undefined) { + if (newestStoredSeq !== undefined && bucket.seq <= newestStoredSeq) { + throw new MessageStoreError( + `Cannot open Inbox bucket ${bucket.seq} after bucket ${newestStoredSeq} has been stored`, + bucket.messages[0], + ); + } + } else if (stored.firstMessageIndex !== bucket.messages[0].index) { + throw new MessageStoreError( + `Incomplete Inbox bucket ${bucket.seq}: stored messages start at index ${stored.firstMessageIndex} ` + + `but the batch starts at index ${bucket.messages[0].index}`, + bucket.messages[0], + ); + } + } + } + + /** + * Writes one snapshot per bucket in the batch, each derived from the bucket's complete message set. Cumulative + * totals thread forward from the bucket preceding the batch, so a bucket re-delivered with extra messages shifts + * the totals of the buckets after it within the same batch. + */ + private async writeIncomingBucketSnapshots(incomingBuckets: IncomingBucket[]): Promise { + let cumulativeTotal = await this.getTotalMsgCountBeforeBucket(incomingBuckets[0].seq); + for (const { seq, messages } of incomingBuckets) { + const lastInBucket = messages.at(-1)!; + cumulativeTotal += BigInt(messages.length); + await this.writeBucketSnapshot(seq, { + inboxRollingHash: lastInBucket.inboxRollingHash, + totalMsgCount: cumulativeTotal, + timestamp: lastInBucket.bucketTimestamp, + l1BlockNumber: lastInBucket.l1BlockNumber, + msgCount: messages.length, + firstMessageIndex: messages[0].index, + lastMessageIndex: lastInBucket.index, + }); + } + } + /** * Gets the L1 to L2 message index in the L1 to L2 message tree. * @param l1ToL2Message - The L1 to L2 message. @@ -281,10 +434,221 @@ export class MessageStore { deleteCount++; } await this.increaseTotalMessageCount(-deleteCount); + await this.rewindBucketsAfterRemoval(); this.#log.warn(`Deleted ${deleteCount} L1 to L2 messages from index ${startIndex} from the store`); }); } + /** + * Rewinds the Inbox bucket snapshots to match the messages left after a removal. Each bucket past the surviving tip + * is deleted together with its own timestamp index entry, leaving the entries of the rollover siblings it shares a + * timestamp with in place. + * + * The bucket holding the last surviving message is rewritten from the messages it has left, because a removal can + * cut inside a bucket: the synchronizer rolls back to the last message it still shares with L1 after a reorg, and + * that message need not be the last of its bucket. Must run inside the removal transaction, after the total message + * count has been updated. + */ + private async rewindBucketsAfterRemoval(): Promise { + const lastRemaining = await this.getLastMessage(); + const boundarySeq = lastRemaining?.bucketSeq; + + const deleteFromKey = boundarySeq === undefined ? 0 : this.bucketSeqToKey(boundarySeq) + 1; + for await (const [seqKey, snapBuffer] of this.#inboxBuckets.entriesAsync({ start: deleteFromKey })) { + const snapshot = deserializeBucketSnapshot(snapBuffer); + await this.#bucketTimestampToSeq.deleteValue(this.timestampToKey(snapshot.timestamp), seqKey); + await this.#inboxBuckets.delete(seqKey); + } + + if (lastRemaining === undefined || boundarySeq === undefined) { + return; + } + let msgCount = 0; + for await (const msg of this.iterateL1ToL2Messages({ reverse: true })) { + if (msg.bucketSeq !== boundarySeq) { + break; + } + msgCount += 1; + } + const stored = await this.getSyncedBucketSnapshot(boundarySeq); + await this.writeBucketSnapshot(boundarySeq, { + ...stored, + inboxRollingHash: lastRemaining.inboxRollingHash, + totalMsgCount: await this.getTotalL1ToL2MessageCount(), + msgCount, + lastMessageIndex: lastRemaining.index, + }); + } + + /** + * Returns the Inbox bucket with the given sequence number, or undefined if it has not been synced. Sequence 0 is + * the genesis sentinel: the on-chain Inbox reserves it as the "consumed nothing" base case + * and never absorbs a message into it, so the archiver ingests no snapshot for it; it is synthesized here (rolling + * hash 0, total 0) so streaming consumers can resolve a genesis parent or an empty checkpoint's last-consumed bucket. + */ + public async getInboxBucket(seq: bigint): Promise { + const snapshot = await this.getBucketSnapshotBySeq(seq); + if (snapshot !== undefined) { + return this.toInboxBucket(seq, snapshot); + } + return seq === 0n ? GENESIS_INBOX_BUCKET : undefined; + } + + /** + * Returns the Inbox bucket whose cumulative message total equals `totalMsgCount`, or undefined if no synced bucket + * sits on that boundary. Sequence 0 (total 0) is the genesis sentinel base case; otherwise the + * message at global index `totalMsgCount - 1` is the last message of the bucket with that cumulative total, so its + * `bucketSeq` resolves the bucket. A total that does not land on a bucket boundary returns undefined. + */ + public async getInboxBucketByTotalMsgCount(totalMsgCount: bigint): Promise { + if (totalMsgCount === 0n) { + return this.getInboxBucket(0n); + } + const buffer = await this.#l1ToL2Messages.getAsync(this.indexToKey(totalMsgCount - 1n)); + if (buffer === undefined) { + return undefined; + } + const bucket = await this.getInboxBucket(deserializeInboxMessage(buffer).bucketSeq); + return bucket !== undefined && bucket.totalMsgCount === totalMsgCount ? bucket : undefined; + } + + /** + * Returns the message leaves in the cumulative Inbox message-count range `[startLeafCount, endLeafCount)`, in + * insertion order. The bounds are compact L1-to-L2 tree leaf counts, which every block header + * carries, so consumers can ask for the messages a block or checkpoint consumed without resolving buckets + * themselves. Both bounds must land on a bucket boundary this archiver has synced; it throws otherwise, since a + * caller asking for a range always expects the messages in it. + */ + public async getL1ToL2MessagesBetweenLeafCounts(startLeafCount: bigint, endLeafCount: bigint): Promise { + if (startLeafCount > endLeafCount) { + throw new Error(`Invalid Inbox leaf count range [${startLeafCount}, ${endLeafCount})`); + } + const startBucket = await this.getBucketAtBoundary(startLeafCount); + const endBucket = await this.getBucketAtBoundary(endLeafCount); + return this.getL1ToL2MessagesBetweenBuckets(startBucket.seq, endBucket.seq); + } + + /** Resolves the bucket ending at the given cumulative message count, failing loudly if there is none. */ + private async getBucketAtBoundary(totalMsgCount: bigint): Promise { + const bucket = await this.getInboxBucketByTotalMsgCount(totalMsgCount); + if (bucket === undefined) { + throw new InboxBucketBoundaryNotSyncedError(totalMsgCount); + } + return bucket; + } + + /** + * Returns the latest Inbox bucket opened at or before the given L1 timestamp, or undefined if every synced bucket + * was opened strictly after it. + */ + public async getLatestInboxBucketAtOrBefore(timestamp: bigint): Promise { + // Bucket timestamps are non-decreasing in sequence number, so the bucket we want is the highest sequence indexed + // at the largest timestamp at-or-before the requested one. A reverse scan bounded above (inclusively) by that + // timestamp visits it first; rollover buckets share a timestamp, so keep the highest of its sequences. + let latestTimestampKey: number | undefined; + let latestSeqKey: number | undefined; + for await (const [timestampKey, seqKey] of this.#bucketTimestampToSeq.entriesAsync({ + end: this.timestampToKey(timestamp), + reverse: true, + })) { + if (latestTimestampKey !== undefined && timestampKey !== latestTimestampKey) { + break; + } + latestTimestampKey = timestampKey; + latestSeqKey = latestSeqKey === undefined ? seqKey : Math.max(latestSeqKey, seqKey); + } + return latestSeqKey === undefined ? undefined : this.getInboxBucket(BigInt(latestSeqKey)); + } + + /** + * Returns the message leaves absorbed into buckets in the range `(fromExclusive, toInclusive]`, in insertion order. + * Both bounds must name buckets this archiver has synced, so that an empty result means the + * range holds no messages rather than hiding an unsynced bound; callers route the + * `InboxBucketNotSyncedError` to their own catch-up handling. Sequence 0 is the genesis base case and always + * resolves: the range then starts at the first message of the Inbox. + */ + public async getL1ToL2MessagesBetweenBuckets(fromExclusive: bigint, toInclusive: bigint): Promise { + if (fromExclusive > toInclusive) { + throw new Error(`Invalid Inbox bucket range (${fromExclusive}, ${toInclusive}]`); + } + if (toInclusive === 0n) { + return []; + } + const endIndexExclusive = (await this.getSyncedBucketSnapshot(toInclusive)).lastMessageIndex + 1n; + const startIndex = + fromExclusive === 0n ? 0n : (await this.getSyncedBucketSnapshot(fromExclusive)).lastMessageIndex + 1n; + return this.getMessageLeavesInIndexRange(startIndex, endIndexExclusive); + } + + /** Collects the message leaves in the global index range `[startIndex, endIndexExclusive)`, in insertion order. */ + private async getMessageLeavesInIndexRange(startIndex: bigint, endIndexExclusive: bigint): Promise { + const leaves: Fr[] = []; + for await (const msgBuffer of this.#l1ToL2Messages.valuesAsync({ + start: this.indexToKey(startIndex), + end: this.indexToKey(endIndexExclusive), + })) { + leaves.push(deserializeInboxMessage(msgBuffer).leaf); + } + return leaves; + } + + private async getBucketSnapshotBySeq(seq: bigint): Promise { + const buffer = await this.#inboxBuckets.getAsync(this.bucketSeqToKey(seq)); + return buffer && deserializeBucketSnapshot(buffer); + } + + /** Reads a bucket snapshot, failing loudly if the archiver has not synced that bucket. */ + private async getSyncedBucketSnapshot(seq: bigint): Promise { + const snapshot = await this.getBucketSnapshotBySeq(seq); + if (snapshot === undefined) { + throw new InboxBucketNotSyncedError(seq); + } + return snapshot; + } + + private async writeBucketSnapshot(seq: bigint, snapshot: BucketSnapshot): Promise { + // A reorg can re-deliver a bucket from an L1 block mined at a different timestamp, so drop the stale index entry. + const stored = await this.getBucketSnapshotBySeq(seq); + if (stored !== undefined && stored.timestamp !== snapshot.timestamp) { + await this.#bucketTimestampToSeq.deleteValue(this.timestampToKey(stored.timestamp), this.bucketSeqToKey(seq)); + } + await this.#inboxBuckets.set(this.bucketSeqToKey(seq), serializeBucketSnapshot(snapshot)); + await this.#bucketTimestampToSeq.set(this.timestampToKey(snapshot.timestamp), this.bucketSeqToKey(seq)); + } + + /** Returns the sequence number of the newest stored bucket, or undefined if none has been stored yet. */ + private async getNewestBucketSeq(): Promise { + const [seqKey] = await toArray(this.#inboxBuckets.keysAsync({ reverse: true, limit: 1 })); + return seqKey === undefined ? undefined : BigInt(seqKey); + } + + /** Returns the cumulative Inbox message count through the newest stored bucket before the given sequence number. */ + private async getTotalMsgCountBeforeBucket(seq: bigint): Promise { + const [snapBuffer] = await toArray( + this.#inboxBuckets.valuesAsync({ end: this.bucketSeqToKey(seq) - 1, reverse: true, limit: 1 }), + ); + return snapBuffer === undefined ? 0n : deserializeBucketSnapshot(snapBuffer).totalMsgCount; + } + + private toInboxBucket(seq: bigint, snapshot: BucketSnapshot): InboxBucket { + return { + seq, + inboxRollingHash: snapshot.inboxRollingHash, + totalMsgCount: snapshot.totalMsgCount, + timestamp: snapshot.timestamp, + msgCount: snapshot.msgCount, + lastMessageIndex: snapshot.lastMessageIndex, + }; + } + + private bucketSeqToKey(seq: bigint): number { + return Number(seq); + } + + private timestampToKey(timestamp: bigint): number { + return Number(timestamp); + } + public rollbackL1ToL2MessagesToCheckpoint(targetCheckpointNumber: CheckpointNumber): Promise { this.#log.debug(`Deleting L1 to L2 messages up to target checkpoint ${targetCheckpointNumber}`); const startIndex = InboxLeaf.smallestIndexForCheckpoint(CheckpointNumber(targetCheckpointNumber + 1)); diff --git a/yarn-project/archiver/src/structs/inbox_message.ts b/yarn-project/archiver/src/structs/inbox_message.ts index 9f981b505e9b..137ee14b1044 100644 --- a/yarn-project/archiver/src/structs/inbox_message.ts +++ b/yarn-project/archiver/src/structs/inbox_message.ts @@ -10,7 +10,14 @@ export type InboxMessage = { checkpointNumber: CheckpointNumber; l1BlockNumber: bigint; l1BlockHash: Buffer32; + /** Legacy 128-bit keccak rolling hash of all messages inserted up to and including this one. */ rollingHash: Buffer16; + /** Consensus rolling hash (truncated sha256 chain) of all messages up to and including this one. */ + inboxRollingHash: Fr; + /** Sequence number of the Inbox bucket this message was absorbed into. */ + bucketSeq: bigint; + /** L1 block timestamp at which this message's bucket was opened; the bucket's recency key, in seconds. */ + bucketTimestamp: bigint; }; export function updateRollingHash(currentRollingHash: Buffer16, leaf: Fr): Buffer16 { @@ -23,9 +30,12 @@ export function serializeInboxMessage(message: InboxMessage): Buffer { bigintToUInt64BE(message.index), message.leaf, message.l1BlockHash, - numToUInt32BE(Number(message.l1BlockNumber)), + bigintToUInt64BE(message.l1BlockNumber), numToUInt32BE(message.checkpointNumber), message.rollingHash, + message.inboxRollingHash, + bigintToUInt64BE(message.bucketSeq), + bigintToUInt64BE(message.bucketTimestamp), ]); } @@ -34,8 +44,21 @@ export function deserializeInboxMessage(buffer: Buffer): InboxMessage { const index = reader.readUInt64(); const leaf = reader.readObject(Fr); const l1BlockHash = reader.readObject(Buffer32); - const l1BlockNumber = BigInt(reader.readNumber()); + const l1BlockNumber = reader.readUInt64(); const checkpointNumber = CheckpointNumber(reader.readNumber()); const rollingHash = reader.readObject(Buffer16); - return { index, leaf, l1BlockHash, l1BlockNumber, checkpointNumber, rollingHash }; + const inboxRollingHash = reader.readObject(Fr); + const bucketSeq = reader.readUInt64(); + const bucketTimestamp = reader.readUInt64(); + return { + index, + leaf, + l1BlockHash, + l1BlockNumber, + checkpointNumber, + rollingHash, + inboxRollingHash, + bucketSeq, + bucketTimestamp, + }; } diff --git a/yarn-project/archiver/src/test/fake_l1_state.ts b/yarn-project/archiver/src/test/fake_l1_state.ts index d5e5bf6e2197..e7354880213d 100644 --- a/yarn-project/archiver/src/test/fake_l1_state.ts +++ b/yarn-project/archiver/src/test/fake_l1_state.ts @@ -14,7 +14,7 @@ import { RollupAbi } from '@aztec/l1-artifacts'; import { CommitteeAttestation, CommitteeAttestationsAndSigners, L2Block } from '@aztec/stdlib/block'; import { Checkpoint } from '@aztec/stdlib/checkpoint'; import { getSlotAtTimestamp } from '@aztec/stdlib/epoch-helpers'; -import { InboxLeaf } from '@aztec/stdlib/messaging'; +import { updateInboxRollingHash } from '@aztec/stdlib/messaging'; import { ConsensusPayload, getHashedSignaturePayloadTypedData } from '@aztec/stdlib/p2p'; import { mockCheckpointAndMessages } from '@aztec/stdlib/testing'; import { AppendOnlyTreeSnapshot } from '@aztec/stdlib/trees'; @@ -109,8 +109,13 @@ type MessageData = { index: bigint; leaf: Fr; rollingHash: Buffer16; + inboxRollingHash: Fr; + bucketSeq: bigint; }; +// Mirror of the on-chain per-bucket message cap: further messages in the same L1 block spill into the next bucket. +const MAX_MSGS_PER_BUCKET = 256; + /** * Stateful fake for L1 data used by the archiver. * @@ -138,6 +143,11 @@ export class FakeL1State { private checkpoints: CheckpointData[] = []; private messages: MessageData[] = []; private messagesRollingHash: Buffer16 = Buffer16.ZERO; + // Consensus rolling-hash and bucket-ring state, mirroring the on-chain Inbox. + private messagesConsensusRollingHash: Fr = Fr.ZERO; + private currentBucketSeq: bigint = 0n; + private currentBucketTimestamp: bigint = 0n; + private currentBucketMsgCount: number = 0; private lastArchive: AppendOnlyTreeSnapshot; private provenCheckpointNumber: CheckpointNumber = CheckpointNumber(0); private targetCommitteeSize: number = 0; @@ -166,9 +176,12 @@ export class FakeL1State { * Use this method only when you need to add messages without creating a checkpoint (e.g., for reorg tests). */ addMessages(checkpointNumber: CheckpointNumber, l1BlockNumber: bigint, messageLeaves: Fr[]): void { - messageLeaves.forEach((leaf, i) => { - const index = InboxLeaf.smallestIndexForCheckpoint(checkpointNumber) + BigInt(i); + const timestamp = this.getTimestampAtL1Block(l1BlockNumber); + messageLeaves.forEach(leaf => { + // Compact global insertion index (AZIP-22 Fast Inbox): position in the Inbox's insertion order. + const index = BigInt(this.messages.length); this.messagesRollingHash = updateRollingHash(this.messagesRollingHash, leaf); + const { bucketSeq, inboxRollingHash } = this.absorbIntoBucket(leaf, timestamp); this.messages.push({ l1BlockNumber, @@ -176,10 +189,49 @@ export class FakeL1State { index, leaf, rollingHash: this.messagesRollingHash, + inboxRollingHash, + bucketSeq, }); }); } + /** Mirrors the on-chain `_absorbIntoBucket`: opens a new bucket on a strictly larger timestamp or a full bucket. */ + private absorbIntoBucket(leaf: Fr, timestamp: bigint): { bucketSeq: bigint; inboxRollingHash: Fr } { + if ( + this.currentBucketSeq === 0n || + this.currentBucketTimestamp < timestamp || + this.currentBucketMsgCount === MAX_MSGS_PER_BUCKET + ) { + this.currentBucketSeq += 1n; + this.currentBucketTimestamp = timestamp; + this.currentBucketMsgCount = 0; + } + this.messagesConsensusRollingHash = updateInboxRollingHash(this.messagesConsensusRollingHash, leaf); + this.currentBucketMsgCount += 1; + return { bucketSeq: this.currentBucketSeq, inboxRollingHash: this.messagesConsensusRollingHash }; + } + + /** Rebuilds all per-message derived state (rolling hashes and bucket assignments) after the message set changes. */ + private recomputeDerivedMessageState(): void { + this.messagesRollingHash = Buffer16.ZERO; + this.messagesConsensusRollingHash = Fr.ZERO; + this.currentBucketSeq = 0n; + this.currentBucketTimestamp = 0n; + this.currentBucketMsgCount = 0; + this.messages.forEach((msg, i) => { + this.messagesRollingHash = updateRollingHash(this.messagesRollingHash, msg.leaf); + const { bucketSeq, inboxRollingHash } = this.absorbIntoBucket( + msg.leaf, + this.getTimestampAtL1Block(msg.l1BlockNumber), + ); + // Keep the compact global insertion index contiguous after the message set changes (e.g. reorgs). + msg.index = BigInt(i); + msg.rollingHash = this.messagesRollingHash; + msg.inboxRollingHash = inboxRollingHash; + msg.bucketSeq = bucketSeq; + }); + } + /** * Creates blocks for a checkpoint without adding them to L1 state. * Useful for creating blocks to pass to addBlock() for testing provisional block handling. @@ -354,8 +406,7 @@ export class FakeL1State { */ removeMessagesAfter(totalIndex: number): void { this.messages = this.messages.slice(0, totalIndex); - // Recalculate rolling hash - this.messagesRollingHash = this.messages.reduce((hash, msg) => updateRollingHash(hash, msg.leaf), Buffer16.ZERO); + this.recomputeDerivedMessageState(); } /** @@ -623,13 +674,14 @@ export class FakeL1State { l1BlockNumber: msg.l1BlockNumber, l1BlockHash: Buffer32.fromBigInt(msg.l1BlockNumber), l1TransactionHash: `0x${msg.l1BlockNumber.toString(16)}` as `0x${string}`, + l1BlockTimestamp: this.getTimestampAtL1Block(msg.l1BlockNumber), args: { checkpointNumber: msg.checkpointNumber, index: msg.index, leaf: msg.leaf, rollingHash: msg.rollingHash, - inboxRollingHash: Fr.ZERO, - bucketSeq: 0n, + inboxRollingHash: msg.inboxRollingHash, + bucketSeq: msg.bucketSeq, }, })); } @@ -648,13 +700,14 @@ export class FakeL1State { l1BlockNumber: msg.l1BlockNumber, l1BlockHash: Buffer32.fromBigInt(msg.l1BlockNumber), l1TransactionHash: `0x${msg.l1BlockNumber.toString(16)}` as `0x${string}`, + l1BlockTimestamp: this.getTimestampAtL1Block(msg.l1BlockNumber), args: { checkpointNumber: msg.checkpointNumber, index: msg.index, leaf: msg.leaf, rollingHash: msg.rollingHash, - inboxRollingHash: Fr.ZERO, - bucketSeq: 0n, + inboxRollingHash: msg.inboxRollingHash, + bucketSeq: msg.bucketSeq, }, }; } @@ -696,6 +749,7 @@ export class FakeL1State { header, archive, oracleInput: { feeAssetPriceModifier: 0n }, + bucketHint: 0n, }, verbatimAttestations, attestationsAndSigners.getSigners().map(signer => signer.toString()), diff --git a/yarn-project/archiver/src/test/mock_archiver.ts b/yarn-project/archiver/src/test/mock_archiver.ts index bcdcc3928d96..029ea7f8a205 100644 --- a/yarn-project/archiver/src/test/mock_archiver.ts +++ b/yarn-project/archiver/src/test/mock_archiver.ts @@ -1,8 +1,8 @@ import type { CheckpointNumber } from '@aztec/foundation/branded-types'; -import type { Fr } from '@aztec/foundation/curves/bn254'; +import { Fr } from '@aztec/foundation/curves/bn254'; import type { L2BlockSource } from '@aztec/stdlib/block'; import type { Checkpoint } from '@aztec/stdlib/checkpoint'; -import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging'; +import type { InboxBucket, L1ToL2MessageSource } from '@aztec/stdlib/messaging'; import { MockL1ToL2MessageSource } from './mock_l1_to_l2_message_source.js'; import { MockL2BlockSource } from './mock_l2_block_source.js'; @@ -17,6 +17,10 @@ export class MockArchiver extends MockL2BlockSource implements L2BlockSource, L1 this.messageSource.setL1ToL2Messages(checkpointNumber, msgs); } + public setInboxBucket(bucket: InboxBucket, msgs: Fr[] = []) { + this.messageSource.setInboxBucket(bucket, msgs); + } + getL1ToL2Messages(checkpointNumber: CheckpointNumber): Promise { return this.messageSource.getL1ToL2Messages(checkpointNumber); } @@ -24,6 +28,26 @@ export class MockArchiver extends MockL2BlockSource implements L2BlockSource, L1 getL1ToL2MessageIndex(_l1ToL2Message: Fr): Promise { return this.messageSource.getL1ToL2MessageIndex(_l1ToL2Message); } + + getLatestInboxBucketAtOrBefore(timestamp: bigint): Promise { + return this.messageSource.getLatestInboxBucketAtOrBefore(timestamp); + } + + getInboxBucket(seq: bigint): Promise { + return this.messageSource.getInboxBucket(seq); + } + + getInboxBucketByTotalMsgCount(totalMsgCount: bigint): Promise { + return this.messageSource.getInboxBucketByTotalMsgCount(totalMsgCount); + } + + getL1ToL2MessagesBetweenBuckets(fromExclusive: bigint, toInclusive: bigint): Promise { + return this.messageSource.getL1ToL2MessagesBetweenBuckets(fromExclusive, toInclusive); + } + + getL1ToL2MessagesBetweenLeafCounts(startLeafCount: bigint, endLeafCount: bigint): Promise { + return this.messageSource.getL1ToL2MessagesBetweenLeafCounts(startLeafCount, endLeafCount); + } } /** @@ -31,6 +55,7 @@ export class MockArchiver extends MockL2BlockSource implements L2BlockSource, L1 */ export class MockPrefilledArchiver extends MockArchiver { private prefilled: Checkpoint[] = []; + private prefilledMessages: Fr[][] = []; constructor(prefilled: { checkpoint: Checkpoint; messages: Fr[] }[]) { super(); @@ -45,6 +70,49 @@ export class MockPrefilledArchiver extends MockArchiver { } this.setL1ToL2Messages(checkpoint.number, messages); } + + for (const { checkpoint, messages } of prefilled) { + this.prefilledMessages[checkpoint.number - 1] = messages; + } + + // Register the Inbox buckets the streaming world-state synchronizer reconstructs each block's consumed + // message bundle from: a genesis sentinel (totalMsgCount 0) so a leaf count of 0 + // resolves to a bucket, plus one bucket per message-carrying checkpoint whose cumulative totalMsgCount + // matches the block's post-insertion L1-to-L2 leaf count. Rebuilt from the full prefilled chain (not just + // this call's checkpoints) so a reorg re-prefill that replaces a suffix keeps the cumulative aligned. + // Without these the synchronizer derives an empty bundle and the reconstructed block state diverges. + this.setInboxBucket( + { + seq: 0n, + inboxRollingHash: Fr.ZERO, + totalMsgCount: 0n, + timestamp: 0n, + msgCount: 0, + lastMessageIndex: 0n, + }, + [], + ); + let bucketSeq = 0n; + let totalMsgCount = 0n; + for (let i = 0; i < this.prefilled.length; i++) { + const messages = this.prefilledMessages[i] ?? []; + if (messages.length === 0) { + continue; + } + bucketSeq += 1n; + totalMsgCount += BigInt(messages.length); + this.setInboxBucket( + { + seq: bucketSeq, + inboxRollingHash: Fr.ZERO, + totalMsgCount, + timestamp: bucketSeq, + msgCount: messages.length, + lastMessageIndex: totalMsgCount - 1n, + }, + messages, + ); + } } public override createBlocks(numBlocks: number) { diff --git a/yarn-project/archiver/src/test/mock_l1_to_l2_message_source.ts b/yarn-project/archiver/src/test/mock_l1_to_l2_message_source.ts index f3b72a1c940f..7a8940a12a23 100644 --- a/yarn-project/archiver/src/test/mock_l1_to_l2_message_source.ts +++ b/yarn-project/archiver/src/test/mock_l1_to_l2_message_source.ts @@ -1,13 +1,15 @@ import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types'; import { Fr } from '@aztec/foundation/curves/bn254'; import type { CheckpointId, L2BlockId, L2TipId, L2Tips } from '@aztec/stdlib/block'; -import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging'; +import type { InboxBucket, L1ToL2MessageSource } from '@aztec/stdlib/messaging'; /** * A mocked implementation of L1ToL2MessageSource to be used in tests. */ export class MockL1ToL2MessageSource implements L1ToL2MessageSource { private messagesPerCheckpoint = new Map(); + private buckets = new Map(); + private messagesPerBucket = new Map(); constructor(private blockNumber: number) {} @@ -15,6 +17,11 @@ export class MockL1ToL2MessageSource implements L1ToL2MessageSource { this.messagesPerCheckpoint.set(checkpointNumber, msgs); } + public setInboxBucket(bucket: InboxBucket, msgs: Fr[] = []) { + this.buckets.set(bucket.seq, bucket); + this.messagesPerBucket.set(bucket.seq, msgs); + } + public setBlockNumber(blockNumber: number) { this.blockNumber = blockNumber; } @@ -27,6 +34,40 @@ export class MockL1ToL2MessageSource implements L1ToL2MessageSource { throw new Error('Method not implemented.'); } + getInboxBucket(seq: bigint): Promise { + return Promise.resolve(this.buckets.get(seq)); + } + + getInboxBucketByTotalMsgCount(totalMsgCount: bigint): Promise { + if (totalMsgCount === 0n) { + return Promise.resolve(this.buckets.get(0n)); + } + return Promise.resolve([...this.buckets.values()].find(bucket => bucket.totalMsgCount === totalMsgCount)); + } + + getLatestInboxBucketAtOrBefore(timestamp: bigint): Promise { + const atOrBefore = [...this.buckets.values()] + .filter(bucket => bucket.timestamp <= timestamp) + .sort((a, b) => Number(a.seq - b.seq)); + return Promise.resolve(atOrBefore.at(-1)); + } + + getL1ToL2MessagesBetweenBuckets(fromExclusive: bigint, toInclusive: bigint): Promise { + const seqs = [...this.messagesPerBucket.keys()] + .filter(seq => seq > fromExclusive && seq <= toInclusive) + .sort((a, b) => Number(a - b)); + return Promise.resolve(seqs.flatMap(seq => this.messagesPerBucket.get(seq) ?? [])); + } + + async getL1ToL2MessagesBetweenLeafCounts(startLeafCount: bigint, endLeafCount: bigint): Promise { + const startBucket = await this.getInboxBucketByTotalMsgCount(startLeafCount); + const endBucket = await this.getInboxBucketByTotalMsgCount(endLeafCount); + if (startBucket === undefined || endBucket === undefined) { + throw new Error(`No mocked Inbox bucket boundary at ${startLeafCount} or ${endLeafCount}`); + } + return this.getL1ToL2MessagesBetweenBuckets(startBucket.seq, endBucket.seq); + } + getBlockNumber() { return Promise.resolve(BlockNumber(this.blockNumber)); } diff --git a/yarn-project/archiver/src/test/mock_structs.ts b/yarn-project/archiver/src/test/mock_structs.ts index 91145fcfcb11..5cc54cfe9bad 100644 --- a/yarn-project/archiver/src/test/mock_structs.ts +++ b/yarn-project/archiver/src/test/mock_structs.ts @@ -15,7 +15,7 @@ import { AztecAddress } from '@aztec/stdlib/aztec-address'; import { CommitteeAttestation, L2Block } from '@aztec/stdlib/block'; import { Checkpoint, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint'; import { PrivateLog, PublicLog, SiloedTag, Tag } from '@aztec/stdlib/logs'; -import { InboxLeaf } from '@aztec/stdlib/messaging'; +import { updateInboxRollingHash } from '@aztec/stdlib/messaging'; import { orderAttestations } from '@aztec/stdlib/p2p'; import { CheckpointHeader } from '@aztec/stdlib/rollup'; import { makeCheckpointAttestationFromCheckpoint } from '@aztec/stdlib/testing'; @@ -33,7 +33,12 @@ export function makeInboxMessage( const { l1BlockHash = Buffer32.random() } = overrides; const { leaf = Fr.random() } = overrides; const { rollingHash = updateRollingHash(previousRollingHash, leaf) } = overrides; - const { index = InboxLeaf.smallestIndexForCheckpoint(checkpointNumber) } = overrides; + // Compact global insertion index: defaults to the first slot. + const { index = 0n } = overrides; + const { inboxRollingHash = updateInboxRollingHash(Fr.ZERO, leaf) } = overrides; + // Default each message to its own bucket, keyed monotonically off its global index. + const { bucketSeq = index + 1n } = overrides; + const { bucketTimestamp = index + 1n } = overrides; return { index, @@ -42,6 +47,9 @@ export function makeInboxMessage( l1BlockNumber, l1BlockHash, rollingHash, + inboxRollingHash, + bucketSeq, + bucketTimestamp, }; } @@ -49,6 +57,8 @@ export function makeInboxMessages( totalCount: number, opts: { initialHash?: Buffer16; + initialInboxHash?: Fr; + initialIndex?: bigint; initialCheckpointNumber?: CheckpointNumber; messagesPerCheckpoint?: number; overrideFn?: (msg: InboxMessage, index: number) => InboxMessage; @@ -56,6 +66,8 @@ export function makeInboxMessages( ): InboxMessage[] { const { initialHash = Buffer16.ZERO, + initialInboxHash = Fr.ZERO, + initialIndex = 0n, overrideFn = msg => msg, initialCheckpointNumber = CheckpointNumber(1), messagesPerCheckpoint = 1, @@ -63,21 +75,24 @@ export function makeInboxMessages( const messages: InboxMessage[] = []; let rollingHash = initialHash; + let inboxRollingHash = initialInboxHash; for (let i = 0; i < totalCount; i++) { - const msgIndex = i % messagesPerCheckpoint; const checkpointNumber = CheckpointNumber.fromBigInt( BigInt(initialCheckpointNumber) + BigInt(i) / BigInt(messagesPerCheckpoint), ); const leaf = Fr.random(); + // Compact global insertion index (AZIP-22 Fast Inbox): contiguous from initialIndex, independent of checkpoint. const message = overrideFn( makeInboxMessage(rollingHash, { leaf, checkpointNumber, - index: InboxLeaf.smallestIndexForCheckpoint(checkpointNumber) + BigInt(msgIndex), + index: initialIndex + BigInt(i), + inboxRollingHash: updateInboxRollingHash(inboxRollingHash, leaf), }), i, ); rollingHash = message.rollingHash; + inboxRollingHash = message.inboxRollingHash; messages.push(message); } return messages; @@ -90,13 +105,13 @@ export function makeInboxMessagesWithFullBlocks( ): InboxMessage[] { const { initialCheckpointNumber = CheckpointNumber(13) } = opts; return makeInboxMessages(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP * blockCount, { + // Keep the compact global index from makeInboxMessages; only spread the (now-vestigial) checkpoint assignment + // across blocks so multi-block coverage still exercises differing checkpoint numbers. overrideFn: (msg, i) => { const checkpointNumber = CheckpointNumber( initialCheckpointNumber + Math.floor(i / NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP), ); - const index = - InboxLeaf.smallestIndexForCheckpoint(checkpointNumber) + BigInt(i % NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP); - return { ...msg, checkpointNumber, index }; + return { ...msg, checkpointNumber }; }, }); } diff --git a/yarn-project/aztec-node/src/aztec-node/node_public_calls_simulator.test.ts b/yarn-project/aztec-node/src/aztec-node/node_public_calls_simulator.test.ts index 7fbfa0bbdcb3..3b42be8a113a 100644 --- a/yarn-project/aztec-node/src/aztec-node/node_public_calls_simulator.test.ts +++ b/yarn-project/aztec-node/src/aztec-node/node_public_calls_simulator.test.ts @@ -438,6 +438,7 @@ function makeProposedCheckpointData(args: { feeAssetPriceModifier: 7n, archive: new AppendOnlyTreeSnapshot(args.archiveRoot ?? Fr.ZERO, 0), checkpointOutHash: Fr.fromString('0xfeed'), + inboxMsgTotal: 0n, }; } diff --git a/yarn-project/aztec-node/src/aztec-node/server.test.ts b/yarn-project/aztec-node/src/aztec-node/server.test.ts index af1e3ec2e105..a74fd09489fe 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.test.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.test.ts @@ -37,7 +37,6 @@ import type { ContractDataSource, ContractInstanceWithAddress } from '@aztec/std import { EmptyL1RollupConstants, type L1RollupConstants } from '@aztec/stdlib/epoch-helpers'; import { GasFees } from '@aztec/stdlib/gas'; import type { L2LogsSource, MerkleTreeReadOperations, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server'; -import { InboxLeaf } from '@aztec/stdlib/messaging'; import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging'; import { CheckpointHeader } from '@aztec/stdlib/rollup'; import { mockTx, randomContractInstanceWithAddress } from '@aztec/stdlib/testing'; @@ -1396,6 +1395,7 @@ describe('aztec node', () => { blockCount: 1, totalManaUsed: 0n, feeAssetPriceModifier: 0n, + inboxMsgTotal: 0n, }; } @@ -1569,13 +1569,18 @@ describe('aztec node', () => { }); describe('getL1ToL2MessageCheckpoint', () => { - it('returns the checkpoint for a message at index 0n', async () => { + it('returns the checkpoint of the block that consumed the message', async () => { const msg = Fr.random(); l1ToL2MessageSource.getL1ToL2MessageIndex.mockResolvedValue(0n); + // The first block (checkpoint 1) consumed message index 0: its L1-to-L2 tree leaf count is 1 > 0. + l2BlockSource.getBlockNumber.mockResolvedValue(BlockNumber(1)); + l2BlockSource.getBlockData.mockResolvedValue({ + checkpointNumber: CheckpointNumber(1), + header: { state: { l1ToL2MessageTree: { nextAvailableLeafIndex: 1 } } }, + } as unknown as BlockData); const result = await node.getL1ToL2MessageCheckpoint(msg); - expect(result).toEqual(InboxLeaf.checkpointNumberFromIndex(0n)); - expect(result).not.toBeUndefined(); + expect(result).toEqual(CheckpointNumber(1)); }); it('returns undefined when the message is not found', async () => { diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index 526557ca861c..b3a3ee8deb64 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -678,6 +678,10 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb return this.worldStateQueries.getL1ToL2MessageCheckpoint(l1ToL2Message); } + public getL1ToL2MessageIndex(l1ToL2Message: Fr): Promise { + return this.worldStateQueries.getL1ToL2MessageIndex(l1ToL2Message); + } + /** * Returns all the L2 to L1 messages in an epoch. * diff --git a/yarn-project/aztec-node/src/modules/node_world_state_queries.ts b/yarn-project/aztec-node/src/modules/node_world_state_queries.ts index 1e7f6dcd0a22..d7ed5e9ae89f 100644 --- a/yarn-project/aztec-node/src/modules/node_world_state_queries.ts +++ b/yarn-project/aztec-node/src/modules/node_world_state_queries.ts @@ -1,4 +1,9 @@ -import { ARCHIVE_HEIGHT, type L1_TO_L2_MSG_TREE_HEIGHT, type NOTE_HASH_TREE_HEIGHT } from '@aztec/constants'; +import { + ARCHIVE_HEIGHT, + INITIAL_L2_BLOCK_NUM, + type L1_TO_L2_MSG_TREE_HEIGHT, + type NOTE_HASH_TREE_HEIGHT, +} from '@aztec/constants'; import { BlockNumber, type CheckpointNumber, type EpochNumber } from '@aztec/foundation/branded-types'; import { chunkBy } from '@aztec/foundation/collection'; import { Fr } from '@aztec/foundation/curves/bn254'; @@ -7,6 +12,7 @@ import { sleep } from '@aztec/foundation/sleep'; import { MembershipWitness, type SiblingPath } from '@aztec/foundation/trees'; import type { AztecAddress } from '@aztec/stdlib/aztec-address'; import { + type BlockData, type BlockHash, type BlockParameter, type DataInBlock, @@ -16,7 +22,7 @@ import { } from '@aztec/stdlib/block'; import { computePublicDataTreeLeafSlot } from '@aztec/stdlib/hash'; import type { WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server'; -import { InboxLeaf, type L1ToL2MessageSource, type L2ToL1MembershipWitness } from '@aztec/stdlib/messaging'; +import type { L1ToL2MessageSource, L2ToL1MembershipWitness } from '@aztec/stdlib/messaging'; import { MerkleTreeId, type NullifierLeafPreimage, @@ -177,9 +183,41 @@ export class NodeWorldStateQueries { return [witness.index, witness.path]; } + public getL1ToL2MessageIndex(l1ToL2Message: Fr): Promise { + return this.l1ToL2MessageSource.getL1ToL2MessageIndex(l1ToL2Message); + } + public async getL1ToL2MessageCheckpoint(l1ToL2Message: Fr): Promise { const messageIndex = await this.l1ToL2MessageSource.getL1ToL2MessageIndex(l1ToL2Message); - return messageIndex !== undefined ? InboxLeaf.checkpointNumberFromIndex(messageIndex) : undefined; + if (messageIndex === undefined) { + return undefined; + } + // Post-flip, an L1-to-L2 message at compact leaf index `i` is consumed by the first block whose L1-to-L2 tree leaf + // count exceeds `i` (leaf counts are monotonic in block number); that block's checkpoint is the answer, sourced + // from the stored block records rather than 1024-per-checkpoint index arithmetic (AZIP-22 Fast Inbox). + const block = await this.#findBlockConsumingL1ToL2MessageIndex(messageIndex); + return block?.checkpointNumber; + } + + /** Binary-searches the block records for the first block whose L1-to-L2 tree leaf count exceeds `messageIndex`. */ + async #findBlockConsumingL1ToL2MessageIndex(messageIndex: bigint): Promise { + let lo: number = INITIAL_L2_BLOCK_NUM; + let hi: number = await this.blockSource.getBlockNumber(); + let result: BlockData | undefined; + while (lo <= hi) { + const mid = lo + Math.floor((hi - lo) / 2); + const block = await this.blockSource.getBlockData({ number: BlockNumber(mid) }); + if (block === undefined) { + break; + } + if (BigInt(block.header.state.l1ToL2MessageTree.nextAvailableLeafIndex) > messageIndex) { + result = block; + hi = mid - 1; + } else { + lo = mid + 1; + } + } + return result; } /** diff --git a/yarn-project/blob-lib/src/encoding/block_blob_data.test.ts b/yarn-project/blob-lib/src/encoding/block_blob_data.test.ts index cd291c70c778..c47c45d8a431 100644 --- a/yarn-project/blob-lib/src/encoding/block_blob_data.test.ts +++ b/yarn-project/blob-lib/src/encoding/block_blob_data.test.ts @@ -2,39 +2,14 @@ import { decodeBlockBlobData, encodeBlockBlobData } from './block_blob_data.js'; import { makeBlockBlobData } from './fixtures.js'; describe('block blob data', () => { - it('encode and decode first block', () => { - const isFirstBlock = true; - const numTxs = 3; - const blockBlobData = makeBlockBlobData({ isFirstBlock, numTxs }); + it('encodes and decodes a block carrying the l1-to-l2 message root', () => { + const blockBlobData = makeBlockBlobData({ numTxs: 3 }); expect(blockBlobData.txs.length).toBe(3); + // Every block carries the l1-to-l2 message tree root. expect(blockBlobData.l1ToL2MessageRoot).toBeDefined(); const encoded = encodeBlockBlobData(blockBlobData); - const decoded = decodeBlockBlobData(encoded, isFirstBlock); + const decoded = decodeBlockBlobData(encoded); expect(decoded).toEqual(blockBlobData); }); - - it('encode and decode second block', () => { - const isFirstBlock = false; - const numTxs = 3; - const blockBlobData = makeBlockBlobData({ isFirstBlock, numTxs }); - expect(blockBlobData.txs.length).toBe(3); - expect(blockBlobData.l1ToL2MessageRoot).toBeUndefined(); - - const encoded = encodeBlockBlobData(blockBlobData); - const decoded = decodeBlockBlobData(encoded, isFirstBlock); - expect(decoded).toEqual(blockBlobData); - }); - - it('does not include l1ToL2MessageRoot if not first block', () => { - const blockBlobData = makeBlockBlobData({ isFirstBlock: true, numTxs: 3 }); - const { l1ToL2MessageRoot, ...blockBlobDataWithoutL1ToL2MessageRoot } = blockBlobData; - // l1ToL2MessageRoot exists in the blob data. - expect(l1ToL2MessageRoot).toBeDefined(); - - const encoded = encodeBlockBlobData(blockBlobData); - // isFirstBlock is false. The l1ToL2MessageRoot should be ignored. - const decoded = decodeBlockBlobData(encoded, false); - expect(decoded).toEqual(blockBlobDataWithoutL1ToL2MessageRoot); - }); }); diff --git a/yarn-project/blob-lib/src/encoding/block_blob_data.ts b/yarn-project/blob-lib/src/encoding/block_blob_data.ts index 8d8d9d83ba60..fd5d6607a7df 100644 --- a/yarn-project/blob-lib/src/encoding/block_blob_data.ts +++ b/yarn-project/blob-lib/src/encoding/block_blob_data.ts @@ -17,16 +17,15 @@ import { type TxBlobData, decodeTxBlobData, encodeTxBlobData } from './tx_blob_d // Must match the implementation in `noir-protocol-circuits/crates/types/src/blob_data/block_blob_data.nr`. -export const NUM_BLOCK_END_BLOB_FIELDS = 6; -export const NUM_FIRST_BLOCK_END_BLOB_FIELDS = 7; +// Every block carries the L1-to-L2 message tree root: once any block can insert its own +// message bundle, the root is per-block, so blob-syncing nodes reconstruct each block's message-tree root from the +// blob alone. +export const NUM_BLOCK_END_BLOB_FIELDS = 7; export const NUM_CHECKPOINT_END_MARKER_FIELDS = 1; -/** - * Returns the number of blob fields used for block end data. - * @param isFirstBlockInCheckpoint - Whether this is the first block in a checkpoint. - */ -export function getNumBlockEndBlobFields(isFirstBlockInCheckpoint: boolean): number { - return isFirstBlockInCheckpoint ? NUM_FIRST_BLOCK_END_BLOB_FIELDS : NUM_BLOCK_END_BLOB_FIELDS; +/** Returns the number of blob fields used for block end data. */ +export function getNumBlockEndBlobFields(): number { + return NUM_BLOCK_END_BLOB_FIELDS; } export interface BlockEndBlobData { @@ -36,7 +35,7 @@ export interface BlockEndBlobData { noteHashRoot: Fr; nullifierRoot: Fr; publicDataRoot: Fr; - l1ToL2MessageRoot: Fr | undefined; + l1ToL2MessageRoot: Fr; } export interface BlockBlobData extends BlockEndBlobData { @@ -51,14 +50,14 @@ export function encodeBlockEndBlobData(blockEndBlobData: BlockEndBlobData): Fr[] blockEndBlobData.noteHashRoot, blockEndBlobData.nullifierRoot, blockEndBlobData.publicDataRoot, - ...(blockEndBlobData.l1ToL2MessageRoot ? [blockEndBlobData.l1ToL2MessageRoot] : []), + blockEndBlobData.l1ToL2MessageRoot, ]; } -export function decodeBlockEndBlobData(fields: Fr[] | FieldReader, isFirstBlock: boolean): BlockEndBlobData { +export function decodeBlockEndBlobData(fields: Fr[] | FieldReader): BlockEndBlobData { const reader = FieldReader.asReader(fields); - const numBlockEndData = getNumBlockEndBlobFields(isFirstBlock); + const numBlockEndData = getNumBlockEndBlobFields(); if (numBlockEndData > reader.remainingFields()) { throw new BlobDeserializationError( `Incorrect encoding of blob fields: not enough fields for block end data. Expected ${numBlockEndData} fields, only ${reader.remainingFields()} remaining.`, @@ -72,7 +71,7 @@ export function decodeBlockEndBlobData(fields: Fr[] | FieldReader, isFirstBlock: noteHashRoot: reader.readField(), nullifierRoot: reader.readField(), publicDataRoot: reader.readField(), - l1ToL2MessageRoot: isFirstBlock ? reader.readField() : undefined, + l1ToL2MessageRoot: reader.readField(), }; } @@ -80,7 +79,7 @@ export function encodeBlockBlobData(blockBlobData: BlockBlobData): Fr[] { return [...blockBlobData.txs.map(tx => encodeTxBlobData(tx)).flat(), ...encodeBlockEndBlobData(blockBlobData)]; } -export function decodeBlockBlobData(fields: Fr[] | FieldReader, isFirstBlock: boolean): BlockBlobData { +export function decodeBlockBlobData(fields: Fr[] | FieldReader): BlockBlobData { const reader = FieldReader.asReader(fields); const txs: TxBlobData[] = []; @@ -98,7 +97,7 @@ export function decodeBlockBlobData(fields: Fr[] | FieldReader, isFirstBlock: bo } } - const blockEndBlobData = decodeBlockEndBlobData(reader, isFirstBlock); + const blockEndBlobData = decodeBlockEndBlobData(reader); const blockEndMarker = blockEndBlobData.blockEndMarker; if (blockEndMarker.numTxs !== txs.length) { diff --git a/yarn-project/blob-lib/src/encoding/checkpoint_blob_data.ts b/yarn-project/blob-lib/src/encoding/checkpoint_blob_data.ts index ad786affd9db..aff4ac2b1588 100644 --- a/yarn-project/blob-lib/src/encoding/checkpoint_blob_data.ts +++ b/yarn-project/blob-lib/src/encoding/checkpoint_blob_data.ts @@ -6,7 +6,6 @@ import { type BlockBlobData, NUM_BLOCK_END_BLOB_FIELDS, NUM_CHECKPOINT_END_MARKER_FIELDS, - NUM_FIRST_BLOCK_END_BLOB_FIELDS, decodeBlockBlobData, encodeBlockBlobData, } from './block_blob_data.js'; @@ -46,7 +45,7 @@ export function decodeCheckpointBlobData(fields: Fr[] | FieldReader): Checkpoint const blocks = []; let checkpointEndMarker: CheckpointEndMarker | undefined; while (!reader.isFinished() && !checkpointEndMarker) { - blocks.push(decodeBlockBlobData(reader, blocks.length === 0 /* isFirstBlock */)); + blocks.push(decodeBlockBlobData(reader)); // After reading a block, the next item must be either a checkpoint end marker or another block. // The first field of a block is always a tx start marker. So if the provided fields are valid, it's not possible to @@ -94,8 +93,7 @@ export function getTotalNumBlobFieldsFromTxs(txsPerBlock: TxStartMarker[][]): nu } return ( - (numBlocks ? NUM_FIRST_BLOCK_END_BLOB_FIELDS - NUM_BLOCK_END_BLOB_FIELDS : 0) + // l1ToL2Messages root in the first block - numBlocks * NUM_BLOCK_END_BLOB_FIELDS + // 6 fields for each block end blob data. + numBlocks * NUM_BLOCK_END_BLOB_FIELDS + // block-end fields for each block (includes the per-block l1-to-l2 root) txsPerBlock.reduce((total, txs) => total + txs.reduce((total, tx) => total + tx.numBlobFields, 0), 0) + NUM_CHECKPOINT_END_MARKER_FIELDS // checkpoint end marker ); diff --git a/yarn-project/blob-lib/src/encoding/fixtures.ts b/yarn-project/blob-lib/src/encoding/fixtures.ts index ab146d5c5dea..c416c0e32d6a 100644 --- a/yarn-project/blob-lib/src/encoding/fixtures.ts +++ b/yarn-project/blob-lib/src/encoding/fixtures.ts @@ -131,12 +131,9 @@ export function makeBlockEndStateField({ } export function makeBlockEndBlobData({ - isFirstBlock = true, seed = 1, ...overrides -}: { seed?: number; isFirstBlock?: boolean } & Partial< - Omit -> & { +}: { seed?: number } & Partial> & { blockEndMarker?: Partial; blockEndStateField?: Partial; } = {}): BlockEndBlobData { @@ -152,18 +149,18 @@ export function makeBlockEndBlobData({ noteHashRoot: fr(seed + 0x300), nullifierRoot: fr(seed + 0x400), publicDataRoot: fr(seed + 0x500), - l1ToL2MessageRoot: isFirstBlock ? fr(seed + 0x600) : undefined, + // Every block carries the l1-to-l2 message tree root. + l1ToL2MessageRoot: fr(seed + 0x600), ...blockEndBlobDataOverrides, }; } export function makeBlockBlobData({ numTxs = 1, - isFirstBlock = true, isFullTx = false, seed = 1, ...overrides -}: { numTxs?: number; isFirstBlock?: boolean; isFullTx?: boolean; seed?: number } & Partial< +}: { numTxs?: number; isFullTx?: boolean; seed?: number } & Partial< Parameters[0] > = {}): BlockBlobData { return { @@ -173,7 +170,6 @@ export function makeBlockBlobData({ blockEndMarker: { numTxs, }, - isFirstBlock, ...overrides, }), }; @@ -193,11 +189,7 @@ export function makeCheckpointBlobData({ } & Partial = {}): CheckpointBlobData { const blocks = overrides.blocks ?? - makeTuple( - numBlocks, - i => makeBlockBlobData({ numTxs: numTxsPerBlock, isFirstBlock: i === seed, isFullTx, seed: seed + i * 0x1000 }), - seed, - ); + makeTuple(numBlocks, i => makeBlockBlobData({ numTxs: numTxsPerBlock, isFullTx, seed: seed + i * 0x1000 }), seed); const numBlobFields = overrides.checkpointEndMarker?.numBlobFields ?? diff --git a/yarn-project/end-to-end/src/shared/wait_for_l1_to_l2_message.ts b/yarn-project/end-to-end/src/shared/wait_for_l1_to_l2_message.ts index c8cc9ac455b5..a17774d3cd23 100644 --- a/yarn-project/end-to-end/src/shared/wait_for_l1_to_l2_message.ts +++ b/yarn-project/end-to-end/src/shared/wait_for_l1_to_l2_message.ts @@ -10,12 +10,12 @@ import { retryUntil } from '@aztec/foundation/retry'; * blocks afterwards to make the message consumable. */ export function waitForL1ToL2MessageSeen( - node: Pick, + node: Pick, l1ToL2MessageHash: Fr, opts: { timeoutSeconds: number }, ) { return retryUntil( - async () => (await node.getL1ToL2MessageCheckpoint(l1ToL2MessageHash)) !== undefined, + async () => (await node.getL1ToL2MessageIndex(l1ToL2MessageHash)) !== undefined, `L1 to L2 message ${l1ToL2MessageHash.toString()} seen`, opts.timeoutSeconds, 0.25, diff --git a/yarn-project/end-to-end/src/single-node/cross-chain/cross_chain_messaging_test.ts b/yarn-project/end-to-end/src/single-node/cross-chain/cross_chain_messaging_test.ts index ed5a141d3247..73dee52ccf8c 100644 --- a/yarn-project/end-to-end/src/single-node/cross-chain/cross_chain_messaging_test.ts +++ b/yarn-project/end-to-end/src/single-node/cross-chain/cross_chain_messaging_test.ts @@ -21,6 +21,7 @@ import { TestERC20Abi, TestERC20Bytecode, TokenPortalAbi, TokenPortalBytecode } import { TokenContract } from '@aztec/noir-contracts.js/Token'; import { TokenBridgeContract } from '@aztec/noir-contracts.js/TokenBridge'; import type { PXEConfig } from '@aztec/pxe/server'; +import type { BlockTag } from '@aztec/stdlib/block'; import { getEpochAtSlot } from '@aztec/stdlib/epoch-helpers'; import type { AztecNodeAdmin } from '@aztec/stdlib/interfaces/client'; @@ -109,6 +110,17 @@ export class CrossChainMessagingTest extends SingleNodeTestContext { this.requireEpochProven = opts.startProverNode ?? false; } + /** + * Chain tip the PXE syncs to. L1→L2 message readiness must be evaluated against this tip rather than + * the default `'latest'`: with per-block inbox insertion the proposed chain reaches a message's + * checkpoint before that checkpoint is published, so a `'latest'` readiness check flips true while a + * PXE anchored to `'checkpointed'` (or `'proven'`) has not yet synced the block the consuming tx will + * anchor to. Defaults to `'latest'` so suites that do not pin a sync tip keep their semantics. + */ + get pxeSyncChainTip(): BlockTag { + return this.pxeOpts.syncChainTip ?? 'latest'; + } + override async setup(opts: SingleNodeTestOpts = {}, pxeOpts: Partial = {}) { this.logger.info('Setting up cross chain messaging test'); // Recompute requireEpochProven from the merged options so per-call startProverNode is honored. diff --git a/yarn-project/end-to-end/src/single-node/cross-chain/l1_to_l2_inbox_drift.test.ts b/yarn-project/end-to-end/src/single-node/cross-chain/l1_to_l2_inbox_drift.test.ts index a7164ada4d0a..f936cb180be8 100644 --- a/yarn-project/end-to-end/src/single-node/cross-chain/l1_to_l2_inbox_drift.test.ts +++ b/yarn-project/end-to-end/src/single-node/cross-chain/l1_to_l2_inbox_drift.test.ts @@ -3,7 +3,6 @@ import { generateClaimSecret } from '@aztec/aztec.js/ethereum'; import { Fr } from '@aztec/aztec.js/fields'; import type { Logger } from '@aztec/aztec.js/log'; import type { AztecNode } from '@aztec/aztec.js/node'; -import { TxExecutionResult } from '@aztec/aztec.js/tx'; import type { Wallet } from '@aztec/aztec.js/wallet'; import type { BlockNumber } from '@aztec/foundation/branded-types'; import { timesAsync } from '@aztec/foundation/collection'; @@ -19,11 +18,13 @@ import { type L1ToL2MessageScope, createL1ToL2MessageHelpers } from './message_t jest.setTimeout(300_000); -// L1→L2 messaging via Inbox: inbox checkpoint drift after a rollup reorg. Uses CrossChainMessagingTest +// L1→L2 messaging via Inbox: an L1→L2 message survives a rollup prune. Uses CrossChainMessagingTest // (prod sequencer, pipelining preset: ethSlot=4s, aztecSlot=12s, inboxLag=2, minTxsPerBlock=1, -// aztecProofSubmissionEpochs=2, aztecEpochDuration=4) with EpochTestSettler for auto-proving and -// CrossChainTestHarness for L1↔L2 token portal bridging. The drift scenario runs over private and -// public scope via it.each, all sharing one node stood up once in beforeAll. +// aztecProofSubmissionEpochs=2, aztecEpochDuration=4) with EpochTestSettler for auto-proving. Messages +// carry a compact L1-assigned index and stream in by insertion order rather than +// pinning to a checkpoint, so a message inserted while the proposed chain drifts must still be +// re-consumed with a stable index after the chain prunes back. Runs over private and public scope via +// it.each, sharing one node stood up once in beforeAll. describe('single-node/cross-chain/l1_to_l2_inbox_drift', () => { let t: CrossChainMessagingTest; @@ -35,7 +36,6 @@ describe('single-node/cross-chain/l1_to_l2_inbox_drift', () => { let sendMessageToL2: ReturnType['sendMessageToL2']; let advanceBlock: ReturnType['advanceBlock']; - let waitForMessageFetched: ReturnType['waitForMessageFetched']; let waitForMessageReady: ReturnType['waitForMessageReady']; // Whether explicit mark-as-proven calls are honored. The inbox-drift scenario flips this to @@ -73,7 +73,7 @@ describe('single-node/cross-chain/l1_to_l2_inbox_drift', () => { ({ logger: log, wallet, user1Address, aztecNode } = t); ({ contract: testContract } = await TestContract.deploy(wallet).send({ from: user1Address })); - ({ sendMessageToL2, advanceBlock, waitForMessageFetched, waitForMessageReady } = createL1ToL2MessageHelpers({ + ({ sendMessageToL2, advanceBlock, waitForMessageReady } = createL1ToL2MessageHelpers({ t, aztecNode, wallet, @@ -127,108 +127,100 @@ describe('single-node/cross-chain/l1_to_l2_inbox_drift', () => { } }; - // Inbox checkpoint number can drift on two scenarios: if the rollup reorgs and rolls back its own - // checkpoint number, or if the inbox receives too many messages and they are inserted faster than - // they are consumed. In this test, we mine several checkpoints without marking them as proven until - // we can trigger a reorg, and then wait until the message can be processed to consume it. - const canConsumeMessageAfterInboxDrift = async (scope: L1ToL2MessageScope) => { + // L1→L2 messages carry a compact, L1-assigned global leaf index and are streamed + // into the L2 tree in insertion order (subject to inbox lag and per-block/checkpoint caps) rather than + // being pinned to a fixed checkpoint. This scenario stresses that message state survives an L2 reorg: + // we let the proposed chain drift by mining several unproven checkpoints, insert a message during the + // drift, then force the rollup to prune back to the pre-drift block. After the prune, the message must + // still be re-consumed on the new chain and its witness leaf index must remain the L1-assigned index. + const canConsumeMessageAfterRollupPrune = async (scope: L1ToL2MessageScope) => { // Stop the background epoch test settler so the drift scenario below can proceed without // an auto-prover racing it. await t.epochTestSettler?.stop(); // Reset the L1 proof window by marking the current pending tip as proven, so L1's prune - // deadline doesn't fire mid-test before we finish mining the 4 drift checkpoints below. + // deadline doesn't fire mid-test before we finish mining the drift checkpoints below. await markAsProven(); - // Stop proving + // Snapshot the block we will prune back to, then stop proving so the proposed chain can drift. const lastProven = await aztecNode.getBlockNumber(); - const [checkpointedProvenBlock] = await aztecNode.getBlocks(lastProven, 1, { - includeL1PublishInfo: true, - includeAttestations: true, - onlyCheckpointed: true, - }); - log.warn(`Stopping proof submission at checkpoint ${checkpointedProvenBlock.checkpointNumber} to allow drift`); + log.warn(`Stopping proof submission at block ${lastProven} to allow drift`); markProvenEnabled = false; // Mine several checkpoints to ensure drift log.warn(`Mining blocks to allow drift`); await timesAsync(4, advanceCheckpoint); - // Generate and send the message to the L1 contract + // Generate and send the message to the L1 contract during the drift log.warn(`Sending L1 to L2 message`); const [secret, secretHash] = await generateClaimSecret(); const message = { recipient: testContract.address, content: Fr.random(), secretHash }; const { msgHash, globalLeafIndex } = await sendMessageToL2(message); - // Wait until the Aztec node has synced it - const msgCheckpointNumber = await waitForMessageFetched(msgHash); - log.warn(`Message synced for checkpoint ${msgCheckpointNumber}`); - expect(checkpointedProvenBlock.checkpointNumber + 4).toBeLessThan(msgCheckpointNumber); - - // And keep mining until we prune back to the original block number. Now the "waiting for two blocks" - // strategy for the message to be ready to use shouldn't work, since the lastProven block is more than - // two blocks behind the message block. This is the scenario we want to test. + // The drift's current L1 pending checkpoint. The node prunes its local view as soon as the proof + // window lapses, but L1 only commits the prune when the rebuilt chain proposes its first checkpoint + // on top of the pre-drift block. Wait for that on-chain commit — L1's pending tip dropping back + // below the drift — before re-enabling proving: marking proven while the drift is still the pending + // tip would pin the drift proven and wedge the rebuild forever with Rollup__InvalidArchive. + const driftPendingCheckpoint = (await t.cheatCodes.rollup.getTips()).pending; + + // Keep mining until the rollup prunes the drifted proposed chain back to the pre-drift block. L1's + // pending tip dropping below the drift is the unambiguous signal that the on-chain prune committed + // (a new checkpoint was proposed on top of the pre-drift block); the node's faster local prune is + // not, which is why we gate on L1 here rather than on the node's block number. log.warn(`Waiting until we prune back to ${lastProven}`); await retryUntil( - async () => - (await aztecNode.getBlockNumber().then(b => b === lastProven || b === lastProven + 1)) || - (await tryAdvanceBlock()), + async () => { + await tryAdvanceBlock(); + return (await t.cheatCodes.rollup.getTips()).pending < driftPendingCheckpoint; + }, 'wait for prune', 180, ); - // The drift condition has been established. Re-enable explicit proving so the catch-up blocks - // below are not pruned a second time before the message checkpoint becomes ready. + // The prune has committed on L1. Re-enable explicit proving so the catch-up chain that re-consumes + // the message is not pruned a second time before we can consume it — safe now that L1's pending tip + // is the rebuilt chain rather than the drift. markProvenEnabled = true; await markAsProven(); - // Check that there is no witness yet - expect(await aztecNode.getL1ToL2MessageMembershipWitness('latest', msgHash)).toBeUndefined(); - - // Define L2 function to consume the message - const consume = () => getConsumeMethod(scope)(message.content, secret, t.ethAccount, globalLeafIndex); + // The invariant under test: the compact-indexed message survives the L2 prune and is re-consumed on the + // new chain within a reasonable window (its bucket and rolling-hash state must persist across the + // reorg), becoming consumable from the requested scope. + await waitForMessageReady(msgHash, scope); - // Wait until the message is ready to be consumed, checking that it cannot be consumed beforehand - await waitForMessageReady(msgHash, scope, async () => { - if (scope === 'private') { - // On private, we simulate the tx locally and check that we get a missing message error, then we advance to the next block - await expect(() => consume().simulate({ from: user1Address })).rejects.toThrow(/No L1 to L2 message found/); - await tryAdvanceBlock(); - } else { - // In public it is harder to determine when a message becomes consumable. - // We send a transaction, this advances the chain and the message MIGHT be consumed in the new block. - // If it does get consumed then we check that the block contains the message. - // If it fails we check that the block doesn't contain the message - const { receipt } = await consume().send({ from: user1Address, wait: { dontThrowOnRevert: true } }); - if (receipt.executionResult === TxExecutionResult.SUCCESS) { - // The consume tx must not succeed before the message checkpoint. It can land in a later - // checkpoint if the node catches up between the readiness poll and the tx being built. - const block = await aztecNode.getBlock(receipt.blockNumber!); - expect(block).toBeDefined(); - expect(block!.checkpointNumber).toBeGreaterThanOrEqual(msgCheckpointNumber); - } else { - expect(receipt.executionResult).toEqual(TxExecutionResult.REVERTED); + // The PXE is anchored to the checkpointed tip (syncChainTip: 'checkpointed'), so the re-consumed + // message only becomes visible to the consume simulation once its block is checkpointed — not merely + // proposed at 'latest'. Drive the rebuilt chain forward (advanceBlock marks each step proven so it + // is not pruned again) until the checkpointed tip covers the block that re-consumed the message. + const messageBlock = await aztecNode.getBlockNumber(); + await retryUntil( + async () => { + if ((await aztecNode.getBlockNumber('checkpointed')) >= messageBlock) { + return true; } - } - await markAsProven(); - }); + await tryAdvanceBlock(); + return false; + }, + 'wait for message block to be checkpointed', + 180, + ); - // Verify the membership witness is available for creating the tx (private-land only) - if (scope === 'private') { - const [messageIndex] = (await aztecNode.getL1ToL2MessageMembershipWitness('latest', msgHash))!; - expect(messageIndex).toEqual(globalLeafIndex.toBigInt()); - // And consume the message for private, public was already consumed. - await consume().send({ from: user1Address }); - } + // The witness leaf index is the L1-assigned compact global index and must be stable across the prune. + const [messageIndex] = (await aztecNode.getL1ToL2MessageMembershipWitness('latest', msgHash))!; + expect(messageIndex).toEqual(globalLeafIndex.toBigInt()); + + // The message is consumable on L2 from the requested scope. + const consume = () => getConsumeMethod(scope)(message.content, secret, t.ethAccount, globalLeafIndex); + await consume().send({ from: user1Address }); }; - // Mines four checkpoints without proving, inserting an L1→L2 message after the drift, then - // triggers a rollup prune back to the pre-drift block. Verifies the message can be consumed only - // after the chain re-syncs to the message's checkpoint, not before, from both private and public - // scope (public uses a send+dontThrowOnRevert loop to probe when the message becomes consumable). + // Mines several unproven checkpoints, inserts an L1→L2 message during the drift, then triggers a + // rollup prune back to the pre-drift block and verifies the message is still re-consumed on the new + // chain — with its L1-assigned witness index intact — from both private and public scope. it.each(['private', 'public'] as const)( - 'can consume L1 to L2 message in %s after inbox drifts away from the rollup', + 'consumes an L1 to L2 message after a rollup prune drops the drifted chain (%s)', async scope => { - await canConsumeMessageAfterInboxDrift(scope); + await canConsumeMessageAfterRollupPrune(scope); }, ); }); diff --git a/yarn-project/end-to-end/src/single-node/cross-chain/message_test_helpers.ts b/yarn-project/end-to-end/src/single-node/cross-chain/message_test_helpers.ts index 4e520ed7002a..afacbaf22788 100644 --- a/yarn-project/end-to-end/src/single-node/cross-chain/message_test_helpers.ts +++ b/yarn-project/end-to-end/src/single-node/cross-chain/message_test_helpers.ts @@ -107,7 +107,7 @@ export function createL1ToL2MessageHelpers(deps: L1ToL2MessageHelperDeps): L1ToL aztecNode.getCheckpointNumber(), ]); const witness = await aztecNode.getL1ToL2MessageMembershipWitness('latest', msgHash); - const isReady = await isL1ToL2MessageReady(aztecNode, msgHash); + const isReady = await isL1ToL2MessageReady(aztecNode, msgHash, t.pxeSyncChainTip); log.info( `Block is ${blockNumber}, checkpoint is ${checkpointNumber}. Message checkpoint is ${msgCheckpoint}. Witness ${!!witness}. Ready ${isReady}.`, ); diff --git a/yarn-project/end-to-end/src/single-node/cross-chain/streaming_inbox.test.ts b/yarn-project/end-to-end/src/single-node/cross-chain/streaming_inbox.test.ts new file mode 100644 index 000000000000..1764ceb01b72 --- /dev/null +++ b/yarn-project/end-to-end/src/single-node/cross-chain/streaming_inbox.test.ts @@ -0,0 +1,328 @@ +import type { AztecAddress } from '@aztec/aztec.js/addresses'; +import { generateClaimSecret } from '@aztec/aztec.js/ethereum'; +import { Fr } from '@aztec/aztec.js/fields'; +import type { Logger } from '@aztec/aztec.js/log'; +import type { AztecNode } from '@aztec/aztec.js/node'; +import { TxExecutionResult } from '@aztec/aztec.js/tx'; +import type { Wallet } from '@aztec/aztec.js/wallet'; +import { INBOX_LAG_SECONDS } from '@aztec/constants'; +import { BlockNumber, SlotNumber } from '@aztec/foundation/branded-types'; +import { retryUntil } from '@aztec/foundation/retry'; +import { TestContract } from '@aztec/noir-test-contracts.js/Test'; +import { getSlotAtTimestamp, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers'; + +import { jest } from '@jest/globals'; + +import { L1_DIRECT_WRITE_ACCOUNT_INDEX, PIPELINING_SETUP_OPTS } from '../../fixtures/fixtures.js'; +import { CrossChainMessagingTest } from './cross_chain_messaging_test.js'; +import { createL1ToL2MessageHelpers } from './message_test_helpers.js'; + +jest.setTimeout(600_000); + +// Streaming Inbox e2e coverage the legacy per-checkpoint suite could not express: when every L1->L2 message +// entered at the first block of the *next* checkpoint, mid-checkpoint inclusion, message-only blocks, and +// per-block streaming latency had no observable surface. Runs the production +// pipelining sequencer via CrossChainMessagingTest with a widened slot (36s / 6s blocks -> up to ~4 blocks +// per checkpoint) so a message can become lag-eligible partway through a checkpoint and land in a non-first +// block. minTxsPerBlock=0 lets a checkpoint carry a zero-tx block whose only content is a streaming bundle. +// +// Grounded on l1_to_l2.test.ts (send/wait helpers, TestContract arbitrary-sender consume) and +// cross_chain_public_message.test.ts (same-block public consume). All cases share one node stood up once. +describe('single-node/cross-chain/streaming_inbox', () => { + let t: CrossChainMessagingTest; + + let log: Logger; + let aztecNode: AztecNode; + let wallet: Wallet; + let user1Address: AztecAddress; + let testContract: TestContract; + + let sendMessageToL2: ReturnType['sendMessageToL2']; + let advanceBlock: ReturnType['advanceBlock']; + let waitForMessageReady: ReturnType['waitForMessageReady']; + + const markAsProven = () => t.cheatCodes.rollup.markAsProven(); + + beforeAll(async () => { + t = new CrossChainMessagingTest( + 'streaming_inbox', + // A 36s slot with 6s blocks yields up to ~4 blocks per checkpoint (the pipelining timing model gives + // maxBlocks = floor((36 - 0.5 - (0.5 + D)) / D) = 4 for D=6), which is what lets a message aged past + // INBOX_LAG_SECONDS land in a non-first block of the same checkpoint. minTxsPerBlock=0 permits a + // zero-tx message-only block (the FI-05 relaxation). + { ...PIPELINING_SETUP_OPTS, aztecSlotDuration: 36, blockDurationMs: 6000, minTxsPerBlock: 0 }, + { aztecProofSubmissionEpochs: 2, aztecEpochDuration: 4 }, + { syncChainTip: 'checkpointed' }, + // Pass arbitrary L1->L2 messages straight to a TestContract; no token bridge needed. + { l1HarnessAccountIndex: L1_DIRECT_WRITE_ACCOUNT_INDEX, deployTokenBridge: false }, + ); + await t.setup(); + + ({ logger: log, wallet, user1Address, aztecNode } = t); + ({ contract: testContract } = await TestContract.deploy(wallet).send({ from: user1Address })); + + ({ sendMessageToL2, advanceBlock, waitForMessageReady } = createL1ToL2MessageHelpers({ + t, + aztecNode, + wallet, + user1Address, + log, + markAsProven, + })); + }, 600_000); + + afterAll(async () => { + await t.teardown(); + }); + + /** The L1 block timestamp at which an L1->L2 message was inserted; equals the message's Inbox bucket key. */ + const getMessageL1Timestamp = async (l1BlockNumber: bigint): Promise => { + const block = await t.harnessL1Client.getBlock({ blockNumber: l1BlockNumber }); + return block.timestamp; + }; + + /** + * Finds the L2 block that inserted `msgHash` into the L1-to-L2 message tree by scanning forward from + * `fromBlock` for the first block whose committed tree resolves a membership witness. Under the streaming + * Inbox a message enters the tree at the block that consumes its Inbox bucket, which need not be the first + * block of a checkpoint. Returns the block-data (checkpoint number + index within checkpoint) of that block. + */ + const findInsertingBlock = (msgHash: Fr, fromBlock: BlockNumber) => { + return retryUntil( + async () => { + const tip = await aztecNode.getBlockNumber(); + for (let n = fromBlock; n <= tip; n = BlockNumber(n + 1)) { + const witness = await aztecNode.getL1ToL2MessageMembershipWitness(n, msgHash); + if (witness !== undefined) { + const data = await aztecNode.getBlockData(n); + return { blockNumber: n, checkpointNumber: data!.checkpointNumber, index: data!.indexWithinCheckpoint }; + } + } + return undefined; + }, + `find block inserting message ${msgHash.toString()}`, + 240, + 0.5, + ); + }; + + /** + * Runs `fn` while a background loop feeds empty txs, so checkpoints build multiple blocks promptly rather + * than stalling on an empty pool. advanceBlock also refreshes the L1 proof window, keeping the chain from + * pruning mid-test. Callers must pass a no-op `onNotReady` to any readiness wait so it does not send its own + * wallet txs concurrently (which would race the feeder on the wallet nonce). + */ + const withBackgroundFeeder = async (fn: () => Promise): Promise => { + let feeding = true; + const feeder = (async () => { + while (feeding) { + try { + await advanceBlock(); + } catch (err) { + log.warn(`Feeder tx failed: ${(err as Error).message}`); + } + } + })(); + try { + return await fn(); + } finally { + feeding = false; + await feeder; + } + }; + + // Test 1 (mid-checkpoint inclusion): a message sent mid-checkpoint becomes available in a *later* block of + // the same checkpoint (indexWithinCheckpoint > 0), which the legacy first-block-of-next-checkpoint flow + // could never produce. Feeds a steady tx stream so checkpoints fill to multiple blocks, times the send so + // the message ages past INBOX_LAG_SECONDS partway through a checkpoint's build, then locates the inserting + // block. Retries with fresh messages so a message that happens to age exactly at a checkpoint boundary (and + // lands at index 0) does not fail the run. + it('includes a message in a non-first block of a checkpoint (mid-checkpoint streaming)', async () => { + const { slotDuration } = t.constants; + + await withBackgroundFeeder(async () => { + let inserting: { blockNumber: BlockNumber; checkpointNumber: number; index: number } | undefined; + let insertedMsgHash: Fr | undefined; + + for (let attempt = 0; attempt < 4 && inserting === undefined; attempt++) { + // Aim the send so the message ages past the lag partway through a checkpoint's build window. The + // eligibility instant is T + INBOX_LAG_SECONDS; targeting it a few seconds into an upcoming build + // window lands it on a non-first block across the ~4-block checkpoint. The eligible window is wide + // (any block after the first whose build time exceeds T + lag), so exact timing is not required. + const nowTs = BigInt(await t.cheatCodes.eth.lastBlockTimestamp()); + const currentSlot = getSlotAtTimestamp(nowTs, t.constants); + const targetSlot = SlotNumber(Number(currentSlot) + 3); + const sendTargetTs = getTimestampForSlot(targetSlot, t.constants) - BigInt(INBOX_LAG_SECONDS) + 4n; + log.warn(`Attempt ${attempt}: waiting for L1 to reach ${sendTargetTs} before sending message`, { + currentSlot, + targetSlot, + }); + await retryUntil( + async () => BigInt(await t.cheatCodes.eth.lastBlockTimestamp()) >= sendTargetTs, + `L1 reaches ${sendTargetTs}`, + Number(slotDuration) * 6, + 0.2, + ); + + const blockAtSend = await aztecNode.getBlockNumber(); + const [, secretHash] = await generateClaimSecret(); + const message = { recipient: testContract.address, content: Fr.random(), secretHash }; + const { msgHash } = await sendMessageToL2(message); + log.warn(`Sent message ${msgHash.toString()} at block ${blockAtSend}`); + + // The background feeder drives block production; findInsertingBlock polls the committed tree without + // sending its own wallet txs (which would race the feeder on the nonce). + const found = await findInsertingBlock(msgHash, BlockNumber(blockAtSend + 1)); + log.warn(`Message ${msgHash.toString()} inserted at block ${found.blockNumber}`, { + checkpointNumber: found.checkpointNumber, + index: found.index, + }); + + if (found.index > 0) { + inserting = found; + insertedMsgHash = msgHash; + } else { + log.warn(`Message landed at index 0 (checkpoint boundary); retrying with a fresh message`); + } + } + + expect(inserting).toBeDefined(); + // A non-first block of its checkpoint carried the message: streaming placed it mid-checkpoint, which the + // legacy path (all messages at the first block of the next checkpoint) could never do. + expect(inserting!.index).toBeGreaterThan(0); + // The immediately preceding block did not yet have the message, confirming this block is the one that + // inserted it (rather than the message having been present since an earlier block of the checkpoint). + const priorWitness = await aztecNode.getL1ToL2MessageMembershipWitness( + BlockNumber(inserting!.blockNumber - 1), + insertedMsgHash!, + ); + expect(priorWitness).toBeUndefined(); + }); + }); + + // Test 2 (latency bound): the delay between a message's L1 inclusion and the L2 block that makes it + // available stays within the streaming bound. Asserted in slot-denominated terms (L1/L2 timestamps, not + // wall-clock): the including block's timestamp minus the message's L1 timestamp must be at most + // INBOX_LAG_SECONDS + 2 * slotDuration (lag + a full slot straddle + one slot of CI slack). No lower bound + // is asserted (eligibility is already enforced by L1 and the validator). The wall-clock latency is logged + // for information only. + it('makes a message available within the streaming latency bound', async () => { + const { slotDuration } = t.constants; + const maxDelaySeconds = BigInt(INBOX_LAG_SECONDS) + 2n * BigInt(slotDuration); + + await withBackgroundFeeder(async () => { + const blockAtSend = await aztecNode.getBlockNumber(); + const wallClockAtSend = Date.now(); + const [, secretHash] = await generateClaimSecret(); + const message = { recipient: testContract.address, content: Fr.random(), secretHash }; + const { msgHash, txReceipt } = await sendMessageToL2(message); + const messageL1Ts = await getMessageL1Timestamp(txReceipt.blockNumber!); + log.warn(`Sent message ${msgHash.toString()} with L1 timestamp ${messageL1Ts}`); + + // The background feeder drives block production; findInsertingBlock polls the committed tree without + // sending its own wallet txs (which would race the feeder on the nonce). + const inserting = await findInsertingBlock(msgHash, BlockNumber(blockAtSend + 1)); + const wallClockLatencyMs = Date.now() - wallClockAtSend; + const insertingBlock = (await aztecNode.getBlock(inserting.blockNumber))!; + const includingBlockTs = insertingBlock.header.globalVariables.timestamp; + const delaySeconds = includingBlockTs - messageL1Ts; + + // Informational only: the wall-clock number flakes under CI load, so it is never + // asserted on; the slot-denominated bound below is the real check. + log.warn(`Streaming latency for message ${msgHash.toString()}`, { + messageL1Ts, + includingBlockTs, + delaySeconds: Number(delaySeconds), + maxDelaySeconds: Number(maxDelaySeconds), + wallClockLatencyMs, + }); + + expect(delaySeconds).toBeGreaterThan(0n); + expect(delaySeconds).toBeLessThanOrEqual(maxDelaySeconds); + }); + }); + + // Test 3 (message-only block): on an empty tx pool, the block that consumes a message carries zero txs and a + // non-empty streaming bundle (the FI-05 shape exercised on a live chain), and the chain keeps proving past + // it. Drains the pool first, then sends a single message and asserts the inserting block has no tx effects. + it('produces a message-only block on an empty tx pool and keeps proving', async () => { + // Let the pool drain so the checkpoint that consumes the message is not padded with unrelated txs. + await retryUntil( + async () => !(await aztecNode.getPendingTxCount()), + 'tx pool drains', + Number(t.constants.slotDuration) * 3, + 0.5, + ); + + const blockAtSend = await aztecNode.getBlockNumber(); + const [, secretHash] = await generateClaimSecret(); + const message = { recipient: testContract.address, content: Fr.random(), secretHash }; + const { msgHash } = await sendMessageToL2(message); + log.warn(`Sent message ${msgHash.toString()} on an empty pool`); + + // Do not feed txs; the sequencer builds empty checkpoints until the message ages past the lag, at which + // point a zero-tx block consumes it. findInsertingBlock polls the committed tree without sending txs, so + // the pool stays empty and the block that consumes the message carries only the bundle. + const inserting = await findInsertingBlock(msgHash, BlockNumber(blockAtSend + 1)); + + const insertingBlock = (await aztecNode.getBlock(inserting.blockNumber, { includeTransactions: true }))!; + log.warn(`Message ${msgHash.toString()} inserted at block ${inserting.blockNumber}`, { + checkpointNumber: inserting.checkpointNumber, + index: inserting.index, + txCount: insertingBlock.body.txEffects.length, + }); + + // The inserting block carried the message with no txs: a message-only block. + expect(insertingBlock.body.txEffects.length).toBe(0); + // The membership witness resolving proves the block's bundle was non-empty (it inserted the message). + expect(await aztecNode.getL1ToL2MessageMembershipWitness('latest', msgHash)).toBeDefined(); + + // The chain keeps proving past the message-only block. + await markAsProven(); + await retryUntil( + async () => (await aztecNode.getBlockNumber('proven')) >= inserting.blockNumber, + `proven tip reaches block ${inserting.blockNumber}`, + Number(t.constants.slotDuration) * t.epochDuration * 3, + 1, + ); + expect(await aztecNode.getBlockNumber('proven')).toBeGreaterThanOrEqual(inserting.blockNumber); + }); + + // Test 4 (send-then-consume on the streaming path): a message inserted by the streaming Inbox is consumed by + // a public L2 tx, passing the compact leaf index, and cannot be consumed twice. Same-block consumption is + // available (a block's BlockConstantData.l1_to_l2_tree_snapshot pins to that block's post-bundle + // root, so the public/AVM read sees the just-inserted message), but which block consumes the message relative + // to its insertion depends on sequencer timing under the production sequencer; the block relationship is + // logged for visibility while the robust invariants asserted are the successful compact-index consume and the + // double-spend revert. Mirrors cross_chain_public_message.test.ts. + it('consumes a streaming-inserted message by compact index and rejects double-spend', async () => { + const l1Account = t.ethAccount; + const blockAtSend = await aztecNode.getBlockNumber(); + const [secret, secretHash] = await generateClaimSecret(); + const message = { recipient: testContract.address, content: Fr.random(), secretHash }; + const { msgHash, globalLeafIndex } = await sendMessageToL2(message); + log.warn(`Sent message ${msgHash.toString()} with compact index ${globalLeafIndex}`); + + await waitForMessageReady(msgHash, 'public'); + const inserting = await findInsertingBlock(msgHash, BlockNumber(blockAtSend + 1)); + + const { receipt: txReceipt } = await testContract.methods + .consume_message_from_arbitrary_sender_public(message.content, secret, l1Account, globalLeafIndex.toBigInt()) + .send({ from: user1Address }); + expect(txReceipt.blockNumber).toBeGreaterThan(0); + // The compact leaf index from the Inbox event resolves to the same message the node inserted. + const [resolvedIndex] = (await aztecNode.getL1ToL2MessageMembershipWitness('latest', msgHash))!; + expect(resolvedIndex).toBe(globalLeafIndex.toBigInt()); + log.warn(`Consumed message ${msgHash.toString()} in block ${txReceipt.blockNumber}`, { + insertingBlock: inserting.blockNumber, + consumeBlock: txReceipt.blockNumber, + sameBlock: Number(txReceipt.blockNumber) === Number(inserting.blockNumber), + }); + + // The message was inserted and consumed; a second consume must revert (the leaf is nullified). + const { receipt: failedReceipt } = await testContract.methods + .consume_message_from_arbitrary_sender_public(message.content, secret, l1Account, globalLeafIndex.toBigInt()) + .send({ from: user1Address, wait: { dontThrowOnRevert: true } }); + expect(failedReceipt.executionResult).toBe(TxExecutionResult.REVERTED); + }); +}); diff --git a/yarn-project/end-to-end/src/single-node/sync/synching.test.ts b/yarn-project/end-to-end/src/single-node/sync/synching.test.ts index 46e1f9214fb5..e2d29d9cb840 100644 --- a/yarn-project/end-to-end/src/single-node/sync/synching.test.ts +++ b/yarn-project/end-to-end/src/single-node/sync/synching.test.ts @@ -502,6 +502,7 @@ describe('single-node/sync/synching', () => { rollupAddress: deployL1ContractsValues.l1ContractAddresses.rollupAddress, }), Signature.empty(), + 0n, ); await cheatCodes.rollup.markAsProven(CheckpointNumber(provenThrough)); diff --git a/yarn-project/ethereum/src/contracts/chain_state_override.ts b/yarn-project/ethereum/src/contracts/chain_state_override.ts index 8693981098d0..b17d841b0683 100644 --- a/yarn-project/ethereum/src/contracts/chain_state_override.ts +++ b/yarn-project/ethereum/src/contracts/chain_state_override.ts @@ -21,6 +21,8 @@ export type PendingCheckpointOverrideState = { outHash?: Fr; payloadDigest?: Buffer32; slotNumber?: SlotNumber; + inboxMsgTotal?: bigint; + inboxConsumedBucket?: bigint; }; export type ChainTipsOverride = { @@ -93,11 +95,12 @@ export class SimulationOverridesBuilder { /** * Overrides one or more `tempCheckpointLogs` cell fields for the configured pending checkpoint. - * Fields are independent: any subset can be provided. The translator (`makeTempCheckpointLogOverride`) - * emits a stateDiff entry per field actually set, so unspecified fields stay at their on-chain - * values. + * Any subset can be provided. The translator (`makeTempCheckpointLogOverride`) emits a stateDiff + * entry per storage word touched, so fields in untouched words stay at their on-chain values; + * `slotNumber`, `inboxMsgTotal` and `inboxConsumedBucket` share a word, so setting any of them + * zeroes the others unless they are supplied too. * - * `slotNumber` is load-bearing for `STFLib.canPruneAtTime`: when the simulation overrides `pending` + * `slotNumber` is required for `STFLib.canPruneAtTime`: when the simulation overrides `pending` * to a checkpoint that has no on-chain `tempCheckpointLogs` entry yet, the missing slotNumber falls * back to 0 and the contract treats the pending tip as belonging to epoch 0, triggering a phantom * prune that silently undoes the `pending` override. @@ -107,6 +110,8 @@ export class SimulationOverridesBuilder { outHash?: Fr; payloadDigest?: Buffer32; slotNumber?: SlotNumber; + inboxMsgTotal?: bigint; + inboxConsumedBucket?: bigint; }): this { this.assertPendingCheckpointNumber(); this.pendingCheckpointState = { ...(this.pendingCheckpointState ?? {}), ...fields }; @@ -174,6 +179,8 @@ export async function buildSimulationOverridesStateOverride( outHash: plan.pendingCheckpointState.outHash, payloadDigest: plan.pendingCheckpointState.payloadDigest, slotNumber: plan.pendingCheckpointState.slotNumber, + inboxMsgTotal: plan.pendingCheckpointState.inboxMsgTotal, + inboxConsumedBucket: plan.pendingCheckpointState.inboxConsumedBucket, feeHeader: plan.pendingCheckpointState.feeHeader, }), ), diff --git a/yarn-project/ethereum/src/contracts/inbox.ts b/yarn-project/ethereum/src/contracts/inbox.ts index 23d7f6ce3df4..1d2a732ace8d 100644 --- a/yarn-project/ethereum/src/contracts/inbox.ts +++ b/yarn-project/ethereum/src/contracts/inbox.ts @@ -1,3 +1,4 @@ +import { asyncPool } from '@aztec/foundation/async-pool'; import { maxBigint } from '@aztec/foundation/bigint'; import { CheckpointNumber } from '@aztec/foundation/branded-types'; import { Buffer16, Buffer32 } from '@aztec/foundation/buffer'; @@ -26,8 +27,11 @@ export type MessageSentArgs = { bucketSeq: bigint; }; -/** Log type for MessageSent events. */ -export type MessageSentLog = L1EventLog; +/** Log type for MessageSent events, enriched with the emitting L1 block's timestamp (the bucket recency key). */ +export type MessageSentLog = L1EventLog & { + /** Timestamp (in seconds) of the L1 block that emitted the event; the key of the message's Inbox bucket. */ + l1BlockTimestamp: bigint; +}; export class InboxContract { private readonly inbox: GetContractReturnType; @@ -81,10 +85,11 @@ export class InboxContract { /** Fetches MessageSent events within the given block range. */ async getMessageSentEvents(fromBlock: bigint, toBlock: bigint): Promise { - const logs = await this.inbox.getEvents.MessageSent({}, { fromBlock, toBlock }); - return logs - .filter(log => log.blockNumber! >= fromBlock && log.blockNumber! <= toBlock) - .map(log => this.mapMessageSentLog(log)); + const logs = (await this.inbox.getEvents.MessageSent({}, { fromBlock, toBlock })).filter( + log => log.blockNumber! >= fromBlock && log.blockNumber! <= toBlock, + ); + const timestamps = await this.getBlockTimestamps(logs.map(log => log.blockHash!)); + return logs.map(log => this.mapMessageSentLog(log, timestamps.get(log.blockHash!)!)); } /** Fetches MessageSent events for a specific message hash around a specific block. */ @@ -97,26 +102,52 @@ export class InboxContract { { hash: msgHash }, { fromBlock: maxBigint(aroundL1BlockNumber - 5n, 1n), toBlock: aroundL1BlockNumber + 5n }, ); - return log && this.mapMessageSentLog(log); + if (!log) { + return log as unknown as MessageSentLog; + } + const [timestamp] = (await this.getBlockTimestamps([log.blockHash!])).values(); + return this.mapMessageSentLog(log, timestamp); } - private mapMessageSentLog(log: { - blockNumber: bigint | null; - blockHash: `0x${string}` | null; - transactionHash: `0x${string}` | null; - args: { - index?: bigint; - hash?: `0x${string}`; - checkpointNumber?: bigint; - rollingHash?: `0x${string}`; - inboxRollingHash?: `0x${string}`; - bucketSeq?: bigint; - }; - }): MessageSentLog { + /** + * Fetches the timestamp of each distinct L1 block, so each MessageSent log can carry its bucket key. Blocks are + * resolved by hash, which pins them to the same fork the logs were read from: resolving by number would silently + * return the same-height block of another fork if the chain reorgs between the log query and this lookup, storing a + * timestamp that never applied to the message. By hash, such a reorg fails the lookup instead, and the caller + * retries against the reorged chain. Fetched with bounded concurrency to keep a large sync batch from fanning out + * unbounded RPC requests. + */ + private async getBlockTimestamps(blockHashes: Hex[]): Promise> { + const uniqueBlockHashes = [...new Set(blockHashes)]; + const timestamps = new Map(); + await asyncPool(10, uniqueBlockHashes, async blockHash => { + const block = await this.client.getBlock({ blockHash, includeTransactions: false }); + timestamps.set(blockHash, block.timestamp); + }); + return timestamps; + } + + private mapMessageSentLog( + log: { + blockNumber: bigint | null; + blockHash: `0x${string}` | null; + transactionHash: `0x${string}` | null; + args: { + index?: bigint; + hash?: `0x${string}`; + checkpointNumber?: bigint; + rollingHash?: `0x${string}`; + inboxRollingHash?: `0x${string}`; + bucketSeq?: bigint; + }; + }, + l1BlockTimestamp: bigint, + ): MessageSentLog { return { l1BlockNumber: log.blockNumber!, l1BlockHash: Buffer32.fromString(log.blockHash!), l1TransactionHash: log.transactionHash!, + l1BlockTimestamp, args: { index: log.args.index!, leaf: Fr.fromString(log.args.hash!), diff --git a/yarn-project/ethereum/src/contracts/rollup.test.ts b/yarn-project/ethereum/src/contracts/rollup.test.ts index 0e32dc63be7a..63b1d75866b4 100644 --- a/yarn-project/ethereum/src/contracts/rollup.test.ts +++ b/yarn-project/ethereum/src/contracts/rollup.test.ts @@ -487,6 +487,26 @@ describe('Rollup', () => { ); }); + it('packs the inbox consumption counts into the slot-number word', async () => { + const checkpointNumber = CheckpointNumber(13); + const override = await rollup.makeTempCheckpointLogOverride(checkpointNumber, { + slotNumber: SlotNumber(7), + inboxMsgTotal: 300n, + inboxConsumedBucket: 5n, + }); + const { map, slotFor } = getDiffMap(checkpointNumber, override); + expect(override[0].stateDiff).toHaveLength(1); + expect(map.get(await slotFor(TempCheckpointLogField.SlotNumber))).toBe( + `0x${(7n | (300n << 32n) | (5n << 96n)).toString(16).padStart(64, '0')}`.toLowerCase(), + ); + }); + + it('throws when an inbox consumption count overflows uint64', async () => { + await expect( + rollup.makeTempCheckpointLogOverride(CheckpointNumber(3), { inboxMsgTotal: 1n << 64n }), + ).rejects.toThrow(/does not fit in uint64/); + }); + it('partial override returns an empty array when no fields are supplied', async () => { const override = await rollup.makeTempCheckpointLogOverride(CheckpointNumber(13), {}); expect(override).toEqual([]); diff --git a/yarn-project/ethereum/src/contracts/rollup.ts b/yarn-project/ethereum/src/contracts/rollup.ts index a6726810d088..f3f39b1020ef 100644 --- a/yarn-project/ethereum/src/contracts/rollup.ts +++ b/yarn-project/ethereum/src/contracts/rollup.ts @@ -139,7 +139,10 @@ export type L1FeeData = { blobFee: bigint; }; -/** Field offsets within the CompressedTempCheckpointLog struct in Solidity storage. */ +/** + * Field offsets within the CompressedTempCheckpointLog struct in Solidity storage. The `SlotNumber` + * word also packs the inbox consumption counts, which Solidity places in the same slot. + */ export enum TempCheckpointLogField { HeaderHash = 0, BlobCommitmentsHash = 1, @@ -148,6 +151,7 @@ export enum TempCheckpointLogField { PayloadDigest = 4, SlotNumber = 5, FeeHeader = 6, + InboxRollingHash = 7, } /** @@ -155,12 +159,19 @@ export enum TempCheckpointLogField { * `propose()` path actually reads back. `payloadDigest` is `Buffer32` because it carries an * arbitrary `bytes32` value rather than a BN254 scalar. `slotNumber` carries the uint32 portion * of the on-chain `CompressedSlot`. + * + * `slotNumber`, `inboxMsgTotal` and `inboxConsumedBucket` share a single storage word, so supplying + * any one of them rewrites all three; the ones left out land as zero. */ export type TempCheckpointLogOverrideFields = { headerHash?: Fr; outHash?: Fr; payloadDigest?: Buffer32; slotNumber?: SlotNumber; + /** Cumulative Inbox message count consumed as of this checkpoint. */ + inboxMsgTotal?: bigint; + /** Inbox bucket sequence number this checkpoint's rolling hash corresponds to. */ + inboxConsumedBucket?: bigint; feeHeader?: FeeHeader; }; @@ -266,6 +277,13 @@ function isHexString(value: unknown): value is Hex { return typeof value === 'string' && value.startsWith('0x'); } +function requireUintFits(value: bigint, bits: number, name: string): bigint { + if (value < 0n || value >= 1n << BigInt(bits)) { + throw new Error(`${name} ${value} does not fit in uint${bits}`); + } + return value; +} + export class RollupContract { private readonly rollup: GetContractReturnType; private readonly logger = createLogger('ethereum:rollup'); @@ -946,6 +964,9 @@ export class RollupContract { * * `blobCommitmentsHash` and `attestationsHash` are intentionally not exposed here — the propose path * never asserts against them, so leaving them at storage zero is harmless. + * + * One diff entry is emitted per storage word touched, so words left out keep their on-chain values. + * `slotNumber` and the two inbox consumption counts share a word: any of them rewrites all three. */ public async makeTempCheckpointLogOverride( checkpointNumber: CheckpointNumber, @@ -970,14 +991,22 @@ export class RollupContract { value: fields.payloadDigest.toString() as `0x${string}`, }); } - if (fields.slotNumber !== undefined) { - // CompressedSlot is uint32 on L1 (SafeCast.toUint32 reverts on overflow). Match that behavior here - // so a malformed override surfaces immediately rather than silently truncating into a wrong slot. - const slotNumber = BigInt(fields.slotNumber); - if (slotNumber < 0n || slotNumber > 0xffffffffn) { - throw new Error(`slotNumber ${slotNumber} does not fit in uint32`); - } - stateDiff.push({ slot: slotAt(TempCheckpointLogField.SlotNumber), value: word(slotNumber) }); + if ( + fields.slotNumber !== undefined || + fields.inboxMsgTotal !== undefined || + fields.inboxConsumedBucket !== undefined + ) { + // The L1 struct packs the slot number and the two inbox consumption counts into one word, so this + // diff always writes all three. Widths are enforced here because the L1 writers cast through + // SafeCast and revert on overflow; a malformed override must surface rather than silently truncate + // into a neighbouring field. + const slotNumber = requireUintFits(BigInt(fields.slotNumber ?? 0), 32, 'slotNumber'); + const inboxMsgTotal = requireUintFits(fields.inboxMsgTotal ?? 0n, 64, 'inboxMsgTotal'); + const inboxConsumedBucket = requireUintFits(fields.inboxConsumedBucket ?? 0n, 64, 'inboxConsumedBucket'); + stateDiff.push({ + slot: slotAt(TempCheckpointLogField.SlotNumber), + value: word(slotNumber | (inboxMsgTotal << 32n) | (inboxConsumedBucket << 96n)), + }); } if (fields.feeHeader) { stateDiff.push({ diff --git a/yarn-project/noir-protocol-circuits-types/src/conversion/server.ts b/yarn-project/noir-protocol-circuits-types/src/conversion/server.ts index 93964d427782..869cf1bf28df 100644 --- a/yarn-project/noir-protocol-circuits-types/src/conversion/server.ts +++ b/yarn-project/noir-protocol-circuits-types/src/conversion/server.ts @@ -825,7 +825,6 @@ function mapL1ToL2MessageBundleToNoir(bundle: L1ToL2MessageBundle) { // get the fixed-length `FixedLengthArray` the generated `L1ToL2MessageBundle.messages` type requires. messages: mapFieldArrayToNoir(bundle.messages, MAX_L1_TO_L2_MSGS_PER_BLOCK), num_msgs: mapNumberToNoir(bundle.numMsgs), - num_real_msgs: mapNumberToNoir(bundle.numRealMsgs), }; } diff --git a/yarn-project/prover-client/src/light/lightweight_checkpoint_builder.bench.test.ts b/yarn-project/prover-client/src/light/lightweight_checkpoint_builder.bench.test.ts index 36319604383b..670fb97b4d22 100644 --- a/yarn-project/prover-client/src/light/lightweight_checkpoint_builder.bench.test.ts +++ b/yarn-project/prover-client/src/light/lightweight_checkpoint_builder.bench.test.ts @@ -146,11 +146,10 @@ describe('LightweightCheckpointBuilder benchmarks', () => { const constants = makeCheckpointConstants(slotNumber); const fork = await worldState.fork(); - const builder = await LightweightCheckpointBuilder.startNewCheckpoint( + const builder = LightweightCheckpointBuilder.startNewCheckpoint( CheckpointNumber(1), constants, [], - [], Fr.ZERO, fork, ); @@ -158,7 +157,7 @@ describe('LightweightCheckpointBuilder benchmarks', () => { const globalVariables = makeGlobalVariables(blockNumber, slotNumber); const txs = await timesAsync(numTxs, i => makeTx(globalVariables, 5000 + i)); - const { timings } = await builder.addBlock(globalVariables, txs, { insertTxsEffects: true }); + const { timings } = await builder.addBlock(globalVariables, txs, [], { insertTxsEffects: true }); const prefix = `addBlock/${label}/${numTxs} txs`; for (const [step, ms] of Object.entries(timings)) { diff --git a/yarn-project/prover-client/src/light/lightweight_checkpoint_builder.test.ts b/yarn-project/prover-client/src/light/lightweight_checkpoint_builder.test.ts index d7f607bd5f19..7361d00f3fc8 100644 --- a/yarn-project/prover-client/src/light/lightweight_checkpoint_builder.test.ts +++ b/yarn-project/prover-client/src/light/lightweight_checkpoint_builder.test.ts @@ -97,16 +97,14 @@ describe('LightweightCheckpointBuilder', () => { const blockNumber = BlockNumber(1); const constants = makeCheckpointConstants(slotNumber); - const l1ToL2Messages: Fr[] = []; const previousCheckpointOutHashes: Fr[] = []; const fork = await worldState.fork(); // Use LightweightCheckpointBuilder directly - const checkpointBuilder = await LightweightCheckpointBuilder.startNewCheckpoint( + const checkpointBuilder = LightweightCheckpointBuilder.startNewCheckpoint( checkpointNumber, constants, - l1ToL2Messages, previousCheckpointOutHashes, Fr.ZERO, fork, @@ -114,7 +112,7 @@ describe('LightweightCheckpointBuilder', () => { // Build empty block const globalVariables = makeGlobalVariables(blockNumber, slotNumber); - const { block } = await checkpointBuilder.addBlock(globalVariables, [], { insertTxsEffects: true }); + const { block } = await checkpointBuilder.addBlock(globalVariables, [], [], { insertTxsEffects: true }); expect(block.header.globalVariables.blockNumber).toEqual(blockNumber); @@ -138,16 +136,14 @@ describe('LightweightCheckpointBuilder', () => { const blockNumber = BlockNumber(1); const constants = makeCheckpointConstants(slotNumber); - const l1ToL2Messages: Fr[] = []; // There are two checkpoints before this one. const previousCheckpointOutHashes = [Fr.random(), Fr.random()]; const fork = await worldState.fork(); - const checkpointBuilder = await LightweightCheckpointBuilder.startNewCheckpoint( + const checkpointBuilder = LightweightCheckpointBuilder.startNewCheckpoint( checkpointNumber, constants, - l1ToL2Messages, previousCheckpointOutHashes, Fr.ZERO, fork, @@ -161,7 +157,7 @@ describe('LightweightCheckpointBuilder', () => { tx.txEffect.l2ToL1Msgs.push(...msgs); // Build block with tx - insertTxsEffects will handle inserting side effects - const { block } = await checkpointBuilder.addBlock(globalVariables, [tx], { + const { block } = await checkpointBuilder.addBlock(globalVariables, [tx], [], { insertTxsEffects: true, }); @@ -190,15 +186,13 @@ describe('LightweightCheckpointBuilder', () => { const blockNumber = BlockNumber(1); const constants = makeCheckpointConstants(slotNumber); - const l1ToL2Messages: Fr[] = []; const previousCheckpointOutHashes: Fr[] = []; const fork = await worldState.fork(); - const checkpointBuilder = await LightweightCheckpointBuilder.startNewCheckpoint( + const checkpointBuilder = LightweightCheckpointBuilder.startNewCheckpoint( checkpointNumber, constants, - l1ToL2Messages, previousCheckpointOutHashes, Fr.ZERO, fork, @@ -209,7 +203,7 @@ describe('LightweightCheckpointBuilder', () => { const txs = await timesAsync(3, i => makeProcessedTx(globalVariables, 1000 + i)); // Build block with txs - insertTxsEffects will handle inserting side effects - const { block } = await checkpointBuilder.addBlock(globalVariables, txs, { + const { block } = await checkpointBuilder.addBlock(globalVariables, txs, [], { insertTxsEffects: true, }); @@ -230,15 +224,13 @@ describe('LightweightCheckpointBuilder', () => { const slotNumber = SlotNumber(15); const constants = makeCheckpointConstants(slotNumber); - const l1ToL2Messages: Fr[] = []; const previousCheckpointOutHashes: Fr[] = []; const fork = await worldState.fork(); - const checkpointBuilder = await LightweightCheckpointBuilder.startNewCheckpoint( + const checkpointBuilder = LightweightCheckpointBuilder.startNewCheckpoint( checkpointNumber, constants, - l1ToL2Messages, previousCheckpointOutHashes, Fr.ZERO, fork, @@ -256,7 +248,7 @@ describe('LightweightCheckpointBuilder', () => { const txs = await timesAsync(txsPerBlock, j => makeProcessedTx(globalVariables, 2000 + i * 10 + j)); // Build block - insertTxsEffects will handle inserting side effects - const { block } = await checkpointBuilder.addBlock(globalVariables, txs, { + const { block } = await checkpointBuilder.addBlock(globalVariables, txs, [], { insertTxsEffects: true, }); @@ -276,65 +268,63 @@ describe('LightweightCheckpointBuilder', () => { await fork.close(); }); - }); - describe('error handling', () => { - it('completing a checkpoint without blocks fails', async () => { + it('builds a mid-checkpoint block with no txs that only carries messages', async () => { const checkpointNumber = CheckpointNumber(1); const slotNumber = SlotNumber(15); const constants = makeCheckpointConstants(slotNumber); - const l1ToL2Messages: Fr[] = []; const previousCheckpointOutHashes: Fr[] = []; const fork = await worldState.fork(); - const checkpointBuilder = await LightweightCheckpointBuilder.startNewCheckpoint( + const checkpointBuilder = LightweightCheckpointBuilder.startNewCheckpoint( checkpointNumber, constants, - l1ToL2Messages, previousCheckpointOutHashes, Fr.ZERO, fork, ); - // Try to complete checkpoint without adding any blocks - await expect(checkpointBuilder.completeCheckpoint()).rejects.toThrow(/no blocks/); + const globalVariables1 = makeGlobalVariables(BlockNumber(1), slotNumber); + const txs1 = await timesAsync(2, i => makeProcessedTx(globalVariables1, 3000 + i)); + await checkpointBuilder.addBlock(globalVariables1, txs1, [], { insertTxsEffects: true }); + + const messages = [new Fr(0xb00), new Fr(0xb01)]; + const globalVariables2 = makeGlobalVariables(BlockNumber(2), slotNumber); + const { block } = await checkpointBuilder.addBlock(globalVariables2, [], messages, { insertTxsEffects: true }); + + expect(block.body.txEffects.length).toBe(0); + expect(block.header.state.l1ToL2MessageTree.nextAvailableLeafIndex).toBe(messages.length); + + const checkpoint = await checkpointBuilder.completeCheckpoint(); + expect(checkpoint.blocks.length).toBe(2); + expect(checkpoint.blocks[1].number).toEqual(BlockNumber(2)); await fork.close(); }); + }); - it('adding an empty (no txs) block that is NOT the first block in the checkpoint fails', async () => { + describe('error handling', () => { + it('completing a checkpoint without blocks fails', async () => { const checkpointNumber = CheckpointNumber(1); const slotNumber = SlotNumber(15); const constants = makeCheckpointConstants(slotNumber); - const l1ToL2Messages: Fr[] = []; const previousCheckpointOutHashes: Fr[] = []; const fork = await worldState.fork(); - const checkpointBuilder = await LightweightCheckpointBuilder.startNewCheckpoint( + const checkpointBuilder = LightweightCheckpointBuilder.startNewCheckpoint( checkpointNumber, constants, - l1ToL2Messages, previousCheckpointOutHashes, Fr.ZERO, fork, ); - // Add first block with txs - insertTxsEffects will handle inserting side effects - const globalVariables1 = makeGlobalVariables(BlockNumber(1), slotNumber); - const txs1 = await timesAsync(2, i => makeProcessedTx(globalVariables1, 3000 + i)); - await checkpointBuilder.addBlock(globalVariables1, txs1, { - insertTxsEffects: true, - }); - - // Try to add second block with no txs - this should fail - const globalVariables2 = makeGlobalVariables(BlockNumber(2), slotNumber); - await expect(checkpointBuilder.addBlock(globalVariables2, [], { insertTxsEffects: true })).rejects.toThrow( - /first block/, - ); + // Try to complete checkpoint without adding any blocks + await expect(checkpointBuilder.completeCheckpoint()).rejects.toThrow(/no blocks/); await fork.close(); }); @@ -344,15 +334,13 @@ describe('LightweightCheckpointBuilder', () => { const slotNumber = SlotNumber(15); const constants = makeCheckpointConstants(slotNumber); - const l1ToL2Messages: Fr[] = []; const previousCheckpointOutHashes: Fr[] = []; const fork = await worldState.fork(); - const checkpointBuilder = await LightweightCheckpointBuilder.startNewCheckpoint( + const checkpointBuilder = LightweightCheckpointBuilder.startNewCheckpoint( checkpointNumber, constants, - l1ToL2Messages, previousCheckpointOutHashes, Fr.ZERO, fork, @@ -363,7 +351,7 @@ describe('LightweightCheckpointBuilder', () => { const wrongBlockNumber = BlockNumber(5); const globalVariables = makeGlobalVariables(wrongBlockNumber, slotNumber); - await expect(checkpointBuilder.addBlock(globalVariables, [], { insertTxsEffects: true })).rejects.toThrow( + await expect(checkpointBuilder.addBlock(globalVariables, [], [], { insertTxsEffects: true })).rejects.toThrow( /Archive tree next leaf index mismatch/, ); diff --git a/yarn-project/prover-client/src/light/lightweight_checkpoint_builder.ts b/yarn-project/prover-client/src/light/lightweight_checkpoint_builder.ts index 59176228a08c..69f260fc8ed7 100644 --- a/yarn-project/prover-client/src/light/lightweight_checkpoint_builder.ts +++ b/yarn-project/prover-client/src/light/lightweight_checkpoint_builder.ts @@ -11,7 +11,6 @@ import { accumulateInboxRollingHash, appendL1ToL2MessagesToTree, computeCheckpointOutHash, - computeInHashFromL1ToL2Messages, } from '@aztec/stdlib/messaging'; import { CheckpointHeader, computeBlockHeadersHash } from '@aztec/stdlib/rollup'; import { AppendOnlyTreeSnapshot, MerkleTreeId } from '@aztec/stdlib/trees'; @@ -31,7 +30,7 @@ import { /** * Builds a checkpoint and its header and the blocks in it from a set of processed tx without running any circuits. * - * It updates the l1-to-l2 message tree when starting a new checkpoint, and then updates the archive tree when each block is added. + * Each added block inserts its own L1-to-L2 message bundle into the message tree and then updates the archive tree. * Finally completes the checkpoint by computing its header. */ export class LightweightCheckpointBuilder { @@ -61,24 +60,24 @@ export class LightweightCheckpointBuilder { this.logger.debug('Starting new checkpoint', { constants, l1ToL2Messages, feeAssetPriceModifier }); } - static async startNewCheckpoint( + /** + * Starts a fresh checkpoint. The checkpoint's L1-to-L2 messages are not supplied here: every block brings its own + * bundle to {@link addBlock}, which inserts it into the tree and accumulates it into the checkpoint's message list. + */ + static startNewCheckpoint( checkpointNumber: CheckpointNumber, constants: CheckpointGlobalVariables, - l1ToL2Messages: Fr[], previousCheckpointOutHashes: Fr[], previousInboxRollingHash: Fr, db: MerkleTreeWriteOperations, bindings?: LoggerBindings, feeAssetPriceModifier: bigint = 0n, - ): Promise { - // Insert l1-to-l2 messages into the tree. - await appendL1ToL2MessagesToTree(db, l1ToL2Messages); - + ): LightweightCheckpointBuilder { return new LightweightCheckpointBuilder( checkpointNumber, constants, feeAssetPriceModifier, - l1ToL2Messages, + [], previousCheckpointOutHashes, previousInboxRollingHash, db, @@ -89,8 +88,8 @@ export class LightweightCheckpointBuilder { /** * Resumes building a checkpoint from existing blocks. This is used for validator re-execution * where blocks have already been built and their effects are already in the database. - * Unlike startNewCheckpoint, this does NOT append l1ToL2Messages to the tree since they - * were already added when the blocks were originally built. + * `l1ToL2Messages` is the whole checkpoint's message list as consumed by the existing blocks: it seeds the + * checkpoint's rolling hash and is not inserted into the tree, since the blocks already inserted it. */ static async resumeCheckpoint( checkpointNumber: CheckpointNumber, @@ -168,20 +167,17 @@ export class LightweightCheckpointBuilder { /** * Adds a new block to the checkpoint. The tx effects must have already been inserted into the db if * this is called after tx processing, if that's not the case, then set `insertTxsEffects` to true. + * @param l1ToL2Messages - The message leaves this block consumes from the Inbox, in insertion order. */ public async addBlock( globalVariables: GlobalVariables, txs: ProcessedTx[], + l1ToL2Messages: Fr[], opts: { insertTxsEffects?: boolean; expectedEndState?: StateReference } = {}, ): Promise<{ block: L2Block; timings: Record }> { const timings: Record = {}; const isFirstBlock = this.blocks.length === 0; - // Empty blocks are only allowed as the first block in a checkpoint - if (!isFirstBlock && txs.length === 0) { - throw new Error('Cannot add empty block that is not the first block in the checkpoint.'); - } - if (isFirstBlock) { const [msGetInitialArchive, initialArchive] = await elapsed(() => getTreeSnapshot(MerkleTreeId.ARCHIVE, this.db)); this.lastArchives.push(initialArchive); @@ -203,6 +199,13 @@ export class LightweightCheckpointBuilder { timings.insertSideEffects = msInsertSideEffects; } + // Streaming Inbox: insert this block's L1-to-L2 message bundle before reading the end state, + // so the block header's L1-to-L2 tree snapshot reflects it. Bundles are appended compactly (unpadded, at the + // tree's current next-available index). The logical messages are accumulated only once the block is fully built + // (below), so a mid-build failure does not pollute the checkpoint's rolling hash; the rolling hash is recomputed + // over them at checkpoint completion. + await appendL1ToL2MessagesToTree(this.db, l1ToL2Messages); + const [msGetEndState, endState] = await elapsed(() => this.db.getStateReference()); timings.getEndState = msGetEndState; @@ -216,7 +219,7 @@ export class LightweightCheckpointBuilder { } const [msBuildHeaderAndBody, { header, body, blockBlobFields }] = await elapsed(() => - buildHeaderAndBodyFromTxs(txs, lastArchive, endState, globalVariables, this.spongeBlob, isFirstBlock), + buildHeaderAndBodyFromTxs(txs, lastArchive, endState, globalVariables, this.spongeBlob), ); timings.buildHeaderAndBody = msBuildHeaderAndBody; @@ -238,6 +241,10 @@ export class LightweightCheckpointBuilder { const block = new L2Block(newArchive, header, body, this.checkpointNumber, indexWithinCheckpoint); this.blocks.push(block); + // Accumulate the streaming bundle now that the block is fully built, so a mid-build throw above leaves the + // checkpoint's message list (and thus its inHash/rolling hash) consistent with the blocks actually built. + this.l1ToL2Messages.push(...l1ToL2Messages); + const [msSpongeAbsorb] = await elapsed(() => this.spongeBlob.absorb(blockBlobFields)); timings.spongeAbsorb = msSpongeAbsorb; this.blobFields.push(...blockBlobFields); @@ -270,7 +277,9 @@ export class LightweightCheckpointBuilder { const blobs = await getBlobsPerL1Block(this.blobFields); const blobsHash = computeBlobsHashFromBlobs(blobs); - const inHash = computeInHashFromL1ToL2Messages(this.l1ToL2Messages); + // Legacy inHash is dead post-flip; the checkpoint header carries zero (AZIP-22 Fast Inbox). The consensus + // rolling hash over the consumed messages is the authoritative Inbox commitment. + const inHash = Fr.ZERO; const inboxRollingHash = accumulateInboxRollingHash(this.previousInboxRollingHash, this.l1ToL2Messages); const { slotNumber, coinbase, feeRecipient, gasFees } = this.constants; diff --git a/yarn-project/prover-client/src/mocks/test_context.ts b/yarn-project/prover-client/src/mocks/test_context.ts index 657a4cf97268..08bf74daabcf 100644 --- a/yarn-project/prover-client/src/mocks/test_context.ts +++ b/yarn-project/prover-client/src/mocks/test_context.ts @@ -1,8 +1,7 @@ import type { BBProverConfig } from '@aztec/bb-prover'; import { TestCircuitProver } from '@aztec/bb-prover'; -import { NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants'; import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types'; -import { padArrayEnd, times, timesAsync } from '@aztec/foundation/collection'; +import { times, timesAsync } from '@aztec/foundation/collection'; import { Fr } from '@aztec/foundation/curves/bn254'; import type { Logger } from '@aztec/foundation/log'; import { SerialQueue } from '@aztec/foundation/queue'; @@ -60,6 +59,10 @@ export class TestContext { private nextBlockNumber = 1; private epochNumber = 1; private feePayerBalance: Fr; + // Running inbox rolling hash across the checkpoints built by this context (never resets: the protocol threads it + // from genesis). Each checkpoint's builder starts from it, and it advances by the checkpoint's messages, so a + // checkpoint built after a message-carrying one commits the correct continuation in its header. + private currentInboxRollingHash = Fr.ZERO; constructor( public worldState: MerkleTreeAdminDatabase, @@ -194,12 +197,10 @@ export class TestContext { const fork = await this.worldState.fork(); - // Build l1 to l2 messages. + // Build l1 to l2 messages. Appended unpadded at compact indices; the mock assigns them all to + // the checkpoint's first block, matching how the per-block driver slices them. const l1ToL2Messages = times(numL1ToL2Messages, i => new Fr(slotNumber * 100 + i)); - await fork.appendLeaves( - MerkleTreeId.L1_TO_L2_MESSAGE_TREE, - padArrayEnd(l1ToL2Messages, Fr.ZERO, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP), - ); + await fork.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2Messages); const newL1ToL2Snapshot = await getTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, fork); const startBlockNumber = this.nextBlockNumber; @@ -244,12 +245,12 @@ export class TestContext { const cleanFork = await this.worldState.fork(); const previousCheckpointOutHashes = this.checkpointOutHashes; - const builder = await LightweightCheckpointBuilder.startNewCheckpoint( + const startInboxRollingHash = this.currentInboxRollingHash; + const builder = LightweightCheckpointBuilder.startNewCheckpoint( checkpointNumber, { ...constants, timestamp }, - l1ToL2Messages, previousCheckpointOutHashes, - Fr.ZERO, + startInboxRollingHash, cleanFork, ); @@ -259,7 +260,7 @@ export class TestContext { const txs = blockTxs[i]; const state = blockEndStates[i]; - const { block } = await builder.addBlock(blockGlobalVariables[i], txs, { + const { block } = await builder.addBlock(blockGlobalVariables[i], txs, i === 0 ? l1ToL2Messages : [], { expectedEndState: state, insertTxsEffects: true, }); @@ -276,6 +277,132 @@ export class TestContext { const checkpoint = await builder.completeCheckpoint(); this.checkpoints.push(checkpoint); this.checkpointOutHashes.push(checkpoint.getCheckpointOutHash()); + this.currentInboxRollingHash = checkpoint.header.inboxRollingHash; + + return { + constants, + checkpoint, + header: checkpoint.header, + blocks, + l1ToL2Messages, + previousBlockHeader, + startInboxRollingHash, + }; + } + + /** + * Like {@link makeCheckpoint} but distributes the L1-to-L2 message bundle across the checkpoint's blocks + * (streaming Inbox): `l1ToL2MessagesPerBlock[i]` is block `i`'s own message slice, + * appended at compact indices in insertion order. Lets tests exercise checkpoints whose messages span more + * than one block, including a non-first block carrying a bundle — the single-block-per-checkpoint + * `makeCheckpoint` puts every message in the first block. `numTxsPerBlock` defaults to 1 because the builder + * rejects a non-first block with neither txs nor messages; a zero-tx entry whose slice is non-empty is valid + * and produces a message-only block. + */ + public async makeCheckpointWithMessagesPerBlock( + l1ToL2MessagesPerBlock: Fr[][], + { + numTxsPerBlock = 1, + makeProcessedTxOpts = () => ({}), + ...constantOpts + }: { + numTxsPerBlock?: number | number[]; + makeProcessedTxOpts?: ( + blockGlobalVariables: GlobalVariables, + txIndex: number, + ) => Partial[0]>; + } & Partial> = {}, + ) { + const numBlocks = l1ToL2MessagesPerBlock.length; + if (numBlocks === 0) { + throw new Error('Cannot make a checkpoint with 0 blocks.'); + } + + const checkpointIndex = this.nextCheckpointIndex++; + const checkpointNumber = this.nextCheckpointNumber; + this.nextCheckpointNumber++; + const slotNumber = checkpointNumber * 15; + + const constants = makeCheckpointConstants(slotNumber, constantOpts); + const l1ToL2Messages = l1ToL2MessagesPerBlock.flat(); + + const fork = await this.worldState.fork(); + + const startBlockNumber = this.nextBlockNumber; + const previousBlockHeader = this.getBlockHeader(BlockNumber(startBlockNumber - 1)); + const timestamp = BigInt(slotNumber * 26); + + const blockGlobalVariables = times(numBlocks, i => + makeGlobals(startBlockNumber + i, slotNumber, { + coinbase: constants.coinbase, + feeRecipient: constants.feeRecipient, + gasFees: constants.gasFees, + timestamp, + }), + ); + this.nextBlockNumber += numBlocks; + + // Build txs per block. Append the block's message slice to the fork before its txs so the per-block end + // state matches the builder's (message-tree and tx-effect trees are independent, so append order does not + // matter, but the cumulative message tree must reflect the slices already inserted). + let totalTxs = 0; + const blockEndStates: StateReference[] = []; + const blockTxs = await timesAsync(numBlocks, async blockIndex => { + await fork.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2MessagesPerBlock[blockIndex]); + const newL1ToL2Snapshot = await getTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, fork); + + const txIndexOffset = totalTxs; + const numTxs = typeof numTxsPerBlock === 'number' ? numTxsPerBlock : numTxsPerBlock[blockIndex]; + totalTxs += numTxs; + const txs = await timesAsync(numTxs, txIndex => + this.makeProcessedTx({ + seed: (txIndexOffset + txIndex + 1) * 321 + (checkpointIndex + 1) * 123456 + this.epochNumber * 0x99999, + globalVariables: blockGlobalVariables[blockIndex], + anchorBlockHeader: previousBlockHeader, + newL1ToL2Snapshot, + ...makeProcessedTxOpts(blockGlobalVariables[blockIndex], txIndexOffset + txIndex), + }), + ); + + const endState = await this.updateTrees(txs, fork); + blockEndStates.push(endState); + + return txs; + }); + + const cleanFork = await this.worldState.fork(); + const previousCheckpointOutHashes = this.checkpointOutHashes; + const startInboxRollingHash = this.currentInboxRollingHash; + const builder = LightweightCheckpointBuilder.startNewCheckpoint( + checkpointNumber, + { ...constants, timestamp }, + previousCheckpointOutHashes, + startInboxRollingHash, + cleanFork, + ); + + const blocks = []; + for (let i = 0; i < numBlocks; i++) { + const txs = blockTxs[i]; + const state = blockEndStates[i]; + + const { block } = await builder.addBlock(blockGlobalVariables[i], txs, l1ToL2MessagesPerBlock[i], { + expectedEndState: state, + insertTxsEffects: true, + }); + + const header = block.header; + this.headers.set(block.number, header); + + await this.worldState.handleL2BlockAndMessages(block, l1ToL2MessagesPerBlock[i]); + + blocks.push({ header, txs }); + } + + const checkpoint = await builder.completeCheckpoint(); + this.checkpoints.push(checkpoint); + this.checkpointOutHashes.push(checkpoint.getCheckpointOutHash()); + this.currentInboxRollingHash = checkpoint.header.inboxRollingHash; return { constants, @@ -283,7 +410,9 @@ export class TestContext { header: checkpoint.header, blocks, l1ToL2Messages, + l1ToL2MessagesPerBlock, previousBlockHeader, + startInboxRollingHash, }; } diff --git a/yarn-project/prover-client/src/orchestrator/block-building-helpers.ts b/yarn-project/prover-client/src/orchestrator/block-building-helpers.ts index 01c0726d70bb..1c340afa73cc 100644 --- a/yarn-project/prover-client/src/orchestrator/block-building-helpers.ts +++ b/yarn-project/prover-client/src/orchestrator/block-building-helpers.ts @@ -312,7 +312,6 @@ export const buildHeaderAndBodyFromTxs = runInSpan( endState: StateReference, globalVariables: GlobalVariables, startSpongeBlob: SpongeBlob, - isFirstBlock: boolean, ) => { span.setAttribute(Attributes.BLOCK_NUMBER, globalVariables.blockNumber); @@ -341,7 +340,8 @@ export const buildHeaderAndBodyFromTxs = runInSpan( noteHashRoot: partial.noteHashTree.root, nullifierRoot: partial.nullifierTree.root, publicDataRoot: partial.publicDataTree.root, - l1ToL2MessageRoot: isFirstBlock ? l1ToL2MessageTree.root : undefined, + // Every block carries its own post-bundle l1-to-l2 message tree root. + l1ToL2MessageRoot: l1ToL2MessageTree.root, txs: body.toTxBlobData(), }); @@ -399,6 +399,15 @@ export async function getSubtreeSiblingPath( return fullSiblingPath.getSubtreeSiblingPath(subtreeHeight).toFields(); } +/** + * Returns the full-height frontier (left-sibling path) at a tree's next-available leaf index — the hint the append + * circuits re-hash against the snapshot root when appending leaves at a compact (unaligned) index. + */ +export async function getFrontierSiblingPath(treeId: MerkleTreeId, db: MerkleTreeReadOperations): Promise { + const nextAvailableLeafIndex = await db.getTreeInfo(treeId).then(t => t.size); + return (await db.getSiblingPath(treeId, nextAvailableLeafIndex)).toFields(); +} + // Scan a tree searching for a specific value and return a membership witness proof for it export async function getMembershipWitnessFor( value: Fr, diff --git a/yarn-project/prover-client/src/orchestrator/block-proving-state.ts b/yarn-project/prover-client/src/orchestrator/block-proving-state.ts index 0409fd2bd61e..a1b48de90321 100644 --- a/yarn-project/prover-client/src/orchestrator/block-proving-state.ts +++ b/yarn-project/prover-client/src/orchestrator/block-proving-state.ts @@ -1,18 +1,15 @@ import { type BlockBlobData, type BlockEndBlobData, type SpongeBlob, encodeBlockEndBlobData } from '@aztec/blob-lib'; -import { - type ARCHIVE_HEIGHT, - L1_TO_L2_MSG_SUBTREE_HEIGHT, - type L1_TO_L2_MSG_SUBTREE_ROOT_SIBLING_PATH_LENGTH, - type L1_TO_L2_MSG_TREE_HEIGHT, - MAX_L1_TO_L2_MSGS_PER_BLOCK, - type NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH, +import type { + ARCHIVE_HEIGHT, + L1_TO_L2_MSG_TREE_HEIGHT, + NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH, } from '@aztec/constants'; import { BlockNumber } from '@aztec/foundation/branded-types'; import { Fr } from '@aztec/foundation/curves/bn254'; import type { Tuple } from '@aztec/foundation/serialize'; import { type TreeNodeLocation, UnbalancedTreeStore } from '@aztec/foundation/trees'; import type { PublicInputsAndRecursiveProof } from '@aztec/stdlib/interfaces/server'; -import { L1ToL2MessageBundle, L1ToL2MessageSponge } from '@aztec/stdlib/messaging'; +import { L1ToL2MessageBundle, type L1ToL2MessageSponge, makeL1ToL2MessageBundle } from '@aztec/stdlib/messaging'; import { BlockRollupPublicInputs, BlockRootNoTxsRollupPrivateInputs, @@ -61,7 +58,6 @@ export class BlockProvingState { private endState: StateReference | undefined; private endSpongeBlob: SpongeBlob | undefined; private txs: TxProvingState[] = []; - private isFirstBlock: boolean; private error: string | undefined; constructor( @@ -72,21 +68,33 @@ export class BlockProvingState { private readonly timestamp: UInt64, public readonly lastArchiveTreeSnapshot: AppendOnlyTreeSnapshot, private readonly lastArchiveSiblingPath: Tuple, - private readonly lastL1ToL2MessageTreeSnapshot: AppendOnlyTreeSnapshot, - private readonly lastL1ToL2MessageSubtreeRootSiblingPath: Tuple< - Fr, - typeof L1_TO_L2_MSG_SUBTREE_ROOT_SIBLING_PATH_LENGTH - >, + // The full state reference of the previous block, before this block's bundle is appended. Feeds the msgs-only + // block root, whose zero-tx block has no tx constants to pin the previous state. + private readonly previousState: StateReference, + // This block's L1-to-L2 message tree snapshot before and after its own bundle. The start is + // the previous block's end (block-merge continuity); the end is this block's own post-bundle snapshot. + private readonly startL1ToL2MessageTreeSnapshot: AppendOnlyTreeSnapshot, public readonly newL1ToL2MessageTreeSnapshot: AppendOnlyTreeSnapshot, - private readonly headerOfLastBlockInPreviousCheckpoint: BlockHeader, + // Full-height frontier (left-sibling path) at the block's start index, pinning the append at a compact index. + private readonly l1ToL2MessageFrontierHint: Tuple, + // This block's own real L1-to-L2 message leaves (unpadded slice). + private readonly l1ToL2Messages: Fr[], + // Message sponge threaded across the checkpoint's blocks: the start is the previous block's + // end sponge (empty for the first block), the end absorbs this block's own slice. Block merges assert the + // continuity, so the end is exposed for the next block to inherit. + private readonly startMsgSponge: L1ToL2MessageSponge, + private readonly endMsgSponge: L1ToL2MessageSponge, private readonly startSpongeBlob: SpongeBlob, public parentCheckpoint: CheckpointProvingState, ) { - this.isFirstBlock = index === 0; - this.baseOrMergeProofs = new UnbalancedTreeStore(totalNumTxs); } + /** The message sponge after absorbing this block's slice; inherited by the next block as its start sponge. */ + public getEndMsgSponge(): L1ToL2MessageSponge { + return this.endMsgSponge; + } + public get epochNumber(): number { return this.parentCheckpoint.epochNumber; } @@ -255,7 +263,8 @@ export class BlockProvingState { noteHashRoot: partial.noteHashTree.root, nullifierRoot: partial.nullifierTree.root, publicDataRoot: partial.publicDataTree.root, - l1ToL2MessageRoot: this.isFirstBlock ? this.newL1ToL2MessageTreeSnapshot.root : undefined, + // Every block carries its own post-bundle l1-to-l2 message tree root. + l1ToL2MessageRoot: this.newL1ToL2MessageTreeSnapshot.root, }; } @@ -298,7 +307,7 @@ export class BlockProvingState { const messageBundle = this.#getMessageBundle(); const frontierHint = this.#getFrontierHint(); - const startMsgSponge = this.#getStartMsgSponge(); + const startMsgSponge = this.startMsgSponge; const [leftRollup, rightRollup] = previousRollups; if (!leftRollup) { @@ -306,7 +315,7 @@ export class BlockProvingState { rollupType: 'rollup-block-root-no-txs' satisfies CircuitName, inputs: new BlockRootNoTxsRollupPrivateInputs( this.lastArchiveTreeSnapshot, - this.headerOfLastBlockInPreviousCheckpoint.state, + this.previousState, this.constants, this.timestamp, this.startSpongeBlob, @@ -322,7 +331,7 @@ export class BlockProvingState { inputs: new BlockRootSingleTxRollupPrivateInputs( leftRollup, messageBundle, - this.lastL1ToL2MessageTreeSnapshot, + this.startL1ToL2MessageTreeSnapshot, startMsgSponge, frontierHint, this.lastArchiveSiblingPath, @@ -334,7 +343,7 @@ export class BlockProvingState { inputs: new BlockRootRollupPrivateInputs( [leftRollup, rightRollup], messageBundle, - this.lastL1ToL2MessageTreeSnapshot, + this.startL1ToL2MessageTreeSnapshot, startMsgSponge, frontierHint, this.lastArchiveSiblingPath, @@ -344,41 +353,21 @@ export class BlockProvingState { } /** - * The message sponge this block starts from. It resets per checkpoint, so the first block starts from empty and - * every later block inherits the checkpoint's accumulated sponge (transitionally the first block carries all of the - * checkpoint's messages, so that value is already final after it). - */ - #getStartMsgSponge(): L1ToL2MessageSponge { - return this.isFirstBlock ? L1ToL2MessageSponge.empty() : this.parentCheckpoint.getCheckpointMsgSponge(); - } - - /** - * The message bundle this block appends. Transitionally the first block carries the whole checkpoint's messages - * padded to `MAX_L1_TO_L2_MSGS_PER_BLOCK` — inserted as a full aligned subtree into the L1-to-L2 tree (`numMsgs` = - * the cap), while only the real messages are absorbed into the message sponge (`numRealMsgs`, matching the - * checkpoint's InboxParity proof). Non-first blocks carry an empty bundle. + * The real-count message bundle this block appends: the block's own message slice, inserted at + * compact indices and absorbed into its block-root message sponge. */ #getMessageBundle(): L1ToL2MessageBundle { - if (this.isFirstBlock) { - return new L1ToL2MessageBundle( - this.parentCheckpoint.getPaddedL1ToL2Messages(), - MAX_L1_TO_L2_MSGS_PER_BLOCK, - this.parentCheckpoint.getNumRealL1ToL2Messages(), - ); - } - return L1ToL2MessageBundle.empty(); + return this.l1ToL2Messages.length === 0 + ? L1ToL2MessageBundle.empty() + : makeL1ToL2MessageBundle(this.l1ToL2Messages); } /** - * Full-height frontier hint for the bundle append. The l1-to-l2 tree index is always subtree-aligned in the - * transitional wiring, so the bottom `L1_TO_L2_MSG_SUBTREE_HEIGHT` levels are left-child (unread, zero) and the top - * levels are exactly the subtree-root sibling path already captured for this block. + * Full-height frontier hint for the bundle append: the left-sibling path at the block's compact start index, which + * the block-root circuit re-hashes against its start snapshot root. */ #getFrontierHint(): Tuple { - return [ - ...Array.from({ length: L1_TO_L2_MSG_SUBTREE_HEIGHT }, () => Fr.ZERO), - ...this.lastL1ToL2MessageSubtreeRootSiblingPath, - ] as Tuple; + return this.l1ToL2MessageFrontierHint; } // Returns a specific transaction proving state diff --git a/yarn-project/prover-client/src/orchestrator/checkpoint-proving-state.ts b/yarn-project/prover-client/src/orchestrator/checkpoint-proving-state.ts index db1adbf703f9..ba29f3a0aa46 100644 --- a/yarn-project/prover-client/src/orchestrator/checkpoint-proving-state.ts +++ b/yarn-project/prover-client/src/orchestrator/checkpoint-proving-state.ts @@ -1,7 +1,7 @@ import { SpongeBlob } from '@aztec/blob-lib'; import type { ARCHIVE_HEIGHT, - L1_TO_L2_MSG_SUBTREE_ROOT_SIBLING_PATH_LENGTH, + L1_TO_L2_MSG_TREE_HEIGHT, NESTED_RECURSIVE_PROOF_LENGTH, NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH, } from '@aztec/constants'; @@ -10,11 +10,11 @@ import { Fr } from '@aztec/foundation/curves/bn254'; import type { Tuple } from '@aztec/foundation/serialize'; import { type TreeNodeLocation, UnbalancedTreeStore } from '@aztec/foundation/trees'; import type { PublicInputsAndRecursiveProof } from '@aztec/stdlib/interfaces/server'; -import { L1ToL2MessageSponge, computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging'; +import { L1ToL2MessageSponge } from '@aztec/stdlib/messaging'; import { InboxParityPrivateInputs, type ParityPublicInputs } from '@aztec/stdlib/parity'; import { BlockMergeRollupPrivateInputs, BlockRollupPublicInputs, CheckpointConstantData } from '@aztec/stdlib/rollup'; import type { AppendOnlyTreeSnapshot } from '@aztec/stdlib/trees'; -import type { BlockHeader } from '@aztec/stdlib/tx'; +import type { BlockHeader, StateReference } from '@aztec/stdlib/tx'; import type { UInt64 } from '@aztec/stdlib/types'; import { toProofData } from './block-building-helpers.js'; @@ -42,25 +42,6 @@ export class CheckpointProvingState { // Inbox rolling hash before this checkpoint's messages (the previous checkpoint's end value; genesis is zero). // Threaded into the InboxParity circuit so the resulting checkpoint header rolling hash matches the proposer's. private readonly startInboxRollingHash: Fr, - // The snapshot and sibling path before the new l1 to l2 message subtree is inserted. - private readonly lastL1ToL2MessageTreeSnapshot: AppendOnlyTreeSnapshot, - private readonly lastL1ToL2MessageSubtreeRootSiblingPath: Tuple< - Fr, - typeof L1_TO_L2_MSG_SUBTREE_ROOT_SIBLING_PATH_LENGTH - >, - // The snapshot and sibling path after the new l1 to l2 message subtree is inserted. - private readonly newL1ToL2MessageTreeSnapshot: AppendOnlyTreeSnapshot, - private readonly newL1ToL2MessageSubtreeRootSiblingPath: Tuple< - Fr, - typeof L1_TO_L2_MSG_SUBTREE_ROOT_SIBLING_PATH_LENGTH - >, - // The checkpoint's messages padded to `MAX_L1_TO_L2_MSGS_PER_CHECKPOINT` (the first block's transitional bundle, - // inserted as a full subtree into the L1-to-L2 tree). - private readonly paddedL1ToL2Messages: Fr[], - // Message-bundle sponge over the checkpoint's real messages (real-count absorb). Equals the InboxParity proof's - // end sponge and the sponge the block roots accumulate, so it is threaded into non-first block roots as their - // inherited `startMsgSponge`. - private readonly checkpointMsgSponge: L1ToL2MessageSponge, public readonly epochNumber: number, /** Owner's liveness check. `verifyState()` returns false once this returns false. */ private readonly isAlive: () => boolean, @@ -71,41 +52,32 @@ export class CheckpointProvingState { this.firstBlockNumber = BlockNumber(headerOfLastBlockInPreviousCheckpoint.globalVariables.blockNumber + 1); } - /** The checkpoint's messages padded to the per-checkpoint cap (the first block's transitional bundle). */ - public getPaddedL1ToL2Messages(): Fr[] { - return this.paddedL1ToL2Messages; + /** The checkpoint's real L1-to-L2 messages (unpadded), consumed across its blocks. */ + public getL1ToL2Messages(): Fr[] { + return this.l1ToL2Messages; } - /** Number of real (non-padding) L1-to-L2 messages in the checkpoint — the sponge/InboxParity real-count. */ - public getNumRealL1ToL2Messages(): number { - return this.l1ToL2Messages.length; - } - - /** The message-bundle sponge over the checkpoint's real messages (real-count absorb) — inherited by non-first block roots. */ - public getCheckpointMsgSponge(): L1ToL2MessageSponge { - return this.checkpointMsgSponge; - } - - public startNewBlock( + public async startNewBlock( blockNumber: BlockNumber, timestamp: UInt64, totalNumTxs: number, lastArchiveTreeSnapshot: AppendOnlyTreeSnapshot, lastArchiveSiblingPath: Tuple, - ): BlockProvingState { + // The full state reference of the previous block (before this block's message bundle is appended). Feeds the + // msgs-only block root, whose zero-tx block carries no tx constants to pin the previous state. + previousState: StateReference, + // Per-block L1-to-L2 message state: the block's start snapshot (its parent's end), its own + // post-bundle end snapshot, the full-height frontier at the start index, and its own real message slice. + startL1ToL2MessageTreeSnapshot: AppendOnlyTreeSnapshot, + endL1ToL2MessageTreeSnapshot: AppendOnlyTreeSnapshot, + l1ToL2MessageFrontierHint: Tuple, + l1ToL2Messages: Fr[], + ): Promise { const index = Number(blockNumber) - Number(this.firstBlockNumber); if (index >= this.totalNumBlocks) { throw new Error(`Unable to start a new block at index ${index}. Expected at most ${this.totalNumBlocks} blocks.`); } - // If this is the first block, we use the snapshot and sibling path before the new l1 to l2 messages are inserted. - // Otherwise, we use the snapshot and sibling path after the new l1 to l2 messages are inserted, which will always - // happen in the first block. - const lastL1ToL2MessageTreeSnapshot = - index === 0 ? this.lastL1ToL2MessageTreeSnapshot : this.newL1ToL2MessageTreeSnapshot; - const lastL1ToL2MessageSubtreeRootSiblingPath = - index === 0 ? this.lastL1ToL2MessageSubtreeRootSiblingPath : this.newL1ToL2MessageSubtreeRootSiblingPath; - const startSpongeBlob = index === 0 ? SpongeBlob.init() : this.blocks[index - 1]?.getEndSpongeBlob(); if (!startSpongeBlob) { throw new Error( @@ -113,6 +85,19 @@ export class CheckpointProvingState { ); } + // Thread the message sponge across the checkpoint's blocks: each block starts from the + // previous block's end sponge (empty for the first block) and absorbs its own real slice. The block merge and + // checkpoint root circuits assert exactly this continuity (`right.start_msg_sponge == left.end_msg_sponge`, first + // block starts empty, merged end equals the InboxParity sponge), so the end sponge is computed eagerly here for + // the next block to inherit. Blocks must therefore be started in order, which the sequential per-block message + // appends already require. + const startMsgSponge = index === 0 ? L1ToL2MessageSponge.empty() : this.blocks[index - 1]?.getEndMsgSponge(); + if (!startMsgSponge) { + throw new Error('Cannot start a new block before the previous block in the checkpoint has been started.'); + } + const endMsgSponge = startMsgSponge.clone(); + await endMsgSponge.absorb(l1ToL2Messages); + const block = new BlockProvingState( index, blockNumber, @@ -121,10 +106,13 @@ export class CheckpointProvingState { timestamp, lastArchiveTreeSnapshot, lastArchiveSiblingPath, - lastL1ToL2MessageTreeSnapshot, - lastL1ToL2MessageSubtreeRootSiblingPath, - this.newL1ToL2MessageTreeSnapshot, - this.headerOfLastBlockInPreviousCheckpoint, + previousState, + startL1ToL2MessageTreeSnapshot, + endL1ToL2MessageTreeSnapshot, + l1ToL2MessageFrontierHint, + l1ToL2Messages, + startMsgSponge, + endMsgSponge, startSpongeBlob, this, ); @@ -178,7 +166,8 @@ export class CheckpointProvingState { return InboxParityPrivateInputs.fromMessages( this.l1ToL2Messages, this.startInboxRollingHash, - computeInHashFromL1ToL2Messages(this.l1ToL2Messages), + // Legacy in_hash is dead post-flip; the InboxParity pass-through hint carries zero (AZIP-22 Fast Inbox). + Fr.ZERO, this.constants.vkTreeRoot, this.constants.proverId, ); diff --git a/yarn-project/prover-client/src/orchestrator/checkpoint-sub-tree-orchestrator.test.ts b/yarn-project/prover-client/src/orchestrator/checkpoint-sub-tree-orchestrator.test.ts index 703aa7ec9b8b..e74aded283ae 100644 --- a/yarn-project/prover-client/src/orchestrator/checkpoint-sub-tree-orchestrator.test.ts +++ b/yarn-project/prover-client/src/orchestrator/checkpoint-sub-tree-orchestrator.test.ts @@ -1,10 +1,10 @@ import { MAX_L2_TO_L1_MSGS_PER_TX } from '@aztec/constants'; import { EpochNumber } from '@aztec/foundation/branded-types'; -import { padArrayEnd } from '@aztec/foundation/collection'; +import { padArrayEnd, sum } from '@aztec/foundation/collection'; import { Fr } from '@aztec/foundation/curves/bn254'; import { EthAddress } from '@aztec/foundation/eth-address'; import { createLogger } from '@aztec/foundation/log'; -import { ScopedL2ToL1Message, computeBlockOutHash } from '@aztec/stdlib/messaging'; +import { L1ToL2MessageSponge, ScopedL2ToL1Message, computeBlockOutHash } from '@aztec/stdlib/messaging'; import { makeScopedL2ToL1Message } from '@aztec/stdlib/testing'; import { TestContext, makeTestDeferredJobQueue } from '../mocks/test_context.js'; @@ -58,9 +58,9 @@ describe('prover/orchestrator/checkpoint-sub-tree', () => { try { const resultPromise = subTree.getSubTreeResult(); - for (const block of blocks) { + for (const [blockIndex, block] of blocks.entries()) { const { blockNumber, timestamp } = block.header.globalVariables; - await subTree.startNewBlock(blockNumber, timestamp, block.txs.length); + await subTree.startNewBlock(blockNumber, timestamp, block.txs.length, blockIndex === 0 ? l1ToL2Messages : []); if (block.txs.length > 0) { await subTree.addTxs(block.txs); } @@ -104,9 +104,9 @@ describe('prover/orchestrator/checkpoint-sub-tree', () => { try { const resultPromise = subTree.getSubTreeResult(); - for (const block of blocks) { + for (const [blockIndex, block] of blocks.entries()) { const { blockNumber, timestamp } = block.header.globalVariables; - await subTree.startNewBlock(blockNumber, timestamp, block.txs.length); + await subTree.startNewBlock(blockNumber, timestamp, block.txs.length, blockIndex === 0 ? l1ToL2Messages : []); if (block.txs.length > 0) { await subTree.addTxs(block.txs); } @@ -149,9 +149,9 @@ describe('prover/orchestrator/checkpoint-sub-tree', () => { try { const resultPromise = subTree.getSubTreeResult(); - for (const block of blocks) { + for (const [blockIndex, block] of blocks.entries()) { const { blockNumber, timestamp } = block.header.globalVariables; - await subTree.startNewBlock(blockNumber, timestamp, block.txs.length); + await subTree.startNewBlock(blockNumber, timestamp, block.txs.length, blockIndex === 0 ? l1ToL2Messages : []); if (block.txs.length > 0) { await subTree.addTxs(block.txs); } @@ -197,9 +197,9 @@ describe('prover/orchestrator/checkpoint-sub-tree', () => { try { const resultPromise = subTree.getSubTreeResult(); - for (const block of blocks) { + for (const [blockIndex, block] of blocks.entries()) { const { blockNumber, timestamp } = block.header.globalVariables; - await subTree.startNewBlock(blockNumber, timestamp, block.txs.length); + await subTree.startNewBlock(blockNumber, timestamp, block.txs.length, blockIndex === 0 ? l1ToL2Messages : []); if (block.txs.length > 0) { await subTree.addTxs(block.txs); } @@ -219,4 +219,96 @@ describe('prover/orchestrator/checkpoint-sub-tree', () => { await subTree.stop(); } }); + + it('slices L1-to-L2 messages per block across a multi-block checkpoint', async () => { + // A checkpoint whose messages span more than one block: the first block carries a bundle, a middle block + // carries none (txs only), and the last block carries a bundle with zero txs (a message-only block, proven by + // the msgs-only block root). The sub-tree must append each block's own slice at compact indices with + // contiguous, non-overlapping per-block snapshots, and thread the message sponge across the blocks. + // The sub-tree result surfaces post-merge top-level nodes (at most two, for the binary + // checkpoint root), not one output per block. + const l1ToL2MessagesPerBlock = [[new Fr(1001), new Fr(1002)], [], [new Fr(1003), new Fr(1004), new Fr(1005)]]; + const numBlocks = l1ToL2MessagesPerBlock.length; + const { constants, blocks, l1ToL2Messages, previousBlockHeader } = await context.makeCheckpointWithMessagesPerBlock( + l1ToL2MessagesPerBlock, + { numTxsPerBlock: [1, 1, 0] }, + ); + expect(l1ToL2Messages.length).toBe(5); + + const subTree = await CheckpointSubTreeOrchestrator.start( + context.worldState, + context.prover, + EthAddress.ZERO, + chonkCache, + EpochNumber(1), + false, + makeTestDeferredJobQueue(), + constants, + l1ToL2Messages, + Fr.ZERO, + numBlocks, + previousBlockHeader, + ); + try { + const resultPromise = subTree.getSubTreeResult(); + + for (const [blockIndex, block] of blocks.entries()) { + const { blockNumber, timestamp } = block.header.globalVariables; + await subTree.startNewBlock(blockNumber, timestamp, block.txs.length, l1ToL2MessagesPerBlock[blockIndex]); + if (block.txs.length > 0) { + await subTree.addTxs(block.txs); + } + await subTree.setBlockCompleted(blockNumber, block.header); + } + + const result = await resultPromise; + // Three block roots reduce to two top-level outputs: a block-merge over blocks 0-1 and block 2's msgs-only + // root. Merge public inputs span their range: is_first_block propagates from the left child, the start + // sponge/state come from the left child and the end sponge/state from the right. + const expectedOutputBlockRanges = [[0, 1], [2]]; + expect(result.blockProofOutputs).toHaveLength(expectedOutputBlockRanges.length); + + // Order the outputs by position in the checkpoint (the archive tree grows by one leaf per block). + const ordered = [...result.blockProofOutputs].sort( + (a, b) => a.inputs.previousArchive.nextAvailableLeafIndex - b.inputs.previousArchive.nextAvailableLeafIndex, + ); + + // Walk the outputs in order, asserting the L1-to-L2 message tree partitions cleanly into per-output slices, + // with each output's start snapshot equal to the previous output's end snapshot (the "threaded" per-block + // L1-to-L2 tree state). + const baseLeaf = ordered[0].inputs.startState.l1ToL2MessageTree.nextAvailableLeafIndex; + let expectedStartLeaf = baseLeaf; + // The message sponge threads across the checkpoint's blocks: the first block starts from the empty sponge and + // each block absorbs exactly its own slice (the block merge and checkpoint root circuits assert this + // continuity against the InboxParity sponge). + const expectedSponge = L1ToL2MessageSponge.empty(); + for (const [i, output] of ordered.entries()) { + const inputs = output.inputs; + const blockIndexes = expectedOutputBlockRanges[i]; + const startLeaf = inputs.startState.l1ToL2MessageTree.nextAvailableLeafIndex; + const endLeaf = inputs.endState.l1ToL2MessageTree.nextAvailableLeafIndex; + const sliceLen = sum(blockIndexes.map(b => l1ToL2MessagesPerBlock[b].length)); + + // Contiguous, non-overlapping slices: this output starts where the previous one ended (no gap/overlap). + expect(startLeaf).toBe(expectedStartLeaf); + // The tree grows by exactly the covered blocks' bundle sizes. + expect(endLeaf - startLeaf).toBe(sliceLen); + expectedStartLeaf = endLeaf; + + // Sponge continuity: this output starts from the previous one's end sponge and absorbs its blocks' slices. + expect(inputs.startMsgSponge.toBuffer()).toEqual(expectedSponge.toBuffer()); + for (const blockIndex of blockIndexes) { + await expectedSponge.absorb(l1ToL2MessagesPerBlock[blockIndex]); + } + expect(inputs.endMsgSponge.toBuffer()).toEqual(expectedSponge.toBuffer()); + } + + // Every message is accounted for with no gap or overlap across the checkpoint's blocks. + expect(expectedStartLeaf - baseLeaf).toBe(l1ToL2Messages.length); + // The last block's end sponge equals the checkpoint's InboxParity end sponge. + expect(result.inboxParityProof.inputs.endSponge.toBuffer()).toEqual(expectedSponge.toBuffer()); + } finally { + await subTree.stop(); + } + }); }); diff --git a/yarn-project/prover-client/src/orchestrator/checkpoint-sub-tree-orchestrator.ts b/yarn-project/prover-client/src/orchestrator/checkpoint-sub-tree-orchestrator.ts index 2afa4fd9dc78..6c12bcfae800 100644 --- a/yarn-project/prover-client/src/orchestrator/checkpoint-sub-tree-orchestrator.ts +++ b/yarn-project/prover-client/src/orchestrator/checkpoint-sub-tree-orchestrator.ts @@ -1,13 +1,10 @@ import type { SpongeBlob } from '@aztec/blob-lib/types'; import { type ARCHIVE_HEIGHT, - L1_TO_L2_MSG_SUBTREE_HEIGHT, - L1_TO_L2_MSG_SUBTREE_ROOT_SIBLING_PATH_LENGTH, - MAX_L1_TO_L2_MSGS_PER_CHECKPOINT, + L1_TO_L2_MSG_TREE_HEIGHT, NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH, } from '@aztec/constants'; import { BlockNumber, type EpochNumber } from '@aztec/foundation/branded-types'; -import { padArrayEnd } from '@aztec/foundation/collection'; import { Fr } from '@aztec/foundation/curves/bn254'; import { AbortError } from '@aztec/foundation/error'; import type { LoggerBindings } from '@aztec/foundation/log'; @@ -23,7 +20,7 @@ import type { ReadonlyWorldStateAccess, ServerCircuitProver, } from '@aztec/stdlib/interfaces/server'; -import { L1ToL2MessageSponge, appendL1ToL2MessagesToTree } from '@aztec/stdlib/messaging'; +import { appendL1ToL2MessagesToTree } from '@aztec/stdlib/messaging'; import type { ParityPublicInputs } from '@aztec/stdlib/parity'; import { type BaseRollupHints, @@ -49,10 +46,10 @@ import { inspect } from 'util'; import { buildHeaderFromCircuitOutputs, + getFrontierSiblingPath, getLastSiblingPath, getPublicChonkVerifierPrivateInputsFromTx, getRootTreeSiblingPath, - getSubtreeSiblingPath, getTreeSnapshot, insertSideEffectsAndBuildBaseRollupHints, validatePartialState, @@ -267,7 +264,7 @@ export class CheckpointSubTreeOrchestrator extends ProvingScheduler { @trackSpan('CheckpointSubTreeOrchestrator.startNewBlock', blockNumber => ({ [Attributes.BLOCK_NUMBER]: blockNumber, })) - public async startNewBlock(blockNumber: BlockNumber, timestamp: UInt64, totalNumTxs: number) { + public async startNewBlock(blockNumber: BlockNumber, timestamp: UInt64, totalNumTxs: number, l1ToL2Messages: Fr[]) { if (!this.provingState) { throw new Error('Empty proving state. The checkpoint sub-tree has not been started.'); } @@ -291,12 +288,31 @@ export class CheckpointSubTreeOrchestrator extends ProvingScheduler { const lastArchiveTreeSnapshot = await getTreeSnapshot(MerkleTreeId.ARCHIVE, db); const lastArchiveSiblingPath = await getRootTreeSiblingPath(MerkleTreeId.ARCHIVE, db); - const blockProvingState = this.provingState.startNewBlock( + // The previous block's full state reference, captured before this block's message bundle is appended. Feeds the + // msgs-only block root, whose zero-tx block carries no tx constants to pin the previous state. + const previousState = await db.getStateReference(); + + // Streaming Inbox: insert this block's own real message slice at compact indices, capturing + // the start snapshot + full-height frontier before the append and the block's own post-bundle end snapshot after. + const startL1ToL2Snapshot = await getTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, db); + const l1ToL2FrontierHint = assertLength( + await getFrontierSiblingPath(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, db), + L1_TO_L2_MSG_TREE_HEIGHT, + ); + await appendL1ToL2MessagesToTree(db, l1ToL2Messages); + const endL1ToL2Snapshot = await getTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, db); + + const blockProvingState = await this.provingState.startNewBlock( blockNumber, timestamp, totalNumTxs, lastArchiveTreeSnapshot, lastArchiveSiblingPath, + previousState, + startL1ToL2Snapshot, + endL1ToL2Snapshot, + l1ToL2FrontierHint, + l1ToL2Messages, ); // Because `addTxs` won't be called for a block without txs, and that's where the sponge blob state is computed, @@ -312,8 +328,8 @@ export class CheckpointSubTreeOrchestrator extends ProvingScheduler { // A block with no txs has no base or merge proof whose completion would enqueue its block root, // and parity now gates the checkpoint root rather than the first block root, so no other callback - // fires it. Enqueue it here. Only a first block may be empty (the block proving state rejects - // any other), so this always drives the empty-tx first block root. + // fires it. Enqueue it here. This drives the empty-tx first block root, or the msgs-only block root + // for a non-first zero-tx block carrying a message bundle. this.checkAndEnqueueBlockRootRollup(blockProvingState); } } @@ -521,21 +537,9 @@ export class CheckpointSubTreeOrchestrator extends ProvingScheduler { // Get archive sibling path before any block in this checkpoint lands. const lastArchiveSiblingPath = await getLastSiblingPath(MerkleTreeId.ARCHIVE, db); - // Insert all the l1 to l2 messages into the db. Get the states before and after the insertion. - const { - lastL1ToL2MessageTreeSnapshot, - lastL1ToL2MessageSubtreeRootSiblingPath, - newL1ToL2MessageTreeSnapshot, - newL1ToL2MessageSubtreeRootSiblingPath, - } = await this.updateL1ToL2MessageTree(l1ToL2Messages, db); - - // The first block inserts the whole checkpoint's messages as a full padded subtree into the L1-to-L2 tree, so keep - // the padded array for the block-root bundle. The message sponge, however, absorbs only the real messages - // (real-count), so it matches the checkpoint's single InboxParity proof; non-first block roots inherit this sponge. - const paddedL1ToL2Messages = padArrayEnd(l1ToL2Messages, Fr.ZERO, MAX_L1_TO_L2_MSGS_PER_CHECKPOINT); - const checkpointMsgSponge = L1ToL2MessageSponge.empty(); - await checkpointMsgSponge.absorb(l1ToL2Messages); - + // Streaming Inbox: messages are inserted per block in `startNewBlock`, not the whole + // checkpoint up front. The message sponge is likewise threaded per block (each block's start sponge is the + // previous block's end), so the last block's end sponge matches the checkpoint's single InboxParity proof. this.provingState = new CheckpointProvingState( /* index */ 0, constants, @@ -544,12 +548,6 @@ export class CheckpointSubTreeOrchestrator extends ProvingScheduler { lastArchiveSiblingPath, l1ToL2Messages, startInboxRollingHash, - lastL1ToL2MessageTreeSnapshot, - lastL1ToL2MessageSubtreeRootSiblingPath, - newL1ToL2MessageTreeSnapshot, - newL1ToL2MessageSubtreeRootSiblingPath, - paddedL1ToL2Messages, - checkpointMsgSponge, Number(this.epochNumber), /* isAlive */ () => !this.cancelled, /* onReject */ reason => this.subTreeResult.reject(new Error(reason)), @@ -562,30 +560,6 @@ export class CheckpointSubTreeOrchestrator extends ProvingScheduler { // ---------------- private: per-block proof orchestration ---------------- - private async updateL1ToL2MessageTree(l1ToL2Messages: Fr[], db: MerkleTreeWriteOperations) { - const lastL1ToL2MessageTreeSnapshot = await getTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, db); - const lastL1ToL2MessageSubtreeRootSiblingPath = assertLength( - await getSubtreeSiblingPath(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, L1_TO_L2_MSG_SUBTREE_HEIGHT, db), - L1_TO_L2_MSG_SUBTREE_ROOT_SIBLING_PATH_LENGTH, - ); - - // Update the local trees to include the new l1 to l2 messages. - await appendL1ToL2MessagesToTree(db, l1ToL2Messages); - - const newL1ToL2MessageTreeSnapshot = await getTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, db); - const newL1ToL2MessageSubtreeRootSiblingPath = assertLength( - await getSubtreeSiblingPath(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, L1_TO_L2_MSG_SUBTREE_HEIGHT, db), - L1_TO_L2_MSG_SUBTREE_ROOT_SIBLING_PATH_LENGTH, - ); - - return { - lastL1ToL2MessageTreeSnapshot, - lastL1ToL2MessageSubtreeRootSiblingPath, - newL1ToL2MessageTreeSnapshot, - newL1ToL2MessageSubtreeRootSiblingPath, - }; - } - // Updates the merkle trees for a transaction. The first enqueued job for a transaction. @trackSpan('CheckpointSubTreeOrchestrator.prepareBaseRollupInputs', tx => ({ [Attributes.TX_HASH]: tx.hash.toString(), diff --git a/yarn-project/prover-client/src/orchestrator/top-tree-orchestrator.test.ts b/yarn-project/prover-client/src/orchestrator/top-tree-orchestrator.test.ts index 74a3ad8e554c..1d7e8aab5805 100644 --- a/yarn-project/prover-client/src/orchestrator/top-tree-orchestrator.test.ts +++ b/yarn-project/prover-client/src/orchestrator/top-tree-orchestrator.test.ts @@ -7,7 +7,7 @@ import { createLogger } from '@aztec/foundation/log'; import { promiseWithResolvers } from '@aztec/foundation/promise'; import { retryUntil } from '@aztec/foundation/retry'; import { sleep } from '@aztec/foundation/sleep'; -import { ScopedL2ToL1Message, computeEpochOutHash } from '@aztec/stdlib/messaging'; +import { ScopedL2ToL1Message, accumulateInboxRollingHash, computeEpochOutHash } from '@aztec/stdlib/messaging'; import { makeScopedL2ToL1Message } from '@aztec/stdlib/testing'; import { TestContext, makeTestDeferredJobQueue } from '../mocks/test_context.js'; @@ -42,20 +42,14 @@ describe('prover/orchestrator/top-tree', () => { await context.cleanup(); }); + /** The checkpoint fixture shape shared by `makeCheckpoint` and `makeCheckpointWithMessagesPerBlock`. */ + type CheckpointFixture = Awaited>; + /** - * Drives a single checkpoint through `CheckpointSubTreeOrchestrator` and returns - * the assembled `CheckpointTopTreeData` plus the originating checkpoint metadata. + * Drives a checkpoint fixture through `CheckpointSubTreeOrchestrator`, feeding block `i` the message slice + * `messagesPerBlock[i]`, and returns the assembled `CheckpointTopTreeData` plus the originating fixture. */ - async function driveSubTree(numBlocks: number, numTxsPerBlock: number, numL1ToL2Messages = 0, numL2ToL1Messages = 0) { - const fixture = await context.makeCheckpoint(numBlocks, { - numTxsPerBlock, - numL1ToL2Messages, - makeProcessedTxOpts: - numL2ToL1Messages > 0 - ? () => ({ privateOnly: false, avmAccumulatedData: { l2ToL1Msgs: makeL2ToL1Messages(numL2ToL1Messages) } }) - : undefined, - }); - + async function driveFixture(fixture: CheckpointFixture, messagesPerBlock: Fr[][]) { const subTree = await CheckpointSubTreeOrchestrator.start( context.worldState, context.prover, @@ -66,15 +60,15 @@ describe('prover/orchestrator/top-tree', () => { makeTestDeferredJobQueue(), fixture.constants, fixture.l1ToL2Messages, - Fr.ZERO, - numBlocks, + fixture.startInboxRollingHash, + fixture.blocks.length, fixture.previousBlockHeader, ); const resultPromise = subTree.getSubTreeResult(); - for (const block of fixture.blocks) { + for (const [blockIndex, block] of fixture.blocks.entries()) { const { blockNumber, timestamp } = block.header.globalVariables; - await subTree.startNewBlock(blockNumber, timestamp, block.txs.length); + await subTree.startNewBlock(blockNumber, timestamp, block.txs.length, messagesPerBlock[blockIndex]); if (block.txs.length > 0) { await subTree.addTxs(block.txs); } @@ -98,6 +92,33 @@ describe('prover/orchestrator/top-tree', () => { return { fixture, topTreeData }; } + /** + * Builds a checkpoint via `makeCheckpoint` (every message in the first block) and drives it through + * `CheckpointSubTreeOrchestrator`, returning the assembled `CheckpointTopTreeData` plus the fixture. + */ + async function driveSubTree(numBlocks: number, numTxsPerBlock: number, numL1ToL2Messages = 0, numL2ToL1Messages = 0) { + const fixture = await context.makeCheckpoint(numBlocks, { + numTxsPerBlock, + numL1ToL2Messages, + makeProcessedTxOpts: + numL2ToL1Messages > 0 + ? () => ({ privateOnly: false, avmAccumulatedData: { l2ToL1Msgs: makeL2ToL1Messages(numL2ToL1Messages) } }) + : undefined, + }); + const messagesPerBlock = fixture.blocks.map((_, i) => (i === 0 ? fixture.l1ToL2Messages : [])); + return await driveFixture(fixture, messagesPerBlock); + } + + /** + * Like {@link driveSubTree} but distributes the checkpoint's messages across its blocks (streaming Inbox): + * block `i` carries `l1ToL2MessagesPerBlock[i]` as its own slice. A zero-tx entry in `numTxsPerBlock` + * whose slice is non-empty produces a message-only block, proven by the msgs-only block root. + */ + async function driveSubTreeWithMessageSlices(l1ToL2MessagesPerBlock: Fr[][], numTxsPerBlock: number[]) { + const fixture = await context.makeCheckpointWithMessagesPerBlock(l1ToL2MessagesPerBlock, { numTxsPerBlock }); + return await driveFixture(fixture, l1ToL2MessagesPerBlock); + } + it('produces an epoch proof for a single-checkpoint, single-block, single-tx epoch', async () => { const { topTreeData } = await driveSubTree(1, 1); const challenges = await context.getFinalBlobChallenges(); @@ -164,6 +185,39 @@ describe('prover/orchestrator/top-tree', () => { } }); + it('produces an epoch proof when messages span blocks, including a message-only block', async () => { + // The streaming Inbox shapes, driven through the entire proving DAG at simulated-circuit + // fidelity: a checkpoint whose messages land in a non-first block, a zero-tx message-only block (proven by the + // msgs-only block root), a block merge above the three block roots, the two-input checkpoint root over per-block + // bundles, the single-block checkpoint root for the follow-on checkpoints, the checkpoint merge asserting inbox + // rolling-hash continuity across a message-carrying boundary, and the root rollup exposing the epoch's + // rolling-hash range. + const ckpt1Slices = [[new Fr(0x100), new Fr(0x101)], [], [new Fr(0x102), new Fr(0x103), new Fr(0x104)]]; + const a = await driveSubTreeWithMessageSlices(ckpt1Slices, [1, 1, 0]); + // A single-block checkpoint with messages after a message-carrying checkpoint: its parity chain starts from + // checkpoint 0's (non-zero) end rolling hash. + const b = await driveSubTree(1, 1, 2); + // A message-less checkpoint after message-carrying ones: the rolling hash must pass through unchanged. + const c = await driveSubTree(1, 1); + const challenges = await context.getFinalBlobChallenges(); + + const topTree = new TopTreeOrchestrator(context.prover, EthAddress.ZERO, makeTestDeferredJobQueue()); + try { + const result = await topTree.prove(EpochNumber(1), 3, challenges, [a.topTreeData, b.topTreeData, c.topTreeData]); + expect(result.proof).toBeDefined(); + expect(result.publicInputs).toBeDefined(); + + // The epoch's rolling-hash range binds the exact message sequence consumed, in block order, across all three + // checkpoints; L1 validates this range against the Inbox when the proof lands. + const epochMessages = [...a.fixture.l1ToL2Messages, ...b.fixture.l1ToL2Messages]; + expect(epochMessages.length).toBe(7); // sanity: the fixtures really did carry messages + expect(result.publicInputs.previousInboxRollingHash).toEqual(Fr.ZERO); + expect(result.publicInputs.endInboxRollingHash).toEqual(accumulateInboxRollingHash(Fr.ZERO, epochMessages)); + } finally { + await topTree.stop(); + } + }, 300_000); + it('pipelines: starts ckpt0 root rollup before ckpt1 sub-tree resolves', async () => { // Drive both sub-trees synchronously (still no top tree running). const a = await driveSubTree(1, 1); diff --git a/yarn-project/prover-client/src/test/bb_prover_full_rollup.test.ts b/yarn-project/prover-client/src/test/bb_prover_full_rollup.test.ts index 4dd1b8136718..26c2b81c50bf 100644 --- a/yarn-project/prover-client/src/test/bb_prover_full_rollup.test.ts +++ b/yarn-project/prover-client/src/test/bb_prover_full_rollup.test.ts @@ -1,5 +1,5 @@ import { BBNativeRollupProver, type BBProverConfig } from '@aztec/bb-prover'; -import { NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, PAIRING_POINTS_SIZE } from '@aztec/constants'; +import { MAX_L1_TO_L2_MSGS_PER_BLOCK, PAIRING_POINTS_SIZE } from '@aztec/constants'; import { EpochNumber } from '@aztec/foundation/branded-types'; import { timesAsync } from '@aztec/foundation/collection'; import { parseBooleanEnv } from '@aztec/foundation/config'; @@ -52,7 +52,8 @@ describe('prover/bb_prover/full-rollup', () => { const checkpoints = await timesAsync(numCheckpoints, () => context.makeCheckpoint(numBlockPerCheckpoint, { numTxsPerBlock, - numL1ToL2Messages: NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, + // makeCheckpoint puts the whole message list into the first block, so cap at the per-block limit. + numL1ToL2Messages: MAX_L1_TO_L2_MSGS_PER_BLOCK, makeProcessedTxOpts: (_, txIndex) => ({ privateOnly: txIndex % 2 === 0 }), }), ); @@ -93,7 +94,7 @@ describe('prover/bb_prover/full-rollup', () => { const { blockNumber, timestamp } = header.globalVariables; log.info(`Starting new block #${blockNumber}`); - await subTree.startNewBlock(blockNumber, timestamp, txs.length); + await subTree.startNewBlock(blockNumber, timestamp, txs.length, i === 0 ? l1ToL2Messages : []); if (txs.length > 0) { await subTree.addTxs(txs); } diff --git a/yarn-project/prover-client/src/test/regenerate_rollup_sample_inputs.test.ts b/yarn-project/prover-client/src/test/regenerate_rollup_sample_inputs.test.ts index 3a7083dc5f2e..b56bc3626d94 100644 --- a/yarn-project/prover-client/src/test/regenerate_rollup_sample_inputs.test.ts +++ b/yarn-project/prover-client/src/test/regenerate_rollup_sample_inputs.test.ts @@ -1,4 +1,4 @@ -import { NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants'; +import { MAX_L1_TO_L2_MSGS_PER_BLOCK } from '@aztec/constants'; import { EpochNumber } from '@aztec/foundation/branded-types'; import { timesAsync } from '@aztec/foundation/collection'; import { Fr } from '@aztec/foundation/curves/bn254'; @@ -44,7 +44,9 @@ describeOrSkip('prover/regenerate-rollup-sample-inputs', () => { dump: CircuitName[]; } - const withMessages = NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP; + // `makeCheckpoint` puts the scenario's whole message list into the first block, so the most a + // scenario can carry is one full per-block bundle, not the per-checkpoint cap. + const withMessages = MAX_L1_TO_L2_MSGS_PER_BLOCK; const scenarios: Scenario[] = [ { @@ -133,7 +135,7 @@ describeOrSkip('prover/regenerate-rollup-sample-inputs', () => { const { header, txs } = blocks[i]; const { blockNumber, timestamp } = header.globalVariables; - await subTree.startNewBlock(blockNumber, timestamp, txs.length); + await subTree.startNewBlock(blockNumber, timestamp, txs.length, i === 0 ? l1ToL2Messages : []); if (txs.length > 0) { await subTree.addTxs(txs); } diff --git a/yarn-project/prover-node/src/job/checkpoint-prover.ts b/yarn-project/prover-node/src/job/checkpoint-prover.ts index 10c29c0caf8a..5518bab686a7 100644 --- a/yarn-project/prover-node/src/job/checkpoint-prover.ts +++ b/yarn-project/prover-node/src/job/checkpoint-prover.ts @@ -1,6 +1,5 @@ -import { type ARCHIVE_HEIGHT, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants'; +import type { ARCHIVE_HEIGHT } from '@aztec/constants'; import { BlockNumber, type EpochNumber, type SlotNumber } from '@aztec/foundation/branded-types'; -import { padArrayEnd } from '@aztec/foundation/collection'; import { Fr } from '@aztec/foundation/curves/bn254'; import type { EthAddress } from '@aztec/foundation/eth-address'; import type { Logger } from '@aztec/foundation/log'; @@ -374,21 +373,31 @@ export class CheckpointProver { } } + // Streaming Inbox: the checkpoint's messages are consumed contiguously across its blocks; + // each block's slice runs from its parent block's L1-to-L2 leaf count to its own (compact indices make leaf + // count equal cumulative message count). + const l1ToL2LeafCount = (block: L2Block) => Number(block.header.state.l1ToL2MessageTree.nextAvailableLeafIndex); + const checkpointStartLeafCount = l1ToL2LeafCount(this.checkpoint.blocks.at(-1)!) - this.l1ToL2Messages.length; + for (let blockIndex = 0; blockIndex < this.checkpoint.blocks.length; blockIndex++) { const blockTimer = new Timer(); const block = this.checkpoint.blocks[blockIndex]; const globalVariables = block.header.globalVariables; const blockTxs = this.getTxsForBlock(block, txs); - await this.subTree.startNewBlock(block.number, globalVariables.timestamp, blockTxs.length); + const prevLeafCount = + blockIndex === 0 ? checkpointStartLeafCount : l1ToL2LeafCount(this.checkpoint.blocks[blockIndex - 1]); + const blockMessages = this.l1ToL2Messages.slice( + prevLeafCount - checkpointStartLeafCount, + l1ToL2LeafCount(block) - checkpointStartLeafCount, + ); + + await this.subTree.startNewBlock(block.number, globalVariables.timestamp, blockTxs.length, blockMessages); if (signal.aborted) { return; } - const db = await this.createFork( - BlockNumber(block.number - 1), - blockIndex === 0 ? this.l1ToL2Messages : undefined, - ); + const db = await this.createFork(BlockNumber(block.number - 1), blockMessages); try { if (signal.aborted) { return; @@ -533,19 +542,10 @@ export class CheckpointProver { return processedTxs; } - private async createFork(blockNumber: BlockNumber, l1ToL2Messages: Fr[] | undefined) { + private async createFork(blockNumber: BlockNumber, l1ToL2Messages: Fr[]) { const db = await this.deps.dbProvider.fork(blockNumber); - - if (l1ToL2Messages !== undefined) { - const l1ToL2MessagesPadded = padArrayEnd( - l1ToL2Messages, - Fr.ZERO, - NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, - 'Too many L1 to L2 messages', - ); - await db.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2MessagesPadded); - } - + // Append the block's real message leaves unpadded at compact indices. + await db.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2Messages); return db; } } diff --git a/yarn-project/prover-node/src/prover-node.test.ts b/yarn-project/prover-node/src/prover-node.test.ts index 1742953aa752..fe66ecf44b6c 100644 --- a/yarn-project/prover-node/src/prover-node.test.ts +++ b/yarn-project/prover-node/src/prover-node.test.ts @@ -61,6 +61,9 @@ describe('ProverNode', () => { l2BlockSource.getGenesisBlockHash.mockReturnValue('0x00' as any); l2BlockSource.getL1Constants.mockResolvedValue(l1Constants); l2BlockSource.getL2Tips.mockResolvedValue({} as L2Tips); + // Registering a checkpoint reads the consumed messages as a compact leaf-count range; these tests + // exercise dispatch and pruning, not message content, so an empty range suffices. + l1ToL2MessageSource.getL1ToL2MessagesBetweenLeafCounts.mockResolvedValue([]); publisherFactory.create.mockResolvedValue(publisher); proverNode = new TestProverNode( @@ -173,7 +176,7 @@ describe('ProverNode', () => { // getBlockData also feeds collectRegisterData when the rebuild re-registers, so it carries a header too. l2BlockSource.getBlockData.mockResolvedValue({ checkpointNumber: CheckpointNumber(2), - header: { lastArchive: { root: Fr.ZERO } }, + header: { lastArchive: { root: Fr.ZERO }, state: { l1ToL2MessageTree: { nextAvailableLeafIndex: 0 } } }, } as any); await proverNode.handleBlockStreamEvent({ type: 'chain-pruned', @@ -691,9 +694,9 @@ describe('ProverNode', () => { l2BlockSource.getBlockNumber.mockResolvedValue(undefined); setupRegistrationSuccess(); // getBlockData returns a header that lets isEpochFullyProven bail out as "not proven" - // and supplies a lastArchive.root for collectRegisterData. + // and supplies a lastArchive.root plus an L1-to-L2 leaf count for collectRegisterData. l2BlockSource.getBlockData.mockResolvedValue({ - header: { lastArchive: { root: Fr.ZERO } }, + header: { lastArchive: { root: Fr.ZERO }, state: { l1ToL2MessageTree: { nextAvailableLeafIndex: 0 } } }, } as any); } @@ -707,7 +710,7 @@ describe('ProverNode', () => { worldState.syncImmediate.mockResolvedValue(undefined as any); l1ToL2MessageSource.getL1ToL2Messages.mockResolvedValue([]); l2BlockSource.getBlockData.mockResolvedValue({ - header: { lastArchive: { root: Fr.ZERO } }, + header: { lastArchive: { root: Fr.ZERO }, state: { l1ToL2MessageTree: { nextAvailableLeafIndex: 0 } } }, } as any); worldState.getSnapshot.mockReturnValue({ getTreeInfo: () => Promise.resolve({ size: 1n }), @@ -725,7 +728,15 @@ describe('ProverNode', () => { number: CheckpointNumber(checkpointNumber), header: { slotNumber: SlotNumber(slot), inboxRollingHash: Fr.ZERO }, archive: { root: archiveRoot }, - blocks: [{ number: blockNumber, header: { hash: () => Promise.resolve('0x01') } }], + blocks: [ + { + number: blockNumber, + header: { + hash: () => Promise.resolve('0x01'), + state: { l1ToL2MessageTree: { nextAvailableLeafIndex: 0 } }, + }, + }, + ], hash: () => new Fr(checkpointNumber), } as unknown as Checkpoint; } diff --git a/yarn-project/prover-node/src/prover-node.ts b/yarn-project/prover-node/src/prover-node.ts index 6afb6bc29f53..5a8cba07a12b 100644 --- a/yarn-project/prover-node/src/prover-node.ts +++ b/yarn-project/prover-node/src/prover-node.ts @@ -350,9 +350,15 @@ export class ProverNode implements L2BlockStreamEventHandler, ProverNodeApi, Tra ): Promise { const previousBlockNumber = BlockNumber(checkpoint.blocks[0].number - 1); const previousBlockHeader = await this.gatherPreviousBlockHeader(previousBlockNumber); - const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(checkpoint.number); - const previousInboxRollingHash = await this.gatherPreviousInboxRollingHash(checkpoint.number); const lastBlock = checkpoint.blocks.at(-1)!; + // Streaming Inbox: the checkpoint's consumed messages are those between the parent + // checkpoint's consumed position and this checkpoint's last block, as a compact leaf-count range. The prover + // slices them per block. + const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2MessagesBetweenLeafCounts( + BigInt(previousBlockHeader.state.l1ToL2MessageTree.nextAvailableLeafIndex), + BigInt(lastBlock.header.state.l1ToL2MessageTree.nextAvailableLeafIndex), + ); + const previousInboxRollingHash = await this.gatherPreviousInboxRollingHash(checkpoint.number); const lastBlockHash = await lastBlock.header.hash(); await this.worldState.syncImmediate(lastBlock.number, lastBlockHash); const previousArchiveSiblingPath = await getLastSiblingPath( diff --git a/yarn-project/sequencer-client/src/publisher/l1_publisher.integration.test.ts b/yarn-project/sequencer-client/src/publisher/l1_publisher.integration.test.ts index 31f948406db4..1d15418c5d42 100644 --- a/yarn-project/sequencer-client/src/publisher/l1_publisher.integration.test.ts +++ b/yarn-project/sequencer-client/src/publisher/l1_publisher.integration.test.ts @@ -1,4 +1,5 @@ import type { ArchiverDataSource } from '@aztec/archiver'; +import { MockL1ToL2MessageSource } from '@aztec/archiver/test'; import { AztecAddress } from '@aztec/aztec.js/addresses'; import { Fr } from '@aztec/aztec.js/fields'; import { createLogger } from '@aztec/aztec.js/log'; @@ -13,10 +14,10 @@ import { } from '@aztec/blob-lib'; import { GENESIS_ARCHIVE_ROOT, + MAX_L1_TO_L2_MSGS_PER_CHECKPOINT, MAX_NULLIFIERS_PER_TX, MAX_PROCESSABLE_L2_GAS, MAX_TX_DA_GAS, - NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, } from '@aztec/constants'; import { EpochCache } from '@aztec/epoch-cache'; import { createEthereumChain } from '@aztec/ethereum/chain'; @@ -47,7 +48,7 @@ import { retryUntil } from '@aztec/foundation/retry'; import { sleep } from '@aztec/foundation/sleep'; import { hexToBuffer } from '@aztec/foundation/string'; import { TestDateProvider } from '@aztec/foundation/timer'; -import { RollupAbi } from '@aztec/l1-artifacts'; +import { InboxAbi, RollupAbi } from '@aztec/l1-artifacts'; import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree'; import { ProtocolContractsList, protocolContractsHash } from '@aztec/protocol-contracts'; import { LightweightCheckpointBuilder } from '@aztec/prover-client/light'; @@ -86,7 +87,7 @@ import { NativeWorldStateService, ServerWorldStateSynchronizer, type WorldStateC import { beforeEach, describe, expect, it, jest } from '@jest/globals'; import { type MockProxy, mock } from 'jest-mock-extended'; -import { type Address, encodeFunctionData, getAbiItem, getAddress, multicall3Abi } from 'viem'; +import { type Address, encodeFunctionData, getAbiItem, getAddress, getContract, multicall3Abi } from 'viem'; import { type PrivateKeyAccount, privateKeyToAccount } from 'viem/accounts'; import { foundry } from 'viem/chains'; @@ -111,7 +112,8 @@ const logger = createLogger('integration_l1_publisher'); // depending on @aztec/aztec-node, which would create a sequencer-client <-> aztec-node cycle. const config: SequencerClientConfig & L1ContractsConfig = { ...getL1ContractsConfigEnvVars(), ...getConfigEnvVars() }; -// Must exceed the inbox lag (network default 2) so at least one checkpoint consumes a real L1->L2 message. +// Several consecutive checkpoints, each consuming the L1->L2 messages sent while it was being built, so real +// messages are genuinely consumed and validated on L1 (AZIP-22 Fast Inbox). const numberOfConsecutiveBlocks = 3; jest.setTimeout(1000000); @@ -138,6 +140,11 @@ describe('L1Publisher integration', () => { let builderDb: NativeWorldStateService; + // Backs the blockSource mock's streaming L1->L2 message queries. The world-state synchronizer reconstructs each + // block's consumed message bundle from Inbox buckets (AZIP-22 Fast Inbox) when it syncs a block back, so the test + // registers one bucket per published block here (see buildAndPublishBlock). + let messageSource: MockL1ToL2MessageSource; + // The header of the last block let prevHeader: BlockHeader; @@ -274,6 +281,20 @@ describe('L1Publisher integration', () => { checkpointNumber: CheckpointNumber.ZERO, indexWithinCheckpoint: IndexWithinCheckpoint(0), }; + // Seed the genesis sentinel bucket (seq 0, no messages) so the world-state synchronizer can resolve a + // totalMsgCount of 0 to a bucket when reconstructing the first block's message bundle. + messageSource = new MockL1ToL2MessageSource(0); + messageSource.setInboxBucket( + { + seq: 0n, + inboxRollingHash: Fr.ZERO, + totalMsgCount: 0n, + timestamp: 0n, + msgCount: 0, + lastMessageIndex: 0n, + }, + [], + ); blockSource = mock({ getBlocks(query: BlocksQuery) { if (!('from' in query)) { @@ -349,6 +370,14 @@ describe('L1Publisher integration', () => { getBlockNumber(): Promise { return Promise.resolve(BlockNumber(blocks.at(-1)?.number ?? BlockNumber.ZERO)); }, + // Streaming L1->L2 message reconstruction (AZIP-22 Fast Inbox): the world-state synchronizer resolves each + // block's consumed message bundle from the Inbox buckets registered per published block in buildAndPublishBlock. + getInboxBucketByTotalMsgCount(totalMsgCount: bigint) { + return messageSource.getInboxBucketByTotalMsgCount(totalMsgCount); + }, + getL1ToL2MessagesBetweenBuckets(fromExclusive: bigint, toInclusive: bigint) { + return messageSource.getL1ToL2MessagesBetweenBuckets(fromExclusive, toInclusive); + }, }); const worldStateConfig: WorldStateConfig = { @@ -444,13 +473,16 @@ describe('L1Publisher integration', () => { /** * Build a checkpoint with a single block using the LightweightCheckpointBuilder. - * This properly computes all checkpoint header fields (blobsHash, blockHeadersHash, inHash, epochOutHash, etc.) + * This properly computes all checkpoint header fields (blobsHash, blockHeadersHash, inHash, inboxRollingHash, + * epochOutHash, etc.). `previousInboxRollingHash` is the previous checkpoint's rolling hash (zero at genesis), so + * the header's `inboxRollingHash` continues the on-chain Inbox chain over `l1ToL2Messages`. */ const buildCheckpoint = async ( globalVariables: GlobalVariables, txs: ProcessedTx[], l1ToL2Messages: Fr[], previousCheckpointOutHashes: Fr[] = [], + previousInboxRollingHash: Fr = Fr.ZERO, ): Promise => { await worldStateSynchronizer.syncImmediate(); const tempFork = await worldStateSynchronizer.fork(BlockNumber(globalVariables.blockNumber - 1)); @@ -467,16 +499,15 @@ describe('L1Publisher integration', () => { // Test uses 1-block-per-checkpoint const checkpointNumber = CheckpointNumber.fromBlockNumber(globalVariables.blockNumber); - const builder = await LightweightCheckpointBuilder.startNewCheckpoint( + const builder = LightweightCheckpointBuilder.startNewCheckpoint( checkpointNumber, checkpointConstants, - l1ToL2Messages, previousCheckpointOutHashes, - Fr.ZERO, + previousInboxRollingHash, tempFork, ); - await builder.addBlock(globalVariables, txs, { insertTxsEffects: true }); + await builder.addBlock(globalVariables, txs, l1ToL2Messages, { insertTxsEffects: true }); const checkpoint = await builder.completeCheckpoint(); await tempFork.close(); @@ -486,7 +517,8 @@ describe('L1Publisher integration', () => { const buildSingleCheckpoint = async ( opts: { l1ToL2Messages?: Fr[]; blockNumber?: BlockNumber; slot?: SlotNumber } = {}, ) => { - const l1ToL2Messages = opts.l1ToL2Messages ?? new Array(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).fill(Fr.ZERO); + // By default a single checkpoint consumes no Inbox messages (bucketHint 0 against the genesis bucket). + const l1ToL2Messages = opts.l1ToL2Messages ?? []; const txs = await Promise.all([makeProcessedTx(0x1000), makeProcessedTx(0x2000)]); const ts = (await l1Client.getBlock()).timestamp; @@ -503,7 +535,6 @@ describe('L1Publisher integration', () => { new GasFees(0, await rollup.getManaMinFeeAt(timestamp, true)), ); const checkpoint = await buildCheckpoint(globalVariables, txs, l1ToL2Messages); - blockSource.getL1ToL2Messages.mockResolvedValueOnce(l1ToL2Messages); return { checkpoint, l1ToL2Messages }; }; @@ -517,10 +548,8 @@ describe('L1Publisher integration', () => { describe('block building', () => { beforeEach(async () => { - // This suite proposes consecutive checkpoints and models the inbox lag by hand (a checkpoint - // consumes the L1->L2 messages sent inboxLag checkpoints earlier -- see the shift register in - // buildAndPublishBlock). It inherits the network default lag and proposes enough checkpoints - // that a real message is actually consumed and validated on L1. + // This suite proposes consecutive checkpoints, each consuming the streaming-Inbox messages sent while it was + // being built (AZIP-22 Fast Inbox), so real messages are genuinely consumed and validated on L1. await setup(); }); @@ -535,30 +564,33 @@ describe('L1Publisher integration', () => { '0x1647b194c649f5dd01d7c832f89b0f496043c9150797923ea89e93d5ac619a93', ); - // The deployed rollup consumes L1->L2 messages with a lag of `inboxLag` checkpoints: a message - // inserted while building checkpoint N is only consumable at checkpoint N + inboxLag. Model that - // with a shift register so a real message is genuinely consumed and validated on L1. - const inboxLag = getL1ContractsConfigEnvVars().inboxLag; - const messagesInFlight: Fr[][] = Array.from({ length: inboxLag }, () => []); + // Streaming Inbox consumption (AZIP-22 Fast Inbox): each checkpoint consumes every message sent so far, so its + // header rolling hash continues the previous checkpoint's and matches the Inbox's current bucket. Consuming + // through the newest bucket trivially satisfies the mandatory-consumption assert. A checkpoint's messages are + // sent one per L1 block, so they span several Inbox buckets; the checkpoint's consumed bucket is the newest, + // whose sequence we read straight off the deployed Inbox and mirror into messageSource so the propose bucket + // hint can be looked up by the checkpoint's cumulative message count. + const inbox = getContract({ + address: getAddress(l1ContractAddresses.inboxAddress.toString()), + abi: InboxAbi, + client: l1Client, + }); + let previousInboxRollingHash = Fr.ZERO; const blobFieldsPerCheckpoint: Fr[][] = []; // The below batched blob is used for testing different epochs with 1..numberOfConsecutiveBlocks blocks on L1. // For real usage, always collect ALL epoch blobs first then call .batch(). let currentBatch: BatchedBlob | undefined; for (let i = 0; i < numberOfConsecutiveBlocks; i++) { - // With just one l1 client (serial sending) this takes too much time to send NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP + // With just one l1 client (serial sending) this takes too much time to send MAX_L1_TO_L2_MSGS_PER_CHECKPOINT // and causes a chain prune - const l1ToL2Content = range(Math.min(16, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP), 128 * i + 1 + 0x400).map(fr); + const l1ToL2Content = range(Math.min(16, MAX_L1_TO_L2_MSGS_PER_CHECKPOINT), 128 * i + 1 + 0x400).map(fr); - const sentThisCheckpoint: Fr[] = []; + const currentL1ToL2Messages: Fr[] = []; for (let j = 0; j < l1ToL2Content.length; j++) { - sentThisCheckpoint.push(await sendToL2(l1ToL2Content[j], recipientAddress)); + currentL1ToL2Messages.push(await sendToL2(l1ToL2Content[j], recipientAddress)); } - // Consume the messages sent inboxLag checkpoints ago, then enqueue this checkpoint's messages. - const currentL1ToL2Messages = messagesInFlight.shift()!; - messagesInFlight.push(sentThisCheckpoint); - // Ensure that each transaction has unique (non-intersecting nullifier values) const totalNullifiersPerBlock = 4 * MAX_NULLIFIERS_PER_TX; const txs = await timesParallel(numTxs, txIndex => @@ -580,14 +612,38 @@ describe('L1Publisher integration', () => { new GasFees(0, await rollup.getManaMinFeeAt(timestamp, true)), ); - const checkpoint = await buildCheckpoint(globalVariables, txs, currentL1ToL2Messages); + const checkpoint = await buildCheckpoint( + globalVariables, + txs, + currentL1ToL2Messages, + [], + previousInboxRollingHash, + ); + previousInboxRollingHash = checkpoint.header.inboxRollingHash; const block = checkpoint.blocks[0]; + // Mirror the Inbox's newest bucket into messageSource so the world-state synchronizer can rebuild this + // block's consumed bundle when it syncs the block back on the next iteration, and so the propose bucket hint + // resolves to the sequence whose rolling hash the checkpoint header committed to. One bucket per block keyed + // by its cumulative L1->L2 tree leaf count -- exactly the value world-state looks the bucket up by. + const currentBucketSeq = await inbox.read.getCurrentBucketSeq(); + const cumulativeMsgCount = BigInt(block.header.state.l1ToL2MessageTree.nextAvailableLeafIndex); + messageSource.setInboxBucket( + { + seq: currentBucketSeq, + inboxRollingHash: checkpoint.header.inboxRollingHash, + totalMsgCount: cumulativeMsgCount, + timestamp, + msgCount: currentL1ToL2Messages.length, + lastMessageIndex: cumulativeMsgCount - 1n, + }, + currentL1ToL2Messages, + ); + const totalManaUsed = txs.reduce((acc, tx) => acc.add(new Fr(tx.gasUsed.billedGas.l2Gas)), Fr.ZERO); expect(totalManaUsed.toBigInt()).toEqual(block.header.totalManaUsed.toBigInt()); prevHeader = block.header; - blockSource.getL1ToL2Messages.mockResolvedValueOnce(currentL1ToL2Messages); const checkpointBlobFields = checkpoint.toBlobFields(); const blockBlobs = await getBlobsPerL1Block(checkpointBlobFields); @@ -611,10 +667,13 @@ describe('L1Publisher integration', () => { deployerAccount.address, ); + // The checkpoint consumed everything sent so far, so its consumed bucket is the Inbox's newest. + const bucketHint = (await messageSource.getInboxBucketByTotalMsgCount(cumulativeMsgCount))!.seq; await publisher.enqueueProposeCheckpoint( checkpoint, CommitteeAttestationsAndSigners.empty(getSignatureContext()), Signature.empty(), + bucketHint, ); // Align chain time so the bundle simulate and the L1 send both run at the header's slot. await progressToSlot(BigInt(checkpoint.header.slotNumber)); @@ -660,6 +719,7 @@ describe('L1Publisher integration', () => { oracleInput: { feeAssetPriceModifier: 0n, }, + bucketHint, }, CommitteeAttestationsAndSigners.packAttestations([]), [], @@ -723,6 +783,7 @@ describe('L1Publisher integration', () => { checkpoint, new CommitteeAttestationsAndSigners(attestations, getSignatureContext()), signature, + 0n, ); // Align chain time so the bundle simulate and the L1 send both run at the header's slot. await progressToSlot(BigInt(checkpoint.header.slotNumber)); @@ -768,7 +829,7 @@ describe('L1Publisher integration', () => { // warn log carrying the on-chain revert reason (raw hex selector since the propose request // has no ABI attached). const loggerWarnSpy = jest.spyOn((publisher as any).log, 'warn'); - await publisher.enqueueProposeCheckpoint(checkpoint, attestationsAndSigners, Signature.empty()); + await publisher.enqueueProposeCheckpoint(checkpoint, attestationsAndSigners, Signature.empty(), 0n); await progressToSlot(BigInt(checkpoint.header.slotNumber)); const result = await publisher.sendRequests(); expect(result).toBeUndefined(); @@ -805,6 +866,7 @@ describe('L1Publisher integration', () => { checkpoint, attestationsAndSigners, flipSignature(attestationsAndSignersSignature), + 0n, ); await progressToSlot(BigInt(checkpoint.header.slotNumber)); const result = await publisher.sendRequests(); @@ -844,7 +906,7 @@ describe('L1Publisher integration', () => { // Enqueue no longer simulates — the bundle simulate at send time drops the failing propose // and sendRequests returns undefined. const loggerWarnSpy = jest.spyOn((publisher as any).log, 'warn'); - await publisher.enqueueProposeCheckpoint(checkpoint, attestationsAndSigners, wrongSig); + await publisher.enqueueProposeCheckpoint(checkpoint, attestationsAndSigners, wrongSig, 0n); await progressToSlot(BigInt(checkpoint.header.slotNumber)); const result = await publisher.sendRequests(); expect(result).toBeUndefined(); @@ -928,7 +990,7 @@ describe('L1Publisher integration', () => { // Invalidate and propose logger.warn('Enqueuing requests to invalidate and propose the checkpoint'); publisher.enqueueInvalidateCheckpoint(invalidateRequest); - await publisher.enqueueProposeCheckpoint(checkpoint, attestationsAndSigners, attestationsAndSignersSignature); + await publisher.enqueueProposeCheckpoint(checkpoint, attestationsAndSigners, attestationsAndSignersSignature, 0n); await progressToSlot(BigInt(checkpoint.header.slotNumber)); const result = await publisher.sendRequests(); expect(result!.successfulActions).toEqual(['invalidate-by-insufficient-attestations', 'propose']); @@ -949,6 +1011,7 @@ describe('L1Publisher integration', () => { checkpoint, CommitteeAttestationsAndSigners.empty(getSignatureContext()), Signature.empty(), + 0n, ); await publisher.enqueueGovernanceCastSignal( l1ContractAddresses.rollupAddress, @@ -964,10 +1027,9 @@ describe('L1Publisher integration', () => { }); it(`shows propose custom errors if tx simulation fails`, async () => { - // Set up different l1-to-l2 messages than the ones on the inbox, so this submission reverts because the - // INBOX.consume does not match the header.inHash and we get a Rollup__BlobHash that is not caught by - // validateHeader before. - const l1ToL2Messages = new Array(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).fill(new Fr(1n)); + // Set up different l1-to-l2 messages than the ones on the inbox, so the checkpoint's inboxRollingHash does not + // match the referenced Inbox bucket and the submission reverts at the streaming-consumption check. + const l1ToL2Messages = new Array(MAX_L1_TO_L2_MSGS_PER_CHECKPOINT).fill(new Fr(1n)); const { checkpoint } = await buildSingleCheckpoint({ l1ToL2Messages }); // Enqueue no longer simulates per action — the bundle simulate at send time drops the @@ -977,16 +1039,17 @@ describe('L1Publisher integration', () => { checkpoint, CommitteeAttestationsAndSigners.empty(getSignatureContext()), Signature.empty(), + 0n, ); await progressToSlot(BigInt(checkpoint.header.slotNumber)); const result = await publisher.sendRequests(); expect(result).toBeUndefined(); - // 0xcd6f4233 == Rollup__InvalidInHash selector + // 0xed1f7bb5 == Rollup__InvalidInboxRollingHash selector expect(loggerWarnSpy).toHaveBeenCalledWith( 'Bundle entry dropped: action reverted in sim', expect.objectContaining({ action: 'propose', - returnData: expect.stringMatching(/^0xcd6f4233/), + returnData: expect.stringMatching(/^0xed1f7bb5/), }), ); }); @@ -1033,6 +1096,7 @@ describe('L1Publisher integration', () => { checkpoint, CommitteeAttestationsAndSigners.empty(getSignatureContext()), Signature.empty(), + 0n, { txTimeoutAt: getProposeTxTimeoutAt(checkpoint) }, ); }; diff --git a/yarn-project/sequencer-client/src/publisher/sequencer-publisher.test.ts b/yarn-project/sequencer-client/src/publisher/sequencer-publisher.test.ts index 4fa19d92c574..9a7f4fdc8476 100644 --- a/yarn-project/sequencer-client/src/publisher/sequencer-publisher.test.ts +++ b/yarn-project/sequencer-client/src/publisher/sequencer-publisher.test.ts @@ -262,6 +262,7 @@ describe('SequencerPublisher', () => { checkpoint, CommitteeAttestationsAndSigners.empty(testSignatureContext), Signature.empty(), + 0n, ); const { govPayload, voteSig } = mockGovernancePayload(); @@ -295,6 +296,7 @@ describe('SequencerPublisher', () => { oracleInput: { feeAssetPriceModifier: 0n, }, + bucketHint: 0n, }, CommitteeAttestationsAndSigners.packAttestations([]), [], @@ -350,6 +352,7 @@ describe('SequencerPublisher', () => { new Checkpoint(l2Block.archive, header, [l2Block], l2Block.checkpointNumber), CommitteeAttestationsAndSigners.empty(testSignatureContext), Signature.empty(), + 0n, ); const result = await publisher.sendRequests(); expect(result).toEqual(undefined); @@ -424,6 +427,7 @@ describe('SequencerPublisher', () => { new Checkpoint(l2Block.archive, header, [l2Block], l2Block.checkpointNumber), CommitteeAttestationsAndSigners.empty(testSignatureContext), Signature.empty(), + 0n, ); const result = await rotatingPublisher.sendRequests(); @@ -460,6 +464,7 @@ describe('SequencerPublisher', () => { new Checkpoint(l2Block.archive, header, [l2Block], l2Block.checkpointNumber), CommitteeAttestationsAndSigners.empty(testSignatureContext), Signature.empty(), + 0n, ); // TimeoutError propagates to the outer catch in sendRequests which returns undefined const result = await rotatingPublisher.sendRequests(); @@ -479,6 +484,7 @@ describe('SequencerPublisher', () => { new Checkpoint(l2Block.archive, header, [l2Block], l2Block.checkpointNumber), CommitteeAttestationsAndSigners.empty(testSignatureContext), Signature.empty(), + 0n, ); const result = await rotatingPublisher.sendRequests(); @@ -493,6 +499,7 @@ describe('SequencerPublisher', () => { new Checkpoint(l2Block.archive, header, [l2Block], l2Block.checkpointNumber), CommitteeAttestationsAndSigners.empty(testSignatureContext), Signature.empty(), + 0n, { txTimeoutAt: pastTimeout }, ); const result = await rotatingPublisher.sendRequests(); @@ -525,6 +532,7 @@ describe('SequencerPublisher', () => { new Checkpoint(l2Block.archive, header, [l2Block], l2Block.checkpointNumber), CommitteeAttestationsAndSigners.empty(testSignatureContext), Signature.empty(), + 0n, { txTimeoutAt: futureTimeout }, ); const result = await rotatingPublisher.sendRequests(); @@ -547,6 +555,7 @@ describe('SequencerPublisher', () => { new Checkpoint(l2Block.archive, header, [l2Block], l2Block.checkpointNumber), CommitteeAttestationsAndSigners.empty(testSignatureContext), Signature.empty(), + 0n, ); const result = await rotatingPublisher.sendRequests(); @@ -561,6 +570,7 @@ describe('SequencerPublisher', () => { new Checkpoint(l2Block.archive, header, [l2Block], l2Block.checkpointNumber), CommitteeAttestationsAndSigners.empty(testSignatureContext), Signature.empty(), + 0n, ); // Simulate the bundle-level validate returning a failed entry for the propose call. @@ -779,6 +789,7 @@ describe('SequencerPublisher', () => { new Checkpoint(l2Block.archive, header, [l2Block], l2Block.checkpointNumber), CommitteeAttestationsAndSigners.empty(testSignatureContext), Signature.empty(), + 0n, ); publisher.interrupt(); const resultPromise = publisher.sendRequests(); @@ -1226,6 +1237,7 @@ describe('SequencerPublisher', () => { new Checkpoint(l2Block.archive, header, [l2Block], l2Block.checkpointNumber), CommitteeAttestationsAndSigners.empty(testSignatureContext), Signature.empty(), + 0n, ); const result = await storedPublisher.sendRequests(); diff --git a/yarn-project/sequencer-client/src/publisher/sequencer-publisher.ts b/yarn-project/sequencer-client/src/publisher/sequencer-publisher.ts index 9b97e0bfedbf..9cccdfb25cd5 100644 --- a/yarn-project/sequencer-client/src/publisher/sequencer-publisher.ts +++ b/yarn-project/sequencer-client/src/publisher/sequencer-publisher.ts @@ -115,6 +115,8 @@ type L1ProcessArgs = { attestationsAndSignersSignature: Signature; /** The fee asset price modifier in basis points (from oracle) */ feeAssetPriceModifier: bigint; + /** Sequence number of the Inbox bucket the header's rolling hash corresponds to. */ + bucketHint: bigint; }; export const Actions = [ @@ -1435,6 +1437,7 @@ export class SequencerPublisher { checkpoint: Checkpoint, attestationsAndSigners: CommitteeAttestationsAndSigners, attestationsAndSignersSignature: Signature, + bucketHint: bigint, opts: EnqueueProposeCheckpointOpts = {}, ): Promise { const checkpointHeader = checkpoint.header; @@ -1449,6 +1452,7 @@ export class SequencerPublisher { attestationsAndSigners, attestationsAndSignersSignature, feeAssetPriceModifier: checkpoint.feeAssetPriceModifier, + bucketHint, }; this.log.verbose(`Enqueuing checkpoint propose transaction`, { @@ -1619,6 +1623,7 @@ export class SequencerPublisher { oracleInput: { feeAssetPriceModifier: encodedData.feeAssetPriceModifier, }, + bucketHint: encodedData.bucketHint, }, encodedData.attestationsAndSigners.getPackedAttestations(), signers, diff --git a/yarn-project/sequencer-client/src/sequencer/automine/automine_sequencer.ts b/yarn-project/sequencer-client/src/sequencer/automine/automine_sequencer.ts index 041dfee55ecc..4f5b9df89082 100644 --- a/yarn-project/sequencer-client/src/sequencer/automine/automine_sequencer.ts +++ b/yarn-project/sequencer-client/src/sequencer/automine/automine_sequencer.ts @@ -1,7 +1,9 @@ import type { Archiver } from '@aztec/archiver'; +import { INBOX_LAG_SECONDS, MAX_L1_TO_L2_MSGS_PER_BLOCK, MAX_L1_TO_L2_MSGS_PER_CHECKPOINT } from '@aztec/constants'; import type { L1TxUtils } from '@aztec/ethereum/l1-tx-utils'; import { type EthCheatCodes, RollupCheatCodes } from '@aztec/ethereum/test'; import { BlockNumber, CheckpointNumber, EpochNumber, SlotNumber } from '@aztec/foundation/branded-types'; +import { Fr } from '@aztec/foundation/curves/bn254'; import type { EthAddress } from '@aztec/foundation/eth-address'; import { Signature } from '@aztec/foundation/eth-signature'; import { type Logger, createLogger } from '@aztec/foundation/log'; @@ -22,8 +24,9 @@ import { getTimestampForSlot, } from '@aztec/stdlib/epoch-helpers'; import { InsufficientValidTxsError, type WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server'; -import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging'; +import { type L1ToL2MessageSource, getInboxCutoffTimestamp } from '@aztec/stdlib/messaging'; import type { CoordinationSignatureContext } from '@aztec/stdlib/p2p'; +import { MerkleTreeId } from '@aztec/stdlib/trees'; import type { FailedTx, Tx } from '@aztec/stdlib/tx'; import type { BuildBlockInCheckpointResult, @@ -35,6 +38,7 @@ import type { GlobalVariableBuilder } from '../../global_variable_builder/global import type { SequencerPublisherFactory } from '../../publisher/sequencer-publisher-factory.js'; import type { SequencerPublisher } from '../../publisher/sequencer-publisher.js'; import type { SequencerConfig } from '../config.js'; +import { selectInboxBucketForBlock } from '../inbox_bucket_selector.js'; /** * L1 rollup constants needed by the AutomineSequencer. Same as SequencerRollupConstants @@ -447,8 +451,6 @@ export class AutomineSequencer { SlotNumber(targetSlot), ); - const l1ToL2Messages = await this.deps.l1ToL2MessageSource.getL1ToL2Messages(checkpointNumber); - const previousCheckpointOutHashes = await getPreviousCheckpointOutHashes({ blockSource: this.deps.l2BlockSource, epoch: targetEpoch, @@ -468,11 +470,34 @@ export class AutomineSequencer { await using fork = await this.deps.worldState.fork(syncedToBlockNumber, { closeDelayMs: 0 }); + // Streaming Inbox: automine builds a single-block checkpoint, so its one block is the + // checkpoint's final block; select its bundle from the newest lag-eligible bucket with the last-block censorship + // floor. The parent total is the fork's L1-to-L2 leaf count (compact indexing), which resolves the parent bucket. + const parentInfo = await fork.getTreeInfo(MerkleTreeId.L1_TO_L2_MESSAGE_TREE); + const parentTotalMsgCount = parentInfo.size; + const parentBucket = await this.deps.l1ToL2MessageSource.getInboxBucketByTotalMsgCount(parentTotalMsgCount); + if (parentBucket === undefined) { + this.log.warn(`Automine streaming inbox: parent bucket for total ${parentTotalMsgCount} not synced; skipping`); + return undefined; + } + const selection = await selectInboxBucketForBlock({ + messageSource: this.deps.l1ToL2MessageSource, + now: BigInt(Math.floor(this.deps.dateProvider.now() / 1000)), + lagSeconds: BigInt(INBOX_LAG_SECONDS), + parent: { seq: parentBucket.seq, totalMsgCount: parentBucket.totalMsgCount }, + checkpointStartTotalMsgCount: parentTotalMsgCount, + perBlockCap: MAX_L1_TO_L2_MSGS_PER_BLOCK, + perCheckpointCap: MAX_L1_TO_L2_MSGS_PER_CHECKPOINT, + isLastBlock: true, + cutoffTimestamp: getInboxCutoffTimestamp(SlotNumber(targetSlot), this.deps.l1Constants, INBOX_LAG_SECONDS), + }); + const streamingBundle = selection.consume ? selection.bundle : []; + const bucketHint = selection.consume ? selection.bucket.seq : parentBucket.seq; + const checkpointBuilder = await this.deps.checkpointsBuilder.startCheckpoint( checkpointNumber, checkpointGlobals, feeAssetPriceModifier, - l1ToL2Messages, previousCheckpointOutHashes, previousInboxRollingHash, fork, @@ -489,6 +514,7 @@ export class AutomineSequencer { checkpointGlobals.timestamp, allowEmpty, checkpointNumber, + streamingBundle, ); if (!buildResult) { return undefined; @@ -516,7 +542,12 @@ export class AutomineSequencer { feeAssetPriceModifier, }); - await this.publisher.enqueueProposeCheckpoint(checkpoint, emptyAttestations, emptyAttestationsSignature); + await this.publisher.enqueueProposeCheckpoint( + checkpoint, + emptyAttestations, + emptyAttestationsSignature, + bucketHint, + ); // Automine publishes synchronously in the current slot via `sendRequests`. It must NOT use the // production `sendRequestsAt` (or `canProposeAt`), which always apply the one-slot pipelining // offset — automine is the deliberate non-pipelined exception and builds/publishes in place. @@ -756,16 +787,19 @@ export class AutomineSequencer { timestamp: bigint, allowEmpty: boolean, checkpointNumber: CheckpointNumber, + l1ToL2Messages: Fr[], ): Promise { let buildResult: BuildBlockInCheckpointResult; try { buildResult = await checkpointBuilder.buildBlock(pendingTxs, nextBlockNumber, timestamp, { maxTransactions: this.deps.config.maxTxsPerBlock, - // Allow empty for explicit-empty builds; require at least 1 valid tx otherwise. - minValidTxs: allowEmpty ? 0 : 1, + // Allow empty for explicit-empty builds; a message-only block (non-empty streaming bundle) also builds + // with zero txs. + minValidTxs: allowEmpty || l1ToL2Messages.length > 0 ? 0 : 1, isBuildingProposal: true, maxBlocksPerCheckpoint: 1, perBlockAllocationMultiplier: 1, + l1ToL2Messages, }); } catch (err) { // Mirrors production's checkpoint_proposal_job: if every pending tx failed execution and diff --git a/yarn-project/sequencer-client/src/sequencer/checkpoint_proposal_job.test.ts b/yarn-project/sequencer-client/src/sequencer/checkpoint_proposal_job.test.ts index 1dad18d8fdf8..8cdddbdf0d9e 100644 --- a/yarn-project/sequencer-client/src/sequencer/checkpoint_proposal_job.test.ts +++ b/yarn-project/sequencer-client/src/sequencer/checkpoint_proposal_job.test.ts @@ -38,9 +38,10 @@ import { InsufficientValidTxsError, type MerkleTreeWriteOperations, type ResolvedSequencerConfig, + type TreeInfo, type WorldStateSynchronizer, } from '@aztec/stdlib/interfaces/server'; -import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging'; +import type { InboxBucket, L1ToL2MessageSource } from '@aztec/stdlib/messaging'; import { BlockProposal, CheckpointProposal, type CoordinationSignatureContext } from '@aztec/stdlib/p2p'; import { CheckpointHeader } from '@aztec/stdlib/rollup'; import type { ProposerTimetable, SubslotSelection } from '@aztec/stdlib/timetable'; @@ -229,6 +230,9 @@ describe('CheckpointProposalJob', () => { const mockFork = mock({ [Symbol.asyncDispose]: jest.fn().mockReturnValue(Promise.resolve()) as () => Promise, }); + // The streaming Inbox cursor resolves the parent bucket from the fork's L1-to-L2 tree leaf count; default to + // an empty tree so checkpoints start at the genesis bucket unless a test seeds buckets. + mockFork.getTreeInfo.mockResolvedValue({ size: 0n } as TreeInfo); worldState.fork.mockResolvedValue(mockFork); // Create fake CheckpointsBuilder and CheckpointBuilder @@ -246,6 +250,16 @@ describe('CheckpointProposalJob', () => { l1ToL2MessageSource = mock(); l1ToL2MessageSource.getL1ToL2Messages.mockResolvedValue(Array(4).fill(Fr.ZERO)); + // Genesis bucket for the empty-tree cursor above; with no newer synced buckets mocked, block bundle + // selection consumes nothing by default. + l1ToL2MessageSource.getInboxBucketByTotalMsgCount.mockResolvedValue({ + seq: 0n, + inboxRollingHash: Fr.ZERO, + totalMsgCount: 0n, + timestamp: 0n, + msgCount: 0, + lastMessageIndex: 0n, + }); l2BlockSource = mock(); l2BlockSource.getCheckpointsData.mockResolvedValue([]); @@ -437,6 +451,7 @@ describe('CheckpointProposalJob', () => { blockCount: 1, totalManaUsed: 5000n, feeAssetPriceModifier: 100n, + inboxMsgTotal: 0n, }, }); @@ -636,6 +651,7 @@ describe('CheckpointProposalJob', () => { blockCount: 1, totalManaUsed: 5000n, feeAssetPriceModifier: 100n, + inboxMsgTotal: 0n, }; job = createCheckpointProposalJob({ @@ -683,6 +699,7 @@ describe('CheckpointProposalJob', () => { blockCount: 1, totalManaUsed: 5000n, feeAssetPriceModifier: 100n, + inboxMsgTotal: 0n, }; job = createCheckpointProposalJob({ targetSlot, targetEpoch, proposedCheckpointData }); @@ -834,6 +851,7 @@ describe('CheckpointProposalJob', () => { blockCount: 1, totalManaUsed: 5000n, feeAssetPriceModifier: 100n, + inboxMsgTotal: 0n, }; let mismatchEvents: { slot: SlotNumber; checkpointNumber: CheckpointNumber; reason: string }[]; @@ -1394,6 +1412,130 @@ describe('CheckpointProposalJob', () => { }); }); + describe('streaming inbox', () => { + beforeEach(() => { + job.setTimetable(makeProposerTimetable({ l1Constants, blockDurationMs: 3000 })); + }); + + it('streams per-block bucket bundles, advances the reference, and skips the bulk message fetch', async () => { + jest + .spyOn(job.getTimetable(), 'selectNextSubslot') + .mockReturnValueOnce(subslot(10, 0, false)) + .mockReturnValueOnce(subslot(18, 1, true)) + .mockReturnValue(noSubslot()); + + // The archiver returns the newest synced bucket (seq 2) for any lag/cutoff lookup, so the first block + // consumes through it and the second block (cursor already at seq 2) consumes nothing and reuses the ref. + const bucket: InboxBucket = { + seq: 2n, + inboxRollingHash: new Fr(99), + totalMsgCount: 5n, + timestamp: 0n, + msgCount: 2, + lastMessageIndex: 4n, + }; + const bundle = Array.from({ length: 5 }, (_, i) => new Fr(i + 1)); + l1ToL2MessageSource.getLatestInboxBucketAtOrBefore.mockResolvedValue(bucket); + l1ToL2MessageSource.getL1ToL2MessagesBetweenBuckets.mockResolvedValue(bundle); + + const { lastBlock } = await setupMultipleBlocks(2, [2, 1]); + validatorClient.collectAttestations.mockResolvedValue(getAttestations(lastBlock)); + + const checkpoint = await job.executeAndAwait(); + + expect(checkpoint).toBeDefined(); + + // Streaming skips the bulk per-checkpoint fetch; every message reaches the builder through a block. + expect(l1ToL2MessageSource.getL1ToL2Messages).not.toHaveBeenCalled(); + expect(checkpointsBuilder.startCheckpointCalls).toHaveLength(1); + + // The first block consumes the selected bundle; the second consumes nothing. + expect(checkpointBuilder.buildBlockCalls).toHaveLength(2); + expect(checkpointBuilder.buildBlockCalls[0].opts.l1ToL2Messages).toEqual(bundle); + expect(checkpointBuilder.buildBlockCalls[1].opts.l1ToL2Messages).toEqual([]); + + // Both block proposals carry the selected bucket reference (the second reuses the first's). + const bucketRefArgs = validatorClient.createBlockProposal.mock.calls.map(call => call[8]); + expect(bucketRefArgs).toHaveLength(2); + expect(bucketRefArgs[0]?.bucketSeq).toBe(2n); + expect(bucketRefArgs[1]?.bucketSeq).toBe(2n); + }); + + it('produces a message-only block when a non-empty bundle is selected and no txs are pending', async () => { + jest + .spyOn(job.getTimetable(), 'selectNextSubslot') + .mockReturnValueOnce(subslot(10, 0, true)) + .mockReturnValue(noSubslot()); + + const bucket: InboxBucket = { + seq: 2n, + inboxRollingHash: new Fr(99), + totalMsgCount: 5n, + timestamp: 0n, + msgCount: 2, + lastMessageIndex: 4n, + }; + const bundle = Array.from({ length: 5 }, (_, i) => new Fr(i + 1)); + l1ToL2MessageSource.getLatestInboxBucketAtOrBefore.mockResolvedValue(bucket); + l1ToL2MessageSource.getL1ToL2MessagesBetweenBuckets.mockResolvedValue(bundle); + + // Empty tx pool with the min-txs threshold at its default of one and no empty-checkpoint building: the + // non-empty bundle alone must count as work, producing a zero-tx (message-only) block. + const { lastBlock } = await setupMultipleBlocks(1, [0]); + validatorClient.collectAttestations.mockResolvedValue(getAttestations(lastBlock)); + + job.updateConfig({ minTxsPerBlock: 1, buildCheckpointIfEmpty: false }); + const checkpoint = await job.executeAndAwait(); + + expect(checkpoint).toBeDefined(); + expect(checkpointBuilder.buildBlockCalls).toHaveLength(1); + expect(checkpointBuilder.buildBlockCalls[0].opts.l1ToL2Messages).toEqual(bundle); + expect(publisher.enqueueProposeCheckpoint).toHaveBeenCalledTimes(1); + }); + + it('carries the consumed bucket hint when the final block fails to build after earlier consumption', async () => { + // Two sub-slots. The first block consumes the pending bucket (seq 2) as a message-only block. The final + // sub-slot block has no txs and nothing left to consume (the cursor already sits at seq 2), so it fails to + // build and is not held for broadcast. The L1 propose bucket hint must still be the bucket the checkpoint + // header committed to (seq 2), not fall back to genesis bucket 0 — otherwise L1 rejects the proposal with an + // inbox-rolling-hash mismatch (the hint's bucket rolling hash would not match the header's). + jest + .spyOn(job.getTimetable(), 'selectNextSubslot') + .mockReturnValueOnce(subslot(10, 0, false)) + .mockReturnValueOnce(subslot(18, 1, true)) + .mockReturnValue(noSubslot()); + + const bucket: InboxBucket = { + seq: 2n, + inboxRollingHash: new Fr(99), + totalMsgCount: 5n, + timestamp: 0n, + msgCount: 2, + lastMessageIndex: 4n, + }; + const bundle = Array.from({ length: 5 }, (_, i) => new Fr(i + 1)); + l1ToL2MessageSource.getLatestInboxBucketAtOrBefore.mockResolvedValue(bucket); + l1ToL2MessageSource.getL1ToL2MessagesBetweenBuckets.mockResolvedValue(bundle); + + // Seed a single message-only block; the second sub-slot has no seeded block, no txs, and no new bucket to + // consume, so it fails the min-work threshold and no block is held for broadcast. + const { lastBlock } = await setupMultipleBlocks(1, [0]); + validatorClient.collectAttestations.mockResolvedValue(getAttestations(lastBlock)); + + job.updateConfig({ minTxsPerBlock: 1, buildCheckpointIfEmpty: false }); + const checkpoint = await job.executeAndAwait(); + + expect(checkpoint).toBeDefined(); + // Only the first (message-only) block built and consumed through bucket 2; the final block did not build. + expect(checkpointBuilder.buildBlockCalls).toHaveLength(1); + expect(publisher.enqueueProposeCheckpoint).toHaveBeenCalledTimes(1); + + // No held last block, yet the bucket hint (4th positional arg) is the consumed bucket seq 2, matching the + // checkpoint header's rolling hash. Before the fix this fell back to genesis bucket 0n. + expect(publisher.enqueueProposeCheckpoint.mock.calls[0][3]).toBe(2n); + }); + }); + describe('build single block', () => { it('does not build a block if not enough valid txs are collected', async () => { // We have enough txs, but not enough valid ones diff --git a/yarn-project/sequencer-client/src/sequencer/checkpoint_proposal_job.timing.test.ts b/yarn-project/sequencer-client/src/sequencer/checkpoint_proposal_job.timing.test.ts index 6088fb1579e6..558059f6fd2e 100644 --- a/yarn-project/sequencer-client/src/sequencer/checkpoint_proposal_job.timing.test.ts +++ b/yarn-project/sequencer-client/src/sequencer/checkpoint_proposal_job.timing.test.ts @@ -16,6 +16,7 @@ import type { BlockBuilderOptions, MerkleTreeWriteOperations, ResolvedSequencerConfig, + TreeInfo, WorldStateSynchronizer, } from '@aztec/stdlib/interfaces/server'; import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging'; @@ -448,10 +449,21 @@ describe('CheckpointProposalJob Timing Tests', () => { const mockFork = mock({ [Symbol.asyncDispose]: jest.fn().mockReturnValue(Promise.resolve()) as () => Promise, }); + // Streaming inbox: the checkpoint job resolves the parent Inbox bucket from the fork's + // L1-to-L2 tree leaf count. Default to an empty tree so it starts at the genesis bucket. + mockFork.getTreeInfo.mockResolvedValue({ size: 0n } as TreeInfo); worldState.fork.mockResolvedValue(mockFork); l1ToL2MessageSource = mock(); l1ToL2MessageSource.getL1ToL2Messages.mockResolvedValue(Array(4).fill(Fr.ZERO)); + l1ToL2MessageSource.getInboxBucketByTotalMsgCount.mockResolvedValue({ + seq: 0n, + inboxRollingHash: Fr.ZERO, + totalMsgCount: 0n, + timestamp: 0n, + msgCount: 0, + lastMessageIndex: 0n, + }); l2BlockSource = mock(); l2BlockSource.getCheckpointsData.mockResolvedValue([]); diff --git a/yarn-project/sequencer-client/src/sequencer/checkpoint_proposal_job.ts b/yarn-project/sequencer-client/src/sequencer/checkpoint_proposal_job.ts index edcb569a2261..7165a1b1db99 100644 --- a/yarn-project/sequencer-client/src/sequencer/checkpoint_proposal_job.ts +++ b/yarn-project/sequencer-client/src/sequencer/checkpoint_proposal_job.ts @@ -1,3 +1,4 @@ +import { INBOX_LAG_SECONDS, MAX_L1_TO_L2_MSGS_PER_BLOCK, MAX_L1_TO_L2_MSGS_PER_CHECKPOINT } from '@aztec/constants'; import { type EpochCache, PROPOSER_PIPELINING_SLOT_OFFSET } from '@aztec/epoch-cache'; import type { SimulationOverridesPlan } from '@aztec/ethereum/contracts'; import { @@ -48,10 +49,11 @@ import { Gas } from '@aztec/stdlib/gas'; import { type BlockBuilderOptions, InsufficientValidTxsError, + type MerkleTreeWriteOperations, type ResolvedSequencerConfig, type WorldStateSynchronizer, } from '@aztec/stdlib/interfaces/server'; -import { type L1ToL2MessageSource, computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging'; +import { InboxBucketRef, type L1ToL2MessageSource, getInboxCutoffTimestamp } from '@aztec/stdlib/messaging'; import type { BlockProposal, BlockProposalOptions, @@ -63,6 +65,7 @@ import type { import { orderAttestations, trimAttestations } from '@aztec/stdlib/p2p'; import type { L2BlockBuiltStats } from '@aztec/stdlib/stats'; import type { ProposerTimetable } from '@aztec/stdlib/timetable'; +import { MerkleTreeId } from '@aztec/stdlib/trees'; import { type FailedTx, Tx } from '@aztec/stdlib/tx'; import { AttestationTimeoutError } from '@aztec/stdlib/validators'; import { Attributes, type Traceable, type Tracer, trackSpan } from '@aztec/telemetry-client'; @@ -75,6 +78,11 @@ import type { CheckpointProposalJobMetricsRecorder } from './checkpoint_proposal import { CheckpointVoter } from './checkpoint_voter.js'; import { SequencerInterruptedError } from './errors.js'; import type { SequencerEvents } from './events.js'; +import { + type ConsumedBucketCursor, + type InboxBucketSelection, + selectInboxBucketForBlock, +} from './inbox_bucket_selector.js'; import type { SequencerMetrics } from './metrics.js'; import type { RequestsTracker } from './requests_tracker.js'; import type { SequencerRollupConstants } from './types.js'; @@ -89,6 +97,13 @@ type CheckpointProposalBroadcast = { checkpoint: Checkpoint; proposal: CheckpointProposal; blockProposedAt: number; + /** + * Sequence number of the last Inbox bucket the checkpoint consumed through — the L1 `propose` lookup aid, which + * must resolve to the bucket whose rolling hash the checkpoint header committed to. Taken from the streaming + * consumption cursor rather than the held last-block proposal, which is absent when the checkpoint's final block + * fails to build after earlier blocks already consumed messages. + */ + bucketHint: bigint; }; /** Result after attestation collection and signing, ready for L1 submission. */ @@ -96,6 +111,21 @@ type CheckpointProposalResult = { checkpoint: Checkpoint; attestations: CommitteeAttestationsAndSigners; attestationsSignature: Signature; + /** Sequence number of the Inbox bucket the checkpoint's rolling hash corresponds to; the L1 `propose` lookup aid. */ + bucketHint: bigint; +}; + +/** + * Running state of streaming Inbox message selection across the blocks of one checkpoint. + * Consumption starts from the parent checkpoint's last-consumed bucket and advances one block at a time. + */ +type StreamingCheckpointState = { + /** Cumulative Inbox message count consumed as of the parent checkpoint; the per-checkpoint cap origin (fixed). */ + checkpointStartTotalMsgCount: bigint; + /** The last bucket consumed so far (parent checkpoint's at the first block); advances as blocks consume. */ + parent: ConsumedBucketCursor; + /** Reference to the last consumed bucket; reused by blocks that consume nothing. */ + lastBucketRef: InboxBucketRef; }; /** @@ -287,6 +317,11 @@ export class CheckpointProposalJob implements Traceable { votesPromises: Promise[], ): Promise { const { checkpoint } = broadcast; + // The bucket the checkpoint consumed through, from the streaming cursor. Sourcing it from the held last-block + // proposal is wrong: when the checkpoint's final block fails to build after earlier blocks already consumed + // messages, no block is held, so the hint would fall back to genesis bucket 0 while the header commits to a + // non-genesis rolling hash, which L1 rejects with Rollup__InvalidInboxRollingHash. + const bucketHint = broadcast.bucketHint; try { // Wait for all votes actions, enqueued at the beginning, to resolve @@ -298,7 +333,7 @@ export class CheckpointProposalJob implements Traceable { // Wait for the previous checkpoint to land on L1 before submitting, so we can check it // matches the proposed checkpoint we used as parent, and has valid attestations. if (signedAttestations && (await this.waitForValidParentCheckpointOnL1())) { - await this.enqueueCheckpointForSubmission({ checkpoint, ...signedAttestations }); + await this.enqueueCheckpointForSubmission({ checkpoint, ...signedAttestations, bucketHint }); } // If we failed to collect attestations, at least check if we need to issue an invalidation @@ -369,7 +404,7 @@ export class CheckpointProposalJob implements Traceable { /** Enqueues the checkpoint for L1 submission. Called after pipeline sleep in execute(). */ private async enqueueCheckpointForSubmission(result: CheckpointProposalResult): Promise { - const { checkpoint, attestations, attestationsSignature } = result; + const { checkpoint, attestations, attestationsSignature, bucketHint } = result; this.setState(SequencerState.PUBLISHING_CHECKPOINT); // Latest L1 block the propose can still land in for the target slot: the last Ethereum block inside @@ -398,7 +433,9 @@ export class CheckpointProposalJob implements Traceable { } } - await this.publisher.enqueueProposeCheckpoint(checkpoint, attestations, attestationsSignature, { txTimeoutAt }); + await this.publisher.enqueueProposeCheckpoint(checkpoint, attestations, attestationsSignature, bucketHint, { + txTimeoutAt, + }); } /** @@ -611,9 +648,9 @@ export class CheckpointProposalJob implements Traceable { this.checkpointSimulationOverridesPlan, ); - // Collect L1 to L2 messages for the checkpoint and compute their hash - const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(this.checkpointNumber); - const inHash = computeInHashFromL1ToL2Messages(l1ToL2Messages); + // Under the streaming Inbox (AZIP-22 Fast Inbox) messages are selected per block, so the legacy inHash is fed + // zero; the running values are computed block by block. + const inHash = Fr.ZERO; // Collect the out hashes of all the checkpoints before this one in the same epoch. // Under pipelining the parent checkpoint may not be on L1 yet at build time, so the helper @@ -649,12 +686,16 @@ export class CheckpointProposalJob implements Traceable { // Create a long-lived forked world state for the checkpoint builder await using fork = await this.worldState.fork(this.syncedToBlockNumber, { closeDelayMs: 12_000 }); + // Streaming Inbox: the consumption cursor starts at the parent state this checkpoint builds on. The fork's + // L1-to-L2 tree leaf count is the parent's cumulative consumed total (compact indexing), which resolves the + // parent's last-consumed bucket. + const streamingState = await this.resolveStreamingCheckpointStart(fork); + // Create checkpoint builder for the entire slot const checkpointBuilder = await this.checkpointsBuilder.startCheckpoint( this.checkpointNumber, checkpointGlobalVariables, feeAssetPriceModifier, - l1ToL2Messages, previousCheckpointOutHashes, previousInboxRollingHash, fork, @@ -684,6 +725,7 @@ export class CheckpointProposalJob implements Traceable { checkpointGlobalVariables.timestamp, inHash, blockProposalOptions, + streamingState, ); blocksInCheckpoint = result.blocksInCheckpoint; blockPendingBroadcast = result.blockPendingBroadcast; @@ -799,7 +841,12 @@ export class CheckpointProposalJob implements Traceable { ); this.metrics.recordCheckpointSuccess(); // Return a broadcast result with a dummy proposal — fisherman mode skips attestation collection - return { checkpoint, proposal: undefined!, blockProposedAt: this.dateProvider.now() }; + return { + checkpoint, + proposal: undefined!, + blockProposedAt: this.dateProvider.now(), + bucketHint: streamingState.lastBucketRef.bucketSeq, + }; } // Validate the header against L1 state before broadcasting. @@ -853,8 +900,10 @@ export class CheckpointProposalJob implements Traceable { this.checkpointMetrics.noteCheckpointBroadcast(this.dateProvider.now()); } - // Return immediately after broadcast — attestation collection happens in the background - return { checkpoint, proposal, blockProposedAt }; + // Return immediately after broadcast — attestation collection happens in the background. The bucket hint is + // the streaming cursor's final consumed bucket, which matches the header's rolling hash whether or not a last + // block was held for broadcast. + return { checkpoint, proposal, blockProposedAt, bucketHint: streamingState.lastBucketRef.bucketSeq }; } catch (err) { if (err && (err instanceof DutyAlreadySignedError || err instanceof SlashingProtectionError)) { // swallow this error. It's already been logged by a function deeper in the stack @@ -875,6 +924,7 @@ export class CheckpointProposalJob implements Traceable { timestamp: bigint, inHash: Fr, blockProposalOptions: BlockProposalOptions, + streamingState?: StreamingCheckpointState, ): Promise<{ blocksInCheckpoint: L2Block[]; blockPendingBroadcast: BlockProposal | undefined; @@ -912,6 +962,18 @@ export class CheckpointProposalJob implements Traceable { break; } + // Streaming Inbox: select this block's message bundle against the current (not-yet-advanced) consumption + // cursor. The state is only advanced once the block builds successfully, so a failed build (retried in a + // later sub-slot) re-derives the bundle rather than losing it. The builder inserts the bundle and rolls it + // back with the fork on failure. The censorship cutoff floor must apply on whichever block ends the + // checkpoint, which includes the block that reaches the per-checkpoint block cap, not just the timetable's + // last sub-slot. + const isCheckpointFinalBlock = timingInfo.isLastBlock || blocksBuilt + 1 >= this.config.maxBlocksPerCheckpoint; + const selection = streamingState + ? await this.selectStreamingBundle(streamingState, isCheckpointFinalBlock, nowSeconds) + : undefined; + const streamingBundle = streamingState ? (selection && selection.consume ? selection.bundle : []) : undefined; + const buildResult = await this.buildSingleBlock(checkpointBuilder, { // Create all blocks with the same timestamp blockTimestamp: timestamp, @@ -921,6 +983,7 @@ export class CheckpointProposalJob implements Traceable { blockNumber, indexWithinCheckpoint, txHashesAlreadyIncluded, + l1ToL2Messages: streamingBundle, }); // If we failed to build the block due to insufficient txs, we try again if there is still time left in the slot @@ -956,13 +1019,32 @@ export class CheckpointProposalJob implements Traceable { blocksInCheckpoint.push(block); usedTxs.forEach(tx => txHashesAlreadyIncluded.add(tx.txHash.toString())); + // Streaming Inbox: the block built successfully, so advance the consumption cursor and carry this block's + // rolling-hash bucket reference. A block that consumed nothing reuses the parent bucket reference. The legacy + // inHash is dead post-flip; block proposals carry zero (AZIP-22 Fast Inbox). + const blockInHash = inHash; + let blockBucketRef: InboxBucketRef | undefined = undefined; + if (streamingState && selection) { + if (selection.consume) { + streamingState.parent = { seq: selection.bucket.seq, totalMsgCount: selection.bucket.totalMsgCount }; + streamingState.lastBucketRef = InboxBucketRef.fromBucket(selection.bucket); + } + blockBucketRef = streamingState.lastBucketRef; + } + // Sign the block proposal. This will throw if HA signing fails. - const proposal = await this.createBlockProposal(block, inHash, usedTxs, { - ...blockProposalOptions, - broadcastInvalidBlockProposal: - blockProposalOptions.broadcastInvalidBlockProposal || - block.indexWithinCheckpoint === this.config.invalidBlockProposalIndexWithinCheckpoint, - }); + const proposal = await this.createBlockProposal( + block, + blockInHash, + usedTxs, + { + ...blockProposalOptions, + broadcastInvalidBlockProposal: + blockProposalOptions.broadcastInvalidBlockProposal || + block.indexWithinCheckpoint === this.config.invalidBlockProposalIndexWithinCheckpoint, + }, + blockBucketRef, + ); // Sync the proposed block to the archiver to make it available, only after we've managed to sign the proposal, // so we avoid polluting our archive with a block that would fail. @@ -1005,6 +1087,7 @@ export class CheckpointProposalJob implements Traceable { inHash: Fr, usedTxs: Tx[], blockProposalOptions: BlockProposalOptions, + bucketRef?: InboxBucketRef, ): Promise { if (this.config.fishermanMode) { this.log.info(`Skipping block proposal for block ${block.number} in fisherman mode`); @@ -1019,9 +1102,57 @@ export class CheckpointProposalJob implements Traceable { usedTxs, this.proposer, blockProposalOptions, + bucketRef, ); } + /** + * Resolves where a streaming-Inbox checkpoint's consumption starts: the parent checkpoint's last-consumed bucket. + * The parent's cumulative consumed total is the L1-to-L2 message tree leaf count of the fork + * this checkpoint builds on (compact indexing makes leaf count equal cumulative message count), which resolves the + * parent bucket by total. Genesis is the `total = 0` case, resolving the genesis bucket 0. + */ + private async resolveStreamingCheckpointStart(fork: MerkleTreeWriteOperations): Promise { + const parentInfo = await fork.getTreeInfo(MerkleTreeId.L1_TO_L2_MESSAGE_TREE); + const parentTotalMsgCount = parentInfo.size; + const parentBucket = await this.l1ToL2MessageSource.getInboxBucketByTotalMsgCount(parentTotalMsgCount); + if (parentBucket === undefined) { + throw new Error( + `Streaming inbox: cannot resolve parent Inbox bucket for cumulative total ${parentTotalMsgCount} ` + + `(checkpoint ${this.checkpointNumber}); local Inbox view has not synced it`, + ); + } + return { + checkpointStartTotalMsgCount: parentTotalMsgCount, + parent: { seq: parentBucket.seq, totalMsgCount: parentBucket.totalMsgCount }, + lastBucketRef: InboxBucketRef.fromBucket(parentBucket), + }; + } + + /** + * Selects this block's streaming-Inbox message bundle against the current consumption cursor, mirroring the L1 + * predicate in `ProposeLib.validateInboxConsumption`. Does not mutate the cursor; the caller + * advances it only after the block builds successfully. + */ + private selectStreamingBundle( + state: StreamingCheckpointState, + isLastBlock: boolean, + nowSeconds: number, + ): Promise { + const cutoffTimestamp = getInboxCutoffTimestamp(this.targetSlot, this.l1Constants, INBOX_LAG_SECONDS); + return selectInboxBucketForBlock({ + messageSource: this.l1ToL2MessageSource, + now: BigInt(Math.floor(nowSeconds)), + lagSeconds: BigInt(INBOX_LAG_SECONDS), + parent: state.parent, + checkpointStartTotalMsgCount: state.checkpointStartTotalMsgCount, + perBlockCap: MAX_L1_TO_L2_MSGS_PER_BLOCK, + perCheckpointCap: MAX_L1_TO_L2_MSGS_PER_CHECKPOINT, + isLastBlock, + cutoffTimestamp, + }); + } + /** * Sleeps until it is time to produce the next block in the slot. * @param nextSubslotStart - Absolute wall-clock timestamp in seconds of the previous sub-slot deadline. @@ -1046,12 +1177,21 @@ export class CheckpointProposalJob implements Traceable { indexWithinCheckpoint: IndexWithinCheckpoint; buildDeadline: Date | undefined; txHashesAlreadyIncluded: Set; + /** Streaming Inbox message bundle to insert into this block's L1-to-L2 tree; undefined in the legacy flow. */ + l1ToL2Messages?: Fr[]; }, ): Promise< { block: L2Block; usedTxs: Tx[] } | { failure: 'insufficient-txs' | 'insufficient-valid-txs' } | { error: Error } > { - const { blockTimestamp, forceCreate, blockNumber, indexWithinCheckpoint, buildDeadline, txHashesAlreadyIncluded } = - opts; + const { + blockTimestamp, + forceCreate, + blockNumber, + indexWithinCheckpoint, + buildDeadline, + txHashesAlreadyIncluded, + l1ToL2Messages, + } = opts; this.log.verbose( `Preparing block ${blockNumber} index ${indexWithinCheckpoint} at checkpoint ${this.checkpointNumber} for slot ${this.targetSlot}`, @@ -1118,6 +1258,7 @@ export class CheckpointProposalJob implements Traceable { maxBlocksPerCheckpoint: this.timetable.getMaxBlocksPerCheckpoint(), perBlockAllocationMultiplier: this.config.perBlockAllocationMultiplier, perBlockDAAllocationMultiplier: this.config.perBlockDAAllocationMultiplier, + l1ToL2Messages, }; // Actually build the block by executing txs. The builder throws InsufficientValidTxsError @@ -1251,11 +1392,21 @@ export class CheckpointProposalJob implements Traceable { blockNumber: BlockNumber; indexWithinCheckpoint: IndexWithinCheckpoint; buildDeadline: Date | undefined; + /** Streaming Inbox message bundle this block consumes; a non-empty bundle permits a zero-tx (message-only) block. */ + l1ToL2Messages?: Fr[]; }): Promise<{ canStartBuilding: boolean; minTxs: number }> { const { indexWithinCheckpoint, blockNumber, buildDeadline, forceCreate } = opts; - // We only allow a block with 0 txs in the first block of the checkpoint - const minTxs = indexWithinCheckpoint > 0 && this.config.minTxsPerBlock === 0 ? 1 : this.config.minTxsPerBlock; + // A non-empty streaming Inbox bundle is work on its own: the block must be produced even with zero txs, + // regardless of minTxsPerBlock, so the messages get inserted (message-only block). + // Without a bundle, a non-first block needs at least one tx to avoid empty filler blocks even when + // minTxsPerBlock is zero. + const hasStreamingBundle = (opts.l1ToL2Messages?.length ?? 0) > 0; + const minTxs = hasStreamingBundle + ? 0 + : indexWithinCheckpoint > 0 && this.config.minTxsPerBlock === 0 + ? 1 + : this.config.minTxsPerBlock; // Latest time to keep waiting for txs: wait_for_txs_deadline = block_build_deadline(k) - min_block_duration. const startBuildingDeadline = buildDeadline diff --git a/yarn-project/sequencer-client/src/sequencer/inbox_bucket_selector.test.ts b/yarn-project/sequencer-client/src/sequencer/inbox_bucket_selector.test.ts new file mode 100644 index 000000000000..4ccd4d9b0eaa --- /dev/null +++ b/yarn-project/sequencer-client/src/sequencer/inbox_bucket_selector.test.ts @@ -0,0 +1,275 @@ +import { Fr } from '@aztec/foundation/curves/bn254'; +import type { InboxBucket } from '@aztec/stdlib/messaging'; + +import { type InboxBucketSource, selectInboxBucketForBlock } from './inbox_bucket_selector.js'; + +/** A test bucket: its cumulative totals and leaves are derived from a running message count. */ +type TestBucketSpec = { seq: bigint; timestamp: bigint; msgCount: number }; + +/** + * Builds an in-memory {@link InboxBucketSource} from a list of bucket specs, mirroring the archiver's dense, + * timestamp-ordered buckets. Bucket seq 0 is the genesis sentinel (never a real bucket); real buckets start at 1. + */ +function makeSource(specs: TestBucketSpec[]): { + source: InboxBucketSource; + buckets: Map; + leaves: Fr[]; +} { + const leaves: Fr[] = []; + const buckets = new Map(); + let total = 0n; + for (const spec of specs) { + for (let i = 0; i < spec.msgCount; i++) { + leaves.push(new Fr(spec.seq * 1000n + BigInt(i))); + } + total += BigInt(spec.msgCount); + buckets.set(spec.seq, { + seq: spec.seq, + inboxRollingHash: new Fr(spec.seq), + totalMsgCount: total, + timestamp: spec.timestamp, + msgCount: spec.msgCount, + lastMessageIndex: total - 1n, + }); + } + + const ordered = [...buckets.values()].sort((a, b) => Number(a.seq - b.seq)); + const source: InboxBucketSource = { + getInboxBucket: (seq: bigint) => Promise.resolve(buckets.get(seq)), + getLatestInboxBucketAtOrBefore: (timestamp: bigint) => { + const eligible = ordered.filter(b => b.timestamp <= timestamp); + return Promise.resolve(eligible.length === 0 ? undefined : eligible[eligible.length - 1]); + }, + getL1ToL2MessagesBetweenBuckets: (fromExclusive: bigint, toInclusive: bigint) => { + const toBucket = buckets.get(toInclusive); + if (toBucket === undefined) { + return Promise.resolve([]); + } + let startIndex = 0n; + if (fromExclusive > 0n) { + const fromBucket = buckets.get(fromExclusive); + if (fromBucket === undefined) { + return Promise.resolve([]); + } + startIndex = fromBucket.lastMessageIndex + 1n; + } + return Promise.resolve(leaves.slice(Number(startIndex), Number(toBucket.lastMessageIndex + 1n))); + }, + }; + + return { source, buckets, leaves }; +} + +const GENESIS_PARENT = { seq: 0n, totalMsgCount: 0n }; + +// Pinned cross-layer values shared with the L1 Foundry harness: genesisTime=100000, slotDuration=36, +// INBOX_LAG_SECONDS=12. +const GENESIS_TIME = 100000n; +const SLOT_DURATION = 36n; +const LAG = 12n; +const cutoffForSlot = (slot: bigint) => GENESIS_TIME + (slot - 1n) * SLOT_DURATION - LAG; + +describe('selectInboxBucketForBlock', () => { + const baseInput = { + lagSeconds: LAG, + checkpointStartTotalMsgCount: 0n, + perBlockCap: 1024, + perCheckpointCap: 1024, + isLastBlock: false, + cutoffTimestamp: 0n, + }; + + it('consumes nothing from an empty Inbox', async () => { + const { source } = makeSource([]); + const result = await selectInboxBucketForBlock({ + ...baseInput, + messageSource: source, + now: 1_000_000n, + parent: GENESIS_PARENT, + }); + expect(result.consume).toBe(false); + }); + + it('picks the newest lag-eligible bucket and derives its bundle from genesis', async () => { + const { source } = makeSource([ + { seq: 1n, timestamp: 100n, msgCount: 3 }, + { seq: 2n, timestamp: 200n, msgCount: 2 }, + { seq: 3n, timestamp: 300n, msgCount: 4 }, + ]); + // now - lag = 250 -> newest bucket at-or-before 250 is seq 2. + const result = await selectInboxBucketForBlock({ + ...baseInput, + messageSource: source, + now: 250n + LAG, + parent: GENESIS_PARENT, + }); + expect(result).toMatchObject({ consume: true }); + if (result.consume) { + expect(result.bucket.seq).toBe(2n); + expect(result.bundle).toHaveLength(5); // buckets 1 (3) + 2 (2) + } + }); + + it('treats a bucket exactly lagSeconds old as eligible (inclusive lag boundary)', async () => { + const { source } = makeSource([{ seq: 1n, timestamp: 500n, msgCount: 1 }]); + // Bucket age exactly == lag: timestamp == now - lag. + const result = await selectInboxBucketForBlock({ + ...baseInput, + messageSource: source, + now: 500n + LAG, + parent: GENESIS_PARENT, + }); + expect(result.consume).toBe(true); + + // One second younger than the lag boundary: not yet eligible. + const tooYoung = await selectInboxBucketForBlock({ + ...baseInput, + messageSource: source, + now: 500n + LAG - 1n, + parent: GENESIS_PARENT, + }); + expect(tooYoung.consume).toBe(false); + }); + + it('walks back to the newest bucket that fits the per-block cap', async () => { + const { source } = makeSource([ + { seq: 1n, timestamp: 100n, msgCount: 200 }, + { seq: 2n, timestamp: 200n, msgCount: 200 }, + { seq: 3n, timestamp: 300n, msgCount: 200 }, + ]); + // Newest eligible is seq 3 (600 msgs from genesis), but perBlockCap=400 only fits through seq 2. + const result = await selectInboxBucketForBlock({ + ...baseInput, + messageSource: source, + now: 300n + LAG, + parent: GENESIS_PARENT, + perBlockCap: 400, + }); + expect(result).toMatchObject({ consume: true }); + if (result.consume) { + expect(result.bucket.seq).toBe(2n); + expect(result.bundle).toHaveLength(400); + } + }); + + it('accumulates the per-checkpoint cap across blocks', async () => { + const { source, buckets } = makeSource([ + { seq: 1n, timestamp: 100n, msgCount: 600 }, + { seq: 2n, timestamp: 200n, msgCount: 600 }, + ]); + // Block 1 already consumed bucket 1 (600 msgs). perCheckpointCap=1000 leaves only 400 headroom, but bucket 2 + // would bring the checkpoint total to 1200 > 1000, so block 2 consumes nothing (cap-escape). + const result = await selectInboxBucketForBlock({ + ...baseInput, + messageSource: source, + now: 200n + LAG, + parent: { seq: 1n, totalMsgCount: buckets.get(1n)!.totalMsgCount }, + checkpointStartTotalMsgCount: 0n, + perCheckpointCap: 1000, + }); + expect(result.consume).toBe(false); + }); + + it('advances across sub-slots as buckets arrive', async () => { + const { source } = makeSource([ + { seq: 1n, timestamp: 100n, msgCount: 2 }, + { seq: 2n, timestamp: 260n, msgCount: 3 }, + ]); + // Block 1 at now-lag=150: only bucket 1 eligible. + const first = await selectInboxBucketForBlock({ + ...baseInput, + messageSource: source, + now: 150n + LAG, + parent: GENESIS_PARENT, + }); + expect(first).toMatchObject({ consume: true }); + if (!first.consume) { + return; + } + expect(first.bucket.seq).toBe(1n); + + // Block 2, later sub-slot (now-lag=300): bucket 2 has now aged in; parent is block 1's bucket. + const second = await selectInboxBucketForBlock({ + ...baseInput, + messageSource: source, + now: 300n + LAG, + parent: { seq: first.bucket.seq, totalMsgCount: first.bucket.totalMsgCount }, + checkpointStartTotalMsgCount: 0n, + }); + expect(second).toMatchObject({ consume: true }); + if (second.consume) { + expect(second.bucket.seq).toBe(2n); + expect(second.bundle).toHaveLength(3); // only bucket 2's messages, not bucket 1's + expect(second.bundle).toEqual(await source.getL1ToL2MessagesBetweenBuckets(1n, 2n)); + } + }); + + it('applies the cutoff as a consumption floor on the last block', async () => { + // Bucket sits at the cutoff for slot 10 but is younger than now-lag, so a non-last block skips it. + const cutoff = cutoffForSlot(10n); + const { source } = makeSource([{ seq: 1n, timestamp: cutoff, msgCount: 5 }]); + + const nonLast = await selectInboxBucketForBlock({ + ...baseInput, + messageSource: source, + now: cutoff + LAG - 1n, // bucket is one second too young for lag eligibility + parent: GENESIS_PARENT, + isLastBlock: false, + cutoffTimestamp: cutoff, + }); + expect(nonLast.consume).toBe(false); + + const last = await selectInboxBucketForBlock({ + ...baseInput, + messageSource: source, + now: cutoff + LAG - 1n, + parent: GENESIS_PARENT, + isLastBlock: true, + cutoffTimestamp: cutoff, + }); + expect(last).toMatchObject({ consume: true }); + if (last.consume) { + expect(last.bucket.seq).toBe(1n); + } + }); + + it('makes a bucket exactly at the cutoff mandatory and one past it optional (§13 boundary)', async () => { + const cutoff = cutoffForSlot(10n); // 100312 + // A bucket AT the cutoff must be consumed by the last block; a bucket one second past it need not be. + const atCutoff = makeSource([{ seq: 1n, timestamp: cutoff, msgCount: 1 }]); + const pastCutoff = makeSource([{ seq: 1n, timestamp: cutoff + 1n, msgCount: 1 }]); + + const mustConsume = await selectInboxBucketForBlock({ + ...baseInput, + messageSource: atCutoff.source, + now: cutoff, // before lag would make it eligible, forcing reliance on the cutoff floor + parent: GENESIS_PARENT, + isLastBlock: true, + cutoffTimestamp: cutoff, + }); + expect(mustConsume.consume).toBe(true); + + const mayskip = await selectInboxBucketForBlock({ + ...baseInput, + messageSource: pastCutoff.source, + now: cutoff, + parent: GENESIS_PARENT, + isLastBlock: true, + cutoffTimestamp: cutoff, + }); + expect(mayskip.consume).toBe(false); + }); + + it('consumes nothing when even the first bucket past the parent exceeds the per-checkpoint cap (cap-escape)', async () => { + const { source } = makeSource([{ seq: 1n, timestamp: 100n, msgCount: 2000 }]); + const result = await selectInboxBucketForBlock({ + ...baseInput, + messageSource: source, + now: 100n + LAG, + parent: GENESIS_PARENT, + perBlockCap: 4096, + perCheckpointCap: 1024, + }); + expect(result.consume).toBe(false); + }); +}); diff --git a/yarn-project/sequencer-client/src/sequencer/inbox_bucket_selector.ts b/yarn-project/sequencer-client/src/sequencer/inbox_bucket_selector.ts new file mode 100644 index 000000000000..b8a984bcb89a --- /dev/null +++ b/yarn-project/sequencer-client/src/sequencer/inbox_bucket_selector.ts @@ -0,0 +1,122 @@ +import type { Fr } from '@aztec/foundation/curves/bn254'; +import type { InboxBucket, L1ToL2MessageSource } from '@aztec/stdlib/messaging'; + +/** The subset of the archiver's Inbox-bucket queries the selector needs. */ +export type InboxBucketSource = Pick< + L1ToL2MessageSource, + 'getInboxBucket' | 'getLatestInboxBucketAtOrBefore' | 'getL1ToL2MessagesBetweenBuckets' +>; + +/** + * The last-consumed Inbox bucket a block streams from. Only the sequence number and cumulative message count are + * needed: the sequence number bounds the derived bundle, and the count is the per-block/per-checkpoint cap origin. + * At a checkpoint's first block this is the parent checkpoint's last-consumed bucket; the genesis base case is + * `{ seq: 0, totalMsgCount: 0 }` (bundles derive from the start of the Inbox). + */ +export type ConsumedBucketCursor = Pick; + +/** Inputs to a single block's streaming Inbox-bucket selection. */ +export type SelectInboxBucketInput = { + /** Archiver Inbox-bucket queries. */ + messageSource: InboxBucketSource; + /** Wall-clock time of this sub-slot, in seconds; the lag-eligibility anchor. */ + now: bigint; + /** Minimum bucket age in seconds (`INBOX_LAG_SECONDS`) for a bucket to be lag-eligible this sub-slot. */ + lagSeconds: bigint; + /** The last bucket consumed by this checkpoint so far (parent checkpoint's at the first block). */ + parent: ConsumedBucketCursor; + /** Cumulative Inbox message count consumed as of the parent checkpoint; the per-checkpoint cap origin. */ + checkpointStartTotalMsgCount: bigint; + /** Maximum number of messages this block may consume. */ + perBlockCap: number; + /** Maximum number of messages the checkpoint may consume in total. */ + perCheckpointCap: number; + /** True on the checkpoint's final block, where the censorship cutoff becomes a consumption floor. */ + isLastBlock: boolean; + /** + * Censorship cutoff timestamp, `toTimestamp(slot - 1) - lagSeconds` (mirrors `ProposeLib.validateInboxConsumption`). + * Buckets opened at or before it are mandatory to consume by the checkpoint's last block. + */ + cutoffTimestamp: bigint; +}; + +/** Result of a block's streaming Inbox-bucket selection. */ +export type InboxBucketSelection = + | { + /** The block consumes messages, advancing to `bucket`. */ + consume: true; + /** The newest bucket this block consumes through. */ + bucket: InboxBucket; + /** The message leaves consumed this block, in insertion order (may be empty for an empty bucket). */ + bundle: Fr[]; + } + | { + /** The block consumes nothing; it reuses the parent bucket reference. */ + consume: false; + }; + +/** + * Selects the newest Inbox bucket a block streams from, mirroring the L1 consumption predicate in + * `ProposeLib.validateInboxConsumption`. The policy, per block: + * + * 1. Pick the newest lag-eligible bucket: the newest bucket opened at or before `now - lagSeconds`. On the + * checkpoint's last block, also consider the cutoff bucket (newest opened at or before `cutoffTimestamp`) and take + * whichever is newer, so the checkpoint reaches the censorship floor even if the sub-slot lag preferred less. + * 2. If nothing is newer than the parent bucket, consume nothing. + * 3. Otherwise walk back from the candidate to the newest bucket whose consumption fits both the per-block cap + * (`bucket.totalMsgCount - parent.totalMsgCount`) and the per-checkpoint cap + * (`bucket.totalMsgCount - checkpointStartTotalMsgCount`). If even the first bucket past the parent overshoots the + * per-checkpoint cap, consume nothing — the L1 cap-escape (`ProposeLib` allows leaving a bucket unconsumed when + * consuming through it would exceed the per-checkpoint cap). + * + * The `<=` comparisons make a bucket exactly `lagSeconds` old lag-eligible and a bucket exactly at the cutoff + * mandatory, matching the strict `>` "past cutoff" test on L1 (`next.timestamp > cutoff` leaves it optional). + * + * A single bucket never exceeds the per-block cap by construction (the Inbox bucket size is at most the per-block cap), + * so per-block walk-back always lands on at least one bucket; only the per-checkpoint cap can force consuming nothing. + */ +export async function selectInboxBucketForBlock(input: SelectInboxBucketInput): Promise { + const { + messageSource, + now, + lagSeconds, + parent, + checkpointStartTotalMsgCount, + perBlockCap, + perCheckpointCap, + isLastBlock, + cutoffTimestamp, + } = input; + + let candidate = await messageSource.getLatestInboxBucketAtOrBefore(now - lagSeconds); + + if (isLastBlock) { + const cutoffBucket = await messageSource.getLatestInboxBucketAtOrBefore(cutoffTimestamp); + if (cutoffBucket !== undefined && (candidate === undefined || cutoffBucket.seq > candidate.seq)) { + candidate = cutoffBucket; + } + } + + if (candidate === undefined || candidate.seq <= parent.seq) { + return { consume: false }; + } + + const perBlockCapBig = BigInt(perBlockCap); + const perCheckpointCapBig = BigInt(perCheckpointCap); + + let selected: InboxBucket | undefined = candidate; + while (selected !== undefined && selected.seq > parent.seq) { + const blockCount = selected.totalMsgCount - parent.totalMsgCount; + const checkpointCount = selected.totalMsgCount - checkpointStartTotalMsgCount; + if (blockCount <= perBlockCapBig && checkpointCount <= perCheckpointCapBig) { + const bundle = await messageSource.getL1ToL2MessagesBetweenBuckets(parent.seq, selected.seq); + return { consume: true, bucket: selected, bundle }; + } + if (selected.seq - 1n <= parent.seq) { + break; + } + selected = await messageSource.getInboxBucket(selected.seq - 1n); + } + + return { consume: false }; +} diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts index fb95eafd1485..95a866e36a3e 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts @@ -35,7 +35,9 @@ import type { ChainConfig } from '@aztec/stdlib/config'; import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers'; import { GasFees } from '@aztec/stdlib/gas'; import { + type MerkleTreeWriteOperations, type SequencerConfig, + type TreeInfo, WorldStateRunningState, type WorldStateSyncStatus, type WorldStateSynchronizer, @@ -166,6 +168,8 @@ describe('sequencer', () => { expect.any(Checkpoint), attestationsAndSigners, getSignatures()[0].signature, + // Streaming inbox: the checkpoint job passes the parent bucket hint (genesis => 0n). + 0n, expect.objectContaining({ txTimeoutAt: expect.any(Date), }), @@ -285,6 +289,13 @@ describe('sequencer', () => { }, } satisfies WorldStateSynchronizerStatus), }); + // Streaming inbox: the checkpoint job forks world state and resolves the parent Inbox bucket + // from the fork's L1-to-L2 tree leaf count. Default to an empty tree so it starts at the genesis bucket. + const mockFork = mock({ + [Symbol.asyncDispose]: jest.fn().mockReturnValue(Promise.resolve()) as () => Promise, + }); + mockFork.getTreeInfo.mockResolvedValue({ size: 0n } as TreeInfo); + worldState.fork.mockResolvedValue(mockFork); // Create fake CheckpointsBuilder and CheckpointBuilder // Uses blockProvider to return the current `block` variable (set per-test) @@ -355,6 +366,14 @@ describe('sequencer', () => { }, }), }); + l1ToL2MessageSource.getInboxBucketByTotalMsgCount.mockResolvedValue({ + seq: 0n, + inboxRollingHash: Fr.ZERO, + totalMsgCount: 0n, + timestamp: 0n, + msgCount: 0, + lastMessageIndex: 0n, + }); validatorClient = mock(); validatorClient.collectAttestations.mockImplementation(() => Promise.resolve(getCheckpointAttestations())); @@ -839,6 +858,8 @@ describe('sequencer', () => { expect.any(Checkpoint), attestationsAndSigners, getSignatures()[0].signature, + // Streaming inbox: the checkpoint job passes the parent bucket hint (genesis => 0n). + 0n, expect.objectContaining({ txTimeoutAt: expect.any(Date), }), @@ -1554,6 +1575,7 @@ describe('sequencer', () => { blockCount: 1, totalManaUsed: 0n, feeAssetPriceModifier: 0n, + inboxMsgTotal: 0n, }); await sequencer.work(); @@ -1679,6 +1701,7 @@ describe('sequencer', () => { blockCount: 1, totalManaUsed: 0n, feeAssetPriceModifier: 0n, + inboxMsgTotal: 0n, }); await sequencer.work(); @@ -1808,6 +1831,7 @@ describe('sequencer', () => { blockCount: 1, totalManaUsed: 0n, feeAssetPriceModifier: 0n, + inboxMsgTotal: 0n, }, }); diff --git a/yarn-project/sequencer-client/src/test/mock_checkpoint_builder.ts b/yarn-project/sequencer-client/src/test/mock_checkpoint_builder.ts index b7cfae439637..f53af34fc862 100644 --- a/yarn-project/sequencer-client/src/test/mock_checkpoint_builder.ts +++ b/yarn-project/sequencer-client/src/test/mock_checkpoint_builder.ts @@ -1,5 +1,6 @@ import { type BlockNumber, CheckpointNumber, IndexWithinCheckpoint } from '@aztec/foundation/branded-types'; import { Fr } from '@aztec/foundation/curves/bn254'; +import type { LoggerBindings } from '@aztec/foundation/log'; import { unfreeze } from '@aztec/foundation/types'; import { L2Block } from '@aztec/stdlib/block'; import { Checkpoint } from '@aztec/stdlib/checkpoint'; @@ -203,7 +204,6 @@ export class MockCheckpointsBuilder implements ICheckpointsBuilder { public startCheckpointCalls: Array<{ checkpointNumber: CheckpointNumber; constants: CheckpointGlobalVariables; - l1ToL2Messages: Fr[]; previousCheckpointOutHashes: Fr[]; feeAssetPriceModifier: bigint; }> = []; @@ -261,15 +261,14 @@ export class MockCheckpointsBuilder implements ICheckpointsBuilder { checkpointNumber: CheckpointNumber, constants: CheckpointGlobalVariables, feeAssetPriceModifier: bigint, - l1ToL2Messages: Fr[], previousCheckpointOutHashes: Fr[], _previousInboxRollingHash: Fr, _fork: MerkleTreeWriteOperations, + _bindings?: LoggerBindings, ): Promise { this.startCheckpointCalls.push({ checkpointNumber, constants, - l1ToL2Messages, previousCheckpointOutHashes, feeAssetPriceModifier, }); diff --git a/yarn-project/stdlib/src/block/l2_block.ts b/yarn-project/stdlib/src/block/l2_block.ts index e7c78f332a1d..06cd9133ac56 100644 --- a/yarn-project/stdlib/src/block/l2_block.ts +++ b/yarn-project/stdlib/src/block/l2_block.ts @@ -116,7 +116,6 @@ export class L2Block { } public toBlockBlobData(): BlockBlobData { - const isFirstBlock = this.indexWithinCheckpoint === 0; return { blockEndMarker: { numTxs: this.body.txEffects.length, @@ -134,7 +133,8 @@ export class L2Block { noteHashRoot: this.header.state.partial.noteHashTree.root, nullifierRoot: this.header.state.partial.nullifierTree.root, publicDataRoot: this.header.state.partial.publicDataTree.root, - l1ToL2MessageRoot: isFirstBlock ? this.header.state.l1ToL2MessageTree.root : undefined, + // Every block carries its own post-bundle l1-to-l2 message tree root. + l1ToL2MessageRoot: this.header.state.l1ToL2MessageTree.root, txs: this.body.toTxBlobData(), }; } diff --git a/yarn-project/stdlib/src/checkpoint/checkpoint_data.ts b/yarn-project/stdlib/src/checkpoint/checkpoint_data.ts index b8ce95fd1231..f8ef256a686c 100644 --- a/yarn-project/stdlib/src/checkpoint/checkpoint_data.ts +++ b/yarn-project/stdlib/src/checkpoint/checkpoint_data.ts @@ -42,6 +42,18 @@ export type ProposedOnlyCheckpointData = { feeAssetPriceModifier: bigint; }; +/** Inbox consumption record of a proposed checkpoint, mirroring what `propose` writes for it on L1. */ +export type ProposedInboxConsumption = { + /** + * Cumulative Inbox message count consumed as of this checkpoint: its last block's L1-to-L2 message tree leaf count + * under compact indexing. L1 records this per checkpoint and reads it back as the parent total + * when validating the next checkpoint's consumption, so a simulation against a not-yet-mined parent must override + * the parent's cell with it — otherwise the parent reads as having consumed nothing and the child's consumption + * looks larger than it is. + */ + inboxMsgTotal: bigint; +}; + /** Lightweight checkpoint metadata without full block data. */ export type CheckpointData = CommonCheckpointData & StorageEnrichedCheckpointData & L1EnrichedCheckpointData; @@ -51,7 +63,7 @@ export type ProposedCheckpointInput = CommonCheckpointData & ProposedOnlyCheckpo /** Full data for a proposed checkpoint (proposed but not yet L1-confirmed). * Includes fee-relevant fields used during pipelining to compute the fee header override. */ -export type ProposedCheckpointData = ProposedCheckpointInput & StorageEnrichedCheckpointData; +export type ProposedCheckpointData = ProposedCheckpointInput & StorageEnrichedCheckpointData & ProposedInboxConsumption; export const ProposedCheckpointDataSchema = z.object({ checkpointNumber: CheckpointNumberSchema, @@ -62,6 +74,7 @@ export const ProposedCheckpointDataSchema = z.object({ blockCount: z.number(), totalManaUsed: schemas.BigInt, feeAssetPriceModifier: schemas.BigInt, + inboxMsgTotal: schemas.BigInt, }); export const CheckpointDataSchema = z diff --git a/yarn-project/stdlib/src/checkpoint/previous_checkpoint_out_hashes.test.ts b/yarn-project/stdlib/src/checkpoint/previous_checkpoint_out_hashes.test.ts index b3994742b7b5..5256cf7e5302 100644 --- a/yarn-project/stdlib/src/checkpoint/previous_checkpoint_out_hashes.test.ts +++ b/yarn-project/stdlib/src/checkpoint/previous_checkpoint_out_hashes.test.ts @@ -37,6 +37,7 @@ function proposedParent(number: number, slot: number, outHash: Fr): ProposedChec blockCount: 1, totalManaUsed: 0n, feeAssetPriceModifier: 0n, + inboxMsgTotal: 0n, }; } diff --git a/yarn-project/stdlib/src/checkpoint/simulation_overrides.test.ts b/yarn-project/stdlib/src/checkpoint/simulation_overrides.test.ts index 0d30e9492eb1..3c7e5bea83d8 100644 --- a/yarn-project/stdlib/src/checkpoint/simulation_overrides.test.ts +++ b/yarn-project/stdlib/src/checkpoint/simulation_overrides.test.ts @@ -31,6 +31,7 @@ describe('computePipelinedParentFeeHeader', () => { blockCount: 1, totalManaUsed: 5000n, feeAssetPriceModifier: 100n, + inboxMsgTotal: 0n, }; const grandparentFeeHeader: FeeHeader = { @@ -157,6 +158,7 @@ describe('buildCheckpointSimulationOverridesPlan', () => { blockCount: 1, totalManaUsed: 5000n, feeAssetPriceModifier: 100n, + inboxMsgTotal: 1500n, }; } @@ -194,6 +196,8 @@ describe('buildCheckpointSimulationOverridesPlan', () => { expect(plan?.pendingCheckpointState?.outHash).toEqual(proposedData.checkpointOutHash); expect(plan?.pendingCheckpointState?.payloadDigest).toBeDefined(); expect(plan?.pendingCheckpointState?.feeHeader).toBeDefined(); + // Without this the parent reads as having consumed no Inbox messages, inflating the child's consumed count. + expect(plan?.pendingCheckpointState?.inboxMsgTotal).toEqual(proposedData.inboxMsgTotal); }); it('throws when the pipelined parent does not match the expected parent checkpoint', async () => { diff --git a/yarn-project/stdlib/src/checkpoint/simulation_overrides.ts b/yarn-project/stdlib/src/checkpoint/simulation_overrides.ts index 937413b1d518..162603b1bfc5 100644 --- a/yarn-project/stdlib/src/checkpoint/simulation_overrides.ts +++ b/yarn-project/stdlib/src/checkpoint/simulation_overrides.ts @@ -73,21 +73,25 @@ export async function buildCheckpointSimulationOverridesPlan( builder.withChainTips({ pending: overridenChainTip, proven: overridenChainTip }); if (input.proposedCheckpointData) { - const { header, archive, checkpointOutHash, feeAssetPriceModifier } = input.proposedCheckpointData; + const { header, archive, checkpointOutHash, feeAssetPriceModifier, inboxMsgTotal } = input.proposedCheckpointData; builder.withPendingArchive(archive.root); // Override every locally-derivable `tempCheckpointLogs[parent]` field that L1 will eventually - // write. `slotNumber` is load-bearing for `STFLib.canPruneAtTime`: without it the cell reads + // write. `slotNumber` is required by `STFLib.canPruneAtTime`: without it the cell reads // slotNumber 0, the contract treats the pending tip as belonging to an expired epoch, and // `getEffectivePendingCheckpointNumber` silently collapses pending back to proven — producing - // a spurious `Rollup__InvalidArchive` against the on-chain genesis archive. The other fields - // (headerHash, outHash, payloadDigest) are not strictly load-bearing for `canProposeAt` / - // `validateBlockHeader`, but mirroring the full cell keeps the simulation byte-faithful with - // what the actual `propose()` send will observe, which is a defense against future reads - // taking dependencies on them. + // a spurious `Rollup__InvalidArchive` against the on-chain genesis archive. `inboxMsgTotal` is + // read by `ProposeLib.validateInboxConsumption` as the parent's consumed total: left at zero it + // makes our consumption look like the Inbox's entire history and trips + // `Rollup__TooManyInboxMessagesConsumed`. The remaining fields (headerHash, outHash, + // payloadDigest) are not read by `canProposeAt` / `validateBlockHeader`, but mirroring the full + // cell keeps the simulation byte-faithful with what the actual `propose()` send will observe, + // which is a defense against future reads taking dependencies on them. The parent's + // `inboxConsumedBucket` shares the word and stays zero: no contract path reads it back. builder.withPendingTempCheckpointLogFields({ headerHash: header.hash(), outHash: checkpointOutHash, slotNumber: header.slotNumber, + inboxMsgTotal, payloadDigest: computeCheckpointPayloadDigest({ header, archiveRoot: archive.root, diff --git a/yarn-project/stdlib/src/deserialization/deserialization.test.ts b/yarn-project/stdlib/src/deserialization/deserialization.test.ts index 274953c9af19..85482da68ba4 100644 --- a/yarn-project/stdlib/src/deserialization/deserialization.test.ts +++ b/yarn-project/stdlib/src/deserialization/deserialization.test.ts @@ -1,7 +1,6 @@ import { NUM_BLOCK_END_BLOB_FIELDS, NUM_CHECKPOINT_END_MARKER_FIELDS, - NUM_FIRST_BLOCK_END_BLOB_FIELDS, getNumTxBlobFields, } from '@aztec/blob-lib/encoding'; import { BLOBS_PER_CHECKPOINT, FIELDS_PER_BLOB } from '@aztec/constants'; @@ -23,13 +22,14 @@ describe('MAX_CAPACITY_BLOCKS_PER_CHECKPOINT', () => { }); // Largest checkpoint that fits in the blob budget with the most compact construction: - // one (possibly empty) first block + (N - 1) single-nullifier blocks + a checkpoint-end marker. + // one (possibly empty) first block + (N - 1) single-nullifier blocks + a checkpoint-end marker. Every block + // spends the same block-end overhead, including the per-block l1-to-l2 root. // This is the real ceiling the proving system / L1 enforce — there is no explicit block-count cap. const blobBudget = BLOBS_PER_CHECKPOINT * FIELDS_PER_BLOB; const maxBlocksThatFitInBlobs = 1 + Math.floor( - (blobBudget - NUM_CHECKPOINT_END_MARKER_FIELDS - NUM_FIRST_BLOCK_END_BLOB_FIELDS) / + (blobBudget - NUM_CHECKPOINT_END_MARKER_FIELDS - NUM_BLOCK_END_BLOB_FIELDS) / (NUM_BLOCK_END_BLOB_FIELDS + MIN_TX_BLOB_FIELDS), ); @@ -42,7 +42,7 @@ describe('MAX_CAPACITY_BLOCKS_PER_CHECKPOINT', () => { it('matches the documented ceiling for the current blob constants', () => { // Pins the arithmetic so a change to BLOBS_PER_CHECKPOINT / FIELDS_PER_BLOB / block-field counts // is noticed and the constant + its comment get revisited. - expect(maxBlocksThatFitInBlobs).toBe(2457); - expect(MAX_CAPACITY_BLOCKS_PER_CHECKPOINT).toBe(2457); + expect(maxBlocksThatFitInBlobs).toBe(2234); + expect(MAX_CAPACITY_BLOCKS_PER_CHECKPOINT).toBe(2234); }); }); diff --git a/yarn-project/stdlib/src/deserialization/index.ts b/yarn-project/stdlib/src/deserialization/index.ts index fc5e693d9357..0448f0733ca8 100644 --- a/yarn-project/stdlib/src/deserialization/index.ts +++ b/yarn-project/stdlib/src/deserialization/index.ts @@ -22,23 +22,23 @@ export const MAX_COMMITTEE_SIZE = 2048; * * budget = BLOBS_PER_CHECKPOINT * FIELDS_PER_BLOB = 6 * 4096 = 24,576 fields * - * per-block minimal blob footprint: - * first block (may be empty) = 7 fields (NUM_FIRST_BLOCK_END_BLOB_FIELDS) - * every other (needs >= 1 tx) = 6 + 4 = 10 fields + * per-block minimal blob footprint (every block carries a per-block l1-to-l2 root): + * first block (may be empty) = 7 fields (NUM_BLOCK_END_BLOB_FIELDS) + * every other (needs >= 1 tx) = 7 + 4 = 11 fields * (NUM_BLOCK_END_BLOB_FIELDS + a 4-field minimal tx: * tx start marker + tx hash + fee + 1 mandatory nullifier) * + 1 checkpoint-end marker (NUM_CHECKPOINT_END_MARKER_FIELDS) * - * max N: 7 + 10*(N - 1) + 1 <= 24,576 => 10*(N - 1) <= 24,568 => N <= 2,457 + * max N: 7 + 11*(N - 1) + 1 <= 24,576 => 11*(N - 1) <= 24,568 => N <= 2,234 * * Only the first block may be empty; every other block needs >= 1 tx (the circuits have no empty-tx - * variant for non-first blocks), so 2457 is the largest provable checkpoint. The blob format can encode - * more blocks than this (up to ~4095 with all-empty blocks), but such a checkpoint is unprovable and + * variant for non-first blocks), so 2234 is the largest provable checkpoint. The blob format can encode + * more blocks than this (up to ~3510 with all-empty blocks), but such a checkpoint is unprovable and * can only reach L1 with a malicious committee supermajority — a terminal network compromise where a * wedged archiver is an acceptable outcome. We therefore bound ingest to the provable maximum. * Invariant checked by the unit test in deserialization.test.ts. */ -export const MAX_CAPACITY_BLOCKS_PER_CHECKPOINT = 2457; +export const MAX_CAPACITY_BLOCKS_PER_CHECKPOINT = 2234; /** * Max blocks per checkpoint we are willing to build or attest to (conservative client policy, and the diff --git a/yarn-project/stdlib/src/gas/tx_gas_limits.test.ts b/yarn-project/stdlib/src/gas/tx_gas_limits.test.ts index a6621bf28fef..0624244922a6 100644 --- a/yarn-project/stdlib/src/gas/tx_gas_limits.test.ts +++ b/yarn-project/stdlib/src/gas/tx_gas_limits.test.ts @@ -37,8 +37,7 @@ describe('computeNetworkTxGasLimits', () => { DA_GAS_PER_FIELD, ); const firstBlockBlobFieldCap = Math.ceil( - ((BLOBS_PER_CHECKPOINT * FIELDS_PER_BLOB - NUM_CHECKPOINT_END_MARKER_FIELDS - getNumBlockEndBlobFields(true)) / - b) * + ((BLOBS_PER_CHECKPOINT * FIELDS_PER_BLOB - NUM_CHECKPOINT_END_MARKER_FIELDS - getNumBlockEndBlobFields()) / b) * MIN_PER_BLOCK_DA_ALLOCATION_MULTIPLIER, ); expect(admittedBlobFields).toBeLessThanOrEqual(firstBlockBlobFieldCap); diff --git a/yarn-project/stdlib/src/gas/tx_gas_limits.ts b/yarn-project/stdlib/src/gas/tx_gas_limits.ts index 17c00bfb8548..e73d8ed79c4c 100644 --- a/yarn-project/stdlib/src/gas/tx_gas_limits.ts +++ b/yarn-project/stdlib/src/gas/tx_gas_limits.ts @@ -1,8 +1,4 @@ -import { - NUM_BLOCK_END_BLOB_FIELDS, - NUM_CHECKPOINT_END_MARKER_FIELDS, - NUM_FIRST_BLOCK_END_BLOB_FIELDS, -} from '@aztec/blob-lib/encoding'; +import { NUM_BLOCK_END_BLOB_FIELDS, NUM_CHECKPOINT_END_MARKER_FIELDS } from '@aztec/blob-lib/encoding'; import { BLOBS_PER_CHECKPOINT, DA_GAS_PER_FIELD, @@ -38,9 +34,8 @@ export const MIN_PER_BLOCK_DA_ALLOCATION_MULTIPLIER = 1.5; * raw blob capacity (`BLOBS_PER_CHECKPOINT * FIELDS_PER_BLOB * DA_GAS_PER_FIELD`) minus the fields the blob * encoding reserves for overhead that no tx pays DA gas for: * - * - one checkpoint-end marker field (`NUM_CHECKPOINT_END_MARKER_FIELDS`), - * - the first block's block-end fields (`NUM_FIRST_BLOCK_END_BLOB_FIELDS`, 7), and - * - `NUM_BLOCK_END_BLOB_FIELDS` (6) for each of the `blocks - 1` subsequent blocks. + * - one checkpoint-end marker field (`NUM_CHECKPOINT_END_MARKER_FIELDS`), and + * - `NUM_BLOCK_END_BLOB_FIELDS` (7, including the per-block l1-to-l2 root) for each of the `blocks` blocks. * * Subtracting the overhead for every block (not just the first) keeps the network DA admission limit at or * below the builder's first-block blob-field cap at every geometry. The builder is the MOST generous for the @@ -57,10 +52,7 @@ export function getDaCheckpointBudgetForTxs(maxBlocksPerCheckpoint: number): num // raw blob capacity, which would otherwise yield a negative advertised DA budget. const fields = Math.max( 0, - BLOBS_PER_CHECKPOINT * FIELDS_PER_BLOB - - NUM_CHECKPOINT_END_MARKER_FIELDS - - NUM_FIRST_BLOCK_END_BLOB_FIELDS - - (blocks - 1) * NUM_BLOCK_END_BLOB_FIELDS, + BLOBS_PER_CHECKPOINT * FIELDS_PER_BLOB - NUM_CHECKPOINT_END_MARKER_FIELDS - blocks * NUM_BLOCK_END_BLOB_FIELDS, ); return fields * DA_GAS_PER_FIELD; } diff --git a/yarn-project/stdlib/src/interfaces/archiver.test.ts b/yarn-project/stdlib/src/interfaces/archiver.test.ts index 5ace4e328afe..c27cffd488c6 100644 --- a/yarn-project/stdlib/src/interfaces/archiver.test.ts +++ b/yarn-project/stdlib/src/interfaces/archiver.test.ts @@ -36,6 +36,7 @@ import { type LogResult, randomLogResult } from '../logs/log_result.js'; import type { PrivateLogsQuery, PublicLogsQuery } from '../logs/logs_query.js'; import { SiloedTag } from '../logs/siloed_tag.js'; import { Tag } from '../logs/tag.js'; +import type { InboxBucket } from '../messaging/inbox_bucket.js'; import { CheckpointHeader } from '../rollup/checkpoint_header.js'; import { getTokenContractArtifact } from '../tests/fixtures.js'; import { AppendOnlyTreeSnapshot } from '../trees/append_only_tree_snapshot.js'; @@ -213,6 +214,31 @@ describe('ArchiverApiSchema', () => { expect(result).toBe(1n); }); + it('getLatestInboxBucketAtOrBefore', async () => { + const result = await context.client.getLatestInboxBucketAtOrBefore(123n); + expect(result).toMatchObject({ seq: 1n, msgCount: 3, totalMsgCount: 3n }); + }); + + it('getInboxBucket', async () => { + const result = await context.client.getInboxBucket(2n); + expect(result).toMatchObject({ seq: 2n, msgCount: 3 }); + }); + + it('getInboxBucketByTotalMsgCount', async () => { + const result = await context.client.getInboxBucketByTotalMsgCount(3n); + expect(result).toMatchObject({ seq: 2n, totalMsgCount: 3n, msgCount: 3 }); + }); + + it('getL1ToL2MessagesBetweenBuckets', async () => { + const result = await context.client.getL1ToL2MessagesBetweenBuckets(0n, 3n); + expect(result).toEqual([expect.any(Fr)]); + }); + + it('getL1ToL2MessagesBetweenLeafCounts', async () => { + const result = await context.client.getL1ToL2MessagesBetweenLeafCounts(0n, 3n); + expect(result).toEqual([expect.any(Fr)]); + }); + it('registerContractFunctionSignatures', async () => { await context.client.registerContractFunctionSignatures(['test()']); }); @@ -258,6 +284,7 @@ describe('ArchiverApiSchema', () => { startBlock: 1, totalManaUsed: 1n, feeAssetPriceModifier: 1n, + inboxMsgTotal: 1n, }); }); @@ -379,6 +406,7 @@ class MockArchiver implements ArchiverApi { startBlock: BlockNumber(1), totalManaUsed: 1n, feeAssetPriceModifier: 1n, + inboxMsgTotal: 1n, }); } syncImmediate() { @@ -551,6 +579,49 @@ class MockArchiver implements ArchiverApi { expect(l1ToL2Message).toBeInstanceOf(Fr); return Promise.resolve(1n); } + getLatestInboxBucketAtOrBefore(timestamp: bigint): Promise { + expect(typeof timestamp).toEqual('bigint'); + return Promise.resolve({ + seq: 1n, + inboxRollingHash: Fr.random(), + totalMsgCount: 3n, + timestamp: 100n, + msgCount: 3, + lastMessageIndex: 2n, + }); + } + getInboxBucket(seq: bigint): Promise { + expect(typeof seq).toEqual('bigint'); + return Promise.resolve({ + seq, + inboxRollingHash: Fr.random(), + totalMsgCount: 3n, + timestamp: 100n, + msgCount: 3, + lastMessageIndex: 2n, + }); + } + getInboxBucketByTotalMsgCount(totalMsgCount: bigint): Promise { + expect(typeof totalMsgCount).toEqual('bigint'); + return Promise.resolve({ + seq: 2n, + inboxRollingHash: Fr.random(), + totalMsgCount, + timestamp: 100n, + msgCount: 3, + lastMessageIndex: 2n, + }); + } + getL1ToL2MessagesBetweenBuckets(fromExclusive: bigint, toInclusive: bigint): Promise { + expect(typeof fromExclusive).toEqual('bigint'); + expect(typeof toInclusive).toEqual('bigint'); + return Promise.resolve([Fr.random()]); + } + getL1ToL2MessagesBetweenLeafCounts(startLeafCount: bigint, endLeafCount: bigint): Promise { + expect(typeof startLeafCount).toEqual('bigint'); + expect(typeof endLeafCount).toEqual('bigint'); + return Promise.resolve([Fr.random()]); + } getL1Constants(): Promise { return Promise.resolve(EmptyL1RollupConstants); } diff --git a/yarn-project/stdlib/src/interfaces/archiver.ts b/yarn-project/stdlib/src/interfaces/archiver.ts index 362da31e3a88..3728c39090cc 100644 --- a/yarn-project/stdlib/src/interfaces/archiver.ts +++ b/yarn-project/stdlib/src/interfaces/archiver.ts @@ -25,6 +25,7 @@ import { import { L1RollupConstantsSchema } from '../epoch-helpers/index.js'; import { LogResultSchema } from '../logs/log_result.js'; import { PrivateLogsQuerySchema, PublicLogsQuerySchema } from '../logs/logs_query.js'; +import { InboxBucketSchema } from '../messaging/inbox_bucket.js'; import type { L1ToL2MessageSource } from '../messaging/l1_to_l2_message_source.js'; import { L2ToL1MembershipWitnessSchema } from '../messaging/l2_to_l1_membership.js'; import { optional, schemas } from '../schemas/schemas.js'; @@ -137,6 +138,23 @@ export const ArchiverApiSchema: ApiSchemaFor = { registerContractFunctionSignatures: z.function({ input: z.tuple([z.array(z.string())]), output: z.void() }), getL1ToL2Messages: z.function({ input: z.tuple([CheckpointNumberSchema]), output: z.array(schemas.Fr) }), getL1ToL2MessageIndex: z.function({ input: z.tuple([schemas.Fr]), output: schemas.BigInt.optional() }), + getLatestInboxBucketAtOrBefore: z.function({ + input: z.tuple([schemas.BigInt]), + output: InboxBucketSchema.optional(), + }), + getInboxBucket: z.function({ input: z.tuple([schemas.BigInt]), output: InboxBucketSchema.optional() }), + getInboxBucketByTotalMsgCount: z.function({ + input: z.tuple([schemas.BigInt]), + output: InboxBucketSchema.optional(), + }), + getL1ToL2MessagesBetweenBuckets: z.function({ + input: z.tuple([schemas.BigInt, schemas.BigInt]), + output: z.array(schemas.Fr), + }), + getL1ToL2MessagesBetweenLeafCounts: z.function({ + input: z.tuple([schemas.BigInt, schemas.BigInt]), + output: z.array(schemas.Fr), + }), getDebugFunctionName: z.function({ input: z.tuple([schemas.AztecAddress, schemas.FunctionSelector]), output: optional(z.string()), diff --git a/yarn-project/stdlib/src/interfaces/aztec-node.test.ts b/yarn-project/stdlib/src/interfaces/aztec-node.test.ts index 0a177636020f..b0bfd03bf8eb 100644 --- a/yarn-project/stdlib/src/interfaces/aztec-node.test.ts +++ b/yarn-project/stdlib/src/interfaces/aztec-node.test.ts @@ -159,6 +159,11 @@ describe('AztecNodeApiSchema', () => { expect(response).toEqual(5); }); + it('getL1ToL2MessageIndex', async () => { + const response = await context.client.getL1ToL2MessageIndex(Fr.random()); + expect(response).toEqual(5n); + }); + it('getL2ToL1Messages', async () => { const response = await context.client.getL2ToL1Messages(EpochNumber(1)); expect(response.length).toBe(3); @@ -714,6 +719,10 @@ class MockAztecNode implements AztecNode { expect(l1ToL2Message).toBeInstanceOf(Fr); return Promise.resolve(CheckpointNumber(5)); } + getL1ToL2MessageIndex(l1ToL2Message: Fr): Promise { + expect(l1ToL2Message).toBeInstanceOf(Fr); + return Promise.resolve(5n); + } getL2ToL1Messages(_epoch: EpochNumber): Promise { return Promise.resolve( Array.from({ length: 3 }, (_, i) => diff --git a/yarn-project/stdlib/src/interfaces/aztec-node.ts b/yarn-project/stdlib/src/interfaces/aztec-node.ts index 644ba4de71ea..26d9a2fb0556 100644 --- a/yarn-project/stdlib/src/interfaces/aztec-node.ts +++ b/yarn-project/stdlib/src/interfaces/aztec-node.ts @@ -203,6 +203,13 @@ export interface AztecNode { /** Returns the L2 checkpoint number in which this L1 to L2 message becomes available, or undefined if not found. */ getL1ToL2MessageCheckpoint(l1ToL2Message: Fr): Promise; + /** + * Returns the compact leaf index assigned to this L1 to L2 message as soon as the node has ingested it from L1, + * before any L2 block consumes it. Returns undefined if the node has not yet seen the message. Unlike + * {@link getL1ToL2MessageCheckpoint}, this does not require the L2 chain to have advanced past the message. + */ + getL1ToL2MessageIndex(l1ToL2Message: Fr): Promise; + /** * Returns all the L2 to L1 messages in an epoch. * @@ -611,6 +618,8 @@ export const AztecNodeApiSchema: ApiSchemaFor = { getL1ToL2MessageCheckpoint: z.function({ input: z.tuple([schemas.Fr]), output: CheckpointNumberSchema.optional() }), + getL1ToL2MessageIndex: z.function({ input: z.tuple([schemas.Fr]), output: schemas.BigInt.optional() }), + getL2ToL1Messages: z.function({ input: z.tuple([EpochNumberSchema]), output: z.array(z.array(z.array(z.array(schemas.Fr)))), diff --git a/yarn-project/stdlib/src/interfaces/block-builder.ts b/yarn-project/stdlib/src/interfaces/block-builder.ts index a925cfd2c08c..6d10698dab4d 100644 --- a/yarn-project/stdlib/src/interfaces/block-builder.ts +++ b/yarn-project/stdlib/src/interfaces/block-builder.ts @@ -56,6 +56,11 @@ export type PublicProcessorLimits = { type BlockBuilderOptionsBase = PublicProcessorLimits & { /** Minimum number of successfully processed txs required. Block is rejected if fewer succeed. */ minValidTxs: number; + /** + * L1-to-L2 message leaves this block consumes, inserted into the fork's L1-to-L2 message tree before the block + * header is built. Omitted when the block consumes nothing from the Inbox. + */ + l1ToL2Messages?: Fr[]; }; /** Proposer mode: redistribution params are required. */ @@ -150,11 +155,14 @@ export interface ICheckpointsBuilder { */ getFork(blockNumber: BlockNumber, blockHash?: BlockHash): Promise; + /** + * Opens a fresh checkpoint. The checkpoint's L1-to-L2 messages are not passed here: each block carries its own + * bundle in `buildBlock`'s `l1ToL2Messages`. + */ startCheckpoint( checkpointNumber: CheckpointNumber, constants: CheckpointGlobalVariables, feeAssetPriceModifier: bigint, - l1ToL2Messages: Fr[], previousCheckpointOutHashes: Fr[], previousInboxRollingHash: Fr, fork: MerkleTreeWriteOperations, diff --git a/yarn-project/stdlib/src/messaging/append_l1_to_l2_messages.ts b/yarn-project/stdlib/src/messaging/append_l1_to_l2_messages.ts index 31f53fd47c03..fd42d32097cd 100644 --- a/yarn-project/stdlib/src/messaging/append_l1_to_l2_messages.ts +++ b/yarn-project/stdlib/src/messaging/append_l1_to_l2_messages.ts @@ -1,21 +1,13 @@ -import { NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants'; -import { padArrayEnd } from '@aztec/foundation/collection'; -import { Fr } from '@aztec/foundation/curves/bn254'; +import type { Fr } from '@aztec/foundation/curves/bn254'; import type { MerkleTreeWriteOperations } from '../interfaces/merkle_tree_operations.js'; import { MerkleTreeId } from '../trees/merkle_tree_id.js'; /** - * Pads `l1ToL2Messages` to `NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP` and appends them to the - * L1→L2 message tree of `db`. Use whenever a fork at "state before the first block of a - * checkpoint" needs to mirror what the world-state synchronizer inserts at sync time. + * Appends a block's real L1→L2 message leaves (unpadded, at compact indices) to the L1→L2 message tree of `db`. + * Use whenever a fork at "state before a block" needs to mirror what the world-state + * synchronizer inserts at sync time. */ export async function appendL1ToL2MessagesToTree(db: MerkleTreeWriteOperations, l1ToL2Messages: Fr[]): Promise { - const padded = padArrayEnd( - l1ToL2Messages, - Fr.ZERO, - NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, - 'Too many L1 to L2 messages', - ); - await db.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, padded); + await db.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2Messages); } diff --git a/yarn-project/stdlib/src/messaging/inbox_bucket.test.ts b/yarn-project/stdlib/src/messaging/inbox_bucket.test.ts new file mode 100644 index 000000000000..4211aebbf9b7 --- /dev/null +++ b/yarn-project/stdlib/src/messaging/inbox_bucket.test.ts @@ -0,0 +1,49 @@ +import { Fr } from '@aztec/foundation/curves/bn254'; +import { jsonParseWithSchema, jsonStringify } from '@aztec/foundation/json-rpc'; + +import type { InboxBucket } from './inbox_bucket.js'; +import { InboxBucketRef } from './inbox_bucket.js'; + +describe('InboxBucketRef', () => { + it('serializes and deserializes round-trip', () => { + const ref = new InboxBucketRef(42n, 1_700_000_000n, Fr.random()); + const deserialized = InboxBucketRef.fromBuffer(ref.toBuffer()); + expect(deserialized).toEqual(ref); + expect(deserialized.equals(ref)).toBe(true); + }); + + it('serializes to the fixed advertised size', () => { + const ref = InboxBucketRef.random(); + expect(ref.toBuffer().length).toBe(InboxBucketRef.SIZE); + expect(ref.getSize()).toBe(InboxBucketRef.SIZE); + }); + + it('equals distinguishes each component', () => { + const ref = new InboxBucketRef(7n, 100n, new Fr(9n)); + expect(ref.equals(new InboxBucketRef(8n, 100n, new Fr(9n)))).toBe(false); + expect(ref.equals(new InboxBucketRef(7n, 101n, new Fr(9n)))).toBe(false); + expect(ref.equals(new InboxBucketRef(7n, 100n, new Fr(10n)))).toBe(false); + expect(ref.equals(new InboxBucketRef(7n, 100n, new Fr(9n)))).toBe(true); + }); + + it('derives from a bucket snapshot', () => { + const bucket: InboxBucket = { + seq: 12n, + inboxRollingHash: new Fr(0xabcn), + totalMsgCount: 30n, + timestamp: 1_650_000_000n, + msgCount: 3, + lastMessageIndex: 29n, + }; + const ref = InboxBucketRef.fromBucket(bucket); + expect(ref.bucketSeq).toBe(bucket.seq); + expect(ref.bucketTimestamp).toBe(bucket.timestamp); + expect(ref.inboxRollingHash).toEqual(bucket.inboxRollingHash); + }); + + it('round-trips through its zod schema', () => { + const ref = InboxBucketRef.random(); + const parsed = jsonParseWithSchema(jsonStringify(ref), InboxBucketRef.schema); + expect(parsed).toEqual(ref); + }); +}); diff --git a/yarn-project/stdlib/src/messaging/inbox_bucket.ts b/yarn-project/stdlib/src/messaging/inbox_bucket.ts new file mode 100644 index 000000000000..e7700882c587 --- /dev/null +++ b/yarn-project/stdlib/src/messaging/inbox_bucket.ts @@ -0,0 +1,125 @@ +import { Fr } from '@aztec/foundation/curves/bn254'; +import { type ZodFor, schemas } from '@aztec/foundation/schemas'; +import { BufferReader, bigintToUInt64BE, serializeToBuffer } from '@aztec/foundation/serialize'; +import type { FieldsOf } from '@aztec/foundation/types'; + +import { z } from 'zod'; + +/** + * Snapshot of an Inbox rolling-hash bucket as tracked by the archiver. + * + * A bucket accumulates the message leaves inserted into the Inbox within a single L1 block (up to a per-bucket + * maximum, after which further messages in the same block spill into the next bucket). Buckets are identified by + * a dense, monotonically increasing sequence number and keyed for recency lookups by the L1 block timestamp at + * which they were opened. Mirrors the on-chain `InboxBucket` struct plus the fields the archiver derives while + * syncing. + */ +export type InboxBucket = { + /** Dense, monotonically increasing sequence number of this bucket in the Inbox ring. */ + seq: bigint; + /** Consensus rolling hash (truncated sha256 chain) after the last message absorbed into this bucket. */ + inboxRollingHash: Fr; + /** Cumulative number of messages inserted into the Inbox up to and including this bucket. */ + totalMsgCount: bigint; + /** L1 block timestamp at which this bucket was opened; the recency key for lag/cutoff comparisons, in seconds. */ + timestamp: bigint; + /** Number of messages absorbed into this bucket. */ + msgCount: number; + /** Global leaf index of the last message absorbed into this bucket. */ + lastMessageIndex: bigint; +}; + +export const InboxBucketSchema = z.object({ + seq: schemas.BigInt, + inboxRollingHash: Fr.schema, + totalMsgCount: schemas.BigInt, + timestamp: schemas.BigInt, + msgCount: schemas.Integer, + lastMessageIndex: schemas.BigInt, +}) satisfies z.ZodType; + +/** + * Reference to a settled Inbox rolling-hash bucket, carried alongside a block proposal so a + * validator can look the bucket up in its own Inbox view and derive the consumed-message bundle itself, rather than + * trusting a proposer-supplied message list. Pins the bucket by its dense sequence number and recency-key timestamp + * and asserts the expected consensus rolling hash. A wrong reference can only cause a lookup miss or hash mismatch; it + * can never change what a validator accepts, because the checkpoint header's `inboxRollingHash` remains the signed + * consensus commitment (mirrors the unsigned bucket hint on L1's `Rollup.propose`). + */ +export class InboxBucketRef { + constructor( + /** Dense, monotonically increasing sequence number of the referenced bucket in the Inbox ring. */ + public readonly bucketSeq: bigint, + /** L1 block timestamp (in seconds) at which the referenced bucket was opened; its recency key. */ + public readonly bucketTimestamp: bigint, + /** Consensus rolling hash (truncated sha256 chain) after the last message absorbed into the referenced bucket. */ + public readonly inboxRollingHash: Fr, + ) {} + + /** Serialized size in bytes: two uint64 fields plus one field element. */ + static readonly SIZE = 8 + 8 + Fr.SIZE_IN_BYTES; + + static get schema(): ZodFor { + return z + .object({ + bucketSeq: schemas.BigInt, + bucketTimestamp: schemas.BigInt, + inboxRollingHash: Fr.schema, + }) + .transform(InboxBucketRef.from); + } + + static from(fields: FieldsOf): InboxBucketRef { + return new InboxBucketRef(fields.bucketSeq, fields.bucketTimestamp, fields.inboxRollingHash); + } + + /** Derives a wire reference from a bucket snapshot as tracked by the archiver. */ + static fromBucket(bucket: InboxBucket): InboxBucketRef { + return new InboxBucketRef(bucket.seq, bucket.timestamp, bucket.inboxRollingHash); + } + + toBuffer(): Buffer { + return serializeToBuffer([ + bigintToUInt64BE(this.bucketSeq), + bigintToUInt64BE(this.bucketTimestamp), + this.inboxRollingHash, + ]); + } + + static fromBuffer(buffer: Buffer | BufferReader): InboxBucketRef { + const reader = BufferReader.asReader(buffer); + return new InboxBucketRef(reader.readUInt64(), reader.readUInt64(), reader.readObject(Fr)); + } + + getSize(): number { + return InboxBucketRef.SIZE; + } + + equals(other: InboxBucketRef): boolean { + return ( + this.bucketSeq === other.bucketSeq && + this.bucketTimestamp === other.bucketTimestamp && + this.inboxRollingHash.equals(other.inboxRollingHash) + ); + } + + static empty(): InboxBucketRef { + return new InboxBucketRef(0n, 0n, Fr.ZERO); + } + + static random(): InboxBucketRef { + return new InboxBucketRef( + BigInt(Math.floor(Math.random() * 1000)), + BigInt(Math.floor(Math.random() * 1_000_000)), + Fr.random(), + ); + } + + toInspect() { + return { + bucketSeq: this.bucketSeq.toString(), + bucketTimestamp: this.bucketTimestamp.toString(), + inboxRollingHash: this.inboxRollingHash.toString(), + }; + } +} diff --git a/yarn-project/stdlib/src/messaging/inbox_consumption.test.ts b/yarn-project/stdlib/src/messaging/inbox_consumption.test.ts new file mode 100644 index 000000000000..36d1f7653b0f --- /dev/null +++ b/yarn-project/stdlib/src/messaging/inbox_consumption.test.ts @@ -0,0 +1,95 @@ +import { SlotNumber } from '@aztec/foundation/branded-types'; + +import { describe, expect, it } from '@jest/globals'; + +import type { L1RollupConstants } from '../epoch-helpers/index.js'; +import { getInboxCutoffTimestamp, isInboxConsumptionSufficient } from './inbox_consumption.js'; + +// Cross-layer test vectors shared with the `ProposeInboxConsumptionTest` Foundry harness. +// The same vectors are asserted against `ProposeLib.validateInboxConsumption` on L1; keeping them identical here makes +// L1, TS, and the design doc agree on the cutoff formula and the mandatory-consumption boundary. +const GENESIS_TIME = 100_000n; +const SLOT_DURATION = 36; +const LAG_SECONDS = 12; + +const l1Constants = { l1GenesisTime: GENESIS_TIME, slotDuration: SLOT_DURATION } as Pick< + L1RollupConstants, + 'l1GenesisTime' | 'slotDuration' +>; + +describe('inbox_consumption', () => { + describe('getInboxCutoffTimestamp', () => { + // buildFrameStart(S) = 100000 + (S - 1) * 36; cutoff(S) = buildFrameStart(S) - 12. + it.each([ + [1, 99_988n], + [2, 100_024n], + [10, 100_312n], + [11, 100_348n], + ])('matches the A-1371 §13 cutoff table for slot %i', (slot, expectedCutoff) => { + expect(getInboxCutoffTimestamp(SlotNumber(slot), l1Constants, LAG_SECONDS)).toBe(expectedCutoff); + }); + }); + + describe('isInboxConsumptionSufficient', () => { + const base = { cutoffTimestamp: 100_312n, checkpointStartTotalMsgCount: 0n, perCheckpointCap: 1024 }; + + it('is sufficient when there is no next bucket (consumed everything)', () => { + expect(isInboxConsumptionSufficient({ ...base, nextBucket: undefined })).toBe(true); + }); + + // Boundary at S=10 (cutoff = 100312): a bucket opened exactly at the cutoff is mandatory (strict `>`). + it('is insufficient when the next bucket opened exactly at the cutoff is left unconsumed', () => { + expect(isInboxConsumptionSufficient({ ...base, nextBucket: { timestamp: 100_312n, totalMsgCount: 5n } })).toBe( + false, + ); + }); + + // Boundary at S=10: a bucket opened at cutoff + 1 is past the cutoff and need not be consumed. + it('is sufficient when the next bucket opened at cutoff + 1 is left unconsumed', () => { + expect(isInboxConsumptionSufficient({ ...base, nextBucket: { timestamp: 100_313n, totalMsgCount: 5n } })).toBe( + true, + ); + }); + + it('is sufficient via cap-escape when consuming through the next bucket would exceed the per-checkpoint cap', () => { + // Next bucket is at/before the cutoff (would otherwise be mandatory), but consuming through it consumes + // 1025 > 1024 messages, so leaving it unconsumed is allowed (the cap-escape branch). + expect( + isInboxConsumptionSufficient({ + ...base, + nextBucket: { timestamp: 100_000n, totalMsgCount: 1025n }, + }), + ).toBe(true); + }); + + it('is insufficient when consuming through the next bucket exactly reaches the per-checkpoint cap', () => { + // Delta of exactly 1024 does not escape (strict `>` on the cap), so a mandatory bucket must still be consumed. + expect( + isInboxConsumptionSufficient({ + ...base, + nextBucket: { timestamp: 100_000n, totalMsgCount: 1024n }, + }), + ).toBe(false); + }); + + it('measures the cap-escape delta from the checkpoint start, not from zero', () => { + // With a non-zero checkpoint start, only the delta consumed this checkpoint counts against the cap. + expect( + isInboxConsumptionSufficient({ + cutoffTimestamp: 100_312n, + checkpointStartTotalMsgCount: 500n, + perCheckpointCap: 1024, + nextBucket: { timestamp: 100_000n, totalMsgCount: 1524n }, + }), + ).toBe(false); // delta = 1024, not an escape + expect( + isInboxConsumptionSufficient({ + cutoffTimestamp: 100_312n, + checkpointStartTotalMsgCount: 500n, + perCheckpointCap: 1024, + nextBucket: { timestamp: 100_000n, totalMsgCount: 1525n }, + }), + ).toBe(true); // delta = 1025, escapes + }); + }); +}); diff --git a/yarn-project/stdlib/src/messaging/inbox_consumption.ts b/yarn-project/stdlib/src/messaging/inbox_consumption.ts new file mode 100644 index 000000000000..d2649dba8579 --- /dev/null +++ b/yarn-project/stdlib/src/messaging/inbox_consumption.ts @@ -0,0 +1,53 @@ +import { SlotNumber } from '@aztec/foundation/branded-types'; + +import { type L1RollupConstants, getTimestampForSlot } from '../epoch-helpers/index.js'; +import type { InboxBucket } from './inbox_bucket.js'; + +/** + * Censorship cutoff timestamp for a checkpoint proposed in `slot`, mirroring the cutoff in + * `ProposeLib.validateInboxConsumption`. A checkpoint proposed in slot `S` is built during slot `S - 1`, so + * `buildFrameStart(S) = toTimestamp(S - 1)` and `cutoff(S) = buildFrameStart(S) - lagSeconds`. Buckets opened at or + * before the cutoff are mandatory to consume by the checkpoint's last block; the strict `>` on the L1 "past cutoff" + * test (see {@link isInboxConsumptionSufficient}) makes a bucket opened exactly at the cutoff mandatory. + * + * This is the single source of truth for the cutoff formula shared by the sequencer's streaming bucket selection and + * the validator's last-block censorship check. + */ +export function getInboxCutoffTimestamp( + slot: SlotNumber, + l1Constants: Pick, + lagSeconds: number, +): bigint { + return getTimestampForSlot(SlotNumber(slot - 1), l1Constants) - BigInt(lagSeconds); +} + +/** + * Whether a checkpoint whose last-consumed bucket is immediately followed by `nextBucket` meets the censorship floor, + * mirroring the mandatory-consumption assert in `ProposeLib.validateInboxConsumption`. + * Consumption is sufficient when the first unconsumed bucket: + * - does not exist (the checkpoint consumed everything the Inbox has), or + * - was opened strictly after the cutoff (`timestamp > cutoffTimestamp`), or + * - consuming through it would exceed the per-checkpoint cap (the cap-escape). + * + * The strict `>` matches L1: a bucket opened exactly at the cutoff must be consumed. This is the single source of + * truth for the minimum-consumption / cap-escape rule shared by the sequencer's selection floor and the validator. + */ +export function isInboxConsumptionSufficient(input: { + /** The first unconsumed bucket (the one after the checkpoint's last-consumed bucket), or undefined if none exists. */ + nextBucket: Pick | undefined; + /** Censorship cutoff timestamp from {@link getInboxCutoffTimestamp}. */ + cutoffTimestamp: bigint; + /** Cumulative Inbox message count consumed as of the parent checkpoint; the per-checkpoint cap origin. */ + checkpointStartTotalMsgCount: bigint; + /** Maximum number of messages the checkpoint may consume in total (`MAX_L1_TO_L2_MSGS_PER_CHECKPOINT`). */ + perCheckpointCap: number; +}): boolean { + const { nextBucket, cutoffTimestamp, checkpointStartTotalMsgCount, perCheckpointCap } = input; + if (nextBucket === undefined) { + return true; + } + if (nextBucket.timestamp > cutoffTimestamp) { + return true; + } + return nextBucket.totalMsgCount - checkpointStartTotalMsgCount > BigInt(perCheckpointCap); +} diff --git a/yarn-project/stdlib/src/messaging/index.ts b/yarn-project/stdlib/src/messaging/index.ts index 8728940e405a..aad4f1a91990 100644 --- a/yarn-project/stdlib/src/messaging/index.ts +++ b/yarn-project/stdlib/src/messaging/index.ts @@ -1,5 +1,7 @@ export * from './append_l1_to_l2_messages.js'; export * from './in_hash.js'; +export * from './inbox_bucket.js'; +export * from './inbox_consumption.js'; export * from './inbox_leaf.js'; export * from './inbox_rolling_hash.js'; export * from './l1_to_l2_message_bundle.js'; diff --git a/yarn-project/stdlib/src/messaging/l1_to_l2_message_bundle.ts b/yarn-project/stdlib/src/messaging/l1_to_l2_message_bundle.ts index a35c4c1c9e7c..6704a5d70c0f 100644 --- a/yarn-project/stdlib/src/messaging/l1_to_l2_message_bundle.ts +++ b/yarn-project/stdlib/src/messaging/l1_to_l2_message_bundle.ts @@ -6,20 +6,17 @@ import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { bufferToHex, hexToBuffer } from '@aztec/foundation/string'; /** - * A block's L1-to-L2 message bundle: the leaves it inserts into the L1-to-L2 message tree plus the two counts a block - * root needs — one for the (transitionally padded) tree insert, one for the real-count sponge absorb. See the Noir - * `L1ToL2MessageBundle`. Once the tree insert becomes real-count too, `numMsgs === numRealMsgs` and `numRealMsgs` is - * dropped. + * A block's L1-to-L2 message bundle: the real message leaves it inserts into the L1-to-L2 message tree and the count + * that drives both the compact (unpadded) tree append and the message-sponge absorb. See the Noir + * `L1ToL2MessageBundle`. */ export class L1ToL2MessageBundle { constructor( - /** The message leaves, padded with zeros to `MAX_L1_TO_L2_MSGS_PER_BLOCK`. Kept as a plain array (not a tuple) - * to avoid TS's deep-instantiation limit on the 1024-lane type. */ + /** The real message leaves in the leading lanes, padded with zeros to `MAX_L1_TO_L2_MSGS_PER_BLOCK`. Kept as a + * plain array (not a tuple) to avoid TS's deep-instantiation limit on the wide type. */ public readonly messages: Fr[], - /** Number of leaves inserted into the L1-to-L2 message tree (the padded subtree size, or 0 for an empty block). */ + /** Number of real messages: drives both the compact tree append and the sponge absorb. */ public readonly numMsgs: number, - /** Number of real (non-padding) messages absorbed into the message sponge. */ - public readonly numRealMsgs: number, ) {} /** An empty bundle: no leaves inserted, nothing absorbed. */ @@ -27,12 +24,11 @@ export class L1ToL2MessageBundle { return new L1ToL2MessageBundle( Array.from({ length: MAX_L1_TO_L2_MSGS_PER_BLOCK }, () => Fr.ZERO), 0, - 0, ); } toBuffer() { - return serializeToBuffer(this.messages, this.numMsgs, this.numRealMsgs); + return serializeToBuffer(this.messages, this.numMsgs); } toString() { @@ -43,7 +39,7 @@ export class L1ToL2MessageBundle { const reader = BufferReader.asReader(buffer); // Array.from (not readArray with the literal cap) keeps the type `Fr[]` and avoids TS's deep-tuple instantiation. const messages = Array.from({ length: MAX_L1_TO_L2_MSGS_PER_BLOCK }, () => Fr.fromBuffer(reader)); - return new L1ToL2MessageBundle(messages, reader.readNumber(), reader.readNumber()); + return new L1ToL2MessageBundle(messages, reader.readNumber()); } static fromString(str: string) { @@ -59,12 +55,11 @@ export class L1ToL2MessageBundle { } } -/** Pads `messages` to a full subtree and wraps it into a bundle whose tree-insert count is the padded subtree size. */ +/** Wraps `messages` (the real leaves) into a bundle: the leaves fill the leading lanes, the count is the real count. */ export function makeL1ToL2MessageBundle(messages: Fr[]): L1ToL2MessageBundle { // Explicit `` keeps the result `Fr[]`; padding to the literal cap would otherwise infer a deep tuple. return new L1ToL2MessageBundle( padArrayEnd(messages, Fr.ZERO, MAX_L1_TO_L2_MSGS_PER_BLOCK), - MAX_L1_TO_L2_MSGS_PER_BLOCK, messages.length, ); } diff --git a/yarn-project/stdlib/src/messaging/l1_to_l2_message_source.ts b/yarn-project/stdlib/src/messaging/l1_to_l2_message_source.ts index 04572c4eb99c..82610dbb461a 100644 --- a/yarn-project/stdlib/src/messaging/l1_to_l2_message_source.ts +++ b/yarn-project/stdlib/src/messaging/l1_to_l2_message_source.ts @@ -2,6 +2,7 @@ import type { CheckpointNumber } from '@aztec/foundation/branded-types'; import type { Fr } from '@aztec/foundation/curves/bn254'; import type { L2Tips } from '../block/l2_block_source.js'; +import type { InboxBucket } from './inbox_bucket.js'; /** * Interface of classes allowing for the retrieval of L1 to L2 messages. @@ -23,6 +24,53 @@ export interface L1ToL2MessageSource { */ getL1ToL2MessageIndex(l1ToL2Message: Fr): Promise; + /** + * Returns the latest Inbox bucket opened at or before the given L1 timestamp, or undefined if no such bucket + * exists (i.e., every synced bucket was opened strictly after the timestamp). Used by the sequencer/validator + * to resolve the censorship cutoff and message-lag boundaries. + * @param timestamp - The L1 timestamp (in seconds) to look up at-or-before. + */ + getLatestInboxBucketAtOrBefore(timestamp: bigint): Promise; + + /** + * Returns the Inbox bucket with the given sequence number, or undefined if it has not been synced. Validators use + * this to resolve the bucket a proposal references and check its rolling hash. + * @param seq - The bucket sequence number. + */ + getInboxBucket(seq: bigint): Promise; + + /** + * Returns the Inbox bucket whose cumulative message total equals `totalMsgCount`, or undefined if no synced bucket + * sits on that boundary. A block's or checkpoint's L1-to-L2 tree leaf count equals the cumulative total of the last + * bucket it consumed (messages are indexed compactly, with no padding), so validators use this to resolve the + * bucket a block consumed through from the block's leaf count, without trusting a wire hint. `totalMsgCount === 0` + * resolves the genesis sentinel bucket (sequence 0); a total that does not land on a bucket boundary returns + * undefined. + * @param totalMsgCount - The cumulative Inbox message count (leaf count) to resolve to a bucket boundary. + */ + getInboxBucketByTotalMsgCount(totalMsgCount: bigint): Promise; + + /** + * Returns the message leaves absorbed into buckets in the range `(fromExclusive, toInclusive]`, in insertion + * order, for streaming message-bundle derivation. Both bounds must name buckets the source + * has synced; it throws otherwise, so that an empty result means the range holds no messages instead of hiding an + * unsynced bound. Callers that can tolerate an unsynced source resolve both bounds first, or map the failure to + * their own catch-up handling. + * @param fromExclusive - The lower bucket sequence bound, exclusive (0 means from the start of the Inbox). + * @param toInclusive - The upper bucket sequence bound, inclusive. + */ + getL1ToL2MessagesBetweenBuckets(fromExclusive: bigint, toInclusive: bigint): Promise; + + /** + * Returns the message leaves in the cumulative Inbox message-count range `[startLeafCount, endLeafCount)`, in + * insertion order. The bounds are compact L1-to-L2 tree leaf counts, which every block header + * carries, so a consumer can ask for the messages a block or checkpoint consumed without resolving Inbox buckets + * itself. Both bounds must land on a bucket boundary the source has synced; it throws otherwise. + * @param startLeafCount - The cumulative Inbox message count the range starts at, inclusive. + * @param endLeafCount - The cumulative Inbox message count the range ends at, exclusive. + */ + getL1ToL2MessagesBetweenLeafCounts(startLeafCount: bigint, endLeafCount: bigint): Promise; + /** * Returns the tips of the L2 chain. */ diff --git a/yarn-project/stdlib/src/p2p/block_proposal.test.ts b/yarn-project/stdlib/src/p2p/block_proposal.test.ts index 2f6601b74506..9ac84730babc 100644 --- a/yarn-project/stdlib/src/p2p/block_proposal.test.ts +++ b/yarn-project/stdlib/src/p2p/block_proposal.test.ts @@ -1,11 +1,34 @@ // Serde test for the block proposal type +import { IndexWithinCheckpoint } from '@aztec/foundation/branded-types'; import { Secp256k1Signer } from '@aztec/foundation/crypto/secp256k1-signer'; +import { Fr } from '@aztec/foundation/curves/bn254'; import { Signature } from '@aztec/foundation/eth-signature'; +import { bufferToHex, hexToBuffer } from '@aztec/foundation/string'; +import { InboxBucketRef } from '../messaging/inbox_bucket.js'; import { TEST_COORDINATION_SIGNATURE_CONTEXT, makeBlockProposal } from '../tests/mocks.js'; +import { BlockHeader } from '../tx/block_header.js'; import { Tx } from '../tx/tx.js'; +import { TxHash } from '../tx/tx_hash.js'; import { BlockProposal } from './block_proposal.js'; +import { EMPTY_COORDINATION_SIGNATURE_CONTEXT } from './signature_utils.js'; import { SignedTxs } from './signed_txs.js'; +import { LEGACY_BLOCK_PROPOSAL_HEX, LEGACY_BLOCK_PROPOSAL_PAYLOAD_HEX } from './wire_compat_fixtures.js'; + +/** + * Deterministic legacy-shaped proposal (no signedTxs, no bucketRef) matching the golden fixtures in + * wire_compat_fixtures.ts. Constructed identically to how those bytes were captured on the pre-change code. + */ +const makeLegacyFixtureProposal = () => + new BlockProposal( + BlockHeader.empty(), + IndexWithinCheckpoint(3), + new Fr(42n), + new Fr(99n), + [TxHash.fromField(new Fr(7n)), TxHash.fromField(new Fr(8n))], + Signature.empty(), + EMPTY_COORDINATION_SIGNATURE_CONTEXT, + ); describe('Block Proposal serialization / deserialization', () => { const checkEquivalence = (serialized: BlockProposal, deserialized: BlockProposal) => { @@ -92,4 +115,115 @@ describe('Block Proposal serialization / deserialization', () => { expect(tampered.getSender()).toBeUndefined(); }); + + describe('bucket reference', () => { + it('round-trips with a bucket reference set', async () => { + const bucketRef = InboxBucketRef.random(); + const proposal = await makeBlockProposal({ bucketRef }); + + const deserialized = BlockProposal.fromBuffer(proposal.toBuffer()); + + expect(deserialized.bucketRef).toBeDefined(); + expect(deserialized.bucketRef!.equals(bucketRef)).toBe(true); + expect(deserialized.getSize()).toEqual(proposal.getSize()); + expect(deserialized).toEqual(proposal); + }); + + it('round-trips with a bucket reference set alongside signed txs', async () => { + const bucketRef = InboxBucketRef.random(); + const txs = await Promise.all([Tx.random(), Tx.random()]); + const proposal = await makeBlockProposal({ txs, bucketRef }); + + const deserialized = BlockProposal.fromBuffer(proposal.toBuffer()); + + expect(deserialized.bucketRef!.equals(bucketRef)).toBe(true); + expect(deserialized.txs?.length).toEqual(txs.length); + expect(deserialized).toEqual(proposal); + }); + + it('serializes byte-identically to the legacy format when unset', () => { + const proposal = makeLegacyFixtureProposal(); + expect(proposal.bucketRef).toBeUndefined(); + expect(bufferToHex(proposal.toBuffer())).toEqual(LEGACY_BLOCK_PROPOSAL_HEX); + expect(bufferToHex(proposal.getPayloadToSign())).toEqual(LEGACY_BLOCK_PROPOSAL_PAYLOAD_HEX); + }); + + it('decodes a legacy buffer (no tail) as having no bucket reference', () => { + const deserialized = BlockProposal.fromBuffer(hexToBuffer(LEGACY_BLOCK_PROPOSAL_HEX)); + expect(deserialized.bucketRef).toBeUndefined(); + // Re-encoding a legacy buffer yields the same legacy bytes: no phantom tail is introduced. + expect(bufferToHex(deserialized.toBuffer())).toEqual(LEGACY_BLOCK_PROPOSAL_HEX); + }); + + it('appends the bucket reference only when set, changing the signed payload', async () => { + const bucketRef = InboxBucketRef.random(); + const withRef = await makeBlockProposal({ bucketRef }); + const withoutRef = await makeBlockProposal({ + blockHeader: withRef.blockHeader, + indexWithinCheckpoint: withRef.indexWithinCheckpoint, + inHash: withRef.inHash, + archiveRoot: withRef.archiveRoot, + txHashes: withRef.txHashes, + }); + + const withRefPayload = withRef.getPayloadToSign(); + const withoutRefPayload = withoutRef.getPayloadToSign(); + + // The set payload extends the unset payload by exactly the reference bytes (appended tail, no marker). + expect(withRefPayload.length).toEqual(withoutRefPayload.length + InboxBucketRef.SIZE); + expect(withRefPayload.subarray(0, withoutRefPayload.length)).toEqual(withoutRefPayload); + // The payload hashes differ, so the attestation pool treats set-vs-unset as distinct payloads. + expect(withRef.getPayloadHash().toString()).not.toEqual(withoutRef.getPayloadHash().toString()); + }); + + it('covers the bucket reference under the proposal signature', async () => { + const signer = Secp256k1Signer.random(); + const bucketRef = InboxBucketRef.random(); + const proposal = await makeBlockProposal({ signer, bucketRef }); + + const deserialized = BlockProposal.fromBuffer(proposal.toBuffer()); + expect(deserialized.getSender()).toEqual(signer.address); + }); + + it('breaks sender recovery when the bucket reference is tampered with', async () => { + const signer = Secp256k1Signer.random(); + const bucketRef = new InboxBucketRef(5n, 100n, new Fr(7n)); + const proposal = await makeBlockProposal({ signer, bucketRef }); + expect(proposal.getSender()).toEqual(signer.address); + + // A relay swapping the signed reference for a different one is not covered by the original signature. + const tampered = new BlockProposal( + proposal.blockHeader, + proposal.indexWithinCheckpoint, + proposal.inHash, + proposal.archiveRoot, + proposal.txHashes, + proposal.signature, + proposal.signatureContext, + proposal.signedTxs, + new InboxBucketRef(6n, 100n, new Fr(7n)), + ); + expect(tampered.getSender()).not.toEqual(signer.address); + }); + + it('breaks sender recovery when a bucket reference is injected into an unsigned proposal', async () => { + const signer = Secp256k1Signer.random(); + const proposal = await makeBlockProposal({ signer }); + expect(proposal.getSender()).toEqual(signer.address); + + // A relay injecting a reference the proposer never signed over is rejected by signature recovery. + const injected = new BlockProposal( + proposal.blockHeader, + proposal.indexWithinCheckpoint, + proposal.inHash, + proposal.archiveRoot, + proposal.txHashes, + proposal.signature, + proposal.signatureContext, + proposal.signedTxs, + InboxBucketRef.random(), + ); + expect(injected.getSender()).not.toEqual(signer.address); + }); + }); }); diff --git a/yarn-project/stdlib/src/p2p/block_proposal.ts b/yarn-project/stdlib/src/p2p/block_proposal.ts index 1d29fa05ff99..750fde3f77ed 100644 --- a/yarn-project/stdlib/src/p2p/block_proposal.ts +++ b/yarn-project/stdlib/src/p2p/block_proposal.ts @@ -18,6 +18,7 @@ import type { L2Block } from '../block/l2_block.js'; import type { L2BlockInfo } from '../block/l2_block_info.js'; import { MAX_TXS_PER_BLOCK } from '../deserialization/index.js'; import { DutyType, type SigningContext } from '../ha-signing/index.js'; +import { InboxBucketRef } from '../messaging/inbox_bucket.js'; import { BlockHeader } from '../tx/block_header.js'; import { TxHash } from '../tx/index.js'; import type { Tx } from '../tx/tx.js'; @@ -87,6 +88,13 @@ export class BlockProposal extends Gossipable implements Signable { /** The signed transactions in the block (optional, for DA guarantees) */ public readonly signedTxs?: SignedTxs, + + /** + * Reference to the Inbox bucket this block proposes to consume, when the proposer commits to one. Validators + * resolve it against their own Inbox view and derive the consumed message bundle from it rather than trusting a + * proposer-supplied message list. Covered by the proposal signature (part of `getPayloadToSign`). + */ + public readonly bucketRef?: InboxBucketRef, ) { super(); } @@ -124,7 +132,9 @@ export class BlockProposal extends Gossipable implements Signable { /** * Get the payload to sign for this block proposal. - * The signature is over: blockHeader + indexWithinCheckpoint + inHash + archiveRoot + txHashes + * The signature is over: blockHeader + indexWithinCheckpoint + inHash + archiveRoot + txHashes, plus the bucket + * reference when set. Appending only when set keeps the pre-flip signed payload byte-identical to the legacy format, + * while binding the reference to the signature so a relay cannot strip or inject it without breaking recovery. */ getPayloadToSign(): Buffer { return serializeToBuffer([ @@ -134,6 +144,7 @@ export class BlockProposal extends Gossipable implements Signable { this.archiveRoot, this.txHashes.length, this.txHashes, + ...(this.bucketRef ? [this.bucketRef] : []), ]); } @@ -163,6 +174,7 @@ export class BlockProposal extends Gossipable implements Signable { signatureContext: CoordinationSignatureContext, proposalSigner: (typedData: TypedDataDefinition, context: SigningContext) => Promise, txsSigner?: (typedData: TypedDataDefinition, context: SigningContext) => Promise, + bucketRef?: InboxBucketRef, ): Promise { // Create a temporary proposal to get the payload to sign const tempProposal = new BlockProposal( @@ -173,6 +185,8 @@ export class BlockProposal extends Gossipable implements Signable { txHashes, Signature.empty(), signatureContext, + undefined, + bucketRef, ); // Create the block signing context @@ -208,6 +222,7 @@ export class BlockProposal extends Gossipable implements Signable { sig, signatureContext, signedTxs, + bucketRef, ); } @@ -261,6 +276,12 @@ export class BlockProposal extends Gossipable implements Signable { } else { buffer.push(0); // hasSignedTxs = false } + // Optional bucket-reference tail (AZIP-22 Fast Inbox). Appended only when set, so pre-flip proposals serialize + // byte-identically to the legacy format and mixed-version peers keep decoding them. + if (this.bucketRef) { + buffer.push(1); // hasBucketRef = true + buffer.push(this.bucketRef.toBuffer()); + } return serializeToBuffer(buffer); } @@ -279,20 +300,21 @@ export class BlockProposal extends Gossipable implements Signable { } const txHashes = reader.readArray(txHashCount, TxHash); + let signedTxs: SignedTxs | undefined; if (!reader.isEmpty()) { const hasSignedTxs = reader.readNumber(); if (hasSignedTxs) { - const signedTxs = SignedTxs.fromBuffer(reader); - return new BlockProposal( - blockHeader, - indexWithinCheckpoint, - inHash, - archiveRoot, - txHashes, - signature, - signatureContext, - signedTxs, - ); + signedTxs = SignedTxs.fromBuffer(reader); + } + } + + // Optional bucket-reference tail (AZIP-22 Fast Inbox). Legacy buffers end after the signedTxs flag, so EOF here + // decodes as "no reference" — this is the cross-version tolerance that keeps mixed-version gossip working. + let bucketRef: InboxBucketRef | undefined; + if (!reader.isEmpty()) { + const hasBucketRef = reader.readNumber(); + if (hasBucketRef) { + bucketRef = InboxBucketRef.fromBuffer(reader); } } @@ -304,6 +326,8 @@ export class BlockProposal extends Gossipable implements Signable { txHashes, signature, signatureContext, + signedTxs, + bucketRef, ); } @@ -319,7 +343,8 @@ export class BlockProposal extends Gossipable implements Signable { 4 /* txHashes.length */ + this.txHashes.length * TxHash.SIZE + 4 /* hasSignedTxs flag */ + - (this.signedTxs ? this.signedTxs.getSize() : 0) + (this.signedTxs ? this.signedTxs.getSize() : 0) + + (this.bucketRef ? 4 /* hasBucketRef flag */ + this.bucketRef.getSize() : 0) ); } @@ -357,6 +382,7 @@ export class BlockProposal extends Gossipable implements Signable { txHashes: this.txHashes.map(h => h.toString()), chainId: this.signatureContext.chainId, rollupAddress: this.signatureContext.rollupAddress.toString(), + bucketRef: this.bucketRef?.toInspect(), }; } @@ -383,6 +409,8 @@ export class BlockProposal extends Gossipable implements Signable { this.txHashes, this.signature, this.signatureContext, + undefined, + this.bucketRef, ); } } diff --git a/yarn-project/stdlib/src/p2p/checkpoint_proposal.test.ts b/yarn-project/stdlib/src/p2p/checkpoint_proposal.test.ts new file mode 100644 index 000000000000..ba1c494d41df --- /dev/null +++ b/yarn-project/stdlib/src/p2p/checkpoint_proposal.test.ts @@ -0,0 +1,126 @@ +// Serde and consistency tests for the checkpoint proposal type +import { IndexWithinCheckpoint } from '@aztec/foundation/branded-types'; +import { Secp256k1Signer } from '@aztec/foundation/crypto/secp256k1-signer'; +import { Fr } from '@aztec/foundation/curves/bn254'; +import { Signature } from '@aztec/foundation/eth-signature'; +import { bufferToHex, hexToBuffer } from '@aztec/foundation/string'; + +import { InboxBucketRef } from '../messaging/inbox_bucket.js'; +import { CheckpointHeader } from '../rollup/checkpoint_header.js'; +import { makeCheckpointProposal } from '../tests/mocks.js'; +import { BlockHeader } from '../tx/block_header.js'; +import { TxHash } from '../tx/tx_hash.js'; +import { CheckpointProposal } from './checkpoint_proposal.js'; +import { EMPTY_COORDINATION_SIGNATURE_CONTEXT } from './signature_utils.js'; +import { LEGACY_CHECKPOINT_PROPOSAL_HEX } from './wire_compat_fixtures.js'; + +/** + * Deterministic legacy-shaped checkpoint proposal (lastBlock without signedTxs or bucketRef) matching the golden + * fixture in wire_compat_fixtures.ts. Constructed identically to how those bytes were captured on the pre-change code. + */ +const makeLegacyFixtureCheckpointProposal = () => + new CheckpointProposal( + CheckpointHeader.empty(), + new Fr(123n), + 0n, + Signature.empty(), + EMPTY_COORDINATION_SIGNATURE_CONTEXT, + { + blockHeader: BlockHeader.empty(), + indexWithinCheckpoint: IndexWithinCheckpoint(4), + txHashes: [TxHash.fromField(new Fr(7n))], + signature: Signature.empty(), + }, + ); + +describe('CheckpointProposal serialization / deserialization', () => { + it('round-trips with a lastBlock', async () => { + const proposal = await makeCheckpointProposal({ lastBlock: {} }); + const deserialized = CheckpointProposal.fromBuffer(proposal.toBuffer()); + // The mock supplies a BlockProposal as lastBlock while decoding rebuilds a plain CheckpointLastBlock, so compare + // the re-serialized bytes rather than deep-equal. + expect(deserialized.getSize()).toEqual(proposal.getSize()); + expect(deserialized.toBuffer()).toEqual(proposal.toBuffer()); + }); + + describe('bucket reference', () => { + it('round-trips with a bucket reference on the last block', async () => { + const checkpointHeader = CheckpointHeader.random(); + const bucketRef = new InboxBucketRef(17n, 1_700_000_000n, checkpointHeader.inboxRollingHash); + const proposal = await makeCheckpointProposal({ checkpointHeader, lastBlock: { bucketRef } }); + + const deserialized = CheckpointProposal.fromBuffer(proposal.toBuffer()); + + expect(deserialized.lastBlock?.bucketRef?.equals(bucketRef)).toBe(true); + expect(deserialized.getSize()).toEqual(proposal.getSize()); + expect(deserialized.toBuffer()).toEqual(proposal.toBuffer()); + }); + + it('serializes byte-identically to the legacy format when unset', () => { + const proposal = makeLegacyFixtureCheckpointProposal(); + expect(proposal.lastBlock?.bucketRef).toBeUndefined(); + expect(bufferToHex(proposal.toBuffer())).toEqual(LEGACY_CHECKPOINT_PROPOSAL_HEX); + }); + + it('decodes a legacy buffer (no tail) as having no bucket reference', () => { + const deserialized = CheckpointProposal.fromBuffer(hexToBuffer(LEGACY_CHECKPOINT_PROPOSAL_HEX)); + expect(deserialized.lastBlock).toBeDefined(); + expect(deserialized.lastBlock?.bucketRef).toBeUndefined(); + expect(bufferToHex(deserialized.toBuffer())).toEqual(LEGACY_CHECKPOINT_PROPOSAL_HEX); + }); + + it('carries the bucket reference through getBlockProposal, covered by the block signature', async () => { + const signer = Secp256k1Signer.random(); + const checkpointHeader = CheckpointHeader.random(); + const bucketRef = new InboxBucketRef(3n, 42n, checkpointHeader.inboxRollingHash); + const proposal = await makeCheckpointProposal({ signer, checkpointHeader, lastBlock: { bucketRef } }); + + const blockProposal = proposal.getBlockProposal(); + expect(blockProposal?.bucketRef?.equals(bucketRef)).toBe(true); + expect(blockProposal?.getSender()).toEqual(signer.address); + expect(proposal.getSender()).toEqual(signer.address); + }); + + it('accepts a last-block reference whose rolling hash matches the checkpoint header', () => { + const checkpointHeader = CheckpointHeader.random({ inboxRollingHash: new Fr(0x1234n) }); + expect( + () => + new CheckpointProposal( + checkpointHeader, + Fr.random(), + 0n, + Signature.empty(), + EMPTY_COORDINATION_SIGNATURE_CONTEXT, + { + blockHeader: BlockHeader.empty(), + indexWithinCheckpoint: IndexWithinCheckpoint(4), + txHashes: [], + signature: Signature.empty(), + bucketRef: new InboxBucketRef(1n, 2n, new Fr(0x1234n)), + }, + ), + ).not.toThrow(); + }); + + it('throws when the last-block reference rolling hash does not match the checkpoint header', () => { + const checkpointHeader = CheckpointHeader.random({ inboxRollingHash: new Fr(0x1234n) }); + expect( + () => + new CheckpointProposal( + checkpointHeader, + Fr.random(), + 0n, + Signature.empty(), + EMPTY_COORDINATION_SIGNATURE_CONTEXT, + { + blockHeader: BlockHeader.empty(), + indexWithinCheckpoint: IndexWithinCheckpoint(4), + txHashes: [], + signature: Signature.empty(), + bucketRef: new InboxBucketRef(1n, 2n, new Fr(0x5678n)), + }, + ), + ).toThrow(/bucketRef rolling hash/); + }); + }); +}); diff --git a/yarn-project/stdlib/src/p2p/checkpoint_proposal.ts b/yarn-project/stdlib/src/p2p/checkpoint_proposal.ts index 286be1c2b64f..126e24b56dc4 100644 --- a/yarn-project/stdlib/src/p2p/checkpoint_proposal.ts +++ b/yarn-project/stdlib/src/p2p/checkpoint_proposal.ts @@ -15,6 +15,7 @@ import type { TypedDataDefinition } from 'viem'; import type { L2BlockInfo } from '../block/l2_block_info.js'; import { MAX_TXS_PER_BLOCK } from '../deserialization/index.js'; import { DutyType, type SigningContext } from '../ha-signing/index.js'; +import { InboxBucketRef } from '../messaging/inbox_bucket.js'; import { CheckpointHeader } from '../rollup/checkpoint_header.js'; import { BlockHeader } from '../tx/block_header.js'; import { TxHash } from '../tx/index.js'; @@ -69,6 +70,11 @@ export type CheckpointLastBlock = Omit & { signature: Signature; /** The signed transactions in the last block (optional, for DA guarantees) */ signedTxs?: SignedTxs; + /** + * Reference to the Inbox bucket the last block proposes to consume. When set, its rolling hash must equal the + * checkpoint header's `inboxRollingHash` (enforced at construction). + */ + bucketRef?: InboxBucketRef; }; /** @@ -110,6 +116,13 @@ export class CheckpointProposal extends Gossipable implements Signable { `CheckpointProposal lastBlock inHash ${lastBlock.inHash} does not match checkpoint inHash ${checkpointHeader.inHash}`, ); } + // The last block's bucket reference (AZIP-22 Fast Inbox) commits to the same rolling hash as the checkpoint header, + // mirroring the inHash cross-check above. Optional pre-flip: only enforced when the reference is set. + if (lastBlock?.bucketRef && !lastBlock.bucketRef.inboxRollingHash.equals(checkpointHeader.inboxRollingHash)) { + throw new Error( + `CheckpointProposal lastBlock bucketRef rolling hash ${lastBlock.bucketRef.inboxRollingHash} does not match checkpoint inboxRollingHash ${checkpointHeader.inboxRollingHash}`, + ); + } if (lastBlock && 'archiveRoot' in lastBlock && !lastBlock.archiveRoot.equals(archive)) { throw new Error( `CheckpointProposal lastBlock archive ${lastBlock.archiveRoot} does not match checkpoint archive ${archive}`, @@ -150,6 +163,7 @@ export class CheckpointProposal extends Gossipable implements Signable { this.lastBlock.signature, this.signatureContext, this.lastBlock.signedTxs, + this.lastBlock.bucketRef, ); } @@ -288,6 +302,12 @@ export class CheckpointProposal extends Gossipable implements Signable { } else { buffer.push(0); // hasSignedTxs = false } + // Optional bucket-reference tail (AZIP-22 Fast Inbox). Appended only when set, so pre-flip proposals serialize + // byte-identically to the legacy format and mixed-version peers keep decoding them. + if (this.lastBlock.bucketRef) { + buffer.push(1); // hasBucketRef = true + buffer.push(this.lastBlock.bucketRef.toBuffer()); + } } else { buffer.push(0); // hasLastBlock = false } @@ -324,12 +344,23 @@ export class CheckpointProposal extends Gossipable implements Signable { } } + // Optional bucket-reference tail (AZIP-22 Fast Inbox). Legacy buffers end after the signedTxs flag, so EOF here + // decodes as "no reference" — the cross-version tolerance that keeps mixed-version gossip working. + let bucketRef: InboxBucketRef | undefined; + if (!reader.isEmpty()) { + const hasBucketRef = reader.readNumber(); + if (hasBucketRef) { + bucketRef = InboxBucketRef.fromBuffer(reader); + } + } + return new CheckpointProposal(checkpointHeader, archive, feeAssetPriceModifier, signature, signatureContext, { blockHeader, indexWithinCheckpoint, txHashes, signature: blockSignature, signedTxs, + bucketRef, }); } @@ -354,7 +385,8 @@ export class CheckpointProposal extends Gossipable implements Signable { 4 /* txHashes.length */ + this.lastBlock.txHashes.length * TxHash.SIZE + 4 /* hasSignedTxs flag */ + - (this.lastBlock.signedTxs ? this.lastBlock.signedTxs.getSize() : 0); + (this.lastBlock.signedTxs ? this.lastBlock.signedTxs.getSize() : 0) + + (this.lastBlock.bucketRef ? 4 /* hasBucketRef flag */ + this.lastBlock.bucketRef.getSize() : 0); } return size; @@ -400,6 +432,7 @@ export class CheckpointProposal extends Gossipable implements Signable { indexWithinCheckpoint: this.lastBlock.indexWithinCheckpoint, txHashes: this.lastBlock.txHashes.map(h => h.toString()), signature: this.lastBlock.signature.toString(), + bucketRef: this.lastBlock.bucketRef?.toInspect(), } : undefined, }; diff --git a/yarn-project/stdlib/src/p2p/wire_compat_fixtures.ts b/yarn-project/stdlib/src/p2p/wire_compat_fixtures.ts new file mode 100644 index 000000000000..61f5720243ce --- /dev/null +++ b/yarn-project/stdlib/src/p2p/wire_compat_fixtures.ts @@ -0,0 +1,22 @@ +// Golden wire fixtures captured from the pre-Fast-Inbox serialization format (AZIP-22, A-1381). They pin the exact +// bytes a legacy peer produces and consumes, so the optional bucket-reference tail added to proposals stays wire +// compatible: an unset proposal must serialize to these bytes, and decoding these bytes must yield no bucket reference. +// +// Both fixtures come from a deterministic proposal built with: +// BlockHeader.empty(), IndexWithinCheckpoint(3), inHash=Fr(42), archiveRoot=Fr(99), +// txHashes=[TxHash.fromField(Fr(7)), TxHash.fromField(Fr(8))], Signature.empty(), EMPTY_COORDINATION_SIGNATURE_CONTEXT +// (checkpoint: CheckpointHeader.empty(), archive=Fr(123), feeAssetPriceModifier=0, empty signature/context, and a +// lastBlock with BlockHeader.empty(), IndexWithinCheckpoint(4), txHashes=[TxHash.fromField(Fr(7))], empty signature). +// DO NOT regenerate from current code — the whole point is that these bytes predate the change. + +/** Legacy `BlockProposal.toBuffer()` bytes (no signedTxs, no bucket reference). */ +export const LEGACY_BLOCK_PROPOSAL_HEX = + '0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000800000000'; + +/** Legacy `CheckpointProposal.toBuffer()` bytes (lastBlock without signedTxs or bucket reference). */ +export const LEGACY_CHECKPOINT_PROPOSAL_HEX = + '0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000700000000'; + +/** Legacy `BlockProposal.getPayloadToSign()` bytes (no bucket reference). */ +export const LEGACY_BLOCK_PROPOSAL_PAYLOAD_HEX = + '0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000630000000200000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008'; diff --git a/yarn-project/stdlib/src/tests/factories.ts b/yarn-project/stdlib/src/tests/factories.ts index 70cb63a2c418..d09525fd7545 100644 --- a/yarn-project/stdlib/src/tests/factories.ts +++ b/yarn-project/stdlib/src/tests/factories.ts @@ -963,11 +963,7 @@ export function makeTxMergeRollupPrivateInputs(seed = 0): TxMergeRollupPrivateIn export function makeBlockRootRollupPrivateInputs(seed = 0) { return new BlockRootRollupPrivateInputs( [makeProofData(seed + 0x1000, makeTxRollupPublicInputs), makeProofData(seed + 0x2000, makeTxRollupPublicInputs)], - new L1ToL2MessageBundle( - makeArray(MAX_L1_TO_L2_MSGS_PER_BLOCK, fr, seed + 0x2500), - MAX_L1_TO_L2_MSGS_PER_BLOCK, - MAX_L1_TO_L2_MSGS_PER_BLOCK, - ), + new L1ToL2MessageBundle(makeArray(MAX_L1_TO_L2_MSGS_PER_BLOCK, fr, seed + 0x2500), MAX_L1_TO_L2_MSGS_PER_BLOCK), makeAppendOnlyTreeSnapshot(seed + 0x3000), makeL1ToL2MessageSponge(seed + 0x3500), makeSiblingPath(seed + 0x4000, L1_TO_L2_MSG_TREE_HEIGHT), @@ -978,7 +974,7 @@ export function makeBlockRootRollupPrivateInputs(seed = 0) { export function makeBlockRootSingleTxRollupPrivateInputs(seed = 0) { return new BlockRootSingleTxRollupPrivateInputs( makeProofData(seed + 0x1000, makeTxRollupPublicInputs), - new L1ToL2MessageBundle(makeArray(MAX_L1_TO_L2_MSGS_PER_BLOCK, fr, seed + 0x2500), 0, 0), + new L1ToL2MessageBundle(makeArray(MAX_L1_TO_L2_MSGS_PER_BLOCK, fr, seed + 0x2500), 0), makeAppendOnlyTreeSnapshot(seed + 0x2800), makeL1ToL2MessageSponge(seed + 0x3000), makeSiblingPath(seed + 0x4000, L1_TO_L2_MSG_TREE_HEIGHT), diff --git a/yarn-project/stdlib/src/tests/mocks.ts b/yarn-project/stdlib/src/tests/mocks.ts index fcd0fcb1af43..6a7df40b724c 100644 --- a/yarn-project/stdlib/src/tests/mocks.ts +++ b/yarn-project/stdlib/src/tests/mocks.ts @@ -51,6 +51,7 @@ import { PrivateToAvmAccumulatedData } from '../kernel/private_to_avm_accumulate import { PrivateToPublicAccumulatedDataBuilder } from '../kernel/private_to_public_accumulated_data_builder.js'; import { PublicCallRequestArrayLengths } from '../kernel/public_call_request.js'; import { computeInHashFromL1ToL2Messages } from '../messaging/in_hash.js'; +import { InboxBucketRef } from '../messaging/inbox_bucket.js'; import { BlockProposal } from '../p2p/block_proposal.js'; import { CheckpointAttestation } from '../p2p/checkpoint_attestation.js'; import { CheckpointProposal } from '../p2p/checkpoint_proposal.js'; @@ -549,6 +550,7 @@ export interface MakeBlockProposalOptions { txHashes?: TxHash[]; txs?: Tx[]; signatureContext?: CoordinationSignatureContext; + bucketRef?: InboxBucketRef; } export interface MakeCheckpointProposalOptions { @@ -563,6 +565,7 @@ export interface MakeCheckpointProposalOptions { indexWithinCheckpoint?: IndexWithinCheckpoint; txHashes?: TxHash[]; txs?: Tx[]; + bucketRef?: InboxBucketRef; }; } @@ -601,6 +604,7 @@ export const makeBlockProposal = (options?: MakeBlockProposalOptions): Promise Promise.resolve(signTypedData(signer, typedData)), (typedData, _context) => Promise.resolve(signTypedData(signer, typedData)), + bucketRef, ); }; @@ -634,6 +639,7 @@ export const makeCheckpointProposal = async (options?: MakeCheckpointProposalOpt txs: options.lastBlock.txs, signer, signatureContext, + bucketRef: options.lastBlock.bucketRef, }) : undefined; diff --git a/yarn-project/stdlib/src/tx/state_reference.ts b/yarn-project/stdlib/src/tx/state_reference.ts index 79a59204c7ac..184b6a89c27f 100644 --- a/yarn-project/stdlib/src/tx/state_reference.ts +++ b/yarn-project/stdlib/src/tx/state_reference.ts @@ -1,9 +1,4 @@ -import { - MAX_NOTE_HASHES_PER_TX, - MAX_NULLIFIERS_PER_TX, - NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, - STATE_REFERENCE_LENGTH, -} from '@aztec/constants'; +import { MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX, STATE_REFERENCE_LENGTH } from '@aztec/constants'; import type { Fr } from '@aztec/foundation/curves/bn254'; import { BufferReader, BufferSink, FieldReader, serializeToSink } from '@aztec/foundation/serialize'; import type { FieldsOf } from '@aztec/foundation/types'; @@ -106,14 +101,12 @@ export class StateReference { } /** - * Validates the trees in world state have the expected number of leaves (multiple of number of insertions per tx) + * Validates the partial-state trees have the expected number of leaves (multiple of number of insertions per tx). + * + * The L1-to-L2 message tree is not checked: it grows by real message counts at compact (unaligned) + * indices, so its next-available leaf index is no longer a multiple of any per-block subtree size. */ public validate() { - if (this.l1ToL2MessageTree.nextAvailableLeafIndex % NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP !== 0) { - throw new Error( - `Invalid L1 to L2 message tree next available leaf index ${this.l1ToL2MessageTree.nextAvailableLeafIndex} (must be a multiple of ${NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP})`, - ); - } if (this.partial.noteHashTree.nextAvailableLeafIndex % MAX_NOTE_HASHES_PER_TX !== 0) { throw new Error( `Invalid note hash tree next available leaf index ${this.partial.noteHashTree.nextAvailableLeafIndex} (must be a multiple of ${MAX_NOTE_HASHES_PER_TX})`, diff --git a/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts b/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts index 21af3d40f03c..327895abe241 100644 --- a/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts +++ b/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts @@ -1,8 +1,4 @@ -import { - CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS, - MAX_PRIVATE_LOGS_PER_TX, - NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, -} from '@aztec/constants'; +import { CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS, MAX_PRIVATE_LOGS_PER_TX } from '@aztec/constants'; import { BlockNumber } from '@aztec/foundation/branded-types'; import { Schnorr } from '@aztec/foundation/crypto/schnorr'; import { Fr } from '@aztec/foundation/curves/bn254'; @@ -672,8 +668,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl txEffect.txHash = new TxHash(new Fr(blockNumber)); - const l1ToL2Messages = Array(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).fill(0).map(Fr.zero); - await forkedWorldTrees.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2Messages); + // TXE blocks carry no L1-to-L2 messages, so the message tree is left unadvanced. const l2Block = await makeTXEBlock(forkedWorldTrees, globals, [txEffect]); @@ -837,8 +832,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl txEffect.txHash = new TxHash(new Fr(blockNumber)); - const l1ToL2Messages = Array(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).fill(0).map(Fr.zero); - await forkedWorldTrees.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2Messages); + // TXE blocks carry no L1-to-L2 messages, so the message tree is left unadvanced. const l2Block = await makeTXEBlock(forkedWorldTrees, globals, [txEffect]); diff --git a/yarn-project/txe/src/state_machine/synchronizer.ts b/yarn-project/txe/src/state_machine/synchronizer.ts index b4d44ebc88bf..78dabcc2e81d 100644 --- a/yarn-project/txe/src/state_machine/synchronizer.ts +++ b/yarn-project/txe/src/state_machine/synchronizer.ts @@ -1,6 +1,4 @@ -import { NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants'; import { BlockNumber } from '@aztec/foundation/branded-types'; -import { padArrayEnd } from '@aztec/foundation/collection'; import { Fr } from '@aztec/foundation/curves/bn254'; import { AvmSimulatorPool } from '@aztec/simulator/server'; import type { BlockHash, L2Block } from '@aztec/stdlib/block'; @@ -35,10 +33,8 @@ export class TXESynchronizer implements WorldStateSynchronizer { } public async handleL2Block(block: L2Block, l1ToL2Messages: Fr[] = []) { - await this.nativeWorldStateService.handleL2BlockAndMessages( - block, - padArrayEnd(l1ToL2Messages, Fr.ZERO, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP), - ); + // Append the block's real message leaves unpadded at compact indices. + await this.nativeWorldStateService.handleL2BlockAndMessages(block, l1ToL2Messages); this.blockNumber = block.header.globalVariables.blockNumber; } diff --git a/yarn-project/txe/src/utils/block_creation.ts b/yarn-project/txe/src/utils/block_creation.ts index dce7c90e1216..101139363fa6 100644 --- a/yarn-project/txe/src/utils/block_creation.ts +++ b/yarn-project/txe/src/utils/block_creation.ts @@ -1,9 +1,4 @@ -import { - MAX_NOTE_HASHES_PER_TX, - MAX_NULLIFIERS_PER_TX, - NULLIFIER_SUBTREE_HEIGHT, - NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, -} from '@aztec/constants'; +import { MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX, NULLIFIER_SUBTREE_HEIGHT } from '@aztec/constants'; import { BlockNumber, CheckpointNumber, IndexWithinCheckpoint } from '@aztec/foundation/branded-types'; import { padArrayEnd } from '@aztec/foundation/collection'; import { Fr } from '@aztec/foundation/curves/bn254'; @@ -36,10 +31,8 @@ export async function insertTxEffectIntoWorldTrees( NULLIFIER_SUBTREE_HEIGHT, ); - await worldTrees.appendLeaves( - MerkleTreeId.L1_TO_L2_MESSAGE_TREE, - padArrayEnd(l1ToL2Messages, Fr.ZERO, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP), - ); + // Append the block's real message leaves unpadded at compact indices. + await worldTrees.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2Messages); // We do not need to add public data writes because we apply them as we go. } diff --git a/yarn-project/validator-client/src/checkpoint_builder.test.ts b/yarn-project/validator-client/src/checkpoint_builder.test.ts index e0fe03bce5d1..1d140f683bc3 100644 --- a/yarn-project/validator-client/src/checkpoint_builder.test.ts +++ b/yarn-project/validator-client/src/checkpoint_builder.test.ts @@ -298,8 +298,8 @@ describe('CheckpointBuilder', () => { describe('capLimitsByCheckpointBudgets (validator mode)', () => { const totalBlobCapacity = BLOBS_PER_CHECKPOINT * FIELDS_PER_BLOB - NUM_CHECKPOINT_END_MARKER_FIELDS; - const firstBlockEndOverhead = getNumBlockEndBlobFields(true); - const nonFirstBlockEndOverhead = getNumBlockEndBlobFields(false); + const firstBlockEndOverhead = getNumBlockEndBlobFields(); + const nonFirstBlockEndOverhead = getNumBlockEndBlobFields(); it('caps L2 gas by remaining checkpoint mana', () => { const rollupManaLimit = 1_000_000; diff --git a/yarn-project/validator-client/src/checkpoint_builder.ts b/yarn-project/validator-client/src/checkpoint_builder.ts index 801dd309e297..7f7c10dddbaa 100644 --- a/yarn-project/validator-client/src/checkpoint_builder.ts +++ b/yarn-project/validator-client/src/checkpoint_builder.ts @@ -131,10 +131,15 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder { // Commit the fork checkpoint await forkCheckpoint.commit(); - // Add block to checkpoint - const { block } = await this.checkpointBuilder.addBlock(globalVariables, processedTxs, { - expectedEndState: opts.expectedEndState, - }); + // Add block to checkpoint, inserting this block's streaming L1-to-L2 message bundle (if any) into the fork. + const { block } = await this.checkpointBuilder.addBlock( + globalVariables, + processedTxs, + opts.l1ToL2Messages ?? [], + { + expectedEndState: opts.expectedEndState, + }, + ); this.contractsDB.commitCheckpoint(); @@ -203,8 +208,7 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder { // Remaining blob fields (block blob fields include both tx data and block-end overhead) const usedBlobFields = sum(existingBlocks.map(b => b.toBlobFields().length)); const totalBlobCapacity = BLOBS_PER_CHECKPOINT * FIELDS_PER_BLOB - NUM_CHECKPOINT_END_MARKER_FIELDS; - const isFirstBlock = existingBlocks.length === 0; - const blockEndOverhead = getNumBlockEndBlobFields(isFirstBlock); + const blockEndOverhead = getNumBlockEndBlobFields(); const maxBlobFieldsForTxs = totalBlobCapacity - usedBlobFields - blockEndOverhead; // Remaining txs @@ -317,7 +321,6 @@ export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder { checkpointNumber: CheckpointNumber, constants: CheckpointGlobalVariables, feeAssetPriceModifier: bigint, - l1ToL2Messages: Fr[], previousCheckpointOutHashes: Fr[], previousInboxRollingHash: Fr, fork: MerkleTreeWriteOperations, @@ -328,17 +331,15 @@ export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder { this.log.verbose(`Building new checkpoint ${checkpointNumber}`, { checkpointNumber, - msgCount: l1ToL2Messages.length, initialStateReference: stateReference.toInspect(), initialArchiveRoot: bufferToHex(archiveTree.root), constants, feeAssetPriceModifier, }); - const lightweightBuilder = await LightweightCheckpointBuilder.startNewCheckpoint( + const lightweightBuilder = LightweightCheckpointBuilder.startNewCheckpoint( checkpointNumber, constants, - l1ToL2Messages, previousCheckpointOutHashes, previousInboxRollingHash, fork, @@ -361,6 +362,9 @@ export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder { /** * Opens a checkpoint, either starting fresh or resuming from existing blocks. + * @param l1ToL2Messages - Messages the existing blocks already consumed, which seed the resumed checkpoint's + * rolling hash. Must be empty when starting fresh: a fresh checkpoint takes its messages per block, via + * `buildBlock`. */ async openCheckpoint( checkpointNumber: CheckpointNumber, @@ -377,11 +381,16 @@ export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder { const archiveTree = await fork.getTreeInfo(MerkleTreeId.ARCHIVE); if (existingBlocks.length === 0) { + if (l1ToL2Messages.length > 0) { + throw new Error( + `Cannot open checkpoint ${checkpointNumber} with ${l1ToL2Messages.length} messages and no existing blocks: ` + + `a fresh checkpoint consumes its messages per block`, + ); + } return this.startCheckpoint( checkpointNumber, constants, feeAssetPriceModifier, - l1ToL2Messages, previousCheckpointOutHashes, previousInboxRollingHash, fork, diff --git a/yarn-project/validator-client/src/duties/validation_service.ts b/yarn-project/validator-client/src/duties/validation_service.ts index 5fd753a7bdce..d5f2157d7a49 100644 --- a/yarn-project/validator-client/src/duties/validation_service.ts +++ b/yarn-project/validator-client/src/duties/validation_service.ts @@ -4,6 +4,7 @@ import type { EthAddress } from '@aztec/foundation/eth-address'; import type { Signature } from '@aztec/foundation/eth-signature'; import { createLogger } from '@aztec/foundation/log'; import { CommitteeAttestationsAndSigners } from '@aztec/stdlib/block'; +import type { InboxBucketRef } from '@aztec/stdlib/messaging'; import { BlockProposal, type BlockProposalOptions, @@ -53,6 +54,7 @@ export class ValidationService { txs: Tx[], proposerAttesterAddress: EthAddress | undefined, options: BlockProposalOptions, + bucketRef?: InboxBucketRef, ): Promise { // For testing: change the new archive to trigger state_mismatch validation failure if (options.broadcastInvalidBlockProposal) { @@ -82,6 +84,7 @@ export class ValidationService { this.signatureContext, payloadSigner, txsSigner, + bucketRef, ); } diff --git a/yarn-project/validator-client/src/proposal_handler.test.ts b/yarn-project/validator-client/src/proposal_handler.test.ts index f3a414719329..b0421851a91e 100644 --- a/yarn-project/validator-client/src/proposal_handler.test.ts +++ b/yarn-project/validator-client/src/proposal_handler.test.ts @@ -1,6 +1,6 @@ import type { Archiver } from '@aztec/archiver'; import type { BlobClientInterface } from '@aztec/blob-client/client'; -import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants'; +import { INITIAL_L2_BLOCK_NUM, MAX_BLOCKS_PER_CHECKPOINT } from '@aztec/constants'; import type { EpochCache } from '@aztec/epoch-cache'; import { MAX_FEE_ASSET_PRICE_MODIFIER_BPS } from '@aztec/ethereum/contracts'; import { BlockNumber, CheckpointNumber, SlotNumber } from '@aztec/foundation/branded-types'; @@ -15,8 +15,8 @@ import type { BlockData, L2Block, L2BlockSink, L2BlockSource } from '@aztec/stdl import { type Checkpoint, CheckpointReexecutionTracker, type ProposedCheckpointData } from '@aztec/stdlib/checkpoint'; import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers'; import type { ITxProvider, ValidatorClientFullConfig, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server'; -import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging'; -import { accumulateCheckpointOutHashes } from '@aztec/stdlib/messaging'; +import type { InboxBucket, L1ToL2MessageSource } from '@aztec/stdlib/messaging'; +import { InboxBucketRef, accumulateCheckpointOutHashes } from '@aztec/stdlib/messaging'; import { CheckpointHeader } from '@aztec/stdlib/rollup'; import { TEST_COORDINATION_SIGNATURE_CONTEXT, @@ -196,6 +196,25 @@ describe('ProposalHandler checkpoint validation', () => { expect(result).toEqual({ isValid: false, reason: 'too_many_blocks_in_checkpoint' }); }); + it('returns too_many_blocks_in_checkpoint when blocks exceed the protocol cap without an operator limit', async () => { + // The checkpoint root circuit caps the blocks a checkpoint may contain; an over-cap checkpoint is unprovable, + // and L1 cannot reject it at propose time, so the validator must not attest to it. + expect(config.maxBlocksPerCheckpoint).toBeUndefined(); + + const archiveRoot = Fr.random(); + blockSource.getBlockData.mockResolvedValue({ header: makeBlockHeader() } as BlockData); + const overCap = MAX_BLOCKS_PER_CHECKPOINT + 1; + const blocks = Array.from({ length: overCap }, (_, i) => ({ + archive: new AppendOnlyTreeSnapshot(i === overCap - 1 ? archiveRoot : Fr.random(), i + 1), + number: i + 1, + })) as unknown as L2Block[]; + blockSource.getBlocksForSlot.mockResolvedValue(blocks); + + const proposal = await makeProposal({ archiveRoot }); + const result = await handler.handleCheckpointProposal(proposal, proposalInfo); + expect(result).toEqual({ isValid: false, reason: 'too_many_blocks_in_checkpoint' }); + }); + it('caches validation result and returns it on second call', async () => { blockSource.getBlockData.mockResolvedValue(undefined); const proposal = await makeProposal(); @@ -503,7 +522,10 @@ describe('ProposalHandler checkpoint validation', () => { archive: new AppendOnlyTreeSnapshot(archiveRoot, 1), number: 1, checkpointNumber: CheckpointNumber(1), - header: { globalVariables: GlobalVariables.empty({ slotNumber: SlotNumber(1) }) }, + header: { + globalVariables: GlobalVariables.empty({ slotNumber: SlotNumber(1) }), + state: { l1ToL2MessageTree: { nextAvailableLeafIndex: 0 } }, + }, } as unknown as L2Block; blockSource.getBlockData.mockResolvedValue({ header: makeBlockHeader() } as BlockData); @@ -798,4 +820,220 @@ describe('ProposalHandler checkpoint validation', () => { }); }); }); + + // Streaming Inbox: a block proposal's L1-to-L2 bundle is derived from its bucket reference and gated by the four + // acceptance checks, replacing the legacy per-checkpoint inHash comparison. + describe('handleBlockProposal streaming inbox checks', () => { + const bucket = (overrides: Partial = {}): InboxBucket => ({ + seq: 1n, + inboxRollingHash: new Fr(0xabc), + totalMsgCount: 2n, + timestamp: 100n, + msgCount: 2, + lastMessageIndex: 1n, + ...overrides, + }); + + /** Genesis-parent streaming block proposal at slot 1, with the handler wired to reach the streaming checks. */ + async function setupStreamingProposal(bucketRef: InboxBucketRef | undefined) { + const proposal = await makeBlockProposal({ + blockHeader: makeBlockHeader(1, { slotNumber: SlotNumber(1) }), + archiveRoot: Fr.random(), + txHashes: [], + bucketRef, + }); + blockSource.getGenesisValues.mockResolvedValue({ + genesisArchiveRoot: proposal.blockHeader.lastArchive.root, + } as any); + blockSource.getBlockData.mockResolvedValue(undefined); + + const blockProposalValidator = mock(); + blockProposalValidator.validate.mockResolvedValue({ result: 'accept' } as any); + const txProvider = mock(); + txProvider.getTxsForBlockProposal.mockResolvedValue({ txs: [], missingTxs: [] } as any); + + // Well past the 12s lag for a bucket opened at t=100. + dateProvider.setTime(1_000_000); + + const blockHandler = new ProposalHandler( + checkpointsBuilder, + mock(), + blockSource, + l1ToL2MessageSource, + txProvider, + blockProposalValidator, + epochCache, + consensusTimetable, + config, + mock(), + new CheckpointReexecutionTracker(), + metrics, + dateProvider, + ); + return { proposal, blockHandler }; + } + + it('rejects (without re-executing) when the referenced bucket is unknown', async () => { + const ref = new InboxBucketRef(1n, 100n, new Fr(0xabc)); + const { proposal, blockHandler } = await setupStreamingProposal(ref); + l1ToL2MessageSource.getInboxBucket.mockResolvedValue(undefined); + const reexecuteSpy = jest.spyOn(blockHandler, 'reexecuteTransactions'); + + const result = await blockHandler.handleBlockProposal(proposal, {} as any, true); + + expect(result).toEqual({ + isValid: false, + blockNumber: BlockNumber(INITIAL_L2_BLOCK_NUM), + reason: 'bucket_unknown', + }); + expect(reexecuteSpy).not.toHaveBeenCalled(); + }); + + it('rejects (without re-executing) when the resolved bucket hash disagrees with the reference', async () => { + const ref = new InboxBucketRef(1n, 100n, new Fr(0xdead)); + const { proposal, blockHandler } = await setupStreamingProposal(ref); + l1ToL2MessageSource.getInboxBucket.mockResolvedValue(bucket({ inboxRollingHash: new Fr(0xabc) })); + const reexecuteSpy = jest.spyOn(blockHandler, 'reexecuteTransactions'); + + const result = await blockHandler.handleBlockProposal(proposal, {} as any, true); + + expect(result).toEqual({ + isValid: false, + blockNumber: BlockNumber(INITIAL_L2_BLOCK_NUM), + reason: 'bucket_hash_mismatch', + }); + expect(reexecuteSpy).not.toHaveBeenCalled(); + }); + + it('re-executes with the bundle derived from the buckets when the checks pass', async () => { + const ref = new InboxBucketRef(1n, 100n, new Fr(0xabc)); + const { proposal, blockHandler } = await setupStreamingProposal(ref); + const derivedBundle = [new Fr(1000), new Fr(1001)]; + l1ToL2MessageSource.getInboxBucket.mockResolvedValue(bucket()); + l1ToL2MessageSource.getInboxBucketByTotalMsgCount.mockResolvedValue( + bucket({ seq: 0n, totalMsgCount: 0n, msgCount: 0 }), + ); + l1ToL2MessageSource.getL1ToL2MessagesBetweenBuckets.mockResolvedValue(derivedBundle); + const reexecuteSpy = jest + .spyOn(blockHandler, 'reexecuteTransactions') + .mockResolvedValue({ block: undefined } as any); + + const result = await blockHandler.handleBlockProposal(proposal, {} as any, true); + + expect(result.isValid).toBe(true); + // The block re-executes with the derived per-block bundle (streaming is the only path). + expect(reexecuteSpy).toHaveBeenCalledWith( + proposal, + BlockNumber(INITIAL_L2_BLOCK_NUM), + CheckpointNumber.INITIAL, + [], + derivedBundle, + expect.anything(), + expect.anything(), + ); + }); + }); + + // Streaming Inbox: the checkpoint handler enforces the last-block minimum-consumption (censorship) rule before + // attesting. + describe('checkpoint proposal last-block censorship', () => { + /** Two-block checkpoint at slot 10 whose last block consumed through leaf count `lastBlockTotal`. */ + function setupCensorshipMocks(lastBlockTotal: number) { + const archiveRoot = Fr.random(); + const blockWithLeafCount = (leafCount: number, archive: Fr, number: number) => + ({ + archive: new AppendOnlyTreeSnapshot(archive, number), + number, + checkpointNumber: CheckpointNumber(1), + header: { + globalVariables: GlobalVariables.empty({ slotNumber: SlotNumber(10) }), + state: { l1ToL2MessageTree: { nextAvailableLeafIndex: leafCount } }, + }, + }) as unknown as L2Block; + + blockSource.getBlockData.mockResolvedValue({ + header: makeBlockHeader(), + checkpointNumber: CheckpointNumber(1), + } as any); + blockSource.getBlocksForSlot.mockResolvedValue([ + blockWithLeafCount(0, Fr.random(), 1), + blockWithLeafCount(lastBlockTotal, archiveRoot, 2), + ]); + return { archiveRoot }; + } + + async function makeSlot10Proposal(archiveRoot: Fr) { + return ( + await makeCheckpointProposal({ + checkpointHeader: makeCheckpointHeader(0, { slotNumber: SlotNumber(10) }), + archiveRoot, + }) + ).toCore(); + } + + it('refuses to attest when a mandatory bucket (at or before the cutoff) is left unconsumed', async () => { + handler.updateConfig(config); + const { archiveRoot } = setupCensorshipMocks(2); + // cutoff(slot=10) with l1GenesisTime=0, slotDuration=24, lag=12 is (10-1)*24 - 12 = 204. + l1ToL2MessageSource.getInboxBucketByTotalMsgCount.mockResolvedValue({ + seq: 1n, + totalMsgCount: 2n, + } as InboxBucket); + // The next (first unconsumed) bucket opened at t=100 <= cutoff 204 is mandatory and was left unconsumed. + l1ToL2MessageSource.getInboxBucket.mockResolvedValue({ + seq: 2n, + totalMsgCount: 5n, + timestamp: 100n, + } as InboxBucket); + + const result = await handler.handleCheckpointProposal(await makeSlot10Proposal(archiveRoot), proposalInfo); + + expect(result).toEqual({ + isValid: false, + reason: 'inbox_consumption_insufficient', + checkpointNumber: CheckpointNumber(1), + }); + }); + + it('does not reject on censorship when the first unconsumed bucket is past the cutoff', async () => { + handler.updateConfig(config); + const { archiveRoot } = setupCensorshipMocks(2); + l1ToL2MessageSource.getInboxBucketByTotalMsgCount.mockResolvedValue({ + seq: 1n, + totalMsgCount: 2n, + } as InboxBucket); + // Next bucket opened at t=205 > cutoff 204: not mandatory, so the censorship check passes and validation + // proceeds past it to the checkpoint rebuild (which mismatches here, an unrelated reason). + l1ToL2MessageSource.getInboxBucket.mockResolvedValue({ + seq: 2n, + totalMsgCount: 5n, + timestamp: 205n, + } as InboxBucket); + const proposal = await makeSlot10Proposal(archiveRoot); + // The fork's archive root is checked against the proposal's before the rebuild; report a match so + // validation gets past it to the rebuild, which is the rejection this test asserts on. + checkpointsBuilder.getFork.mockResolvedValue({ + [Symbol.asyncDispose]: jest.fn(), + getTreeInfo: () => Promise.resolve({ root: proposal.checkpointHeader.lastArchiveRoot.toBuffer() }), + } as any); + const mockBuilder = mock(); + mockBuilder.completeCheckpoint.mockResolvedValue({ + header: CheckpointHeader.empty(), + archive: new AppendOnlyTreeSnapshot(Fr.ZERO, 0), + getCheckpointOutHash: () => Fr.random(), + blocks: [], + number: CheckpointNumber(1), + } as unknown as Checkpoint); + checkpointsBuilder.openCheckpoint.mockResolvedValue(mockBuilder); + + const result = await handler.handleCheckpointProposal(proposal, proposalInfo); + + // The censorship rule passed; the checkpoint is rejected later by the rebuild, not the consumption check. + expect(result).toEqual({ + isValid: false, + reason: 'checkpoint_header_mismatch', + checkpointNumber: CheckpointNumber(1), + }); + }); + }); }); diff --git a/yarn-project/validator-client/src/proposal_handler.ts b/yarn-project/validator-client/src/proposal_handler.ts index 96f20aada864..107b56ed3228 100644 --- a/yarn-project/validator-client/src/proposal_handler.ts +++ b/yarn-project/validator-client/src/proposal_handler.ts @@ -1,7 +1,13 @@ import type { Archiver } from '@aztec/archiver'; import type { BlobClientInterface } from '@aztec/blob-client/client'; import { type Blob, encodeCheckpointBlobDataFromBlocks, getBlobsPerL1Block } from '@aztec/blob-lib'; -import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants'; +import { + INBOX_LAG_SECONDS, + INITIAL_L2_BLOCK_NUM, + MAX_BLOCKS_PER_CHECKPOINT, + MAX_L1_TO_L2_MSGS_PER_BLOCK, + MAX_L1_TO_L2_MSGS_PER_CHECKPOINT, +} from '@aztec/constants'; import type { EpochCache } from '@aztec/epoch-cache'; import { validateFeeAssetPriceModifier } from '@aztec/ethereum/contracts'; import { @@ -38,7 +44,8 @@ import type { import { type L1ToL2MessageSource, accumulateCheckpointOutHashes, - computeInHashFromL1ToL2Messages, + getInboxCutoffTimestamp, + isInboxConsumptionSufficient, } from '@aztec/stdlib/messaging'; import type { BlockProposal, CheckpointAttestation, CheckpointProposalCore } from '@aztec/stdlib/p2p'; import type { ConsensusTimetable } from '@aztec/stdlib/timetable'; @@ -55,6 +62,11 @@ import { type TelemetryClient, type Tracer, getTelemetryClient } from '@aztec/te import type { FullNodeCheckpointsBuilder } from './checkpoint_builder.js'; import type { ValidatorMetrics } from './metrics.js'; +import { + type StreamingBlockCheckReason, + type StreamingBlockCheckResult, + checkStreamingBlockProposal, +} from './streaming_inbox_checks.js'; export type BlockProposalValidationFailureReason = | 'invalid_signature' @@ -62,6 +74,8 @@ export type BlockProposalValidationFailureReason = | 'parent_block_not_found' | 'parent_block_wrong_slot' | 'in_hash_mismatch' + // Streaming Inbox per-block acceptance failures. + | StreamingBlockCheckReason | 'global_variables_mismatch' | 'block_number_already_exists' | 'txs_not_available' @@ -109,6 +123,8 @@ export type CheckpointProposalValidationFailureReason = | 'checkpoint_header_mismatch' | 'archive_mismatch' | 'out_hash_mismatch' + // Streaming Inbox last-block censorship failure. + | 'inbox_consumption_insufficient' | 'checkpoint_validation_failed'; /** @@ -134,6 +150,7 @@ const CHECKPOINT_VALIDATION_REASON_TO_OUTCOME: Record< checkpoint_header_mismatch: 'invalid', archive_mismatch: 'invalid', out_hash_mismatch: 'invalid', + inbox_consumption_insufficient: 'invalid', checkpoint_validation_failed: 'invalid', }; @@ -196,6 +213,9 @@ export const SLASHABLE_CHECKPOINT_PROPOSAL_VALIDATION_RESULT: Record< ['last_block_archive_mismatch']: true, // disabled + // Streaming Inbox last-block censorship: kept out of slashing while the streaming path is new; L1 `propose` is the + // authoritative reject (Rollup__UnconsumedInboxMessages). + ['inbox_consumption_insufficient']: false, ['invalid_signature']: false, ['last_block_not_found']: false, ['block_fetch_error']: false, @@ -556,18 +576,18 @@ export class ProposalHandler { const checkpointNumber = checkpointResult.checkpointNumber; proposalInfo.checkpointNumber = checkpointNumber; - // Check that I have the same set of l1ToL2Messages as the proposal - const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(checkpointNumber); - const computedInHash = computeInHashFromL1ToL2Messages(l1ToL2Messages); - const proposalInHash = proposal.inHash; - if (!computedInHash.equals(proposalInHash)) { - this.log.warn(`L1 to L2 messages in hash mismatch, skipping processing`, { - proposalInHash: proposalInHash.toString(), - computedInHash: computedInHash.toString(), + // Resolve this block's L1-to-L2 message bundle from its proposal bucket reference, gated by the four streaming + // acceptance checks. + const streamingResult = await this.runStreamingBlockChecks(proposal, blockNumber, parentBlock); + if (!streamingResult.accepted) { + this.log.warn(`Streaming Inbox block acceptance check failed, skipping processing`, { + reason: streamingResult.reason, + bucketRef: proposal.bucketRef?.toInspect(), ...proposalInfo, }); - return { isValid: false, blockNumber, reason: 'in_hash_mismatch' }; + return { isValid: false, blockNumber, reason: streamingResult.reason }; } + const l1ToL2Messages = streamingResult.bundle; // Check that all of the transactions in the proposal are available if (missingTxs.length > 0) { @@ -886,6 +906,121 @@ export class ProposalHandler { } } + /** + * Runs the streaming-Inbox per-block acceptance checks for a block proposal and returns the derived L1-to-L2 + * message bundle for re-execution, or a rejection reason. The parent block's consumed total and the checkpoint's + * starting total are derived from L1-to-L2 tree leaf counts; a parent whose count does not sit on a bucket boundary + * is rejected inside {@link checkStreamingBlockProposal}. + */ + private async runStreamingBlockChecks( + proposal: BlockProposal, + blockNumber: BlockNumber, + parentBlock: 'genesis' | BlockData, + ): Promise { + const parentTotalMsgCount = this.getConsumedMsgTotal(parentBlock); + const checkpointStartTotalMsgCount = await this.resolveCheckpointStartTotal( + blockNumber, + proposal.indexWithinCheckpoint, + parentTotalMsgCount, + ); + if (checkpointStartTotalMsgCount === undefined) { + // The block before the checkpoint's first block has not synced locally, so the per-checkpoint cap origin is + // unavailable: treat as an unknown local view. There is no bounded wait for the missing block yet. + return { accepted: false, reason: 'bucket_unknown' }; + } + const nowSeconds = BigInt(Math.floor(this.dateProvider.now() / 1000)); + return checkStreamingBlockProposal({ + messageSource: this.l1ToL2MessageSource, + bucketRef: proposal.bucketRef, + parentTotalMsgCount, + checkpointStartTotalMsgCount, + nowSeconds, + lagSeconds: INBOX_LAG_SECONDS, + perBlockCap: MAX_L1_TO_L2_MSGS_PER_BLOCK, + perCheckpointCap: MAX_L1_TO_L2_MSGS_PER_CHECKPOINT, + }); + } + + /** A block's L1-to-L2 message tree leaf count: the cumulative Inbox message count it consumed through. */ + private blockLeafCount(block: BlockData | L2Block): bigint { + return BigInt(block.header.state.l1ToL2MessageTree.nextAvailableLeafIndex); + } + + /** The cumulative Inbox message count consumed through a block: its L1-to-L2 tree leaf count (0 at genesis). */ + private getConsumedMsgTotal(block: 'genesis' | BlockData): bigint { + return block === 'genesis' ? 0n : this.blockLeafCount(block); + } + + /** + * The cumulative Inbox message count consumed as of the parent checkpoint (the per-checkpoint cap origin). For a + * checkpoint's first block this is the parent block's total; for a later block it is the leaf count of the block + * before the checkpoint's first block. Returns undefined when that block has not synced locally. + */ + private resolveCheckpointStartTotal( + blockNumber: BlockNumber, + indexWithinCheckpoint: number, + parentTotalMsgCount: bigint, + ): Promise { + return indexWithinCheckpoint === 0 + ? Promise.resolve(parentTotalMsgCount) + : this.getPreBlockConsumedTotal(blockNumber - indexWithinCheckpoint); + } + + /** + * The cumulative Inbox message count consumed as of the block immediately before `firstBlockNumber` (its L1-to-L2 + * tree leaf count): 0 when that block is genesis, undefined when it has not synced locally. + */ + private async getPreBlockConsumedTotal(firstBlockNumber: number): Promise { + const preBlockNumber = firstBlockNumber - 1; + if (preBlockNumber < INITIAL_L2_BLOCK_NUM) { + return 0n; + } + const preBlock = await this.blockSource.getBlockData({ number: BlockNumber(preBlockNumber) }); + return preBlock === undefined ? undefined : this.blockLeafCount(preBlock); + } + + /** + * Enforces the streaming-Inbox last-block minimum-consumption (censorship) rule for a checkpoint, mirroring + * `ProposeLib.validateInboxConsumption`: the first bucket the checkpoint left unconsumed must be absent, past the + * cutoff, or a cap-escape. Returns true (sufficient) when the checkpoint's consumption cannot be resolved against + * the local Inbox view, deferring to L1 `propose` as the authoritative reject. + */ + private async isLastBlockConsumptionSufficient(slot: SlotNumber, blocks: L2Block[]): Promise { + const lastBlockTotal = this.blockLeafCount(blocks[blocks.length - 1]); + const checkpointStartTotal = await this.getPreBlockConsumedTotal(blocks[0].number); + const lastConsumedBucket = await this.l1ToL2MessageSource.getInboxBucketByTotalMsgCount(lastBlockTotal); + if (checkpointStartTotal === undefined || lastConsumedBucket === undefined) { + return true; + } + const nextBucket = await this.l1ToL2MessageSource.getInboxBucket(lastConsumedBucket.seq + 1n); + const cutoffTimestamp = getInboxCutoffTimestamp(slot, this.epochCache.getL1Constants(), INBOX_LAG_SECONDS); + return isInboxConsumptionSufficient({ + nextBucket, + cutoffTimestamp, + checkpointStartTotalMsgCount: checkpointStartTotal, + perCheckpointCap: MAX_L1_TO_L2_MSGS_PER_CHECKPOINT, + }); + } + + /** + * Derives the ordered list of L1-to-L2 messages a checkpoint consumed across its blocks, from the Inbox buckets + * between the parent checkpoint's consumed position and the checkpoint's last block. Empty when + * the checkpoint consumed nothing or its consumption cannot be resolved against the local Inbox view. + */ + private async deriveCheckpointConsumedMessages(blocks: L2Block[]): Promise { + const checkpointStartTotal = await this.getPreBlockConsumedTotal(blocks[0].number); + const lastBlockTotal = this.blockLeafCount(blocks[blocks.length - 1]); + if (checkpointStartTotal === undefined || lastBlockTotal <= checkpointStartTotal) { + return []; + } + const startBucket = await this.l1ToL2MessageSource.getInboxBucketByTotalMsgCount(checkpointStartTotal); + const endBucket = await this.l1ToL2MessageSource.getInboxBucketByTotalMsgCount(lastBlockTotal); + if (startBucket === undefined || endBucket === undefined) { + return []; + } + return this.l1ToL2MessageSource.getL1ToL2MessagesBetweenBuckets(startBucket.seq, endBucket.seq); + } + async reexecuteTransactions( proposal: BlockProposal, blockNumber: BlockNumber, @@ -935,12 +1070,13 @@ export class ProposalHandler { gasFees: blockHeader.globalVariables.gasFees, }; - // Create checkpoint builder with prior blocks + // Create checkpoint builder with prior blocks. The messages the prior blocks consumed are not needed: this path + // only re-executes and compares the new block, never completing the checkpoint (whose rolling hash they seed). const checkpointBuilder = await this.checkpointsBuilder.openCheckpoint( checkpointNumber, constants, 0n, // only takes effect in the following checkpoint. - l1ToL2Messages, + [], previousCheckpointOutHashes, previousInboxRollingHash, fork, @@ -961,6 +1097,7 @@ export class ProposalHandler { expectedEndState: blockHeader.state, maxTransactions: this.config.validateMaxTxsPerBlock, maxBlockGas, + l1ToL2Messages, }); const { block, failedTxs } = result; @@ -1146,9 +1283,15 @@ export class ProposalHandler { }; } - // Note this condition should never trigger, since we dont process block proposals that exceed indexWithinCheckpoint - const maxBlocksPerCheckpoint = this.config.maxBlocksPerCheckpoint; - if (maxBlocksPerCheckpoint !== undefined && blocks.length > maxBlocksPerCheckpoint) { + // The checkpoint root circuit caps how many blocks a checkpoint may contain, and L1 cannot check it when the + // checkpoint is proposed (`ProposedHeader` carries no block count, only `blockHeadersHash`). An over-cap + // checkpoint would therefore be accepted on L1 and then be unprovable, forcing an epoch reorg, so validators + // reject it before attesting. An operator-configured limit may only tighten the protocol cap, never relax it. + const maxBlocksPerCheckpoint = Math.min( + this.config.maxBlocksPerCheckpoint ?? MAX_BLOCKS_PER_CHECKPOINT, + MAX_BLOCKS_PER_CHECKPOINT, + ); + if (blocks.length > maxBlocksPerCheckpoint) { this.log.warn(`Checkpoint proposal exceeds maxBlocksPerCheckpoint`, { ...proposalInfo, blocksInProposal: blocks.length, @@ -1171,8 +1314,20 @@ export class ProposalHandler { const constants = this.extractCheckpointConstants(firstBlock); const checkpointNumber = firstBlock.checkpointNumber; - // Get L1-to-L2 messages for this checkpoint - const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(checkpointNumber); + // Streaming Inbox: on the last block of a checkpoint, enforce the minimum-consumption + // (censorship) rule before attesting. Reject (no attestation) if a mandatory bucket was left unconsumed. + if (!(await this.isLastBlockConsumptionSufficient(slot, blocks))) { + this.log.warn(`Streaming Inbox last-block censorship check failed, refusing to attest`, { + ...proposalInfo, + checkpointNumber, + }); + return { isValid: false, reason: 'inbox_consumption_insufficient', checkpointNumber }; + } + + // Derive the checkpoint's consumed L1-to-L2 message list from the Inbox buckets between the parent checkpoint's + // consumed position and the last block's (compact indexing). The messages are already in the db from per-block + // validation; this list only drives the checkpoint's rolling-hash recomputation in completeCheckpoint. + const l1ToL2Messages = await this.deriveCheckpointConsumedMessages(blocks); // Collect the out hashes of all the checkpoints before this one in the same epoch. // See note on the analogous block-proposal site: the helper handles pipelining lag. diff --git a/yarn-project/validator-client/src/streaming_inbox_checks.test.ts b/yarn-project/validator-client/src/streaming_inbox_checks.test.ts new file mode 100644 index 000000000000..1608f2bcd2b7 --- /dev/null +++ b/yarn-project/validator-client/src/streaming_inbox_checks.test.ts @@ -0,0 +1,286 @@ +import { Fr } from '@aztec/foundation/curves/bn254'; +import type { InboxBucket } from '@aztec/stdlib/messaging'; +import { InboxBucketRef } from '@aztec/stdlib/messaging'; + +import { describe, expect, it } from '@jest/globals'; + +import { + type StreamingBlockCheckInput, + type StreamingInboxBucketSource, + checkStreamingBlockProposal, +} from './streaming_inbox_checks.js'; + +const LAG_SECONDS = 12; +const PER_BLOCK_CAP = 1024; +const PER_CHECKPOINT_CAP = 1024; +const NOW = 10_000n; + +/** + * In-memory Inbox-bucket view mirroring the archiver store's index semantics: buckets keyed by sequence number, a flat + * leaves array indexed by global message index, and `getL1ToL2MessagesBetweenBuckets` slicing that array by the + * `(from, to]` bucket range (start = fromBucket.lastMessageIndex + 1, genesis when from == 0). + */ +class FakeInboxView implements StreamingInboxBucketSource { + private readonly buckets = new Map(); + private readonly leaves: Fr[] = []; + + constructor() { + // Genesis sentinel bucket 0 {total 0}, as the archiver store holds it (the "consumed nothing" base case). + this.buckets.set(0n, { + seq: 0n, + inboxRollingHash: Fr.ZERO, + totalMsgCount: 0n, + timestamp: 0n, + msgCount: 0, + lastMessageIndex: 0n, + }); + } + + /** Appends a bucket of `msgCount` leaves opened at `timestamp`, with a rolling hash derived from `seq`. */ + addBucket(seq: number, msgCount: number, timestamp: number, rollingHash?: Fr): InboxBucket { + const priorTotal = this.leaves.length; + for (let i = 0; i < msgCount; i++) { + this.leaves.push(new Fr(1000 + priorTotal + i)); + } + const totalMsgCount = BigInt(this.leaves.length); + const bucket: InboxBucket = { + seq: BigInt(seq), + inboxRollingHash: rollingHash ?? new Fr(500 + seq), + totalMsgCount, + timestamp: BigInt(timestamp), + msgCount, + lastMessageIndex: totalMsgCount - 1n, + }; + this.buckets.set(BigInt(seq), bucket); + return bucket; + } + + getInboxBucket(seq: bigint): Promise { + return Promise.resolve(this.buckets.get(seq)); + } + + getInboxBucketByTotalMsgCount(totalMsgCount: bigint): Promise { + if (totalMsgCount === 0n) { + return Promise.resolve(this.buckets.get(0n)); + } + return Promise.resolve([...this.buckets.values()].find(b => b.totalMsgCount === totalMsgCount)); + } + + getL1ToL2MessagesBetweenBuckets(fromExclusive: bigint, toInclusive: bigint): Promise { + const toBucket = this.buckets.get(toInclusive); + if (toBucket === undefined) { + return Promise.resolve([]); + } + let startIndex = 0n; + if (fromExclusive > 0n) { + const fromBucket = this.buckets.get(fromExclusive); + if (fromBucket === undefined) { + return Promise.resolve([]); + } + startIndex = fromBucket.lastMessageIndex + 1n; + } + return Promise.resolve(this.leaves.slice(Number(startIndex), Number(toBucket.lastMessageIndex + 1n))); + } +} + +function refFor(bucket: InboxBucket, rollingHash = bucket.inboxRollingHash): InboxBucketRef { + return new InboxBucketRef(bucket.seq, bucket.timestamp, rollingHash); +} + +function baseInput(overrides: Partial): StreamingBlockCheckInput { + return { + messageSource: new FakeInboxView(), + bucketRef: undefined, + parentTotalMsgCount: 0n, + checkpointStartTotalMsgCount: 0n, + nowSeconds: NOW, + lagSeconds: LAG_SECONDS, + perBlockCap: PER_BLOCK_CAP, + perCheckpointCap: PER_CHECKPOINT_CAP, + ...overrides, + }; +} + +describe('checkStreamingBlockProposal', () => { + describe('check 1: bucket exists and hash matches', () => { + it('rejects a proposal with no bucket reference', async () => { + const result = await checkStreamingBlockProposal(baseInput({ bucketRef: undefined })); + expect(result).toEqual({ accepted: false, reason: 'bucket_unknown' }); + }); + + it('rejects promptly when the referenced bucket is unknown (no waiting)', async () => { + const view = new FakeInboxView(); + const start = Date.now(); + const result = await checkStreamingBlockProposal( + baseInput({ messageSource: view, bucketRef: new InboxBucketRef(7n, 100n, new Fr(1)) }), + ); + expect(result).toEqual({ accepted: false, reason: 'bucket_unknown' }); + // The happy path rejects immediately; there is no bounded wait yet. Assert it did not sleep. + expect(Date.now() - start).toBeLessThan(500); + }); + + it('rejects when the resolved bucket hash differs from the reference', async () => { + const view = new FakeInboxView(); + const bucket = view.addBucket(1, 3, 100); + const result = await checkStreamingBlockProposal( + baseInput({ messageSource: view, bucketRef: refFor(bucket, new Fr(999)) }), + ); + expect(result).toEqual({ accepted: false, reason: 'bucket_hash_mismatch' }); + }); + }); + + describe('check 2: consumption moves forward', () => { + it('rejects when the bucket total is behind the parent block', async () => { + const view = new FakeInboxView(); + const bucket = view.addBucket(1, 3, 100); // total 3 + const result = await checkStreamingBlockProposal( + baseInput({ messageSource: view, bucketRef: refFor(bucket), parentTotalMsgCount: 5n }), + ); + expect(result).toEqual({ accepted: false, reason: 'bucket_moves_backwards' }); + }); + + it('accepts an empty-consumption block that reuses the parent bucket (empty bundle)', async () => { + const view = new FakeInboxView(); + const bucket = view.addBucket(1, 3, 100); // total 3 + const result = await checkStreamingBlockProposal( + baseInput({ messageSource: view, bucketRef: refFor(bucket), parentTotalMsgCount: 3n }), + ); + expect(result).toEqual({ accepted: true, bundle: [] }); + }); + }); + + describe('check 3: bucket is at least lagSeconds old', () => { + it('accepts a bucket exactly lagSeconds old (inclusive boundary)', async () => { + const view = new FakeInboxView(); + const bucket = view.addBucket(1, 2, Number(NOW) - LAG_SECONDS); // timestamp == now - lag + const result = await checkStreamingBlockProposal(baseInput({ messageSource: view, bucketRef: refFor(bucket) })); + expect(result.accepted).toBe(true); + }); + + it('rejects a bucket one second too new', async () => { + const view = new FakeInboxView(); + const bucket = view.addBucket(1, 2, Number(NOW) - LAG_SECONDS + 1); + const result = await checkStreamingBlockProposal(baseInput({ messageSource: view, bucketRef: refFor(bucket) })); + expect(result).toEqual({ accepted: false, reason: 'bucket_too_new' }); + }); + }); + + describe('check 4: caps', () => { + it('accepts a block consuming exactly the per-block cap', async () => { + const view = new FakeInboxView(); + const bucket = view.addBucket(1, PER_BLOCK_CAP, 100); + const result = await checkStreamingBlockProposal( + baseInput({ messageSource: view, bucketRef: refFor(bucket), perBlockCap: PER_BLOCK_CAP }), + ); + expect(result.accepted).toBe(true); + }); + + it('rejects a block consuming one over the per-block cap', async () => { + const view = new FakeInboxView(); + const bucket = view.addBucket(1, 4, 100); + const result = await checkStreamingBlockProposal( + baseInput({ messageSource: view, bucketRef: refFor(bucket), perBlockCap: 3 }), + ); + expect(result).toEqual({ accepted: false, reason: 'bundle_over_block_cap' }); + }); + + it('rejects when the running checkpoint total exceeds the per-checkpoint cap', async () => { + const view = new FakeInboxView(); + view.addBucket(1, 3, 100); // total 3, the checkpoint's earlier consumption + const bucket = view.addBucket(2, 3, 100); // total 6 + const result = await checkStreamingBlockProposal( + baseInput({ + messageSource: view, + bucketRef: refFor(bucket), + parentTotalMsgCount: 3n, + checkpointStartTotalMsgCount: 0n, + perCheckpointCap: 5, // 6 - 0 = 6 > 5 + }), + ); + expect(result).toEqual({ accepted: false, reason: 'checkpoint_over_msg_cap' }); + }); + }); + + describe('bundle derivation', () => { + it('derives the bundle for a genesis-parent first block', async () => { + const view = new FakeInboxView(); + const bucket = view.addBucket(1, 3, 100); // leaves at global indices 0..2 + const result = await checkStreamingBlockProposal( + baseInput({ messageSource: view, bucketRef: refFor(bucket), parentTotalMsgCount: 0n }), + ); + expect(result).toEqual({ accepted: true, bundle: [new Fr(1000), new Fr(1001), new Fr(1002)] }); + }); + + it('derives the bundle spanning multiple buckets since the parent', async () => { + const view = new FakeInboxView(); + view.addBucket(1, 2, 100); // parent consumed through here, total 2 + view.addBucket(2, 2, 100); // total 4 + const proposed = view.addBucket(3, 1, 100); // total 5 + const result = await checkStreamingBlockProposal( + baseInput({ messageSource: view, bucketRef: refFor(proposed), parentTotalMsgCount: 2n }), + ); + // Bundle = leaves at global indices 2,3,4 (buckets 2 and 3), derived after resolving the parent bucket (seq 1). + expect(result).toEqual({ accepted: true, bundle: [new Fr(1002), new Fr(1003), new Fr(1004)] }); + }); + + it('rejects when the parent leaf count does not sit on a bucket boundary (padded legacy parent)', async () => { + const view = new FakeInboxView(); + view.addBucket(1, 2, 100); // total 2 + const proposed = view.addBucket(2, 2, 100); // total 4 + // Parent leaf count 3 is between bucket boundaries (2 and 4): unresolvable. + const result = await checkStreamingBlockProposal( + baseInput({ messageSource: view, bucketRef: refFor(proposed), parentTotalMsgCount: 3n }), + ); + expect(result).toEqual({ accepted: false, reason: 'parent_bucket_unresolved' }); + }); + }); + + describe('running-total accumulation across a checkpoint', () => { + it('accumulates the per-checkpoint total across three blocks against a fixed start', async () => { + // Checkpoint starts after bucket 1 (total 2). Three blocks each consume 2 messages: totals 4, 6, 8. + const view = new FakeInboxView(); + view.addBucket(1, 2, 100); // checkpoint start total 2 + const b2 = view.addBucket(2, 2, 100); // total 4 + const b3 = view.addBucket(3, 2, 100); // total 6 + const b4 = view.addBucket(4, 2, 100); // total 8 + const checkpointStart = 2n; + + // Block 1 (parent = bucket 1): checkpoint delta 4 - 2 = 2. + const r1 = await checkStreamingBlockProposal( + baseInput({ + messageSource: view, + bucketRef: refFor(b2), + parentTotalMsgCount: 2n, + checkpointStartTotalMsgCount: checkpointStart, + perCheckpointCap: 6, + }), + ); + expect(r1.accepted).toBe(true); + + // Block 3 (parent = bucket 3): checkpoint delta 8 - 2 = 6, exactly at the cap. + const r3 = await checkStreamingBlockProposal( + baseInput({ + messageSource: view, + bucketRef: refFor(b4), + parentTotalMsgCount: 6n, + checkpointStartTotalMsgCount: checkpointStart, + perCheckpointCap: 6, + }), + ); + expect(r3.accepted).toBe(true); + + // Same block against a tighter cap of 5: the accumulated delta 6 now exceeds it. + const r3Tight = await checkStreamingBlockProposal( + baseInput({ + messageSource: view, + bucketRef: refFor(b4), + parentTotalMsgCount: 6n, + checkpointStartTotalMsgCount: checkpointStart, + perCheckpointCap: 5, + }), + ); + expect(r3Tight).toEqual({ accepted: false, reason: 'checkpoint_over_msg_cap' }); + void b3; + }); + }); +}); diff --git a/yarn-project/validator-client/src/streaming_inbox_checks.ts b/yarn-project/validator-client/src/streaming_inbox_checks.ts new file mode 100644 index 000000000000..1859ead4d90d --- /dev/null +++ b/yarn-project/validator-client/src/streaming_inbox_checks.ts @@ -0,0 +1,132 @@ +import type { Fr } from '@aztec/foundation/curves/bn254'; +import type { InboxBucketRef, L1ToL2MessageSource } from '@aztec/stdlib/messaging'; + +/** + * Reason a streaming-Inbox block proposal fails the per-block acceptance checks. Follows the + * handler's existing `{ isValid, reason }` string style. + */ +export type StreamingBlockCheckReason = + | 'bucket_unknown' + | 'bucket_hash_mismatch' + | 'parent_bucket_unresolved' + | 'bucket_moves_backwards' + | 'bucket_too_new' + | 'bundle_over_block_cap' + | 'checkpoint_over_msg_cap'; + +/** The subset of the archiver's Inbox-bucket queries the per-block streaming checks need. */ +export type StreamingInboxBucketSource = Pick< + L1ToL2MessageSource, + 'getInboxBucket' | 'getInboxBucketByTotalMsgCount' | 'getL1ToL2MessagesBetweenBuckets' +>; + +/** Inputs to the per-block streaming Inbox acceptance checks. */ +export type StreamingBlockCheckInput = { + /** Archiver Inbox-bucket queries (resolved against this node's own Inbox view). */ + messageSource: StreamingInboxBucketSource; + /** The proposal's bucket reference: `bucketSeq` is the lookup hint, `inboxRollingHash` the expected commitment. */ + bucketRef: InboxBucketRef | undefined; + /** Cumulative Inbox message count consumed through the parent block (its L1-to-L2 tree leaf count; 0 at genesis). */ + parentTotalMsgCount: bigint; + /** Cumulative Inbox message count consumed as of the parent checkpoint; the per-checkpoint cap origin. */ + checkpointStartTotalMsgCount: bigint; + /** Validation-time wall clock in seconds; the lag-eligibility anchor. */ + nowSeconds: bigint; + /** Minimum bucket age in seconds (`INBOX_LAG_SECONDS`) for a bucket to be lag-eligible. */ + lagSeconds: number; + /** Maximum number of messages this block may consume (`MAX_L1_TO_L2_MSGS_PER_BLOCK`). */ + perBlockCap: number; + /** Maximum number of messages the checkpoint may consume in total (`MAX_L1_TO_L2_MSGS_PER_CHECKPOINT`). */ + perCheckpointCap: number; +}; + +/** Result of the per-block streaming Inbox acceptance checks. */ +export type StreamingBlockCheckResult = + | { + /** All four checks passed; `bundle` is the message-leaf bundle this block consumes, for re-execution. */ + accepted: true; + bundle: Fr[]; + } + | { + /** A check failed; `reason` mirrors the L1 acceptance condition that would have rejected the proposal. */ + accepted: false; + reason: StreamingBlockCheckReason; + }; + +/** + * Runs the per-block acceptance checks a validator applies to a streaming block proposal, and + * derives the message-leaf bundle the block consumes (for re-execution). Mirrors the L1 acceptance conditions: + * + * 1. **Exists**: the referenced bucket resolves in this node's own Inbox view, and its consensus rolling hash matches + * the reference. An unknown bucket is an immediate reject here (there is no bounded wait yet); a hash + * mismatch means the wire reference disagrees with the local bucket. The reference is trusted only as a `bucketSeq` + * lookup hint — timestamp and message counts are read from the locally resolved bucket, never from the wire. + * 2. **Moves forward**: the bucket's cumulative total is at least the parent block's, so consumption never rewinds. + * Equal totals mean the block consumes nothing (empty bundle). + * 3. **Not too new**: the bucket is at least `lagSeconds` old at validation time (`timestamp <= now - lagSeconds`, + * inclusive — a bucket exactly `lagSeconds` old is eligible, matching L1's strict `>` "too new" test). + * 4. **Caps**: the per-block message count and the running per-checkpoint total fit their respective caps. + * + * The reject branch is a single function so a future bounded wait can wrap `bucket_unknown`. + */ +export async function checkStreamingBlockProposal(input: StreamingBlockCheckInput): Promise { + const { + messageSource, + bucketRef, + parentTotalMsgCount, + checkpointStartTotalMsgCount, + nowSeconds, + lagSeconds, + perBlockCap, + perCheckpointCap, + } = input; + + // A streaming proposal must carry a bucket reference to derive its bundle from. + if (bucketRef === undefined) { + return { accepted: false, reason: 'bucket_unknown' }; + } + + // Check 1: exists in our own Inbox view, and the resolved rolling hash matches the reference. + const bucket = await messageSource.getInboxBucket(bucketRef.bucketSeq); + if (bucket === undefined) { + return { accepted: false, reason: 'bucket_unknown' }; + } + if (!bucket.inboxRollingHash.equals(bucketRef.inboxRollingHash)) { + return { accepted: false, reason: 'bucket_hash_mismatch' }; + } + + // Check 2: consumption moves forward relative to the parent block. + if (bucket.totalMsgCount < parentTotalMsgCount) { + return { accepted: false, reason: 'bucket_moves_backwards' }; + } + + // Check 3: the bucket is at least `lagSeconds` old at validation time. + if (bucket.timestamp > nowSeconds - BigInt(lagSeconds)) { + return { accepted: false, reason: 'bucket_too_new' }; + } + + // Check 4a: the per-block message count fits the per-block cap. + const blockCount = bucket.totalMsgCount - parentTotalMsgCount; + if (blockCount > BigInt(perBlockCap)) { + return { accepted: false, reason: 'bundle_over_block_cap' }; + } + + // Check 4b: the running per-checkpoint total fits the per-checkpoint cap. + const checkpointCount = bucket.totalMsgCount - checkpointStartTotalMsgCount; + if (checkpointCount > BigInt(perCheckpointCap)) { + return { accepted: false, reason: 'checkpoint_over_msg_cap' }; + } + + // Derive the message bundle for re-execution: the leaves consumed between the parent bucket and the proposed one. + // The parent bucket is the one whose cumulative total equals the parent block's leaf count (messages are indexed + // compactly, with no padding); a parent whose count does not sit on a bucket boundary is unresolvable. + const parentBucket = await messageSource.getInboxBucketByTotalMsgCount(parentTotalMsgCount); + if (parentBucket === undefined) { + return { accepted: false, reason: 'parent_bucket_unresolved' }; + } + const bundle = + parentBucket.seq === bucket.seq + ? [] + : await messageSource.getL1ToL2MessagesBetweenBuckets(parentBucket.seq, bucket.seq); + return { accepted: true, bundle }; +} diff --git a/yarn-project/validator-client/src/validator.integration.test.ts b/yarn-project/validator-client/src/validator.integration.test.ts index 4dbb9302ce25..d12bb5608f50 100644 --- a/yarn-project/validator-client/src/validator.integration.test.ts +++ b/yarn-project/validator-client/src/validator.integration.test.ts @@ -26,7 +26,7 @@ import { CheckpointReexecutionTracker, L1PublishedData, PublishedCheckpoint } fr import { type L1RollupConstants, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers'; import { Gas, GasFees } from '@aztec/stdlib/gas'; import { tryStop } from '@aztec/stdlib/interfaces/server'; -import { computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging'; +import { InboxBucketRef, computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging'; import { type BlockProposal, CheckpointProposal } from '@aztec/stdlib/p2p'; import { mockTx } from '@aztec/stdlib/testing'; import { BlockHeader, type CheckpointGlobalVariables, Tx } from '@aztec/stdlib/tx'; @@ -48,7 +48,9 @@ jest.setTimeout(60_000); describe('ValidatorClient Integration', () => { // Constants for L1 const l1Constants: L1RollupConstants = { - l1GenesisTime: 0n, + // Non-zero genesis time so the slot-1 validation clock is well past INBOX_LAG_SECONDS; otherwise the streaming + // Inbox acceptance check rejects even a genesis-timestamp (0) bucket as `bucket_too_new`. + l1GenesisTime: 1_700_000_000n, slotDuration: 24, epochDuration: 16, ethereumSlotDuration: 12, @@ -253,8 +255,17 @@ describe('ValidatorClient Integration', () => { maxBlocksPerCheckpoint: 1, perBlockAllocationMultiplier: 1.2, minValidTxs: 0, + l1ToL2Messages, }); + // Resolve the Inbox bucket this block consumed through (keyed by its cumulative L1-to-L2 leaf count) and attach + // the reference, mirroring the sequencer's block-building loop which carries a bucketRef on every proposal. + // Without it the validator's streaming acceptance check rejects the proposal as + // `bucket_unknown`. A block that consumed nothing resolves to the genesis (or reused parent) bucket. + const blockTotal = BigInt(block.header.state.l1ToL2MessageTree.nextAvailableLeafIndex); + const bucket = await proposer.archiver.getInboxBucketByTotalMsgCount(blockTotal); + const bucketRef = bucket ? InboxBucketRef.fromBucket(bucket) : undefined; + const proposal = await proposer.validator.createBlockProposal( block.header, cpNumber, @@ -264,6 +275,7 @@ describe('ValidatorClient Integration', () => { usedTxs, proposerSigner.address, {}, + bucketRef, ); logger.warn(`Built block proposal for block ${blockNumber}`, { ...block.toBlockInfo() }); @@ -332,7 +344,6 @@ describe('ValidatorClient Integration', () => { checkpointNumber, globalVariables, 0n, - l1ToL2Messages, previousCheckpointOutHashes, Fr.ZERO, fork, @@ -342,7 +353,15 @@ describe('ValidatorClient Integration', () => { for (let i = 0; i < blockCount; i++) { const blockNumber = BlockNumber(startBlockNumber + i); const txs = await getTxsForBlock(blockNumber, blocks); - const block = await buildBlockProposal(builder, blockNumber, checkpointNumber, txs, l1ToL2Messages); + // The checkpoint's whole message bundle goes into its first block, matching how a validator derives each + // block's bundle from the block's own L1-to-L2 leaf-count delta. + const block = await buildBlockProposal( + builder, + blockNumber, + checkpointNumber, + txs, + i === 0 ? l1ToL2Messages : [], + ); blocks.push(block); } @@ -460,8 +479,8 @@ describe('ValidatorClient Integration', () => { it('validates and attests with txs anchored to proposed blocks and non-empty l1-to-l2 messages', async () => { // Create l1 to l2 messages and seed them into the archivers const l1ToL2Messages = makeInboxMessages(4, { messagesPerCheckpoint: 4 }); - await proposer.archiver.dataStores.messages.addL1ToL2Messages(l1ToL2Messages); - await attestor.archiver.dataStores.messages.addL1ToL2Messages(l1ToL2Messages); + await proposer.archiver.dataStores.messages.addL1ToL2MessageBuckets(l1ToL2Messages); + await attestor.archiver.dataStores.messages.addL1ToL2MessageBuckets(l1ToL2Messages); // Build txs anchored to the previously proposed block const { blocks, proposal } = await buildCheckpoint( @@ -660,10 +679,10 @@ describe('ValidatorClient Integration', () => { it('refuses block proposal with mismatching l1 to l2 messages', async () => { const l1ToL2Messages = makeInboxMessages(4, { messagesPerCheckpoint: 4 }); - await proposer.archiver.dataStores.messages.addL1ToL2Messages(l1ToL2Messages); + await proposer.archiver.dataStores.messages.addL1ToL2MessageBuckets(l1ToL2Messages); const otherL1ToL2Messages = makeInboxMessages(4, { messagesPerCheckpoint: 4 }); - await attestor.archiver.dataStores.messages.addL1ToL2Messages(otherL1ToL2Messages); + await attestor.archiver.dataStores.messages.addL1ToL2MessageBuckets(otherL1ToL2Messages); const { blocks } = await buildCheckpoint( CheckpointNumber(1), diff --git a/yarn-project/validator-client/src/validator.test.ts b/yarn-project/validator-client/src/validator.test.ts index 0dbc04e997a4..1af680ff3218 100644 --- a/yarn-project/validator-client/src/validator.test.ts +++ b/yarn-project/validator-client/src/validator.test.ts @@ -32,7 +32,12 @@ import { AztecAddress } from '@aztec/stdlib/aztec-address'; import { type BlockData, BlockHash, L2Block, type L2BlockSink, type L2BlockSource } from '@aztec/stdlib/block'; import { type Checkpoint, CheckpointReexecutionTracker, type ProposedCheckpointData } from '@aztec/stdlib/checkpoint'; import type { SlasherConfig, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server'; -import { type L1ToL2MessageSource, computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging'; +import { + type InboxBucket, + InboxBucketRef, + type L1ToL2MessageSource, + computeInHashFromL1ToL2Messages, +} from '@aztec/stdlib/messaging'; import type { BlockProposal } from '@aztec/stdlib/p2p'; import { CheckpointHeader } from '@aztec/stdlib/rollup'; import { @@ -453,11 +458,23 @@ describe('ValidatorClient', () => { Array.isArray(args) && args[0]?.offenseType === OffenseType.ATTESTED_TO_INVALID_CHECKPOINT_PROPOSAL, ); + // Streaming Inbox: an empty-consumption streaming setup. Proposals reference the genesis Inbox bucket, the + // parent block's L1-to-L2 leaf count equals its cumulative total (0), so the derived per-block bundle is empty. + const genesisInboxBucket: InboxBucket = { + seq: 0n, + inboxRollingHash: Fr.ZERO, + totalMsgCount: 0n, + timestamp: 0n, + msgCount: 0, + lastMessageIndex: 0n, + }; + const genesisBucketRef = InboxBucketRef.fromBucket(genesisInboxBucket); + beforeEach(async () => { const emptyInHash = computeInHashFromL1ToL2Messages([]); const blockHeader = makeBlockHeader(1, { blockNumber: BlockNumber(100), slotNumber: SlotNumber(100) }); blockNumber = BlockNumber(blockHeader.globalVariables.blockNumber); - proposal = await makeBlockProposal({ blockHeader, inHash: emptyInHash }); + proposal = await makeBlockProposal({ blockHeader, inHash: emptyInHash, bucketRef: genesisBucketRef }); // The proposal targets slot 100, which under pipelining is built during the previous slot. Set the // wall clock to the start of that build slot (target_slot_start - S), matching how a pipelined // proposer is positioned when validating an inbound block proposal. With S - 2E = 0 in this config @@ -517,6 +534,7 @@ describe('ValidatorClient', () => { getBlockNumber: () => blockNumber - 1, getSlot: () => parentSlot, globalVariables: blockHeader.globalVariables, + state: { l1ToL2MessageTree: { nextAvailableLeafIndex: 0 } }, }, archive: new AppendOnlyTreeSnapshot(Fr.random(), blockNumber - 1), blockHash: BlockHash.random(), @@ -530,6 +548,11 @@ describe('ValidatorClient', () => { blockSource.getGenesisValues.mockResolvedValue({ genesisArchiveRoot: new Fr(GENESIS_ARCHIVE_ROOT) }); blockSource.syncImmediate.mockImplementation(() => Promise.resolve()); + // Resolve every Inbox bucket query to the genesis bucket, so streaming checks accept with an empty bundle. + l1ToL2MessageSource.getInboxBucket.mockResolvedValue(genesisInboxBucket); + l1ToL2MessageSource.getInboxBucketByTotalMsgCount.mockResolvedValue(genesisInboxBucket); + l1ToL2MessageSource.getL1ToL2MessagesBetweenBuckets.mockResolvedValue([]); + const clonedBlockHeader = blockHeader.clone(); blockBuildResult = { publicProcessorDuration: 0, @@ -732,6 +755,7 @@ describe('ValidatorClient', () => { archiveRoot: proposal.archive, txHashes: proposal.txHashes, signer: selfSigner, + bucketRef: genesisBucketRef, }); epochCache.getProposerAttesterAddressInSlot.mockResolvedValue(selfSigner.address); @@ -1435,13 +1459,6 @@ describe('ValidatorClient', () => { expect(isValid).toBe(false); }); - it('should return false if messages do not match', async () => { - l1ToL2MessageSource.getL1ToL2Messages.mockResolvedValue([Fr.random()]); - - const isValid = await validatorClient.validateBlockProposal(proposal, sender); - expect(isValid).toBe(false); - }); - describe('non-first block in checkpoint validation', () => { // When indexWithinCheckpoint > 0, global variables must match parent block (except blockNumber). // The inHash validation is implicitly handled: all blocks in a checkpoint share the same diff --git a/yarn-project/validator-client/src/validator.ts b/yarn-project/validator-client/src/validator.ts index d81bc2f2fb66..97f8e6f9114e 100644 --- a/yarn-project/validator-client/src/validator.ts +++ b/yarn-project/validator-client/src/validator.ts @@ -31,7 +31,7 @@ import type { ValidatorClientFullConfig, WorldStateSynchronizer, } from '@aztec/stdlib/interfaces/server'; -import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging'; +import type { InboxBucketRef, L1ToL2MessageSource } from '@aztec/stdlib/messaging'; import { type BlockProposal, type BlockProposalOptions, @@ -922,6 +922,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter) txs: Tx[], proposerAddress: EthAddress | undefined, options: BlockProposalOptions = {}, + bucketRef?: InboxBucketRef, ): Promise { // Validate that we're not creating a proposal for an older or equal position if (this.lastProposedBlock) { @@ -953,6 +954,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter) broadcastInvalidBlockProposal: options.broadcastInvalidBlockProposal || this.config.broadcastInvalidBlockProposal, }, + bucketRef, ); this.lastProposedBlock = newProposal; return newProposal; diff --git a/yarn-project/world-state/src/native/native_world_state.test.ts b/yarn-project/world-state/src/native/native_world_state.test.ts index 5d9e1b96c922..aeceac469d4e 100644 --- a/yarn-project/world-state/src/native/native_world_state.test.ts +++ b/yarn-project/world-state/src/native/native_world_state.test.ts @@ -32,7 +32,14 @@ import { tmpdir } from 'os'; import { join } from 'path'; import type { WorldStateTreeMapSizes } from '../synchronizer/factory.js'; -import { assertSameState, compareChains, mockBlock, mockEmptyBlock, updateBlockState } from '../test/utils.js'; +import { + assertSameState, + compareChains, + mockBlock, + mockBlockWithIndex, + mockEmptyBlock, + updateBlockState, +} from '../test/utils.js'; import { INITIAL_NULLIFIER_TREE_SIZE, INITIAL_PUBLIC_DATA_TREE_SIZE } from '../world-state-db/merkle_tree_db.js'; import type { WorldStateStatusSummary } from './message.js'; import { NativeWorldStateService, WORLD_STATE_DB_VERSION, WORLD_STATE_DIR } from './native_world_state.js'; @@ -79,7 +86,7 @@ describe('NativeWorldState', () => { await ws.close(); }); - it('pads messages, note hashes, nullifiers correctly for first block', async () => { + it('appends messages unpadded and pads note hashes, nullifiers for first block', async () => { const isFirstBlock = true; const txsPerBlock = 2; const maxEffects = 1; @@ -95,7 +102,8 @@ describe('NativeWorldState', () => { const status = await ws.handleL2BlockAndMessages(block, messages); - expect(status.meta.messageTreeMeta.size).toBe(BigInt(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP)); + // Messages are appended unpadded at compact indices. + expect(status.meta.messageTreeMeta.size).toBe(BigInt(numMessages)); const expectedNoteHashCount = txsPerBlock * MAX_NOTE_HASHES_PER_TX; expect(status.meta.noteHashTreeMeta.size).toBe(BigInt(expectedNoteHashCount)); @@ -140,23 +148,143 @@ describe('NativeWorldState', () => { expect(status.meta.publicDataTreeMeta.size).toBe(BigInt(INITIAL_PUBLIC_DATA_TREE_SIZE + expectedPublicDataCount)); }); - it('pads empty messages array for first block', async () => { + it('leaves the message tree empty for a first block with no messages', async () => { const isFirstBlock = true; const numMessages = 0; const { block, messages } = await mockBlock(BlockNumber(1), 1, fork, 1, numMessages, isFirstBlock); const status = await ws.handleL2BlockAndMessages(block, messages); - expect(status.meta.messageTreeMeta.size).toBe(BigInt(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP)); + expect(status.meta.messageTreeMeta.size).toBe(0n); }); - it('throws error if messages are provided for non-first block', async () => { - const isFirstBlock = false; - const numMessages = 1; - const { block, messages } = await mockBlock(BlockNumber(1), 1, fork, 1, numMessages, isFirstBlock); - - await expect(ws.handleL2BlockAndMessages(block, messages)).rejects.toThrow( - 'L1 to L2 messages must be empty for non-first blocks', + it('appends a non-first block bundle without padding', async () => { + const numMessages = 3; + const { block, messages } = await mockBlockWithIndex( + BlockNumber(1), + /*indexWithinCheckpoint=*/ 1, + 1, + fork, + numMessages, + 1, ); + + const status = await ws.handleL2BlockAndMessages(block, messages); + + // Non-first blocks append their bundle exactly as given (no padding to NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP). + expect(status.meta.messageTreeMeta.size).toBe(BigInt(numMessages)); + }); + }); + + describe('Per-block message insertion', () => { + let ws: NativeWorldStateService; + + beforeEach(async () => { + ws = await NativeWorldStateService.tmp(); + }); + + afterEach(async () => { + await ws.close(); + }); + + it('advances the L1-to-L2 message tree per block, including on non-first blocks', async () => { + const fork = await ws.fork(); + + // Block 1 is first-in-checkpoint carrying 3 messages, appended unpadded at compact indices. + const { block: b1, messages: m1 } = await mockBlockWithIndex(BlockNumber(1), /*index=*/ 0, 1, fork, 3, 1); + const s1 = await ws.handleL2BlockAndMessages(b1, m1); + expect(s1.meta.messageTreeMeta.size).toBe(3n); + expect(s1.meta.messageTreeMeta.unfinalizedBlockHeight).toBe(1); + + // Block 2 is non-first and carries no messages: the message tree size and root are unchanged, but the tree is + // still committed as a new block (so its per-block history stays in lockstep with the other trees). + const { block: b2, messages: m2 } = await mockBlockWithIndex(BlockNumber(2), /*index=*/ 1, 1, fork, 0, 1); + const s2 = await ws.handleL2BlockAndMessages(b2, m2); + expect(s2.meta.messageTreeMeta.size).toBe(3n); + expect(s2.meta.messageTreeMeta.root).toEqual(s1.meta.messageTreeMeta.root); + expect(s2.meta.messageTreeMeta.unfinalizedBlockHeight).toBe(2); + + // Block 3 is non-first and carries messages: the bundle is appended unpadded, so the tree grows by exactly the + // bundle size and the root changes on a non-first block. + const { block: b3, messages: m3 } = await mockBlockWithIndex(BlockNumber(3), /*index=*/ 2, 1, fork, 5, 1); + const s3 = await ws.handleL2BlockAndMessages(b3, m3); + expect(s3.meta.messageTreeMeta.size).toBe(8n); + expect(s3.meta.messageTreeMeta.root).not.toEqual(s2.meta.messageTreeMeta.root); + expect(s3.meta.messageTreeMeta.unfinalizedBlockHeight).toBe(3); + + await fork.close(); + + // A fork opened at block 2 sees exactly the first two bundles (3 + 0); at block 3 it also sees the third. + const forkAt2 = await ws.fork(BlockNumber(2)); + expect((await forkAt2.getTreeInfo(MerkleTreeId.L1_TO_L2_MESSAGE_TREE)).size).toBe(3n); + await forkAt2.close(); + + const forkAt3 = await ws.fork(BlockNumber(3)); + expect((await forkAt3.getTreeInfo(MerkleTreeId.L1_TO_L2_MESSAGE_TREE)).size).toBe(8n); + await forkAt3.close(); + }); + + it('unwinds per block, reverting exactly the messages appended by a non-first block', async () => { + const fork = await ws.fork(); + + const { block: b1, messages: m1 } = await mockBlockWithIndex(BlockNumber(1), /*index=*/ 0, 1, fork, 2, 1); + const s1 = await ws.handleL2BlockAndMessages(b1, m1); + + // Non-first block carrying 4 messages. + const { block: b2, messages: m2 } = await mockBlockWithIndex(BlockNumber(2), /*index=*/ 1, 1, fork, 4, 1); + const s2 = await ws.handleL2BlockAndMessages(b2, m2); + expect(s2.meta.messageTreeMeta.size).toBe(6n); + + // Non-first block carrying 5 messages. + const { block: b3, messages: m3 } = await mockBlockWithIndex(BlockNumber(3), /*index=*/ 2, 1, fork, 5, 1); + const s3 = await ws.handleL2BlockAndMessages(b3, m3); + expect(s3.meta.messageTreeMeta.size).toBe(11n); + await fork.close(); + + // Unwind block 3: the message tree returns to the post-block-2 state. + const afterUnwind3 = await ws.unwindBlocks(BlockNumber(2)); + expect(afterUnwind3.meta.messageTreeMeta.size).toBe(s2.meta.messageTreeMeta.size); + expect(afterUnwind3.meta.messageTreeMeta.root).toEqual(s2.meta.messageTreeMeta.root); + + // Unwind block 2 (a message-carrying non-first block): its 4 messages are reverted, back to the post-block-1 + // state — the pending-chain rollback does not assume the message tree only changes at checkpoint starts. + const afterUnwind2 = await ws.unwindBlocks(BlockNumber(1)); + expect(afterUnwind2.meta.messageTreeMeta.size).toBe(s1.meta.messageTreeMeta.size); + expect(afterUnwind2.meta.messageTreeMeta.root).toEqual(s1.meta.messageTreeMeta.root); + + // Re-syncing after the unwind reconverges: fresh non-first blocks append cleanly on top of block 1. + const resyncFork = await ws.fork(); + const { block: b2b, messages: m2b } = await mockBlockWithIndex(BlockNumber(2), /*index=*/ 1, 1, resyncFork, 4, 1); + const s2b = await ws.handleL2BlockAndMessages(b2b, m2b); + expect(s2b.meta.messageTreeMeta.size).toBe(6n); + + const { block: b3b, messages: m3b } = await mockBlockWithIndex(BlockNumber(3), /*index=*/ 2, 1, resyncFork, 5, 1); + const s3b = await ws.handleL2BlockAndMessages(b3b, m3b); + expect(s3b.meta.messageTreeMeta.size).toBe(11n); + await resyncFork.close(); + }); + + it('leaves the message tree unchanged on every non-first block that carries no messages', async () => { + const fork = await ws.fork(); + + // A checkpoint whose messages are all consumed by its first block: non-first blocks carry an empty bundle, so the + // message tree must be unchanged (size and root) at every non-first block, not just at the checkpoint end. + const { block: b1, messages: m1 } = await mockBlockWithIndex(BlockNumber(1), /*index=*/ 0, 2, fork, 6, 2); + const s1 = await ws.handleL2BlockAndMessages(b1, m1); + expect(s1.meta.messageTreeMeta.size).toBe(6n); + + for (let index = 1; index <= 2; index++) { + const blockNumber = index + 1; + const { block, messages } = await mockBlockWithIndex(BlockNumber(blockNumber), index, 2, fork, 0, 2); + const status = await ws.handleL2BlockAndMessages(block, messages); + + // The message tree is untouched by non-first blocks in the legacy shape. + expect(status.meta.messageTreeMeta.size).toEqual(s1.meta.messageTreeMeta.size); + expect(status.meta.messageTreeMeta.root).toEqual(s1.meta.messageTreeMeta.root); + // But the chain as a whole still advances: the archive tree grows with each block. + expect(status.meta.archiveTreeMeta.unfinalizedBlockHeight).toBe(blockNumber); + } + + await fork.close(); }); }); diff --git a/yarn-project/world-state/src/native/native_world_state.ts b/yarn-project/world-state/src/native/native_world_state.ts index dff25d024f5d..432c70e566b2 100644 --- a/yarn-project/world-state/src/native/native_world_state.ts +++ b/yarn-project/world-state/src/native/native_world_state.ts @@ -1,4 +1,4 @@ -import { MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants'; +import { MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX } from '@aztec/constants'; import { BlockNumber } from '@aztec/foundation/branded-types'; import { fromEntries, padArrayEnd } from '@aztec/foundation/collection'; import { Fr } from '@aztec/foundation/curves/bn254'; @@ -264,19 +264,12 @@ export class NativeWorldStateService implements MerkleTreeDatabase { } public async handleL2BlockAndMessages(l2Block: L2Block, l1ToL2Messages: Fr[]): Promise { - const isFirstBlock = l2Block.indexWithinCheckpoint === 0; - if (!isFirstBlock && l1ToL2Messages.length > 0) { - throw new Error( - `L1 to L2 messages must be empty for non-first blocks, but got ${l1ToL2Messages.length} messages for block ${l2Block.number}.`, - ); - } - - // We have to pad the given l1 to l2 messages, and the note hashes and nullifiers within tx effects, because that's - // how the trees are built by circuits. - const paddedL1ToL2Messages = isFirstBlock - ? padArrayEnd(l1ToL2Messages, Fr.ZERO, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP) - : []; + // Any block may carry an L1-to-L2 message bundle and transition the L1-to-L2 message tree by its real (unpadded, + // compact) leaves, matching how the circuits build the tree. + const paddedL1ToL2Messages = l1ToL2Messages; + // We have to pad the note hashes and nullifiers within tx effects because that's how the trees are built by + // circuits. const paddedNoteHashes = l2Block.body.txEffects.flatMap(txEffect => padArrayEnd(txEffect.noteHashes, Fr.ZERO, MAX_NOTE_HASHES_PER_TX), ); diff --git a/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.test.ts b/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.test.ts index f0de3e58977b..1edcaf2bdb64 100644 --- a/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.test.ts +++ b/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.test.ts @@ -51,9 +51,6 @@ describe('ServerWorldStateSynchronizer', () => { beforeEach(() => { blockAndMessagesSource = mock(); blockAndMessagesSource.getBlockNumber.mockResolvedValue(BlockNumber(LATEST_BLOCK_NUMBER)); - blockAndMessagesSource.getL1ToL2Messages.mockImplementation(checkNumber => { - return Promise.resolve(checkpoints.find(c => c.checkpoint.number === checkNumber)?.messages ?? []); - }); merkleTreeRead = mock(); merkleTreeRead.getInitialHeader.mockReturnValue({ @@ -251,34 +248,6 @@ describe('ServerWorldStateSynchronizer', () => { await expect(pushBlocks(1, 5)).rejects.toThrow(/Test error/i); }); - it('fetches L1->L2 messages only for the first block in a checkpoint', async () => { - // Generate 3 mock checkpoints, each with i + 1 block and i + 2 message. - checkpoints = await timesParallel(3, i => - mockCheckpointAndMessages(CheckpointNumber(i + 1), { - startBlockNumber: BlockNumber( - Array(i + 1) - .fill(0) - .reduce((acc, _, index) => acc + index, 1), - ), - numBlocks: i + 1, - numL1ToL2Messages: i + 2, - }), - ); - - void server.start(); - await pushBlocks(1, 6); - - await expectServerStatus(WorldStateRunningState.RUNNING, 6); - - expect(merkleTreeDb.handleL2BlockAndMessages).toHaveBeenCalledTimes(6); - expect(merkleTreeDb.handleL2BlockAndMessages.mock.calls[0][1]).toEqual(checkpoints[0].messages); - expect(merkleTreeDb.handleL2BlockAndMessages.mock.calls[1][1]).toEqual(checkpoints[1].messages); - expect(merkleTreeDb.handleL2BlockAndMessages.mock.calls[2][1]).toEqual([]); - expect(merkleTreeDb.handleL2BlockAndMessages.mock.calls[3][1]).toEqual(checkpoints[2].messages); - expect(merkleTreeDb.handleL2BlockAndMessages.mock.calls[4][1]).toEqual([]); - expect(merkleTreeDb.handleL2BlockAndMessages.mock.calls[5][1]).toEqual([]); - }); - describe('getVerifiedSnapshot', () => { let snapshot: MockProxy; diff --git a/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.ts b/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.ts index fb14ea56c8d2..eefe75f2a976 100644 --- a/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.ts +++ b/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.ts @@ -1,3 +1,4 @@ +import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants'; import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types'; import type { Fr } from '@aztec/foundation/curves/bn254'; import { type Logger, createLogger } from '@aztec/foundation/log'; @@ -368,16 +369,25 @@ export class ServerWorldStateSynchronizer private async handleL2Blocks(l2Blocks: L2Block[]) { this.log.debug(`Handling L2 blocks ${l2Blocks[0].number} to ${l2Blocks.at(-1)!.number}`); - // Fetch the L1->L2 messages for the first block in a checkpoint. + // Derive each block's real L1-to-L2 message bundle from the compact leaf-index range it inserted: + // the messages between the parent block's L1-to-L2 tree leaf count and this block's, resolved via the + // Inbox buckets. Blocks in a batch are consecutive, so we track the running leaf count. const messagesForBlocks = new Map(); - await Promise.all( - l2Blocks - .filter(b => b.indexWithinCheckpoint === 0) - .map(async block => { - const l1ToL2Messages = await this.l2BlockSource.getL1ToL2Messages(block.checkpointNumber); - messagesForBlocks.set(block.number, l1ToL2Messages); - }), - ); + let prevLeafCount = await this.getL1ToL2LeafCountBefore(l2Blocks[0].number); + for (const block of l2Blocks) { + const blockLeafCount = BigInt(block.header.state.l1ToL2MessageTree.nextAvailableLeafIndex); + if (blockLeafCount > prevLeafCount) { + const startBucket = await this.l2BlockSource.getInboxBucketByTotalMsgCount(prevLeafCount); + const endBucket = await this.l2BlockSource.getInboxBucketByTotalMsgCount(blockLeafCount); + if (startBucket !== undefined && endBucket !== undefined) { + messagesForBlocks.set( + block.number, + await this.l2BlockSource.getL1ToL2MessagesBetweenBuckets(startBucket.seq, endBucket.seq), + ); + } + } + prevLeafCount = blockLeafCount; + } let updateStatus: WorldStateStatusFull | undefined = undefined; for (const block of l2Blocks) { @@ -400,13 +410,23 @@ export class ServerWorldStateSynchronizer this.instrumentation.updateWorldStateMetrics(updateStatus); } + /** The L1-to-L2 message tree leaf count as of the block before `blockNumber` (0 if that block is genesis). */ + private async getL1ToL2LeafCountBefore(blockNumber: BlockNumber): Promise { + const parentNumber = blockNumber - 1; + if (parentNumber < INITIAL_L2_BLOCK_NUM) { + return 0n; + } + const parentBlock = await this.l2BlockSource.getBlockData({ number: BlockNumber(parentNumber) }); + return parentBlock === undefined ? 0n : BigInt(parentBlock.header.state.l1ToL2MessageTree.nextAvailableLeafIndex); + } + /** * Handles a single L2 block (i.e. Inserts the new note hashes into the merkle tree). * @param l2Block - The L2 block to handle. * @param l1ToL2Messages - The L1 to L2 messages for the block. * @returns Whether the block handled was produced by this same node. */ - private async handleL2Block(l2Block: L2Block, l1ToL2Messages: Fr[]): Promise { + protected async handleL2Block(l2Block: L2Block, l1ToL2Messages: Fr[]): Promise { this.log.debug(`Pushing L2 block ${l2Block.number} to merkle tree db `, { blockNumber: l2Block.number, blockHash: await l2Block.hash().then(h => h.toString()), diff --git a/yarn-project/world-state/src/test/utils.ts b/yarn-project/world-state/src/test/utils.ts index 1364155ec208..8870091b3951 100644 --- a/yarn-project/world-state/src/test/utils.ts +++ b/yarn-project/world-state/src/test/utils.ts @@ -50,13 +50,9 @@ export async function updateBlockState(block: L2Block, l1ToL2Messages: Fr[], for padArrayEnd(txEffect.noteHashes, Fr.ZERO, MAX_NOTE_HASHES_PER_TX), ); - const l1ToL2MessagesPadded = - block.indexWithinCheckpoint === 0 - ? padArrayEnd(l1ToL2Messages, Fr.ZERO, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP) - : l1ToL2Messages; - + // Every block appends its real message leaves unpadded at compact indices. const noteHashInsert = fork.appendLeaves(MerkleTreeId.NOTE_HASH_TREE, noteHashesPadded); - const messageInsert = fork.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2MessagesPadded); + const messageInsert = fork.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2Messages); await Promise.all([publicDataInsert, nullifierInsert, noteHashInsert, messageInsert]); const state = await fork.getStateReference(); @@ -77,16 +73,32 @@ export async function updateBlockState(block: L2Block, l1ToL2Messages: Fr[], for block.archive = new AppendOnlyTreeSnapshot(Fr.fromBuffer(archiveState.root), Number(archiveState.size)); } -export async function mockBlock( +export function mockBlock( blockNum: BlockNumber, size: number, fork: MerkleTreeWriteOperations, maxEffects: number | undefined = 1000, // Defaults to the maximum tx effects. numL1ToL2Messages: number = NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, isFirstBlockInCheckpoint: boolean = true, +) { + return mockBlockWithIndex(blockNum, isFirstBlockInCheckpoint ? 0 : 1, size, fork, numL1ToL2Messages, maxEffects); +} + +/** + * Builds a mock L2 block at an explicit position within its checkpoint, applying its state (including its L1-to-L2 + * message bundle) to the given fork. Unlike {@link mockBlock}, the caller chooses the `indexWithinCheckpoint`, so + * non-first blocks can carry message bundles — exercising the per-block message insertion path. + */ +export async function mockBlockWithIndex( + blockNum: BlockNumber, + indexWithinCheckpoint: number, + size: number, + fork: MerkleTreeWriteOperations, + numL1ToL2Messages: number = NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, + maxEffects: number | undefined = 1000, // Defaults to the maximum tx effects. ) { const block = await L2Block.random(blockNum, { - indexWithinCheckpoint: isFirstBlockInCheckpoint ? IndexWithinCheckpoint(0) : IndexWithinCheckpoint(1), + indexWithinCheckpoint: IndexWithinCheckpoint(indexWithinCheckpoint), txsPerBlock: size, txOptions: { maxEffects }, }); diff --git a/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts b/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts index 86ff3ee57655..1dabb0aacd30 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts @@ -31,7 +31,11 @@ export const INITIAL_PUBLIC_DATA_TREE_SIZE = 2 * MAX_TOTAL_PUBLIC_DATA_UPDATE_RE export interface MerkleTreeAdminDatabase extends ForkMerkleTreeOperations, ReadonlyWorldStateAccess { /** - * Handles a single L2 block (i.e. Inserts the new note hashes into the merkle tree). + * Handles a single L2 block: inserts its note hashes, nullifiers, public data writes, and the block's L1-to-L2 + * message bundle into the merkle trees. Any block may carry a message bundle and transition the L1-to-L2 message + * tree, not just the first block of a checkpoint. A first-in-checkpoint bundle is padded to + * NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP to match how the circuits build the tree; a non-first bundle is appended + * exactly as given. Padding is a transitional concern of this method that moves entirely to the caller at the flip. * @param block - The L2 block to handle. * @param l1ToL2Messages - The L1 to L2 messages for the block. */