mirror of
https://github.com/luxfi/genesis.git
synced 2026-07-27 05:54:08 +00:00
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.
This commit is contained in:
+39
-1
@@ -2,6 +2,44 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
### 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".
|
||||
|
||||
### Breaking — HIP-0077 §"Identity" HD path alignment
|
||||
|
||||
`pkg/genesis/keys.go` now derives keys on the per-network hardened
|
||||
@@ -57,7 +95,7 @@ reproduce under the new layout. Specifically:
|
||||
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
|
||||
`KEYS_DIR` instead of `MNEMONIC` — `LoadKeysFromDir` is unchanged.
|
||||
`KEYS_DIR` instead of `LUX_MNEMONIC` — `LoadKeysFromDir` is unchanged.
|
||||
|
||||
Per CLAUDE.md `no backwards compatibility, only forwards perfection`:
|
||||
this is the correct break; clients deriving from the HIP-0077 spec
|
||||
|
||||
@@ -17,7 +17,7 @@ silent classification change.
|
||||
|-----|-----|--------|
|
||||
| `85a8fc8` | v1.9.6 | Pin ChainSecurityProfile into Config + Resolve at load (closes F102 genesis layer) |
|
||||
| `1d73c8d` | v1.9.6 | Swap mldsa65 keygen from cloudflare/circl to luxfi/crypto/pq/mldsa |
|
||||
| `e698307` | v1.9.5 | HD branch hardening + LIGHT_MNEMONIC production refusal (F12/F30/F31/F35) |
|
||||
| `e698307` | v1.9.5 | HD branch hardening + LightMnemonic production refusal (F12/F30/F31/F35) |
|
||||
| `fe4781c` | v1.9.5 | `LUX_DISABLE_CCHAIN=1` omits C-Chain from primary genesis |
|
||||
| `2b16f45` | v1.9.5 | Operator-overridable C-Chain genesis via `LUX_CCHAIN_GENESIS_FILE` |
|
||||
|
||||
@@ -350,7 +350,7 @@ Generate genesis configurations:
|
||||
|
||||
```bash
|
||||
# Generate from mnemonic
|
||||
MNEMONIC="test test..." ./genesis -network-id 96369 -validators 5 -output genesis.json
|
||||
LUX_MNEMONIC="test test..." ./genesis -network-id 96369 -validators 5 -output genesis.json
|
||||
|
||||
# From existing keys
|
||||
./genesis -network mainnet -keys-dir ~/.lux/keys -output genesis.json
|
||||
|
||||
@@ -8,15 +8,9 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
mnemonic := ""
|
||||
for _, env := range []string{"MNEMONIC", "LIGHT_MNEMONIC"} {
|
||||
if v := os.Getenv(env); v != "" {
|
||||
mnemonic = v
|
||||
break
|
||||
}
|
||||
}
|
||||
mnemonic := os.Getenv(genesis.MnemonicEnvVar)
|
||||
if mnemonic == "" {
|
||||
fmt.Println("mnemonic not set (set MNEMONIC or LIGHT_MNEMONIC)")
|
||||
fmt.Printf("mnemonic not set (set %s)\n", genesis.MnemonicEnvVar)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ func main() {
|
||||
fmt.Fprintln(os.Stderr, "usage: derive100 <hrp> <rpc> <mnemonic-env>")
|
||||
fmt.Fprintln(os.Stderr, " hrp: test|lux|dev|local")
|
||||
fmt.Fprintln(os.Stderr, " rpc: http://24.199.71.151:9650")
|
||||
fmt.Fprintln(os.Stderr, " mnemonic-env: MNEMONIC|LIGHT_MNEMONIC")
|
||||
fmt.Fprintln(os.Stderr, " mnemonic-env: LUX_MNEMONIC")
|
||||
os.Exit(1)
|
||||
}
|
||||
hrp := os.Args[1]
|
||||
|
||||
+6
-6
@@ -42,7 +42,7 @@ func main() {
|
||||
output = flag.String("output", "", "Output file path (default: stdout)")
|
||||
allocation = flag.Uint64("allocation", genesis.DefaultAllocationPerValidator, "Allocation per validator in nLUX")
|
||||
validators = flag.Int("validators", 3, "Number of validators for mnemonic-based genesis")
|
||||
walletKeys = flag.Int("wallet-keys", 0, "Number of mnemonic-derived wallet keys to fund (Lux-internal hardened path m/44'/9000'/nid'/1'/i'; requires MNEMONIC env)")
|
||||
walletKeys = flag.Int("wallet-keys", 0, "Number of mnemonic-derived wallet keys to fund (Lux-internal hardened path m/44'/9000'/nid'/1'/i'; requires LUX_MNEMONIC env)")
|
||||
walletAmount = flag.Uint64("wallet-amount", 10000, "Allocation per wallet key in LUX (default: 10000)")
|
||||
bip44WalletKeys = flag.Int("bip44-wallet-keys", 0, "Number of canonical BIP44 wallet keys to fund (m/44'/9000'/0'/0/i — SLIP-0044 coin type 9000)")
|
||||
cchainPath = flag.String("cchain", "", "Path to existing C-Chain genesis (preserves original)")
|
||||
@@ -115,10 +115,10 @@ Flags:
|
||||
|
||||
Environment Variables:
|
||||
KEYS_DIR Directory containing node keys
|
||||
MNEMONIC BIP39 mnemonic for key derivation
|
||||
LUX_MNEMONIC BIP39 mnemonic for key derivation
|
||||
PRIVATE_KEY Single private key (hex)
|
||||
GENESIS_DIR Directory containing genesis component files
|
||||
NETWORK_ID Network ID
|
||||
GENESIS_DIR Directory containing genesis component files
|
||||
NETWORK_ID Network ID
|
||||
|
||||
Examples:
|
||||
# Generate local genesis from keys in ~/.lux/keys
|
||||
@@ -128,7 +128,7 @@ Examples:
|
||||
genesis -network mainnet -cchain mainnet/cchain.json -output primary.json
|
||||
|
||||
# Generate from mnemonic with 3 validators
|
||||
MNEMONIC="your mnemonic here" genesis -validators 3 -output primary.json
|
||||
LUX_MNEMONIC="your mnemonic here" genesis -validators 3 -output primary.json
|
||||
|
||||
# Load from existing genesis directory
|
||||
genesis -genesis-dir ./mainnet -output primary.json`)
|
||||
@@ -189,7 +189,7 @@ func buildConfig(networkID uint32, keysDir, genesisDir string, allocation uint64
|
||||
|
||||
config, err = genesis.BuildConfigFromKeys(networkID, keysDir, allocation)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build config: %w (try setting KEYS_DIR, MNEMONIC, or PRIVATE_KEY)", err)
|
||||
return nil, fmt.Errorf("failed to build config: %w (try setting KEYS_DIR, LUX_MNEMONIC, or PRIVATE_KEY)", err)
|
||||
}
|
||||
|
||||
config = maybeAddWalletAllocations(config, walletKeys, walletAmount)
|
||||
|
||||
@@ -111,7 +111,7 @@ func TestGetGenesisLocalnet(t *testing.T) {
|
||||
}
|
||||
|
||||
// Verify first allocation is canonical BIP44 m/44'/9000'/0'/0/0
|
||||
// for LIGHT_MNEMONIC. ETH addr is the keccak256(pubkey) projection
|
||||
// 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{})
|
||||
|
||||
+18
-76
@@ -9,36 +9,19 @@ import (
|
||||
)
|
||||
|
||||
// ChainAllocations holds genesis allocations for all chains.
|
||||
// Use NewAllocations() or AllocationBuilder to create.
|
||||
// Use NewAllocations() to create.
|
||||
//
|
||||
// All allocations are immediate (no vesting). The long-locked validator
|
||||
// stake bucket is attached separately by buildConfigFromKeyInfos and is
|
||||
// the only place an UnlockSchedule appears in the canonical genesis path.
|
||||
type ChainAllocations struct {
|
||||
keys []ValidatorKeyInfo
|
||||
hrp string
|
||||
amount uint64
|
||||
|
||||
// Vesting configuration (optional)
|
||||
vesting *VestingConfig
|
||||
}
|
||||
|
||||
// VestingConfig defines the vesting schedule parameters.
|
||||
type VestingConfig struct {
|
||||
StartTime uint64 // Unix timestamp when vesting starts
|
||||
Interval uint64 // Seconds between each unlock
|
||||
Periods int // Number of unlock periods
|
||||
}
|
||||
|
||||
// DefaultVesting returns the standard vesting config:
|
||||
// - Start: Jan 1, 2020
|
||||
// - 1% unlocks per year for 100 years
|
||||
func DefaultVesting() *VestingConfig {
|
||||
return &VestingConfig{
|
||||
StartTime: StakingStartTime, // Jan 1, 2020
|
||||
Interval: UnlockInterval, // 1 year
|
||||
Periods: 100, // 100 periods = 1% per year
|
||||
}
|
||||
}
|
||||
|
||||
// NewAllocations creates a ChainAllocations for the given validator keys.
|
||||
// Default amount is 100M LUX per key, no vesting.
|
||||
// Default amount is DefaultValidatorAllocation per key, immediate spend.
|
||||
func NewAllocations(keys []ValidatorKeyInfo, hrp string) *ChainAllocations {
|
||||
return &ChainAllocations{
|
||||
keys: keys,
|
||||
@@ -53,14 +36,8 @@ func (a *ChainAllocations) WithAmount(amount uint64) *ChainAllocations {
|
||||
return a
|
||||
}
|
||||
|
||||
// WithVesting enables vesting with the specified config.
|
||||
// Use DefaultVesting() for standard 100-year vesting from Jan 1 2020.
|
||||
func (a *ChainAllocations) WithVesting(config *VestingConfig) *ChainAllocations {
|
||||
a.vesting = config
|
||||
return a
|
||||
}
|
||||
|
||||
// PChain returns P-chain allocations in the standard AllocationJSON format.
|
||||
// All entries are immediately spendable (locktime=0).
|
||||
func (a *ChainAllocations) PChain() ([]AllocationJSON, error) {
|
||||
allocations := make([]AllocationJSON, len(a.keys))
|
||||
|
||||
@@ -70,25 +47,11 @@ func (a *ChainAllocations) PChain() ([]AllocationJSON, error) {
|
||||
return nil, fmt.Errorf("failed to format P-chain address for key %d: %w", i, err)
|
||||
}
|
||||
|
||||
var unlockSchedule []LockedAmount
|
||||
var initialAmount uint64
|
||||
|
||||
if a.vesting != nil {
|
||||
// Vested allocation
|
||||
unlockSchedule = buildUnlockSchedule(a.amount, a.vesting.StartTime, a.vesting.Interval, a.vesting.Periods)
|
||||
initialAmount = a.amount // X-chain gets initial amount with vesting
|
||||
} else {
|
||||
// Immediate allocation (no vesting)
|
||||
// X-chain gets full initial amount for chain creation operations
|
||||
unlockSchedule = []LockedAmount{{Amount: a.amount, Locktime: 0}}
|
||||
initialAmount = a.amount
|
||||
}
|
||||
|
||||
allocations[i] = AllocationJSON{
|
||||
EVMAddr: key.EVMAddr,
|
||||
UTXOAddr: utxoAddr,
|
||||
InitialAmount: initialAmount,
|
||||
UnlockSchedule: unlockSchedule,
|
||||
InitialAmount: a.amount,
|
||||
UnlockSchedule: []LockedAmount{{Amount: a.amount, Locktime: 0}},
|
||||
}
|
||||
}
|
||||
return allocations, nil
|
||||
@@ -130,6 +93,7 @@ func (a *ChainAllocations) CChainMap() map[string]map[string]string {
|
||||
}
|
||||
|
||||
// PChainMap returns P-chain allocations as interface maps (for netrunner compatibility).
|
||||
// All entries are immediately spendable (locktime=0).
|
||||
func (a *ChainAllocations) PChainMap() ([]map[string]interface{}, error) {
|
||||
allocations := make([]map[string]interface{}, len(a.keys))
|
||||
|
||||
@@ -139,34 +103,13 @@ func (a *ChainAllocations) PChainMap() ([]map[string]interface{}, error) {
|
||||
return nil, fmt.Errorf("failed to format P-chain address for key %d: %w", i, err)
|
||||
}
|
||||
|
||||
var unlockSchedule []map[string]interface{}
|
||||
var initialAmount uint64
|
||||
|
||||
if a.vesting != nil {
|
||||
// Build vesting schedule as maps
|
||||
locked := buildUnlockSchedule(a.amount, a.vesting.StartTime, a.vesting.Interval, a.vesting.Periods)
|
||||
unlockSchedule = make([]map[string]interface{}, len(locked))
|
||||
for j, l := range locked {
|
||||
unlockSchedule[j] = map[string]interface{}{
|
||||
"amount": l.Amount,
|
||||
"locktime": l.Locktime,
|
||||
}
|
||||
}
|
||||
initialAmount = a.amount
|
||||
} else {
|
||||
// Immediate allocation (no vesting)
|
||||
// X-chain gets full initial amount for chain creation operations
|
||||
unlockSchedule = []map[string]interface{}{
|
||||
{"amount": a.amount, "locktime": uint64(0)},
|
||||
}
|
||||
initialAmount = a.amount
|
||||
}
|
||||
|
||||
allocations[i] = map[string]interface{}{
|
||||
"evmAddr": key.EVMAddr,
|
||||
"utxoAddr": utxoAddr,
|
||||
"initialAmount": initialAmount,
|
||||
"unlockSchedule": unlockSchedule,
|
||||
"evmAddr": key.EVMAddr,
|
||||
"utxoAddr": utxoAddr,
|
||||
"initialAmount": a.amount,
|
||||
"unlockSchedule": []map[string]interface{}{
|
||||
{"amount": a.amount, "locktime": uint64(0)},
|
||||
},
|
||||
}
|
||||
}
|
||||
return allocations, nil
|
||||
@@ -206,15 +149,14 @@ func QuickAllocations(keys []ValidatorKeyInfo, hrp string, amount uint64) (*AllC
|
||||
return NewAllocations(keys, hrp).WithAmount(amount).All()
|
||||
}
|
||||
|
||||
// MainnetAllocations creates allocations with standard mainnet vesting (1B LUX, 100 years).
|
||||
// MainnetAllocations creates immediate-spend mainnet allocations (1B LUX per key).
|
||||
func MainnetAllocations(keys []ValidatorKeyInfo, hrp string) (*AllChainAllocations, error) {
|
||||
return NewAllocations(keys, hrp).
|
||||
WithAmount(OneBillionLUX).
|
||||
WithVesting(DefaultVesting()).
|
||||
All()
|
||||
}
|
||||
|
||||
// TestnetAllocations creates allocations suitable for testnet (100M LUX, no vesting).
|
||||
// TestnetAllocations creates immediate-spend testnet allocations (100M LUX per key).
|
||||
func TestnetAllocations(keys []ValidatorKeyInfo, hrp string) (*AllChainAllocations, error) {
|
||||
return NewAllocations(keys, hrp).
|
||||
WithAmount(OneHundredMillionLUX).
|
||||
|
||||
+67
-92
@@ -34,53 +34,46 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultAllocationPerAccount is 50M LUX per account per chain (P and X).
|
||||
// 1000 accounts × 50M × 2 chains = 100B LUX of UTXOs in genesis (well
|
||||
// under the 2T LUX SupplyCap on every network).
|
||||
// X-Chain: 50M free (immediately spendable from genesis).
|
||||
// P-Chain: 50M free at genesis (no vesting); the long-tail unlock
|
||||
// schedule below adds a separate 50M per account that vests 1%/year
|
||||
// over 100 years from Jan 1 2020 — i.e. each address sees 50M
|
||||
// spendable on X, 50M spendable on P + 50M vesting on P.
|
||||
// DefaultAllocationPerAccount is the canonical fresh-genesis amount:
|
||||
// 50M LUX per derived account, spendable immediately on both X-Chain
|
||||
// and P-Chain (same address — bech32(ripemd160(sha256(secp256k1_pubkey)))).
|
||||
//
|
||||
// 1000 accounts × 50M × 2 chains = 100B LUX of UTXOs in genesis.
|
||||
// Per-chain SupplyCap is 2T LUX, so each chain individually clears.
|
||||
//
|
||||
// Address scheme — Bitcoin-UTXO-style (more quantum-resistant):
|
||||
// P-Chain / X-Chain addresses are bech32(ripemd160(sha256(pubkey))).
|
||||
// The public key is *hidden behind two hash layers* until the
|
||||
// address is first spent. Until first spend, even a future
|
||||
// quantum adversary cannot recover the private key — Shor's
|
||||
// algorithm works against the secp256k1 pubkey, but not against
|
||||
// sha256+ripemd160. Contrast with C-Chain (Ethereum) addresses,
|
||||
// which are keccakBytes(pubkey)[12:] and effectively expose the
|
||||
// pubkey on every signature (recovery in ECDSA). Long-term holds
|
||||
// should sit on P/X, not C, for this reason.
|
||||
// The public key is hidden behind two hash layers until first spend.
|
||||
// A future quantum adversary cannot recover the private key from the
|
||||
// address alone — Shor's works on the pubkey, not on sha256+ripemd160.
|
||||
// C-Chain (Ethereum) addresses expose the pubkey via ECDSA recovery
|
||||
// on every signed tx; long-term holds belong on P/X, not C.
|
||||
DefaultAllocationPerAccount = 50_000_000 * Lux
|
||||
|
||||
// DefaultAllocationPerValidator is kept for backward compatibility
|
||||
DefaultAllocationPerValidator = DefaultAllocationPerAccount
|
||||
|
||||
// DefaultNumAccounts is the default number of mnemonic-derived accounts
|
||||
// Funds 1000 wallet keys at canonical BIP44 m/44'/9000'/0'/0/i so that
|
||||
// any wallet that derives at this path against the SAME mnemonic sees
|
||||
// a fundable address on both P and X.
|
||||
// DefaultNumAccounts is the default number of mnemonic-derived accounts.
|
||||
// Funds 1000 BIP44 wallet keys at m/44'/9000'/0'/0/i so that any
|
||||
// canonical-BIP44 wallet (Core, MetaMask, AvalancheJS, `lux key derive`)
|
||||
// sees a fundable address on both P and X under the same mnemonic.
|
||||
// Per-network isolation comes from a DIFFERENT mnemonic per env
|
||||
// (loaded from KMS), not from path divergence.
|
||||
DefaultNumAccounts = 1000
|
||||
|
||||
// StakingStartTime is Jan 1, 2020 00:00:00 UTC
|
||||
StakingStartTime = 1577836800
|
||||
|
||||
// UnlockInterval is 1 year in seconds
|
||||
UnlockInterval = 365 * 24 * 60 * 60
|
||||
|
||||
// TreasuryAddress is the C-Chain treasury with 2T LUX
|
||||
// TreasuryAddress is the C-Chain treasury (no per-account C-Chain alloc).
|
||||
TreasuryAddress = "0x9011E888251AB053B7bD1cdB598Db4f9DEd94714"
|
||||
|
||||
// TreasuryAmount is 2 trillion LUX in microLUX (2T * 10^6)
|
||||
// TreasuryAmount is 2 trillion LUX in microLUX (2T * 10^6).
|
||||
TreasuryAmount = 2_000_000_000_000 * Lux
|
||||
|
||||
// PChainFeeReserve is 10,000 LUX per validator for P-Chain fees
|
||||
// PChainFeeReserve is 10,000 LUX per validator earmarked for P-Chain fees.
|
||||
PChainFeeReserve = 10_000 * Lux
|
||||
|
||||
// LightMnemonic is the well-known dev mnemonic for local networks (network-id >= 1337).
|
||||
// NEVER use on public networks (mainnet, testnet, devnet).
|
||||
// LightMnemonic is the well-known public dev seed. Pass this as the
|
||||
// value of LUX_MNEMONIC to bootstrap a local network (network ID >=
|
||||
// 1337). RefuseLightMnemonicOnProduction enforces it cannot be used
|
||||
// on mainnet/testnet/devnet (network IDs 1/2/3).
|
||||
LightMnemonic = "light light light light light light light light light light light energy"
|
||||
)
|
||||
|
||||
@@ -312,21 +305,6 @@ func deriveFeeKey(keysDir string, validatorKey KeyInfo, index int) (*KeyInfo, er
|
||||
}, nil
|
||||
}
|
||||
|
||||
// buildUnlockSchedule creates a vesting schedule
|
||||
func buildUnlockSchedule(totalAmount uint64, startTime uint64, interval uint64, periods int) []LockedAmount {
|
||||
amountPerPeriod := totalAmount / uint64(periods)
|
||||
schedule := make([]LockedAmount, 0, periods)
|
||||
|
||||
for i := 0; i < periods; i++ {
|
||||
schedule = append(schedule, LockedAmount{
|
||||
Amount: amountPerPeriod,
|
||||
Locktime: startTime + uint64(i)*interval,
|
||||
})
|
||||
}
|
||||
|
||||
return schedule
|
||||
}
|
||||
|
||||
// buildCChainGenesisTreasury creates C-chain genesis JSON with only the treasury allocation.
|
||||
// Treasury: 0x9011E888251AB053B7bD1cdB598Db4f9DEd94714 gets 2T LUX.
|
||||
// No mnemonic-derived account allocations on C-Chain.
|
||||
@@ -539,14 +517,14 @@ func mldsaKeygenFromChildSeed(childSeed []byte) ([]byte, error) {
|
||||
return pk.Bytes(), nil
|
||||
}
|
||||
|
||||
// LoadKeysFromMnemonicEnv loads keys from mnemonic env vars (MNEMONIC > LIGHT_MNEMONIC).
|
||||
// LoadKeysFromMnemonicEnv loads keys from the LUX_MNEMONIC env var.
|
||||
// Callers that already know the network id MUST use
|
||||
// LoadKeysFromMnemonicEnvForNetwork instead — it adds the production-safe
|
||||
// public-mnemonic guard around this entry point.
|
||||
func LoadKeysFromMnemonicEnv(numAccounts int) ([]KeyInfo, error) {
|
||||
mnemonic := getMnemonicEnv()
|
||||
if mnemonic == "" {
|
||||
return nil, fmt.Errorf("mnemonic not set (set MNEMONIC or LIGHT_MNEMONIC)")
|
||||
return nil, fmt.Errorf("mnemonic not set (set %s)", MnemonicEnvVar)
|
||||
}
|
||||
return LoadKeysFromMnemonic(mnemonic, numAccounts)
|
||||
}
|
||||
@@ -630,28 +608,24 @@ func genesisMessage(networkID uint32) string {
|
||||
}
|
||||
}
|
||||
|
||||
// getMnemonicEnv returns the mnemonic from environment variables.
|
||||
// Priority: MNEMONIC > LIGHT_MNEMONIC
|
||||
//
|
||||
// LIGHT_MNEMONIC is the publicly-known dev seed. Anyone in the world can
|
||||
// derive its first 200 child keys; the auto-fund pre-allocation in
|
||||
// HIP-0077 §"Auto-funding the first 200 devices" is *only* meant for
|
||||
// network IDs >= 1337 (dev / primary local mesh). Production networks
|
||||
// MUST set MNEMONIC.
|
||||
//
|
||||
// Use IsLightMnemonic and RefuseLightMnemonicOnProduction to enforce that
|
||||
// rule wherever a key is loaded for a known network ID.
|
||||
// MnemonicEnvVar is the single canonical env var name for the genesis
|
||||
// mnemonic. One env, one way:
|
||||
// - mainnet/testnet/devnet (IDs 1/2/3): a private hardware-RNG mnemonic
|
||||
// loaded from KMS; RefuseLightMnemonicOnProduction rejects all known
|
||||
// public mnemonics so the production guard is enforced at derivation.
|
||||
// - local (ID 1337): typically set to LightMnemonic for fast dev boot;
|
||||
// the same public-mnemonic blacklist still applies, but local IDs
|
||||
// are exempt by IsProductionNetwork.
|
||||
const MnemonicEnvVar = "LUX_MNEMONIC"
|
||||
|
||||
// getMnemonicEnv returns the mnemonic from the canonical LUX_MNEMONIC env
|
||||
// var. No fallback chain — one and only one env name.
|
||||
func getMnemonicEnv() string {
|
||||
for _, env := range []string{"MNEMONIC", "LIGHT_MNEMONIC"} {
|
||||
if v := os.Getenv(env); v != "" {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return ""
|
||||
return os.Getenv(MnemonicEnvVar)
|
||||
}
|
||||
|
||||
// IsLightMnemonic reports whether the given mnemonic is exactly the
|
||||
// well-known LIGHT_MNEMONIC dev seed. Compared in constant time so a
|
||||
// well-known LightMnemonic dev seed value. Compared in constant time so a
|
||||
// timing attacker can't probe the running config from outside.
|
||||
func IsLightMnemonic(mnemonic string) bool {
|
||||
return subtleConstantTimeEqual([]byte(mnemonic), []byte(LightMnemonic))
|
||||
@@ -660,7 +634,7 @@ func IsLightMnemonic(mnemonic string) bool {
|
||||
// knownPublicMnemonics is the curated set of seeds that anyone in the
|
||||
// world can derive from. Production deployments MUST refuse all of them.
|
||||
//
|
||||
// LIGHT_MNEMONIC is one row; the rest are the most-frequently-cited public
|
||||
// LightMnemonic is one row; the rest are the most-frequently-cited public
|
||||
// mnemonics from BIP-39 test vectors, common dev tooling defaults, and
|
||||
// hardware-wallet demo seeds. Any of these on a production network →
|
||||
// every derived child key is publicly enumerable.
|
||||
@@ -687,7 +661,7 @@ var knownPublicMnemonics = []string{
|
||||
}
|
||||
|
||||
// IsKnownPublicMnemonic reports whether the given mnemonic appears in any
|
||||
// well-known public list (LIGHT_MNEMONIC, BIP-39 test vectors, Hardhat
|
||||
// well-known public list (LightMnemonic, BIP-39 test vectors, Hardhat
|
||||
// default, hardware-wallet demos, etc.). Compared in constant time per
|
||||
// entry so a timing attacker can't probe which entry matched.
|
||||
//
|
||||
@@ -710,15 +684,16 @@ func IsKnownPublicMnemonic(mnemonic string) bool {
|
||||
// IsProductionNetwork reports whether the given numeric network ID is on
|
||||
// the list of *production* Lux networks. Local / primary-local meshes
|
||||
// (network IDs >= 1337, including constants.LocalID = 1337) deliberately
|
||||
// allow LIGHT_MNEMONIC; mainnet, testnet and any other reserved low-ID
|
||||
// network refuse it.
|
||||
// allow public mnemonics like LightMnemonic; mainnet, testnet and any
|
||||
// other reserved low-ID network refuse them.
|
||||
//
|
||||
// Network ID convention (mirrors lux/constants):
|
||||
// - 1 mainnet (production)
|
||||
// - 2 testnet (production-grade staging — refuses LIGHT_MNEMONIC)
|
||||
// - 1337 LocalID (free-form local dev, allows LIGHT_MNEMONIC)
|
||||
// - >= 1337 any tenant local / dev mesh (allows LIGHT_MNEMONIC)
|
||||
// - 3..1336 reserved; treated as production by default
|
||||
// Canonical network ID map (mirrors lux/constants):
|
||||
// - 1 mainnet (production — refuses public mnemonics)
|
||||
// - 2 testnet (production-grade staging — refuses public mnemonics)
|
||||
// - 3 devnet (production-grade dev mesh — refuses public mnemonics)
|
||||
// - 1337 LocalID (free-form local dev — allows LightMnemonic)
|
||||
// - >= 1337 any tenant local / dev mesh (allows LightMnemonic)
|
||||
// - 4..1336 reserved; treated as production by default
|
||||
func IsProductionNetwork(networkID uint32) bool {
|
||||
switch networkID {
|
||||
case constants.MainnetID, constants.TestnetID:
|
||||
@@ -730,7 +705,7 @@ func IsProductionNetwork(networkID uint32) bool {
|
||||
}
|
||||
|
||||
// RefuseLightMnemonicOnProduction returns a non-nil error iff the running
|
||||
// process is configured with any publicly-known mnemonic (LIGHT_MNEMONIC,
|
||||
// process is configured with any publicly-known mnemonic (LightMnemonic,
|
||||
// BIP-39 test vectors, Hardhat / Trezor demos, …) AND the supplied
|
||||
// networkID is a production network. Runtime guard required by HIP-0077
|
||||
// §"Mnemonic exposure" / "Auto-funded blast radius".
|
||||
@@ -743,9 +718,9 @@ func IsProductionNetwork(networkID uint32) bool {
|
||||
// derivation point, never silently produces public-mnemonic-derived
|
||||
// signing keys.
|
||||
//
|
||||
// The guard widens beyond LIGHT_MNEMONIC to cover the broader
|
||||
// public-mnemonic blacklist (HIP-0077 red-review F31): BIP-39 abandon
|
||||
// vector, Hardhat default, Trezor demo, etc. See knownPublicMnemonics.
|
||||
// The guard covers the full public-mnemonic blacklist (HIP-0077 red-review
|
||||
// F31): LightMnemonic, BIP-39 abandon vector, Hardhat default, Trezor
|
||||
// demo, etc. See knownPublicMnemonics.
|
||||
func RefuseLightMnemonicOnProduction(networkID uint32) error {
|
||||
mnemonic := getMnemonicEnv()
|
||||
if mnemonic == "" {
|
||||
@@ -761,11 +736,11 @@ func RefuseLightMnemonicOnProduction(networkID uint32) error {
|
||||
}
|
||||
return fmt.Errorf(
|
||||
"refusing to derive keys: a publicly-known mnemonic is set on production "+
|
||||
"network %d (mainnet/testnet/<1337). Public mnemonics (LIGHT_MNEMONIC, "+
|
||||
"network %d (mainnet/testnet/<1337). Public mnemonics (LightMnemonic, "+
|
||||
"BIP-39 test vectors, Hardhat/Trezor demos) are deterministic — anyone "+
|
||||
"can derive every child key. Set MNEMONIC env var with "+
|
||||
"a private hardware-RNG mnemonic loaded from KMS, or run on a dev "+
|
||||
"network ID (>= 1337)", networkID,
|
||||
"can derive every child key. Set %s with a private hardware-RNG "+
|
||||
"mnemonic loaded from KMS, or run on a dev network ID (>= 1337)",
|
||||
networkID, MnemonicEnvVar,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -776,7 +751,7 @@ func RefuseLightMnemonicOnProduction(networkID uint32) error {
|
||||
//
|
||||
// Closes HIP-0077 red-review F30 (the prior LoadKeysFromMnemonicEnv was
|
||||
// guard-free and operators could silently derive on production from
|
||||
// LIGHT_MNEMONIC).
|
||||
// the public LightMnemonic).
|
||||
func LoadKeysFromMnemonicEnvForNetwork(networkID uint32, numAccounts int) ([]KeyInfo, error) {
|
||||
if err := RefuseLightMnemonicOnProduction(networkID); err != nil {
|
||||
return nil, err
|
||||
@@ -868,7 +843,7 @@ func BuildBIP44WalletAllocations(networkID uint32, numKeys int, amountPerKey uin
|
||||
_ = networkID // see comment above
|
||||
mnemonic := getMnemonicEnv()
|
||||
if mnemonic == "" {
|
||||
return nil, fmt.Errorf("wallet allocations require MNEMONIC env var")
|
||||
return nil, fmt.Errorf("wallet allocations require %s env var", MnemonicEnvVar)
|
||||
}
|
||||
if !bip39.IsMnemonicValid(mnemonic) {
|
||||
return nil, fmt.Errorf("invalid mnemonic for wallet key derivation")
|
||||
@@ -933,13 +908,13 @@ func BuildBIP44WalletAllocations(networkID uint32, numKeys int, amountPerKey uin
|
||||
return allocations, nil
|
||||
}
|
||||
|
||||
// BuildConfigFromEnv builds genesis config from environment variables
|
||||
// Checks in order: KEYS_DIR, mnemonic (MNEMONIC/LIGHT_MNEMONIC), PRIVATE_KEY
|
||||
// BuildConfigFromEnv builds genesis config from environment variables.
|
||||
// Checks in order: KEYS_DIR, LUX_MNEMONIC, PRIVATE_KEY.
|
||||
//
|
||||
// Architecture:
|
||||
// - X-Chain: 100 accounts × 500M LUX each, FREE
|
||||
// - P-Chain: 100 accounts × 500M LUX each, vesting 1%/year from 2020-01-01
|
||||
// - C-Chain: treasury 0x9011...4714 gets 2T LUX
|
||||
// - X-Chain: DefaultNumAccounts × allocationPerKey, immediate spend
|
||||
// - P-Chain: DefaultNumAccounts × allocationPerKey, immediate spend
|
||||
// - C-Chain: treasury 0x9011...4714 gets 2T LUX (no per-account alloc)
|
||||
func BuildConfigFromEnv(networkID uint32, numValidators int, allocationPerKey uint64) (*Config, error) {
|
||||
var err error
|
||||
|
||||
@@ -960,7 +935,7 @@ func BuildConfigFromEnv(networkID uint32, numValidators int, allocationPerKey ui
|
||||
// m/44'/9000'/0'/0/i (the same path the genesis CLI's -bip44-wallet-keys
|
||||
// flag uses, and the same path the derive100 / luxfi wallet UIs use).
|
||||
// This is what every user-facing tool that scans the chain for funded
|
||||
// addresses will expect — derive100 against $MNEMONIC and the
|
||||
// addresses will expect — derive100 against $LUX_MNEMONIC and the
|
||||
// addresses here must match byte-for-byte.
|
||||
//
|
||||
// NOTE: this is intentionally a SEPARATE concern from
|
||||
|
||||
@@ -53,16 +53,16 @@ func TestIsProductionNetwork(t *testing.T) {
|
||||
}
|
||||
|
||||
// TestIsKnownPublicMnemonic — F31 expansion. The guard now refuses every
|
||||
// known-public mnemonic, not just LIGHT_MNEMONIC. The BIP-39 abandon
|
||||
// known-public mnemonic, not just LightMnemonic. The BIP-39 abandon
|
||||
// vector, Hardhat default, and Trezor demo previously passed the
|
||||
// LIGHT_MNEMONIC-only check.
|
||||
// LightMnemonic-only check.
|
||||
func TestIsKnownPublicMnemonic(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
mnemonic string
|
||||
want bool
|
||||
}{
|
||||
{"LIGHT_MNEMONIC", LightMnemonic, true},
|
||||
{"LightMnemonic", LightMnemonic, true},
|
||||
{"BIP-39 abandon vector #1",
|
||||
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
true},
|
||||
@@ -93,48 +93,46 @@ func TestIsKnownPublicMnemonic(t *testing.T) {
|
||||
|
||||
// TestRefuseLightMnemonicOnProduction is the headline F12 fix +
|
||||
// F31 expansion: derivation MUST refuse on production for any
|
||||
// publicly-known mnemonic, not just the literal LIGHT_MNEMONIC.
|
||||
// publicly-known mnemonic, not just the literal LightMnemonic.
|
||||
func TestRefuseLightMnemonicOnProduction(t *testing.T) {
|
||||
// 1. No mnemonic set: not our problem at this layer.
|
||||
t.Setenv("MNEMONIC", "")
|
||||
t.Setenv("LIGHT_MNEMONIC", "")
|
||||
t.Setenv("LUX_MNEMONIC", "")
|
||||
if err := RefuseLightMnemonicOnProduction(constants.MainnetID); err != nil {
|
||||
t.Fatalf("no-mnemonic case: unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// 2. LIGHT_MNEMONIC on dev mesh: allowed.
|
||||
t.Setenv("LIGHT_MNEMONIC", LightMnemonic)
|
||||
// 2. LightMnemonic on dev mesh: allowed.
|
||||
t.Setenv("LUX_MNEMONIC", LightMnemonic)
|
||||
if err := RefuseLightMnemonicOnProduction(constants.LocalID); err != nil {
|
||||
t.Fatalf("LIGHT_MNEMONIC on LocalID(1337): unexpected error: %v", err)
|
||||
t.Fatalf("LightMnemonic on LocalID(1337): unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// 3. LIGHT_MNEMONIC on mainnet: REFUSED.
|
||||
// 3. LightMnemonic on mainnet: REFUSED.
|
||||
if err := RefuseLightMnemonicOnProduction(constants.MainnetID); err == nil {
|
||||
t.Fatalf("LIGHT_MNEMONIC on MainnetID: expected refusal, got nil")
|
||||
t.Fatalf("LightMnemonic on MainnetID: expected refusal, got nil")
|
||||
}
|
||||
|
||||
// 4. LIGHT_MNEMONIC on testnet: REFUSED.
|
||||
// 4. LightMnemonic on testnet: REFUSED.
|
||||
if err := RefuseLightMnemonicOnProduction(constants.TestnetID); err == nil {
|
||||
t.Fatalf("LIGHT_MNEMONIC on TestnetID: expected refusal, got nil")
|
||||
t.Fatalf("LightMnemonic on TestnetID: expected refusal, got nil")
|
||||
}
|
||||
|
||||
// 5. F31: BIP-39 abandon vector via MNEMONIC on mainnet → REFUSED
|
||||
// 5. F31: BIP-39 abandon vector via LUX_MNEMONIC on mainnet → REFUSED
|
||||
// (was previously ALLOWED — see prior red-review F31).
|
||||
t.Setenv("LIGHT_MNEMONIC", "")
|
||||
t.Setenv("MNEMONIC", "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about")
|
||||
t.Setenv("LUX_MNEMONIC", "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about")
|
||||
if err := RefuseLightMnemonicOnProduction(constants.MainnetID); err == nil {
|
||||
t.Fatalf("BIP-39 abandon vector on mainnet: expected refusal, got nil")
|
||||
}
|
||||
|
||||
// 6. F31: Hardhat default on mainnet → REFUSED.
|
||||
t.Setenv("MNEMONIC", "test test test test test test test test test test test junk")
|
||||
t.Setenv("LUX_MNEMONIC", "test test test test test test test test test test test junk")
|
||||
if err := RefuseLightMnemonicOnProduction(constants.MainnetID); err == nil {
|
||||
t.Fatalf("Hardhat default on mainnet: expected refusal, got nil")
|
||||
}
|
||||
|
||||
// 7. Real (non-public) mnemonic on mainnet: allowed.
|
||||
realMnemonic := "venue armor mouse cheese fork stem siren acquire rocket cabbage sentence vibrant"
|
||||
t.Setenv("MNEMONIC", realMnemonic)
|
||||
t.Setenv("LUX_MNEMONIC", realMnemonic)
|
||||
if err := RefuseLightMnemonicOnProduction(constants.MainnetID); err != nil {
|
||||
t.Fatalf("real mnemonic on mainnet: unexpected error: %v", err)
|
||||
}
|
||||
@@ -147,19 +145,18 @@ func TestRefuseLightMnemonicOnProduction(t *testing.T) {
|
||||
// trusted callers), this one fails-closed on a production network with
|
||||
// any public mnemonic.
|
||||
func TestLoadKeysFromMnemonicEnvForNetwork(t *testing.T) {
|
||||
// Setup: LIGHT_MNEMONIC on mainnet should refuse derivation.
|
||||
t.Setenv("MNEMONIC", "")
|
||||
t.Setenv("LIGHT_MNEMONIC", LightMnemonic)
|
||||
// Setup: LightMnemonic on mainnet should refuse derivation.
|
||||
t.Setenv("LUX_MNEMONIC", LightMnemonic)
|
||||
_, err := LoadKeysFromMnemonicEnvForNetwork(constants.MainnetID, 1)
|
||||
if err == nil {
|
||||
t.Fatalf("LoadKeysFromMnemonicEnvForNetwork: LIGHT_MNEMONIC on mainnet must refuse, got nil")
|
||||
t.Fatalf("LoadKeysFromMnemonicEnvForNetwork: LightMnemonic on mainnet must refuse, got nil")
|
||||
}
|
||||
|
||||
// Same env on a dev mesh should succeed (the guard does not refuse;
|
||||
// derivation proceeds via the underlying LoadKeysFromMnemonicEnv).
|
||||
keys, err := LoadKeysFromMnemonicEnvForNetwork(constants.LocalID, 1)
|
||||
if err != nil {
|
||||
t.Fatalf("LoadKeysFromMnemonicEnvForNetwork: LIGHT_MNEMONIC on dev (LocalID=1337): unexpected error: %v", err)
|
||||
t.Fatalf("LoadKeysFromMnemonicEnvForNetwork: LightMnemonic on dev (LocalID=1337): unexpected error: %v", err)
|
||||
}
|
||||
if len(keys) != 1 {
|
||||
t.Fatalf("expected 1 key, got %d", len(keys))
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package genesis
|
||||
|
||||
import "github.com/luxfi/constants"
|
||||
|
||||
// Canonical network ID map — the only legal values for the primary
|
||||
// network ID. Anything else is "custom" and a bug.
|
||||
//
|
||||
// primary network ID EVM (C-Chain) chain ID
|
||||
// ────────────────── ──────────────────────
|
||||
// constants.MainnetID (1) constants.MainnetChainID (96369)
|
||||
// constants.TestnetID (2) constants.TestnetChainID (96368)
|
||||
// constants.DevnetID (3) constants.DevnetChainID (96370)
|
||||
// constants.LocalID (1337) constants.LocalChainID (31337)
|
||||
//
|
||||
// The primary network ID is SHARED across all Lux-derived L1s (Lux,
|
||||
// Hanzo, Zoo, Pars, …). EVM chain IDs are per-subnet, per-env.
|
||||
//
|
||||
// Mnemonic source (LUX_MNEMONIC):
|
||||
// - mainnet/testnet/devnet (1/2/3): private hardware-RNG mnemonic from
|
||||
// KMS; RefuseLightMnemonicOnProduction enforces this.
|
||||
// - local (1337): LightMnemonic is the well-known dev seed value.
|
||||
//
|
||||
// All four envs read the mnemonic from the same env var (LUX_MNEMONIC);
|
||||
// per-env isolation comes from a different mnemonic per env, not from
|
||||
// the env-var name.
|
||||
var CanonicalPrimaryNetworkIDs = []uint32{
|
||||
constants.MainnetID, // 1
|
||||
constants.TestnetID, // 2
|
||||
constants.DevnetID, // 3
|
||||
constants.LocalID, // 1337
|
||||
}
|
||||
|
||||
// IsCanonicalPrimaryNetwork reports whether networkID is one of the four
|
||||
// canonical primary network IDs. Custom IDs (e.g. UnitTestID) are not
|
||||
// canonical and callers should treat them explicitly.
|
||||
func IsCanonicalPrimaryNetwork(networkID uint32) bool {
|
||||
switch networkID {
|
||||
case constants.MainnetID, constants.TestnetID, constants.DevnetID, constants.LocalID:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -27,13 +27,8 @@ const (
|
||||
|
||||
// DefaultValidatorAllocation is 1B LUX per validator
|
||||
DefaultValidatorAllocation uint64 = OneBillionLUX
|
||||
|
||||
// VestingPeriods is 100 years (1% unlocks per year)
|
||||
VestingPeriods = 100
|
||||
)
|
||||
|
||||
// StakingStartTime and UnlockInterval are defined in keys.go
|
||||
|
||||
// ValidatorKeyInfo contains computed addresses from a validator's private key
|
||||
type ValidatorKeyInfo struct {
|
||||
PrivKeyHex string // Hex-encoded private key
|
||||
@@ -167,42 +162,6 @@ func GeneratePChainAllocations(keys []ValidatorKeyInfo, hrp string, amountPerKey
|
||||
return allocations, nil
|
||||
}
|
||||
|
||||
// GeneratePChainAllocationsWithVesting creates P-chain allocations with a vesting schedule.
|
||||
// Each key gets the specified amount vested over the given number of periods.
|
||||
func GeneratePChainAllocationsWithVesting(keys []ValidatorKeyInfo, hrp string, amountPerKey uint64, startTime uint64, interval uint64, periods int) ([]AllocationJSON, error) {
|
||||
if amountPerKey == 0 {
|
||||
amountPerKey = OneBillionLUX
|
||||
}
|
||||
if startTime == 0 {
|
||||
startTime = StakingStartTime
|
||||
}
|
||||
if interval == 0 {
|
||||
interval = UnlockInterval
|
||||
}
|
||||
if periods == 0 {
|
||||
periods = 100 // 100 years default
|
||||
}
|
||||
|
||||
allocations := make([]AllocationJSON, len(keys))
|
||||
for i, key := range keys {
|
||||
utxoAddr, err := FormatChainAddress("P", hrp, key.ShortID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to format address for key %d: %w", i, err)
|
||||
}
|
||||
|
||||
// Build vesting schedule
|
||||
unlockSchedule := buildUnlockSchedule(amountPerKey, startTime, interval, periods)
|
||||
|
||||
allocations[i] = AllocationJSON{
|
||||
EVMAddr: key.EVMAddr,
|
||||
UTXOAddr: utxoAddr,
|
||||
InitialAmount: amountPerKey, // X-chain initial amount
|
||||
UnlockSchedule: unlockSchedule,
|
||||
}
|
||||
}
|
||||
return allocations, nil
|
||||
}
|
||||
|
||||
// GenerateCChainAlloc creates C-chain genesis allocations for validator keys.
|
||||
func GenerateCChainAlloc(keys []ValidatorKeyInfo, amount uint64) map[string]Balance {
|
||||
if amount == 0 {
|
||||
|
||||
Reference in New Issue
Block a user