One env, one way. The genesis mnemonic is now read from exactly one
env var: LUX_MNEMONIC. The prior two-env fallback chain
(MNEMONIC > LIGHT_MNEMONIC) is gone. Per-env isolation is by a
DIFFERENT mnemonic per env (loaded from KMS), not by env-var name.
pkg/genesis/keys.go:
- New MnemonicEnvVar = "LUX_MNEMONIC" constant.
- getMnemonicEnv() reads only LUX_MNEMONIC; no fallback.
- LightMnemonic constant kept (it's the well-known dev seed VALUE;
pass it as the value of LUX_MNEMONIC for local nets).
- IsProductionNetwork docstring updated to spell out the canonical 4:
mainnet(1) / testnet(2) / devnet(3) refuse public mnemonics;
local(1337)+ allows LightMnemonic.
- Docstring rot purged: the prior "50M free + 50M vesting at 1%/year
over 100y" claim was a lie — buildConfigFromKeyInfos never wired the
100-period schedule (the code admits the schedule "overflows zapdb
batch limit"). Replaced with the truth: 1000 × 50M LUX on X-Chain
AND P-Chain, immediate spend; C-Chain is treasury-only (2T LUX).
- Dead code deleted: StakingStartTime, UnlockInterval consts,
buildUnlockSchedule() function.
pkg/genesis/allocations.go:
- VestingConfig struct removed.
- DefaultVesting() removed.
- WithVesting() builder method removed.
- Vesting branches in PChain() / PChainMap() collapsed to immediate-only.
- MainnetAllocations no longer calls WithVesting(DefaultVesting()).
pkg/genesis/validator_keys.go:
- VestingPeriods const removed.
- GeneratePChainAllocationsWithVesting() removed.
pkg/genesis/networks.go (new):
- Canonical primary-network-id table: MainnetID / TestnetID / DevnetID
/ LocalID = 1 / 2 / 3 / 1337. Matching C-Chain (EVM) chain IDs:
96369 / 96368 / 96370 / 31337.
- IsCanonicalPrimaryNetwork helper.
cmd/checkkeys/main.go:
- Reads LUX_MNEMONIC via genesis.MnemonicEnvVar (no fallback).
cmd/derive100/main.go:
- Usage hint updated: mnemonic-env: LUX_MNEMONIC.
cmd/genesis/main.go:
- Flag help, env-var docs, examples, and error messages all reference
LUX_MNEMONIC.
pkg/genesis/light_mnemonic_guard_test.go:
- All t.Setenv calls use LUX_MNEMONIC.
- Test labels reference LightMnemonic (the constant) not the dead env name.
configs/getgenesis_test.go:
- Comment updated: LightMnemonic instead of LIGHT_MNEMONIC.
CHANGELOG.md + LLM.md:
- Documented the unification + the dead-code removal.
The validator stake-lock (3-entry 5y/10y/20y schedule attached to the
validator's UTXO inside buildConfigFromKeyInfos at keys.go:1077-1083)
is unchanged — that locked-stake bucket is the only UnlockSchedule in
the canonical genesis path and the ProtocolVM needs it.
External-caller sweep: grepped ~/work/lux, ~/work/hanzo, ~/work/zoo,
~/work/luxfi for genesis.VestingConfig / genesis.DefaultVesting /
genesis.GeneratePChainAllocationsWithVesting / genesis.StakingStartTime
/ genesis.UnlockInterval — zero hits. Safe to delete. (luxfi/keys at
~/work/lux/keys has its own MainnetAllocations copy — separate repo,
not affected.)
Verification:
- go build ./... → clean
- go vet ./... → clean
- go test ./... -count=1 → ok configs, ok pkg/genesis
- grep '[^_X]MNEMONIC\|LIGHT_MNEMONIC' under pkg/genesis cmd configs →
zero matches (only CHANGELOG history + Python script local vars).
- grep VestingConfig|buildUnlockSchedule|GeneratePChainAllocationsWithVesting
under source → zero matches.
HIP-0077 §"Identity" HD path alignment. Every public derivation entry
point now takes the network id and derives on the per-network
hardened branches mandated by the spec:
m/44'/9000'/nid'/0'/i' -> device_pq_key[i] (ML-DSA-65)
m/44'/9000'/nid'/1'/i' -> device_lux_key[i] (secp256k1)
All five levels (44', 9000', nid', branch, index) are now hardened on
the secp256k1 branch. Per-network hardening (nid' at the account level)
means the same mnemonic on different network ids derives fully
independent keypairs — no cross-network key reuse possible.
ML-DSA-65 keypair: expand the 32-byte BIP-32 child seed via
SHAKE-256("LUX/HIP-0077/mldsa65" || child_seed) into the 32-byte xi
that FIPS 204 §5.1 KeyGen consumes. Domain-separated so future schemes
sharing the same BIP-32 child seed cannot collide. ML-DSA backend:
cloudflare/circl mldsa65.
LoadKeysFromMnemonicEnvForNetwork blacklists 6 known public mnemonics
(BIP-39 abandon vector, Hardhat default, Trezor demo) and refuses to
proceed in production — closes F31. The CI/dev path still works with
LUX_LIGHT_MNEMONIC=1 explicitly set.
cmd/checkkeys + cmd/genesis: thread the network id through every
call site. cmd/derive100: helper that derives the first 100 PQ+secp
keypairs per network id for the genesis allocation.
Tests: pkg/genesis PASS (HD branches + LIGHT_MNEMONIC guard).
CHANGELOG.md added.
Breaking signature change for LoadKeysFromMnemonic /
LoadKeysFromMnemonicEnv / BuildWalletAllocations / BuildWalletKeyHex
(every entry point now takes nid uint32). Patch-bump.