Skip to content

chore(fast-inbox): delete legacy node message paths (A-1388) - #24793

Closed
spalladino wants to merge 18 commits into
spl/a-1387-circuits-cleanupfrom
spl/a-1388-node-cleanup
Closed

spalladino wants to merge 18 commits into
spl/a-1387-circuits-cleanupfrom
spl/a-1388-node-cleanup

Conversation

@spalladino

@spalladino spalladino commented Jul 18, 2026 •

Copy link
Copy Markdown
Contributor

Node cleanup for the Fast Inbox (AZIP-22) project — removes the legacy L1-to-L2 message paths the flip (#24789) left dead. Sits on the Fast Inbox stack (#24784..#24792); base is spl/a-1387-circuits-cleanup.

What's removed

  • stdlib / p2p: computeInHashFromL1ToL2Messages and the whole in_hash.ts; the inHash field on BlockProposal (constructor, signed payload, wire, and the createBlockProposal validator-interface argument); the CheckpointProposal.getBlockProposal inHash pass-through; the padded per-checkpoint InboxLeaf helpers (smallestIndexForCheckpoint / indexRangeForCheckpoint / checkpointNumberFromIndex); the legacy getL1ToL2Messages(checkpointNumber) member from the L1ToL2MessageSource and archiver RPC interfaces.
  • archiver: the legacy per-checkpoint getL1ToL2Messages flow, the padded per-checkpoint index invariants, the inboxTreeInProgress readiness gate + L1ToL2MessagesNotReadyError, and the 128-bit keccak rolling hash. InboxMessage now carries only the compact global index and the full-width consensus rolling hash (the vacuous derived checkpointNumber and the 128-bit rollingHash are gone). Reorg detection compares the local consensus rolling hash and total against the Inbox's current rolling-hash bucket (new getBucket / getCurrentBucketSeq / getCurrentBucket wrappers) instead of the 128-bit getState.
  • sequencer / validator: the dead inHash = Fr.ZERO threading through the checkpoint proposal job and the validator/validation-service create-proposal path, plus the dead in_hash_mismatch validation-failure reason.
  • world-state: the no-op first-in-checkpoint padding alias and the obsolete non-first-block-empty-bundle transitional test (the production assertion was already removed at the flip).
  • node: the public-calls simulator's dead next-checkpoint message fetch and its now-unused l1ToL2MessageSource dependency.
  • config / env: AZTEC_INBOX_LAG / inboxLag from ethereum config, foundation env vars, the network-consensus-config list, the l1-contracts + spartan network defaults, and the e2e option plumbing.
  • docs: THREAT_MODEL.md and the archiver README rewritten from the inHash == inbox.consume(...) model to the consensus rolling-hash / bucket model.

Format / store / wire notes

  • Store version bump: ARCHIVER_DB_VERSION 8 → 9 because InboxMessage serialization dropped rollingHash + checkpointNumber and the inboxTreeInProgress singleton is gone. No migration — nodes resync (fresh rollup instance per release line, same no-migration policy as the rest of the stack).
  • p2p wire format: dropping the (zeroed-since-flip) inHash shrinks the block-proposal bytes. Done as a plain removal at a release boundary (fresh networks), matching the A-1381 optional-tail precedent; the golden wire_compat_fixtures.ts buffers were regenerated. The checkpoint-proposal fixture is unaffected (it never carried inHash).
  • L1 follow-through: done (leftover sweep, below). The on-chain Inbox 128-bit messagesRollingHash accumulation, InboxState.rollingHash, and the MessageSent.rollingHash event arg are now removed in this PR — this is the earliest branch where it is safe, since the TS reads disappear here. getState() / getTotalMessagesInserted() are kept: chain_monitor and fast node sync still read the message count.

Testing

Locally green (unit suites that run without @aztec/bb-avm-sim): archiver (561), stdlib p2p + interfaces (76), world-state synchronizer + native (74), validator-client unit (38), ethereum config (6). Suites that import @aztec/bb-avm-sim (p2p libp2p, sequencer-client, node simulator, validator integration) and full typecheck of the packages downstream of the pinned-VK blocker are CI-validated. No e2e / VK regen locally.

Review follow-up (phase-2 final review)

Four commits were appended by the final review pass:

  • The env sweep removed AZTEC_INBOX_LAG from scripts/network-defaults.json while both L1 deploy-script test setUps still readUint the key, reverting before any test ran; the reads are gone.
  • The p2p attestation store version is bumped 2 -> 3: it persists proposal/attestation buffers whose formats changed in this stack (checkpoint header lost inHash; block-proposal wire dropped it), and stored checkpoint attestations decode without a tolerant fallback.
  • l1_publisher.integration.test.ts is reworked for streaming consumption: each checkpoint consumes every message sent while it was built, threading the previous checkpoint's rolling hash and reading the propose bucketHint from the Inbox's current bucket (the legacy inboxLag shift register is gone).
  • Remaining inboxLag/inHash references are swept from the spartan environment profiles, the governance-upgrade tutorial, the validator/sequencer READMEs, and stale e2e comments.

l1 publisher test dissolved into A-1387 (pass-8)

This branch's rewrite of l1_publisher.integration.test.ts is superseded by the corrected streaming-selector version landed at A-1387 (#24792): the branch's own message-reconstruction rework commit is dropped and the file is byte-identical to A-1387's here (segment diff for this file is zero). The A-1384 world-state mock bucket registration coexists with this branch's legacy-message-path deletion (the buckets survive the cleanup). First real box verification of this suite at A-1388: l1_publisher 13/13; world-state integration 12/12 at top.

Leftover sweep (post-review)

  • Removed the legacy 128-bit keccak inbox rolling hash from L1: InboxState.rollingHash, the MessageSent bytes16 rollingHash event arg, and its keccak accumulation in Inbox.sol, updating the four Solidity test suites that asserted it (Inbox.t, InboxBuckets.t, TokenPortal.t, depositToAztecPublic.t) and the three live docs-example inbox ABIs that embedded the old event shape. The node's message sync and L1-reorg detection already run entirely on the full-width consensus rolling hash from the buckets.
  • Deleted the dead InboxLeaf class from stdlib (this branch had already removed its per-checkpoint index helpers) and fixed a stale InboxLeaf mention in the archiver retrieval JSDoc.
  • Clarified the checkpoint-builder comments: the up-front whole-checkpoint message insertion (insertMessagesPerBlock = false) is only exercised by tests; production always streams messages per block.

@spalladino
spalladino force-pushed the spl/a-1388-node-cleanup branch from 92cecbd to 83a74e1 Compare July 19, 2026 01:25
@spalladino
spalladino force-pushed the spl/a-1387-circuits-cleanup branch from d06eb0f to 7e1dc3d Compare July 19, 2026 01:25
@spalladino
spalladino force-pushed the spl/a-1387-circuits-cleanup branch from 7e1dc3d to 0e1e0ed Compare July 19, 2026 14:05
@spalladino
spalladino force-pushed the spl/a-1388-node-cleanup branch 2 times, most recently from 115b351 to 5817030 Compare July 19, 2026 15:10
@spalladino
spalladino force-pushed the spl/a-1387-circuits-cleanup branch 2 times, most recently from 2277ca8 to 43ac45a Compare July 19, 2026 15:36
@spalladino
spalladino force-pushed the spl/a-1388-node-cleanup branch from 5817030 to 01b921a Compare July 19, 2026 15:36
@spalladino
spalladino force-pushed the spl/a-1387-circuits-cleanup branch from 43ac45a to b82f5cc Compare July 19, 2026 17:57
@spalladino
spalladino force-pushed the spl/a-1388-node-cleanup branch from 01b921a to 6269f44 Compare July 19, 2026 17:57
@spalladino
spalladino force-pushed the spl/a-1387-circuits-cleanup branch from b82f5cc to 8287158 Compare July 19, 2026 18:02
@spalladino
spalladino force-pushed the spl/a-1388-node-cleanup branch from 6269f44 to 3111d18 Compare July 19, 2026 18:02
@spalladino
spalladino force-pushed the spl/a-1387-circuits-cleanup branch from 8287158 to 4c4c1a8 Compare July 19, 2026 20:30
@spalladino
spalladino force-pushed the spl/a-1388-node-cleanup branch from 38cadbf to 5660774 Compare July 19, 2026 20:30
@spalladino
spalladino force-pushed the spl/a-1387-circuits-cleanup branch from 4c4c1a8 to c51c85b Compare July 19, 2026 20:48
@spalladino
spalladino force-pushed the spl/a-1388-node-cleanup branch from 5660774 to 7fa4b0a Compare July 19, 2026 20:48
@spalladino
spalladino force-pushed the spl/a-1387-circuits-cleanup branch from c51c85b to de57be7 Compare July 19, 2026 20:51
@spalladino
spalladino force-pushed the spl/a-1388-node-cleanup branch from 7fa4b0a to 0224d57 Compare July 19, 2026 20:51
@spalladino
spalladino force-pushed the spl/a-1387-circuits-cleanup branch from de57be7 to ce83790 Compare July 20, 2026 13:32
@spalladino
spalladino force-pushed the spl/a-1388-node-cleanup branch from 0224d57 to 02cd879 Compare July 20, 2026 13:32
@spalladino
spalladino force-pushed the spl/a-1387-circuits-cleanup branch from ce83790 to f853ebe Compare July 20, 2026 14:07
@spalladino
spalladino force-pushed the spl/a-1388-node-cleanup branch from 02cd879 to 05a1f0c Compare July 20, 2026 14:07
@spalladino
spalladino force-pushed the spl/a-1387-circuits-cleanup branch from f853ebe to 9d37080 Compare July 20, 2026 15:30
@spalladino
spalladino force-pushed the spl/a-1388-node-cleanup branch from 05a1f0c to 93b18af Compare July 20, 2026 15:30
@spalladino
spalladino force-pushed the spl/a-1387-circuits-cleanup branch from 9d37080 to c50ce18 Compare July 20, 2026 17:28
@spalladino
spalladino force-pushed the spl/a-1388-node-cleanup branch 2 times, most recently from 5cc8950 to 94dec7d Compare July 20, 2026 21:21
…ts (A-1388)

The optional bucket-reference tail keeps proposals that omit it round-tripping
cleanly; it does not make the wire byte-identical to the pre-inHash-removal format.
Reword the toBuffer/fromBuffer comments to describe only the tail's unset case.
… tests (A-1388)

The env sweep removed AZTEC_INBOX_LAG from scripts/network-defaults.json but
both deploy-script test setUps still readUint the key, which reverts
(vm.parseJsonUint on a missing path), failing the suites before any test
runs. The deploy configuration no longer consumes the env var.
…proposal formats (A-1388)

The attestation store persists raw BlockProposal and CheckpointAttestation
buffers. Both changed shape in this stack (the checkpoint header lost inHash
and the block-proposal wire format dropped its zeroed inHash), and stored
checkpoint attestations are decoded without a tolerant fallback, so a store
written by a pre-Fast-Inbox node would throw on read. Bump 2 -> 3 to wipe
stale pools, matching the archiver's no-migration bump.
…388)

Removes the dead AZTEC_INBOX_LAG entries from the spartan environment
profiles and the governance-upgrade tutorial, rewrites the validator and
sequencer README sections that still described the legacy inHash flow, and
drops e2e comments that still explained fixture settings in terms of the
deleted inboxLag / L1ToL2MessagesNotReadyError behavior. Versioned docs
snapshots are left untouched.
…(A-1388)

Finding the cut point for an L1-block rollback walked the messages backwards from
the tip, deserializing every message being removed just to learn where the removal
starts. A bucket lives entirely within one L1 block and its snapshot now records
that block plus the bucket's first message index, so scan the bucket snapshots
instead — a few records per L1 block rather than one per message.
…sage leaf index (A-1388)

Blocks consume Inbox messages in order into consecutive L1-to-L2 tree leaves, so a message is
available at a tip exactly when that tip's leaf count has grown past the message's index — one
comparison against data every block header already carries. `isL1ToL2MessageReady` now does that
via `getL1ToL2MessageIndex`, which also drops its stale claim that messages land in the first block
of a checkpoint. The `getL1ToL2MessageCheckpoint` node RPC method existed only to answer this
question and needed a binary search over block records to do it, so delete it: node service,
world-state queries, interface, schema, and its callers. `getL1ToL2MessageIndex` takes its place in
the operator API reference.
…ing public calls (A-1388)

A transaction that consumes an L1-to-L2 message sent moments ago failed public simulation until a
block actually consumed the message, because the simulation fork stops at the tip's message tree.
Predict the bundle the next block would take — the sequencer's own bucket selection, so lag
eligibility and the per-block/per-checkpoint caps match what will be built — and append it to the
fork. The prediction treats the next block as non-final (the censorship cutoff only widens
consumption on a checkpoint's last block, which the node cannot know) and derives its cursor from
the fork's own leaf count, so it can never re-insert messages the fork already holds. Best-effort:
unsynced buckets or a missing header leave the simulation on the bare tip, as before.
@spalladino

Copy link
Copy Markdown
Contributor Author

Superseded by #25038.

The Fast Inbox stack has been regrouped from 20 per-issue PRs (plus 2 umbrellas) down to 3 area PRs plus an umbrella, and rebased onto merge-train/spartan including #25007 (the noir-projects fnd//labs/ split). This PR's diff is preserved verbatim as a single squashed commit in #25038, and this description is preserved in that commit's message.

New structure: #25036 (circuits + L1) → #25037 (node + flip) → #25038 (cleanup), with #25039 as the full-stack umbrella. The original branch for this PR is left on the remote as a recovery point.

Closing here to cut the rebase and review overhead of maintaining 22 PRs; the work is not abandoned.

@spalladino spalladino closed this Jul 28, 2026
spalladino added a commit that referenced this pull request Jul 28, 2026
Deletes the legacy L1 Inbox path now that `propose` enforces streaming-inbox consumption (AZIP-22 Fast Inbox, FI-16). Stacked on `spl/a-1385-streaming-e2e`; part of the Fast Inbox stack (#24784..#24790 below this one).

## What is deleted

- **Frontier trees**: the `trees` mapping, `forest`, `HEIGHT`/`SIZE`/`EMPTY_ROOT`, the per-message tree insert, `getRoot()`, and the whole `consume()` flow.
- **LAG / inboxLag**: the `LAG` immutable and `_lag`/`_height` constructor args (the Inbox constructor is now `(rollup, feeAsset, version, bucketRingSize)`), `RollupConfigInput.inboxLag` and its `RollupCore` pass-through, and `Constants.L1_TO_L2_MSG_SUBTREE_HEIGHT` from the Inbox construction.
- **Dead event field**: the `MessageSent.checkpointNumber` (former `inProgress`) argument, which became meaningless once `consume()` stopped advancing it.
- **Orphaned errors**: `Inbox__Unauthorized`, `Inbox__MustBuildBeforeConsume`, `Rollup__InvalidInHash`.

## What is re-homed / kept

- The compact message index now reads the current bucket's running total (`totalMsgCount`) instead of a parallel counter; `InboxState.inProgress` and `getInProgress()` are gone.
- The 128-bit `rollingHash`, `InboxState.{rollingHash, totalMessagesInserted}`, `getState()`, and `getTotalMessagesInserted()` are **kept**: the node still consumes them for message sync and L1-reorg detection. Their removal is the node cleanup's job (FI-18).
- `FrontierLib` is **kept** — it still backs the base-parity (`test/Parity.t.sol`) and merkle (`test/merkle/Frontier.t.sol`) tests, which are unrelated to the Inbox (parity-circuit territory, FI-17). The plan's "delete the file" assumption is contradicted by grep.

## TS follow-through

- `ethereum` InboxContract: drop `getLag()`, drop the `MessageSent.checkpointNumber` decode, stop reading `inProgress`; `InboxContractState.treeInProgress` is now optional (no longer tracked on-chain).
- Stop threading `inboxLag` through `queries.getL1ContractsConfig` and the L1 deploy env. The broader `AZTEC_INBOX_LAG` config sweep (`ethereum/src/config.ts`, `foundation`, `stdlib`, ~30 e2e configs) is deferred to FI-18, which explicitly owns it.
- Archiver decode derives the message's checkpoint number from the compact index (the event no longer carries it), keeping the legacy per-checkpoint store shape untouched for FI-18.
- Removed the obsolete `advanceInboxInProgress` cheat code + its inbox-drift tests, and the orphaned archiver `inHash`-mismatch sync test (the cross-check was removed at the flip).

## Gas

Deleting the frontier insert is a large `sendL2Message` win. Whole-test gas (`forge test` on `InboxBuckets.t.sol`), before → after:

| Case | Before | After |
| --- | --- | --- |
| First-ever message | 175,251 | 116,835 |
| First message of a new L1 block | 329,460 | 195,555 |
| Absorb into an existing bucket | 286,673 | 149,968 |
| Rollover mid-block (256 messages) | 18,923,988 | 3,068,480 |

Per-call `sendL2Message` gas after cleanup: first-ever 99,526; first-of-new-block 53,763; existing-bucket absorb 9,273; rollover 53,801.

## Testing

- `forge build` + `forge test` green: 887 passed, 0 failed. This fixes 4 pre-existing baseline failures the flip stranded (`fee_portal`/`TokenPortal` deposit tests using tree-relative indices + the old event shape).
- `@aztec/ethereum` builds clean; `config`/`queries` unit tests green.
- `@aztec/archiver` has no self-owned type errors; `message_store` (36), `archiver-sync` (59), and the decode/struct suites (58) all pass. (Pre-existing `noir-protocol-circuits-types` stale-artifact errors are unaffected.)
- e2e not run locally. The stability gate ("a few days of green e2e/networks before deleting the fallback") applies at merge time for this stacked line, not at PR creation.


## Review follow-up (phase-2 final review)

Deleting the inbox-drift bot test left `bot.test.ts`'s `cheatCodes` variable unused (lint error); a follow-up commit removes it.

## Leftover sweep (post-review)

- Corrected the `PublicInputArgs` natspec in `IRollup.sol`: it still described `previousInboxRollingHash` / `endInboxRollingHash` as deliberately unvalidated "until the Fast Inbox flip", but the flip (#24789, below this branch) added exactly that validation — `EpochProofLib` anchors the start boundary against the propose-time record, and the end boundary is covered transitively by the stored checkpoint header hashes. The comment now describes the implemented behavior.
- The 128-bit rolling hash this PR deliberately kept (see "What is re-homed / kept") is now removed at the node-cleanup PR #24793, where its last TS readers disappear.



Replaces #24791.
spalladino added a commit that referenced this pull request Jul 28, 2026
Node cleanup for the Fast Inbox (AZIP-22) project — removes the legacy L1-to-L2 message paths the flip (#24789) left dead. Sits on the Fast Inbox stack (#24784..#24792); base is `spl/a-1387-circuits-cleanup`.

## What's removed

- **stdlib / p2p**: `computeInHashFromL1ToL2Messages` and the whole `in_hash.ts`; the `inHash` field on `BlockProposal` (constructor, signed payload, wire, and the `createBlockProposal` validator-interface argument); the `CheckpointProposal.getBlockProposal` `inHash` pass-through; the padded per-checkpoint `InboxLeaf` helpers (`smallestIndexForCheckpoint` / `indexRangeForCheckpoint` / `checkpointNumberFromIndex`); the legacy `getL1ToL2Messages(checkpointNumber)` member from the `L1ToL2MessageSource` and archiver RPC interfaces.
- **archiver**: the legacy per-checkpoint `getL1ToL2Messages` flow, the padded per-checkpoint index invariants, the `inboxTreeInProgress` readiness gate + `L1ToL2MessagesNotReadyError`, and the 128-bit keccak rolling hash. `InboxMessage` now carries only the compact global index and the full-width consensus rolling hash (the vacuous derived `checkpointNumber` and the 128-bit `rollingHash` are gone). Reorg detection compares the local consensus rolling hash and total against the Inbox's current rolling-hash bucket (new `getBucket` / `getCurrentBucketSeq` / `getCurrentBucket` wrappers) instead of the 128-bit `getState`.
- **sequencer / validator**: the dead `inHash = Fr.ZERO` threading through the checkpoint proposal job and the validator/validation-service create-proposal path, plus the dead `in_hash_mismatch` validation-failure reason.
- **world-state**: the no-op first-in-checkpoint padding alias and the obsolete non-first-block-empty-bundle transitional test (the production assertion was already removed at the flip).
- **node**: the public-calls simulator's dead next-checkpoint message fetch and its now-unused `l1ToL2MessageSource` dependency.
- **config / env**: `AZTEC_INBOX_LAG` / `inboxLag` from ethereum config, foundation env vars, the network-consensus-config list, the l1-contracts + spartan network defaults, and the e2e option plumbing.
- **docs**: `THREAT_MODEL.md` and the archiver README rewritten from the `inHash == inbox.consume(...)` model to the consensus rolling-hash / bucket model.

## Format / store / wire notes

- **Store version bump**: `ARCHIVER_DB_VERSION` 8 → 9 because `InboxMessage` serialization dropped `rollingHash` + `checkpointNumber` and the `inboxTreeInProgress` singleton is gone. No migration — nodes resync (fresh rollup instance per release line, same no-migration policy as the rest of the stack).
- **p2p wire format**: dropping the (zeroed-since-flip) `inHash` shrinks the block-proposal bytes. Done as a plain removal at a release boundary (fresh networks), matching the A-1381 optional-tail precedent; the golden `wire_compat_fixtures.ts` buffers were regenerated. The checkpoint-proposal fixture is unaffected (it never carried `inHash`).
- **L1 follow-through: done (leftover sweep, below).** The on-chain Inbox 128-bit `messagesRollingHash` accumulation, `InboxState.rollingHash`, and the `MessageSent.rollingHash` event arg are now removed in this PR — this is the earliest branch where it is safe, since the TS reads disappear here. `getState()` / `getTotalMessagesInserted()` are kept: `chain_monitor` and fast node sync still read the message count.

## Testing

Locally green (unit suites that run without `@aztec/bb-avm-sim`): archiver (561), stdlib p2p + interfaces (76), world-state synchronizer + native (74), validator-client unit (38), ethereum config (6). Suites that import `@aztec/bb-avm-sim` (p2p libp2p, sequencer-client, node simulator, validator integration) and full typecheck of the packages downstream of the pinned-VK blocker are CI-validated. No e2e / VK regen locally.


## Review follow-up (phase-2 final review)

Four commits were appended by the final review pass:

- The env sweep removed `AZTEC_INBOX_LAG` from `scripts/network-defaults.json` while both L1 deploy-script test setUps still `readUint` the key, reverting before any test ran; the reads are gone.
- The p2p attestation store version is bumped 2 -> 3: it persists proposal/attestation buffers whose formats changed in this stack (checkpoint header lost `inHash`; block-proposal wire dropped it), and stored checkpoint attestations decode without a tolerant fallback.
- `l1_publisher.integration.test.ts` is reworked for streaming consumption: each checkpoint consumes every message sent while it was built, threading the previous checkpoint's rolling hash and reading the propose `bucketHint` from the Inbox's current bucket (the legacy inboxLag shift register is gone).
- Remaining `inboxLag`/`inHash` references are swept from the spartan environment profiles, the governance-upgrade tutorial, the validator/sequencer READMEs, and stale e2e comments.


## l1 publisher test dissolved into A-1387 (pass-8)

This branch's rewrite of `l1_publisher.integration.test.ts` is superseded by the corrected streaming-selector version landed at A-1387 (#24792): the branch's own message-reconstruction rework commit is dropped and the file is byte-identical to A-1387's here (segment diff for this file is zero). The A-1384 world-state mock bucket registration coexists with this branch's legacy-message-path deletion (the buckets survive the cleanup). First real box verification of this suite at A-1388: l1_publisher 13/13; world-state integration 12/12 at top.

## Leftover sweep (post-review)

- Removed the legacy 128-bit keccak inbox rolling hash from L1: `InboxState.rollingHash`, the `MessageSent` `bytes16 rollingHash` event arg, and its keccak accumulation in `Inbox.sol`, updating the four Solidity test suites that asserted it (`Inbox.t`, `InboxBuckets.t`, `TokenPortal.t`, `depositToAztecPublic.t`) and the three live docs-example inbox ABIs that embedded the old event shape. The node's message sync and L1-reorg detection already run entirely on the full-width consensus rolling hash from the buckets.
- Deleted the dead `InboxLeaf` class from stdlib (this branch had already removed its per-checkpoint index helpers) and fixed a stale `InboxLeaf` mention in the archiver retrieval JSDoc.
- Clarified the checkpoint-builder comments: the up-front whole-checkpoint message insertion (`insertMessagesPerBlock = false`) is only exercised by tests; production always streams messages per block.



Replaces #24793.
spalladino added a commit that referenced this pull request Jul 28, 2026
…ts generator (A-1434)

Moves `INBOX_LAG_SECONDS` (12) and `MAX_L1_TO_L2_MSGS_PER_CHECKPOINT` (1024) out of the hand-declared file-scope constants in `ProposeLib.sol` and into the generated `Constants` library, giving L1 Solidity and the TS node a single source of truth (A-1434, Fast Inbox cleanup).

Both values already lived in `constants.nr` (the Noir source of truth) and were already emitted to `constants.gen.ts`; the only gap was the Solidity side, which gates emission behind an allowlist.

- Add both names to the `SOLIDITY_CONSTANTS` allowlist in `constants/src/scripts/constants.in.ts`, so `yarn remake-constants` emits them into `ConstantsGen.sol`. `constants.gen.ts` is unchanged (already present).
- Regenerate `ConstantsGen.sol` (generator output; not hand-edited).
- `ProposeLib.sol` imports `Constants` and references the generated values; the hand-declared file-scope copies are deleted.
- Update `ProposeInboxConsumption.t.sol` to import the constants from `ConstantsGen.sol` instead of `ProposeLib.sol`.

TS node code that validates inbox consumption (`stdlib` cutoff predicate, `validator-client` streaming checks, `sequencer-client` bucket selector) already reads these from `@aztec/constants`, so no TS source change is needed.

Testing:
- `forge build` and `forge test test/rollup/ProposeInboxConsumption.t.sol` (15 tests) pass.
- The three TS regression suites pass: `stdlib` inbox_consumption (10), `validator-client` streaming_inbox_checks (14), `sequencer-client` inbox_bucket_selector (9).
- `@aztec/constants`, `@aztec/l1-artifacts`, and `@aztec/ethereum` build.

Part of the Fast Inbox stack, on top of #24784..#24793.


Replaces #24794.
spalladino added a commit that referenced this pull request Aug 18, 2026
Coordinated cutover of the Fast Inbox (AZIP-22) project (issue A-1384, FI-14). Flips the authoritative path across L1, circuits, and the node from the legacy `inHash`/frontier-tree consumption to the streaming rolling-hash Inbox.

Stacked on the node phase: #24784 (A-1379) -> #24785 (A-1380) -> #24786 (A-1381) -> #24787 (A-1382) -> #24788 (A-1383). Base is `spl/a-1383-validator-streaming`.

- **A-1431 (domain-separation decision) must resolve before this merges.** It is deliberately deferred and nothing is adopted here. If it adopts a separator, the rolling-hash link-encoding change must be inserted below this PR in the stack.

- `ProposeLib.propose` calls `validateInboxConsumption` against the parent checkpoint's consumed position (read from the parent temp-log record) instead of `inbox.consume()` + the `Rollup__InvalidInHash` check; the returned consumed total is stored in the new record.
- `ProposeArgs` gains an unsigned `bucketHint` calldata field (out of the attested payload digest).
- `TempCheckpointLog` / `CompressedTempCheckpointLog` carry `{inboxRollingHash, inboxMsgTotal, inboxConsumedBucket}`; genesis is `{0,0,0}`.
- `EpochProofLib.getEpochProofPublicInputs` anchors the rolling-hash chain start to the record of checkpoint `start - 1` (`Rollup__InvalidPreviousInboxRollingHash`).
- `Inbox.sendL2Message` returns and emits the compact cumulative message index.

- `MAX_L1_TO_L2_MSGS_PER_BLOCK` 1024 -> 256.
- `L1ToL2MessageBundle` drops `num_real_msgs`; a single `num_msgs` drives the compact tree append and the message-sponge absorb.
- `SpongeBlob::absorb_block_end_data` absorbs the L1-to-L2 tree root for every block.

- **Streaming is the only path**: the `streamingInbox` flag is removed across foundation/stdlib config + the sequencer/validator plumbing.
- **Sequencer**: sources the parent bucket from the fork's L1-to-L2 leaf count + `getInboxBucketByTotalMsgCount` (cross-checkpoint); feeds inHash zero; relaxes `waitForMinTxs` for message-only blocks; threads the consumed bucket seq as the propose `bucketHint`. Automine mirrors the selection.
- **Validator**: per-block acceptance + 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` / `handleL2BlockAndMessages` append real leaves at compact indices; the synchronizer derives each block's bundle from its leaf-index range.
- **Blob / constants / bundle**: per-block blob root, `MAX_L1_TO_L2_MSGS_PER_BLOCK = 256`, `L1ToL2MessageBundle` drops `numRealMsgs`; provable per-checkpoint ceiling 2457 -> 2234.
- **Archiver / TXE**: the dead inHash cross-check is removed; TXE appends unpadded compact leaves.
- **L1 ABI**: `@aztec/l1-artifacts` regenerated for the new `ProposeArgs` (gitignored generated output; the `ethereum` package builds against it).

- L1 `forge test`: full propose/inbox suite green (ProposeInboxConsumption, Inbox, InboxBuckets, Rollup incl. a new anchoring negative, RollupFieldRange, FeeRollup, ValidatorSelection, escape-hatch, tmnt207/419).
- Circuits `nargo test`: parity, block_root structure, msgs_only green on touched crates.
- Node jest (runnable locally): validator proposal_handler (34) + streaming checks (14), sequencer inbox selector (9), world-state native (56), blob-lib + stdlib. tsc for the broken-build-zone packages (prover-client, sequencer-client, validator-client, etc.) is CI's job of record.
- Codex (gpt-5.6-terra) reviewed both the L1/circuits layer and the node wiring: no consensus-critical issue; bucket cursor/range `(fromExclusive, toInclusive]` and pipelining-parent sourcing verified correct.

VK regen and `Prover.toml` sample-input regen ride CI (local `bb write_vk` OOMs on `rollup_root`).

- **Prover per-block message split**: the checkpoint's messages are sliced per block from the blocks' L1-to-L2 leaf-count ranges; each block root appends its own real slice at compact indices with per-block start/end snapshots and a full-height frontier hint. `startCheckpoint` no longer inserts messages up front.
- **Checkpoint keying from records**: `getL1ToL2MessageCheckpoint` binary-searches the block records for the first block whose L1-to-L2 leaf count exceeds the message index (portal claim helpers depend on this); the prover-node derives the checkpoint's consumed messages from the Inbox buckets.

Codex (gpt-5.6-terra) reviewed the prover/keying rework: no consensus defect; slice partitioning, bucket-range agreement, fork ordering, frontier height, and the checkpoint boundary rule all verified.

The `message_store` `InboxLeaf` index-formula methods (`smallestIndexForCheckpoint` etc.) have no correctness-critical live callers post-flip; the public-simulator's next-checkpoint fetch degrades safely without them (simulates without the not-yet-consumed messages). Deleted in FI-18.

A-1390 (unconsumed-bucket overwrite protection) stays post-flip: a consumption backlog older than the Inbox ring can overwrite unconsumed buckets and halt proposals until an upgrade. Acceptable for non-production lines.

A final review pass appended `fix(fast-inbox): thread per-block message sponges and wire the msgs-only block root in the prover`:

- The prover supplied the checkpoint-wide message sponge as every non-first block root's start sponge, which the block-merge/checkpoint-root continuity asserts reject whenever a non-first block carries messages. The sponge is now threaded per block.
- A zero-tx non-first block (the message-only shape this PR lets the sequencer produce) was rejected by `BlockProvingState` and the lightweight builder, and the orchestrator never selected the msgs-only block root. Both now accept it and route it through `BlockRootMsgsOnlyRollupPrivateInputs`.
- The required `bucketHint` parameter added to `enqueueProposeCheckpoint` here is now passed by the publisher unit/integration tests and the e2e synching test (the integration suite's consumption model is reworked for streaming semantics in #24793).

The `cross_chain_messages` e2e ("builds multiple blocks per slot with L1 to L2 messages") caught a product regression: when a checkpoint's first block is message-only (consuming a bucket) and the final block fails to build, the `bucketHint` on the propose payload was reconstructed from the last successfully-built block's streaming cursor and lost the already-consumed bucket, so L1 `validateInboxConsumption` rejected the checkpoint. `CheckpointProposalBroadcast` now carries `bucketHint` explicitly, sourced from the streaming state's `lastBucketRef.bucketSeq` at both propose sites (fisherman and main), so the consumed bucket survives a final-block build failure. A `checkpoint_proposal_job.test.ts` regression case drives message-only-first-block + final-block-failure and asserts the enqueued hint is the consumed bucket (RED reverting the fix: `Expected 2n, Received 0n`).

The shared cross-chain e2e helper (`message_test_helpers.ts`) evaluated `isL1ToL2MessageReady` against the default `'latest'` tip, while these suites anchor the PXE to `syncChainTip: 'checkpointed'`. Pre-flip every message entered at the first block of the next checkpoint, so `'latest'` readiness coincided with checkpointed availability; post-flip per-block insertion lets the proposed chain reach a message's consume-checkpoint a full checkpoint before it is published on L1, so readiness flipped true while the PXE had not yet synced the message and the private consume simulated against a missing membership witness (`No L1 to L2 message found`). This deterministically failed `l1_to_l2.test.ts` from this branch up. The helper now gates on the configured PXE sync tip via a new `CrossChainMessagingTest.pxeSyncChainTip` getter (defaulting to `'latest'`, so suites that do not pin a tip keep their semantics). Test-support only; box-verified green on all three cross-chain suites (`l1_to_l2`, `streaming_inbox`, `l1_to_l2_inbox_drift`).

The archiver's checkpoint reconstruction (`retrievedToPublishedCheckpoint` in `data_retrieval.ts`) read the L1→L2 message tree root once from the checkpoint's **first** block and applied it to every block — a stale pre-flip per-checkpoint assumption. Post-flip the blob carries a **per-block** root (any block in a checkpoint can insert messages). A follower node (prover / sync-only) that rebuilds a non-first message-inserting block therefore got the first block's root, so its world-state synchronizer inserted the correct message, recomputed the real root, and mismatched the reconstructed header (`block state does not match world state`) — forking the node from the sequencer on the first message-consuming block. In production this would fork every prover/follower on the first cross-chain-consuming block; it surfaced here as `cross_chain_public_message` and `token_bridge` failing (only follower nodes reconstruct via the archiver, which is why proposal validation — building from each validator's own world-state fork — never caught it). Reconstruction now uses each block's own `l1ToL2MessageRoot` from the blob. A `data_retrieval.test.ts` unit case asserts each reconstructed block's tree root matches its own blob root (RED: non-first blocks got the first block's root); both e2es green at a-1384.

`world-state/src/test/integration.test.ts` drives the streaming synchronizer through `MockPrefilledArchiver`, but `setPrefilled` only seeded the legacy per-checkpoint message map and never registered Inbox buckets. Post-flip the synchronizer reconstructs each block's consumed L1→L2 bundle from buckets (`getInboxBucketByTotalMsgCount`), which returned undefined against the empty bucket map, so every block synced an empty bundle and the reconstructed state diverged from the header (`block state does not match world state`, then a 600s timeout). `setPrefilled` now registers a genesis sentinel plus one bucket per message-carrying checkpoint (cumulative `totalMsgCount` = the block's post-insertion leaf count), rebuilt from the full prefilled chain so reorg re-prefills stay aligned. Box: 12/12 (reorg 32s→2.4s), verified at a-1384 and top.

- Removed the inert `isFirstBlock` parameter from the blob-data test fixtures (`makeBlockEndBlobData` / `makeBlockBlobData` and the archiver `makeBlockBlobDataFromBody` helper). Post-flip every block carries its own L1-to-L2 root, so the flag had no effect on output; the earlier lint fix had only underscore-bound it "for call-site compatibility". The two now-identical blob-data tests are collapsed into one.

Replaces #24789.
spalladino added a commit that referenced this pull request Aug 18, 2026
Deletes the legacy L1 Inbox path now that `propose` enforces streaming-inbox consumption (AZIP-22 Fast Inbox, FI-16). Stacked on `spl/a-1385-streaming-e2e`; part of the Fast Inbox stack (#24784..#24790 below this one).

- **Frontier trees**: the `trees` mapping, `forest`, `HEIGHT`/`SIZE`/`EMPTY_ROOT`, the per-message tree insert, `getRoot()`, and the whole `consume()` flow.
- **LAG / inboxLag**: the `LAG` immutable and `_lag`/`_height` constructor args (the Inbox constructor is now `(rollup, feeAsset, version, bucketRingSize)`), `RollupConfigInput.inboxLag` and its `RollupCore` pass-through, and `Constants.L1_TO_L2_MSG_SUBTREE_HEIGHT` from the Inbox construction.
- **Dead event field**: the `MessageSent.checkpointNumber` (former `inProgress`) argument, which became meaningless once `consume()` stopped advancing it.
- **Orphaned errors**: `Inbox__Unauthorized`, `Inbox__MustBuildBeforeConsume`, `Rollup__InvalidInHash`.

- The compact message index now reads the current bucket's running total (`totalMsgCount`) instead of a parallel counter; `InboxState.inProgress` and `getInProgress()` are gone.
- The 128-bit `rollingHash`, `InboxState.{rollingHash, totalMessagesInserted}`, `getState()`, and `getTotalMessagesInserted()` are **kept**: the node still consumes them for message sync and L1-reorg detection. Their removal is the node cleanup's job (FI-18).
- `FrontierLib` is **kept** — it still backs the base-parity (`test/Parity.t.sol`) and merkle (`test/merkle/Frontier.t.sol`) tests, which are unrelated to the Inbox (parity-circuit territory, FI-17). The plan's "delete the file" assumption is contradicted by grep.

- `ethereum` InboxContract: drop `getLag()`, drop the `MessageSent.checkpointNumber` decode, stop reading `inProgress`; `InboxContractState.treeInProgress` is now optional (no longer tracked on-chain).
- Stop threading `inboxLag` through `queries.getL1ContractsConfig` and the L1 deploy env. The broader `AZTEC_INBOX_LAG` config sweep (`ethereum/src/config.ts`, `foundation`, `stdlib`, ~30 e2e configs) is deferred to FI-18, which explicitly owns it.
- Archiver decode derives the message's checkpoint number from the compact index (the event no longer carries it), keeping the legacy per-checkpoint store shape untouched for FI-18.
- Removed the obsolete `advanceInboxInProgress` cheat code + its inbox-drift tests, and the orphaned archiver `inHash`-mismatch sync test (the cross-check was removed at the flip).

Deleting the frontier insert is a large `sendL2Message` win. Whole-test gas (`forge test` on `InboxBuckets.t.sol`), before → after:

| Case | Before | After |
| --- | --- | --- |
| First-ever message | 175,251 | 116,835 |
| First message of a new L1 block | 329,460 | 195,555 |
| Absorb into an existing bucket | 286,673 | 149,968 |
| Rollover mid-block (256 messages) | 18,923,988 | 3,068,480 |

Per-call `sendL2Message` gas after cleanup: first-ever 99,526; first-of-new-block 53,763; existing-bucket absorb 9,273; rollover 53,801.

- `forge build` + `forge test` green: 887 passed, 0 failed. This fixes 4 pre-existing baseline failures the flip stranded (`fee_portal`/`TokenPortal` deposit tests using tree-relative indices + the old event shape).
- `@aztec/ethereum` builds clean; `config`/`queries` unit tests green.
- `@aztec/archiver` has no self-owned type errors; `message_store` (36), `archiver-sync` (59), and the decode/struct suites (58) all pass. (Pre-existing `noir-protocol-circuits-types` stale-artifact errors are unaffected.)
- e2e not run locally. The stability gate ("a few days of green e2e/networks before deleting the fallback") applies at merge time for this stacked line, not at PR creation.

Deleting the inbox-drift bot test left `bot.test.ts`'s `cheatCodes` variable unused (lint error); a follow-up commit removes it.

- Corrected the `PublicInputArgs` natspec in `IRollup.sol`: it still described `previousInboxRollingHash` / `endInboxRollingHash` as deliberately unvalidated "until the Fast Inbox flip", but the flip (#24789, below this branch) added exactly that validation — `EpochProofLib` anchors the start boundary against the propose-time record, and the end boundary is covered transitively by the stored checkpoint header hashes. The comment now describes the implemented behavior.
- The 128-bit rolling hash this PR deliberately kept (see "What is re-homed / kept") is now removed at the node-cleanup PR #24793, where its last TS readers disappear.

Replaces #24791.
spalladino added a commit that referenced this pull request Aug 18, 2026
Node cleanup for the Fast Inbox (AZIP-22) project — removes the legacy L1-to-L2 message paths the flip (#24789) left dead. Sits on the Fast Inbox stack (#24784..#24792); base is `spl/a-1387-circuits-cleanup`.

- **stdlib / p2p**: `computeInHashFromL1ToL2Messages` and the whole `in_hash.ts`; the `inHash` field on `BlockProposal` (constructor, signed payload, wire, and the `createBlockProposal` validator-interface argument); the `CheckpointProposal.getBlockProposal` `inHash` pass-through; the padded per-checkpoint `InboxLeaf` helpers (`smallestIndexForCheckpoint` / `indexRangeForCheckpoint` / `checkpointNumberFromIndex`); the legacy `getL1ToL2Messages(checkpointNumber)` member from the `L1ToL2MessageSource` and archiver RPC interfaces.
- **archiver**: the legacy per-checkpoint `getL1ToL2Messages` flow, the padded per-checkpoint index invariants, the `inboxTreeInProgress` readiness gate + `L1ToL2MessagesNotReadyError`, and the 128-bit keccak rolling hash. `InboxMessage` now carries only the compact global index and the full-width consensus rolling hash (the vacuous derived `checkpointNumber` and the 128-bit `rollingHash` are gone). Reorg detection compares the local consensus rolling hash and total against the Inbox's current rolling-hash bucket (new `getBucket` / `getCurrentBucketSeq` / `getCurrentBucket` wrappers) instead of the 128-bit `getState`.
- **sequencer / validator**: the dead `inHash = Fr.ZERO` threading through the checkpoint proposal job and the validator/validation-service create-proposal path, plus the dead `in_hash_mismatch` validation-failure reason.
- **world-state**: the no-op first-in-checkpoint padding alias and the obsolete non-first-block-empty-bundle transitional test (the production assertion was already removed at the flip).
- **node**: the public-calls simulator's dead next-checkpoint message fetch and its now-unused `l1ToL2MessageSource` dependency.
- **config / env**: `AZTEC_INBOX_LAG` / `inboxLag` from ethereum config, foundation env vars, the network-consensus-config list, the l1-contracts + spartan network defaults, and the e2e option plumbing.
- **docs**: `THREAT_MODEL.md` and the archiver README rewritten from the `inHash == inbox.consume(...)` model to the consensus rolling-hash / bucket model.

- **Store version bump**: `ARCHIVER_DB_VERSION` 8 → 9 because `InboxMessage` serialization dropped `rollingHash` + `checkpointNumber` and the `inboxTreeInProgress` singleton is gone. No migration — nodes resync (fresh rollup instance per release line, same no-migration policy as the rest of the stack).
- **p2p wire format**: dropping the (zeroed-since-flip) `inHash` shrinks the block-proposal bytes. Done as a plain removal at a release boundary (fresh networks), matching the A-1381 optional-tail precedent; the golden `wire_compat_fixtures.ts` buffers were regenerated. The checkpoint-proposal fixture is unaffected (it never carried `inHash`).
- **L1 follow-through: done (leftover sweep, below).** The on-chain Inbox 128-bit `messagesRollingHash` accumulation, `InboxState.rollingHash`, and the `MessageSent.rollingHash` event arg are now removed in this PR — this is the earliest branch where it is safe, since the TS reads disappear here. `getState()` / `getTotalMessagesInserted()` are kept: `chain_monitor` and fast node sync still read the message count.

Locally green (unit suites that run without `@aztec/bb-avm-sim`): archiver (561), stdlib p2p + interfaces (76), world-state synchronizer + native (74), validator-client unit (38), ethereum config (6). Suites that import `@aztec/bb-avm-sim` (p2p libp2p, sequencer-client, node simulator, validator integration) and full typecheck of the packages downstream of the pinned-VK blocker are CI-validated. No e2e / VK regen locally.

Four commits were appended by the final review pass:

- The env sweep removed `AZTEC_INBOX_LAG` from `scripts/network-defaults.json` while both L1 deploy-script test setUps still `readUint` the key, reverting before any test ran; the reads are gone.
- The p2p attestation store version is bumped 2 -> 3: it persists proposal/attestation buffers whose formats changed in this stack (checkpoint header lost `inHash`; block-proposal wire dropped it), and stored checkpoint attestations decode without a tolerant fallback.
- `l1_publisher.integration.test.ts` is reworked for streaming consumption: each checkpoint consumes every message sent while it was built, threading the previous checkpoint's rolling hash and reading the propose `bucketHint` from the Inbox's current bucket (the legacy inboxLag shift register is gone).
- Remaining `inboxLag`/`inHash` references are swept from the spartan environment profiles, the governance-upgrade tutorial, the validator/sequencer READMEs, and stale e2e comments.

This branch's rewrite of `l1_publisher.integration.test.ts` is superseded by the corrected streaming-selector version landed at A-1387 (#24792): the branch's own message-reconstruction rework commit is dropped and the file is byte-identical to A-1387's here (segment diff for this file is zero). The A-1384 world-state mock bucket registration coexists with this branch's legacy-message-path deletion (the buckets survive the cleanup). First real box verification of this suite at A-1388: l1_publisher 13/13; world-state integration 12/12 at top.

- Removed the legacy 128-bit keccak inbox rolling hash from L1: `InboxState.rollingHash`, the `MessageSent` `bytes16 rollingHash` event arg, and its keccak accumulation in `Inbox.sol`, updating the four Solidity test suites that asserted it (`Inbox.t`, `InboxBuckets.t`, `TokenPortal.t`, `depositToAztecPublic.t`) and the three live docs-example inbox ABIs that embedded the old event shape. The node's message sync and L1-reorg detection already run entirely on the full-width consensus rolling hash from the buckets.
- Deleted the dead `InboxLeaf` class from stdlib (this branch had already removed its per-checkpoint index helpers) and fixed a stale `InboxLeaf` mention in the archiver retrieval JSDoc.
- Clarified the checkpoint-builder comments: the up-front whole-checkpoint message insertion (`insertMessagesPerBlock = false`) is only exercised by tests; production always streams messages per block.

Replaces #24793.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant