Skip to content

feat: introduce inboxRollingHash end to end, dual with inHash (A-1373)#24600

Open
spalladino wants to merge 4 commits into
spl/a-1372-msg-bundle-componentsfrom
spl/a-1373-inbox-rolling-hash
Open

feat: introduce inboxRollingHash end to end, dual with inHash (A-1373)#24600
spalladino wants to merge 4 commits into
spl/a-1372-msg-bundle-componentsfrom
spl/a-1373-inbox-rolling-hash

Conversation

@spalladino

Copy link
Copy Markdown
Contributor

Stacked on #24587 (A-1372). Part of the AZIP-22 Fast Inbox work.

What

Introduces the inboxRollingHash — a truncated-to-field sha256 rolling chain over the L1→L2 message leaves — end to end, carried as a dual of the legacy inHash. The legacy inHash remains authoritative; the rolling hash is computed and threaded everywhere but not yet enforced on L1, so this change is behavior-preserving pre-flip.

Each link is h' = sha256ToField(h_be32 || leaf_be32) with the top byte of the digest zeroed, genesis value zero.

Changes

Circuits (noir)

  • Parity base computes the rolling chain over its real message leaves (start_rolling_hashend_rolling_hash, num_msgs), asserting trailing padding lanes are zero. Parity root asserts chunk continuity (children[i].start == children[i-1].end) and sums the counts.
  • Block and checkpoint rollup public inputs carry a {start, end} rolling-hash pair, propagated exactly like in_hash. Checkpoint merges assert right.start == left.end (decision 11 anchoring).
  • The checkpoint header gains inbox_rolling_hash immediately after in_hash.
  • The root rollup public inputs expose the {previous, end} pair sourced from the merged checkpoint public inputs, so the epoch's consumed chain segment is passed through to proof verification.

L1 (Solidity)

  • ProposedHeaderLib serializes the new header field (header 348 → 380 bytes).
  • PublicInputArgs gains previousInboxRollingHash / endInboxRollingHash; EpochProofLib places them at public-input positions 3 and 4 (header hashes, fees, constants and blob inputs shift by two). Both values are deliberately unvalidated until the Fast Inbox flip — for now they are pass-through only.

TypeScript

  • updateInboxRollingHash / accumulateInboxRollingHash mirror the circuit chain; getPreviousCheckpointInboxRollingHash sources the previous checkpoint's end value (returns zero for checkpoint ≤ 1). The sequencer, validator, and prover populate the header field; the orchestrator threads per-base start hashes; the prover-node publisher fills the two new PublicInputArgs.
  • RootRollupPublicInputs gains the pair with matching serialization, conversion, factories and viem types.

Constants

  • CHECKPOINT_HEADER_LENGTH 13 → 14, BLOCK_ROLLUP_PUBLIC_INPUTS_LENGTH 56 → 58, CHECKPOINT_ROLLUP_PUBLIC_INPUTS_LENGTH 149 → 151, ROOT_ROLLUP_PUBLIC_INPUTS_LENGTH 111 → 113.
  • Circuit ABIs changed, so pinned-build.tar.gz is regenerated and committed.

Testing

  • yarn build green; forge test green (870 passed); noir rollup-lib root suites green (176 passed).
  • stdlib serde, prover-node publisher, and the checkpoint-sub-tree / top-tree orchestrator suites green.
  • Cross-chain l1_to_l2 e2e confirms the header field round-trips through L1 and the archiver (decoded inboxRollingHash = 0 for the genesis checkpoint, as expected). This suite is registered flaky (.test_patterns.yml); the repeated-consumption cases exhibit a pre-existing nullifier-sync timing race unrelated to this change.

Adds the Fast Inbox (AZIP-22) rolling-hash commitment as a dual of the
legacy inHash, flowing through parity circuits, block/checkpoint rollup
public inputs, the checkpoint header, L1, and the node/prover.

Circuits (noir):
- Parity base absorbs only real leaves into a truncated-to-field sha256
  chain (accumulate_inbox_rolling_hash, guarded by num_msgs +
  assert_trailing_zeros); merkle roots keep absorbing padding. Parity
  public inputs gain start_rolling_hash / end_rolling_hash / num_msgs;
  parity root threads the four base segments sequentially and sums counts.
- BlockRollupPublicInputs and CheckpointRollupPublicInputs carry a
  {start,end} pair propagated exactly like in_hash (first block root sets
  it, merges take left's, validate asserts right's is zero). Checkpoint
  merge asserts right.start == left.end (decision 11). CheckpointHeader
  gains inbox_rolling_hash (the end value) immediately after in_hash.
- Root rollup public inputs are unchanged: exposing the epoch pair is
  inseparable from the L1 epoch-proof anchoring that is out of scope here
  (FI-08/FI-14), so it is deferred with the rest of L1 anchoring.

TS: stdlib inbox_rolling_hash mirror (unit-tested against the FI-02
vectors), CheckpointHeader / parity / block / checkpoint PI classes,
noir-protocol-circuits-types conversions, orchestrator threading of
per-base start hashes + counts, and node header population at the
sequencer / prover / validator sites (sourced from the previous
checkpoint header via getPreviousCheckpointInboxRollingHash).

L1: ProposedHeader + ProposedHeaderLib.hash pack inboxRollingHash after
inHash (no new propose checks); test fixtures + headerHash regenerated.
Add the {previous, end} inbox rolling-hash pair to RootRollupPublicInputs so
the epoch's consumed chain segment is carried through to proof verification.
The root rollup sources the pair from the merged checkpoint public inputs;
continuity within the range is already enforced by the checkpoint merges.

On L1, PublicInputArgs gains previousInboxRollingHash/endInboxRollingHash and
EpochProofLib places them at public-input positions 3 and 4 (header hashes,
fees, constants and blob inputs shift by two). Both values are deliberately
unvalidated until the Fast Inbox flip, when they will be checked against the
per-checkpoint records written at propose; for now they are pass-through only.

Also fixes two ivc-integration benchmark generators whose hand-built parity
base inputs were missing start_rolling_hash/num_msgs.
Comment on lines +146 to +155
const start = baseParityIndex * NUM_MSGS_PER_BASE_PARITY;
const realMessages = this.l1ToL2Messages.slice(start, start + NUM_MSGS_PER_BASE_PARITY);
const messages = padArrayEnd(realMessages, Fr.ZERO, NUM_MSGS_PER_BASE_PARITY);
// Thread the rolling hash: this base's start is the chain value after all real messages in earlier bases, so the
// four bases chain sequentially and the parity root ends at the checkpoint's rolling hash. Only real (non-padding)
// messages are absorbed, matching the proposer's `accumulateInboxRollingHash`.
const startRollingHash = accumulateInboxRollingHash(
this.startInboxRollingHash,
this.l1ToL2Messages.slice(0, start),
);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is correct. Aren't the this.l1ToL2Messages already padded? And shouldn't realMessages drop the pads? Or is that left for a future PR?

…1373)

Exposing the inbox rolling hash on the parity-root and rollup public inputs
changed the ABI of the block-root-first variants, block-merge, checkpoint-merge
and root circuits, but their committed crates/rollup-*/Prover.toml sample inputs
were not refreshed. CI runs `nargo execute` against these fixtures, so all six
failed to deserialize (e.g. missing parity_root.public_inputs.start_rolling_hash
and previous_rollups[].public_inputs.start_inbox_rolling_hash).

Regenerate the six stale fixtures. The circuits push their serialized inputs via
pushTestData when run through the prover, so a new prover-client test drives
representative epochs through the simulated orchestrator and dumps the captured
inputs, replacing the dead orchestrator_single_checkpoint.test.ts regeneration
path. The suite is skipped unless AZTEC_GENERATE_TEST_DATA=1 is set.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this needed? How were those inputs generated before for the tests? Or are these new tests?

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