2026-05-10 17:19:38 -07:00
|
|
|
# genesis CHANGELOG
|
|
|
|
|
|
|
|
|
|
## Unreleased
|
|
|
|
|
|
2026-06-02 22:21:27 -07:00
|
|
|
### Mnemonic env unification — one var, one way
|
|
|
|
|
|
|
|
|
|
The genesis mnemonic is now read from exactly one env var: `LUX_MNEMONIC`.
|
|
|
|
|
The previous two-env fallback chain (`MNEMONIC` then `LIGHT_MNEMONIC`)
|
|
|
|
|
is gone — there is one and only one canonical name.
|
|
|
|
|
|
|
|
|
|
- `pkg/genesis.MnemonicEnvVar` constant exposes the name (`"LUX_MNEMONIC"`).
|
|
|
|
|
- `getMnemonicEnv()` reads only `LUX_MNEMONIC`. No fallback.
|
|
|
|
|
- All CLI help, error messages and tests now reference `LUX_MNEMONIC`.
|
|
|
|
|
- `LightMnemonic` constant still holds the well-known dev seed value:
|
|
|
|
|
pass it as the value of `LUX_MNEMONIC` to bootstrap a local network.
|
|
|
|
|
|
|
|
|
|
Per-env isolation comes from a DIFFERENT mnemonic per env (loaded from
|
|
|
|
|
KMS for production), not from the env-var name.
|
|
|
|
|
|
|
|
|
|
### Dead-code removal — vesting rot
|
|
|
|
|
|
|
|
|
|
The "50M free + 50M vesting at 1%/year over 100y from Jan 1 2020"
|
|
|
|
|
docstring was a lie: the canonical builder `buildConfigFromKeyInfos`
|
|
|
|
|
never wired the 100-period schedule (the prior code admits the schedule
|
|
|
|
|
"overflows zapdb batch limit"). Drop the unreachable plumbing:
|
|
|
|
|
|
|
|
|
|
- `StakingStartTime`, `UnlockInterval`, `VestingPeriods` constants
|
|
|
|
|
- `buildUnlockSchedule()` function (pkg/genesis/keys.go)
|
|
|
|
|
- `VestingConfig` struct + `DefaultVesting()` + `WithVesting()`
|
|
|
|
|
- vesting branches in `ChainAllocations.PChain()` / `PChainMap()`
|
|
|
|
|
- `GeneratePChainAllocationsWithVesting()` function
|
|
|
|
|
- `MainnetAllocations` no longer calls `WithVesting(DefaultVesting())`
|
|
|
|
|
|
|
|
|
|
The validator stake-lock (3-entry 5y/10y/20y schedule attached to the
|
|
|
|
|
validator's UTXO inside `buildConfigFromKeyInfos`) is unchanged — that
|
|
|
|
|
locked-stake bucket is the only `UnlockSchedule` in the canonical
|
|
|
|
|
genesis path and the ProtocolVM needs it.
|
|
|
|
|
|
|
|
|
|
`pkg/genesis/networks.go` adds the canonical primary-network-id table
|
|
|
|
|
(`MainnetID` 1, `TestnetID` 2, `DevnetID` 3, `LocalID` 1337) as the
|
|
|
|
|
single source of truth for "what envs exist".
|
|
|
|
|
|
2026-05-10 17:19:38 -07:00
|
|
|
### Breaking — HIP-0077 §"Identity" HD path alignment
|
|
|
|
|
|
|
|
|
|
`pkg/genesis/keys.go` now derives keys on the per-network hardened
|
|
|
|
|
branches mandated by HIP-0077:
|
|
|
|
|
|
|
|
|
|
| derived | new path | curve / scheme | old path (removed) |
|
|
|
|
|
|----------------------|-----------------------------------|----------------|-------------------------------------|
|
|
|
|
|
| `device_pq_key[i]` | `m/44'/9000'/nid'/0'/i'` | ML-DSA-65 | n/a (was loaded from disk only) |
|
|
|
|
|
| `device_lux_key[i]` | `m/44'/9000'/nid'/1'/i'` | secp256k1 | `m/44'/9000'/0'/0/i` |
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
The ML-DSA-65 keypair is generated by expanding the 32-byte BIP-32
|
|
|
|
|
child seed via `SHAKE-256("LUX/HIP-0077/mldsa65" || child_seed)` into
|
|
|
|
|
the 32-byte ξ that FIPS 204 §5.1 KeyGen consumes. The expansion is
|
|
|
|
|
domain-separated so future schemes sharing the same BIP-32 child seed
|
|
|
|
|
cannot collide.
|
|
|
|
|
|
|
|
|
|
#### Function signature changes
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
// before
|
|
|
|
|
LoadKeysFromMnemonic(mnemonic string, numAccounts int) ([]KeyInfo, error)
|
|
|
|
|
LoadKeysFromMnemonicEnv(numAccounts int) ([]KeyInfo, error)
|
|
|
|
|
BuildWalletAllocations(numKeys int, amountPerKey uint64) ([]Allocation, error)
|
|
|
|
|
BuildWalletKeyHex(index int) (string, error)
|
|
|
|
|
|
|
|
|
|
// after — every public derivation entry point now takes the network id
|
|
|
|
|
LoadKeysFromMnemonic(mnemonic string, nid uint32, numAccounts int) ([]KeyInfo, error)
|
|
|
|
|
LoadKeysFromMnemonicEnv(nid uint32, numAccounts int) ([]KeyInfo, error)
|
|
|
|
|
BuildWalletAllocations(nid uint32, numKeys int, amountPerKey uint64) ([]Allocation, error)
|
|
|
|
|
BuildWalletKeyHex(nid uint32, index int) (string, error)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`LoadKeysFromMnemonicEnvForNetwork(networkID, numAccounts)` keeps its
|
|
|
|
|
signature; it now passes `networkID` through as the `nid` for the
|
|
|
|
|
underlying derivation, preserving the F30 public-mnemonic guard.
|
|
|
|
|
|
|
|
|
|
`KeyInfo.MLDSAPublicKey` is now populated from the deterministic
|
|
|
|
|
mnemonic derivation. Old loads from `nodeDir/mldsa/public.key` still
|
|
|
|
|
work for keys-on-disk paths (`LoadKeysFromDir`).
|
|
|
|
|
|
|
|
|
|
#### Migration
|
|
|
|
|
|
|
|
|
|
Any address derived under the old `m/44'/9000'/0'/0/i` path will NOT
|
|
|
|
|
reproduce under the new layout. Specifically:
|
|
|
|
|
|
|
|
|
|
- Treasury and any other addresses pinned via the mnemonic (validator
|
|
|
|
|
ETH/P-chain addresses, fee reserve keys, wallet allocations) must be
|
|
|
|
|
re-derived after upgrading.
|
|
|
|
|
- If you need to keep the old addresses spendable, export their private
|
|
|
|
|
keys (`BuildWalletKeyHex` on the old code) and load them via
|
2026-06-02 22:21:27 -07:00
|
|
|
`KEYS_DIR` instead of `LUX_MNEMONIC` — `LoadKeysFromDir` is unchanged.
|
2026-05-10 17:19:38 -07:00
|
|
|
|
|
|
|
|
Per CLAUDE.md `no backwards compatibility, only forwards perfection`:
|
|
|
|
|
this is the correct break; clients deriving from the HIP-0077 spec
|
|
|
|
|
agree with `luxd` going forward, and nobody is left guessing which
|
|
|
|
|
historical path to use.
|
|
|
|
|
|
|
|
|
|
#### Library note
|
|
|
|
|
|
|
|
|
|
ML-DSA-65 keygen currently uses `github.com/cloudflare/circl/sign/mldsa/mldsa65`
|
|
|
|
|
because `github.com/luxfi/crypto/pq/mldsa` (v1.17.44) does not yet
|
|
|
|
|
expose a public `NewKeyFromSeed` entry point. Migration is tracked by
|
|
|
|
|
a `TODO(canonical)` at the import site in `pkg/genesis/keys.go`; the
|
|
|
|
|
canonical replacement MUST adopt the same
|
|
|
|
|
`SHAKE-256("LUX/HIP-0077/mldsa65" || child_seed)` expansion to preserve
|
|
|
|
|
determinism.
|