@@ -14,7 +14,7 @@ import {
1414} from '@aztec/kv-store' ;
1515import { type InboxBucket , InboxLeaf , updateInboxRollingHash } from '@aztec/stdlib/messaging' ;
1616
17- import { L1ToL2MessagesNotReadyError } from '../errors.js' ;
17+ import { InboxBucketNotSyncedError , L1ToL2MessagesNotReadyError } from '../errors.js' ;
1818import {
1919 type InboxMessage ,
2020 deserializeInboxMessage ,
@@ -505,27 +505,27 @@ export class MessageStore {
505505 }
506506
507507 /**
508- * Returns the message leaves absorbed into buckets in the range `(fromExclusive, toInclusive]`, in insertion
509- * order (AZIP-22 Fast Inbox). Returns an empty array if the upper bucket has not been synced.
508+ * Returns the message leaves absorbed into buckets in the range `(fromExclusive, toInclusive]`, in insertion order
509+ * (AZIP-22 Fast Inbox). Both bounds must name buckets this archiver has synced, so that an empty result means the
510+ * range holds no messages rather than hiding an unsynced bound; callers route the
511+ * `InboxBucketNotSyncedError` to their own catch-up handling. Sequence 0 is the genesis base case and always
512+ * resolves: the range then starts at the first message of the Inbox.
510513 */
511514 public async getL1ToL2MessagesBetweenBuckets ( fromExclusive : bigint , toInclusive : bigint ) : Promise < Fr [ ] > {
512- const toBucket = await this . getBucketSnapshotBySeq ( toInclusive ) ;
513- if ( toBucket === undefined ) {
514- return [ ] ;
515+ if ( fromExclusive > toInclusive ) {
516+ throw new Error ( `Invalid Inbox bucket range (${ fromExclusive } , ${ toInclusive } ]` ) ;
515517 }
516- // A nonzero lower bound must reference a synced bucket; otherwise the range is unavailable (rather than
517- // defaulting to genesis, which would silently over-return). Sequence 0 is the genesis base case: start from
518- // the beginning of the Inbox.
519- let startIndex = 0n ;
520- if ( fromExclusive > 0n ) {
521- const fromBucket = await this . getBucketSnapshotBySeq ( fromExclusive ) ;
522- if ( fromBucket === undefined ) {
523- return [ ] ;
524- }
525- startIndex = fromBucket . lastMessageIndex + 1n ;
518+ if ( toInclusive === 0n ) {
519+ return [ ] ;
526520 }
527- const endIndexExclusive = toBucket . lastMessageIndex + 1n ;
521+ const endIndexExclusive = ( await this . getSyncedBucketSnapshot ( toInclusive ) ) . lastMessageIndex + 1n ;
522+ const startIndex =
523+ fromExclusive === 0n ? 0n : ( await this . getSyncedBucketSnapshot ( fromExclusive ) ) . lastMessageIndex + 1n ;
524+ return this . getMessageLeavesInIndexRange ( startIndex , endIndexExclusive ) ;
525+ }
528526
527+ /** Collects the message leaves in the global index range `[startIndex, endIndexExclusive)`, in insertion order. */
528+ private async getMessageLeavesInIndexRange ( startIndex : bigint , endIndexExclusive : bigint ) : Promise < Fr [ ] > {
529529 const leaves : Fr [ ] = [ ] ;
530530 for await ( const msgBuffer of this . #l1ToL2Messages. valuesAsync ( {
531531 start : this . indexToKey ( startIndex ) ,
@@ -541,6 +541,15 @@ export class MessageStore {
541541 return buffer && deserializeBucketSnapshot ( buffer ) ;
542542 }
543543
544+ /** Reads a bucket snapshot, failing loudly if the archiver has not synced that bucket. */
545+ private async getSyncedBucketSnapshot ( seq : bigint ) : Promise < BucketSnapshot > {
546+ const snapshot = await this . getBucketSnapshotBySeq ( seq ) ;
547+ if ( snapshot === undefined ) {
548+ throw new InboxBucketNotSyncedError ( seq ) ;
549+ }
550+ return snapshot ;
551+ }
552+
544553 private async writeBucketSnapshot ( seq : bigint , snapshot : BucketSnapshot ) : Promise < void > {
545554 await this . #inboxBuckets. set ( this . bucketSeqToKey ( seq ) , serializeBucketSnapshot ( snapshot ) ) ;
546555 await this . #bucketTimestampToSeq. set ( this . timestampToKey ( snapshot . timestamp ) , this . bucketSeqToKey ( seq ) ) ;
0 commit comments