Skip to content

Commit 9e80502

Browse files
spalladinoclaude
andcommitted
feat(fast-inbox): make streaming the only node consumption path (A-1384)
Remove the streamingInbox flag and wire streaming as the sole path (AZIP-22 Fast Inbox): - Config: drop streamingInbox from foundation env vars, stdlib sequencer/validator config + schemas, and the sequencer/validator config plumbing. - Sequencer: source the parent checkpoint's consumed bucket from the fork's L1-to-L2 tree leaf count + getInboxBucketByTotalMsgCount (cross-checkpoint, not genesis-only); feed the header/block inHash zero; relax waitForMinTxs for message-only blocks; pass the consumed bucket seq as the propose bucketHint. Automine selects its single block's bundle the same way. - Validator: per-block acceptance and checkpoint re-execution always run; the checkpoint rebuild derives the consumed message list from the buckets between the parent checkpoint's position and the last block. - World state: appendL1ToL2MessagesToTree and handleL2BlockAndMessages append real leaves at compact indices; the synchronizer derives each block's bundle from its leaf-index range. - Lightweight checkpoint builder feeds the checkpoint inHash zero. Legacy bulk-fetch/insert paths go dead (deleted in FI-18). Verified via jest: validator proposal_handler (34) + streaming checks (14), sequencer inbox selector (9), world-state native (56). tsc rides CI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent eafd9b8 commit 9e80502

18 files changed

Lines changed: 218 additions & 219 deletions

File tree

yarn-project/foundation/src/config/env_var.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,6 @@ export type EnvVar =
382382
| 'FISHERMAN_MODE'
383383
| 'MAX_ALLOWED_ETH_CLIENT_DRIFT_SECONDS'
384384
| 'MAX_BLOCKS_PER_CHECKPOINT'
385-
| 'STREAMING_INBOX'
386385
| 'LEGACY_BLS_CLI'
387386
| 'DEBUG_FORCE_TX_PROOF_VERIFICATION'
388387
| 'VALIDATOR_ALLOW_EPHEMERAL_SIGNING_PROTECTION'

yarn-project/prover-client/src/light/lightweight_checkpoint_builder.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import {
1111
accumulateInboxRollingHash,
1212
appendL1ToL2MessagesToTree,
1313
computeCheckpointOutHash,
14-
computeInHashFromL1ToL2Messages,
1514
} from '@aztec/stdlib/messaging';
1615
import { CheckpointHeader, computeBlockHeadersHash } from '@aztec/stdlib/rollup';
1716
import { AppendOnlyTreeSnapshot, MerkleTreeId } from '@aztec/stdlib/trees';
@@ -295,7 +294,9 @@ export class LightweightCheckpointBuilder {
295294
const blobs = await getBlobsPerL1Block(this.blobFields);
296295
const blobsHash = computeBlobsHashFromBlobs(blobs);
297296

298-
const inHash = computeInHashFromL1ToL2Messages(this.l1ToL2Messages);
297+
// Legacy inHash is dead post-flip; the checkpoint header carries zero (AZIP-22 Fast Inbox). The consensus
298+
// rolling hash over the consumed messages is the authoritative Inbox commitment.
299+
const inHash = Fr.ZERO;
299300
const inboxRollingHash = accumulateInboxRollingHash(this.previousInboxRollingHash, this.l1ToL2Messages);
300301

301302
const { slotNumber, coinbase, feeRecipient, gasFees } = this.constants;

yarn-project/sequencer-client/src/config.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ export const DefaultSequencerConfig = {
6868
skipPushProposedBlocksToArchiver: false,
6969
skipPublishingCheckpointsPercent: 0,
7070
maxBlocksPerCheckpoint: DEFAULT_MAX_BLOCKS_PER_CHECKPOINT,
71-
streamingInbox: false,
7271
} satisfies ResolvedSequencerConfig;
7372

7473
/**

yarn-project/sequencer-client/src/publisher/sequencer-publisher.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ type L1ProcessArgs = {
113113
attestationsAndSignersSignature: Signature;
114114
/** The fee asset price modifier in basis points (from oracle) */
115115
feeAssetPriceModifier: bigint;
116+
/** Sequence number of the Inbox bucket the header's rolling hash corresponds to (AZIP-22 Fast Inbox lookup aid). */
117+
bucketHint: bigint;
116118
};
117119

118120
export const Actions = [
@@ -1404,6 +1406,7 @@ export class SequencerPublisher {
14041406
checkpoint: Checkpoint,
14051407
attestationsAndSigners: CommitteeAttestationsAndSigners,
14061408
attestationsAndSignersSignature: Signature,
1409+
bucketHint: bigint,
14071410
opts: EnqueueProposeCheckpointOpts = {},
14081411
): Promise<void> {
14091412
const checkpointHeader = checkpoint.header;
@@ -1418,6 +1421,7 @@ export class SequencerPublisher {
14181421
attestationsAndSigners,
14191422
attestationsAndSignersSignature,
14201423
feeAssetPriceModifier: checkpoint.feeAssetPriceModifier,
1424+
bucketHint,
14211425
};
14221426

14231427
this.log.verbose(`Enqueuing checkpoint propose transaction`, {
@@ -1588,6 +1592,7 @@ export class SequencerPublisher {
15881592
oracleInput: {
15891593
feeAssetPriceModifier: encodedData.feeAssetPriceModifier,
15901594
},
1595+
bucketHint: encodedData.bucketHint,
15911596
},
15921597
encodedData.attestationsAndSigners.getPackedAttestations(),
15931598
signers,

yarn-project/sequencer-client/src/sequencer/automine/automine_sequencer.ts

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import type { Archiver } from '@aztec/archiver';
2+
import { INBOX_LAG_SECONDS, MAX_L1_TO_L2_MSGS_PER_BLOCK, MAX_L1_TO_L2_MSGS_PER_CHECKPOINT } from '@aztec/constants';
23
import type { L1TxUtils } from '@aztec/ethereum/l1-tx-utils';
34
import { type EthCheatCodes, RollupCheatCodes } from '@aztec/ethereum/test';
45
import { BlockNumber, CheckpointNumber, EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
6+
import { Fr } from '@aztec/foundation/curves/bn254';
57
import type { EthAddress } from '@aztec/foundation/eth-address';
68
import { Signature } from '@aztec/foundation/eth-signature';
79
import { type Logger, createLogger } from '@aztec/foundation/log';
@@ -22,8 +24,9 @@ import {
2224
getTimestampForSlot,
2325
} from '@aztec/stdlib/epoch-helpers';
2426
import { InsufficientValidTxsError, type WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
25-
import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
27+
import { type L1ToL2MessageSource, getInboxCutoffTimestamp } from '@aztec/stdlib/messaging';
2628
import type { CoordinationSignatureContext } from '@aztec/stdlib/p2p';
29+
import { MerkleTreeId } from '@aztec/stdlib/trees';
2730
import type { FailedTx, Tx } from '@aztec/stdlib/tx';
2831
import type {
2932
BuildBlockInCheckpointResult,
@@ -35,6 +38,7 @@ import type { GlobalVariableBuilder } from '../../global_variable_builder/global
3538
import type { SequencerPublisherFactory } from '../../publisher/sequencer-publisher-factory.js';
3639
import type { SequencerPublisher } from '../../publisher/sequencer-publisher.js';
3740
import type { SequencerConfig } from '../config.js';
41+
import { selectInboxBucketForBlock } from '../inbox_bucket_selector.js';
3842

3943
/**
4044
* L1 rollup constants needed by the AutomineSequencer. Same as SequencerRollupConstants
@@ -447,8 +451,6 @@ export class AutomineSequencer {
447451
SlotNumber(targetSlot),
448452
);
449453

450-
const l1ToL2Messages = await this.deps.l1ToL2MessageSource.getL1ToL2Messages(checkpointNumber);
451-
452454
const previousCheckpointOutHashes = await getPreviousCheckpointOutHashes({
453455
blockSource: this.deps.l2BlockSource,
454456
epoch: targetEpoch,
@@ -468,15 +470,40 @@ export class AutomineSequencer {
468470

469471
await using fork = await this.deps.worldState.fork(syncedToBlockNumber, { closeDelayMs: 0 });
470472

473+
// Streaming Inbox (AZIP-22 Fast Inbox): automine builds a single-block checkpoint, so its one block is the
474+
// checkpoint's final block; select its bundle from the newest lag-eligible bucket with the last-block censorship
475+
// floor. The parent total is the fork's L1-to-L2 leaf count (compact indexing), which resolves the parent bucket.
476+
const parentInfo = await fork.getTreeInfo(MerkleTreeId.L1_TO_L2_MESSAGE_TREE);
477+
const parentTotalMsgCount = parentInfo.size;
478+
const parentBucket = await this.deps.l1ToL2MessageSource.getInboxBucketByTotalMsgCount(parentTotalMsgCount);
479+
if (parentBucket === undefined) {
480+
this.log.warn(`Automine streaming inbox: parent bucket for total ${parentTotalMsgCount} not synced; skipping`);
481+
return undefined;
482+
}
483+
const selection = await selectInboxBucketForBlock({
484+
messageSource: this.deps.l1ToL2MessageSource,
485+
now: BigInt(Math.floor(this.deps.dateProvider.now() / 1000)),
486+
lagSeconds: BigInt(INBOX_LAG_SECONDS),
487+
parent: { seq: parentBucket.seq, totalMsgCount: parentBucket.totalMsgCount },
488+
checkpointStartTotalMsgCount: parentTotalMsgCount,
489+
perBlockCap: MAX_L1_TO_L2_MSGS_PER_BLOCK,
490+
perCheckpointCap: MAX_L1_TO_L2_MSGS_PER_CHECKPOINT,
491+
isLastBlock: true,
492+
cutoffTimestamp: getInboxCutoffTimestamp(SlotNumber(targetSlot), this.deps.l1Constants, INBOX_LAG_SECONDS),
493+
});
494+
const streamingBundle = selection.consume ? selection.bundle : [];
495+
const bucketHint = selection.consume ? selection.bucket.seq : parentBucket.seq;
496+
471497
const checkpointBuilder = await this.deps.checkpointsBuilder.startCheckpoint(
472498
checkpointNumber,
473499
checkpointGlobals,
474500
feeAssetPriceModifier,
475-
l1ToL2Messages,
501+
[],
476502
previousCheckpointOutHashes,
477503
previousInboxRollingHash,
478504
fork,
479505
this.log.getBindings(),
506+
true,
480507
);
481508

482509
// Block building only executes txs, and automine publishes straight to L1, so the proofs are never needed.
@@ -489,6 +516,7 @@ export class AutomineSequencer {
489516
checkpointGlobals.timestamp,
490517
allowEmpty,
491518
checkpointNumber,
519+
streamingBundle,
492520
);
493521
if (!buildResult) {
494522
return undefined;
@@ -516,7 +544,12 @@ export class AutomineSequencer {
516544
feeAssetPriceModifier,
517545
});
518546

519-
await this.publisher.enqueueProposeCheckpoint(checkpoint, emptyAttestations, emptyAttestationsSignature);
547+
await this.publisher.enqueueProposeCheckpoint(
548+
checkpoint,
549+
emptyAttestations,
550+
emptyAttestationsSignature,
551+
bucketHint,
552+
);
520553
// Automine publishes synchronously in the current slot via `sendRequests`. It must NOT use the
521554
// production `sendRequestsAt` (or `canProposeAt`), which always apply the one-slot pipelining
522555
// offset — automine is the deliberate non-pipelined exception and builds/publishes in place.
@@ -756,16 +789,19 @@ export class AutomineSequencer {
756789
timestamp: bigint,
757790
allowEmpty: boolean,
758791
checkpointNumber: CheckpointNumber,
792+
l1ToL2Messages: Fr[],
759793
): Promise<BuildBlockInCheckpointResult | undefined> {
760794
let buildResult: BuildBlockInCheckpointResult;
761795
try {
762796
buildResult = await checkpointBuilder.buildBlock(pendingTxs, nextBlockNumber, timestamp, {
763797
maxTransactions: this.deps.config.maxTxsPerBlock,
764-
// Allow empty for explicit-empty builds; require at least 1 valid tx otherwise.
765-
minValidTxs: allowEmpty ? 0 : 1,
798+
// Allow empty for explicit-empty builds; a message-only block (non-empty streaming bundle) also builds
799+
// with zero txs.
800+
minValidTxs: allowEmpty || l1ToL2Messages.length > 0 ? 0 : 1,
766801
isBuildingProposal: true,
767802
maxBlocksPerCheckpoint: 1,
768803
perBlockAllocationMultiplier: 1,
804+
l1ToL2Messages,
769805
});
770806
} catch (err) {
771807
// Mirrors production's checkpoint_proposal_job: if every pending tx failed execution and

yarn-project/sequencer-client/src/sequencer/checkpoint_proposal_job.test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1389,10 +1389,9 @@ describe('CheckpointProposalJob', () => {
13891389
});
13901390
});
13911391

1392-
describe('streaming inbox (flag on)', () => {
1392+
describe('streaming inbox', () => {
13931393
beforeEach(() => {
13941394
job.setTimetable(makeProposerTimetable({ l1Constants, blockDurationMs: 3000 }));
1395-
job.updateConfig({ streamingInbox: true });
13961395
});
13971396

13981397
it('streams per-block bucket bundles, advances the reference, and skips the bulk message fetch', async () => {

0 commit comments

Comments
 (0)