Today's bootstrap-chain CreateChainTx (#168, 2026-06-04) produced a zoo mainnet chain with block-0 hash `0x88dd3a53…`, which orphans the Zoo mainnet RLP archive (799 blocks, recorded parent hash `0x7c548af4…`). admin_importChain returns "block 1 parent mismatch". Root cause: bootstrap-chain consumed the cc7d3d7 form of zoo-mainnet/ genesis.json (969 bytes), which is a SUBSET of the empirical canonical. The cc7d3d7 commit was labeled "RLP-matching pristine form" but omitted the explicit block-0 header fields (parentHash, mixHash, nonce, number, gasUsed, extraData, coinbase) and the precompile-defer config fields (quasarTimestamp, fortunaTimestamp, graniteTimestamp = 253399622400). luxd's defaults for these fields produce a DIFFERENT block-0 hash than the empirical form. Fix: sync this deployment-side genesis.json to the empirical canonical at ~/work/lux/state/chains/zoo-mainnet/genesis.json (1449 bytes, sha256 f8482568e7a10e882c6bd0248549ad75257edec79a83f659f27e1a34e71b59fa). Now bootstrap-chain will produce a chain with block-0 hash 0x7c548af47de27560779ccc67dda32a540944accc71dac3343da3b9cd18f14933 which matches the RLP archive's recorded parent. Add LLM.md sibling at this path documenting: - state-repo as source-of-truth (canonical) vs zoo/universe as deployment mirror - the regression history (cc7d3d7 incomplete form vs empirical) - the trade-off when re-firing CreateChainTx after the wrong-genesis chain has already committed (new chain ID required; aliases must re-sync) This unblocks the zoo mainnet RLP import — a fresh CreateChainTx will spawn a chain whose block-0 matches the RLP, and the script at ~/work/lux/universe/k8s/lux-mainnet/scripts/zoo-rlp-reimport.sh can fire cleanly. The orphaned oxDk6jU7… chain stays in P-state but is effectively unused; the chain-aliases map updates to the new ID. Followup: testnet+devnet zoo genesis files and hanzo/pars/spc mainnet genesis files may have the same regression. Audit + sync in a sibling commit.
4.1 KiB
Zoo Mainnet — deployment genesis (canonical sync)
Source-of-truth
Authoritative provenance + empirical genesis hash live in lux/state:
- Canonical genesis JSON:
~/work/lux/state/chains/zoo-mainnet/genesis.json - Provenance + hash + RLP details:
~/work/lux/state/chains/zoo-mainnet/LLM.md - RLP archive:
~/work/lux/state/rlp/zoo-mainnet/zoo-mainnet-200200.rlp(799 blocks, 1.3 MiB, sha2560850841a368dd1f85ae09d40f0552aecb679225b58ce4524b2d0a3337c7afc98) - Empirical genesis hash:
0x7c548af47de27560779ccc67dda32a540944accc71dac3343da3b9cd18f14933
genesis.json in this directory is a byte-for-byte mirror of the state-repo
canonical form. The sync target is the deployment artifact that
bootstrap-chain (luxfi/genesis cmd) and the operator's tenantImports
flow consume. Drift between this file and the state-repo source produces
a different block-0 hash, which orphans every chain spawned from it from
the RLP archive (no parent-hash match → admin_importChain returns
"block 1 parent mismatch" and refuses to load).
Why this file exists separately from the source-of-truth
~/work/lux/state/is the empirical archive (immutable artifacts + documentation of historical chain state). It is the ground truth for what the historical Zoo mainnet was.~/work/zoo/universe/configs/genesis/is the deployment artifact consumed by chain-bringup tools. It must mirror the state-repo canonical to spawn a chain whose block 0 matches the RLP archive.
This split is one half of the brand-isolation pattern landed in #109
(brand L1 genesis moved out of luxfi/genesis into per-brand universe
repos). The state-repo continues to live at lux/state as the empirical
oracle for what each historical chain WAS. The per-brand universe repo
carries the deployment artifact that drives bringup of what each chain
IS GOING TO BE.
Regression history (read this before editing)
The cc7d3d7 commit in luxfi/genesis was labeled "revert genesis.json to
RLP-matching pristine form" but landed a SUBSET of the empirical form —
missing the explicit block-0 header fields (parentHash, mixHash,
nonce, number, gasUsed, extraData, coinbase) and the
precompile-defer config fields (quasarTimestamp, fortunaTimestamp,
graniteTimestamp all set to 253399622400 = far-future-deferred).
luxd's EVM applies defaults for missing fields when building the block 0
header, and the cc7d3d7 defaults produced hash
0x88dd3a539b0329d13be6dafd2c320e3753e00639f1156d8d66f4f8d044acff2c
INSTEAD OF the empirical 0x7c548af4…. Today's bootstrap-chain
(2026-06-04) used the cc7d3d7-form genesis from
~/work/lux/genesis/configs/zoo-mainnet/genesis.json (now MOVED.txt
per #109), spawned oxDk6jU7W3JS3CyxCpzJ8mgytVwgHM8joWTPCoJZJo9yL9Cuf
with the WRONG block-0 hash → the RLP cannot be loaded.
The fix is in this commit: deployment-side genesis.json is now byte-
identical to the state-repo canonical (sha256
f8482568e7a10e882c6bd0248549ad75257edec79a83f659f27e1a34e71b59fa,
1449 bytes). Any future bootstrap-chain run will produce the canonical
0x7c548af4… block-0 hash and the RLP will load.
What to do if the bootstrap-chain CreateChainTx already fired
The previous CreateChainTx (with wrong genesis) is committed to P-state
and cannot be reverted in-place. To restore the chain with the canonical
genesis you have to fire ANOTHER CreateChainTx; this assigns a NEW
blockchainID (different from both the historical Vqx99tRk… and today's
oxDk6jU7…). The new chain accepts the RLP cleanly because its block 0
matches what the RLP archive expects.
Trade-off: each fresh CreateChainTx changes the chainAlias→blockchainID mapping that the gateway (KrakenD) and chain-aliases ConfigMap reference. Keep them in sync.
Layout note
There is also a ~/work/zoo/genesis/ directory (currently a stub with
LICENSE + LLM.md only). It exists for future Zoo-specific genesis pkg
work (e.g. zoo-fork-specific genesis tooling) — do NOT duplicate
deployment artifacts into it. The split stays clean:
lux/state/chains/zoo-mainnet/— empirical canonicalzoo/universe/configs/genesis/mainnet/— deployment mirrorzoo/genesis/— future tooling repo (currently empty)