feat(protocol)!: seed protocol contract registration nullifiers at genesis - #25497
spalladino wants to merge 4 commits into
Conversation
…nesis A production genesis world state now starts with the protocol contracts already registered: for each of the three protocol contracts, the class registration nullifier siloed by ContractClassRegistry and the instance publication nullifier siloed by ContractInstanceRegistry over the magic protocol address. The six leaves replace padding inside the nullifier tree's 128-leaf prefill region, so the tree size is unchanged while the root moves. That shifts the canonical genesis constants: GENESIS_BLOCK_HEADER_HASH 0x22c20c79... -> 0x075fee31... GENESIS_ARCHIVE_ROOT 0x0a0877e7... -> 0x1e8df7d0... and the genesis nullifier-tree root 0x18935581... -> 0x21a19fe6... Because the genesis archive root is what a rollup stores in archives[0], this only applies to a network initialized from the new genesis; already deployed networks keep their original roots. The TypeScript side is carried as labs patch 0009: protocol-contracts generates ProtocolContractGenesisNullifiers from the contract artifacts and exposes DEFAULT_GENESIS_DATA, GenesisData.prefilledNullifiers becomes required, and world-state defaults resolve to the protocol baseline. getGenesisValues' fifth argument is now *additional* nullifiers composed on top of that baseline, with duplicates rejected. The remaining patches are renumbered by `labs-patches/bootstrap.sh export`; their contents are unchanged.
…undled artifacts The membership assertions in world-state read back the same generated list they seed, so they cannot catch a protocol class id rotating out from under the constants. Derive the expected set independently from the artifacts.
…ive root The first checkpoint of each family builds on the genesis archive, and the second builds on the first, so all six move.
|
Validation update — everything below was run on a clean clone of this branch (all nine patches applied cleanly on a fresh checkout), fully built from source. Checkpoint fixtures. Regenerated all six through
New e2e case. Two pre-existing failures, confirmed unrelated.
|
|
Smoke-tested the removed fast path from the packaged CLI, since Zero-prefund genesis with no explicit |
…ects The generated protocol contract data is rebuilt from the same artifacts the test recomputes from, so a rotated class id moves both sides and passes. The pinned-root assertion in world-state is what catches that.
|
Re: the regeneration flow — opened as #25503, stacked on this PR. It reduces the pins before automating them: a new Also corrected here in |
Revives #24254 (closed in favour of this branch, which is rebuilt on current
next; the old one still carried the deleted N-API backend, obsolete generated files and stale roots), and builds on #24567.A production genesis world state now starts with the protocol contracts already registered. For each of the three protocol contracts we seed two nullifiers:
siloNullifier(ContractClassRegistry, classId)— the class registrationsiloNullifier(ContractInstanceRegistry, magicAddress)— the instance publication, over the magic protocol address (1/2/3), which is the address the canonical instance actually hasThe six leaves replace padding inside the nullifier tree's 128-leaf prefill region, so the tree size stays 128 while the root moves.
New canonical constants
GENESIS_BLOCK_HEADER_HASH0x22c20c79…0x075fee3188fcffcdcdfaf61295ed5ff6e12f4a2316274c1fca25481e09709ac7GENESIS_ARCHIVE_ROOT0x0a0877e7…0x1e8df7d033f879c44fbdddbacf525017e2f35a5ea1f3c33d4d1f531735b9a1100x18935581…0x21a19fe6f636fb24d9f63edb7b807613492cc0001c91e531917a2539f57e2ba8The genesis archive root is what a rollup stores in
archives[0], so this only applies to a network initialized from the new genesis. Already deployed networks keep their original roots; no legacy-genesis configuration, chain detection or migration is in scope. The mainnet/testnet compatibility tests now reconstruct each network's historical genesis (empty nullifier list, its own prefunding) and still assert its deployed root.Foundation
constants.nr/block_header.nr: new constants and the genesis nullifier root inhash_of_genesis_block_header.world_state.test.cpp:GetInitialTreeInfoForAllTreesconstructs the world state with the six canonical nullifiers and asserts the new root.GetInitialTreeInfoWithPrefilledNullifiersis untouched.aztec_constants.hpp,ConstantsGen.sol, labsconstants.gen.ts) are untracked and regenerate fromconstants.nr.WorldStatealready supportedprefilled_nullifiers(feat(world-state): support prefilled nullifiers in genesis state #24567) — no new plumbing.Labs (patch
0009)protocol-contracts:generate_data.tsemitsProtocolContractGenesisNullifiersderived from the contract artifacts (sorted ascending, duplicates rejected, since the indexed nullifier tree requires unique strictly-increasing prefilled leaves); newDEFAULT_GENESIS_DATA.stdlib:GenesisData.prefilledNullifiersbecomes required, forcing an audit of every explicit literal.EMPTY_GENESIS_DATAstays as a deliberately empty state for low-level tests and is documented as not matching production roots.world-state: native-service and synchronizer-factory defaults resolve toDEFAULT_GENESIS_DATA.getGenesisValues' fifth argument is redefined as additional nullifiers, composed on top of the protocol baseline, sorted on a copy, with duplicates rejected rather than discarded. The empty-genesis fast path is gone — the root is always computed on an ephemeral world state.FeeJuiceclass is rejected as a duplicate nullifier.getL1FixturePath(AZTEC_L1_FIXTURES_DIR) so the checkpoint-fixture writer resolves from inside the labs checkout.Patches
0001–0008are only renumbered bylabs-patches/bootstrap.sh export; their patch IDs are unchanged.Validation
NativeWorldStateService; the same measurement withEMPTY_GENESIS_DATAreproduces today's pinned constants exactly.nargo test --package types hash_of_genesis— 1 passed.world_state_tests --gtest_filter='*GetInitialTreeInfo*'— 3 passed.world-state/src/testing.test.ts(7),world-state/src/native/native_world_state.test.ts(56),stdlib/src/block/l2_block.test.ts(3) — passed.labs-patches/bootstrap.sh check— clean.protocol-contracts/src/genesis_data.test.ts— 2 passed; the artifact-derived recomputation matches the generated constants exactly.l1-contracts/test/fixtures/*_checkpoint_{1,2}.jsonregenerated (checkpoint 1 builds on the genesis archive, checkpoint 2 on checkpoint 1).forge test --match-contract 'CheckpointPreflight|Rollup'— 99 passed, 0 failed, against the regenerated fixtures.Known outstanding
Protocol class IDs are now genesis inputs, so an artifact or compiler change that rotates a class ID rotates the genesis roots.
protocol-contracts/src/genesis_data.test.tsrecomputes the expected set from the bundled artifacts, so that drift fails a test rather than silently moving the roots.