Skip to content

feat(fast-inbox): domain-separate the inbox rolling-hash chain - #25067

Open
spalladino wants to merge 1 commit into
spl/fast-inbox-3-cleanupfrom
spl/fast-inbox-4-domain-sep
Open

feat(fast-inbox): domain-separate the inbox rolling-hash chain#25067
spalladino wants to merge 1 commit into
spl/fast-inbox-3-cleanupfrom
spl/fast-inbox-4-domain-sep

Conversation

@spalladino

Copy link
Copy Markdown
Contributor

Part of the Fast Inbox (AZIP-22) stack, on top of #25038. Umbrella: #25041.

Context

The streaming-inbox consensus rolling hash advanced as h' = sha256ToField(h || leaf) over two 32-byte big-endian values — a 64-byte preimage byte-identical to the L2-to-L1 out_hash merkle node hash, so nothing structurally distinguished a chain link from a merkle node. The domain-separation assessment (A-1431) concluded this identical-preimage ambiguity is not exploitable today but is ~free to close, and the free window ends when the streaming-inbox flip lands (post-flip the construction is consensus).

Approach

Each link now prefixes a domain separator: h' = sha256ToField(DOM_SEP__INBOX_ROLLING_HASH || h || leaf). The tag is a 4-byte big-endian u32, so the preimage grows 64 → 68 bytes and stays inside two sha256 compression blocks in-circuit; on L1 the precompile moves up one word tier (84 → 96 gas per link), with a measured end-to-end sendL2Message delta of +36–38 gas. Genesis stays zero, so the Inbox needs no migration. out_hash is deliberately left untagged.

  • DOM_SEP__INBOX_ROLLING_HASH joins the domain-separator registry in constants.nr, derived and collision-checked like its siblings in constants_tests.nr (hash_to_u32("az_dom_sep", "inbox_rolling_hash") = 3737216265).
  • generateSolidityConstants now emits domain separators alongside plain constants (mirroring the C++, PIL and Rust generators), so the tag reaches ConstantsGen.sol through the solidity.json allowlist.
  • The L1 accumulator, the noir accumulate_inbox_rolling_hash (via a new types::hash::accumulate_sha256_with_separator), and the TS mirror all adopt the tagged construction.
  • The pinned cross-language test vectors (noir, stdlib, InboxBuckets.t.sol) were recomputed from an independent sha256 reference over the exact 68-byte preimages, and the nine rollup Prover.toml sample inputs carrying rolling-hash values were regenerated through the canonical prover-client path.

Fixes A-1431

Every Inbox message leaf advanced the consensus rolling hash as `h' = sha256ToField(h || leaf)` over the two 32-byte
big-endian values. That 64-byte preimage is byte-identical to the one the L2-to-L1 `out_hash` merkle node hash absorbs,
so nothing distinguished a chain link from a merkle node. Each link now absorbs a domain separator first:

    h' = sha256ToField(DOM_SEP__INBOX_ROLLING_HASH || h || leaf)

The tag is a 4-byte big-endian u32 written as a prefix, not a padded field element, so the preimage grows from 64 to 68
bytes and stays inside two sha256 compression blocks in-circuit. On L1 the precompile moves up one word tier
(60 + 12*ceil(len/32), 84 -> 96 gas per link); the measured end-to-end `sendL2Message` delta is +36-38 gas including
the extra `abi.encodePacked` memory word (absorb into an open bucket: 8369 -> 8405). The genesis rolling hash is
unchanged at zero, so `Inbox.sol` needs no migration. `out_hash` is deliberately left untagged and is not touched here.

- `DOM_SEP__INBOX_ROLLING_HASH` joins the domain-separator registry in `constants.nr`, derived and collision-checked
  like its siblings in `constants_tests.nr` (`hash_to_u32("az_dom_sep", "inbox_rolling_hash")` = 3737216265).
- `generateSolidityConstants` now emits domain separators alongside plain constants, mirroring the C++, PIL and Rust
  generators, so the tag can reach `ConstantsGen.sol` through the `solidity.json` allowlist rather than by hand.
- `Hash.accumulateInboxRollingHash` prepends `uint32(Constants.DOM_SEP__INBOX_ROLLING_HASH)`.
- `types::hash` gains `accumulate_sha256_with_separator`, the tagged sibling of `accumulate_sha256` (this is the
  variant the `accumulate_sha256` TODO asked for); `accumulate_inbox_rolling_hash` uses it.
- The TS mirror in `stdlib` prepends the same 4-byte tag from the generated `DomainSeparator` enum.

The pinned cross-language test vectors in the noir helper, the `stdlib` suite and `InboxBuckets.t.sol` were recomputed
from an independent node `crypto.createHash` reference over the exact 68-byte preimages, and the nine rollup
`Prover.toml` sample inputs that carry rolling-hash values were regenerated with
`AZTEC_GENERATE_TEST_DATA=1 yarn workspace @aztec/prover-client test regenerate_rollup_sample_inputs`.

Testing:
- `nargo test --package rollup_lib`: 385/385 pass; `--package types`: 413/413 pass.
- `nargo execute` passes for all 16 crates with committed sample inputs.
- `forge test` (full l1-contracts suite): 889 passed, 0 failed, 3 skipped.
- `@aztec/stdlib` inbox_rolling_hash (6) and `@aztec/archiver` message_store (38) pass; constants-codegen node tests
  (20) pass.
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