diff --git a/docs/docs-developers/docs/foundational-topics/ethereum-aztec-messaging/inbox.md b/docs/docs-developers/docs/foundational-topics/ethereum-aztec-messaging/inbox.md index d3d818b629c5..0341b4610439 100644 --- a/docs/docs-developers/docs/foundational-topics/ethereum-aztec-messaging/inbox.md +++ b/docs/docs-developers/docs/foundational-topics/ethereum-aztec-messaging/inbox.md @@ -29,14 +29,30 @@ Sends a message from L1 to L2. - Will revert with `Inbox__ContentTooLarge(bytes32 content)` if the content is larger than the field size (~254 bits). - Will revert with `Inbox__SecretHashTooLarge(bytes32 secretHash)` if the secret hash is larger than the field size (~254 bits). +## Buckets and the rolling hash + +Every message inserted into the Inbox extends a rolling hash: a truncated sha256 chain over the message leaves, which +the rollup circuits recompute and L1 checks when a checkpoint is proposed. Messages are grouped into **buckets**: a +bucket holds the messages sent within a single L1 block (up to a per-bucket maximum, after which further messages in +the same block spill into the next bucket), and buckets are identified by a dense, monotonically increasing sequence +number. A checkpoint always consumes whole buckets. + +Each link of the chain is `sha256ToField(separator || previousRollingHash || leaf)` over a 4-byte big-endian domain +separator followed by the two 32-byte values. There are two separators: `DOM_SEP__INBOX_ROLLING_HASH_BUCKET_START` is +used when the leaf is the first message of its bucket, and `DOM_SEP__INBOX_ROLLING_HASH` for every other message. The +chain therefore commits to how the messages were packed into buckets, not only to their order: the same messages +regrouped across a different set of L1 blocks produce a different rolling hash. The genesis value is zero. + ## View functions These functions allow you to query the current state of the Inbox. | Function | Returns | Description | | -------------------------- | ----------------- | ------------------------------------------------ | -| `getState()` | `InboxState` | Returns the current inbox state (rolling hash, total messages inserted). | +| `getState()` | `InboxState` | Returns the current inbox state (rolling hash, total messages inserted, current bucket sequence). | | `getTotalMessagesInserted()` | `uint64` | Returns the total number of messages inserted into the inbox. | +| `getCurrentBucketSeq()` | `uint64` | Returns the sequence number of the bucket messages are currently absorbed into. | +| `getBucket(uint256 seq)` | `InboxBucket` | Returns the snapshot of the bucket with the given sequence number (rolling hash, cumulative and per-bucket message counts, opening timestamp). Reverts if the bucket is outside the ring the Inbox retains. | | `getFeeAssetPortal()` | `address` | Returns the address of the Fee Juice portal. | ## Related pages diff --git a/l1-contracts/gas_report.json b/l1-contracts/gas_report.json index 7876ade81881..4bd81aedfe9c 100644 --- a/l1-contracts/gas_report.json +++ b/l1-contracts/gas_report.json @@ -3,7 +3,7 @@ "contract": "src/core/messagebridge/Inbox.sol:Inbox", "deployment": { "gas": 0, - "size": 6805 + "size": 6859 }, "functions": { "getBucket(uint256)": { @@ -43,10 +43,10 @@ }, "sendL2Message((bytes32,uint256),bytes32,bytes32)": { "calls": 37409, - "min": 43269, - "mean": 46588, - "median": 43269, - "max": 102063 + "min": 43360, + "mean": 46678, + "median": 43360, + "max": 102144 } } }, @@ -132,7 +132,7 @@ "contract": "test/RollupWithPreheating.sol:RollupWithPreheating", "deployment": { "gas": 0, - "size": 43818 + "size": 43872 }, "functions": { "archive()": { diff --git a/l1-contracts/scripts/constants-codegen/solidity.json b/l1-contracts/scripts/constants-codegen/solidity.json index 67650fce4f17..55500fee5943 100644 --- a/l1-contracts/scripts/constants-codegen/solidity.json +++ b/l1-contracts/scripts/constants-codegen/solidity.json @@ -10,5 +10,6 @@ "FEE_JUICE_ADDRESS", "BLS12_POINT_COMPRESSED_BYTES", "ROOT_ROLLUP_PUBLIC_INPUTS_LENGTH", - "DOM_SEP__INBOX_ROLLING_HASH" + "DOM_SEP__INBOX_ROLLING_HASH", + "DOM_SEP__INBOX_ROLLING_HASH_BUCKET_START" ] diff --git a/l1-contracts/scripts/inbox_rolling_hash_vectors.py b/l1-contracts/scripts/inbox_rolling_hash_vectors.py new file mode 100644 index 000000000000..45be85e901c3 --- /dev/null +++ b/l1-contracts/scripts/inbox_rolling_hash_vectors.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +"""Derives the Inbox rolling-hash reference vectors pinned by the L1, Noir and TypeScript tests. + +The rolling hash is a sha256 chain over the Inbox message leaves. Each link is + + h' = sha256ToField(u32_be(separator) || h(32) || leaf(32)) + +where `sha256ToField(x) = 0x00 || sha256(x)[0..31]` (the last digest byte is dropped so the result fits a field), and +the separator is INBOX_ROLLING_HASH_BUCKET_START when the leaf is the first message of an L1 Inbox bucket and +INBOX_ROLLING_HASH otherwise. The genesis rolling hash is zero. + +This script depends on nothing but hashlib, so the vectors it prints are independent of all three implementations. +Run it and paste the values into: + + - l1-contracts/test/InboxBuckets.t.sol (testRollingHashTestVectors) + - noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/inbox_rolling_hash.nr (tests module) + - yarn-project/stdlib/src/messaging/inbox_rolling_hash.test.ts +""" + +import hashlib + +# Domain separators, mirroring DOM_SEP__INBOX_ROLLING_HASH and DOM_SEP__INBOX_ROLLING_HASH_BUCKET_START in +# noir-projects/fnd/noir-protocol-circuits/crates/types/src/constants.nr. They are poseidon2 hashes of a name, so +# their derivation is checked by the Noir constants test rather than here. +LINK = 3737216265 +BUCKET_START = 3204844280 + + +def sha256_to_field(preimage: bytes) -> int: + return int.from_bytes(b"\x00" + hashlib.sha256(preimage).digest()[:31], "big") + + +def link(prev: int, leaf: int, opens_bucket: bool) -> int: + separator = BUCKET_START if opens_bucket else LINK + preimage = separator.to_bytes(4, "big") + prev.to_bytes(32, "big") + leaf.to_bytes(32, "big") + return sha256_to_field(preimage) + + +def chain(start: int, buckets: list[list[int]]) -> int: + """Chains message leaves grouped per Inbox bucket; the first leaf of each group opens a bucket.""" + acc = start + for bucket in buckets: + assert bucket, "an Inbox bucket always holds at least one message" + for i, leaf in enumerate(bucket): + acc = link(acc, leaf, i == 0) + return acc + + +def main() -> None: + vectors = [ + ("single leaf: chain(0, [[11]])", chain(0, [[11]])), + ("three leaves in one bucket: chain(0, [[11, 22, 33]])", chain(0, [[11, 22, 33]])), + ("256 leaves 1..=256 in one bucket", chain(0, [list(range(1, 257))])), + ("non-zero start, one leaf: chain(0x2a, [[7]])", chain(0x2A, [[7]])), + ("non-zero start, two leaves in one bucket: chain(0x2a, [[7, 8]])", chain(0x2A, [[7, 8]])), + ("one bucket: chain(0, [[11, 22, 33, 44]])", chain(0, [[11, 22, 33, 44]])), + ("two buckets: chain(0, [[11, 22], [33, 44]])", chain(0, [[11, 22], [33, 44]])), + ] + for name, value in vectors: + print(f"{value:#066x} {name}") + + # The pair above is the boundary-commitment vector: identical leaves, different bucket grouping. + assert chain(0, [[11, 22, 33, 44]]) != chain(0, [[11, 22], [33, 44]]) + # Continuity: a chain split into segments threads the intermediate hash, flags following the leaves. + assert chain(0x2A, [[7, 8]]) == link(chain(0x2A, [[7]]), 8, False) + + +if __name__ == "__main__": + main() diff --git a/l1-contracts/src/core/interfaces/messagebridge/IInbox.sol b/l1-contracts/src/core/interfaces/messagebridge/IInbox.sol index cbecaf0e5b37..41c41b18c2aa 100644 --- a/l1-contracts/src/core/interfaces/messagebridge/IInbox.sol +++ b/l1-contracts/src/core/interfaces/messagebridge/IInbox.sol @@ -36,8 +36,10 @@ interface IInbox { */ struct InboxBucket { // Rolling hash after the last message absorbed into this bucket. Each link is - // `sha256ToField(DOM_SEP__INBOX_ROLLING_HASH || previousRollingHash || leaf)`, over the 4-byte big-endian domain - // separator followed by the two 32-byte big-endian values; the genesis value is zero. + // `sha256ToField(separator || previousRollingHash || leaf)`, over the 4-byte big-endian domain separator followed + // by the two 32-byte big-endian values; the separator is `DOM_SEP__INBOX_ROLLING_HASH_BUCKET_START` for the first + // message of a bucket and `DOM_SEP__INBOX_ROLLING_HASH` for the rest, so the chain commits to the bucket + // boundaries. The genesis value is zero. bytes32 rollingHash; // Cumulative number of messages inserted into the Inbox up to and including this bucket. uint64 totalMsgCount; diff --git a/l1-contracts/src/core/libraries/crypto/Hash.sol b/l1-contracts/src/core/libraries/crypto/Hash.sol index 10ae9d286eb2..d5d2adaa9303 100644 --- a/l1-contracts/src/core/libraries/crypto/Hash.sol +++ b/l1-contracts/src/core/libraries/crypto/Hash.sol @@ -53,16 +53,26 @@ library Hash { /** * @notice Advances the Inbox consensus rolling hash by one message leaf - * @dev Each link is `sha256ToField(DOM_SEP__INBOX_ROLLING_HASH || rollingHash || leaf)` over the 4-byte big-endian - * domain separator followed by the two 32-byte big-endian values. The separator keeps a chain link from being - * reinterpreted as an untagged two-field sha256 hash, such as an `outHash` merkle node. Truncated at every link so - * the value is always a field element; the rollup circuits recompute the identical chain over the message leaves - * they insert. The genesis value is zero. + * @dev Each link is `sha256ToField(separator || rollingHash || leaf)` over the 4-byte big-endian domain separator + * followed by the two 32-byte big-endian values. The separator is `DOM_SEP__INBOX_ROLLING_HASH_BUCKET_START` when + * the leaf is the first message of a bucket and `DOM_SEP__INBOX_ROLLING_HASH` otherwise, so the chain commits to + * how the messages were packed into buckets and not just to their order. Both separators keep a chain link from + * being reinterpreted as an untagged two-field sha256 hash, such as an `outHash` merkle node. Truncated at every + * link so the value is always a field element; the rollup circuits recompute the identical chain over the message + * leaves they insert. The genesis value is zero. * @param _rollingHash - The current rolling hash * @param _leaf - The message leaf to absorb + * @param _opensBucket - Whether the leaf is the first message of its bucket * @return The updated rolling hash */ - function accumulateInboxRollingHash(bytes32 _rollingHash, bytes32 _leaf) internal pure returns (bytes32) { - return sha256ToField(abi.encodePacked(uint32(Constants.DOM_SEP__INBOX_ROLLING_HASH), _rollingHash, _leaf)); + function accumulateInboxRollingHash(bytes32 _rollingHash, bytes32 _leaf, bool _opensBucket) + internal + pure + returns (bytes32) + { + uint32 separator = _opensBucket + ? uint32(Constants.DOM_SEP__INBOX_ROLLING_HASH_BUCKET_START) + : uint32(Constants.DOM_SEP__INBOX_ROLLING_HASH); + return sha256ToField(abi.encodePacked(separator, _rollingHash, _leaf)); } } diff --git a/l1-contracts/src/core/messagebridge/Inbox.sol b/l1-contracts/src/core/messagebridge/Inbox.sol index 21cf892c42b5..bb253ab21b26 100644 --- a/l1-contracts/src/core/messagebridge/Inbox.sol +++ b/l1-contracts/src/core/messagebridge/Inbox.sol @@ -187,6 +187,9 @@ contract Inbox is IInbox { * open reverts unless the proven chain has consumed that entry, so in-flight messages are never destroyed — * sends halt instead until proving catches up. * + * The first message of a bucket is tagged with its own domain separator in the rolling hash, so the chain + * commits to the bucket boundaries and not just to the message order. + * * @param _leaf - The message leaf to absorb * * @return The sequence number of the bucket the leaf was absorbed into and the updated rolling hash @@ -217,7 +220,7 @@ contract Inbox is IInbox { }); } - bucket.rollingHash = Hash.accumulateInboxRollingHash(bucket.rollingHash, _leaf); + bucket.rollingHash = Hash.accumulateInboxRollingHash(bucket.rollingHash, _leaf, bucket.msgCount == 0); bucket.totalMsgCount += 1; bucket.msgCount += 1; buckets[bucketSeq % BUCKET_RING_SIZE] = bucket; diff --git a/l1-contracts/test/Inbox.t.sol b/l1-contracts/test/Inbox.t.sol index c97a4399f138..0dc4a11f8839 100644 --- a/l1-contracts/test/Inbox.t.sol +++ b/l1-contracts/test/Inbox.t.sol @@ -66,7 +66,7 @@ contract InboxTest is Test { DataStructures.L1ToL2Msg memory message = _boundMessage(_message, globalLeafIndex); bytes32 leaf = message.sha256ToField(); - bytes32 expectedInboxRollingHash = Hash.accumulateInboxRollingHash(bytes32(0), leaf); + bytes32 expectedInboxRollingHash = Hash.accumulateInboxRollingHash(bytes32(0), leaf, true); vm.expectEmit(true, true, true, true); // event we expect emit IInbox.MessageSent(leaf, expectedInboxRollingHash, 1, message); diff --git a/l1-contracts/test/InboxBuckets.t.sol b/l1-contracts/test/InboxBuckets.t.sol index bad3cd4e59c5..8fd81481be72 100644 --- a/l1-contracts/test/InboxBuckets.t.sol +++ b/l1-contracts/test/InboxBuckets.t.sol @@ -28,12 +28,16 @@ contract InboxBucketsTest is Test { } function _send(InboxHarness _inbox, uint256 _salt) internal returns (bytes32) { + uint64 seqBefore = _inbox.getCurrentBucketSeq(); (bytes32 leaf,) = _inbox.sendL2Message( DataStructures.L2Actor({actor: bytes32(uint256(0x1000 + _salt)), version: version}), bytes32(uint256(0x2000 + _salt)), bytes32(uint256(0x3000 + _salt)) ); - expectedRollingHash = Hash.accumulateInboxRollingHash(expectedRollingHash, leaf); + // A message opens a bucket exactly when it advances the bucket sequence: the first message of an L1 block, or + // the message that spills over out of a full bucket. + bool opensBucket = _inbox.getCurrentBucketSeq() != seqBefore; + expectedRollingHash = Hash.accumulateInboxRollingHash(expectedRollingHash, leaf, opensBucket); return leaf; } @@ -52,27 +56,45 @@ contract InboxBucketsTest is Test { gasUsed = gasBefore - gasleft(); } - // Shared test vectors for the rolling-hash chain, pinned across the noir circuits, the TS mirror, - // and this L1 implementation. Generated from an independent sha256 implementation. + // Shared test vectors for the rolling-hash chain, pinned across the noir circuits, the TS mirror, and this L1 + // implementation. Derived independently of all three by `scripts/inbox_rolling_hash_vectors.py`. Leaves are + // grouped per bucket: the first leaf of each group opens a bucket. function testRollingHashTestVectors() public pure { - bytes32 h = Hash.accumulateInboxRollingHash(bytes32(0), bytes32(uint256(11))); - assertEq(h, 0x00066dfa22681f66d50aae7d84f190e3555d2d82e4a5e33c2291c3060d441f04, "chain(0, [11])"); + bytes32 h = Hash.accumulateInboxRollingHash(bytes32(0), bytes32(uint256(11)), true); + assertEq(h, 0x00551b59fed79dcce036e55050cf38ef367abfec03557e234866ac023879b245, "chain(0, [[11]])"); - h = Hash.accumulateInboxRollingHash(h, bytes32(uint256(22))); - h = Hash.accumulateInboxRollingHash(h, bytes32(uint256(33))); - assertEq(h, 0x0077423b713a725ce4bf0b792847c68da87c316d52921de25652756bfe4c3e81, "chain(0, [11, 22, 33])"); + h = Hash.accumulateInboxRollingHash(h, bytes32(uint256(22)), false); + h = Hash.accumulateInboxRollingHash(h, bytes32(uint256(33)), false); + assertEq(h, 0x00e6cba8a055d279f8568edc4d0969a107fcda0c48347afdfd3dfeb053aa22c7, "chain(0, [[11, 22, 33]])"); h = bytes32(0); for (uint256 i = 1; i <= 256; i++) { - h = Hash.accumulateInboxRollingHash(h, bytes32(i)); + h = Hash.accumulateInboxRollingHash(h, bytes32(i), i == 1); } - assertEq(h, 0x0030493fcb5915459bba42f03f283b58dfaa082dac02fbb3a494d5db8063238b, "chain(0, [1..=256])"); + assertEq(h, 0x009ff152cad9525e1c092ae6d4fb390149de5599eac09b76b0ebd1c6e26bb504, "chain(0, [[1..=256]])"); - h = Hash.accumulateInboxRollingHash(bytes32(uint256(0x2a)), bytes32(uint256(7))); - assertEq(h, 0x0048097cafad7fed00ccb578806b3855d5ee7bf11045fb8d41b2880ba36ef28f, "chain(0x2a, [7])"); + h = Hash.accumulateInboxRollingHash(bytes32(uint256(0x2a)), bytes32(uint256(7)), true); + assertEq(h, 0x00f13cb848052a7ab6f1de788a5979f5a5caa8c11cf176715d63481618e3b575, "chain(0x2a, [[7]])"); - h = Hash.accumulateInboxRollingHash(h, bytes32(uint256(8))); - assertEq(h, 0x00a64d14c4b0234f5d835dc202bf8f9a857bc0734baf281dccd4b4978a48b2f9, "chain(0x2a, [7, 8])"); + h = Hash.accumulateInboxRollingHash(h, bytes32(uint256(8)), false); + assertEq(h, 0x00d84d0b60599b1c7380a723d84310d40efaa4f5673dd62e0af41b03bc9a07a6, "chain(0x2a, [[7, 8]])"); + + // The same four leaves in one bucket and split across two buckets reach different chain positions. + h = bytes32(0); + uint256[4] memory leaves = [uint256(11), 22, 33, 44]; + for (uint256 i = 0; i < 4; i++) { + h = Hash.accumulateInboxRollingHash(h, bytes32(leaves[i]), i == 0); + } + assertEq(h, 0x00e37b7cc5526ab379c54209bc1c6a4ba2c457d024330281b97a533561701551, "chain(0, [[11, 22, 33, 44]])"); + + bytes32 split = bytes32(0); + for (uint256 i = 0; i < 4; i++) { + split = Hash.accumulateInboxRollingHash(split, bytes32(leaves[i]), i == 0 || i == 2); + } + assertEq( + split, 0x00fa0346e7c4ee1bdf29a48af28182fdc236e2936e4d0c2e951dbd4b9b6464fc, "chain(0, [[11, 22], [33, 44]])" + ); + assertTrue(h != split, "bucket boundaries change the chain"); } function testGenesisBucket() public { @@ -101,12 +123,13 @@ contract InboxBucketsTest is Test { } function testAccumulationWithinSingleBlock() public { + // Only the first message of the block opens a bucket; the rest continue it. bytes32 leaf1 = _send(inbox, 1); - bytes32 chain1 = Hash.accumulateInboxRollingHash(bytes32(0), leaf1); + bytes32 chain1 = Hash.accumulateInboxRollingHash(bytes32(0), leaf1, true); bytes32 leaf2 = _send(inbox, 2); - bytes32 chain2 = Hash.accumulateInboxRollingHash(chain1, leaf2); + bytes32 chain2 = Hash.accumulateInboxRollingHash(chain1, leaf2, false); bytes32 leaf3 = _send(inbox, 3); - bytes32 chain3 = Hash.accumulateInboxRollingHash(chain2, leaf3); + bytes32 chain3 = Hash.accumulateInboxRollingHash(chain2, leaf3, false); assertEq(inbox.getCurrentBucketSeq(), 1, "all messages share one bucket"); @@ -117,6 +140,25 @@ contract InboxBucketsTest is Test { assertEq(bucket.msgCount, 3, "bucket msg count"); } + function testBucketBoundariesChangeTheChain() public { + // Two messages sent in one L1 block share a bucket; the same two messages one L1 block apart open two buckets. + // The leaves are identical either way, so only the bucket-start separator tells the two histories apart. + InboxHarness oneBucket = _deployInbox(TestConstants.AZTEC_INBOX_BUCKET_RING_SIZE); + bytes32 leafA = _send(oneBucket, 1); + bytes32 leafB = _send(oneBucket, 2); + assertEq(oneBucket.getCurrentBucketSeq(), 1, "both messages in one bucket"); + bytes32 oneBucketHash = oneBucket.getState().rollingHash; + + InboxHarness twoBuckets = _deployInbox(TestConstants.AZTEC_INBOX_BUCKET_RING_SIZE); + assertEq(_send(twoBuckets, 1), leafA, "same first leaf"); + vm.roll(block.number + 1); + vm.warp(block.timestamp + 12); + assertEq(_send(twoBuckets, 2), leafB, "same second leaf"); + assertEq(twoBuckets.getCurrentBucketSeq(), 2, "one bucket per block"); + + assertTrue(oneBucketHash != twoBuckets.getState().rollingHash, "packing is committed to"); + } + function testStateReturnsCurrentPositionAtomically() public { // Genesis: nothing inserted, bucket 0 current, zero rolling hash. IInbox.InboxState memory state = inbox.getState(); @@ -161,7 +203,7 @@ contract InboxBucketsTest is Test { index: inbox.getState().totalMessagesInserted }); bytes32 leaf = Hash.sha256ToField(message); - bytes32 inboxRollingHash = Hash.accumulateInboxRollingHash(bytes32(0), leaf); + bytes32 inboxRollingHash = Hash.accumulateInboxRollingHash(bytes32(0), leaf, true); vm.expectEmit(true, true, true, true, address(inbox)); emit IInbox.MessageSent(leaf, inboxRollingHash, 1, message); @@ -189,7 +231,7 @@ contract InboxBucketsTest is Test { // The new bucket continues the chain from the previous bucket. IInbox.InboxBucket memory bucket2 = inbox.getBucket(2); - assertEq(bucket2.rollingHash, Hash.accumulateInboxRollingHash(bucket1.rollingHash, leaf3), "chain continuity"); + assertEq(bucket2.rollingHash, Hash.accumulateInboxRollingHash(bucket1.rollingHash, leaf3, true), "chain continuity"); assertEq(bucket2.rollingHash, expectedRollingHash, "chain matches reference"); assertEq(bucket2.totalMsgCount, 3, "cumulative total spans buckets"); assertEq(bucket2.timestamp, uint64(block.timestamp), "bucket 2 timestamp"); @@ -210,7 +252,7 @@ contract InboxBucketsTest is Test { assertEq(inbox.getCurrentBucketSeq(), 2, "rollover opened next bucket"); IInbox.InboxBucket memory bucket2 = inbox.getBucket(2); - assertEq(bucket2.rollingHash, Hash.accumulateInboxRollingHash(bucket1.rollingHash, leaf), "chain continuity"); + assertEq(bucket2.rollingHash, Hash.accumulateInboxRollingHash(bucket1.rollingHash, leaf, true), "chain continuity"); assertEq(bucket2.totalMsgCount, cap + 1, "cumulative total"); assertEq(bucket2.timestamp, bucket1.timestamp, "same block, same timestamp"); assertEq(bucket2.msgCount, 1, "spilled message only"); diff --git a/l1-contracts/test/InboxBucketsFuzz.t.sol b/l1-contracts/test/InboxBucketsFuzz.t.sol index 7378a1a8e6c2..2de06209b431 100644 --- a/l1-contracts/test/InboxBucketsFuzz.t.sol +++ b/l1-contracts/test/InboxBucketsFuzz.t.sol @@ -150,7 +150,8 @@ contract InboxBucketsFuzzTest is Test { assertLe(bucket.msgCount, cap, "per-bucket cap"); for (uint256 i = 0; i < bucket.msgCount; i++) { - rollingHash = Hash.accumulateInboxRollingHash(rollingHash, leaves[counted + i]); + // The first message of each bucket opens it and so takes the bucket-start separator. + rollingHash = Hash.accumulateInboxRollingHash(rollingHash, leaves[counted + i], i == 0); } counted += bucket.msgCount; diff --git a/l1-contracts/test/InboxOverwriteProtection.t.sol b/l1-contracts/test/InboxOverwriteProtection.t.sol index 1e262bb308fd..27b469ca28af 100644 --- a/l1-contracts/test/InboxOverwriteProtection.t.sol +++ b/l1-contracts/test/InboxOverwriteProtection.t.sol @@ -168,7 +168,9 @@ contract InboxOverwriteProtectionTest is Test { (bytes32 leaf, uint256 index) = _send(inbox, 1234); IInbox.InboxBucket memory opened = inbox.getBucket(RING_SIZE + 1); - assertEq(opened.rollingHash, Hash.accumulateInboxRollingHash(headHash, leaf), "chain continues from the ring head"); + assertEq( + opened.rollingHash, Hash.accumulateInboxRollingHash(headHash, leaf, true), "chain continues from the ring head" + ); assertEq(index, totalBefore, "the failed send consumed no index"); assertEq(opened.totalMsgCount, totalBefore + 1, "cumulative total advanced by one"); } diff --git a/l1-contracts/test/fee_portal/depositToAztecPublic.t.sol b/l1-contracts/test/fee_portal/depositToAztecPublic.t.sol index 318ab3135e86..f1136680efaf 100644 --- a/l1-contracts/test/fee_portal/depositToAztecPublic.t.sol +++ b/l1-contracts/test/fee_portal/depositToAztecPublic.t.sol @@ -86,7 +86,7 @@ contract DepositToAztecPublic is Test { assertEq(inbox.getTotalMessagesInserted(), 0); - bytes32 expectedInboxRollingHash = Hash.accumulateInboxRollingHash(bytes32(0), expectedKey); + bytes32 expectedInboxRollingHash = Hash.accumulateInboxRollingHash(bytes32(0), expectedKey, true); vm.expectEmit(true, true, true, true, address(inbox)); emit IInbox.MessageSent(expectedKey, expectedInboxRollingHash, 1, message); vm.expectEmit(true, true, true, true, address(feeJuicePortal)); diff --git a/l1-contracts/test/portals/TokenPortal.t.sol b/l1-contracts/test/portals/TokenPortal.t.sol index ffbb8789bb0b..62b6a0638128 100644 --- a/l1-contracts/test/portals/TokenPortal.t.sol +++ b/l1-contracts/test/portals/TokenPortal.t.sol @@ -120,7 +120,7 @@ contract TokenPortalTest is Test { DataStructures.L1ToL2Msg memory expectedMessage = _createExpectedMintPrivateL1ToL2Message(expectedIndex); bytes32 expectedLeaf = expectedMessage.sha256ToField(); - bytes32 expectedInboxRollingHash = Hash.accumulateInboxRollingHash(bytes32(0), expectedLeaf); + bytes32 expectedInboxRollingHash = Hash.accumulateInboxRollingHash(bytes32(0), expectedLeaf, true); // Check the event was emitted vm.expectEmit(true, true, true, true); // event we expect @@ -147,7 +147,7 @@ contract TokenPortalTest is Test { uint256 expectedIndex = 0; DataStructures.L1ToL2Msg memory expectedMessage = _createExpectedMintPublicL1ToL2Message(expectedIndex); bytes32 expectedLeaf = expectedMessage.sha256ToField(); - bytes32 expectedInboxRollingHash = Hash.accumulateInboxRollingHash(bytes32(0), expectedLeaf); + bytes32 expectedInboxRollingHash = Hash.accumulateInboxRollingHash(bytes32(0), expectedLeaf, true); // Check the event was emitted vm.expectEmit(true, true, true, true); 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 d176255fd361..59c714e37bd8 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 = "0x2f5269d82d387b9da55f7c6aa9559bcdad03152cb9297c727d0ef0b7386d0a75" + block_headers_hash = "0x19c62c5f2113cf3a1882c7231ae1704814ce26f875b6d370c560b8c10fc0f146" 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 = "0x17f4111918fd7fd073ef3c7b0685a0b0f9de9c06978162bfb3284f1949143cfb" + vk_tree_root = "0x29b7bb0a371549fbde9a7c1967f9368c8b78f8d537b1c0a47626757643283c1d" protocol_contracts_hash = "0x14cec0ae6f31e913c3be04691c6d49c3ec07b3f2d024c9558693ea62fc530bbf" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" slot_number = "0x000000000000000000000000000000000000000000000000000000000000000f" @@ -512,7 +512,7 @@ proof = [ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000001" [inputs.previous_rollups.public_inputs.new_archive] - root = "0x1e97c93cb4174d45a8a02061726c03e9a1443d5dc3c72e92307f766f85718e22" + root = "0x0f9544cbd0e96c102033eb1b7e3767a4ab5d0fdfcd8453af3e55f1517e871f7c" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000002" [inputs.previous_rollups.public_inputs.start_state.l1_to_l2_message_tree] @@ -575,10 +575,10 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x0c123047b777079e8db5f669ddaf1ffa1c7780d56b21e29f35e4772ac0f6280c" ] state = [ - "0x26d2ddc07ff22beab8288c1c0b9d5b186bcef21b51fd6c36aac0c08a85b02a3d", - "0x0b5c879e38b476648f27959441fd207909356488366428bdae1bca76f64154ce", - "0x20fbc28bb7d4f4ab2fdfb6233d1f83153d5230123e95522cfbfaf11394b6db47", - "0x0e5a596b8883e30fd429125478e11ffc1c28d5476737c738408d1b114536ad14" + "0x1fc8d5506e8f4c8073e4e7d80984942c7f62d70d1ee82c34d8c44a99bc6841cc", + "0x0d8bd53ef5d273e7eb918237b82bdf1d6eff563194d0f182234fe749e845eaf6", + "0x07a128a4a5076e84318d6224347b7a70851dc40408cf276720acb6b2a8ab3546", + "0x0746b6a0ba2aaf9d2e439e84e564f33a32c8617de4b41d13f37a68b253f55678" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000003" squeeze_mode = false @@ -628,7 +628,7 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x20f1c701d84b280c9f80a517272153688a9e1b92166d1000cfe7c829b7c25f69", "0x28acc2354e5ea51f25dd1a1e980d97c826400f67bc116d1b6187bdfe5726cb6a", "0x0db3ab63c7556f8724d86819156701855018cc79140a052893d67fb67c99c1f1", - "0x011ce78c298fc81d89c739267f5a1f69e22587103f51d97947d04724c545a848" + "0x08b0c1ac4553f1257a42dcf5a95191fc747ad0228ffc5c16e6244c4ed4858fea" ] [inputs.previous_rollups.vk_data.vk] @@ -1237,7 +1237,7 @@ proof = [ [inputs.previous_rollups.public_inputs] timestamp = "0x0000000000000000000000000000000000000000000000000000000000000186" - block_headers_hash = "0x014d2c1b7493f19d65aed76b34bc75fc102c4ef0cf000926cc2d6d82d4d01ee6" + block_headers_hash = "0x04397632f72bd3e1bb176893a988ddae120cbfe5f011e1bb9b9251c5ce2292dc" 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 = "0x17f4111918fd7fd073ef3c7b0685a0b0f9de9c06978162bfb3284f1949143cfb" + vk_tree_root = "0x29b7bb0a371549fbde9a7c1967f9368c8b78f8d537b1c0a47626757643283c1d" protocol_contracts_hash = "0x14cec0ae6f31e913c3be04691c6d49c3ec07b3f2d024c9558693ea62fc530bbf" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" slot_number = "0x000000000000000000000000000000000000000000000000000000000000000f" @@ -1261,11 +1261,11 @@ proof = [ fee_per_l2_gas = "0x0000000000000000000000000000000000000000000000000000000000000000" [inputs.previous_rollups.public_inputs.previous_archive] - root = "0x1e97c93cb4174d45a8a02061726c03e9a1443d5dc3c72e92307f766f85718e22" + root = "0x0f9544cbd0e96c102033eb1b7e3767a4ab5d0fdfcd8453af3e55f1517e871f7c" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000002" [inputs.previous_rollups.public_inputs.new_archive] - root = "0x2580befe564b92754738d41b1a5351a0bd83c1835b5e8fbc5a8de35f768dd932" + root = "0x0e3319a0c0b57d17e881c45b23ab5856cbca6fc7e89e8b2167d4bd53a9ff7eb3" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000003" [inputs.previous_rollups.public_inputs.start_state.l1_to_l2_message_tree] @@ -1310,10 +1310,10 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x0c123047b777079e8db5f669ddaf1ffa1c7780d56b21e29f35e4772ac0f6280c" ] state = [ - "0x26d2ddc07ff22beab8288c1c0b9d5b186bcef21b51fd6c36aac0c08a85b02a3d", - "0x0b5c879e38b476648f27959441fd207909356488366428bdae1bca76f64154ce", - "0x20fbc28bb7d4f4ab2fdfb6233d1f83153d5230123e95522cfbfaf11394b6db47", - "0x0e5a596b8883e30fd429125478e11ffc1c28d5476737c738408d1b114536ad14" + "0x1fc8d5506e8f4c8073e4e7d80984942c7f62d70d1ee82c34d8c44a99bc6841cc", + "0x0d8bd53ef5d273e7eb918237b82bdf1d6eff563194d0f182234fe749e845eaf6", + "0x07a128a4a5076e84318d6224347b7a70851dc40408cf276720acb6b2a8ab3546", + "0x0746b6a0ba2aaf9d2e439e84e564f33a32c8617de4b41d13f37a68b253f55678" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000003" squeeze_mode = false @@ -1328,10 +1328,10 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x0c123047b777079e8db5f669ddaf1ffa1c7780d56b21e29f35e4772ac0f6280c" ] state = [ - "0x0136ddb8e63e19ceb37cc1cb5f09183134b29903de9ac6125131d9e873e98d63", - "0x03710711ebe2fcd59ca990189104549239a66a6fa543ab59a939826c82d420b3", - "0x0fdc06c2da7f9f67e79fd441c4238710007f868ab6572dcd87752679876cc842", - "0x21a7758283bf47dee2eda50773f56a33945f3363cbad890b6efff037cbcf6cc2" + "0x13a8e2dc8bfd7d568d80ce9ffe4fb93fd7f8912cb0def4059ee2e5bf22e9eb36", + "0x2a4f0795668cecf5e702e3f80b7201d42c468229285628b33a4423ed261882e9", + "0x262e18d1e44aa9232ec6cd77fd1bb2176bd71e32f1e90e4018ca4a1181f0e9f5", + "0x1165a14aa0c3ef22de7bd8b0b53f2c74050f7961eec7a6d5e3ad9c5c3750f317" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000003" squeeze_mode = false @@ -1381,7 +1381,7 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x20f1c701d84b280c9f80a517272153688a9e1b92166d1000cfe7c829b7c25f69", "0x28acc2354e5ea51f25dd1a1e980d97c826400f67bc116d1b6187bdfe5726cb6a", "0x0db3ab63c7556f8724d86819156701855018cc79140a052893d67fb67c99c1f1", - "0x011ce78c298fc81d89c739267f5a1f69e22587103f51d97947d04724c545a848" + "0x08b0c1ac4553f1257a42dcf5a95191fc747ad0228ffc5c16e6244c4ed4858fea" ] [inputs.previous_rollups.vk_data.vk] 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 e3c17c20f66e..554ead41ab57 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 @@ -40,7 +40,7 @@ l1_to_l2_message_frontier_hint = [ ] new_archive_sibling_path = [ "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x29f324b858bac5a5d748db53a566c3b4bd986055fea5aafbc0d29c34aae1a2ec", + "0x22d0ab9ec763785ab7253236d2bb39bde992cb804891430f1f9ba0726578c238", "0x14e4b977b2203b70e6ee1c2456eb7114d090fe4b907f631eecd0919fed432e7d", "0x30105bad22ddcc508b739b7c9ad87a561c569ff5cb0098a853c1c4ac21b7a037", "0x1e20ad4181460cbfdc74ca773502c59b890f184efe300ebad895956d318422da", @@ -72,7 +72,7 @@ new_archive_sibling_path = [ ] [inputs.previous_archive] - root = "0x2d5b54ba57f64566a553c0b24f32a258e2dd0a01f9c35f5ecbda41eecef5816a" + root = "0x1fd6eb741587b7f886a1d4dcf053cc60de997addf7ec82e0d6be17246037375f" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000002" [inputs.previous_state.l1_to_l2_message_tree] @@ -94,7 +94,7 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 [inputs.constants] chain_id = "0x0000000000000000000000000000000000000000000000000000000000000000" version = "0x0000000000000000000000000000000000000000000000000000000000000000" - vk_tree_root = "0x17f4111918fd7fd073ef3c7b0685a0b0f9de9c06978162bfb3284f1949143cfb" + vk_tree_root = "0x29b7bb0a371549fbde9a7c1967f9368c8b78f8d537b1c0a47626757643283c1d" protocol_contracts_hash = "0x14cec0ae6f31e913c3be04691c6d49c3ec07b3f2d024c9558693ea62fc530bbf" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" slot_number = "0x000000000000000000000000000000000000000000000000000000000000000f" @@ -119,10 +119,10 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x141653e4c04837aba0830a28648767ceea6fcbcefaa4d401d0a37f89f5d88fa2" ] state = [ - "0x1155888dcf0789e8526cadee6739414891d6bb51a6594fa7d047288b4403252b", - "0x0e35ef4e227980151c689db69320986d5ddcd232c2e52784335954a7c5e4348a", - "0x00a9e793b460da6f68e7b73542ac9aca93234f6a83113b3d6963553e4931c6d1", - "0x1ce6e3ae0aff73980767e324e4c281ba2fa731eb6ccf345e77955aa3ae34a279" + "0x0dbcca908f415980e16a4532373f6b1bd6c064e523760bbd8ca013bd91730493", + "0x29a5015d30e811b4f3e91a2486fdbdfccd794c53435bab6d09c3abb7e97fc226", + "0x0d72bdac4082e865195c9f24387dec254e8994e87aa66e897307f1b536890806", + "0x12c1e3fddc015e4451841d2fb9022bf63a5276aa0d1f4c28ede0b348ca16d5bd" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000003" squeeze_mode = false 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 de9c8216baaa..c585c21f97d6 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 @@ -561,7 +561,7 @@ new_archive_sibling_path = [ accumulated_mana_used = "0x000000000000000000000000000000000000000000000000000000000006b6c0" [inputs.previous_rollup.public_inputs.constants] - vk_tree_root = "0x17f4111918fd7fd073ef3c7b0685a0b0f9de9c06978162bfb3284f1949143cfb" + vk_tree_root = "0x29b7bb0a371549fbde9a7c1967f9368c8b78f8d537b1c0a47626757643283c1d" protocol_contracts_hash = "0x14cec0ae6f31e913c3be04691c6d49c3ec07b3f2d024c9558693ea62fc530bbf" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" @@ -642,10 +642,10 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x00000000000000000000000000000000000000000000000000000000b7d1b44c" ] state = [ - "0x297a006e52bb0b647602c8d1dbb6c7bde6c13fad9330c04dfc566d1c007ecc07", - "0x067e3be3ce94aaeb869a6b9fd9c409f1047c811bcbbf723b96e32230186fa559", - "0x2b6213ee5e15131d69efca7d502c1ab866e616aaf649c0816759c0e6ce12d141", - "0x041d3307a58b6b77175f9b08c19d5854c9f759eceda659c1f412c8754fffb48c" + "0x09d3f1c0c7be5b63c7f1ba990fde6e969a114eb2b6dd2236d09fc927042d67c2", + "0x25dea767c96d3b173d27e39bf58b72452c6d6a9e931ec2fcac716a7c61f89876", + "0x2a32ad6a1cd5dd48025964615a836be7f3fea680c78dadcd438259571d638ab5", + "0x175d28e27f0acb721db629a35351c8b5158ddb11823604ae43a1e8c1658ee1b0" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000002" squeeze_mode = false @@ -659,7 +659,7 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x1800b90c40fa738a64bbc71d11bdc768c7c43a4e8876d6e5bf203355830af7e2", "0x28acc2354e5ea51f25dd1a1e980d97c826400f67bc116d1b6187bdfe5726cb6a", "0x0db3ab63c7556f8724d86819156701855018cc79140a052893d67fb67c99c1f1", - "0x011ce78c298fc81d89c739267f5a1f69e22587103f51d97947d04724c545a848" + "0x08b0c1ac4553f1257a42dcf5a95191fc747ad0228ffc5c16e6244c4ed4858fea" ] [inputs.previous_rollup.vk_data.vk] 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 ac639c443f33..9f187823bf34 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 @@ -561,7 +561,7 @@ new_archive_sibling_path = [ accumulated_mana_used = "0x000000000000000000000000000000000000000000000000000000000006b6c0" [inputs.previous_rollups.public_inputs.constants] - vk_tree_root = "0x17f4111918fd7fd073ef3c7b0685a0b0f9de9c06978162bfb3284f1949143cfb" + vk_tree_root = "0x29b7bb0a371549fbde9a7c1967f9368c8b78f8d537b1c0a47626757643283c1d" protocol_contracts_hash = "0x14cec0ae6f31e913c3be04691c6d49c3ec07b3f2d024c9558693ea62fc530bbf" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" @@ -642,10 +642,10 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x00000000000000000000000000000000000000000000000000000000b7d1b44c" ] state = [ - "0x25cd87511ee2f54b24cd3811c5007f7758d3fca4daf047d1fc69a5f348c64707", - "0x239b5d1e1fcee06a16c3bddbb0a03ba8f9d44aeaad631380aea777a792e27c59", - "0x048c114ce54ec22d9792d316668533745cbbc30769d05a297c1a650a7c8f4ded", - "0x069518aa2420bd0c176b4adcd90228f5d16bf81c52f5263851f5010e3d5d9e9d" + "0x02c875a368b87760e8f57e31c6cbf464d0ccaddcf3b2902a15b80710e24ac9ec", + "0x148abc9dfaa1858679b9fc063bc11143b8a408aed1eb779cbaff0ed677400031", + "0x21a05dbb62319c139adf57659a893717ffe8671e696c6e9229057931140ed324", + "0x2880cbe0eef47a35e6f60adedec4c01c135cff19e24004e1b08edac3445c893d" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000002" squeeze_mode = false @@ -659,7 +659,7 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x1800b90c40fa738a64bbc71d11bdc768c7c43a4e8876d6e5bf203355830af7e2", "0x28acc2354e5ea51f25dd1a1e980d97c826400f67bc116d1b6187bdfe5726cb6a", "0x0db3ab63c7556f8724d86819156701855018cc79140a052893d67fb67c99c1f1", - "0x011ce78c298fc81d89c739267f5a1f69e22587103f51d97947d04724c545a848" + "0x08b0c1ac4553f1257a42dcf5a95191fc747ad0228ffc5c16e6244c4ed4858fea" ] [inputs.previous_rollups.vk_data.vk] @@ -1273,7 +1273,7 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 accumulated_mana_used = "0x0000000000000000000000000000000000000000000000000000000000000000" [inputs.previous_rollups.public_inputs.constants] - vk_tree_root = "0x17f4111918fd7fd073ef3c7b0685a0b0f9de9c06978162bfb3284f1949143cfb" + vk_tree_root = "0x29b7bb0a371549fbde9a7c1967f9368c8b78f8d537b1c0a47626757643283c1d" protocol_contracts_hash = "0x14cec0ae6f31e913c3be04691c6d49c3ec07b3f2d024c9558693ea62fc530bbf" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" @@ -1336,10 +1336,10 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x00000000000000000000000000000000000000000000000000000000b7d1b44c" ] state = [ - "0x25cd87511ee2f54b24cd3811c5007f7758d3fca4daf047d1fc69a5f348c64707", - "0x239b5d1e1fcee06a16c3bddbb0a03ba8f9d44aeaad631380aea777a792e27c59", - "0x048c114ce54ec22d9792d316668533745cbbc30769d05a297c1a650a7c8f4ded", - "0x069518aa2420bd0c176b4adcd90228f5d16bf81c52f5263851f5010e3d5d9e9d" + "0x02c875a368b87760e8f57e31c6cbf464d0ccaddcf3b2902a15b80710e24ac9ec", + "0x148abc9dfaa1858679b9fc063bc11143b8a408aed1eb779cbaff0ed677400031", + "0x21a05dbb62319c139adf57659a893717ffe8671e696c6e9229057931140ed324", + "0x2880cbe0eef47a35e6f60adedec4c01c135cff19e24004e1b08edac3445c893d" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000002" squeeze_mode = false @@ -1354,10 +1354,10 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x00000000000000000000000000000000000000000000000000000000b7e5c34d" ] state = [ - "0x013d54165f30aff2c2d690dbda10d09661f5e23b61ba54d67ca3a24d1472964e", - "0x109ad9b3d57f4d283401724d7b431d9d17ee9aa37bdb6e3274d60178d4d27f27", - "0x20e8eeec6726826910e2eac9ae1b0b44443f0bfa52f97fffab2bb206898b271d", - "0x094d4a260652f04952a3fe39e1abf4c1cb1ae92414f46c59e4f2f5b76ac1a8ca" + "0x06e8c4180bf4d4b18a68281e69a9615e071bfe01e4335950133ed37861ba74b3", + "0x2e75a068bf5504440ff9cf8d707f146f2932fdb5576cefc956d0a48b91c66229", + "0x125aaa219148d8ed8b94620af4398be3b96b812e5cc910a82195d718930e53a1", + "0x2155f776303d5893965bec3fff9dce99030d7bbac4b8d1a082a13d49a7aaffa0" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000001" squeeze_mode = false @@ -1371,7 +1371,7 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x20f1c701d84b280c9f80a517272153688a9e1b92166d1000cfe7c829b7c25f69", "0x28acc2354e5ea51f25dd1a1e980d97c826400f67bc116d1b6187bdfe5726cb6a", "0x0db3ab63c7556f8724d86819156701855018cc79140a052893d67fb67c99c1f1", - "0x011ce78c298fc81d89c739267f5a1f69e22587103f51d97947d04724c545a848" + "0x08b0c1ac4553f1257a42dcf5a95191fc747ad0228ffc5c16e6244c4ed4858fea" ] [inputs.previous_rollups.vk_data.vk] 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 dae104e4fb7a..d19f963cc938 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 = [ - "0x0020b52a29d455f41db5b2ab594a19cc1d54cde2744df797fb4ad636c02c530e", + "0x00bc4d9adb04df45102526dfee8e7ba8206e67db1f268a15dfb7d5254834d805", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", @@ -523,7 +523,7 @@ proof = [ [inputs.previous_rollups.public_inputs.constants] chain_id = "0x0000000000000000000000000000000000000000000000000000000000000000" version = "0x0000000000000000000000000000000000000000000000000000000000000000" - vk_tree_root = "0x17f4111918fd7fd073ef3c7b0685a0b0f9de9c06978162bfb3284f1949143cfb" + vk_tree_root = "0x29b7bb0a371549fbde9a7c1967f9368c8b78f8d537b1c0a47626757643283c1d" protocol_contracts_hash = "0x14cec0ae6f31e913c3be04691c6d49c3ec07b3f2d024c9558693ea62fc530bbf" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" @@ -532,7 +532,7 @@ proof = [ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000001" [inputs.previous_rollups.public_inputs.new_archive] - root = "0x1758f6b8dee66a7853b3b5caf367258a2d08320c56b6dd919d448c881ccd54b0" + root = "0x2af6738039af401e1a5060d0987e2c055c3cef20e1d77df9fe6e35d38b481cbb" 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 = "0x00a64c6c1344bb53c8b16408964fc4ae8b2d25031c162dd44e0f8f24c8fc85c8" - z_acc = "0x050b676c523ba7f24abe2c8c579d6a4c2adf90a5d815e5ab91c1268979ed51e8" - gamma_acc = "0x0a0d40d3381bee9f5de2b0c8a163e7439127722bca9bac2ff79d2c71dfdba80a" + blob_commitments_hash_acc = "0x0098cbc3e5cb0d332bc4128da2b7ffbacf4f4dcfa9d355c1a8a9c76232cd225d" + z_acc = "0x185616adcd1c0bab9b0f61db3855d604afa0ff1e52a7dff01e9dbca658fa746e" + gamma_acc = "0x2d344f64fe7440b6769094eeea3928b4d0e0c23a7b3b17b25586f44a0106b0e0" [inputs.previous_rollups.public_inputs.end_blob_accumulator.y_acc] limbs = [ - "0xb449395155b8c48df89114b8f7b4e8", - "0xdee2f23f7891f3f8d8321a9b6f0784", - "0x4f0f" + "0x7e18576eb31b1510b326215417a988", + "0x3306c6d16469c513778b40790d2cf8", + "0x463c" ] [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 = [ - "0x85ebe5dfff0a47b629909c2cb9e019", - "0xe96ae1b66aabb5b95f19fb7bb667c6", - "0xb4fc7c13c8f44350bc2d1718706d25", - "0x147a73" + "0x633c6563442ae35b0a393340ad6f87", + "0x69c1addec79df2ca6670aff7b54adc", + "0xfbd028e8d5fc32e7c648d385afbb8f", + "0x01ac95" ] [inputs.previous_rollups.public_inputs.end_blob_accumulator.c_acc.y] limbs = [ - "0xf25b45ca073d71d37107d1935edf86", - "0xe587ba8582544c5f89e0edda8ce7f9", - "0x38d5e766d97fd1e056eb9c27b139aa", - "0x0d1061" + "0xb9904455e6e480fc1d04ab68adb066", + "0xbb9ba7db013afee70656c7430cf1e9", + "0x89d0193d074fa493b7a228aa61c6cc", + "0x16170a" ] [inputs.previous_rollups.public_inputs.end_blob_accumulator.gamma_pow_acc] limbs = [ - "0xffeae17c28cdb649a9ab3e3910959c", - "0xc8d17b4db7caa676cdf716e5f89e0b", - "0x0015" + "0x919f7cc0dab4317c12798ca8df4aa9", + "0x6dfac5bd57a22620637cb7c7d4deed", + "0x0bb5" ] [inputs.previous_rollups.public_inputs.final_blob_challenges] - z = "0x20dd8e06b175404201b14b67a4ddfd46ee473fc3e58213a98382f411cfa41350" + z = "0x2986f0bec70b9cf1cc17ec1c01c31cce54f7d672c1763394f893dc185dda3945" [inputs.previous_rollups.public_inputs.final_blob_challenges.gamma] limbs = [ - "0xffeae17c28cdb649a9ab3e3910959c", - "0xc8d17b4db7caa676cdf716e5f89e0b", - "0x0015" + "0x919f7cc0dab4317c12798ca8df4aa9", + "0x6dfac5bd57a22620637cb7c7d4deed", + "0x0bb5" ] [inputs.previous_rollups.vk_data] @@ -830,7 +830,7 @@ proof = [ "0x02dfab3acbc7708b1b3654912e946fe846568b8cb0e8af8da6a1845278664e55", "0x29ef5024937c71b580f6cfb649bd06b4a62241ec399bd579b0d0b2bec78c4cae", "0x0db3ab63c7556f8724d86819156701855018cc79140a052893d67fb67c99c1f1", - "0x011ce78c298fc81d89c739267f5a1f69e22587103f51d97947d04724c545a848" + "0x08b0c1ac4553f1257a42dcf5a95191fc747ad0228ffc5c16e6244c4ed4858fea" ] [inputs.previous_rollups.vk_data.vk] @@ -1441,7 +1441,7 @@ proof = [ start_inbox_rolling_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" end_inbox_rolling_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" checkpoint_header_hashes = [ - "0x008838f828a909c8437776e6e579bec161452199117611bcb5c9cfa2de614507", + "0x0090d9297d9d7c1de5e794142a49e395ec3dd4f0ffcb829ca42ad2a7b3912ea7", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", @@ -1478,16 +1478,16 @@ proof = [ [inputs.previous_rollups.public_inputs.constants] chain_id = "0x0000000000000000000000000000000000000000000000000000000000000000" version = "0x0000000000000000000000000000000000000000000000000000000000000000" - vk_tree_root = "0x17f4111918fd7fd073ef3c7b0685a0b0f9de9c06978162bfb3284f1949143cfb" + vk_tree_root = "0x29b7bb0a371549fbde9a7c1967f9368c8b78f8d537b1c0a47626757643283c1d" protocol_contracts_hash = "0x14cec0ae6f31e913c3be04691c6d49c3ec07b3f2d024c9558693ea62fc530bbf" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" [inputs.previous_rollups.public_inputs.previous_archive] - root = "0x1758f6b8dee66a7853b3b5caf367258a2d08320c56b6dd919d448c881ccd54b0" + root = "0x2af6738039af401e1a5060d0987e2c055c3cef20e1d77df9fe6e35d38b481cbb" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000002" [inputs.previous_rollups.public_inputs.new_archive] - root = "0x2b6d7e94bd1348bc84ac58e03f55bd02c81bd4b3d76a55e5e88f83b7a947d54e" + root = "0x23c70cda9ddf7f006c7ba6ae755bba84dedf050077ac3b79c754cd00cf0b48a2" 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 = "0x00a64c6c1344bb53c8b16408964fc4ae8b2d25031c162dd44e0f8f24c8fc85c8" - z_acc = "0x050b676c523ba7f24abe2c8c579d6a4c2adf90a5d815e5ab91c1268979ed51e8" - gamma_acc = "0x0a0d40d3381bee9f5de2b0c8a163e7439127722bca9bac2ff79d2c71dfdba80a" + blob_commitments_hash_acc = "0x0098cbc3e5cb0d332bc4128da2b7ffbacf4f4dcfa9d355c1a8a9c76232cd225d" + z_acc = "0x185616adcd1c0bab9b0f61db3855d604afa0ff1e52a7dff01e9dbca658fa746e" + gamma_acc = "0x2d344f64fe7440b6769094eeea3928b4d0e0c23a7b3b17b25586f44a0106b0e0" [inputs.previous_rollups.public_inputs.start_blob_accumulator.y_acc] limbs = [ - "0xb449395155b8c48df89114b8f7b4e8", - "0xdee2f23f7891f3f8d8321a9b6f0784", - "0x4f0f" + "0x7e18576eb31b1510b326215417a988", + "0x3306c6d16469c513778b40790d2cf8", + "0x463c" ] [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 = [ - "0x85ebe5dfff0a47b629909c2cb9e019", - "0xe96ae1b66aabb5b95f19fb7bb667c6", - "0xb4fc7c13c8f44350bc2d1718706d25", - "0x147a73" + "0x633c6563442ae35b0a393340ad6f87", + "0x69c1addec79df2ca6670aff7b54adc", + "0xfbd028e8d5fc32e7c648d385afbb8f", + "0x01ac95" ] [inputs.previous_rollups.public_inputs.start_blob_accumulator.c_acc.y] limbs = [ - "0xf25b45ca073d71d37107d1935edf86", - "0xe587ba8582544c5f89e0edda8ce7f9", - "0x38d5e766d97fd1e056eb9c27b139aa", - "0x0d1061" + "0xb9904455e6e480fc1d04ab68adb066", + "0xbb9ba7db013afee70656c7430cf1e9", + "0x89d0193d074fa493b7a228aa61c6cc", + "0x16170a" ] [inputs.previous_rollups.public_inputs.start_blob_accumulator.gamma_pow_acc] limbs = [ - "0xffeae17c28cdb649a9ab3e3910959c", - "0xc8d17b4db7caa676cdf716e5f89e0b", - "0x0015" + "0x919f7cc0dab4317c12798ca8df4aa9", + "0x6dfac5bd57a22620637cb7c7d4deed", + "0x0bb5" ] [inputs.previous_rollups.public_inputs.end_blob_accumulator] - blob_commitments_hash_acc = "0x00771a9e93c756e2d34c05997d3f392aa8d36b0e1120ffb2cb81de511129199f" - z_acc = "0x1a0c6e449d156d191add5e3b0d37027b2480dc205f8ec009165bc4a0ad5adc5b" - gamma_acc = "0x13d45af539c9fd0c236ff6b7a7416a03d10a935864002c6a578c28ff6b1b4d54" + blob_commitments_hash_acc = "0x008995253eebf93c3d7aaa58c1478caae2c6ff3c8696c762f4fc4a4b08484c50" + z_acc = "0x25a4eaa47570c69f721ccb4e92b8b131ec85f11821d2ad9f95b7880d8bc9659f" + gamma_acc = "0x2dcc2e03b93607b12d0d95a155f38a8a9854202d51f2d2581c5608477e92a246" [inputs.previous_rollups.public_inputs.end_blob_accumulator.y_acc] limbs = [ - "0xef4eb6f373dddd6c73c84385a0b5a0", - "0x05dd516e51404c876fd73885b54f55", - "0x71fd" + "0x80b35955348433fc7a68b473a15650", + "0x768b90ef08c5d307a1e10cbf739b48", + "0x7148" ] [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 = [ - "0xa307eb3902f506138520f68ba71044", - "0x16517b4764337f763fd3da6c920508", - "0x196de8a12d40a79f8839063b31fe16", - "0x059c9c" + "0x79fbdf1a447d706b58c654d1a7e0cc", + "0x2250b9a737a1e38a0a4099ecef1f52", + "0x8ad0e8112518702ee5455d1f500fe8", + "0x03b602" ] [inputs.previous_rollups.public_inputs.end_blob_accumulator.c_acc.y] limbs = [ - "0x19e00489a527a6114a879adfed39b7", - "0x851b8e7d51b18cc50ad7a35f9433f1", - "0x3c165f7846fe72fb2316544de914bf", - "0x032c1f" + "0x062c3d14b07e1d6ea6ee55e1627bd4", + "0xff15dc978e58aed898f0cbb947d04c", + "0xeb5c34260e4ba8d90f549d6d6a5861", + "0x10a79e" ] [inputs.previous_rollups.public_inputs.end_blob_accumulator.gamma_pow_acc] limbs = [ - "0xfcccda7b40f7bc7d6c3ccc9592662c", - "0xef35d48ca019120ee2b09d04c96606", - "0x1539" + "0x916bd32e3d5ff5e8c793a3bd4b1064", + "0xa2c65d77418c839cab8d1924aa1c74", + "0x1d7d" ] [inputs.previous_rollups.public_inputs.final_blob_challenges] - z = "0x20dd8e06b175404201b14b67a4ddfd46ee473fc3e58213a98382f411cfa41350" + z = "0x2986f0bec70b9cf1cc17ec1c01c31cce54f7d672c1763394f893dc185dda3945" [inputs.previous_rollups.public_inputs.final_blob_challenges.gamma] limbs = [ - "0xffeae17c28cdb649a9ab3e3910959c", - "0xc8d17b4db7caa676cdf716e5f89e0b", - "0x0015" + "0x919f7cc0dab4317c12798ca8df4aa9", + "0x6dfac5bd57a22620637cb7c7d4deed", + "0x0bb5" ] [inputs.previous_rollups.vk_data] @@ -1785,7 +1785,7 @@ proof = [ "0x02dfab3acbc7708b1b3654912e946fe846568b8cb0e8af8da6a1845278664e55", "0x29ef5024937c71b580f6cfb649bd06b4a62241ec399bd579b0d0b2bec78c4cae", "0x0db3ab63c7556f8724d86819156701855018cc79140a052893d67fb67c99c1f1", - "0x011ce78c298fc81d89c739267f5a1f69e22587103f51d97947d04724c545a848" + "0x08b0c1ac4553f1257a42dcf5a95191fc747ad0228ffc5c16e6244c4ed4858fea" ] [inputs.previous_rollups.vk_data.vk] 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 946285e4fdff..a24570954eb1 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 = "0x17d4f290a6e05b1ac6b5be2e1edc3481c28068cde1eadb7824bef517390bb382" + block_headers_hash = "0x267912696e4e10bbf77edbf485472a2f053d99df617f5e8d22fe641c9aa3cb9a" 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 = "0x17f4111918fd7fd073ef3c7b0685a0b0f9de9c06978162bfb3284f1949143cfb" + vk_tree_root = "0x29b7bb0a371549fbde9a7c1967f9368c8b78f8d537b1c0a47626757643283c1d" protocol_contracts_hash = "0x14cec0ae6f31e913c3be04691c6d49c3ec07b3f2d024c9558693ea62fc530bbf" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" slot_number = "0x000000000000000000000000000000000000000000000000000000000000000f" @@ -512,7 +512,7 @@ proof = [ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000001" [inputs.previous_rollup.public_inputs.new_archive] - root = "0x0e638cc0de2c75bdbdbaa20710be401108bec288937504f7cf3b53c4d340f57f" + root = "0x03047a26aae0869a941cb5bc9681c4bca632a6aa52ad3c2361f171d7fdcdda20" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000002" [inputs.previous_rollup.public_inputs.start_state.l1_to_l2_message_tree] @@ -575,10 +575,10 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x1e1c597744057b88e39a9780ed087c39b1fc42864e05ef03a59ebd9e96b70b00" ] state = [ - "0x1dd5bbb9536ea8526866b5606589ead05f013e77c99beb3dae1a679550886fce", - "0x09833a542f590810f34785daa35213eacef84500ae3806736f649b5e04c5c107", - "0x073b4cd03dfc738b8e552c9d7e04d7b098463c58f46ba070dec2d21b370a0c69", - "0x1f89cecfdf8fec72e2bde9542a09569400bd7f2d2cf83ec52f54ebf59ad7186d" + "0x1f11b531babd54da28a996ff0e8917d936458a96751da9a3e3b981ddfb2b2c53", + "0x193e3e141f86215b2f8b9738aa43173e37048d9f4926dbf3b15c57e4b40f569e", + "0x0d6a7b508ef799b3eb43b0b3a7bb353d1f38835cc3f49675d19079fd32721838", + "0x281f53b5055adda18d9097eee4fd1fa523f4d4176eff7502e6662451229031f7" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000002" squeeze_mode = false @@ -628,7 +628,7 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x20f1c701d84b280c9f80a517272153688a9e1b92166d1000cfe7c829b7c25f69", "0x28acc2354e5ea51f25dd1a1e980d97c826400f67bc116d1b6187bdfe5726cb6a", "0x0db3ab63c7556f8724d86819156701855018cc79140a052893d67fb67c99c1f1", - "0x011ce78c298fc81d89c739267f5a1f69e22587103f51d97947d04724c545a848" + "0x08b0c1ac4553f1257a42dcf5a95191fc747ad0228ffc5c16e6244c4ed4858fea" ] [inputs.previous_rollup.vk_data.vk] @@ -1167,7 +1167,7 @@ proof = [ [inputs.inbox_parity.public_inputs] start_rolling_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" - end_rolling_hash = "0x00087fcd5a2ee0849c3801915ea5ebdc9b1934b7f233157fafb558f693a706a4" + end_rolling_hash = "0x006d131248894be46de839138bff160c07ff1727a08d40d3566dfadae7de82f3" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" [inputs.inbox_parity.public_inputs.end_sponge] @@ -1191,9 +1191,9 @@ proof = [ [inputs.inbox_parity.vk_data] leaf_index = "0x000000000000000000000000000000000000000000000000000000000000004b" sibling_path = [ - "0x1abd1a8458c08a3a574d06a9b202d9581cb4d3b2c2cd2b387dab0bf3d35342e5", + "0x0cd41d6e5b8b30ed20ce983ed5597482bdd5a3a71bea308d77d6a570fb566a25", "0x1d438b6650ee2b4a994c0eec98be4e2fd6f843225ad6fb72d292d652cfe1aa2b", - "0x21e0cbd22d0a0b2139090bb11909329af47df46c4a4a93a32582c8a11e28ad5e", + "0x0a1834c236ff39a85ada2b2e0d720a1f39737fb84582978fa3233ec2504127bc", "0x21ed90cd61698dc1e3dfc970eefcedc837dfedf46c2c4e594ce076ee8fba9386", "0x1e20ad4181460cbfdc74ca773502c59b890f184efe300ebad895956d318422da", "0x1434e6e2d5db1053ab8a3be58704509c799ee17e109c77f441f7bf1755400249", @@ -1205,46 +1205,46 @@ proof = [ "0x0000000000000000000000000000000000000000000000000000000000000016", "0x0000000000000000000000000000000000000000000000000000000000000015", "0x0000000000000000000000000000000000000000000000000000000000000005", - "0x00000000000000000000000000000047930989a1435aefabec7a53da1295a7ae", - "0x00000000000000000000000000000000002793641df42a1dbd2da6c51b4f328c", - "0x0000000000000000000000000000003d537d131b889b164336ba29b0b06f8bad", - "0x0000000000000000000000000000000000002772f48879c267496bae5be69d5d", - "0x000000000000000000000000000000d48cedfb66ccfff6fafacf2cb170e9f0c4", - "0x00000000000000000000000000000000002a32aff6611eb90b640610d9308ae4", - "0x000000000000000000000000000000b4778b04d31485f324bf78414f15d9d0b0", - "0x000000000000000000000000000000000012ac9ac58b356133025d524c776da8", - "0x0000000000000000000000000000002cd2dc479b1f12b0c178c1dceeeff9c14e", - "0x00000000000000000000000000000000001dd1c1b32b7f68f4b08c7793d4b860", - "0x0000000000000000000000000000004333ad5d0df7f193023d36b6cb7c32f7b8", - "0x000000000000000000000000000000000023b7011f3244cd75e65704ebc59b53", - "0x0000000000000000000000000000008cb2ff51d8094334792b19939fb966bd4f", - "0x0000000000000000000000000000000000100cba9a83d12d01b0a0961e2ebb9f", - "0x0000000000000000000000000000002e9f81b955250b17dfc5d64e5a8a994233", - "0x00000000000000000000000000000000000dcd68306bffac0b80e9f3d78ed39f", - "0x000000000000000000000000000000ae4c0d6b427a1b5f0d5040101b3f13b00a", - "0x000000000000000000000000000000000024ab486469085fe5f4666026a8128e", - "0x00000000000000000000000000000092f58cf4788881f7166be79949512095c7", - "0x0000000000000000000000000000000000009c381ea4e47d4837f1d8f2864353", - "0x000000000000000000000000000000614c3aa118f4fd86815221782e946f5432", - "0x00000000000000000000000000000000002b2eb2a4b0c175d5c5d7bf156d041c", - "0x0000000000000000000000000000005567dc26b8ba93db0c259c137705613cfc", - "0x000000000000000000000000000000000013eb1d0eec54f88b2aee69897359dc", - "0x000000000000000000000000000000e5c7d897e65de199b4d392734a689e88f7", - "0x000000000000000000000000000000000002da963311d2014a98ce75cc1d9936", - "0x000000000000000000000000000000de90e6ba417fc3520216444617c88c71d2", - "0x00000000000000000000000000000000000fcde67d681340bf4a41a558e2645c", - "0x00000000000000000000000000000095d9e42f1c702aef317e157f548a943557", - "0x000000000000000000000000000000000014f0071ef4953864274c855f2c7383", - "0x0000000000000000000000000000007cdc4528c7dd164b9102b61ff9e5c9a8da", - "0x0000000000000000000000000000000000104c978c445466aca1d0cbcb3d4311", + "0x000000000000000000000000000000e7c17b68bae36db64bdde5e0eb6f3f9bd4", + "0x00000000000000000000000000000000000593a32f6c85695b30ea73366c227c", + "0x00000000000000000000000000000026bd48770d9caddc256e8501c4de229a23", + "0x00000000000000000000000000000000002728effa00f22475b1a098f3bce712", + "0x000000000000000000000000000000d01e7d20b5ebedd296fb0791f648f409a8", + "0x0000000000000000000000000000000000012e5fb9bbf45a25da0d9bbfd84141", + "0x00000000000000000000000000000019639dace67ee351bb798c77450001385f", + "0x00000000000000000000000000000000001f6ea37a0daf717dcd99a339724de0", + "0x0000000000000000000000000000003c2b69238d205aa18f1eebd1c060e3f555", + "0x00000000000000000000000000000000002e941b87544b8ff5b0988922f0c300", + "0x000000000000000000000000000000c808c0aeed958bc35dd8b11b171215e27d", + "0x00000000000000000000000000000000000da79baa9c3b4292c26f6895325f1d", + "0x000000000000000000000000000000dcab1cc6e80b3dd9c8b27d1aa6def696c1", + "0x00000000000000000000000000000000002dc2aa37956e715c057981354492c1", + "0x0000000000000000000000000000001271a00a75bbe7c6321bd8a69177a6e956", + "0x0000000000000000000000000000000000257c575dff984c414cb9646ae71419", + "0x000000000000000000000000000000d0a8f121c3861c92ed6bb8e14a4f50ad4d", + "0x00000000000000000000000000000000000a7054c838e4f845b62eb24a85fce0", + "0x000000000000000000000000000000425b572d1bcffb8107a40573ddc9e3bfa9", + "0x000000000000000000000000000000000025e3e1bc0c49e5b6cae83e8b8f11c0", + "0x00000000000000000000000000000032a44a22c00d7dcd8e699e2b9a8ad2f867", + "0x0000000000000000000000000000000000036873a6b1827ae041c9f878124611", + "0x0000000000000000000000000000008551970868f2312f14383645d8d416eaed", + "0x00000000000000000000000000000000002e77559cbe03609e8de54519f283a2", + "0x0000000000000000000000000000001c7ad8fd28af4fbade76635a19f61a120e", + "0x00000000000000000000000000000000001b7ac87b1d17d20809537ff1c2792f", + "0x00000000000000000000000000000090d43cdb7f8c76fa6c217c7e7034cd3a63", + "0x00000000000000000000000000000000002e4e1bfef78c05409d9eb37a4aeea6", + "0x0000000000000000000000000000006c0a7549b4f359b7ecc2836653557f0e41", + "0x000000000000000000000000000000000010f2d9bb59ec0bbc7044b089f2e3a1", + "0x0000000000000000000000000000004af36bb613905f59ffee63383525ed3ba3", + "0x0000000000000000000000000000000000174ddf49655c326312900dc95f6a77", "0x0000000000000000000000000000001eee81b23a887f299049b14c11e98460d6", "0x00000000000000000000000000000000002a56ce41f6b0be13b9c26747621b82", "0x000000000000000000000000000000d5827d6338c78656c0d12ca1aea6ef2c7c", "0x00000000000000000000000000000000001aa98f2de3ddda547d8f6de4e725de", - "0x00000000000000000000000000000066341bbf529c7fad5fd153574fb3432a8f", - "0x000000000000000000000000000000000027c63f98a4fdee70e388554a9ab618", - "0x0000000000000000000000000000001fbda67d83c4e81231645e5d101f7a0db4", - "0x0000000000000000000000000000000000081cde5879a1fa613d63078740afc0", + "0x0000000000000000000000000000009b7cd1de1691978893be0a72effb79eee6", + "0x0000000000000000000000000000000000220f51daff9561df94499921a4020b", + "0x000000000000000000000000000000f5902e979595fe35956fd67baba15afa55", + "0x000000000000000000000000000000000013d09d0d31724a524759b20e7a7482", "0x0000000000000000000000000000003363223748dc8017a1ae54572e8ced332d", "0x00000000000000000000000000000000000c2d58a8a6a41eaf8a3f64cd43d9b0", "0x0000000000000000000000000000004d11560c39b212646db44d9686f7aa1f35", @@ -1265,60 +1265,60 @@ proof = [ "0x00000000000000000000000000000000002dceef653c5027bea7843d9a6bfc54", "0x000000000000000000000000000000e21c32ae5098d40c042c9cf2470aa6a53a", "0x00000000000000000000000000000000001df0da2cd83af63341d4d8cf44f90c", - "0x00000000000000000000000000000030246fcb081b533b016cbfa1e0b0b05a13", - "0x00000000000000000000000000000000002196db705ebe475f1d8dccb7dc8730", - "0x0000000000000000000000000000008485fe1e85a07d6c18d492d1eade19b75c", - "0x0000000000000000000000000000000000249e0c56d91147a3e5e1e3f996de09", - "0x0000000000000000000000000000007253c016c13fa72f7cd495878794017219", - "0x00000000000000000000000000000000000b65b27fe0b43f1c3e78bc6f78e84b", - "0x000000000000000000000000000000a6f17961eebb65f14c95f23df208cb1a08", - "0x00000000000000000000000000000000001232e79e176f03f76f2b4fd3794c5d", - "0x000000000000000000000000000000791c73b853392645125c9ed94764ecc583", - "0x00000000000000000000000000000000001928a456d61ca3566ebae10c0ffe3d", - "0x0000000000000000000000000000001a300cd6759c5c57434afb2338e03fc49b", - "0x0000000000000000000000000000000000074e7dcd4c30965831f7725044785b", - "0x000000000000000000000000000000f5506ea763daf570fc1b1b0e2fef86b7f4", - "0x0000000000000000000000000000000000208d741e795af93bb75a6d9a65c744", - "0x000000000000000000000000000000ce0fdd5d95bd36bfddd41bfdb35ed2b4ac", - "0x00000000000000000000000000000000001b20086e22082a40dea6c8a17c0dc3", - "0x0000000000000000000000000000000b020a91ac91be6a9ef6ef62fa3e43ffad", - "0x000000000000000000000000000000000007c307e6826c80816c303770c00bac", - "0x000000000000000000000000000000ca42f28581b8bc12ee4dee8d7a95fb0365", - "0x000000000000000000000000000000000011bd6a10fe3676027e7c1dfb5bee35", - "0x000000000000000000000000000000d427f01ae0d5332a425f553a7d2734579e", - "0x000000000000000000000000000000000027d39fdc5cf531dd49896c6f9b035e", - "0x000000000000000000000000000000ef50b91ddf7325dc8d59c618580e6a9604", - "0x00000000000000000000000000000000002535248d9432873aef84fd8c7aed8c", - "0x000000000000000000000000000000691cee5b21e92439ea471c8cf8495739a8", - "0x00000000000000000000000000000000002a31221e52767a14c2557a1f2d1897", - "0x0000000000000000000000000000006f8e40382b35728a13cf57b61148ceae89", - "0x00000000000000000000000000000000001aadd0d2b7a94e8b13bf4a2244b348", - "0x000000000000000000000000000000b3257992bf1c6d06f760eb62ba85604f0f", - "0x00000000000000000000000000000000001dc0e2f8ab9f0e920d0a5f4ebb68fa", - "0x0000000000000000000000000000000f843955243dda5b9e342b0a9ee9fc6f41", - "0x00000000000000000000000000000000001ba988c0aee72bff791fb473e81ea0", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000003a1ffe2fe14a563eb282d7c9e010c620cc", - "0x000000000000000000000000000000000016319e7fb4892a891be566381e6cec", - "0x000000000000000000000000000000c29ff36064dcecccbfa0b3b44e10b364d2", - "0x0000000000000000000000000000000000056d6b442820853d0e58589275e761", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000007bac18c3e2c7f482de9d296a297fe988dd", - "0x000000000000000000000000000000000009514d0aebc1f6b7b2c1952ba5a7b1", - "0x00000000000000000000000000000095a339fe9509250fb46e3eb0795ae541ed", - "0x000000000000000000000000000000000016a9da14fcaebdef571c0e84ad59e4", - "0x0000000000000000000000000000001ce40f81ecd0b1b8390dfd2e7ac7e79072", - "0x00000000000000000000000000000000002a88c291b8e90c6ef2c083192d8cd9", - "0x00000000000000000000000000000021cdbc5c5d647ca1becc00ae13df36731a", - "0x0000000000000000000000000000000000300b3fb2ab5456dda5bc0eafa52e95" + "0x00000000000000000000000000000020543500a522dadc8ed396d1e6f4b1268b", + "0x00000000000000000000000000000000001eacd0bd44302d83219e3010b37b19", + "0x000000000000000000000000000000fab0d52766b135251b4181abbef77f067c", + "0x000000000000000000000000000000000015c5f3a1f9fd8b4211f4f235d38741", + "0x000000000000000000000000000000339902c9c623b37e9fad57509fd9aacbdd", + "0x00000000000000000000000000000000002bec57df803bc36abf7223d88ef21d", + "0x000000000000000000000000000000f824b1938c45d4302dd5f6877d61acad20", + "0x000000000000000000000000000000000008b7e31565a75dfa2ccca207c76a93", + "0x0000000000000000000000000000001468fcf9bb5413271623a3c096f74162f4", + "0x000000000000000000000000000000000024d710a0684cdc53db9ada4161189b", + "0x00000000000000000000000000000052188b11bcdd2bb3f5d12bad2c2108d9b4", + "0x000000000000000000000000000000000020c95dc4712754f07656ada01b09dd", + "0x00000000000000000000000000000030085b959125780f654a06db4e6efb913b", + "0x0000000000000000000000000000000000122db198aa277435a9e49af71d43be", + "0x000000000000000000000000000000d0f5cb068c1a73b525645bc1f8658c4136", + "0x000000000000000000000000000000000000edf43f6a852810d97499ad210959", + "0x0000000000000000000000000000005d313a64ee3975af0b767f5b7f0d96c079", + "0x00000000000000000000000000000000001da455ebbc0b9f765a887343817cdd", + "0x00000000000000000000000000000000b4a12a3c4e29a3070076988225d538d5", + "0x00000000000000000000000000000000000a335130a9ebae6f2151e4a0d50466", + "0x0000000000000000000000000000004ac831e9113742ba9dc9bc3420d177f355", + "0x00000000000000000000000000000000001187aecc95cba84ce09e111f86839a", + "0x00000000000000000000000000000001f24a9ff702d1696158c3f4f49af6fcef", + "0x000000000000000000000000000000000023eb372ddbf6f32cdabceea5f25418", + "0x000000000000000000000000000000349a66ecf7184e09234976672551d9b067", + "0x00000000000000000000000000000000002fa70c6323da30d9aadac8d11def2f", + "0x0000000000000000000000000000002a99c6066f46ad5e678ca5e9681f5d5900", + "0x00000000000000000000000000000000000d16adf72b3cbfbbad15ad764f6c1c", + "0x0000000000000000000000000000001d9fb5118d3e5f58079c2bf8bca553e879", + "0x00000000000000000000000000000000002d342e2493b6bc2d5201d722459b3a", + "0x00000000000000000000000000000086dc796a18b2d38c366165ab5de55fbd55", + "0x000000000000000000000000000000000013cac21a38efb3b9683ef818365d79", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x00000000000000000000000000000076e90358146fefc33347cdc89bb46404c9", + "0x000000000000000000000000000000000022c9188032ca089fd74216593b5e25", + "0x0000000000000000000000000000003af799d79849c6260fc9bee45d3b10d7d9", + "0x0000000000000000000000000000000000242d346da2ce4d4bb91cbc3b26a919", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x00000000000000000000000000000066870195dc71c07b939cb585c578e6cbc3", + "0x0000000000000000000000000000000000296d718516aeb29f910a6d4a468680", + "0x000000000000000000000000000000c76e5173b81ee794e54d1df5a4dfba5723", + "0x0000000000000000000000000000000000103e53a22fc0b47fad3d2cdc2b482d", + "0x000000000000000000000000000000252055494f51104dc247533dc7188c51e8", + "0x00000000000000000000000000000000000628b22cd5d29c14e2991fcdc1d268", + "0x0000000000000000000000000000000482a6c938afd45b58eb2a8e5c5efe4943", + "0x0000000000000000000000000000000000200a4c46a6cb2070a558beb3fba77c" ] - hash = "0x210246d873a17a21706c9dd14f001ac6fb33c3c5aa0fbb50fc4fec836f69f679" + hash = "0x2763158d71590235cedb6b42528c45cea97c34953ff01ee103ac217e575d9958" [inputs.hints] previous_archive_sibling_path = [ @@ -1362,7 +1362,7 @@ new_out_hash_sibling_path = [ ] blobs_fields = [ "0x00000000009c70751800400040000800010040040000000000000000000004cd", - "0x136b519d48f0d49bbd222b3d599abd3b6cbe415b3ab8ea1f13625b23421579c7", + "0x2db02fd65d8798289fbe4a8dad894566a82b765bbed0054a120dac3dd2090c39", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x00000000000000000000000000000000000000000000000000000000b7d1b100", "0x00000000000000000000000000000000000000000000000000000000b7d1b101", @@ -2591,7 +2591,7 @@ blobs_fields = [ "0x00000000000000000000000000000000000000000000000000000000b7d1b44d", "0x00000000000000000000000000000000000000000000000000000000b7d1b44e", "0x00000000009c707518004000400008004000400400000000000000000000054b", - "0x0a2e3c53fbd09fc43d1355a0fad5d3f1d7a886b0b82fb66ceb78f4a5faf61880", + "0x12b3324404b7f83002af382d3ce2e873a2cd25091226735ddf4c09d8458c644a", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x00000000000000000000000000000000000000000000000000000000b7e5c000", "0x00000000000000000000000000000000000000000000000000000000b7e5c001", @@ -25938,7 +25938,7 @@ blobs_fields = [ "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000" ] -blobs_hash = "0x000da5bd0bd391d71f3ee5d060fea5ca3e46218b28ca4d12c72d8c2cff5fee7e" +blobs_hash = "0x00531113277479cf32899e119363e61ff9ea80ee59cefec9177377b4d6e0d3f6" [inputs.hints.previous_block_header] sponge_blob_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" @@ -26025,13 +26025,13 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 ] [inputs.hints.final_blob_challenges] - z = "0x11d22738f56155d5a19c668fe8701d9c809f96d53c46e0a88970e6481ab68df4" + z = "0x286207660fa53035b4c5c05e71adaaedd8cdada9df34621c3f70e473c3250318" [inputs.hints.final_blob_challenges.gamma] limbs = [ - "0x38ebe790252c889a44010717a68c37", - "0xc0257e7db95b8b227f3e39d6139aea", - "0x2193" + "0xd5cb58fe803b11943c330499ae2f0d", + "0x5a4cdcc14064fc0f57ba18f212c644", + "0x0875" ] [[inputs.hints.blob_commitments]] @@ -26039,18 +26039,18 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 [inputs.hints.blob_commitments.x] limbs = [ - "0xf70cf83afc172d45e03e9f0e320d17", - "0x8996b5045c2c70ca03a0985fa6fbb3", - "0xedf99a2e4793301faf550d9afcdec3", - "0x10dcf4" + "0xabe21afbe58efea8015d3206608b1f", + "0x5f4c483a53108fd73cfdcc6aebed35", + "0xa9869c3384a64b66622873c610a3a5", + "0x11f5f0" ] [inputs.hints.blob_commitments.y] limbs = [ - "0x3520b7b92d97d1be8fc5f911b6dc55", - "0x5de9c5d32ef1dc3b021b12d2508c09", - "0xd0dd575ba591b5f4d1840e8e068f82", - "0x00f762" + "0x5b1437ff95d2879b53850427a9160a", + "0x475e3795313e04ab9680366d0e19bf", + "0xcde92c76fa707a5a657680604dd84a", + "0x17a6b1" ] [[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 7fe0933909a4..990f1b6fffd4 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 = "0x09f261f0f2c5b4862baf21d71b12c055f5a40913c343970df6774802c5289e70" + block_headers_hash = "0x1802383f96f2d83fbff3d337d52d3e1631a85e599a78884b2c5f5447780cfe38" 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 = "0x17f4111918fd7fd073ef3c7b0685a0b0f9de9c06978162bfb3284f1949143cfb" + vk_tree_root = "0x29b7bb0a371549fbde9a7c1967f9368c8b78f8d537b1c0a47626757643283c1d" protocol_contracts_hash = "0x14cec0ae6f31e913c3be04691c6d49c3ec07b3f2d024c9558693ea62fc530bbf" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" slot_number = "0x000000000000000000000000000000000000000000000000000000000000000f" @@ -512,7 +512,7 @@ proof = [ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000001" [inputs.previous_rollups.public_inputs.new_archive] - root = "0x2580befe564b92754738d41b1a5351a0bd83c1835b5e8fbc5a8de35f768dd932" + root = "0x0e3319a0c0b57d17e881c45b23ab5856cbca6fc7e89e8b2167d4bd53a9ff7eb3" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000003" [inputs.previous_rollups.public_inputs.start_state.l1_to_l2_message_tree] @@ -575,10 +575,10 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x0c123047b777079e8db5f669ddaf1ffa1c7780d56b21e29f35e4772ac0f6280c" ] state = [ - "0x0136ddb8e63e19ceb37cc1cb5f09183134b29903de9ac6125131d9e873e98d63", - "0x03710711ebe2fcd59ca990189104549239a66a6fa543ab59a939826c82d420b3", - "0x0fdc06c2da7f9f67e79fd441c4238710007f868ab6572dcd87752679876cc842", - "0x21a7758283bf47dee2eda50773f56a33945f3363cbad890b6efff037cbcf6cc2" + "0x13a8e2dc8bfd7d568d80ce9ffe4fb93fd7f8912cb0def4059ee2e5bf22e9eb36", + "0x2a4f0795668cecf5e702e3f80b7201d42c468229285628b33a4423ed261882e9", + "0x262e18d1e44aa9232ec6cd77fd1bb2176bd71e32f1e90e4018ca4a1181f0e9f5", + "0x1165a14aa0c3ef22de7bd8b0b53f2c74050f7961eec7a6d5e3ad9c5c3750f317" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000003" squeeze_mode = false @@ -628,7 +628,7 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x20f1c701d84b280c9f80a517272153688a9e1b92166d1000cfe7c829b7c25f69", "0x28acc2354e5ea51f25dd1a1e980d97c826400f67bc116d1b6187bdfe5726cb6a", "0x0db3ab63c7556f8724d86819156701855018cc79140a052893d67fb67c99c1f1", - "0x011ce78c298fc81d89c739267f5a1f69e22587103f51d97947d04724c545a848" + "0x08b0c1ac4553f1257a42dcf5a95191fc747ad0228ffc5c16e6244c4ed4858fea" ] [inputs.previous_rollups.vk_data.vk] @@ -1237,7 +1237,7 @@ proof = [ [inputs.previous_rollups.public_inputs] timestamp = "0x0000000000000000000000000000000000000000000000000000000000000186" - block_headers_hash = "0x2b094c247f59356cc7fb98089a530d2d319d960571e58743d76978f9a07de76d" + block_headers_hash = "0x2f00402059035136c8f2092afe1f77746aefd2d7660779c82e12ca995a4a9bbb" 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 = "0x17f4111918fd7fd073ef3c7b0685a0b0f9de9c06978162bfb3284f1949143cfb" + vk_tree_root = "0x29b7bb0a371549fbde9a7c1967f9368c8b78f8d537b1c0a47626757643283c1d" protocol_contracts_hash = "0x14cec0ae6f31e913c3be04691c6d49c3ec07b3f2d024c9558693ea62fc530bbf" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" slot_number = "0x000000000000000000000000000000000000000000000000000000000000000f" @@ -1261,11 +1261,11 @@ proof = [ fee_per_l2_gas = "0x0000000000000000000000000000000000000000000000000000000000000000" [inputs.previous_rollups.public_inputs.previous_archive] - root = "0x2580befe564b92754738d41b1a5351a0bd83c1835b5e8fbc5a8de35f768dd932" + root = "0x0e3319a0c0b57d17e881c45b23ab5856cbca6fc7e89e8b2167d4bd53a9ff7eb3" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000003" [inputs.previous_rollups.public_inputs.new_archive] - root = "0x010cceb7a8ca336670ed5ef20611e86025ee4a6dda419ea50ce8c4c245c66704" + root = "0x11b1ca422aad8b1de21a7ac59695dfba13ac966823999830b600a1492f3aef69" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000004" [inputs.previous_rollups.public_inputs.start_state.l1_to_l2_message_tree] @@ -1310,10 +1310,10 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x0c123047b777079e8db5f669ddaf1ffa1c7780d56b21e29f35e4772ac0f6280c" ] state = [ - "0x0136ddb8e63e19ceb37cc1cb5f09183134b29903de9ac6125131d9e873e98d63", - "0x03710711ebe2fcd59ca990189104549239a66a6fa543ab59a939826c82d420b3", - "0x0fdc06c2da7f9f67e79fd441c4238710007f868ab6572dcd87752679876cc842", - "0x21a7758283bf47dee2eda50773f56a33945f3363cbad890b6efff037cbcf6cc2" + "0x13a8e2dc8bfd7d568d80ce9ffe4fb93fd7f8912cb0def4059ee2e5bf22e9eb36", + "0x2a4f0795668cecf5e702e3f80b7201d42c468229285628b33a4423ed261882e9", + "0x262e18d1e44aa9232ec6cd77fd1bb2176bd71e32f1e90e4018ca4a1181f0e9f5", + "0x1165a14aa0c3ef22de7bd8b0b53f2c74050f7961eec7a6d5e3ad9c5c3750f317" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000003" squeeze_mode = false @@ -1328,10 +1328,10 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x0c123047b777079e8db5f669ddaf1ffa1c7780d56b21e29f35e4772ac0f6280c" ] state = [ - "0x19300a7dd46ae21dae1e0d77a0bac1020e0e8149cdbcbaaf8b7116bda1a581b4", - "0x0a6fde0688f5c1ca99abf6eb2a921ffe803e7672d3072896230b4e4cced44294", - "0x0fcda92a55aa71b1af013064f628ef2e82069e540a2a351b8f03b07cf2a6dc12", - "0x18b13daf6a4120af94b368346ed263d370c549ab761f890042c35ecffeae6e38" + "0x1dc6282b612fd72001ee45a7359338940133c2ccadea480c3fc0420cfe2871b3", + "0x1b9e546f516bb498d359dc4aee535eb3f8246f6d02b5a4be0b46efbb4d897d3d", + "0x057efef90486b51e280a17fc177bf76adde26d4921ba4971b7cc09198ba99686", + "0x0760af9c2ec2cff2d7ba4c2758547f38a5e2de3568c4090430f0dc53958c9efc" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000003" squeeze_mode = false @@ -1381,7 +1381,7 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x20f1c701d84b280c9f80a517272153688a9e1b92166d1000cfe7c829b7c25f69", "0x28acc2354e5ea51f25dd1a1e980d97c826400f67bc116d1b6187bdfe5726cb6a", "0x0db3ab63c7556f8724d86819156701855018cc79140a052893d67fb67c99c1f1", - "0x011ce78c298fc81d89c739267f5a1f69e22587103f51d97947d04724c545a848" + "0x08b0c1ac4553f1257a42dcf5a95191fc747ad0228ffc5c16e6244c4ed4858fea" ] [inputs.previous_rollups.vk_data.vk] @@ -1920,7 +1920,7 @@ proof = [ [inputs.inbox_parity.public_inputs] start_rolling_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" - end_rolling_hash = "0x00087fcd5a2ee0849c3801915ea5ebdc9b1934b7f233157fafb558f693a706a4" + end_rolling_hash = "0x006d131248894be46de839138bff160c07ff1727a08d40d3566dfadae7de82f3" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" [inputs.inbox_parity.public_inputs.end_sponge] @@ -1944,9 +1944,9 @@ proof = [ [inputs.inbox_parity.vk_data] leaf_index = "0x000000000000000000000000000000000000000000000000000000000000004b" sibling_path = [ - "0x1abd1a8458c08a3a574d06a9b202d9581cb4d3b2c2cd2b387dab0bf3d35342e5", + "0x0cd41d6e5b8b30ed20ce983ed5597482bdd5a3a71bea308d77d6a570fb566a25", "0x1d438b6650ee2b4a994c0eec98be4e2fd6f843225ad6fb72d292d652cfe1aa2b", - "0x21e0cbd22d0a0b2139090bb11909329af47df46c4a4a93a32582c8a11e28ad5e", + "0x0a1834c236ff39a85ada2b2e0d720a1f39737fb84582978fa3233ec2504127bc", "0x21ed90cd61698dc1e3dfc970eefcedc837dfedf46c2c4e594ce076ee8fba9386", "0x1e20ad4181460cbfdc74ca773502c59b890f184efe300ebad895956d318422da", "0x1434e6e2d5db1053ab8a3be58704509c799ee17e109c77f441f7bf1755400249", @@ -1958,46 +1958,46 @@ proof = [ "0x0000000000000000000000000000000000000000000000000000000000000016", "0x0000000000000000000000000000000000000000000000000000000000000015", "0x0000000000000000000000000000000000000000000000000000000000000005", - "0x00000000000000000000000000000047930989a1435aefabec7a53da1295a7ae", - "0x00000000000000000000000000000000002793641df42a1dbd2da6c51b4f328c", - "0x0000000000000000000000000000003d537d131b889b164336ba29b0b06f8bad", - "0x0000000000000000000000000000000000002772f48879c267496bae5be69d5d", - "0x000000000000000000000000000000d48cedfb66ccfff6fafacf2cb170e9f0c4", - "0x00000000000000000000000000000000002a32aff6611eb90b640610d9308ae4", - "0x000000000000000000000000000000b4778b04d31485f324bf78414f15d9d0b0", - "0x000000000000000000000000000000000012ac9ac58b356133025d524c776da8", - "0x0000000000000000000000000000002cd2dc479b1f12b0c178c1dceeeff9c14e", - "0x00000000000000000000000000000000001dd1c1b32b7f68f4b08c7793d4b860", - "0x0000000000000000000000000000004333ad5d0df7f193023d36b6cb7c32f7b8", - "0x000000000000000000000000000000000023b7011f3244cd75e65704ebc59b53", - "0x0000000000000000000000000000008cb2ff51d8094334792b19939fb966bd4f", - "0x0000000000000000000000000000000000100cba9a83d12d01b0a0961e2ebb9f", - "0x0000000000000000000000000000002e9f81b955250b17dfc5d64e5a8a994233", - "0x00000000000000000000000000000000000dcd68306bffac0b80e9f3d78ed39f", - "0x000000000000000000000000000000ae4c0d6b427a1b5f0d5040101b3f13b00a", - "0x000000000000000000000000000000000024ab486469085fe5f4666026a8128e", - "0x00000000000000000000000000000092f58cf4788881f7166be79949512095c7", - "0x0000000000000000000000000000000000009c381ea4e47d4837f1d8f2864353", - "0x000000000000000000000000000000614c3aa118f4fd86815221782e946f5432", - "0x00000000000000000000000000000000002b2eb2a4b0c175d5c5d7bf156d041c", - "0x0000000000000000000000000000005567dc26b8ba93db0c259c137705613cfc", - "0x000000000000000000000000000000000013eb1d0eec54f88b2aee69897359dc", - "0x000000000000000000000000000000e5c7d897e65de199b4d392734a689e88f7", - "0x000000000000000000000000000000000002da963311d2014a98ce75cc1d9936", - "0x000000000000000000000000000000de90e6ba417fc3520216444617c88c71d2", - "0x00000000000000000000000000000000000fcde67d681340bf4a41a558e2645c", - "0x00000000000000000000000000000095d9e42f1c702aef317e157f548a943557", - "0x000000000000000000000000000000000014f0071ef4953864274c855f2c7383", - "0x0000000000000000000000000000007cdc4528c7dd164b9102b61ff9e5c9a8da", - "0x0000000000000000000000000000000000104c978c445466aca1d0cbcb3d4311", + "0x000000000000000000000000000000e7c17b68bae36db64bdde5e0eb6f3f9bd4", + "0x00000000000000000000000000000000000593a32f6c85695b30ea73366c227c", + "0x00000000000000000000000000000026bd48770d9caddc256e8501c4de229a23", + "0x00000000000000000000000000000000002728effa00f22475b1a098f3bce712", + "0x000000000000000000000000000000d01e7d20b5ebedd296fb0791f648f409a8", + "0x0000000000000000000000000000000000012e5fb9bbf45a25da0d9bbfd84141", + "0x00000000000000000000000000000019639dace67ee351bb798c77450001385f", + "0x00000000000000000000000000000000001f6ea37a0daf717dcd99a339724de0", + "0x0000000000000000000000000000003c2b69238d205aa18f1eebd1c060e3f555", + "0x00000000000000000000000000000000002e941b87544b8ff5b0988922f0c300", + "0x000000000000000000000000000000c808c0aeed958bc35dd8b11b171215e27d", + "0x00000000000000000000000000000000000da79baa9c3b4292c26f6895325f1d", + "0x000000000000000000000000000000dcab1cc6e80b3dd9c8b27d1aa6def696c1", + "0x00000000000000000000000000000000002dc2aa37956e715c057981354492c1", + "0x0000000000000000000000000000001271a00a75bbe7c6321bd8a69177a6e956", + "0x0000000000000000000000000000000000257c575dff984c414cb9646ae71419", + "0x000000000000000000000000000000d0a8f121c3861c92ed6bb8e14a4f50ad4d", + "0x00000000000000000000000000000000000a7054c838e4f845b62eb24a85fce0", + "0x000000000000000000000000000000425b572d1bcffb8107a40573ddc9e3bfa9", + "0x000000000000000000000000000000000025e3e1bc0c49e5b6cae83e8b8f11c0", + "0x00000000000000000000000000000032a44a22c00d7dcd8e699e2b9a8ad2f867", + "0x0000000000000000000000000000000000036873a6b1827ae041c9f878124611", + "0x0000000000000000000000000000008551970868f2312f14383645d8d416eaed", + "0x00000000000000000000000000000000002e77559cbe03609e8de54519f283a2", + "0x0000000000000000000000000000001c7ad8fd28af4fbade76635a19f61a120e", + "0x00000000000000000000000000000000001b7ac87b1d17d20809537ff1c2792f", + "0x00000000000000000000000000000090d43cdb7f8c76fa6c217c7e7034cd3a63", + "0x00000000000000000000000000000000002e4e1bfef78c05409d9eb37a4aeea6", + "0x0000000000000000000000000000006c0a7549b4f359b7ecc2836653557f0e41", + "0x000000000000000000000000000000000010f2d9bb59ec0bbc7044b089f2e3a1", + "0x0000000000000000000000000000004af36bb613905f59ffee63383525ed3ba3", + "0x0000000000000000000000000000000000174ddf49655c326312900dc95f6a77", "0x0000000000000000000000000000001eee81b23a887f299049b14c11e98460d6", "0x00000000000000000000000000000000002a56ce41f6b0be13b9c26747621b82", "0x000000000000000000000000000000d5827d6338c78656c0d12ca1aea6ef2c7c", "0x00000000000000000000000000000000001aa98f2de3ddda547d8f6de4e725de", - "0x00000000000000000000000000000066341bbf529c7fad5fd153574fb3432a8f", - "0x000000000000000000000000000000000027c63f98a4fdee70e388554a9ab618", - "0x0000000000000000000000000000001fbda67d83c4e81231645e5d101f7a0db4", - "0x0000000000000000000000000000000000081cde5879a1fa613d63078740afc0", + "0x0000000000000000000000000000009b7cd1de1691978893be0a72effb79eee6", + "0x0000000000000000000000000000000000220f51daff9561df94499921a4020b", + "0x000000000000000000000000000000f5902e979595fe35956fd67baba15afa55", + "0x000000000000000000000000000000000013d09d0d31724a524759b20e7a7482", "0x0000000000000000000000000000003363223748dc8017a1ae54572e8ced332d", "0x00000000000000000000000000000000000c2d58a8a6a41eaf8a3f64cd43d9b0", "0x0000000000000000000000000000004d11560c39b212646db44d9686f7aa1f35", @@ -2018,60 +2018,60 @@ proof = [ "0x00000000000000000000000000000000002dceef653c5027bea7843d9a6bfc54", "0x000000000000000000000000000000e21c32ae5098d40c042c9cf2470aa6a53a", "0x00000000000000000000000000000000001df0da2cd83af63341d4d8cf44f90c", - "0x00000000000000000000000000000030246fcb081b533b016cbfa1e0b0b05a13", - "0x00000000000000000000000000000000002196db705ebe475f1d8dccb7dc8730", - "0x0000000000000000000000000000008485fe1e85a07d6c18d492d1eade19b75c", - "0x0000000000000000000000000000000000249e0c56d91147a3e5e1e3f996de09", - "0x0000000000000000000000000000007253c016c13fa72f7cd495878794017219", - "0x00000000000000000000000000000000000b65b27fe0b43f1c3e78bc6f78e84b", - "0x000000000000000000000000000000a6f17961eebb65f14c95f23df208cb1a08", - "0x00000000000000000000000000000000001232e79e176f03f76f2b4fd3794c5d", - "0x000000000000000000000000000000791c73b853392645125c9ed94764ecc583", - "0x00000000000000000000000000000000001928a456d61ca3566ebae10c0ffe3d", - "0x0000000000000000000000000000001a300cd6759c5c57434afb2338e03fc49b", - "0x0000000000000000000000000000000000074e7dcd4c30965831f7725044785b", - "0x000000000000000000000000000000f5506ea763daf570fc1b1b0e2fef86b7f4", - "0x0000000000000000000000000000000000208d741e795af93bb75a6d9a65c744", - "0x000000000000000000000000000000ce0fdd5d95bd36bfddd41bfdb35ed2b4ac", - "0x00000000000000000000000000000000001b20086e22082a40dea6c8a17c0dc3", - "0x0000000000000000000000000000000b020a91ac91be6a9ef6ef62fa3e43ffad", - "0x000000000000000000000000000000000007c307e6826c80816c303770c00bac", - "0x000000000000000000000000000000ca42f28581b8bc12ee4dee8d7a95fb0365", - "0x000000000000000000000000000000000011bd6a10fe3676027e7c1dfb5bee35", - "0x000000000000000000000000000000d427f01ae0d5332a425f553a7d2734579e", - "0x000000000000000000000000000000000027d39fdc5cf531dd49896c6f9b035e", - "0x000000000000000000000000000000ef50b91ddf7325dc8d59c618580e6a9604", - "0x00000000000000000000000000000000002535248d9432873aef84fd8c7aed8c", - "0x000000000000000000000000000000691cee5b21e92439ea471c8cf8495739a8", - "0x00000000000000000000000000000000002a31221e52767a14c2557a1f2d1897", - "0x0000000000000000000000000000006f8e40382b35728a13cf57b61148ceae89", - "0x00000000000000000000000000000000001aadd0d2b7a94e8b13bf4a2244b348", - "0x000000000000000000000000000000b3257992bf1c6d06f760eb62ba85604f0f", - "0x00000000000000000000000000000000001dc0e2f8ab9f0e920d0a5f4ebb68fa", - "0x0000000000000000000000000000000f843955243dda5b9e342b0a9ee9fc6f41", - "0x00000000000000000000000000000000001ba988c0aee72bff791fb473e81ea0", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000003a1ffe2fe14a563eb282d7c9e010c620cc", - "0x000000000000000000000000000000000016319e7fb4892a891be566381e6cec", - "0x000000000000000000000000000000c29ff36064dcecccbfa0b3b44e10b364d2", - "0x0000000000000000000000000000000000056d6b442820853d0e58589275e761", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000007bac18c3e2c7f482de9d296a297fe988dd", - "0x000000000000000000000000000000000009514d0aebc1f6b7b2c1952ba5a7b1", - "0x00000000000000000000000000000095a339fe9509250fb46e3eb0795ae541ed", - "0x000000000000000000000000000000000016a9da14fcaebdef571c0e84ad59e4", - "0x0000000000000000000000000000001ce40f81ecd0b1b8390dfd2e7ac7e79072", - "0x00000000000000000000000000000000002a88c291b8e90c6ef2c083192d8cd9", - "0x00000000000000000000000000000021cdbc5c5d647ca1becc00ae13df36731a", - "0x0000000000000000000000000000000000300b3fb2ab5456dda5bc0eafa52e95" + "0x00000000000000000000000000000020543500a522dadc8ed396d1e6f4b1268b", + "0x00000000000000000000000000000000001eacd0bd44302d83219e3010b37b19", + "0x000000000000000000000000000000fab0d52766b135251b4181abbef77f067c", + "0x000000000000000000000000000000000015c5f3a1f9fd8b4211f4f235d38741", + "0x000000000000000000000000000000339902c9c623b37e9fad57509fd9aacbdd", + "0x00000000000000000000000000000000002bec57df803bc36abf7223d88ef21d", + "0x000000000000000000000000000000f824b1938c45d4302dd5f6877d61acad20", + "0x000000000000000000000000000000000008b7e31565a75dfa2ccca207c76a93", + "0x0000000000000000000000000000001468fcf9bb5413271623a3c096f74162f4", + "0x000000000000000000000000000000000024d710a0684cdc53db9ada4161189b", + "0x00000000000000000000000000000052188b11bcdd2bb3f5d12bad2c2108d9b4", + "0x000000000000000000000000000000000020c95dc4712754f07656ada01b09dd", + "0x00000000000000000000000000000030085b959125780f654a06db4e6efb913b", + "0x0000000000000000000000000000000000122db198aa277435a9e49af71d43be", + "0x000000000000000000000000000000d0f5cb068c1a73b525645bc1f8658c4136", + "0x000000000000000000000000000000000000edf43f6a852810d97499ad210959", + "0x0000000000000000000000000000005d313a64ee3975af0b767f5b7f0d96c079", + "0x00000000000000000000000000000000001da455ebbc0b9f765a887343817cdd", + "0x00000000000000000000000000000000b4a12a3c4e29a3070076988225d538d5", + "0x00000000000000000000000000000000000a335130a9ebae6f2151e4a0d50466", + "0x0000000000000000000000000000004ac831e9113742ba9dc9bc3420d177f355", + "0x00000000000000000000000000000000001187aecc95cba84ce09e111f86839a", + "0x00000000000000000000000000000001f24a9ff702d1696158c3f4f49af6fcef", + "0x000000000000000000000000000000000023eb372ddbf6f32cdabceea5f25418", + "0x000000000000000000000000000000349a66ecf7184e09234976672551d9b067", + "0x00000000000000000000000000000000002fa70c6323da30d9aadac8d11def2f", + "0x0000000000000000000000000000002a99c6066f46ad5e678ca5e9681f5d5900", + "0x00000000000000000000000000000000000d16adf72b3cbfbbad15ad764f6c1c", + "0x0000000000000000000000000000001d9fb5118d3e5f58079c2bf8bca553e879", + "0x00000000000000000000000000000000002d342e2493b6bc2d5201d722459b3a", + "0x00000000000000000000000000000086dc796a18b2d38c366165ab5de55fbd55", + "0x000000000000000000000000000000000013cac21a38efb3b9683ef818365d79", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x00000000000000000000000000000076e90358146fefc33347cdc89bb46404c9", + "0x000000000000000000000000000000000022c9188032ca089fd74216593b5e25", + "0x0000000000000000000000000000003af799d79849c6260fc9bee45d3b10d7d9", + "0x0000000000000000000000000000000000242d346da2ce4d4bb91cbc3b26a919", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x00000000000000000000000000000066870195dc71c07b939cb585c578e6cbc3", + "0x0000000000000000000000000000000000296d718516aeb29f910a6d4a468680", + "0x000000000000000000000000000000c76e5173b81ee794e54d1df5a4dfba5723", + "0x0000000000000000000000000000000000103e53a22fc0b47fad3d2cdc2b482d", + "0x000000000000000000000000000000252055494f51104dc247533dc7188c51e8", + "0x00000000000000000000000000000000000628b22cd5d29c14e2991fcdc1d268", + "0x0000000000000000000000000000000482a6c938afd45b58eb2a8e5c5efe4943", + "0x0000000000000000000000000000000000200a4c46a6cb2070a558beb3fba77c" ] - hash = "0x210246d873a17a21706c9dd14f001ac6fb33c3c5aa0fbb50fc4fec836f69f679" + hash = "0x2763158d71590235cedb6b42528c45cea97c34953ff01ee103ac217e575d9958" [inputs.hints] previous_archive_sibling_path = [ @@ -2115,7 +2115,7 @@ new_out_hash_sibling_path = [ ] blobs_fields = [ "0x00000000009c70751800400040000800010040040000000000000000000004cd", - "0x23528136990a2f1a9d14b3ff19e7e952c9111e0788b45ce44b0c2a59c35d237a", + "0x0159142e031f4f062faad95da399c0679e1a8970cf8c532c7a0207e4ae69ecdd", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x00000000000000000000000000000000000000000000000000000000b7d1b100", "0x00000000000000000000000000000000000000000000000000000000b7d1b101", @@ -3351,7 +3351,7 @@ blobs_fields = [ "0x1a90881964e28a92a419f1d8361c14ac147b6f9175c04fdf57dadf0d7ba781c9", "0x0c123047b777079e8db5f669ddaf1ffa1c7780d56b21e29f35e4772ac0f6280c", "0x00000000009c707518004000400008004000400400000000000000000000054b", - "0x09250f47dce0a670e30edd470fc56be11dbd666fc3ee53b5529ef58eae8ebaff", + "0x0e6418e40abd83763034598be534147ff7bad9c43bbfdaf37bc5918bce867534", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x00000000000000000000000000000000000000000000000000000000b7e5c000", "0x00000000000000000000000000000000000000000000000000000000b7e5c001", @@ -4707,13 +4707,13 @@ blobs_fields = [ "0x00000000000000000000000000000000000000000000000000000000b7e5c34e", "0x0000000000000000000000000000eb8dcdbf0000000000000186000000020001", "0x00000000000000000010000000000200000000010000000000bf000000000000", - "0x1e97c93cb4174d45a8a02061726c03e9a1443d5dc3c72e92307f766f85718e22", + "0x0f9544cbd0e96c102033eb1b7e3767a4ab5d0fdfcd8453af3e55f1517e871f7c", "0x2326bf220c6839c1856478f0c082f0c5883b2baed0bc222a1fa5e1244184c82b", "0x1e1c597744057b88e39a9780ed087c39b1fc42864e05ef03a59ebd9e96b70b00", "0x2306af8b455a9cbf87331182183be8c0759fd5e1a4f606cea8a9e24efa461759", "0x0c123047b777079e8db5f669ddaf1ffa1c7780d56b21e29f35e4772ac0f6280c", "0x00000000009c70751800400040000800010040040000000000000000000004cd", - "0x29c72e9f4308f95ef317d486630f6777c23fec373dc191a4f7f56309241abea0", + "0x0be22206d680db690db696e4b63fea18b645d390c787a60cddbd694398da08f3", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x00000000000000000000000000000000000000000000000000000000b7f9d100", "0x00000000000000000000000000000000000000000000000000000000b7f9d101", @@ -5943,7 +5943,7 @@ blobs_fields = [ "0x00000000000000000000000000000000000000000000000000000000b7f9d44e", "0x0000000000000000000000000000eb8dcdbf0000000000000186000000030001", "0x00000000000000000010000000000300000000014000000000bf00000006b6c0", - "0x2580befe564b92754738d41b1a5351a0bd83c1835b5e8fbc5a8de35f768dd932", + "0x0e3319a0c0b57d17e881c45b23ab5856cbca6fc7e89e8b2167d4bd53a9ff7eb3", "0x144f9224dee4aac6eddc5d988e7c6965528d2e08db91cf58989655a68fbfcc52", "0x191d19a6ad2b7bba03d122035938544f5e65de24aeaa436cd5e4d977bd014505", "0x2306af8b455a9cbf87331182183be8c0759fd5e1a4f606cea8a9e24efa461759", @@ -26691,7 +26691,7 @@ blobs_fields = [ "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000" ] -blobs_hash = "0x00257c2f936bb9a42b779d49806216ca208e762a739dcfa039e50d122a3f2095" +blobs_hash = "0x00537f47e5bf3781334c22b0b81eba0b72ff6712317c80f859a993157af28ac9" [inputs.hints.previous_block_header] sponge_blob_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" @@ -26778,13 +26778,13 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 ] [inputs.hints.final_blob_challenges] - z = "0x26f697daf2f849f89e9a43e58fe5ecfdab371b84b1a3ab59982dcdb27133068f" + z = "0x03121c85b92986d276333f0c43027d749f3b3e5a3a1a0d1772443e30509c5a9f" [inputs.hints.final_blob_challenges.gamma] limbs = [ - "0x5ce7083c366aa119b408c8740d67be", - "0xe10870f3b17401b41f3dc305fdd736", - "0x29c0" + "0x02bf59f0c8692e762fe259566e467e", + "0x3904c7afe5fe08f74ad8721f65e55c", + "0x2314" ] [[inputs.hints.blob_commitments]] @@ -26792,18 +26792,18 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 [inputs.hints.blob_commitments.x] limbs = [ - "0x3508b75d951a7eefbd96b656a3fe04", - "0xd24363d94ea8e3bcc6b3329b2e50ed", - "0xcdb2c26201e1c2c06a5d61b85bdf6f", - "0x108b79" + "0x320f1670b142d01fbbb6e2e09caf15", + "0x58771cc2ebf4d09e457541691e42b4", + "0x71c6f6553330669493228ee11467b0", + "0x1533a8" ] [inputs.hints.blob_commitments.y] limbs = [ - "0xa210f43dd6ed3c8fba7f75249e022a", - "0x295e5bc00122b57bc0faa479e1e009", - "0x2a1e10ea9dce52c87e8f9963836971", - "0x121277" + "0xebe36538f481f5d41329379138b5a4", + "0x89f23cbf865edf4c752a9d5a3208ff", + "0x90e08694cbfea879fa248897833d3e", + "0x0fba7d" ] [[inputs.hints.blob_commitments]] diff --git a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/abis/parity_public_inputs.nr b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/abis/parity_public_inputs.nr index faa0af38d822..3af68836a7cd 100644 --- a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/abis/parity_public_inputs.nr +++ b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/abis/parity_public_inputs.nr @@ -6,8 +6,9 @@ pub struct ParityPublicInputs { // Rolling hash of the Inbox message chain before absorbing this checkpoint's messages. pub start_rolling_hash: Field, // Rolling hash of the Inbox message chain after absorbing the checkpoint's real messages. Each link is - // `accumulate_sha256_with_separator(DOM_SEP__INBOX_ROLLING_HASH, prev, msg)`, matching the domain-separated - // truncated-to-field sha256 the L1 Inbox accumulates. + // `accumulate_sha256_with_separator(sep, prev, msg)`, matching the domain-separated truncated-to-field sha256 the + // L1 Inbox accumulates, with `sep` being `DOM_SEP__INBOX_ROLLING_HASH_BUCKET_START` for the first message of an + // L1 bucket and `DOM_SEP__INBOX_ROLLING_HASH` for every other message. pub end_rolling_hash: Field, // Message-bundle sponge after absorbing the same real messages into the empty sponge the circuit starts from. The // checkpoint root asserts this equals the sponge accumulated across the checkpoint's block roots, tying the diff --git a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/inbox_rolling_hash.nr b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/inbox_rolling_hash.nr index abdd5aeb09e6..5f40ca123f03 100644 --- a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/inbox_rolling_hash.nr +++ b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/inbox_rolling_hash.nr @@ -1,11 +1,16 @@ -use types::{constants::DOM_SEP__INBOX_ROLLING_HASH, hash::accumulate_sha256_with_separator}; +use types::{ + constants::{DOM_SEP__INBOX_ROLLING_HASH, DOM_SEP__INBOX_ROLLING_HASH_BUCKET_START}, + hash::accumulate_sha256_with_separator, +}; /// Extends the Inbox rolling sha256 chain by the first `num_leaves` of `leaves`, returning the new rolling hash. /// -/// Each link is `h' = sha256ToField(DOM_SEP__INBOX_ROLLING_HASH || h || leaf)` over the 4-byte big-endian separator -/// followed by the two 32-byte big-endian values, matching the truncated-to-field sha256 the L1 `Inbox` accumulates. -/// The separator distinguishes a chain link from the untagged `out_hash` merkle node hash, which absorbs the same -/// two-field preimage shape. The genesis rolling hash is zero. +/// Each link is `h' = sha256ToField(sep || h || leaf)` over the 4-byte big-endian separator followed by the two +/// 32-byte big-endian values, matching the truncated-to-field sha256 the L1 `Inbox` accumulates. The separator is +/// `DOM_SEP__INBOX_ROLLING_HASH_BUCKET_START` when the leaf is the first message of an L1 bucket and +/// `DOM_SEP__INBOX_ROLLING_HASH` otherwise, so the chain itself commits to where each bucket begins. Both separators +/// distinguish a chain link from the untagged `out_hash` merkle node hash, which absorbs the same two-field preimage +/// shape. The genesis rolling hash is zero. /// /// The function takes a `start` and returns an end with no assumption about chunk position, so it chains sequentially /// across chunked circuits: a segment's start hash is the previous segment's end. Lanes beyond `num_leaves` are never @@ -13,6 +18,7 @@ use types::{constants::DOM_SEP__INBOX_ROLLING_HASH, hash::accumulate_sha256_with pub fn accumulate_inbox_rolling_hash( start: Field, leaves: [Field; N], + bucket_starts: [bool; N], num_leaves: u32, ) -> Field { assert(num_leaves <= N, "num_leaves is greater than the leaves array length"); @@ -20,7 +26,12 @@ pub fn accumulate_inbox_rolling_hash( let mut acc = start; for i in 0..N { if i < num_leaves { - acc = accumulate_sha256_with_separator(DOM_SEP__INBOX_ROLLING_HASH, acc, leaves[i]); + let separator = if bucket_starts[i] { + DOM_SEP__INBOX_ROLLING_HASH_BUCKET_START + } else { + DOM_SEP__INBOX_ROLLING_HASH + }; + acc = accumulate_sha256_with_separator(separator, acc, leaves[i]); } } acc @@ -29,78 +40,145 @@ pub fn accumulate_inbox_rolling_hash( mod tests { use super::accumulate_inbox_rolling_hash; use types::{ - constants::{DOM_SEP__INBOX_ROLLING_HASH, INBOX_PARITY_SIZE_MEDIUM}, + constants::{DOM_SEP__INBOX_ROLLING_HASH_BUCKET_START, INBOX_PARITY_SIZE_MEDIUM}, hash::{accumulate_sha256, accumulate_sha256_with_separator}, }; + // The pinned hashes below are derived independently of every implementation of this chain by + // `l1-contracts/scripts/inbox_rolling_hash_vectors.py` (plain `hashlib`); the same values are pinned in the L1 + // and TypeScript tests. + + // Flags for a chain whose first leaf opens a bucket and whose remaining leaves continue it: the shape of a + // checkpoint consuming a single contiguous run out of one bucket. + fn only_first_is_bucket_start() -> [bool; N] { + let mut bucket_starts = [false; N]; + bucket_starts[0] = true; + bucket_starts + } + #[test] fn empty_bundle_passes_start_through() { - assert_eq(accumulate_inbox_rolling_hash(0, [0; 8], 0), 0); + assert_eq(accumulate_inbox_rolling_hash(0, [0; 8], [false; 8], 0), 0); // A non-zero start with no leaves is returned unchanged (segment threading base case). - assert_eq(accumulate_inbox_rolling_hash(0x2a, [11, 22, 33], 0), 0x2a); + assert_eq(accumulate_inbox_rolling_hash(0x2a, [11, 22, 33], [false; 3], 0), 0x2a); } #[test] fn single_leaf_matches_reference() { - // Independent sha256 reference (see PR description): sha256ToField(0xdec16509 || 0x00..00 || 0x00..0b), last - // byte dropped. - let expected = 0x00066dfa22681f66d50aae7d84f190e3555d2d82e4a5e33c2291c3060d441f04; - assert_eq(accumulate_inbox_rolling_hash(0, [11, 0, 0, 0], 1), expected); - assert_eq(accumulate_sha256_with_separator(DOM_SEP__INBOX_ROLLING_HASH, 0, 11), expected); + // sha256ToField(u32_be(DOM_SEP__INBOX_ROLLING_HASH_BUCKET_START) || 0x00..00 || 0x00..0b), last byte dropped. + let expected = 0x00551b59fed79dcce036e55050cf38ef367abfec03557e234866ac023879b245; + assert_eq( + accumulate_inbox_rolling_hash(0, [11, 0, 0, 0], only_first_is_bucket_start(), 1), + expected, + ); + assert_eq( + accumulate_sha256_with_separator(DOM_SEP__INBOX_ROLLING_HASH_BUCKET_START, 0, 11), + expected, + ); } #[test] fn three_leaves_matches_reference() { - let expected = 0x0077423b713a725ce4bf0b792847c68da87c316d52921de25652756bfe4c3e81; - assert_eq(accumulate_inbox_rolling_hash(0, [11, 22, 33, 0, 0], 3), expected); + let expected = 0x00e6cba8a055d279f8568edc4d0969a107fcda0c48347afdfd3dfeb053aa22c7; + assert_eq( + accumulate_inbox_rolling_hash(0, [11, 22, 33, 0, 0], only_first_is_bucket_start(), 3), + expected, + ); } #[test] fn batch_matches_reference() { - // 256 leaves valued 1..=256 from a zero start. + // 256 leaves valued 1..=256 from a zero start, all in a single bucket. let mut leaves = [0; INBOX_PARITY_SIZE_MEDIUM]; for i in 0..INBOX_PARITY_SIZE_MEDIUM { leaves[i] = (i + 1) as Field; } - let expected = 0x0030493fcb5915459bba42f03f283b58dfaa082dac02fbb3a494d5db8063238b; - assert_eq(accumulate_inbox_rolling_hash(0, leaves, INBOX_PARITY_SIZE_MEDIUM), expected); + let expected = 0x009ff152cad9525e1c092ae6d4fb390149de5599eac09b76b0ebd1c6e26bb504; + assert_eq( + accumulate_inbox_rolling_hash( + 0, + leaves, + only_first_is_bucket_start(), + INBOX_PARITY_SIZE_MEDIUM, + ), + expected, + ); } #[test] fn non_zero_start_matches_reference() { - let expected = 0x00a64d14c4b0234f5d835dc202bf8f9a857bc0734baf281dccd4b4978a48b2f9; - assert_eq(accumulate_inbox_rolling_hash(0x2a, [7, 8], 2), expected); + let after_first = 0x00f13cb848052a7ab6f1de788a5979f5a5caa8c11cf176715d63481618e3b575; + assert_eq( + accumulate_inbox_rolling_hash(0x2a, [7], only_first_is_bucket_start(), 1), + after_first, + ); + + let expected = 0x00d84d0b60599b1c7380a723d84310d40efaa4f5673dd62e0af41b03bc9a07a6; + assert_eq( + accumulate_inbox_rolling_hash(0x2a, [7, 8], only_first_is_bucket_start(), 2), + expected, + ); + } + + #[test] + fn bucket_start_flag_changes_hash() { + // Flipping a middle lane's bucket-start bit selects the other separator for that link, so the chain differs + // even though the leaves are identical: regrouping the same messages into different buckets is detectable. + let leaves = [11, 22, 33, 44]; + let one_bucket: [bool; 4] = only_first_is_bucket_start(); + let mut split_at_third = one_bucket; + split_at_third[2] = true; + + let expected_one_bucket = + 0x00e37b7cc5526ab379c54209bc1c6a4ba2c457d024330281b97a533561701551; + let expected_two_buckets = + 0x00fa0346e7c4ee1bdf29a48af28182fdc236e2936e4d0c2e951dbd4b9b6464fc; + assert_eq(accumulate_inbox_rolling_hash(0, leaves, one_bucket, 4), expected_one_bucket); + assert_eq( + accumulate_inbox_rolling_hash(0, leaves, split_at_third, 4), + expected_two_buckets, + ); + assert(expected_one_bucket != expected_two_buckets); } #[test] fn separator_distinguishes_link_from_untagged_hash() { - // A link must not coincide with an untagged two-field sha256 over the same values, such as an `out_hash` - // merkle node. - assert(accumulate_inbox_rolling_hash(0, [11], 1) != accumulate_sha256(0, 11)); + // Neither separator may let a link coincide with an untagged two-field sha256 over the same values, such as + // an `out_hash` merkle node. + assert( + accumulate_inbox_rolling_hash(0, [11], only_first_is_bucket_start(), 1) + != accumulate_sha256(0, 11), + ); + assert(accumulate_inbox_rolling_hash(0, [11], [false], 1) != accumulate_sha256(0, 11)); } #[test] fn chain_is_continuous_across_segments() { - // Chaining [7,8,9] from a start equals chaining [7] then [8,9] threaded by the intermediate hash. + // Chaining [7,8,9] from a start equals chaining [7] then [8,9] threaded by the intermediate hash, with the + // bucket-start flags following the leaves across the split. let start = 0x2a; - let full = accumulate_inbox_rolling_hash(start, [7, 8, 9, 0, 0], 3); + let full = + accumulate_inbox_rolling_hash(start, [7, 8, 9, 0, 0], only_first_is_bucket_start(), 3); - let mid = accumulate_inbox_rolling_hash(start, [7, 0], 1); - let split = accumulate_inbox_rolling_hash(mid, [8, 9], 2); + let mid = accumulate_inbox_rolling_hash(start, [7, 0], only_first_is_bucket_start(), 1); + let split = accumulate_inbox_rolling_hash(mid, [8, 9], [false; 2], 2); assert_eq(full, split); } #[test] fn padding_lanes_are_not_absorbed() { - // A padded array with num_leaves set correctly matches the exact-length chain regardless of lane contents. - let padded = accumulate_inbox_rolling_hash(0, [11, 22, 999, 888], 2); - let exact = accumulate_inbox_rolling_hash(0, [11, 22], 2); + // A padded array with num_leaves set correctly matches the exact-length chain regardless of lane contents, + // flags included. + let mut padded_starts: [bool; 4] = only_first_is_bucket_start(); + padded_starts[3] = true; + let padded = accumulate_inbox_rolling_hash(0, [11, 22, 999, 888], padded_starts, 2); + let exact = accumulate_inbox_rolling_hash(0, [11, 22], only_first_is_bucket_start(), 2); assert_eq(padded, exact); } #[test(should_fail_with = "num_leaves is greater than the leaves array length")] fn num_leaves_past_array_fails() { - let _ = accumulate_inbox_rolling_hash(0, [11, 22, 33], 4); + let _ = accumulate_inbox_rolling_hash(0, [11, 22, 33], only_first_is_bucket_start(), 4); } } diff --git a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/parity/inbox_parity.nr b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/parity/inbox_parity.nr index 9dabcb9837aa..b30febbe9947 100644 --- a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/parity/inbox_parity.nr +++ b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/parity/inbox_parity.nr @@ -11,6 +11,9 @@ use types::utils::arrays::assert_trailing_zeros; pub struct InboxParityPrivateInputs { // The checkpoint's L1-to-L2 message leaves, padded with zeros beyond `num_msgs`. pub(crate) msgs: [Field; S], + // Per-lane flag marking the leaves that are the first message of an L1 bucket, so the rolling hash commits to the + // bucket boundaries. False beyond `num_msgs`. + pub(crate) bucket_starts: [bool; S], // Number of real (non-padding) messages in `msgs`. pub(crate) num_msgs: u32, // Rolling hash of the Inbox chain before this checkpoint's messages (the previous checkpoint's end value; genesis @@ -24,11 +27,13 @@ pub struct InboxParityPrivateInputs { /// parity root family with a single variable-size proof per checkpoint. /// /// This circuit: -/// - Chains the `num_msgs` real messages into the rolling hash (two sha256 compressions per leaf) +/// - Chains the `num_msgs` real messages into the rolling hash (two sha256 compressions per leaf), tagging each link +/// as a bucket start or a bucket continuation /// - Absorbs the same real messages into the empty message sponge (real-count absorb; the checkpoint root asserts this /// equals the sponge the block roots accumulate). The sponge resets per checkpoint and there is one proof per /// checkpoint, so the start is always the empty sponge and is hard-coded rather than witnessed. -/// - Asserts the padding lanes past `num_msgs` are zero so they cannot silently enter either accumulator +/// - Asserts the padding lanes past `num_msgs` are zero so they cannot silently enter either accumulator, and that +/// they claim no bucket start; a consuming checkpoint must also open a bucket at lane 0 /// /// The output feeds the Checkpoint Root circuits, which verify this proof against the `{64, 256, 1024}` VK ladder. /// @@ -37,8 +42,26 @@ pub fn execute(inputs: InboxParityPrivateInputs) -> ParityPublicI // Guard the padding lanes so they can't silently enter either accumulator (`num_msgs <= S` is asserted here too). assert_trailing_zeros(inputs.msgs, inputs.num_msgs); - let end_rolling_hash = - accumulate_inbox_rolling_hash(inputs.start_rolling_hash, inputs.msgs, inputs.num_msgs); + // A checkpoint always begins consuming at a bucket boundary, since a bucket is only ever consumed from its first + // unconsumed message onwards and the previous checkpoint stopped at a bucket end. + if inputs.num_msgs > 0 { + assert(inputs.bucket_starts[0], "First consumed message must open a bucket"); + } + // Padding lanes must not claim to open a bucket, mirroring the zero-padding guard on `msgs`. + let mut is_padding = false; + for i in 0..S { + is_padding |= i == inputs.num_msgs; + if is_padding { + assert(!inputs.bucket_starts[i], "Found bucket start after breakpoint"); + } + } + + let end_rolling_hash = accumulate_inbox_rolling_hash( + inputs.start_rolling_hash, + inputs.msgs, + inputs.bucket_starts, + inputs.num_msgs, + ); let mut end_sponge = L1ToL2MessageSponge::new(); end_sponge.absorb(inputs.msgs, inputs.num_msgs); 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 32313ecbeb4f..da0b63c2eb69 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 @@ -7,21 +7,38 @@ use types::traits::Empty; global S: u32 = 64; +// Flags for a checkpoint consuming a single contiguous run out of one bucket: lane 0 opens the bucket and every +// other lane continues it. +fn only_first_is_bucket_start() -> [bool; S] { + let mut bucket_starts = [false; S]; + bucket_starts[0] = true; + bucket_starts +} + #[test] fn public_inputs_match_expected() { let mut msgs = [0; S]; for i in 0..S { msgs[i] = i as Field; } - let private_inputs = - InboxParityPrivateInputs { msgs, num_msgs: S, start_rolling_hash: 0, prover_id: 7 }; + let bucket_starts = only_first_is_bucket_start(); + let private_inputs = InboxParityPrivateInputs { + msgs, + bucket_starts, + num_msgs: S, + start_rolling_hash: 0, + prover_id: 7, + }; let public_inputs = inbox_parity::execute(private_inputs); assert_eq(public_inputs.prover_id, 7); // The rolling hash chains all real messages on top of the start value, which the circuit echoes back. assert_eq(public_inputs.start_rolling_hash, 0); - assert_eq(public_inputs.end_rolling_hash, accumulate_inbox_rolling_hash(0, msgs, S)); + assert_eq( + public_inputs.end_rolling_hash, + accumulate_inbox_rolling_hash(0, msgs, bucket_starts, S), + ); // The sponge absorbs exactly the real messages (real-count) into the empty sponge the circuit starts from. let mut expected_end_sponge = L1ToL2MessageSponge::empty(); @@ -29,6 +46,43 @@ fn public_inputs_match_expected() { assert_eq(public_inputs.end_sponge, expected_end_sponge); } +#[test] +fn bucket_starts_are_committed_to() { + // Two checkpoints over the same messages but with a bucket boundary in a different place reach different rolling + // hashes, so the chain pins the grouping and not just the message order. + let mut msgs = [0; S]; + msgs[0] = 101; + msgs[1] = 202; + msgs[2] = 303; + + let one_bucket = only_first_is_bucket_start(); + let mut split_at_third = one_bucket; + split_at_third[2] = true; + + let single = inbox_parity::execute( + InboxParityPrivateInputs { + msgs, + bucket_starts: one_bucket, + num_msgs: 3, + start_rolling_hash: 0, + prover_id: 7, + }, + ); + let split = inbox_parity::execute( + InboxParityPrivateInputs { + msgs, + bucket_starts: split_at_third, + num_msgs: 3, + start_rolling_hash: 0, + prover_id: 7, + }, + ); + + assert(single.end_rolling_hash != split.end_rolling_hash); + // The sponge is unaffected: it only sees the leaves. + assert_eq(single.end_sponge, split.end_sponge); +} + #[test] fn sponge_matches_block_root_real_count_absorb() { // The checkpoint root asserts InboxParity's end sponge equals the sponge the block roots accumulate. The block @@ -38,8 +92,13 @@ fn sponge_matches_block_root_real_count_absorb() { msgs[0] = 101; msgs[1] = 202; - let private_inputs = - InboxParityPrivateInputs { msgs, num_msgs: 2, start_rolling_hash: 0, prover_id: 7 }; + let private_inputs = InboxParityPrivateInputs { + msgs, + bucket_starts: only_first_is_bucket_start(), + num_msgs: 2, + start_rolling_hash: 0, + prover_id: 7, + }; let public_inputs = inbox_parity::execute(private_inputs); // A block root absorbing the same two real leaves out of its wider bundle reaches the same sponge. @@ -56,15 +115,70 @@ fn non_zero_padding_lane_fails() { msgs[1] = 22; msgs[5] = 999; - let private_inputs = - InboxParityPrivateInputs { msgs, num_msgs: 2, start_rolling_hash: 0, prover_id: 7 }; + let private_inputs = InboxParityPrivateInputs { + msgs, + bucket_starts: only_first_is_bucket_start(), + num_msgs: 2, + start_rolling_hash: 0, + prover_id: 7, + }; let _ = inbox_parity::execute(private_inputs); } +#[test(should_fail_with = "First consumed message must open a bucket")] +fn first_message_not_a_bucket_start_fails() { + let mut msgs = [0; S]; + msgs[0] = 11; + msgs[1] = 22; + + let private_inputs = InboxParityPrivateInputs { + msgs, + bucket_starts: [false; S], + num_msgs: 2, + start_rolling_hash: 0, + prover_id: 7, + }; + let _ = inbox_parity::execute(private_inputs); +} + +#[test(should_fail_with = "Found bucket start after breakpoint")] +fn bucket_start_in_padding_lane_fails() { + let mut msgs = [0; S]; + msgs[0] = 11; + msgs[1] = 22; + + let mut bucket_starts = only_first_is_bucket_start(); + bucket_starts[5] = true; + + let private_inputs = InboxParityPrivateInputs { + msgs, + bucket_starts, + num_msgs: 2, + start_rolling_hash: 0, + prover_id: 7, + }; + let _ = inbox_parity::execute(private_inputs); +} + +#[test] +fn empty_checkpoint_needs_no_bucket_start() { + let public_inputs = inbox_parity::execute( + InboxParityPrivateInputs { + msgs: [0; S], + bucket_starts: [false; S], + num_msgs: 0, + start_rolling_hash: 0x2a, + prover_id: 7, + }, + ); + assert_eq(public_inputs.end_rolling_hash, 0x2a); +} + #[test(should_fail_with = "in_len is greater than the input array len")] fn num_msgs_greater_than_size_fails() { let private_inputs = InboxParityPrivateInputs { msgs: [0; S], + bucket_starts: only_first_is_bucket_start(), num_msgs: S + 1, start_rolling_hash: 0, prover_id: 7, 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 ae35095273ad..d81bdb5e8bae 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 = "0x00087fcd5a2ee0849c3801915ea5ebdc9b1934b7f233157fafb558f693a706a4" + end_inbox_rolling_hash = "0x006d131248894be46de839138bff160c07ff1727a08d40d3566dfadae7de82f3" checkpoint_header_hashes = [ - "0x0008e8aa79c86128b8b7079536f059201e42f2af83920f38c5e47592ad1eb32a", + "0x0047f8a5738906575fb6aa7722413fe98ac4f540caddcc3a20b62680ec63db9a", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", @@ -523,7 +523,7 @@ proof = [ [inputs.previous_rollups.public_inputs.constants] chain_id = "0x0000000000000000000000000000000000000000000000000000000000000000" version = "0x0000000000000000000000000000000000000000000000000000000000000000" - vk_tree_root = "0x17f4111918fd7fd073ef3c7b0685a0b0f9de9c06978162bfb3284f1949143cfb" + vk_tree_root = "0x29b7bb0a371549fbde9a7c1967f9368c8b78f8d537b1c0a47626757643283c1d" protocol_contracts_hash = "0x14cec0ae6f31e913c3be04691c6d49c3ec07b3f2d024c9558693ea62fc530bbf" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" @@ -532,7 +532,7 @@ proof = [ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000001" [inputs.previous_rollups.public_inputs.new_archive] - root = "0x0e638cc0de2c75bdbdbaa20710be401108bec288937504f7cf3b53c4d340f57f" + root = "0x03047a26aae0869a941cb5bc9681c4bca632a6aa52ad3c2361f171d7fdcdda20" 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 = "0x0033ba00a6f735f620551bf80e7b7cee13029ec3dd27ad7a61f44eb8701c94b1" - z_acc = "0x11d22738f56155d5a19c668fe8701d9c809f96d53c46e0a88970e6481ab68df4" - gamma_acc = "0x171a303a0bd36a8df1b8a39d8bbbab72f6b9b30064373af65fb76b8e910ea6f8" + blob_commitments_hash_acc = "0x00324689f8c45b2b94f3e6378a0cdedb35d3d9573360aa4acd0d4fae3d55dc1f" + z_acc = "0x286207660fa53035b4c5c05e71adaaedd8cdada9df34621c3f70e473c3250318" + gamma_acc = "0x1d363675972ecdb3af2f93d69a23eccb33e154d828921c20ed2da7a231b90938" [inputs.previous_rollups.public_inputs.end_blob_accumulator.y_acc] limbs = [ - "0x8930e977a3ca06e3dfd977e9fcd07c", - "0x2119a109a3ebff6237ae753fc17792", - "0x577c" + "0xbb2d86c39271375d58fd40b324793a", + "0xf1b5e1cf35e65f071f40b2def235b8", + "0x0bd5" ] [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 = [ - "0xf70cf83afc172d45e03e9f0e320d17", - "0x8996b5045c2c70ca03a0985fa6fbb3", - "0xedf99a2e4793301faf550d9afcdec3", - "0x10dcf4" + "0xabe21afbe58efea8015d3206608b1f", + "0x5f4c483a53108fd73cfdcc6aebed35", + "0xa9869c3384a64b66622873c610a3a5", + "0x11f5f0" ] [inputs.previous_rollups.public_inputs.end_blob_accumulator.c_acc.y] limbs = [ - "0x3520b7b92d97d1be8fc5f911b6dc55", - "0x5de9c5d32ef1dc3b021b12d2508c09", - "0xd0dd575ba591b5f4d1840e8e068f82", - "0x00f762" + "0x5b1437ff95d2879b53850427a9160a", + "0x475e3795313e04ab9680366d0e19bf", + "0xcde92c76fa707a5a657680604dd84a", + "0x17a6b1" ] [inputs.previous_rollups.public_inputs.end_blob_accumulator.gamma_pow_acc] limbs = [ - "0x38ebe790252c889a44010717a68c37", - "0xc0257e7db95b8b227f3e39d6139aea", - "0x2193" + "0xd5cb58fe803b11943c330499ae2f0d", + "0x5a4cdcc14064fc0f57ba18f212c644", + "0x0875" ] [inputs.previous_rollups.public_inputs.final_blob_challenges] - z = "0x11d22738f56155d5a19c668fe8701d9c809f96d53c46e0a88970e6481ab68df4" + z = "0x286207660fa53035b4c5c05e71adaaedd8cdada9df34621c3f70e473c3250318" [inputs.previous_rollups.public_inputs.final_blob_challenges.gamma] limbs = [ - "0x38ebe790252c889a44010717a68c37", - "0xc0257e7db95b8b227f3e39d6139aea", - "0x2193" + "0xd5cb58fe803b11943c330499ae2f0d", + "0x5a4cdcc14064fc0f57ba18f212c644", + "0x0875" ] [inputs.previous_rollups.vk_data] @@ -830,7 +830,7 @@ proof = [ "0x02dfab3acbc7708b1b3654912e946fe846568b8cb0e8af8da6a1845278664e55", "0x29ef5024937c71b580f6cfb649bd06b4a62241ec399bd579b0d0b2bec78c4cae", "0x0db3ab63c7556f8724d86819156701855018cc79140a052893d67fb67c99c1f1", - "0x011ce78c298fc81d89c739267f5a1f69e22587103f51d97947d04724c545a848" + "0x08b0c1ac4553f1257a42dcf5a95191fc747ad0228ffc5c16e6244c4ed4858fea" ] [inputs.previous_rollups.vk_data.vk] @@ -1785,7 +1785,7 @@ proof = [ "0x02dfab3acbc7708b1b3654912e946fe846568b8cb0e8af8da6a1845278664e55", "0x29ef5024937c71b580f6cfb649bd06b4a62241ec399bd579b0d0b2bec78c4cae", "0x0db3ab63c7556f8724d86819156701855018cc79140a052893d67fb67c99c1f1", - "0x011ce78c298fc81d89c739267f5a1f69e22587103f51d97947d04724c545a848" + "0x08b0c1ac4553f1257a42dcf5a95191fc747ad0228ffc5c16e6244c4ed4858fea" ] [inputs.previous_rollups.vk_data.vk] diff --git a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-tx-merge/Prover.toml b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-tx-merge/Prover.toml index 036164e25d63..2829350bf30c 100644 --- a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-tx-merge/Prover.toml +++ b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-tx-merge/Prover.toml @@ -489,7 +489,7 @@ proof = [ accumulated_mana_used = "0x000000000000000000000000000000000000000000000000000000000006b6c0" [inputs.previous_rollups.public_inputs.constants] - vk_tree_root = "0x17f4111918fd7fd073ef3c7b0685a0b0f9de9c06978162bfb3284f1949143cfb" + vk_tree_root = "0x29b7bb0a371549fbde9a7c1967f9368c8b78f8d537b1c0a47626757643283c1d" protocol_contracts_hash = "0x14cec0ae6f31e913c3be04691c6d49c3ec07b3f2d024c9558693ea62fc530bbf" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" @@ -570,10 +570,10 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x00000000000000000000000000000000000000000000000000000000b7d1b44c" ] state = [ - "0x010da2645a26ac94c8808d9b4ff077ea56c61fe2ff51c100a1593faa95654d18", - "0x07ac9fcc67e51e1efae11729c221ee5954bc3801bd44ee23f267847e46b0e246", - "0x11b2d8c933b05c3993c454fab008734aa688433f3c4be1791137ca13c65bd291", - "0x0abde0b2c458a567bd4e90d20caeefa0544738dd7dbf3b5029afe1e76029d8a5" + "0x2f3a14c87f57a861a321686e4827d9241f488539aa8aed63dcc5d538b48ebab3", + "0x0572e61e8aa26d9272bb2fea86e1359317b60817c9ff89e55240b90b8d7af271", + "0x302f314594020e470df49a6e3b16344152713e6388111630a6059bab02407b99", + "0x2220ae6b5fd96311f294d193f366a6794862ef5e4309ea6ec7b397e3fdb037f8" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000002" squeeze_mode = false @@ -587,7 +587,7 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x1800b90c40fa738a64bbc71d11bdc768c7c43a4e8876d6e5bf203355830af7e2", "0x28acc2354e5ea51f25dd1a1e980d97c826400f67bc116d1b6187bdfe5726cb6a", "0x0db3ab63c7556f8724d86819156701855018cc79140a052893d67fb67c99c1f1", - "0x011ce78c298fc81d89c739267f5a1f69e22587103f51d97947d04724c545a848" + "0x08b0c1ac4553f1257a42dcf5a95191fc747ad0228ffc5c16e6244c4ed4858fea" ] [inputs.previous_rollups.vk_data.vk] @@ -1201,7 +1201,7 @@ proof = [ accumulated_mana_used = "0x0000000000000000000000000000000000000000000000000000000000000000" [inputs.previous_rollups.public_inputs.constants] - vk_tree_root = "0x17f4111918fd7fd073ef3c7b0685a0b0f9de9c06978162bfb3284f1949143cfb" + vk_tree_root = "0x29b7bb0a371549fbde9a7c1967f9368c8b78f8d537b1c0a47626757643283c1d" protocol_contracts_hash = "0x14cec0ae6f31e913c3be04691c6d49c3ec07b3f2d024c9558693ea62fc530bbf" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" @@ -1264,10 +1264,10 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x00000000000000000000000000000000000000000000000000000000b7d1b44c" ] state = [ - "0x010da2645a26ac94c8808d9b4ff077ea56c61fe2ff51c100a1593faa95654d18", - "0x07ac9fcc67e51e1efae11729c221ee5954bc3801bd44ee23f267847e46b0e246", - "0x11b2d8c933b05c3993c454fab008734aa688433f3c4be1791137ca13c65bd291", - "0x0abde0b2c458a567bd4e90d20caeefa0544738dd7dbf3b5029afe1e76029d8a5" + "0x2f3a14c87f57a861a321686e4827d9241f488539aa8aed63dcc5d538b48ebab3", + "0x0572e61e8aa26d9272bb2fea86e1359317b60817c9ff89e55240b90b8d7af271", + "0x302f314594020e470df49a6e3b16344152713e6388111630a6059bab02407b99", + "0x2220ae6b5fd96311f294d193f366a6794862ef5e4309ea6ec7b397e3fdb037f8" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000002" squeeze_mode = false @@ -1282,10 +1282,10 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x00000000000000000000000000000000000000000000000000000000b7e5c34d" ] state = [ - "0x10ff02ecd68832e12358733c09b2efb4a10023f55cf280ab65ef8bc4d0ef79aa", - "0x13744cf6d8e5439f0345d0e891154c5d2a2d0df273d9ae51b33ad57747c5527d", - "0x0b543321962af9434965b2cce519bf52b60a6482e72074408417d4d33e7bc7cf", - "0x01cf08fb824db9e3597373d6f9a812d4ed56757c160d3120cf4f7fc5752c1054" + "0x10e10c0582e68248e8eddc5cc85695fff7543cfcdfe0d4695834df707b519131", + "0x134c983b5af9bd780a7069792bfbe258024d1b597fbf0aa0505021a353a59f95", + "0x20ac148a57b95b2622e62e2f4ce202b25ca6d9e7d2843a0549438aa71818dc9f", + "0x2fb938cdd0ad852992d4e08f5c2d92b02769016b1c6f1efc1c3770ea0035c569" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000001" squeeze_mode = false @@ -1299,7 +1299,7 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x20f1c701d84b280c9f80a517272153688a9e1b92166d1000cfe7c829b7c25f69", "0x28acc2354e5ea51f25dd1a1e980d97c826400f67bc116d1b6187bdfe5726cb6a", "0x0db3ab63c7556f8724d86819156701855018cc79140a052893d67fb67c99c1f1", - "0x011ce78c298fc81d89c739267f5a1f69e22587103f51d97947d04724c545a848" + "0x08b0c1ac4553f1257a42dcf5a95191fc747ad0228ffc5c16e6244c4ed4858fea" ] [inputs.previous_rollups.vk_data.vk] 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 1c68751e7200..a30545c4fa60 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 @@ -731,6 +731,13 @@ pub global DOM_SEP__RETRIEVED_BYTECODES_MERKLE: u32 = 2789215184; /// hash, in particular the `out_hash` merkle node hash, which absorbs the same 64-byte two-field preimage shape. pub global DOM_SEP__INBOX_ROLLING_HASH: u32 = 3737216265; +/// Domain separator for an Inbox rolling-hash link whose leaf is the first message of an L1 bucket. +/// +/// Buckets group the messages inserted in a single L1 block, so tagging the first leaf of each bucket makes the chain +/// itself commit to the bucket boundaries: a chain over the same leaves regrouped into different buckets yields a +/// different hash. +pub global DOM_SEP__INBOX_ROLLING_HASH_BUCKET_START: u32 = 3204844280; + // --------------------------------------------------------------- // Contract Address diff --git a/noir-projects/fnd/noir-protocol-circuits/crates/types/src/constants_tests.nr b/noir-projects/fnd/noir-protocol-circuits/crates/types/src/constants_tests.nr index 616769ed144f..e57c0d135b41 100644 --- a/noir-projects/fnd/noir-protocol-circuits/crates/types/src/constants_tests.nr +++ b/noir-projects/fnd/noir-protocol-circuits/crates/types/src/constants_tests.nr @@ -10,7 +10,8 @@ use crate::{ DOM_SEP__CONSTRAINED_MSG_LOG_TAG, DOM_SEP__CONSTRAINED_MSG_SENDER_SECRET, DOM_SEP__CONTRACT_ADDRESS_V2, DOM_SEP__CONTRACT_CLASS_ID, DOM_SEP__EVENT_COMMITMENT, DOM_SEP__EVENT_LOG_TAG, DOM_SEP__FBSK_M, DOM_SEP__FUNCTION_ARGS, - DOM_SEP__HANDSHAKE_FORGERY_PROTECTION, DOM_SEP__INBOX_ROLLING_HASH, DOM_SEP__INITIALIZER, + DOM_SEP__HANDSHAKE_FORGERY_PROTECTION, DOM_SEP__INBOX_ROLLING_HASH, + DOM_SEP__INBOX_ROLLING_HASH_BUCKET_START, DOM_SEP__INITIALIZER, DOM_SEP__INTERACTIVE_HANDSHAKE_SIGNATURE, DOM_SEP__IVSK_M, DOM_SEP__MERKLE_HASH, DOM_SEP__MESSAGE_NULLIFIER, DOM_SEP__MSSK_M, DOM_SEP__NHK_M, DOM_SEP__NON_INTERACTIVE_HANDSHAKE_LOG_TAG, DOM_SEP__NOTE_HASH, DOM_SEP__NOTE_HASH_NONCE, @@ -140,7 +141,7 @@ impl HashedValueTester::new(); + let mut tester = HashedValueTester::<69, 64>::new(); // ----------------- // Domain separators @@ -247,6 +248,10 @@ fn hashed_values_match_derived() { "retrieved_bytecodes_merkle", ); tester.assert_dom_sep_matches_derived(DOM_SEP__INBOX_ROLLING_HASH, "inbox_rolling_hash"); + tester.assert_dom_sep_matches_derived( + DOM_SEP__INBOX_ROLLING_HASH_BUCKET_START, + "inbox_rolling_hash_bucket_start", + ); // -------------------------- // Protocol circuit constants diff --git a/yarn-project/archiver/src/archiver-sync.test.ts b/yarn-project/archiver/src/archiver-sync.test.ts index cb6ae0e9efa2..2fb9d23f082c 100644 --- a/yarn-project/archiver/src/archiver-sync.test.ts +++ b/yarn-project/archiver/src/archiver-sync.test.ts @@ -289,10 +289,10 @@ describe('Archiver Sync', () => { 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); + // Messages between buckets, in insertion order, one group per bucket. + 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 () => { diff --git a/yarn-project/archiver/src/modules/data_source_base.ts b/yarn-project/archiver/src/modules/data_source_base.ts index 9e407d9e6765..959313a7e11b 100644 --- a/yarn-project/archiver/src/modules/data_source_base.ts +++ b/yarn-project/archiver/src/modules/data_source_base.ts @@ -40,7 +40,12 @@ 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 { InboxBucket, L1ToL2MessageSource, L2ToL1MembershipWitness } from '@aztec/stdlib/messaging'; +import type { + InboxBucket, + InboxMessageBundle, + 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'; @@ -332,11 +337,11 @@ export abstract class ArchiverDataSourceBase return this.stores.messages.getInboxBucketByTotalMsgCount(totalMsgCount); } - public getL1ToL2MessagesBetweenBuckets(fromExclusive: bigint, toInclusive: bigint): Promise { + public getL1ToL2MessagesBetweenBuckets(fromExclusive: bigint, toInclusive: bigint): Promise { return this.stores.messages.getL1ToL2MessagesBetweenBuckets(fromExclusive, toInclusive); } - public getL1ToL2MessagesBetweenLeafCounts(startLeafCount: bigint, endLeafCount: bigint): Promise { + public getL1ToL2MessagesBetweenLeafCounts(startLeafCount: bigint, endLeafCount: bigint): Promise { return this.stores.messages.getL1ToL2MessagesBetweenLeafCounts(startLeafCount, endLeafCount); } diff --git a/yarn-project/archiver/src/store/message_store.test.ts b/yarn-project/archiver/src/store/message_store.test.ts index b9718fcfe818..49c8a639a377 100644 --- a/yarn-project/archiver/src/store/message_store.test.ts +++ b/yarn-project/archiver/src/store/message_store.test.ts @@ -227,20 +227,22 @@ describe('MessageStore', () => { describe('Inbox buckets', () => { // Builds `count` consecutive valid messages, 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; l1BlockNumber?: bigint }[], - ): InboxMessage[] => { - const msgs = makeInboxMessages(spec.length); - msgs.forEach((msg, i) => { - msg.bucketSeq = spec[i].seq; - msg.bucketTimestamp = spec[i].timestamp; - // Buckets opened at the same L1 timestamp are rollover siblings within one L1 block, so derive the block - // from the timestamp rather than from the bucket sequence. - msg.l1BlockNumber = spec[i].l1BlockNumber ?? makeL1BlockNumberForBucket(spec[i].timestamp); - msg.l1BlockHash = makeL1BlockHash(msg.l1BlockNumber); + const makeBucketedMessages = (spec: { seq: bigint; timestamp: bigint; l1BlockNumber?: bigint }[]): InboxMessage[] => + // Reassign the buckets through the override so the rolling-hash chain is built over the final bucket layout. + makeInboxMessages(spec.length, { + overrideFn: (msg, i) => { + // Buckets opened at the same L1 timestamp are rollover siblings within one L1 block, so derive the block + // from the timestamp rather than from the bucket sequence. + const l1BlockNumber = spec[i].l1BlockNumber ?? makeL1BlockNumberForBucket(spec[i].timestamp); + return { + ...msg, + bucketSeq: spec[i].seq, + bucketTimestamp: spec[i].timestamp, + l1BlockNumber, + l1BlockHash: makeL1BlockHash(l1BlockNumber), + }; + }, }); - 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 => { @@ -249,7 +251,7 @@ describe('MessageStore', () => { ...previous, leaf, index: previous.index + 1n, - inboxRollingHash: updateInboxRollingHash(previous.inboxRollingHash, leaf), + inboxRollingHash: updateInboxRollingHash(previous.inboxRollingHash, leaf, bucket.seq !== previous.bucketSeq), bucketSeq: bucket.seq, bucketTimestamp: bucket.timestamp, }; @@ -445,14 +447,19 @@ describe('MessageStore', () => { expect((await messageStore.getLatestInboxBucketAtOrBefore(200n))!.seq).toEqual(3n); }); - it('returns messages between buckets in insertion order', async () => { + it('returns messages between buckets grouped per bucket, in insertion order', async () => { const msgs = makeBucketedMessages(threeBucketSpec); await messageStore.addL1ToL2MessageBuckets(msgs); const leaves = msgs.map(m => m.leaf); - 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)); + // Bucket 1 = [0,1,2], bucket 2 = [3,4], bucket 3 = [5]; each range comes back as one group per bucket. + expect(await messageStore.getL1ToL2MessagesBetweenBuckets(0n, 3n)).toEqual([ + leaves.slice(0, 3), + leaves.slice(3, 5), + leaves.slice(5), + ]); + 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([]); }); @@ -475,10 +482,14 @@ describe('MessageStore', () => { 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)); + expect(await messageStore.getL1ToL2MessagesBetweenLeafCounts(0n, 6n)).toEqual([ + leaves.slice(0, 3), + leaves.slice(3, 5), + leaves.slice(5), + ]); + 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([]); diff --git a/yarn-project/archiver/src/store/message_store.ts b/yarn-project/archiver/src/store/message_store.ts index 19b539332a4e..e7740cdae2cb 100644 --- a/yarn-project/archiver/src/store/message_store.ts +++ b/yarn-project/archiver/src/store/message_store.ts @@ -12,7 +12,7 @@ import { type CustomRange, mapRange, } from '@aztec/kv-store'; -import { type InboxBucket, updateInboxRollingHash } from '@aztec/stdlib/messaging'; +import { type InboxBucket, type InboxMessageBundle, updateInboxRollingHash } from '@aztec/stdlib/messaging'; import { InboxBucketBoundaryNotSyncedError, InboxBucketNotSyncedError } from '../errors.js'; import { type InboxMessage, deserializeInboxMessage, serializeInboxMessage } from '../structs/inbox_message.js'; @@ -248,9 +248,11 @@ export class MessageStore { } // Check the consensus rolling-hash chain is valid: each message's rolling hash must - // continue the chain from the previously inserted message. + // continue the chain from the previously inserted message. A message whose bucket differs from the previous + // message's is the first of its bucket, so it takes the bucket-start separator. const previousInboxRollingHash = lastMessage?.inboxRollingHash ?? Fr.ZERO; - const expectedInboxRollingHash = updateInboxRollingHash(previousInboxRollingHash, message.leaf); + const opensBucket = message.bucketSeq !== lastMessage?.bucketSeq; + const expectedInboxRollingHash = updateInboxRollingHash(previousInboxRollingHash, message.leaf, opensBucket); if (!expectedInboxRollingHash.equals(message.inboxRollingHash)) { throw new MessageStoreError( `Invalid inbox rolling hash for incoming L1 to L2 message ${message.leaf.toString()} ` + @@ -479,7 +481,10 @@ export class MessageStore { * 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 { + public async getL1ToL2MessagesBetweenLeafCounts( + startLeafCount: bigint, + endLeafCount: bigint, + ): Promise { if (startLeafCount > endLeafCount) { throw new Error(`Invalid Inbox leaf count range [${startLeafCount}, ${endLeafCount})`); } @@ -527,7 +532,10 @@ export class MessageStore { * `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 { + public async getL1ToL2MessagesBetweenBuckets( + fromExclusive: bigint, + toInclusive: bigint, + ): Promise { if (fromExclusive > toInclusive) { throw new Error(`Invalid Inbox bucket range (${fromExclusive}, ${toInclusive}]`); } @@ -540,16 +548,28 @@ export class MessageStore { 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[] = []; + /** + * Collects the message leaves in the global index range `[startIndex, endIndexExclusive)`, in insertion order, + * grouped per Inbox bucket. A group is only started by a message, so no group is ever empty. + */ + private async getMessageLeavesInIndexRange( + startIndex: bigint, + endIndexExclusive: bigint, + ): Promise { + const bundle: InboxMessageBundle = []; + let currentBucketSeq: bigint | undefined; for await (const msgBuffer of this.#l1ToL2Messages.valuesAsync({ start: this.indexToKey(startIndex), end: this.indexToKey(endIndexExclusive), })) { - leaves.push(deserializeInboxMessage(msgBuffer).leaf); + const message = deserializeInboxMessage(msgBuffer); + if (message.bucketSeq !== currentBucketSeq) { + bundle.push([]); + currentBucketSeq = message.bucketSeq; + } + bundle.at(-1)!.push(message.leaf); } - return leaves; + return bundle; } private async getBucketSnapshotBySeq(seq: bigint): Promise { diff --git a/yarn-project/archiver/src/test/fake_l1_state.ts b/yarn-project/archiver/src/test/fake_l1_state.ts index ad2090885ef4..fb77297851c5 100644 --- a/yarn-project/archiver/src/test/fake_l1_state.ts +++ b/yarn-project/archiver/src/test/fake_l1_state.ts @@ -197,7 +197,11 @@ export class FakeL1State { this.currentBucketTimestamp = timestamp; this.currentBucketMsgCount = 0; } - this.messagesConsensusRollingHash = updateInboxRollingHash(this.messagesConsensusRollingHash, leaf); + this.messagesConsensusRollingHash = updateInboxRollingHash( + this.messagesConsensusRollingHash, + leaf, + this.currentBucketMsgCount === 0, + ); this.currentBucketMsgCount += 1; return { bucketSeq: this.currentBucketSeq, inboxRollingHash: this.messagesConsensusRollingHash }; } diff --git a/yarn-project/archiver/src/test/mock_archiver.ts b/yarn-project/archiver/src/test/mock_archiver.ts index 5ef6b4d49500..531d42378416 100644 --- a/yarn-project/archiver/src/test/mock_archiver.ts +++ b/yarn-project/archiver/src/test/mock_archiver.ts @@ -2,7 +2,7 @@ import { Buffer32 } from '@aztec/foundation/buffer'; import { Fr } from '@aztec/foundation/curves/bn254'; import type { L2BlockSource } from '@aztec/stdlib/block'; import type { Checkpoint } from '@aztec/stdlib/checkpoint'; -import type { InboxBucket, L1ToL2MessageSource } from '@aztec/stdlib/messaging'; +import type { InboxBucket, InboxMessageBundle, L1ToL2MessageSource } from '@aztec/stdlib/messaging'; import { MockL1ToL2MessageSource } from './mock_l1_to_l2_message_source.js'; import { MockL2BlockSource } from './mock_l2_block_source.js'; @@ -34,11 +34,11 @@ export class MockArchiver extends MockL2BlockSource implements L2BlockSource, L1 return this.messageSource.getInboxBucketByTotalMsgCount(totalMsgCount); } - getL1ToL2MessagesBetweenBuckets(fromExclusive: bigint, toInclusive: bigint): Promise { + getL1ToL2MessagesBetweenBuckets(fromExclusive: bigint, toInclusive: bigint): Promise { return this.messageSource.getL1ToL2MessagesBetweenBuckets(fromExclusive, toInclusive); } - getL1ToL2MessagesBetweenLeafCounts(startLeafCount: bigint, endLeafCount: bigint): Promise { + getL1ToL2MessagesBetweenLeafCounts(startLeafCount: bigint, endLeafCount: bigint): Promise { return this.messageSource.getL1ToL2MessagesBetweenLeafCounts(startLeafCount, endLeafCount); } } 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 53b652f8d279..3cd890ff8a94 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,7 +1,8 @@ import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types'; +import { compactArray } from '@aztec/foundation/collection'; import { Fr } from '@aztec/foundation/curves/bn254'; import type { CheckpointId, L2BlockId, L2TipId, L2Tips } from '@aztec/stdlib/block'; -import type { InboxBucket, L1ToL2MessageSource } from '@aztec/stdlib/messaging'; +import type { InboxBucket, InboxMessageBundle, L1ToL2MessageSource } from '@aztec/stdlib/messaging'; /** * A mocked implementation of L1ToL2MessageSource to be used in tests. @@ -43,14 +44,14 @@ export class MockL1ToL2MessageSource implements L1ToL2MessageSource { return Promise.resolve(atOrBefore.at(-1)); } - getL1ToL2MessagesBetweenBuckets(fromExclusive: bigint, toInclusive: bigint): Promise { + 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) ?? [])); + return Promise.resolve(compactArray(seqs.map(seq => this.messagesPerBucket.get(seq))).filter(m => m.length > 0)); } - async getL1ToL2MessagesBetweenLeafCounts(startLeafCount: bigint, endLeafCount: bigint): Promise { + async getL1ToL2MessagesBetweenLeafCounts(startLeafCount: bigint, endLeafCount: bigint): Promise { const startBucket = await this.getInboxBucketByTotalMsgCount(startLeafCount); const endBucket = await this.getInboxBucketByTotalMsgCount(endLeafCount); if (startBucket === undefined || endBucket === undefined) { diff --git a/yarn-project/archiver/src/test/mock_structs.ts b/yarn-project/archiver/src/test/mock_structs.ts index b1de6c46af8e..7a8a4b450bd8 100644 --- a/yarn-project/archiver/src/test/mock_structs.ts +++ b/yarn-project/archiver/src/test/mock_structs.ts @@ -46,9 +46,9 @@ export function makeInboxMessage( const { leaf = Fr.random() } = overrides; // Compact global insertion index: defaults to the first slot. const { index = 0n } = overrides; - const { inboxRollingHash = updateInboxRollingHash(previousInboxRollingHash, leaf) } = overrides; - // Default each message to its own bucket, keyed monotonically off its global index. + // Default each message to its own bucket, keyed monotonically off its global index, so it opens that bucket. const { bucketSeq = index + 1n } = overrides; + const { inboxRollingHash = updateInboxRollingHash(previousInboxRollingHash, leaf, true) } = overrides; const { bucketTimestamp = index + 1n } = overrides; // A bucket is opened by the first message of its L1 block timestamp, so derive the block from that timestamp. const { l1BlockNumber = makeL1BlockNumberForBucket(bucketTimestamp) } = overrides; @@ -67,32 +67,35 @@ export function makeInboxMessage( /** * Builds a contiguous run of `totalCount` inbox messages with compact global indices starting at `initialIndex` - * and a chained consensus rolling hash starting from `initialInboxHash`. + * and a chained consensus rolling hash starting from `initialInboxHash`. The chain is computed after `overrideFn` + * runs, so a message reassigned to another bucket still gets the rolling hash the Inbox would have produced. */ export function makeInboxMessages( totalCount: number, opts: { initialInboxHash?: Fr; initialIndex?: bigint; + /** Bucket of the message preceding this run; the first message opens a bucket unless it matches. */ + previousBucketSeq?: bigint; overrideFn?: (msg: InboxMessage, index: number) => InboxMessage; } = {}, ): InboxMessage[] { - const { initialInboxHash = Fr.ZERO, initialIndex = 0n, overrideFn = msg => msg } = opts; + const { initialInboxHash = Fr.ZERO, initialIndex = 0n, previousBucketSeq, overrideFn = msg => msg } = opts; const messages: InboxMessage[] = []; let inboxRollingHash = initialInboxHash; + let lastBucketSeq = previousBucketSeq; for (let i = 0; i < totalCount; i++) { - const leaf = Fr.random(); - inboxRollingHash = updateInboxRollingHash(inboxRollingHash, leaf); const message = overrideFn( makeInboxMessage(Fr.ZERO, { - leaf, + leaf: Fr.random(), index: initialIndex + BigInt(i), - inboxRollingHash, }), i, ); - messages.push(message); + inboxRollingHash = updateInboxRollingHash(inboxRollingHash, message.leaf, message.bucketSeq !== lastBucketSeq); + lastBucketSeq = message.bucketSeq; + messages.push({ ...message, inboxRollingHash }); } return messages; } 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 7cbccb09975c..25808d233967 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 @@ -131,7 +131,7 @@ describe('NodePublicCallsSimulator', () => { l1BlockNumber: seq, l1BlockHash: Buffer32.fromBigInt(seq), }); - const bundle = [new Fr(0x1234), new Fr(0x5678)]; + const bundle = [[new Fr(0x1234), new Fr(0x5678)]]; l1ToL2MessageSource.getInboxBucketByTotalMsgCount.mockResolvedValue(makeBucket(0n, 0n)); l1ToL2MessageSource.getLatestInboxBucketAtOrBefore.mockResolvedValue(makeBucket(1n, 2n)); l1ToL2MessageSource.getL1ToL2MessagesBetweenBuckets.mockResolvedValue(bundle); @@ -266,7 +266,7 @@ describe('NodePublicCallsSimulator', () => { await simulator.simulate(tx); - expect(merkleTreeFork.appendLeaves).toHaveBeenCalledWith(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, bundle); + expect(merkleTreeFork.appendLeaves).toHaveBeenCalledWith(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, bundle.flat()); }); it('simulates against the tip when the parent Inbox bucket is not synced', async () => { @@ -353,7 +353,7 @@ describe('NodePublicCallsSimulator', () => { await makeSimulator({ l1Client }).simulate(tx); - expect(merkleTreeFork.appendLeaves).toHaveBeenCalledWith(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, bundle); + expect(merkleTreeFork.appendLeaves).toHaveBeenCalledWith(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, bundle.flat()); // Bucket 1 was opened by L1 block 1, so its child is block 2. expect(l1Client.reads).toEqual([2n]); }); @@ -365,7 +365,7 @@ describe('NodePublicCallsSimulator', () => { await makeSimulator({ l1Client, useAutomineSequencer: true }).simulate(tx); - expect(merkleTreeFork.appendLeaves).toHaveBeenCalledWith(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, bundle); + expect(merkleTreeFork.appendLeaves).toHaveBeenCalledWith(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, bundle.flat()); expect(l1Client.reads).toEqual([]); }); @@ -375,7 +375,7 @@ describe('NodePublicCallsSimulator', () => { await makeSimulator({}).simulate(tx); - expect(merkleTreeFork.appendLeaves).toHaveBeenCalledWith(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, bundle); + expect(merkleTreeFork.appendLeaves).toHaveBeenCalledWith(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, bundle.flat()); }); }); @@ -435,7 +435,7 @@ describe('NodePublicCallsSimulator', () => { await expect(simulator.simulate(tx)).resolves.toBeDefined(); // Only the first block's worth of messages: a fresh checkpoint starts its per-checkpoint budget at the tip. - expect(merkleTreeFork.appendLeaves).toHaveBeenCalledWith(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, bundle); + expect(merkleTreeFork.appendLeaves).toHaveBeenCalledWith(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, bundle.flat()); }); it('targets parentSlot + 1 and carries the parent overrides when pipelining on a proposed checkpoint', async () => { diff --git a/yarn-project/aztec-node/src/aztec-node/node_public_calls_simulator.ts b/yarn-project/aztec-node/src/aztec-node/node_public_calls_simulator.ts index 5414b6e73fa0..1768918b4d42 100644 --- a/yarn-project/aztec-node/src/aztec-node/node_public_calls_simulator.ts +++ b/yarn-project/aztec-node/src/aztec-node/node_public_calls_simulator.ts @@ -27,7 +27,12 @@ import type { L2BlockSource, L2Tips } from '@aztec/stdlib/block'; import { type ProposedCheckpointData, buildCheckpointSimulationOverridesPlan } from '@aztec/stdlib/checkpoint'; import type { ContractDataSource } from '@aztec/stdlib/contract'; import type { MerkleTreeWriteOperations, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server'; -import { type L1ToL2MessageSource, appendL1ToL2MessagesToTree, getInboxCutoffTimestamp } from '@aztec/stdlib/messaging'; +import { + type L1ToL2MessageSource, + appendL1ToL2MessagesToTree, + flattenBundle, + getInboxCutoffTimestamp, +} from '@aztec/stdlib/messaging'; import type { CoordinationSignatureContext } from '@aztec/stdlib/p2p'; import { MerkleTreeId } from '@aztec/stdlib/trees'; import { @@ -315,14 +320,15 @@ export class NodePublicCallsSimulator { isLastBlock: false, cutoffTimestamp: getInboxCutoffTimestamp(opts.slotNumber, l1Constants), }); - if (!selection.consume || selection.bundle.length === 0) { + const leaves = selection.consume ? flattenBundle(selection.bundle) : []; + if (!selection.consume || leaves.length === 0) { return; } - await appendL1ToL2MessagesToTree(fork, selection.bundle); - this.log.debug(`Appended ${selection.bundle.length} predicted L1-to-L2 messages to the simulation fork`, { + await appendL1ToL2MessagesToTree(fork, leaves); + this.log.debug(`Appended ${leaves.length} predicted L1-to-L2 messages to the simulation fork`, { bucketSeq: selection.bucket.seq, - messageCount: selection.bundle.length, + messageCount: leaves.length, }); } catch (err) { this.log.verbose(`Could not predict the next block's L1-to-L2 messages, simulating against the tip: ${err}`); diff --git a/yarn-project/ivc-integration/src/base_parity_inputs.test.ts b/yarn-project/ivc-integration/src/base_parity_inputs.test.ts index 728c22e07f0e..938b42f25b1e 100644 --- a/yarn-project/ivc-integration/src/base_parity_inputs.test.ts +++ b/yarn-project/ivc-integration/src/base_parity_inputs.test.ts @@ -40,18 +40,19 @@ describe('Inbox Parity Benchmark Inputs', () => { const l1ToL2Messages = new Array(INBOX_PARITY_SIZE_MEDIUM).fill(null).map(() => Fr.random()); // Create InboxParity inputs (picks the 256 rung for 256 messages). - const inputs = InboxParityPrivateInputs.fromMessages(l1ToL2Messages, Fr.ZERO, Fr.random()); + const inputs = InboxParityPrivateInputs.fromMessages([l1ToL2Messages], Fr.ZERO, Fr.random()); logger.info('Created inbox parity inputs'); // Convert inputs to Noir format (inline the mapping since it's simple) const noirInputs = { msgs: inputs.messages.map(m => m.toString()), // eslint-disable-next-line camelcase + bucket_starts: inputs.bucketStarts, + // eslint-disable-next-line camelcase num_msgs: inputs.numMessages, // eslint-disable-next-line camelcase start_rolling_hash: inputs.startRollingHash.toString(), // eslint-disable-next-line camelcase - // eslint-disable-next-line camelcase prover_id: inputs.proverId.toString(), }; logger.info('Converted inputs to Noir format'); diff --git a/yarn-project/ivc-integration/src/bb_js_debug.test.ts b/yarn-project/ivc-integration/src/bb_js_debug.test.ts index 8f758d8fadb1..1805c10df616 100644 --- a/yarn-project/ivc-integration/src/bb_js_debug.test.ts +++ b/yarn-project/ivc-integration/src/bb_js_debug.test.ts @@ -55,16 +55,17 @@ describe('BB.js Debug Wrapper', () => { // Generate inbox parity inputs (same approach as base_parity_inputs.test.ts) const l1ToL2Messages = new Array(INBOX_PARITY_SIZE_MEDIUM).fill(null).map(() => Fr.random()); - const inboxParityInputs = InboxParityPrivateInputs.fromMessages(l1ToL2Messages, Fr.ZERO, Fr.random()); + const inboxParityInputs = InboxParityPrivateInputs.fromMessages([l1ToL2Messages], Fr.ZERO, Fr.random()); const noirInputs = { msgs: inboxParityInputs.messages.map(m => m.toString()), // eslint-disable-next-line camelcase + bucket_starts: inboxParityInputs.bucketStarts, + // eslint-disable-next-line camelcase num_msgs: inboxParityInputs.numMessages, // eslint-disable-next-line camelcase start_rolling_hash: inboxParityInputs.startRollingHash.toString(), // eslint-disable-next-line camelcase - // eslint-disable-next-line camelcase prover_id: inboxParityInputs.proverId.toString(), }; diff --git a/yarn-project/noir-protocol-circuits-types/src/conversion/common.ts b/yarn-project/noir-protocol-circuits-types/src/conversion/common.ts index b4ee876ece3a..dd3509bf61dd 100644 --- a/yarn-project/noir-protocol-circuits-types/src/conversion/common.ts +++ b/yarn-project/noir-protocol-circuits-types/src/conversion/common.ts @@ -337,6 +337,13 @@ export function mapFieldArrayToNoir( return mapTupleToNoir(assertLength(array, length), mapFieldToNoir); } +export function mapBoolArrayToNoir( + array: boolean[], + length: N = array.length as N, +): FixedLengthArray { + return mapTupleToNoir(assertLength(array, length), (value: boolean) => value); +} + export function mapClaimedLengthArrayFromNoir( claimedLengthArray: ClaimedLengthArrayNoir, mapper: (item: S) => T, 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 51b7cf32d28f..380de81f2e7e 100644 --- a/yarn-project/noir-protocol-circuits-types/src/conversion/server.ts +++ b/yarn-project/noir-protocol-circuits-types/src/conversion/server.ts @@ -116,6 +116,7 @@ import { mapAztecAddressFromNoir, mapAztecAddressToNoir, mapBlockHeaderToNoir, + mapBoolArrayToNoir, mapEthAddressFromNoir, mapEthAddressToNoir, mapFieldArrayToNoir, @@ -720,6 +721,7 @@ function mapTreeSnapshotDiffHintsToNoir(hints: TreeSnapshotDiffHints): TreeSnaps export function mapInboxParityPrivateInputsToNoir(inputs: InboxParityPrivateInputs) { return { msgs: mapFieldArrayToNoir(inputs.messages), + bucket_starts: mapBoolArrayToNoir(inputs.bucketStarts), num_msgs: mapNumberToNoir(inputs.numMessages), start_rolling_hash: mapFieldToNoir(inputs.startRollingHash), prover_id: mapFieldToNoir(inputs.proverId), 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 82f408f3d2bc..28f711bbd52b 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 @@ -286,7 +286,7 @@ describe('LightweightCheckpointBuilder', () => { const messages = [new Fr(0xb00), new Fr(0xb01)]; const globalVariables2 = makeGlobalVariables(BlockNumber(2), slotNumber); - const { block } = await checkpointBuilder.applyEffectsAndSealBlock(globalVariables2, [], messages); + const { block } = await checkpointBuilder.applyEffectsAndSealBlock(globalVariables2, [], [messages]); expect(block.body.txEffects.length).toBe(0); expect(block.header.state.l1ToL2MessageTree.nextAvailableLeafIndex).toBe(messages.length); @@ -308,13 +308,13 @@ describe('LightweightCheckpointBuilder', () => { // applyEffectsAndSealBlock appends the messages itself. const fork1 = await worldState.fork(); const builder1 = LightweightCheckpointBuilder.startNewCheckpoint(checkpointNumber, constants, [], Fr.ZERO, fork1); - const { block: block1 } = await builder1.applyEffectsAndSealBlock(globalVariables, [], messages); + const { block: block1 } = await builder1.applyEffectsAndSealBlock(globalVariables, [], [messages]); // sealBlock expects the caller to have appended the messages already. const fork2 = await worldState.fork(); const builder2 = LightweightCheckpointBuilder.startNewCheckpoint(checkpointNumber, constants, [], Fr.ZERO, fork2); await appendL1ToL2MessagesToTree(fork2, messages); - const { block: block2 } = await builder2.sealBlock(globalVariables, [], messages); + const { block: block2 } = await builder2.sealBlock(globalVariables, [], [messages]); expect(block2.header.equals(block1.header)).toBe(true); expect(block1.header.state.l1ToL2MessageTree.nextAvailableLeafIndex).toBe(messages.length); 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 5a19733441cf..0e9f1e3697b4 100644 --- a/yarn-project/prover-client/src/light/lightweight_checkpoint_builder.ts +++ b/yarn-project/prover-client/src/light/lightweight_checkpoint_builder.ts @@ -7,10 +7,12 @@ import { L2Block } from '@aztec/stdlib/block'; import { Checkpoint } from '@aztec/stdlib/checkpoint'; import type { MerkleTreeWriteOperations } from '@aztec/stdlib/interfaces/server'; import { + type InboxMessageBundle, accumulateCheckpointOutHashes, accumulateInboxRollingHash, appendL1ToL2MessagesToTree, computeCheckpointOutHash, + flattenBundle, } from '@aztec/stdlib/messaging'; import { CheckpointHeader, computeBlockHeadersHash } from '@aztec/stdlib/rollup'; import { AppendOnlyTreeSnapshot, MerkleTreeId } from '@aztec/stdlib/trees'; @@ -45,7 +47,7 @@ export class LightweightCheckpointBuilder { public readonly checkpointNumber: CheckpointNumber, public readonly constants: CheckpointGlobalVariables, public feeAssetPriceModifier: bigint, - public readonly l1ToL2Messages: Fr[], + public readonly l1ToL2Messages: InboxMessageBundle, private readonly previousCheckpointOutHashes: Fr[], // Inbox rolling hash of the previous checkpoint (this checkpoint's chain start); genesis is zero. private readonly previousInboxRollingHash: Fr, @@ -95,7 +97,7 @@ export class LightweightCheckpointBuilder { checkpointNumber: CheckpointNumber, constants: CheckpointGlobalVariables, feeAssetPriceModifier: bigint, - l1ToL2Messages: Fr[], + l1ToL2Messages: InboxMessageBundle, previousCheckpointOutHashes: Fr[], previousInboxRollingHash: Fr, db: MerkleTreeWriteOperations, @@ -106,7 +108,8 @@ export class LightweightCheckpointBuilder { checkpointNumber, constants, feeAssetPriceModifier, - l1ToL2Messages, + // Copied because `addBlock` appends to this list; the caller's bundle (often a shared empty one) must not move. + [...l1ToL2Messages], previousCheckpointOutHashes, previousInboxRollingHash, db, @@ -168,13 +171,13 @@ export class LightweightCheckpointBuilder { * Seals a block whose state updates are already in the db: the caller has inserted the tx effects and appended the * block's L1-to-L2 messages to the tree (so the AVM read the same post-append tree the prover and the block-root * circuit use). Reads the end state, builds the header and body, and records the block in the checkpoint. - * @param l1ToL2Messages - The message leaves this block consumes from the Inbox, in insertion order. + * @param l1ToL2Messages - The message leaves this block consumes from the Inbox, grouped per Inbox bucket. * @param opts.expectedEndState - If set, the db's end state must match it or the block is rejected. */ public sealBlock( globalVariables: GlobalVariables, txs: ProcessedTx[], - l1ToL2Messages: Fr[], + l1ToL2Messages: InboxMessageBundle, opts: { expectedEndState?: StateReference } = {}, ): Promise<{ block: L2Block; timings: Record }> { return this.addBlock(globalVariables, txs, l1ToL2Messages, { ...opts, applyStateUpdates: false }); @@ -183,13 +186,13 @@ export class LightweightCheckpointBuilder { /** * Inserts the txs' side effects into the db, appends the block's L1-to-L2 messages to the tree, and then seals the * block as `sealBlock` does. - * @param l1ToL2Messages - The message leaves this block consumes from the Inbox, in insertion order. + * @param l1ToL2Messages - The message leaves this block consumes from the Inbox, grouped per Inbox bucket. * @param opts.expectedEndState - If set, the db's end state must match it or the block is rejected. */ public applyEffectsAndSealBlock( globalVariables: GlobalVariables, txs: ProcessedTx[], - l1ToL2Messages: Fr[], + l1ToL2Messages: InboxMessageBundle, opts: { expectedEndState?: StateReference } = {}, ): Promise<{ block: L2Block; timings: Record }> { return this.addBlock(globalVariables, txs, l1ToL2Messages, { ...opts, applyStateUpdates: true }); @@ -198,7 +201,7 @@ export class LightweightCheckpointBuilder { private async addBlock( globalVariables: GlobalVariables, txs: ProcessedTx[], - l1ToL2Messages: Fr[], + l1ToL2Messages: InboxMessageBundle, opts: { applyStateUpdates: boolean; expectedEndState?: StateReference }, ): Promise<{ block: L2Block; timings: Record }> { const timings: Record = {}; @@ -231,7 +234,7 @@ export class LightweightCheckpointBuilder { // mid-build failure does not pollute the checkpoint's rolling hash; the rolling hash is recomputed over them at // checkpoint completion. if (opts.applyStateUpdates) { - await appendL1ToL2MessagesToTree(this.db, l1ToL2Messages); + await appendL1ToL2MessagesToTree(this.db, flattenBundle(l1ToL2Messages)); } const [msGetEndState, endState] = await elapsed(() => this.db.getStateReference()); diff --git a/yarn-project/prover-client/src/mocks/test_context.ts b/yarn-project/prover-client/src/mocks/test_context.ts index cad4fd21bcd1..bad0abc0ce47 100644 --- a/yarn-project/prover-client/src/mocks/test_context.ts +++ b/yarn-project/prover-client/src/mocks/test_context.ts @@ -13,6 +13,7 @@ import { PublicDataWrite } from '@aztec/stdlib/avm'; import { AztecAddress } from '@aztec/stdlib/aztec-address'; import type { Checkpoint } from '@aztec/stdlib/checkpoint'; import type { MerkleTreeWriteOperations, ServerCircuitProver } from '@aztec/stdlib/interfaces/server'; +import type { InboxMessageBundle } from '@aztec/stdlib/messaging'; import type { CheckpointConstantData } from '@aztec/stdlib/rollup'; import { mockProcessedTx } from '@aztec/stdlib/testing'; import { MerkleTreeId, PublicDataTreeLeaf } from '@aztec/stdlib/trees'; @@ -200,6 +201,8 @@ export class TestContext { // 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)); + // The mock puts the whole checkpoint in one Inbox bucket carried by its first block. + const l1ToL2MessageBundle: InboxMessageBundle = l1ToL2Messages.length > 0 ? [l1ToL2Messages] : []; await fork.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2Messages); const newL1ToL2Snapshot = await getTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, fork); @@ -263,7 +266,7 @@ export class TestContext { const { block } = await builder.applyEffectsAndSealBlock( blockGlobalVariables[i], txs, - i === 0 ? l1ToL2Messages : [], + i === 0 ? l1ToL2MessageBundle : [], { expectedEndState: state }, ); @@ -287,6 +290,7 @@ export class TestContext { header: checkpoint.header, blocks, l1ToL2Messages, + l1ToL2MessageBundle, previousBlockHeader, startInboxRollingHash, }; @@ -327,6 +331,8 @@ export class TestContext { const constants = makeCheckpointConstants(slotNumber, constantOpts); const l1ToL2Messages = l1ToL2MessagesPerBlock.flat(); + // Each non-empty per-block slice is one Inbox bucket; the checkpoint's bundle is their concatenation. + const l1ToL2MessageBundle: InboxMessageBundle = l1ToL2MessagesPerBlock.filter(slice => slice.length > 0); const fork = await this.worldState.fork(); @@ -388,12 +394,10 @@ export class TestContext { const txs = blockTxs[i]; const state = blockEndStates[i]; - const { block } = await builder.applyEffectsAndSealBlock( - blockGlobalVariables[i], - txs, - l1ToL2MessagesPerBlock[i], - { expectedEndState: state }, - ); + const blockBundle: InboxMessageBundle = l1ToL2MessagesPerBlock[i].length > 0 ? [l1ToL2MessagesPerBlock[i]] : []; + const { block } = await builder.applyEffectsAndSealBlock(blockGlobalVariables[i], txs, blockBundle, { + expectedEndState: state, + }); const header = block.header; this.headers.set(block.number, header); @@ -414,6 +418,7 @@ export class TestContext { header: checkpoint.header, blocks, l1ToL2Messages, + l1ToL2MessageBundle, l1ToL2MessagesPerBlock, previousBlockHeader, startInboxRollingHash, 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 7ebede67f5a3..c23400249f8f 100644 --- a/yarn-project/prover-client/src/orchestrator/checkpoint-proving-state.ts +++ b/yarn-project/prover-client/src/orchestrator/checkpoint-proving-state.ts @@ -10,7 +10,7 @@ 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 } from '@aztec/stdlib/messaging'; +import { type InboxMessageBundle, 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'; @@ -38,7 +38,7 @@ export class CheckpointProvingState { public readonly totalNumBlocks: number, private readonly headerOfLastBlockInPreviousCheckpoint: BlockHeader, private readonly lastArchiveSiblingPath: Tuple, - private readonly l1ToL2Messages: Fr[], + private readonly l1ToL2Messages: InboxMessageBundle, // 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, @@ -52,8 +52,8 @@ export class CheckpointProvingState { this.firstBlockNumber = BlockNumber(headerOfLastBlockInPreviousCheckpoint.globalVariables.blockNumber + 1); } - /** The checkpoint's real L1-to-L2 messages (unpadded), consumed across its blocks. */ - public getL1ToL2Messages(): Fr[] { + /** The checkpoint's real L1-to-L2 messages (unpadded), grouped per Inbox bucket, consumed across its blocks. */ + public getL1ToL2Messages(): InboxMessageBundle { return this.l1ToL2Messages; } 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 e74aded283ae..d9124b21b672 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 @@ -37,9 +37,10 @@ describe('prover/orchestrator/checkpoint-sub-tree', () => { it('resolves the sub-tree result with block-level proofs for a single-block checkpoint', async () => { const numBlocks = 1; const numTxsPerBlock = 1; - const { constants, blocks, l1ToL2Messages, previousBlockHeader } = await context.makeCheckpoint(numBlocks, { - numTxsPerBlock, - }); + const { constants, blocks, l1ToL2Messages, l1ToL2MessageBundle, previousBlockHeader } = + await context.makeCheckpoint(numBlocks, { + numTxsPerBlock, + }); const subTree = await CheckpointSubTreeOrchestrator.start( context.worldState, @@ -50,7 +51,7 @@ describe('prover/orchestrator/checkpoint-sub-tree', () => { false, makeTestDeferredJobQueue(), constants, - l1ToL2Messages, + l1ToL2MessageBundle, Fr.ZERO, numBlocks, previousBlockHeader, @@ -83,9 +84,10 @@ describe('prover/orchestrator/checkpoint-sub-tree', () => { it('resolves with two block proofs for a two-block checkpoint', async () => { const numBlocks = 2; const numTxsPerBlock = 1; - const { constants, blocks, l1ToL2Messages, previousBlockHeader } = await context.makeCheckpoint(numBlocks, { - numTxsPerBlock, - }); + const { constants, blocks, l1ToL2Messages, l1ToL2MessageBundle, previousBlockHeader } = + await context.makeCheckpoint(numBlocks, { + numTxsPerBlock, + }); const subTree = await CheckpointSubTreeOrchestrator.start( context.worldState, @@ -96,7 +98,7 @@ describe('prover/orchestrator/checkpoint-sub-tree', () => { false, makeTestDeferredJobQueue(), constants, - l1ToL2Messages, + l1ToL2MessageBundle, Fr.ZERO, numBlocks, previousBlockHeader, @@ -126,10 +128,11 @@ describe('prover/orchestrator/checkpoint-sub-tree', () => { // Cross-chain messages flow into the checkpoint's first block via the L1-to-L2 // message tree; the sub-tree must prove them through without error (A-1039). const numBlocks = 1; - const { constants, blocks, l1ToL2Messages, previousBlockHeader } = await context.makeCheckpoint(numBlocks, { - numTxsPerBlock: 1, - numL1ToL2Messages: 3, - }); + const { constants, blocks, l1ToL2Messages, l1ToL2MessageBundle, previousBlockHeader } = + await context.makeCheckpoint(numBlocks, { + numTxsPerBlock: 1, + numL1ToL2Messages: 3, + }); expect(l1ToL2Messages.length).toBe(3); const subTree = await CheckpointSubTreeOrchestrator.start( @@ -141,7 +144,7 @@ describe('prover/orchestrator/checkpoint-sub-tree', () => { false, makeTestDeferredJobQueue(), constants, - l1ToL2Messages, + l1ToL2MessageBundle, Fr.ZERO, numBlocks, previousBlockHeader, @@ -170,13 +173,14 @@ describe('prover/orchestrator/checkpoint-sub-tree', () => { // L2-to-L1 (cross-chain) messages are carried on the public tx effects; the sub-tree // must prove them through the base/block rollups without error (A-1039). const numBlocks = 1; - const { constants, blocks, l1ToL2Messages, previousBlockHeader } = await context.makeCheckpoint(numBlocks, { - numTxsPerBlock: 1, - makeProcessedTxOpts: () => ({ - privateOnly: false, - avmAccumulatedData: { l2ToL1Msgs: makeL2ToL1Messages(2) }, - }), - }); + const { constants, blocks, l1ToL2Messages, l1ToL2MessageBundle, previousBlockHeader } = + await context.makeCheckpoint(numBlocks, { + numTxsPerBlock: 1, + makeProcessedTxOpts: () => ({ + privateOnly: false, + avmAccumulatedData: { l2ToL1Msgs: makeL2ToL1Messages(2) }, + }), + }); // Confirm the fixture actually attached the messages. expect(blocks[0].txs[0].txEffect.l2ToL1Msgs.length).toBe(2); @@ -189,7 +193,7 @@ describe('prover/orchestrator/checkpoint-sub-tree', () => { false, makeTestDeferredJobQueue(), constants, - l1ToL2Messages, + l1ToL2MessageBundle, Fr.ZERO, numBlocks, previousBlockHeader, @@ -229,10 +233,8 @@ describe('prover/orchestrator/checkpoint-sub-tree', () => { // 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] }, - ); + const { constants, blocks, l1ToL2Messages, l1ToL2MessageBundle, previousBlockHeader } = + await context.makeCheckpointWithMessagesPerBlock(l1ToL2MessagesPerBlock, { numTxsPerBlock: [1, 1, 0] }); expect(l1ToL2Messages.length).toBe(5); const subTree = await CheckpointSubTreeOrchestrator.start( @@ -244,7 +246,7 @@ describe('prover/orchestrator/checkpoint-sub-tree', () => { false, makeTestDeferredJobQueue(), constants, - l1ToL2Messages, + l1ToL2MessageBundle, Fr.ZERO, numBlocks, previousBlockHeader, 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 6c12bcfae800..9f14238bff58 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 @@ -20,7 +20,7 @@ import type { ReadonlyWorldStateAccess, ServerCircuitProver, } from '@aztec/stdlib/interfaces/server'; -import { appendL1ToL2MessagesToTree } from '@aztec/stdlib/messaging'; +import { type InboxMessageBundle, appendL1ToL2MessagesToTree } from '@aztec/stdlib/messaging'; import type { ParityPublicInputs } from '@aztec/stdlib/parity'; import { type BaseRollupHints, @@ -197,7 +197,7 @@ export class CheckpointSubTreeOrchestrator extends ProvingScheduler { cancelJobsOnStop: boolean, deferredJobQueue: SerialQueue, checkpointConstants: CheckpointConstantData, - l1ToL2Messages: Fr[], + l1ToL2Messages: InboxMessageBundle, startInboxRollingHash: Fr, totalNumBlocks: number, headerOfLastBlockInPreviousCheckpoint: BlockHeader, @@ -518,7 +518,7 @@ export class CheckpointSubTreeOrchestrator extends ProvingScheduler { */ private async startCheckpoint( constants: CheckpointConstantData, - l1ToL2Messages: Fr[], + l1ToL2Messages: InboxMessageBundle, startInboxRollingHash: Fr, totalNumBlocks: number, headerOfLastBlockInPreviousCheckpoint: BlockHeader, 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 1d7e8aab5805..46ed8d5a290e 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 @@ -59,7 +59,7 @@ describe('prover/orchestrator/top-tree', () => { false, makeTestDeferredJobQueue(), fixture.constants, - fixture.l1ToL2Messages, + fixture.l1ToL2MessageBundle, fixture.startInboxRollingHash, fixture.blocks.length, fixture.previousBlockHeader, @@ -209,10 +209,10 @@ describe('prover/orchestrator/top-tree', () => { // 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 + const epochBundle = [...a.fixture.l1ToL2MessageBundle, ...b.fixture.l1ToL2MessageBundle]; + expect(epochBundle.flat().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)); + expect(result.publicInputs.endInboxRollingHash).toEqual(accumulateInboxRollingHash(Fr.ZERO, epochBundle)); } finally { await topTree.stop(); } diff --git a/yarn-project/prover-client/src/prover-client/prover-client.ts b/yarn-project/prover-client/src/prover-client/prover-client.ts index c9040b46b685..cc5a88f345da 100644 --- a/yarn-project/prover-client/src/prover-client/prover-client.ts +++ b/yarn-project/prover-client/src/prover-client/prover-client.ts @@ -17,6 +17,7 @@ import { type ServerCircuitProver, tryStop, } from '@aztec/stdlib/interfaces/server'; +import type { InboxMessageBundle } from '@aztec/stdlib/messaging'; import type { CheckpointConstantData } from '@aztec/stdlib/rollup'; import type { BlockHeader } from '@aztec/stdlib/tx'; import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client'; @@ -55,7 +56,7 @@ export interface EpochProverFactory { chonkCache: ChonkCache, epochNumber: EpochNumber, checkpointConstants: CheckpointConstantData, - l1ToL2Messages: Fr[], + l1ToL2Messages: InboxMessageBundle, startInboxRollingHash: Fr, totalNumBlocks: number, headerOfLastBlockInPreviousCheckpoint: BlockHeader, @@ -133,7 +134,7 @@ export class ProverClient implements EpochProverManager, EpochProverFactory { chonkCache: ChonkCache, epochNumber: EpochNumber, checkpointConstants: CheckpointConstantData, - l1ToL2Messages: Fr[], + l1ToL2Messages: InboxMessageBundle, startInboxRollingHash: Fr, totalNumBlocks: number, headerOfLastBlockInPreviousCheckpoint: BlockHeader, 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 26c2b81c50bf..4dfb2a31487d 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 @@ -67,7 +67,8 @@ describe('prover/bb_prover/full-rollup', () => { // Drive each checkpoint through its own sub-tree, mirroring the production // CheckpointProver flow. The top tree starts proving as each sub-tree completes. for (let checkpointIndex = 0; checkpointIndex < numCheckpoints; checkpointIndex++) { - const { constants, blocks, l1ToL2Messages, previousBlockHeader, checkpoint } = checkpoints[checkpointIndex]; + const { constants, blocks, l1ToL2Messages, l1ToL2MessageBundle, previousBlockHeader, checkpoint } = + checkpoints[checkpointIndex]; const previousInboxRollingHash = checkpointIndex === 0 ? Fr.ZERO : checkpoints[checkpointIndex - 1].checkpoint.header.inboxRollingHash; @@ -82,7 +83,7 @@ describe('prover/bb_prover/full-rollup', () => { /* cancelJobsOnStop */ false, makeTestDeferredJobQueue(), constants, - l1ToL2Messages, + l1ToL2MessageBundle, previousInboxRollingHash, numBlockPerCheckpoint, previousBlockHeader, diff --git a/yarn-project/prover-client/src/test/bb_prover_parity.test.ts b/yarn-project/prover-client/src/test/bb_prover_parity.test.ts index 61632396dc05..dc1fd3269a4a 100644 --- a/yarn-project/prover-client/src/test/bb_prover_parity.test.ts +++ b/yarn-project/prover-client/src/test/bb_prover_parity.test.ts @@ -47,7 +47,7 @@ describe('prover/bb_prover/parity', () => { const messages = Array.from({ length: size }, () => Fr.random()); const proverId = Fr.random(); - const inputs = InboxParityPrivateInputs.fromMessages(messages, Fr.ZERO, proverId); + const inputs = InboxParityPrivateInputs.fromMessages([messages], Fr.ZERO, proverId); expect(inputs.size).toBe(size); const output = await context.prover.getInboxParityProof(inputs); 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 79059cc680af..2eaaf038335e 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 @@ -151,7 +151,8 @@ describeOrSkip('prover/regenerate-rollup-sample-inputs', () => { try { for (let checkpointIndex = 0; checkpointIndex < numCheckpoints; checkpointIndex++) { - const { constants, blocks, l1ToL2Messages, previousBlockHeader, checkpoint } = checkpoints[checkpointIndex]; + const { constants, blocks, l1ToL2Messages, l1ToL2MessageBundle, previousBlockHeader, checkpoint } = + checkpoints[checkpointIndex]; // First checkpoint starts from genesis; the multi-checkpoint scenario carries no messages, // so every checkpoint's previous rolling hash is zero. @@ -166,7 +167,7 @@ describeOrSkip('prover/regenerate-rollup-sample-inputs', () => { /* cancelJobsOnStop */ false, makeTestDeferredJobQueue(), constants, - l1ToL2Messages, + l1ToL2MessageBundle, previousInboxRollingHash, numBlocksPerCheckpoint, previousBlockHeader, diff --git a/yarn-project/prover-node/src/actions/rerun-epoch-proving-job.ts b/yarn-project/prover-node/src/actions/rerun-epoch-proving-job.ts index d909738c118b..a02ab3aa0dd0 100644 --- a/yarn-project/prover-node/src/actions/rerun-epoch-proving-job.ts +++ b/yarn-project/prover-node/src/actions/rerun-epoch-proving-job.ts @@ -12,6 +12,7 @@ import type { L2Block } from '@aztec/stdlib/block'; import { getEpochAtSlot, getSlotRangeForEpoch } from '@aztec/stdlib/epoch-helpers'; import type { ITxProvider } from '@aztec/stdlib/interfaces/server'; import type { DataStoreConfig } from '@aztec/stdlib/kv-store'; +import { EMPTY_BUNDLE } from '@aztec/stdlib/messaging'; import { MerkleTreeId } from '@aztec/stdlib/trees'; import type { Tx, TxHash } from '@aztec/stdlib/tx'; import type { GenesisData } from '@aztec/stdlib/world-state'; @@ -169,7 +170,7 @@ async function buildCheckpointProver(ctx: RerunContext, index: number, log: Logg index === 0 ? jobData.previousBlockHeader : jobData.checkpoints[index - 1].blocks.at(-1)!.header; const previousInboxRollingHash = index === 0 ? jobData.previousInboxRollingHash : jobData.checkpoints[index - 1].header.inboxRollingHash; - const l1ToL2Messages = jobData.l1ToL2Messages[checkpoint.number] ?? []; + const l1ToL2Messages = jobData.l1ToL2Messages[checkpoint.number] ?? EMPTY_BUNDLE; const previousArchiveSiblingPath = await getLastSiblingPath( MerkleTreeId.ARCHIVE, worldState.getSnapshot(BlockNumber(checkpoint.blocks[0].number - 1)), diff --git a/yarn-project/prover-node/src/job/checkpoint-prover.ts b/yarn-project/prover-node/src/job/checkpoint-prover.ts index 5518bab686a7..f346e492367d 100644 --- a/yarn-project/prover-node/src/job/checkpoint-prover.ts +++ b/yarn-project/prover-node/src/job/checkpoint-prover.ts @@ -19,6 +19,7 @@ import { PublicSimulatorConfig } from '@aztec/stdlib/avm'; import type { CommitteeAttestation, L2Block } from '@aztec/stdlib/block'; import type { Checkpoint } from '@aztec/stdlib/checkpoint'; import type { ForkMerkleTreeOperations, ITxProvider } from '@aztec/stdlib/interfaces/server'; +import { type InboxMessageBundle, bundleLength, flattenBundle } from '@aztec/stdlib/messaging'; import { CheckpointConstantData } from '@aztec/stdlib/rollup'; import { MerkleTreeId } from '@aztec/stdlib/trees'; import type { BlockHeader, ProcessedTx, Tx, TxHash } from '@aztec/stdlib/tx'; @@ -67,7 +68,7 @@ export type CheckpointProverArgs = { epochNumber: EpochNumber; attestations: CommitteeAttestation[]; previousBlockHeader: BlockHeader; - l1ToL2Messages: Fr[]; + l1ToL2Messages: InboxMessageBundle; /** Inbox rolling hash of the previous checkpoint (this checkpoint's chain start); genesis is zero. */ previousInboxRollingHash: Fr; previousArchiveSiblingPath: Tuple; @@ -101,7 +102,7 @@ export class CheckpointProver { readonly slotNumber: SlotNumber; readonly attestations: CommitteeAttestation[]; readonly previousBlockHeader: BlockHeader; - readonly l1ToL2Messages: Fr[]; + readonly l1ToL2Messages: InboxMessageBundle; readonly previousInboxRollingHash: Fr; readonly previousArchiveSiblingPath: Tuple; @@ -151,7 +152,7 @@ export class CheckpointProver { epochNumber: this.epochNumber, slotNumber: this.slotNumber, blockCount: this.checkpoint.blocks.length, - l1ToL2MessageCount: this.l1ToL2Messages.length, + l1ToL2MessageCount: bundleLength(this.l1ToL2Messages), archiveRoot: this.checkpoint.archive.root.toString(), }); // Kick off the eager gather + sub-tree pipeline. @@ -377,7 +378,8 @@ export class CheckpointProver { // 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; + const checkpointLeaves = flattenBundle(this.l1ToL2Messages); + const checkpointStartLeafCount = l1ToL2LeafCount(this.checkpoint.blocks.at(-1)!) - checkpointLeaves.length; for (let blockIndex = 0; blockIndex < this.checkpoint.blocks.length; blockIndex++) { const blockTimer = new Timer(); @@ -387,7 +389,7 @@ export class CheckpointProver { const prevLeafCount = blockIndex === 0 ? checkpointStartLeafCount : l1ToL2LeafCount(this.checkpoint.blocks[blockIndex - 1]); - const blockMessages = this.l1ToL2Messages.slice( + const blockMessages = checkpointLeaves.slice( prevLeafCount - checkpointStartLeafCount, l1ToL2LeafCount(block) - checkpointStartLeafCount, ); diff --git a/yarn-project/prover-node/src/job/epoch-proving-job-data.test.ts b/yarn-project/prover-node/src/job/epoch-proving-job-data.test.ts index 58652eb13217..0683e22ce505 100644 --- a/yarn-project/prover-node/src/job/epoch-proving-job-data.test.ts +++ b/yarn-project/prover-node/src/job/epoch-proving-job-data.test.ts @@ -24,10 +24,10 @@ describe('EpochProvingJobData', () => { ), txs, l1ToL2Messages: { - [CheckpointNumber(0)]: [Fr.random(), Fr.random()], - [CheckpointNumber(1)]: [Fr.random()], - [CheckpointNumber(2)]: [Fr.random(), Fr.random(), Fr.random()], - [CheckpointNumber(3)]: [Fr.random()], + [CheckpointNumber(0)]: [[Fr.random(), Fr.random()]], + [CheckpointNumber(1)]: [[Fr.random()], [Fr.random(), Fr.random()]], + [CheckpointNumber(2)]: [[Fr.random(), Fr.random(), Fr.random()]], + [CheckpointNumber(3)]: [[Fr.random()]], }, previousBlockHeader: BlockHeader.random(), previousInboxRollingHash: Fr.random(), diff --git a/yarn-project/prover-node/src/job/epoch-proving-job-data.ts b/yarn-project/prover-node/src/job/epoch-proving-job-data.ts index c2ee95938d6e..517c73f11b10 100644 --- a/yarn-project/prover-node/src/job/epoch-proving-job-data.ts +++ b/yarn-project/prover-node/src/job/epoch-proving-job-data.ts @@ -3,6 +3,7 @@ import { Fr } from '@aztec/foundation/curves/bn254'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { CommitteeAttestation } from '@aztec/stdlib/block'; import { Checkpoint } from '@aztec/stdlib/checkpoint'; +import type { InboxMessageBundle } from '@aztec/stdlib/messaging'; import { BlockHeader, Tx } from '@aztec/stdlib/tx'; /** All data from an epoch used in proving. */ @@ -10,7 +11,7 @@ export type EpochProvingJobData = { epochNumber: EpochNumber; checkpoints: Checkpoint[]; txs: Map; - l1ToL2Messages: Record; + l1ToL2Messages: Record; previousBlockHeader: BlockHeader; /** Inbox rolling hash of the checkpoint before the epoch's first checkpoint (its chain start); genesis is zero. */ previousInboxRollingHash: Fr; @@ -40,10 +41,11 @@ export function validateEpochProvingJobData(data: EpochProvingJobData) { export function serializeEpochProvingJobData(data: EpochProvingJobData): Buffer { const checkpoints = data.checkpoints.map(checkpoint => checkpoint.toBuffer()); const txs = Array.from(data.txs.values()).map(tx => tx.toBuffer()); - const l1ToL2Messages = Object.entries(data.l1ToL2Messages).map(([checkpointNumber, messages]) => [ + // Each checkpoint's bundle is written as a bucket count followed by one length-prefixed vector per bucket. + const l1ToL2Messages = Object.entries(data.l1ToL2Messages).map(([checkpointNumber, bundle]) => [ Number(checkpointNumber), - messages.length, - ...messages, + bundle.length, + ...bundle.map(bucket => [bucket.length, ...bucket]), ]); const attestations = data.attestations.map(attestation => attestation.toBuffer()); @@ -71,11 +73,11 @@ export function deserializeEpochProvingJobData(buf: Buffer): EpochProvingJobData const txArray = reader.readVector(Tx); const l1ToL2MessageCheckpointCount = reader.readNumber(); - const l1ToL2Messages: Record = {}; + const l1ToL2Messages: Record = {}; for (let i = 0; i < l1ToL2MessageCheckpointCount; i++) { const checkpointNumber = CheckpointNumber(reader.readNumber()); - const messages = reader.readVector(Fr); - l1ToL2Messages[checkpointNumber] = messages; + const bucketCount = reader.readNumber(); + l1ToL2Messages[checkpointNumber] = Array.from({ length: bucketCount }, () => reader.readVector(Fr)); } const attestations = reader.readVector(CommitteeAttestation); diff --git a/yarn-project/prover-node/src/session-manager.ts b/yarn-project/prover-node/src/session-manager.ts index b612256ff568..399a14bafa62 100644 --- a/yarn-project/prover-node/src/session-manager.ts +++ b/yarn-project/prover-node/src/session-manager.ts @@ -1,5 +1,4 @@ import { BlockNumber, type EpochNumber } from '@aztec/foundation/branded-types'; -import { Fr } from '@aztec/foundation/curves/bn254'; import type { EthAddress } from '@aztec/foundation/eth-address'; import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log'; import { SerialQueue } from '@aztec/foundation/queue'; @@ -15,6 +14,7 @@ import { getSlotRangeForEpoch, } from '@aztec/stdlib/epoch-helpers'; import type { EpochProvingJobState } from '@aztec/stdlib/interfaces/server'; +import type { InboxMessageBundle } from '@aztec/stdlib/messaging'; import type { CheckpointStore } from './checkpoint-store.js'; import { CheckpointProver } from './job/checkpoint-prover.js'; @@ -443,7 +443,7 @@ export class SessionManager { // for the snapshot. The pool retains them past the proving window (A-1274), so this is durable. const perCheckpoint = await Promise.all(checkpoints.map(async c => ({ c, txs: await c.getTxsForUpload() }))); const txs = new Map(); - const l1ToL2Messages: Record = {}; + const l1ToL2Messages: Record = {}; for (const { c, txs: checkpointTxs } of perCheckpoint) { for (const [hash, tx] of checkpointTxs) { txs.set(hash, tx); 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 a65d86047d59..e9cba4640221 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 @@ -79,6 +79,7 @@ import { } from '@aztec/stdlib/epoch-helpers'; import { Gas, GasFees, GasSettings } from '@aztec/stdlib/gas'; import { tryStop } from '@aztec/stdlib/interfaces/server'; +import { EMPTY_BUNDLE, type InboxMessageBundle } from '@aztec/stdlib/messaging'; import { CheckpointProposal, ConsensusPayload, @@ -491,7 +492,7 @@ describe('L1Publisher integration', () => { const buildCheckpoint = async ( globalVariables: GlobalVariables, txs: ProcessedTx[], - l1ToL2Messages: Fr[], + l1ToL2Messages: InboxMessageBundle, previousCheckpointOutHashes: Fr[] = [], previousInboxRollingHash: Fr = Fr.ZERO, ): Promise => { @@ -526,7 +527,7 @@ describe('L1Publisher integration', () => { }; const buildSingleCheckpoint = async ( - opts: { l1ToL2Messages?: Fr[]; blockNumber?: BlockNumber; slot?: SlotNumber } = {}, + opts: { l1ToL2Messages?: InboxMessageBundle; blockNumber?: BlockNumber; slot?: SlotNumber } = {}, ) => { // By default a single checkpoint consumes no Inbox messages (bucketHint 0 against the genesis bucket). const l1ToL2Messages = opts.l1ToL2Messages ?? []; @@ -550,7 +551,7 @@ describe('L1Publisher integration', () => { }; const buildSingleCheckpointForPipelinedProposer = async ( - opts: { l1ToL2Messages?: Fr[]; blockNumber?: BlockNumber } = {}, + opts: { l1ToL2Messages?: InboxMessageBundle; blockNumber?: BlockNumber } = {}, ) => { const slot = await getPipelinedProposalSlot(); proposer = await epochCache.getProposerAttesterAddressInSlot(slot); @@ -685,7 +686,7 @@ describe('L1Publisher integration', () => { isLastBlock: true, cutoffTimestamp, }); - const currentL1ToL2Messages = selection.consume ? selection.bundle : []; + const currentL1ToL2Messages = selection.consume ? selection.bundle : EMPTY_BUNDLE; const bucketHint = selection.consume ? selection.bucket.seq : parent.seq; const checkpoint = await buildCheckpoint( @@ -1088,7 +1089,7 @@ 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 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 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 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 3acd2ffc5c63..dc109b9dea53 100644 --- a/yarn-project/sequencer-client/src/sequencer/automine/automine_sequencer.ts +++ b/yarn-project/sequencer-client/src/sequencer/automine/automine_sequencer.ts @@ -3,7 +3,6 @@ import { MAX_L1_TO_L2_MSGS_PER_BLOCK, MAX_L1_TO_L2_MSGS_PER_CHECKPOINT } from '@ 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'; @@ -24,7 +23,13 @@ import { getTimestampForSlot, } from '@aztec/stdlib/epoch-helpers'; import { InsufficientValidTxsError, type WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server'; -import { type L1ToL2MessageSource, getInboxCutoffTimestamp } from '@aztec/stdlib/messaging'; +import { + EMPTY_BUNDLE, + type InboxMessageBundle, + type L1ToL2MessageSource, + bundleLength, + 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'; @@ -495,7 +500,7 @@ export class AutomineSequencer { isLastBlock: true, cutoffTimestamp: getInboxCutoffTimestamp(SlotNumber(targetSlot), this.deps.l1Constants), }); - const streamingBundle = selection.consume ? selection.bundle : []; + const streamingBundle = selection.consume ? selection.bundle : EMPTY_BUNDLE; const bucketHint = selection.consume ? selection.bucket.seq : parentBucket.seq; const checkpointBuilder = await this.deps.checkpointsBuilder.startCheckpoint( @@ -791,7 +796,7 @@ export class AutomineSequencer { timestamp: bigint, allowEmpty: boolean, checkpointNumber: CheckpointNumber, - l1ToL2Messages: Fr[], + l1ToL2Messages: InboxMessageBundle, ): Promise { let buildResult: BuildBlockInCheckpointResult; try { @@ -799,7 +804,7 @@ export class AutomineSequencer { maxTransactions: this.deps.config.maxTxsPerBlock, // 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, + minValidTxs: allowEmpty || bundleLength(l1ToL2Messages) > 0 ? 0 : 1, isBuildingProposal: true, maxBlocksPerCheckpoint: 1, perBlockAllocationMultiplier: 1, 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 f41f118479ee..3590bcbc9150 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 @@ -1223,8 +1223,8 @@ describe('CheckpointProposalJob', () => { .mockResolvedValueOnce(makeBucket(2n, 2n, 1n)) .mockResolvedValue(makeBucket(3n, 4n, 3n)); l1ToL2MessageSource.getL1ToL2MessagesBetweenBuckets - .mockResolvedValueOnce([new Fr(1), new Fr(2)]) - .mockResolvedValue([new Fr(3), new Fr(4)]); + .mockResolvedValueOnce([[new Fr(1), new Fr(2)]]) + .mockResolvedValue([[new Fr(3), new Fr(4)]]); const { lastBlock } = await setupMultipleBlocks(2, [2, 0]); validatorClient.collectAttestations.mockResolvedValue(getAttestations(lastBlock)); @@ -1233,7 +1233,7 @@ describe('CheckpointProposalJob', () => { await job.executeAndAwait(); expect(checkpointBuilder.buildBlockCalls).toHaveLength(2); - expect(checkpointBuilder.buildBlockCalls[1].opts.l1ToL2Messages).toEqual([new Fr(3), new Fr(4)]); + expect(checkpointBuilder.buildBlockCalls[1].opts.l1ToL2Messages).toEqual([[new Fr(3), new Fr(4)]]); expect(checkpointBuilder.buildBlockCalls[1].opts.minValidTxs).toBe(0); }); @@ -1508,7 +1508,7 @@ describe('CheckpointProposalJob', () => { l1BlockNumber: 2n, l1BlockHash: Buffer32.fromBigInt(2n), }; - const bundle = Array.from({ length: 5 }, (_, i) => new Fr(i + 1)); + const bundle = [Array.from({ length: 5 }, (_, i) => new Fr(i + 1))]; l1ToL2MessageSource.getLatestInboxBucketAtOrBefore.mockResolvedValue(bucket); l1ToL2MessageSource.getL1ToL2MessagesBetweenBuckets.mockResolvedValue(bundle); @@ -1550,7 +1550,7 @@ describe('CheckpointProposalJob', () => { l1BlockNumber: 2n, l1BlockHash: Buffer32.fromBigInt(2n), }; - const bundle = Array.from({ length: 5 }, (_, i) => new Fr(i + 1)); + const bundle = [Array.from({ length: 5 }, (_, i) => new Fr(i + 1))]; l1ToL2MessageSource.getLatestInboxBucketAtOrBefore.mockResolvedValue(bucket); l1ToL2MessageSource.getL1ToL2MessagesBetweenBuckets.mockResolvedValue(bundle); @@ -1590,7 +1590,7 @@ describe('CheckpointProposalJob', () => { l1BlockNumber: 2n, l1BlockHash: Buffer32.fromBigInt(2n), }; - const bundle = Array.from({ length: 5 }, (_, i) => new Fr(i + 1)); + const bundle = [Array.from({ length: 5 }, (_, i) => new Fr(i + 1))]; l1ToL2MessageSource.getLatestInboxBucketAtOrBefore.mockResolvedValue(bucket); l1ToL2MessageSource.getL1ToL2MessagesBetweenBuckets.mockResolvedValue(bundle); @@ -1638,11 +1638,11 @@ describe('CheckpointProposalJob', () => { l1ToL2MessageSource.getLatestInboxBucketAtOrBefore.mockResolvedValue(buckets[3]); l1ToL2MessageSource.getInboxBucket.mockImplementation(seq => Promise.resolve(buckets[Number(seq) - 1])); l1ToL2MessageSource.getL1ToL2MessagesBetweenBuckets.mockImplementation((from, to) => - Promise.resolve( + Promise.resolve([ Array.from({ length: Number(totals[Number(to) - 1] - (from === 0n ? 0n : totals[Number(from) - 1])) }, () => Fr.random(), ), - ), + ]), ); const { lastBlock } = await setupMultipleBlocks(2, [2, 1]); 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 7a48f9a12d91..25a6fdd85f87 100644 --- a/yarn-project/sequencer-client/src/sequencer/checkpoint_proposal_job.ts +++ b/yarn-project/sequencer-client/src/sequencer/checkpoint_proposal_job.ts @@ -14,7 +14,6 @@ import { generateRecoverableSignature, generateUnrecoverableSignature, } from '@aztec/foundation/crypto/secp256k1-signer'; -import { Fr } from '@aztec/foundation/curves/bn254'; import { InterruptError, TimeoutError } from '@aztec/foundation/error'; import { EthAddress } from '@aztec/foundation/eth-address'; import { Signature } from '@aztec/foundation/eth-signature'; @@ -54,9 +53,12 @@ import { type WorldStateSynchronizer, } from '@aztec/stdlib/interfaces/server'; import { + EMPTY_BUNDLE, type InboxBucket, InboxBucketRef, + type InboxMessageBundle, type L1ToL2MessageSource, + bundleLength, getInboxCutoffTimestamp, isInboxConsumptionSufficient, } from '@aztec/stdlib/messaging'; @@ -1031,7 +1033,11 @@ export class CheckpointProposalJob implements Traceable { break; } - const streamingBundle = streamingState ? (selection && selection.consume ? selection.bundle : []) : undefined; + const streamingBundle = streamingState + ? selection && selection.consume + ? selection.bundle + : EMPTY_BUNDLE + : undefined; const buildResult = await this.buildSingleBlock(checkpointBuilder, { // Create all blocks with the same timestamp @@ -1256,7 +1262,7 @@ export class CheckpointProposalJob implements Traceable { buildDeadline: Date | undefined; txHashesAlreadyIncluded: Set; /** Streaming Inbox message bundle for this block's L1-to-L2 tree; undefined when it consumes nothing. */ - l1ToL2Messages?: Fr[]; + l1ToL2Messages?: InboxMessageBundle; }, ): Promise< { block: L2Block; usedTxs: Tx[] } | { failure: 'insufficient-txs' | 'insufficient-valid-txs' } | { error: Error } @@ -1327,7 +1333,7 @@ export class CheckpointProposalJob implements Traceable { // nor messages past the first block is pure padding, so the floor for minValidTxs is 1 there. const configuredMinValidTxs = forceCreate ? 0 : (this.config.minValidTxsPerBlock ?? minTxs); const minValidTxs = - indexWithinCheckpoint > 0 && (l1ToL2Messages?.length ?? 0) === 0 + indexWithinCheckpoint > 0 && bundleLength(l1ToL2Messages ?? EMPTY_BUNDLE) === 0 ? Math.max(configuredMinValidTxs, 1) : configuredMinValidTxs; const blockBuilderOptions: BlockBuilderOptions = { @@ -1477,7 +1483,7 @@ export class CheckpointProposalJob implements Traceable { 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[]; + l1ToL2Messages?: InboxMessageBundle; }): Promise<{ canStartBuilding: boolean; minTxs: number }> { const { indexWithinCheckpoint, blockNumber, buildDeadline, forceCreate } = opts; @@ -1485,7 +1491,7 @@ export class CheckpointProposalJob implements Traceable { // 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 hasStreamingBundle = bundleLength(opts.l1ToL2Messages ?? EMPTY_BUNDLE) > 0; const minTxs = hasStreamingBundle ? 0 : indexWithinCheckpoint > 0 && this.config.minTxsPerBlock === 0 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 index 7240fe4ebdf0..2933eb13f039 100644 --- a/yarn-project/sequencer-client/src/sequencer/inbox_bucket_selector.test.ts +++ b/yarn-project/sequencer-client/src/sequencer/inbox_bucket_selector.test.ts @@ -56,19 +56,17 @@ function makeSource(specs: TestBucketSpec[]): { return Promise.resolve(eligible.length === 0 ? undefined : eligible[eligible.length - 1]); }, getL1ToL2MessagesBetweenBuckets: (fromExclusive: bigint, toInclusive: bigint) => { - const toBucket = buckets.get(toInclusive); - if (toBucket === undefined) { + if (buckets.get(toInclusive) === undefined || (fromExclusive > 0n && buckets.get(fromExclusive) === 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))); + // One group per bucket in the range, as the archiver returns them. + return Promise.resolve( + ordered + .filter(bucket => bucket.seq > fromExclusive && bucket.seq <= toInclusive && bucket.msgCount > 0) + .map(bucket => + leaves.slice(Number(bucket.lastMessageIndex + 1n) - bucket.msgCount, Number(bucket.lastMessageIndex + 1n)), + ), + ); }, }; @@ -139,7 +137,8 @@ describe('selectInboxBucketForBlock', () => { expect(result).toMatchObject({ consume: true }); if (result.consume) { expect(result.bucket.seq).toBe(2n); - expect(result.bundle).toHaveLength(5); // buckets 1 (3) + 2 (2) + expect(result.bundle.flat()).toHaveLength(5); // buckets 1 (3) + 2 (2) + expect(result.bundle).toHaveLength(2); // one group per bucket } }); @@ -160,7 +159,7 @@ describe('selectInboxBucketForBlock', () => { expect(result).toMatchObject({ consume: true }); if (result.consume) { expect(result.bucket.seq).toBe(2n); - expect(result.bundle).toHaveLength(5); + expect(result.bundle.flat()).toHaveLength(5); // buckets 1 (3) + 2 (2) } // The walk starts at the archiver's head bucket and stops at the first eligible one. expect(isEligible.asked).toEqual([3n, 2n]); @@ -299,7 +298,7 @@ describe('selectInboxBucketForBlock', () => { expect(result).toMatchObject({ consume: true }); if (result.consume) { expect(result.bucket.seq).toBe(2n); - expect(result.bundle).toHaveLength(400); + expect(result.bundle.flat()).toHaveLength(400); } }); @@ -350,7 +349,7 @@ describe('selectInboxBucketForBlock', () => { 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.flat()).toHaveLength(3); // only bucket 2's messages, not bucket 1's expect(second.bundle).toEqual(await source.getL1ToL2MessagesBetweenBuckets(1n, 2n)); } }); diff --git a/yarn-project/sequencer-client/src/sequencer/inbox_bucket_selector.ts b/yarn-project/sequencer-client/src/sequencer/inbox_bucket_selector.ts index e05834860cba..576ca9344ccc 100644 --- a/yarn-project/sequencer-client/src/sequencer/inbox_bucket_selector.ts +++ b/yarn-project/sequencer-client/src/sequencer/inbox_bucket_selector.ts @@ -1,6 +1,10 @@ -import type { Fr } from '@aztec/foundation/curves/bn254'; import { type Logger, createLogger } from '@aztec/foundation/log'; -import { type InboxBucket, type L1ToL2MessageSource, isInboxConsumptionSufficient } from '@aztec/stdlib/messaging'; +import { + type InboxBucket, + type InboxMessageBundle, + type L1ToL2MessageSource, + isInboxConsumptionSufficient, +} from '@aztec/stdlib/messaging'; import type { InboxBucketEligibility } from './inbox_bucket_eligibility.js'; @@ -71,8 +75,8 @@ type InboxBucketConsumption = 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 message leaves consumed this block, grouped per Inbox bucket in insertion order. */ + bundle: InboxMessageBundle; } | { /** The block consumes nothing; it reuses the parent bucket reference. */ 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 f53af34fc862..a7eaac148895 100644 --- a/yarn-project/sequencer-client/src/test/mock_checkpoint_builder.ts +++ b/yarn-project/sequencer-client/src/test/mock_checkpoint_builder.ts @@ -11,6 +11,7 @@ import type { ICheckpointsBuilder, MerkleTreeWriteOperations, } from '@aztec/stdlib/interfaces/server'; +import type { InboxMessageBundle } from '@aztec/stdlib/messaging'; import { CheckpointHeader } from '@aztec/stdlib/rollup'; import { makeAppendOnlyTreeSnapshot } from '@aztec/stdlib/testing'; import type { CheckpointGlobalVariables, Tx } from '@aztec/stdlib/tx'; @@ -210,7 +211,7 @@ export class MockCheckpointsBuilder implements ICheckpointsBuilder { public openCheckpointCalls: Array<{ checkpointNumber: CheckpointNumber; constants: CheckpointGlobalVariables; - l1ToL2Messages: Fr[]; + l1ToL2Messages: InboxMessageBundle; previousCheckpointOutHashes: Fr[]; existingBlocks: L2Block[]; feeAssetPriceModifier: bigint; @@ -287,7 +288,7 @@ export class MockCheckpointsBuilder implements ICheckpointsBuilder { checkpointNumber: CheckpointNumber, constants: CheckpointGlobalVariables, feeAssetPriceModifier: bigint, - l1ToL2Messages: Fr[], + l1ToL2Messages: InboxMessageBundle, previousCheckpointOutHashes: Fr[], _previousInboxRollingHash: Fr, _fork: MerkleTreeWriteOperations, diff --git a/yarn-project/stdlib/src/interfaces/archiver.test.ts b/yarn-project/stdlib/src/interfaces/archiver.test.ts index d505b005bea0..df3812904c93 100644 --- a/yarn-project/stdlib/src/interfaces/archiver.test.ts +++ b/yarn-project/stdlib/src/interfaces/archiver.test.ts @@ -39,6 +39,7 @@ 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 type { InboxMessageBundle } from '../messaging/inbox_message_bundle.js'; import { CheckpointHeader } from '../rollup/checkpoint_header.js'; import { getTokenContractArtifact } from '../tests/fixtures.js'; import { AppendOnlyTreeSnapshot } from '../trees/append_only_tree_snapshot.js'; @@ -234,12 +235,12 @@ describe('ArchiverApiSchema', () => { it('getL1ToL2MessagesBetweenBuckets', async () => { const result = await context.client.getL1ToL2MessagesBetweenBuckets(0n, 3n); - expect(result).toEqual([expect.any(Fr)]); + expect(result).toEqual([[expect.any(Fr)]]); }); it('getL1ToL2MessagesBetweenLeafCounts', async () => { const result = await context.client.getL1ToL2MessagesBetweenLeafCounts(0n, 3n); - expect(result).toEqual([expect.any(Fr)]); + expect(result).toEqual([[expect.any(Fr)]]); }); it('registerContractFunctionSignatures', async () => { @@ -640,15 +641,15 @@ class MockArchiver implements ArchiverApi { l1BlockHash: Buffer32.fromBigInt(20n), }); } - getL1ToL2MessagesBetweenBuckets(fromExclusive: bigint, toInclusive: bigint): Promise { + getL1ToL2MessagesBetweenBuckets(fromExclusive: bigint, toInclusive: bigint): Promise { expect(typeof fromExclusive).toEqual('bigint'); expect(typeof toInclusive).toEqual('bigint'); - return Promise.resolve([Fr.random()]); + return Promise.resolve([[Fr.random()]]); } - getL1ToL2MessagesBetweenLeafCounts(startLeafCount: bigint, endLeafCount: bigint): Promise { + getL1ToL2MessagesBetweenLeafCounts(startLeafCount: bigint, endLeafCount: bigint): Promise { expect(typeof startLeafCount).toEqual('bigint'); expect(typeof endLeafCount).toEqual('bigint'); - return Promise.resolve([Fr.random()]); + 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 605f34e03233..8b1802ceb9df 100644 --- a/yarn-project/stdlib/src/interfaces/archiver.ts +++ b/yarn-project/stdlib/src/interfaces/archiver.ts @@ -26,6 +26,7 @@ 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 { InboxMessageBundleSchema } from '../messaging/inbox_message_bundle.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'; @@ -148,11 +149,11 @@ export const ArchiverApiSchema: ApiSchemaFor = { }), getL1ToL2MessagesBetweenBuckets: z.function({ input: z.tuple([schemas.BigInt, schemas.BigInt]), - output: z.array(schemas.Fr), + output: InboxMessageBundleSchema, }), getL1ToL2MessagesBetweenLeafCounts: z.function({ input: z.tuple([schemas.BigInt, schemas.BigInt]), - output: z.array(schemas.Fr), + output: InboxMessageBundleSchema, }), getDebugFunctionName: z.function({ input: z.tuple([schemas.AztecAddress, schemas.FunctionSelector]), diff --git a/yarn-project/stdlib/src/interfaces/block-builder.ts b/yarn-project/stdlib/src/interfaces/block-builder.ts index 6d10698dab4d..0434457e63a6 100644 --- a/yarn-project/stdlib/src/interfaces/block-builder.ts +++ b/yarn-project/stdlib/src/interfaces/block-builder.ts @@ -7,6 +7,7 @@ import type { L2Block } from '../block/l2_block.js'; import type { ChainConfig, SequencerConfig } from '../config/chain-config.js'; import type { L1RollupConstants } from '../epoch-helpers/index.js'; import type { Gas } from '../gas/gas.js'; +import type { InboxMessageBundle } from '../messaging/inbox_message_bundle.js'; import type { BlockHeader } from '../tx/block_header.js'; import type { CheckpointGlobalVariables, GlobalVariables } from '../tx/global_variables.js'; import type { FailedTx, ProcessedTx } from '../tx/processed_tx.js'; @@ -57,10 +58,10 @@ 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. + * L1-to-L2 message leaves this block consumes, grouped per Inbox bucket, 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[]; + l1ToL2Messages?: InboxMessageBundle; }; /** Proposer mode: redistribution params are required. */ diff --git a/yarn-project/stdlib/src/messaging/inbox_message_bundle.ts b/yarn-project/stdlib/src/messaging/inbox_message_bundle.ts new file mode 100644 index 000000000000..38d1a4946c79 --- /dev/null +++ b/yarn-project/stdlib/src/messaging/inbox_message_bundle.ts @@ -0,0 +1,47 @@ +import { sum } from '@aztec/foundation/collection'; +import { Fr } from '@aztec/foundation/curves/bn254'; +import { schemas } from '@aztec/foundation/schemas'; + +import { z } from 'zod'; + +/** + * The L1-to-L2 message leaves consumed by a checkpoint (or by a single block within it), grouped per L1 Inbox bucket + * in insertion order. + * + * The grouping is what the Inbox rolling hash commits to: the first leaf of each group is hashed with the + * bucket-start domain separator and the rest with the plain link separator, so two histories over the same leaves + * packed into different buckets reach different rolling hashes. Every inner array is therefore non-empty — a bucket + * exists only once its first message opened it, and an empty group would claim a bucket boundary with no leaf behind + * it while flattening away silently. Bucket sequence numbers are deliberately not carried: nothing that recomputes + * the hash needs them, and the components that do resolve them separately. + */ +export type InboxMessageBundle = Fr[][]; + +/** An empty bundle: a checkpoint or block that consumed no messages. Must not be mutated. */ +export const EMPTY_BUNDLE: InboxMessageBundle = []; + +/** Total number of message leaves in the bundle, across all buckets. */ +export function bundleLength(bundle: InboxMessageBundle): number { + return sum(bundle.map(bucket => bucket.length)); +} + +/** The bundle's message leaves in insertion order, with the bucket grouping dropped. */ +export function flattenBundle(bundle: InboxMessageBundle): Fr[] { + return bundle.flat(); +} + +/** + * Per-leaf flags marking which leaves open an Inbox bucket, aligned with the leaves {@link flattenBundle} returns. + * @throws If a bucket holds no leaves, which would silently shift every flag after it. + */ +export function bucketStartsOf(bundle: InboxMessageBundle): boolean[] { + return bundle.flatMap((bucket, index) => { + if (bucket.length === 0) { + throw new Error(`Inbox message bundle has an empty bucket at index ${index}`); + } + return bucket.map((_leaf, leafIndex) => leafIndex === 0); + }); +} + +/** Schema for a bundle crossing an RPC boundary; rejects the empty buckets the type forbids. */ +export const InboxMessageBundleSchema = z.array(z.array(schemas.Fr).nonempty()); diff --git a/yarn-project/stdlib/src/messaging/inbox_rolling_hash.test.ts b/yarn-project/stdlib/src/messaging/inbox_rolling_hash.test.ts index 97f4361101f5..c821a0bf6adc 100644 --- a/yarn-project/stdlib/src/messaging/inbox_rolling_hash.test.ts +++ b/yarn-project/stdlib/src/messaging/inbox_rolling_hash.test.ts @@ -1,48 +1,77 @@ import { Fr } from '@aztec/foundation/curves/bn254'; +import { bucketStartsOf, bundleLength, flattenBundle } from './inbox_message_bundle.js'; import { accumulateInboxRollingHash, updateInboxRollingHash } from './inbox_rolling_hash.js'; describe('inbox rolling hash', () => { - // Shared test vectors pinned against the noir `accumulate_inbox_rolling_hash` helper (FI-02). Any divergence here - // means the L1 / noir / TS rolling hashes would disagree. + // Shared test vectors, derived independently of the L1, noir and TS implementations by + // `l1-contracts/scripts/inbox_rolling_hash_vectors.py`. Any divergence here means the three rolling hashes would + // disagree. const range = (from: number, to: number) => Array.from({ length: to - from + 1 }, (_, i) => new Fr(from + i)); it('chains a single leaf from zero', () => { - expect(accumulateInboxRollingHash(Fr.ZERO, [new Fr(11)])).toEqual( - Fr.fromHexString('0x00066dfa22681f66d50aae7d84f190e3555d2d82e4a5e33c2291c3060d441f04'), + expect(accumulateInboxRollingHash(Fr.ZERO, [[new Fr(11)]])).toEqual( + Fr.fromHexString('0x00551b59fed79dcce036e55050cf38ef367abfec03557e234866ac023879b245'), ); }); - it('chains three leaves from zero', () => { - expect(accumulateInboxRollingHash(Fr.ZERO, [new Fr(11), new Fr(22), new Fr(33)])).toEqual( - Fr.fromHexString('0x0077423b713a725ce4bf0b792847c68da87c316d52921de25652756bfe4c3e81'), + it('chains three leaves in one bucket from zero', () => { + expect(accumulateInboxRollingHash(Fr.ZERO, [[new Fr(11), new Fr(22), new Fr(33)]])).toEqual( + Fr.fromHexString('0x00e6cba8a055d279f8568edc4d0969a107fcda0c48347afdfd3dfeb053aa22c7'), ); }); - it('chains 256 leaves from zero', () => { - expect(accumulateInboxRollingHash(Fr.ZERO, range(1, 256))).toEqual( - Fr.fromHexString('0x0030493fcb5915459bba42f03f283b58dfaa082dac02fbb3a494d5db8063238b'), + it('chains 256 leaves in one bucket from zero', () => { + expect(accumulateInboxRollingHash(Fr.ZERO, [range(1, 256)])).toEqual( + Fr.fromHexString('0x009ff152cad9525e1c092ae6d4fb390149de5599eac09b76b0ebd1c6e26bb504'), ); }); it('chains from a non-zero start', () => { - expect(accumulateInboxRollingHash(new Fr(0x2a), [new Fr(7), new Fr(8)])).toEqual( - Fr.fromHexString('0x00a64d14c4b0234f5d835dc202bf8f9a857bc0734baf281dccd4b4978a48b2f9'), + expect(accumulateInboxRollingHash(new Fr(0x2a), [[new Fr(7), new Fr(8)]])).toEqual( + Fr.fromHexString('0x00d84d0b60599b1c7380a723d84310d40efaa4f5673dd62e0af41b03bc9a07a6'), ); }); it('is continuous across segments', () => { const start = new Fr(0x2a); - const mid = updateInboxRollingHash(start, new Fr(7)); - expect(mid).toEqual(Fr.fromHexString('0x0048097cafad7fed00ccb578806b3855d5ee7bf11045fb8d41b2880ba36ef28f')); - // chain(chain(0x2a, [7]), [8]) == chain(0x2a, [7, 8]) - expect(accumulateInboxRollingHash(mid, [new Fr(8)])).toEqual( - accumulateInboxRollingHash(start, [new Fr(7), new Fr(8)]), + const mid = updateInboxRollingHash(start, new Fr(7), true); + expect(mid).toEqual(Fr.fromHexString('0x00f13cb848052a7ab6f1de788a5979f5a5caa8c11cf176715d63481618e3b575')); + // Continuing the same bucket in a second segment matches chaining both leaves at once. + expect(updateInboxRollingHash(mid, new Fr(8), false)).toEqual( + accumulateInboxRollingHash(start, [[new Fr(7), new Fr(8)]]), ); }); - it('returns the start unchanged for an empty list', () => { + it('commits to the bucket boundaries', () => { + const leaves = [new Fr(11), new Fr(22), new Fr(33), new Fr(44)]; + const oneBucket = accumulateInboxRollingHash(Fr.ZERO, [leaves]); + const twoBuckets = accumulateInboxRollingHash(Fr.ZERO, [leaves.slice(0, 2), leaves.slice(2)]); + + expect(oneBucket).toEqual(Fr.fromHexString('0x00e37b7cc5526ab379c54209bc1c6a4ba2c457d024330281b97a533561701551')); + expect(twoBuckets).toEqual(Fr.fromHexString('0x00fa0346e7c4ee1bdf29a48af28182fdc236e2936e4d0c2e951dbd4b9b6464fc')); + expect(oneBucket).not.toEqual(twoBuckets); + }); + + it('returns the start unchanged for an empty bundle', () => { const start = new Fr(0x2a); expect(accumulateInboxRollingHash(start, [])).toEqual(start); }); }); + +describe('inbox message bundle', () => { + const bundle = [[new Fr(11), new Fr(22)], [new Fr(33)]]; + + it('flattens the buckets in insertion order', () => { + expect(flattenBundle(bundle)).toEqual([new Fr(11), new Fr(22), new Fr(33)]); + expect(bundleLength(bundle)).toBe(3); + }); + + it('flags the first leaf of every bucket', () => { + expect(bucketStartsOf(bundle)).toEqual([true, false, true]); + }); + + it('rejects an empty bucket', () => { + expect(() => bucketStartsOf([[new Fr(11)], []])).toThrow('empty bucket at index 1'); + }); +}); diff --git a/yarn-project/stdlib/src/messaging/inbox_rolling_hash.ts b/yarn-project/stdlib/src/messaging/inbox_rolling_hash.ts index b911437a7936..ac76566ae0c8 100644 --- a/yarn-project/stdlib/src/messaging/inbox_rolling_hash.ts +++ b/yarn-project/stdlib/src/messaging/inbox_rolling_hash.ts @@ -3,22 +3,34 @@ import { sha256ToField } from '@aztec/foundation/crypto/sha256'; import { Fr } from '@aztec/foundation/curves/bn254'; import { numToUInt32BE } from '@aztec/foundation/serialize'; +import type { InboxMessageBundle } from './inbox_message_bundle.js'; + /** * Extends the Inbox rolling-hash chain by a single message leaf, returning the new rolling hash. * - * Each link is `sha256ToField(DOM_SEP__INBOX_ROLLING_HASH || prev || leaf)` over the 4-byte big-endian domain - * separator followed by the two 32-byte big-endian values, matching the truncated-to-field sha256 the L1 Inbox - * accumulates and the noir `accumulate_inbox_rolling_hash` helper. The separator distinguishes a chain link from the - * untagged `outHash` merkle node hash, which absorbs the same two-field preimage shape. + * Each link is `sha256ToField(separator || prev || leaf)` over the 4-byte big-endian domain separator followed by the + * two 32-byte big-endian values, matching the truncated-to-field sha256 the L1 Inbox accumulates and the noir + * `accumulate_inbox_rolling_hash` helper. The separator is `INBOX_ROLLING_HASH_BUCKET_START` when the leaf is the + * first message of an L1 bucket and `INBOX_ROLLING_HASH` otherwise, so the chain commits to how L1 packed the + * messages into buckets. Both separators distinguish a chain link from the untagged `outHash` merkle node hash, which + * absorbs the same two-field preimage shape. */ -export function updateInboxRollingHash(prev: Fr, leaf: Fr): Fr { - return sha256ToField([numToUInt32BE(DomainSeparator.INBOX_ROLLING_HASH), prev.toBuffer(), leaf.toBuffer()]); +export function updateInboxRollingHash(prev: Fr, leaf: Fr, opensBucket: boolean): Fr { + const separator = opensBucket ? DomainSeparator.INBOX_ROLLING_HASH_BUCKET_START : DomainSeparator.INBOX_ROLLING_HASH; + return sha256ToField([numToUInt32BE(separator), prev.toBuffer(), leaf.toBuffer()]); } /** - * Extends the Inbox rolling-hash chain by a list of message leaves, in order, returning the new rolling hash. - * The genesis rolling hash is `Fr.ZERO`, and an empty list returns `start` unchanged. + * Extends the Inbox rolling-hash chain by a bundle of message leaves, in order, returning the new rolling hash. The + * first leaf of every bucket in the bundle opens a bucket. The genesis rolling hash is `Fr.ZERO`, and an empty bundle + * returns `start` unchanged. */ -export function accumulateInboxRollingHash(start: Fr, leaves: Fr[]): Fr { - return leaves.reduce(updateInboxRollingHash, start); +export function accumulateInboxRollingHash(start: Fr, bundle: InboxMessageBundle): Fr { + let acc = start; + for (const bucket of bundle) { + for (const [index, leaf] of bucket.entries()) { + acc = updateInboxRollingHash(acc, leaf, index === 0); + } + } + return acc; } diff --git a/yarn-project/stdlib/src/messaging/index.ts b/yarn-project/stdlib/src/messaging/index.ts index 3f8fc52654d4..e9eb07f1f6a5 100644 --- a/yarn-project/stdlib/src/messaging/index.ts +++ b/yarn-project/stdlib/src/messaging/index.ts @@ -1,6 +1,7 @@ export * from './append_l1_to_l2_messages.js'; export * from './inbox_bucket.js'; export * from './inbox_consumption.js'; +export * from './inbox_message_bundle.js'; export * from './inbox_rolling_hash.js'; export * from './l1_to_l2_message_bundle.js'; export * from './l1_to_l2_message_sponge.js'; 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 4040483f144b..79aeae586f89 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 { Fr } from '@aztec/foundation/curves/bn254'; import type { L2Tips } from '../block/l2_block_source.js'; import type { InboxBucket } from './inbox_bucket.js'; +import type { InboxMessageBundle } from './inbox_message_bundle.js'; /** * Interface of classes allowing for the retrieval of L1 to L2 messages. @@ -42,24 +43,24 @@ export interface L1ToL2MessageSource { /** * 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 + * order and grouped per bucket, 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; + 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 + * insertion order and grouped per Inbox bucket. 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; + getL1ToL2MessagesBetweenLeafCounts(startLeafCount: bigint, endLeafCount: bigint): Promise; /** * Returns the tips of the L2 chain. diff --git a/yarn-project/stdlib/src/parity/inbox_parity_private_inputs.ts b/yarn-project/stdlib/src/parity/inbox_parity_private_inputs.ts index def64aa457ff..8e132f81959a 100644 --- a/yarn-project/stdlib/src/parity/inbox_parity_private_inputs.ts +++ b/yarn-project/stdlib/src/parity/inbox_parity_private_inputs.ts @@ -5,6 +5,8 @@ import { bufferSchemaFor } from '@aztec/foundation/schemas'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { bufferToHex, hexToBuffer } from '@aztec/foundation/string'; +import { type InboxMessageBundle, bucketStartsOf, flattenBundle } from '../messaging/inbox_message_bundle.js'; + /** The InboxParity size ladder, ascending. One VK per size; the prover proves the smallest that fits. */ export const INBOX_PARITY_SIZES = [INBOX_PARITY_SIZE_SMALL, INBOX_PARITY_SIZE_MEDIUM, INBOX_PARITY_SIZE_LARGE] as const; @@ -35,6 +37,11 @@ export class InboxParityPrivateInputs { public readonly size: InboxParitySize, /** The checkpoint's L1-to-L2 messages, padded with zeros to `size`; the first `numMessages` are real. */ public readonly messages: Fr[], + /** + * Flags the messages that are the first of their L1 Inbox bucket, so the rolling hash commits to the bucket + * boundaries. Aligned with `messages` and padded with `false` to `size`. + */ + public readonly bucketStarts: boolean[], /** Number of real (non-padding) messages in `messages`. */ public readonly numMessages: number, /** Inbox rolling hash before this checkpoint's messages (the previous checkpoint's end; genesis is zero). */ @@ -45,18 +52,25 @@ export class InboxParityPrivateInputs { if (messages.length !== size) { throw new Error(`InboxParity messages length (${messages.length}) must equal size (${size})`); } + if (bucketStarts.length !== size) { + throw new Error(`InboxParity bucketStarts length (${bucketStarts.length}) must equal size (${size})`); + } } /** - * Builds the inputs from a checkpoint's real messages, sizing the circuit by the message count and padding the - * message array out to that size. + * Builds the inputs from a checkpoint's message bundle, sizing the circuit by the message count and padding the + * message and flag arrays out to that size. This is the only place the per-bucket grouping is turned into the + * flat leaves and bucket-start flags the circuit takes. */ - static fromMessages(messages: Fr[], startRollingHash: Fr, proverId: Fr): InboxParityPrivateInputs { + static fromMessages(bundle: InboxMessageBundle, startRollingHash: Fr, proverId: Fr): InboxParityPrivateInputs { + const messages = flattenBundle(bundle); + const bucketStarts = bucketStartsOf(bundle); const size = pickInboxParitySize(messages.length); // Explicit `` keeps the result `Fr[]`; padding to the union-literal `size` would infer a deep tuple. return new InboxParityPrivateInputs( size, padArrayEnd(messages, Fr.ZERO, size), + padArrayEnd(bucketStarts, false, size), messages.length, startRollingHash, proverId, @@ -68,6 +82,7 @@ export class InboxParityPrivateInputs { return serializeToBuffer( new Fr(this.size), this.messages, + this.bucketStarts, new Fr(this.numMessages), this.startRollingHash, this.proverId, @@ -88,9 +103,11 @@ export class InboxParityPrivateInputs { const size = Fr.fromBuffer(reader).toNumber() as InboxParitySize; // Array.from keeps the type `Fr[]`; readArray with the union-literal `size` would infer a deep tuple. const messages = Array.from({ length: size }, () => Fr.fromBuffer(reader)); + const bucketStarts = Array.from({ length: size }, () => reader.readBoolean()); return new InboxParityPrivateInputs( size, messages, + bucketStarts, Fr.fromBuffer(reader).toNumber(), Fr.fromBuffer(reader), Fr.fromBuffer(reader), diff --git a/yarn-project/stdlib/src/tests/factories.ts b/yarn-project/stdlib/src/tests/factories.ts index 777df24ff9f8..af2e9e5c481d 100644 --- a/yarn-project/stdlib/src/tests/factories.ts +++ b/yarn-project/stdlib/src/tests/factories.ts @@ -829,7 +829,16 @@ export function makeInboxParityPrivateInputs(seed = 0): InboxParityPrivateInputs const size = INBOX_PARITY_SIZE_SMALL; const numMsgs = seed % (size + 1); const messages = Array.from({ length: size }, (_, i) => (i < numMsgs ? fr(i + seed + 0x3000) : Fr.ZERO)); - return new InboxParityPrivateInputs(size, messages, numMsgs, new Fr(seed + 0x3500), new Fr(seed + 0x5000)); + // A single bucket holding every real message: lane 0 opens it, padding lanes claim nothing. + const bucketStarts = Array.from({ length: size }, (_, i) => i === 0 && numMsgs > 0); + return new InboxParityPrivateInputs( + size, + messages, + bucketStarts, + numMsgs, + new Fr(seed + 0x3500), + new Fr(seed + 0x5000), + ); } /** diff --git a/yarn-project/validator-client/src/checkpoint_builder.test.ts b/yarn-project/validator-client/src/checkpoint_builder.test.ts index df07c251a53b..42e571c30548 100644 --- a/yarn-project/validator-client/src/checkpoint_builder.test.ts +++ b/yarn-project/validator-client/src/checkpoint_builder.test.ts @@ -836,7 +836,7 @@ describe('CheckpointBuilder', () => { const { block } = await builder.buildBlock([], firstBlockNumber, 1000n, { ...validatorOpts(), - l1ToL2Messages: messages, + l1ToL2Messages: [messages], }); // The AVM must read the same post-append tree the prover and the block-root circuit use. @@ -851,7 +851,7 @@ describe('CheckpointBuilder', () => { processor.process.mockRejectedValue(new Error('processor failure')); await expect( - builder.buildBlock([], firstBlockNumber, 1000n, { ...validatorOpts(), l1ToL2Messages: messages }), + builder.buildBlock([], firstBlockNumber, 1000n, { ...validatorOpts(), l1ToL2Messages: [messages] }), ).rejects.toThrow('processor failure'); expect(await getL1ToL2TreeSize()).toBe(0n); @@ -864,7 +864,7 @@ describe('CheckpointBuilder', () => { await expect( builder.buildBlock([], firstBlockNumber, 1000n, { ...validatorOpts({ minValidTxs: 1 }), - l1ToL2Messages: messages, + l1ToL2Messages: [messages], }), ).rejects.toThrow(InsufficientValidTxsError); diff --git a/yarn-project/validator-client/src/checkpoint_builder.ts b/yarn-project/validator-client/src/checkpoint_builder.ts index f55a850bc57d..35c2f348cafa 100644 --- a/yarn-project/validator-client/src/checkpoint_builder.ts +++ b/yarn-project/validator-client/src/checkpoint_builder.ts @@ -33,7 +33,12 @@ import { type WorldStateSynchronizer, } from '@aztec/stdlib/interfaces/server'; import { type DebugLogStore, NullDebugLogStore } from '@aztec/stdlib/logs'; -import { appendL1ToL2MessagesToTree } from '@aztec/stdlib/messaging'; +import { + type InboxMessageBundle, + appendL1ToL2MessagesToTree, + bundleLength, + flattenBundle, +} from '@aztec/stdlib/messaging'; import { MerkleTreeId } from '@aztec/stdlib/trees'; import { type CheckpointGlobalVariables, GlobalVariables, StateReference, Tx } from '@aztec/stdlib/tx'; import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client'; @@ -125,7 +130,7 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder { // proposal time and succeed at proving time. Appending inside the fork checkpoint means a failed block rolls the // leaves back together with the tx effects. const l1ToL2Messages = opts.l1ToL2Messages ?? []; - await appendL1ToL2MessagesToTree(this.fork, l1ToL2Messages); + await appendL1ToL2MessagesToTree(this.fork, flattenBundle(l1ToL2Messages)); const [publicProcessorDuration, [processedTxs, failedTxs, usedTxs]] = await elapsed(() => processor.process(pendingTxs, cappedOpts, validator), @@ -373,7 +378,7 @@ export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder { checkpointNumber: CheckpointNumber, constants: CheckpointGlobalVariables, feeAssetPriceModifier: bigint, - l1ToL2Messages: Fr[], + l1ToL2Messages: InboxMessageBundle, previousCheckpointOutHashes: Fr[], previousInboxRollingHash: Fr, fork: MerkleTreeWriteOperations, @@ -384,9 +389,9 @@ export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder { const archiveTree = await fork.getTreeInfo(MerkleTreeId.ARCHIVE); if (existingBlocks.length === 0) { - if (l1ToL2Messages.length > 0) { + if (bundleLength(l1ToL2Messages) > 0) { throw new Error( - `Cannot open checkpoint ${checkpointNumber} with ${l1ToL2Messages.length} messages and no existing blocks: ` + + `Cannot open checkpoint ${checkpointNumber} with ${bundleLength(l1ToL2Messages)} messages and no existing blocks: ` + `a fresh checkpoint consumes its messages per block`, ); } @@ -403,7 +408,7 @@ export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder { this.log.verbose(`Resuming checkpoint ${checkpointNumber} with ${existingBlocks.length} existing blocks`, { checkpointNumber, - msgCount: l1ToL2Messages.length, + msgCount: bundleLength(l1ToL2Messages), existingBlockCount: existingBlocks.length, initialStateReference: stateReference.toInspect(), initialArchiveRoot: bufferToHex(archiveTree.root), diff --git a/yarn-project/validator-client/src/proposal_handler.test.ts b/yarn-project/validator-client/src/proposal_handler.test.ts index 4bf7316a6f25..bc59937164b6 100644 --- a/yarn-project/validator-client/src/proposal_handler.test.ts +++ b/yarn-project/validator-client/src/proposal_handler.test.ts @@ -1063,7 +1063,7 @@ describe('ProposalHandler checkpoint validation', () => { 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, txProvider } = await setupStreamingProposal(ref); - const derivedBundle = [new Fr(1000), new Fr(1001)]; + const derivedBundle = [[new Fr(1000), new Fr(1001)]]; l1ToL2MessageSource.getInboxBucket.mockResolvedValue(bucket()); l1ToL2MessageSource.getInboxBucketByTotalMsgCount.mockResolvedValue( bucket({ seq: 0n, totalMsgCount: 0n, msgCount: 0 }), @@ -1110,7 +1110,7 @@ describe('ProposalHandler checkpoint validation', () => { l1ToL2MessageSource.getInboxBucketByTotalMsgCount.mockResolvedValue( eligibleBucket({ seq: 0n, totalMsgCount: 0n, msgCount: 0 }), ); - l1ToL2MessageSource.getL1ToL2MessagesBetweenBuckets.mockResolvedValue([new Fr(1000), new Fr(1001)]); + l1ToL2MessageSource.getL1ToL2MessagesBetweenBuckets.mockResolvedValue([[new Fr(1000), new Fr(1001)]]); } it('attests once the referenced bucket shows up on a later archiver sync', async () => { diff --git a/yarn-project/validator-client/src/proposal_handler.ts b/yarn-project/validator-client/src/proposal_handler.ts index 33faaa69ec8d..a076ccd215c0 100644 --- a/yarn-project/validator-client/src/proposal_handler.ts +++ b/yarn-project/validator-client/src/proposal_handler.ts @@ -41,6 +41,8 @@ import type { WorldStateSynchronizer, } from '@aztec/stdlib/interfaces/server'; import { + EMPTY_BUNDLE, + type InboxMessageBundle, type L1ToL2MessageSource, accumulateCheckpointOutHashes, getInboxCutoffTimestamp, @@ -1160,16 +1162,16 @@ export class ProposalHandler { * 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 { + 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 []; + return EMPTY_BUNDLE; } const startBucket = await this.l1ToL2MessageSource.getInboxBucketByTotalMsgCount(checkpointStartTotal); const endBucket = await this.l1ToL2MessageSource.getInboxBucketByTotalMsgCount(lastBlockTotal); if (startBucket === undefined || endBucket === undefined) { - return []; + return EMPTY_BUNDLE; } return this.l1ToL2MessageSource.getL1ToL2MessagesBetweenBuckets(startBucket.seq, endBucket.seq); } @@ -1179,7 +1181,7 @@ export class ProposalHandler { blockNumber: BlockNumber, checkpointNumber: CheckpointNumber, txs: Tx[], - l1ToL2Messages: Fr[], + l1ToL2Messages: InboxMessageBundle, previousCheckpointOutHashes: Fr[], previousInboxRollingHash: Fr, ): Promise { diff --git a/yarn-project/validator-client/src/streaming_inbox_checks.test.ts b/yarn-project/validator-client/src/streaming_inbox_checks.test.ts index 78b823e1ace0..a64156ff0bce 100644 --- a/yarn-project/validator-client/src/streaming_inbox_checks.test.ts +++ b/yarn-project/validator-client/src/streaming_inbox_checks.test.ts @@ -1,6 +1,6 @@ import { Buffer32 } from '@aztec/foundation/buffer'; import { Fr } from '@aztec/foundation/curves/bn254'; -import type { InboxBucket } from '@aztec/stdlib/messaging'; +import type { InboxBucket, InboxMessageBundle } from '@aztec/stdlib/messaging'; import { InboxBucketRef } from '@aztec/stdlib/messaging'; import { describe, expect, it } from '@jest/globals'; @@ -70,20 +70,23 @@ class FakeInboxView implements StreamingInboxBucketSource { return Promise.resolve([...this.buckets.values()].find(b => b.totalMsgCount === totalMsgCount)); } - getL1ToL2MessagesBetweenBuckets(fromExclusive: bigint, toInclusive: bigint): Promise { + 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; + if (fromExclusive > 0n && this.buckets.get(fromExclusive) === undefined) { + return Promise.resolve([]); } - return Promise.resolve(this.leaves.slice(Number(startIndex), Number(toBucket.lastMessageIndex + 1n))); + // One group per bucket in the range, mirroring the archiver's per-bucket grouping. + const inRange = [...this.buckets.values()] + .filter(b => b.seq > fromExclusive && b.seq <= toInclusive && b.msgCount > 0) + .sort((a, b) => Number(a.seq - b.seq)); + return Promise.resolve( + inRange.map(b => + this.leaves.slice(Number(b.lastMessageIndex + 1n) - b.msgCount, Number(b.lastMessageIndex + 1n)), + ), + ); } } @@ -157,7 +160,7 @@ describe('checkStreamingBlockProposal', () => { const view = new FakeInboxView(); const bucket = view.addBucket(1, 2, Number(NOW) - 1); const result = await checkStreamingBlockProposal(baseInput({ messageSource: view, bucketRef: refFor(bucket) })); - expect(result).toEqual({ accepted: true, bundle: [new Fr(1000), new Fr(1001)] }); + expect(result).toEqual({ accepted: true, bundle: [[new Fr(1000), new Fr(1001)]] }); }); }); @@ -204,7 +207,7 @@ describe('checkStreamingBlockProposal', () => { 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)] }); + 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 () => { @@ -215,8 +218,9 @@ describe('checkStreamingBlockProposal', () => { 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)] }); + // Bundle = leaves at global indices 2,3,4 (buckets 2 and 3), derived after resolving the parent bucket (seq 1), + // grouped per bucket. + 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 () => { diff --git a/yarn-project/validator-client/src/streaming_inbox_checks.ts b/yarn-project/validator-client/src/streaming_inbox_checks.ts index 2e07fb3b841a..81c377d33a82 100644 --- a/yarn-project/validator-client/src/streaming_inbox_checks.ts +++ b/yarn-project/validator-client/src/streaming_inbox_checks.ts @@ -1,5 +1,10 @@ -import type { Fr } from '@aztec/foundation/curves/bn254'; -import type { InboxBucket, InboxBucketRef, L1ToL2MessageSource } from '@aztec/stdlib/messaging'; +import { + EMPTY_BUNDLE, + type InboxBucket, + type InboxBucketRef, + type InboxMessageBundle, + type L1ToL2MessageSource, +} from '@aztec/stdlib/messaging'; /** * Reason a streaming-Inbox block proposal fails the per-block acceptance checks. Follows the @@ -65,7 +70,7 @@ export type StreamingBlockCheckResult = | { /** All checks passed; `bundle` is the message-leaf bundle this block consumes, for re-execution. */ accepted: true; - bundle: Fr[]; + bundle: InboxMessageBundle; } | { /** A check failed; `reason` mirrors the L1 acceptance condition that would have rejected the proposal. */ @@ -158,10 +163,10 @@ export async function checkStreamingBlockProposalMetadata( export function getStreamingBlockBundle( messageSource: Pick, range: StreamingBlockBucketRange, -): Promise { +): Promise { const { bucket, parentBucket } = range; return parentBucket.seq === bucket.seq - ? Promise.resolve([]) + ? Promise.resolve(EMPTY_BUNDLE) : messageSource.getL1ToL2MessagesBetweenBuckets(parentBucket.seq, bucket.seq); } diff --git a/yarn-project/validator-client/src/validator.integration.test.ts b/yarn-project/validator-client/src/validator.integration.test.ts index 3d3922dad677..3928c5c7671d 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 { InboxBucketRef } from '@aztec/stdlib/messaging'; +import { InboxBucketRef, type InboxMessageBundle } from '@aztec/stdlib/messaging'; import { type BlockProposal, CheckpointProposal, @@ -63,7 +63,7 @@ describe('ValidatorClient Integration', () => { rollupManaLimit: 200_000_000, }; - const emptyL1ToL2Messages: Fr[] = []; + const emptyL1ToL2Messages: InboxMessageBundle = []; const emptyPreviousCheckpointOutHashes: Fr[] = []; type ValidatorContext = { @@ -249,7 +249,7 @@ describe('ValidatorClient Integration', () => { blockNumber: BlockNumber, cpNumber: CheckpointNumber, txs: Tx[] = [], - l1ToL2Messages: Fr[] = [], + l1ToL2Messages: InboxMessageBundle = [], ): Promise<{ block: L2Block; proposal: BlockProposal }> => { const blockTimestamp = getTimestampForSlot(checkpointBuilder.getConstantData().slotNumber, l1Constants); const { block, usedTxs } = await checkpointBuilder.buildBlock(txs, blockNumber, blockTimestamp, { @@ -318,7 +318,7 @@ describe('ValidatorClient Integration', () => { const buildCheckpoint = async ( checkpointNumber: CheckpointNumber, slot: SlotNumber, - l1ToL2Messages: Fr[], + l1ToL2Messages: InboxMessageBundle, previousCheckpointOutHashes: Fr[], startBlockNumber: BlockNumber, blockCount: number, @@ -327,7 +327,7 @@ describe('ValidatorClient Integration', () => { blocks: BlockProposalResult[]; checkpoint: Awaited>; proposal: Awaited>; - l1ToL2Messages: Fr[]; + l1ToL2Messages: InboxMessageBundle; globalVariables: CheckpointGlobalVariables; }> => { const globalVariables: CheckpointGlobalVariables = { @@ -492,7 +492,8 @@ describe('ValidatorClient Integration', () => { const { blocks, proposal } = await buildCheckpoint( CheckpointNumber(1), slotNumber, - l1ToL2Messages.map(m => m.leaf), + // Each mocked message sits in its own Inbox bucket, so the bundle is one group per message. + l1ToL2Messages.map(m => [m.leaf]), emptyPreviousCheckpointOutHashes, BlockNumber(1), 3, @@ -711,7 +712,8 @@ describe('ValidatorClient Integration', () => { const { blocks } = await buildCheckpoint( CheckpointNumber(1), slotNumber, - l1ToL2Messages.map(m => m.leaf), + // Each mocked message sits in its own Inbox bucket, so the bundle is one group per message. + l1ToL2Messages.map(m => [m.leaf]), emptyPreviousCheckpointOutHashes, BlockNumber(1), 1, 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 eefe75f2a976..082bb7e4dff6 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 @@ -20,7 +20,7 @@ import { type WorldStateSynchronizer, type WorldStateSynchronizerStatus, } from '@aztec/stdlib/interfaces/server'; -import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging'; +import { type L1ToL2MessageSource, flattenBundle } from '@aztec/stdlib/messaging'; import type { SnapshotDataKeys } from '@aztec/stdlib/snapshots'; import type { L2BlockHandledStats } from '@aztec/stdlib/stats'; import { MerkleTreeId, type MerkleTreeReadOperations, type MerkleTreeWriteOperations } from '@aztec/stdlib/trees'; @@ -382,7 +382,7 @@ export class ServerWorldStateSynchronizer if (startBucket !== undefined && endBucket !== undefined) { messagesForBlocks.set( block.number, - await this.l2BlockSource.getL1ToL2MessagesBetweenBuckets(startBucket.seq, endBucket.seq), + flattenBundle(await this.l2BlockSource.getL1ToL2MessagesBetweenBuckets(startBucket.seq, endBucket.seq)), ); } }