feat(protocol)!: seed protocol contract registration nullifiers at genesis#24254
Open
spalladino wants to merge 3 commits into
Open
feat(protocol)!: seed protocol contract registration nullifiers at genesis#24254spalladino wants to merge 3 commits into
spalladino wants to merge 3 commits into
Conversation
…nesis The world-state genesis only seeded prefilled public data, never the registration nullifiers for the bundled protocol contract classes. The archiver preloads those classes at a synthetic block 0, so a permissionless on-chain re-publish of a bundled protocol class id was protocol-valid (the class-id nullifier was absent at genesis), and when the archiver replayed that block it hit a duplicate-key throw on the block-0 preload and stalled L1 sync. Seed the canonical protocol contract registration nullifiers into the genesis nullifier tree so such a re-publish pushes an already-existing nullifier and is rejected as a duplicate nullifier before it ever reaches the archiver. - Generate ProtocolContractGenesisNullifiers (siloed class-id and magic-address instance nullifiers, sorted ascending as the indexed tree requires) in protocol-contracts. - Add DEFAULT_GENESIS_DATA (the canonical genesis: empty public data + the protocol nullifiers) and make GenesisData.prefilledNullifiers required. Production world-state defaults switch from EMPTY_GENESIS_DATA to DEFAULT_GENESIS_DATA; EMPTY_GENESIS_DATA now seeds an empty nullifier tree for low-level tree tests only. - Plumb prefilled_nullifiers through the native WorldState and the napi wrapper, enforcing uniqueness/strict-increase before handing leaves to C++. BREAKING CHANGE: seeding the nullifiers changes the genesis nullifier-tree root, hence the genesis block header hash and genesis archive root. Recomputed via WorldStateTest.GetInitialTreeInfoForAllTrees and propagated through constants.nr to constants.gen.ts, ConstantsGen.sol and aztec_constants.hpp: GENESIS_ARCHIVE_ROOT = 0x063786f95f1ae8ebd17b22cb07d7ba122cefae9b0ecb975f5dc3e6e576a5bddc, GENESIS_BLOCK_HEADER_HASH = 0x1302a9e6f643ec596522764c20e5d08d60d33988f11c1559ee13bcd2e2bd8e5d. Adds an e2e regression test that re-publishing a bundled protocol class fails with a duplicate nullifier. The instance nullifier cannot be triggered on-chain (a publish emits the derived address, never a magic protocol address), which is documented in the test. Fixes A-1257
…ests (A-1257) The genesis nullifier-tree root changed to 0x1bcda34f33b87d40db8bb8ee1378ef7123c16c197da1ded6ea47659230559f42 after seeding protocol-contract registration nullifiers, so update the two test sites that hardcode the old stale root.
41ad236 to
35c2820
Compare
…ullifiers (A-1257)
The genesis defaults in NativeWorldStateService now use DEFAULT_GENESIS_DATA, which seeds
6 protocol-contract registration nullifiers into the nullifier tree at genesis. This changes
the nullifier-tree root embedded in the serialized AvmCircuitInputs, so the golden binary
used by avm_minimal.test.ts and the C++ hinting_dbs tests must be regenerated.
Regenerated with:
AZTEC_GENERATE_TEST_DATA=1 yarn workspace @aztec/simulator test \
src/public/public_tx_simulator/apps_tests/avm_minimal.test.ts
Verification: TS test passes without env var; C++ vm2_tests HintingDBs suite passes (15/15).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
Root-cause fix for A-1257, complementing the archiver-resilience fix in #24227. Nodes preload every bundled protocol contract class/instance at synthetic block 0, but world-state genesis seeded no matching registration nullifiers. That gap made a first on-chain
ContractClassRegistry.publishof a bundled protocol class id protocol-valid, which then collided with the archiver's block-0 preload and stalled L1 sync. Seeding those nullifiers at genesis rejects the re-publish at the protocol level (duplicate nullifier), so it never reaches the archiver at all.Fixes A-1264
Approach
Pre-insert the protocol contracts' registration nullifiers into the genesis nullifier tree. For each of the three protocol contracts we seed two siloed nullifiers:
the class nullifier
siloNullifier(ContractClassRegistry, classId)— matches whatContractClassRegistry.publishemits;the instance nullifier
siloNullifier(ContractInstanceRegistry, magicAddress)— uses the magic protocol address (1/2/3), consistent with the node's block-0 instance preload.C++ world-state gains a
prefilled_nullifiersinput, threaded through theWorldStateconstructors, the napi binding, andcreate_canonical_fork, which inserts them as the nullifier tree's initial leaves. The indexed tree requires its initial leaves to be unique and strictly increasing, so the seeded list is sorted ascending (and a defensive check enforces this before the C++ call).GenesisData.prefilledNullifiersis a required field.EMPTY_GENESIS_DATA(truly empty) is retained for low-level tree tests; a new canonicalDEFAULT_GENESIS_DATA(in@aztec/protocol-contracts, which can see both the type and the generated nullifier list) carries the protocol nullifiers and is now the default in all production world-state constructors.GenesisDatastays in@aztec/stdlib, which cannot depend onprotocol-contracts(circular), so the default is applied one layer up.The seeded set changes the genesis nullifier-tree root, hence the genesis block header hash and archive root. Both constants were recomputed authoritatively by seeding the same nullifiers in the C++
WorldStateTest.GetInitialTreeInfoForAllTreesand reading the result, then propagated to every location that pins them:constants.nr,constants.gen.ts,ConstantsGen.sol,aztec_constants.hpp, the V5 deploy script, themainnet_compatibilitytest, and the regenerated L1 checkpoint fixtures.Breaking change
This changes the genesis state root. It applies to new networks only — an already-deployed chain has the old genesis archive root committed to L1
archives[0]and cannot adopt the new one. Coordinated genesis migration is required for any redeploy.Validation
WorldStateTestgenesis tests pass with the recomputed roots;@aztec/world-statenative tests 50/50.protocol_class_publish.test.ts: re-publishing a bundled protocol class is rejected as a duplicate nullifier. The protocol instance path is documented as unreachable on-chain (publish emits the derived address; no tx can have a magic address asmsg_sender), so it is not separately testable.forge teston their consumers passes (69/69).map_size/thread_pool/ephemeralindices had to shift to 7/8/9 after insertingprefilled_nullifiersat index 4), which otherwise broke everyNativeWorldStateconstructor.Changes
prefilled_nullifiersplumbing + recomputed genesis constants; standalone wsdb IPC documented as non-production (seeds empty nullifiers).GENESIS_ARCHIVE_ROOT/GENESIS_BLOCK_HEADER_HASH; V5 deploy script and 6 checkpoint fixtures updated.prefilledNullifiersonGenesisData.ProtocolContractGenesisNullifiers+DEFAULT_GENESIS_DATA.DEFAULT_GENESIS_DATA; seeded-nullifier wiring + ordering guard.