mirror of
https://github.com/luxfi/genesis.git
synced 2026-07-27 04:11:41 +00:00
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.
183 lines
4.5 KiB
Go
183 lines
4.5 KiB
Go
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
|
// derive100 derives 100 P-chain addresses from a mnemonic across testnet/mainnet/devnet
|
|
// HRPs, then probes platform.getBalance over JSON-RPC for each to find which idx is funded.
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/luxfi/crypto/secp256k1"
|
|
"github.com/luxfi/go-bip32"
|
|
"github.com/luxfi/go-bip39"
|
|
"github.com/luxfi/ids"
|
|
"github.com/luxfi/node/utils/formatting/address"
|
|
)
|
|
|
|
const numAccounts = 100
|
|
|
|
type rpcReq struct {
|
|
JSONRPC string `json:"jsonrpc"`
|
|
ID int `json:"id"`
|
|
Method string `json:"method"`
|
|
Params map[string]interface{} `json:"params"`
|
|
}
|
|
|
|
type rpcResp struct {
|
|
Result struct {
|
|
Balance string `json:"balance"`
|
|
Unlocked string `json:"unlocked"`
|
|
LockedStakeable string `json:"lockedStakeable"`
|
|
LockedNotStakeable string `json:"lockedNotStakeable"`
|
|
UTXOIDs []string `json:"utxoIDs"`
|
|
} `json:"result"`
|
|
Error *struct {
|
|
Message string `json:"message"`
|
|
} `json:"error"`
|
|
}
|
|
|
|
func formatPAddr(hrp string, addr ids.ShortID) string {
|
|
b32, err := address.FormatBech32(hrp, addr[:])
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return "P-" + b32
|
|
}
|
|
|
|
func deriveAddrs(mnemonic, hrp string) []string {
|
|
if !bip39.IsMnemonicValid(mnemonic) {
|
|
fmt.Fprintln(os.Stderr, "invalid mnemonic")
|
|
os.Exit(1)
|
|
}
|
|
seed := bip39.NewSeed(mnemonic, "")
|
|
master, err := bip32.NewMasterKey(seed)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
purpose, _ := master.NewChildKey(bip32.FirstHardenedChild + 44)
|
|
coinType, _ := purpose.NewChildKey(bip32.FirstHardenedChild + 9000)
|
|
account, _ := coinType.NewChildKey(bip32.FirstHardenedChild + 0)
|
|
change, _ := account.NewChildKey(0)
|
|
|
|
out := make([]string, numAccounts)
|
|
for i := 0; i < numAccounts; i++ {
|
|
child, err := change.NewChildKey(uint32(i))
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
priv, err := secp256k1.ToPrivateKey(child.Key)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
out[i] = formatPAddr(hrp, priv.Address())
|
|
}
|
|
return out
|
|
}
|
|
|
|
func getBalance(rpc, paddr string) (uint64, string, error) {
|
|
req := rpcReq{
|
|
JSONRPC: "2.0",
|
|
ID: 1,
|
|
Method: "platform.getBalance",
|
|
Params: map[string]interface{}{
|
|
"addresses": []string{paddr},
|
|
},
|
|
}
|
|
body, _ := json.Marshal(req)
|
|
httpReq, err := http.NewRequest("POST", rpc+"/ext/bc/P", bytes.NewReader(body))
|
|
if err != nil {
|
|
return 0, "", err
|
|
}
|
|
httpReq.Header.Set("Content-Type", "application/json")
|
|
cli := &http.Client{Timeout: 10 * time.Second}
|
|
resp, err := cli.Do(httpReq)
|
|
if err != nil {
|
|
return 0, "", err
|
|
}
|
|
defer resp.Body.Close()
|
|
raw, _ := io.ReadAll(resp.Body)
|
|
var r rpcResp
|
|
if err := json.Unmarshal(raw, &r); err != nil {
|
|
return 0, string(raw), err
|
|
}
|
|
if r.Error != nil {
|
|
return 0, r.Error.Message, fmt.Errorf("rpc error")
|
|
}
|
|
bal, _ := parseUint64(r.Result.Balance)
|
|
return bal, r.Result.Balance, nil
|
|
}
|
|
|
|
func parseUint64(s string) (uint64, error) {
|
|
var x uint64
|
|
for _, c := range s {
|
|
if c < '0' || c > '9' {
|
|
return 0, fmt.Errorf("non-digit")
|
|
}
|
|
x = x*10 + uint64(c-'0')
|
|
}
|
|
return x, nil
|
|
}
|
|
|
|
func main() {
|
|
if len(os.Args) < 4 {
|
|
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: LUX_MNEMONIC")
|
|
os.Exit(1)
|
|
}
|
|
hrp := os.Args[1]
|
|
rpc := os.Args[2]
|
|
envName := os.Args[3]
|
|
mnemonic := strings.TrimSpace(os.Getenv(envName))
|
|
if mnemonic == "" {
|
|
fmt.Fprintf(os.Stderr, "%s not set\n", envName)
|
|
os.Exit(1)
|
|
}
|
|
|
|
addrs := deriveAddrs(mnemonic, hrp)
|
|
|
|
fmt.Printf("hrp=%s rpc=%s mnemonic=%s\n", hrp, rpc, envName)
|
|
fmt.Printf("idx0 = %s\n", addrs[0])
|
|
fmt.Printf("idx5 = %s\n", addrs[5])
|
|
|
|
type hit struct {
|
|
idx int
|
|
bal uint64
|
|
raw string
|
|
}
|
|
hits := []hit{}
|
|
for i, a := range addrs {
|
|
bal, raw, err := getBalance(rpc, a)
|
|
if err != nil {
|
|
fmt.Printf("idx %3d %s ERR: %v %s\n", i, a, err, raw)
|
|
continue
|
|
}
|
|
if bal > 0 {
|
|
hits = append(hits, hit{i, bal, raw})
|
|
fmt.Printf("idx %3d %s balance=%d nLUX (%s LUX)\n", i, a, bal, formatLUX(bal))
|
|
}
|
|
}
|
|
fmt.Printf("\nFUNDED IDXES (%d): ", len(hits))
|
|
for _, h := range hits {
|
|
fmt.Printf("%d ", h.idx)
|
|
}
|
|
fmt.Println()
|
|
}
|
|
|
|
func formatLUX(nLUX uint64) string {
|
|
// On P-chain platform.getBalance returns nLUX (1 LUX = 1e9 nLUX)
|
|
whole := nLUX / 1_000_000_000
|
|
frac := nLUX % 1_000_000_000
|
|
if frac == 0 {
|
|
return fmt.Sprintf("%d", whole)
|
|
}
|
|
return fmt.Sprintf("%d.%09d", whole, frac)
|
|
}
|