Files
genesis/configs/getgenesis_test.go
T
Hanzo AI 4383ad4583 genesis: unify mnemonic env to LUX_MNEMONIC + kill vesting docstring rot
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.
2026-06-02 22:21:27 -07:00

166 lines
5.2 KiB
Go

package configs
import (
"encoding/json"
"testing"
)
func TestGetGenesisMainnet(t *testing.T) {
data, err := GetGenesis(1) // mainnet
if err != nil {
t.Fatalf("Error: %v", err)
}
var m map[string]interface{}
if err := json.Unmarshal(data, &m); err != nil {
t.Fatalf("Failed to parse: %v", err)
}
allocs := m["allocations"].([]interface{})
t.Logf("Total allocations: %d", len(allocs))
if len(allocs) == 0 {
t.Error("No allocations found in mainnet genesis")
}
// Verify each allocation has required fields
for i, a := range allocs {
alloc := a.(map[string]interface{})
if _, ok := alloc["utxoAddr"]; !ok {
t.Errorf("Allocation %d missing utxoAddr", i)
}
}
// Verify C-Chain genesis exists with treasury
cchainIface, ok := m["cChainGenesis"]
if !ok {
t.Fatal("cChainGenesis not found in genesis")
}
// cChainGenesis can be a string or object
var cchain map[string]interface{}
switch v := cchainIface.(type) {
case string:
if err := json.Unmarshal([]byte(v), &cchain); err != nil {
t.Fatalf("Failed to parse cChainGenesis string: %v", err)
}
case map[string]interface{}:
cchain = v
default:
t.Fatalf("Unexpected cChainGenesis type: %T", v)
}
// Check that alloc section exists in C-Chain
if alloc, ok := cchain["alloc"]; ok {
allocMap := alloc.(map[string]interface{})
t.Logf("C-Chain alloc entries: %d", len(allocMap))
// Check for treasury address (without 0x prefix, lowercase)
treasuryAddr := "9011e888251ab053b7bd1cdb598db4f9ded94714"
if _, found := allocMap[treasuryAddr]; found {
t.Logf("Treasury found in C-Chain alloc: 0x%s", treasuryAddr)
}
}
}
func TestGetGenesisLocalnet(t *testing.T) {
data, err := GetGenesis(1337) // localnet
if err != nil {
t.Fatalf("Error: %v", err)
}
var m map[string]interface{}
if err := json.Unmarshal(data, &m); err != nil {
t.Fatalf("Failed to parse: %v", err)
}
// Verify networkID
if nid := uint32(m["networkID"].(float64)); nid != 1337 {
t.Fatalf("Expected networkID 1337, got %d", nid)
}
// 1000 BIP44 wallet allocations + 3 validator-stake allocations.
// Each wallet alloc lands on P AND X (the node builder fans
// InitialAmount to both chains).
allocs := m["allocations"].([]interface{})
t.Logf("Total allocations: %d", len(allocs))
const wantWalletAllocs = 1000
const wantValidatorAllocs = 3
if len(allocs) != wantWalletAllocs+wantValidatorAllocs {
t.Errorf("Expected %d allocations (1000 wallet + 3 validator), got %d",
wantWalletAllocs+wantValidatorAllocs, len(allocs))
}
// First 1000 entries are wallet allocations at 10M LUX (= 10^13
// microLUX). The trailing 3 are validator stake allocs with a
// locked-stake UnlockSchedule and InitialAmount = 0; skip them in
// the per-entry check.
const wantMicroLUXPerWallet uint64 = 50_000_000 * 1_000_000
for i := 0; i < wantWalletAllocs && i < len(allocs); i++ {
alloc := allocs[i].(map[string]interface{})
if _, ok := alloc["utxoAddr"]; !ok {
t.Errorf("Allocation %d missing utxoAddr", i)
}
if _, ok := alloc["evmAddr"]; !ok {
t.Errorf("Allocation %d missing evmAddr", i)
}
amt := uint64(alloc["initialAmount"].(float64))
if amt != wantMicroLUXPerWallet {
t.Errorf("Allocation %d: expected %d, got %d", i, wantMicroLUXPerWallet, amt)
}
}
// Verify first allocation is canonical BIP44 m/44'/9000'/0'/0/0
// for LightMnemonic. ETH addr is the keccak256(pubkey) projection
// of the same secp256k1 spending key, computed from the BIP44 child
// at index 0 (not the Lux-internal hardened path).
first := allocs[0].(map[string]interface{})
const wantFirstETH = "0x5369615110ca435bdf798f31c20ba6163d7b0a54"
if addr := first["evmAddr"].(string); addr != wantFirstETH {
t.Errorf("First allocation evmAddr mismatch: got %s want %s", addr, wantFirstETH)
}
// initialStakedFunds tracks each initial staker's reward address.
// With validators=3 we expect 3 entries.
staked := m["initialStakedFunds"].([]interface{})
if len(staked) != wantValidatorAllocs {
t.Errorf("Expected %d staked funds, got %d", wantValidatorAllocs, len(staked))
}
// Verify C-Chain genesis
cchainIface, ok := m["cChainGenesis"]
if !ok {
t.Fatal("cChainGenesis not found")
}
var cchain map[string]interface{}
switch v := cchainIface.(type) {
case string:
if err := json.Unmarshal([]byte(v), &cchain); err != nil {
t.Fatalf("Failed to parse cChainGenesis: %v", err)
}
case map[string]interface{}:
cchain = v
default:
t.Fatalf("Unexpected cChainGenesis type: %T", v)
}
// Verify chainId
config := cchain["config"].(map[string]interface{})
if chainID := uint32(config["chainId"].(float64)); chainID != 31337 {
t.Fatalf("Expected C-Chain chainId 31337, got %d", chainID)
}
// C-Chain genesis ships with the canonical 5 LIGHT accounts (per
// configs/local/cchain.json) plus the warp precompile. The 1000
// BIP44 wallet allocations live on P/X chains only — they are NOT
// merged into the C-Chain alloc unless the operator passes
// -bip44-wallet-keys to the CLI, which the embedded local genesis
// does not.
allocMap := cchain["alloc"].(map[string]interface{})
t.Logf("C-Chain alloc entries: %d", len(allocMap))
if len(allocMap) < 1 {
t.Errorf("Expected at least 1 C-Chain alloc entry (warp precompile), got %d", len(allocMap))
}
}