Single LICENSING.md file referencing the canonical three-tier IP and
licensing strategy at github.com/luxfi/.github/blob/main/profile/README.md.
LICENSE file is unchanged; this only adds a navigational pointer.
Renames LUX_-prefixed env vars to canonical, non-noisy forms:
- LUX_MNEMONIC dropped from getMnemonicEnv() lookup chain.
Priority is now MNEMONIC > LIGHT_MNEMONIC.
All callers, doc comments, and test
t.Setenv("LUX_MNEMONIC", "") clears removed.
- LUX_NETWORK_ID -> NETWORK_ID
- LUX_GENESIS_DIR -> GENESIS_DIR
- LUX_KEYS_DIR -> KEYS_DIR (doc fix; code already used KEYS_DIR)
- LUX_BOOTSTRAPPERS_FILE-> BOOTSTRAPPERS_FILE
- LUX_PCHAIN_ALLOCS -> PCHAIN_ALLOCS
- LUX_PCHAIN_ALLOCS_FILE-> PCHAIN_ALLOCS_FILE
Doc string mentions of past-but-still-supported `LUX_X` env var names
("set MNEMONIC or LUX_MNEMONIC env var") are also dropped — the env
var is just MNEMONIC now.
LUX_DISABLE_CCHAIN / LUX_CCHAIN_GENESIS_FILE history left in LLM.md
(historical changelog table) and chain_shards_test.go (comment
explicitly documenting the data-driven replacement contract). These
are documenting past behavior, per task convention for historical
references.
BREAKING: external callers/operators must update env var names. Per
CLAUDE.md no-backwards-compatibility rule, the old forms are removed.
- configs/mainnet/cchain.json + the embedded cChainGenesis blob in
configs/mainnet/genesis.json: gasLimit 0x3b9aca00 (1G) → 0xb71b00 (12M)
to match the deployed mainnet C-Chain genesis.
- configs/mainnet/genesis.json: also reorders evmTimestamp into the canonical
alphabetical position inside the cChainGenesis JSON blob (no semantic change).
- configs/devnet/genesis.json + configs/devnet/pchain.json: drop the trailing
three initialStakers, leaving 3 stakers wired to real luxd staker certs
(fixed publicKey + mldsaPublicKey for the 2 surviving + reset for the 1 we
keep). Matches what's actually validating on lux-k8s do-sfo3-lux-devnet.
Repoints BuildConfigFromEnv account-allocation derivation from the
Lux-internal hardened branch (m/44'/9000'/nid'/1'/i') to the canonical
BIP44 path (m/44'/9000'/0'/0/i) — same path derive100, BIP44 web wallets,
and any wallet-against-coin-9000 tool uses. Validator NODE identities
(NodeID + BLS + ML-DSA-65) keep the internal hardened layout; spending
keys and node identities are independently complete.
DefaultAllocationPerAccount: 500_000_000*Lux → 10_000_000*Lux (keys.go:36)
DefaultNumAccounts: 100 → 1000 (keys.go:46)
LocalParams.MinValidatorStake: 1*Lux → 1_000_000*Lux (params.go:64)
TestnetParams.MinValidatorStake: 1*Lux → 1_000_000*Lux (params.go:102)
LocalStakingConfig.MinValidatorStake: 1*Lux → 1_000_000*Lux (params.go:166)
TestnetStakingConfig.MinValidatorStake: 1*Lux→ 1_000_000*Lux (params.go:148)
MainnetParams + MainnetStakingConfig: unchanged
Wallet allocations no longer carry a 100-period UnlockSchedule. With
1000 wallet entries × 100 schedule rows each, the encoded P-Chain
genesis blob overflows zapdb's single-batch write limit and luxd dies
at boot with "Txn is too big to fit into one request". Wallets get
clean InitialAmount only — fans out to a free P-Chain UTXO and a free
X-Chain UTXO at the same address per the node builder. Validator
stake allocations still carry a long-locktime UnlockSchedule so the
ProtocolVM has weight to gate them.
New LoadBIP44WalletKeysFromMnemonic returns []KeyInfo at the canonical
path so BuildConfigFromEnv flows the same derivation into validator
fallback and allocation set. BuildBIP44WalletAllocations is unchanged
and remains the path the genesis CLI -bip44-wallet-keys flag uses.
configs/local/, configs/localnet/genesis.json + pchain.json
regenerated against LIGHT_MNEMONIC. testnet/devnet/mainnet JSONs
unchanged — those need regeneration against the operator's real
$LUX_MNEMONIC via the CLI, and remain pinned to their existing
addresses until the operator re-applies.
Operator runbook to regenerate per-network:
export LUX_MNEMONIC=... # from KMS, in-process only, never persist
cd ~/work/lux/genesis
go run ./cmd/genesis -network devnet -validators 5 \
-output configs/devnet/genesis.json -format pretty
# then split into pchain.json (allocations + initialStake*) so the
# embedded loader's split-file preference picks them up
go run ./cmd/genesis -network testnet -validators 5 \
-output configs/testnet/genesis.json -format pretty
Bumps min validator stake on local + testnet to 1M LUX as planned for
the L1-bootstrap flow; mainnet stays at 2000 LUX MinValidatorStake.
Verified end-to-end on a local single-node luxd: bootstrap succeeded,
platform.getBalance for P-local1jfdgmqduuyuxjp9sq7szp08xpynav88sd7qxvv
(BIP44 m/44'/9000'/0'/0/0 of LIGHT_MNEMONIC) returns 10_000_000_000_000
nLUX = 10M LUX, fully unlocked. xvm.getBalance for the same address on
X-Chain returns identical 10M LUX. All pkg/genesis + configs tests pass
(TestTransferPChain skipped — pre-existing live-RPC flake, see aa3d175).
Pre-existing security_profile_test.go fixed for consensus@v1.23.15
rename (LuxStrictPQ → StrictPQ, ProfileLuxStrictPQ → ProfileStrictPQ);
that was blocking the whole pkg/genesis test build at v1.9.8.
The struct already only carries shards (one field per chain shard
file), so the 'Shard' qualifier in the type name was redundant. Drop
it. Identifier is unexported and used in three sites only inside
configs/configs.go, so the rename has no API impact on consumers.
Values, not places: the type captures 'the set of primary-network
chain genesis blobs' — qualifying it by where the data came from
(shard files) is a place-leak, not a value distinction.
Make every primary-network chain (X/C/D/Q/A/B/T/Z/G/K) opt-in via its
own <x>chain.json shard. Absent shard -> empty ConfigOutput field ->
builder skips the CreateChainTx -> daemon doesn't start that chain.
Lux mainnet/testnet/devnet ship full chain set as shards; localnet and
local ship the same canonical X asset shard. P-only sovereign L1 boot
shape (<tenant> etc.) is achieved by shipping pchain.json+network.json
only — no env knob, no special builder branch.
X-Chain genesis was previously hardcoded inside FromConfig as a literal
{Symbol:"LUX",Name:"Lux",Denomination:9}. Now sourced from xchain.json
shard, which keeps the same canonical asset for Lux but lets a sibling
network (lqd / <tenant>) ship its own descriptor or ship none and
boot a P-only primary network.
builder.FromConfig:
- HRP hoisted above the X-Chain conditional (used on both branches).
- X-Chain construction gated on config.XChainGenesis != "".
- Returns ids.Empty for xAssetID when X is absent.
- Single chainEntries table replaces the per-chain switch and the
DefaultChainGenesis stub-data fallback.
- "platform" alias renamed to "protocol" (PChainAliases / VMAliases /
Aliases output) — value is "P-Chain", served by ProtocolVM.
configs.loadAllChainShards / readAllChainShards:
- Single dispatch over primaryChainShardFiles in canonical order
(X,C,D,Q,A,B,T,Z,G,K). Adding a new primary chain is one entry +
one slot, not a new env knob and not a new builder branch.
pkg/genesis/types.go:
- ConfigOutput.XChainGenesis (omitempty) added; threaded through
Config <-> ConfigOutput conversions.
Tests:
- builder/builder_test.go: TestFromConfig_ChainSetIsShardDriven pins
the contract — full mainnet emits 10 chains + non-empty xAssetID;
same config with all chain shards stripped emits 0 chains + empty
xAssetID, validators + UTXOs intact.
- configs/chain_shards_test.go: TestGetGenesis_XChainShardPresent...
asserts every Lux network embeds the canonical {LUX,Lux,9} asset;
AbsentShardEmptyOptIn extended to cover xChainGenesis.
Q-Chain and Z-Chain are opt-in (per the builder refactor), but every
Lux-derived primary network bakes them. Make the intent explicit: ship
qchain.json + zchain.json shards alongside cchain.json so the chain set
is data-visible rather than implied by a builder default.
Downstreams (<tenant> etc.) that want P+X only simply don't ship these
shards in their config tree. No env, no flag — the shard set is the
network's identity.
The placeholder content {"version":1,"message":"Lux Chain Genesis"} is
the same string the old loadSpecialtyChainGenesis fallback substituted;
materialising it here keeps the on-wire genesis bytes stable across the
refactor.
Asymmetry rip: Q-Chain and Z-Chain were MANDATORY in the builder while
C/D/B/T were opt-in, with three different env knobs (LUX_DISABLE_CCHAIN,
LUX_DISABLE_QCHAIN, LUX_DISABLE_ZCHAIN) controlling embed-time inclusion.
That meant adding a new primary-network chain meant adding both a builder
case AND an env knob AND a *_GENESIS_FILE override — three places to keep
in sync, and operators got to wonder which knob applied to which chain.
Replaced with one pattern, one place:
- builder.go: all opt-in chains in a single table-driven slice, each row
is `{genesisData, vmID, name}`. Adding a chain is one row, not a new
conditional. P and X stay mandatory (P is implicit, X anchors fees).
- configs.go: loadOptionalChainShard / readDirShard load per-chain JSON
shards. Shard present → chain baked into primary genesis. Shard absent
→ empty string → builder skips the entry. fs.ErrNotExist is the gate;
every other read failure surfaces.
- LUX_DISABLE_*CHAIN env knobs deleted. LUX_*CHAIN_GENESIS_FILE deleted.
loadSpecialtyChainGenesis / specialtyChainResult / DefaultPlaceholderGenesis
deleted — all dead code now.
Runtime tracking is unchanged: luxd's --track-chains / --track-all-chains
gates which baked chains a particular node actually runs. Baked-but-untracked
chains stay dormant. Bake = "is this chain part of the network's identity";
track = "is this node validating it". Decomplected.
To run a P+X-only network (<tenant> etc.): ship a config tree with just
network.json + pchain.json (+ securityProfile.json). No cchain.json,
qchain.json, or zchain.json. No flag, no env, no patched binary.
Tests:
- TestGetGenesis_CChainShardPresentEmbedsCChainGenesis: embedded networks
with cchain.json shards do emit a non-empty cChainGenesis.
- TestBuildGenesisFromDir_AbsentShardEmptyOptIn: FS fallback with only
network.json + pchain.json produces empty cChain/qChain/zChain fields.
Production e2e PQ wire-up:
mainnet, testnet, local, localnet → StrictPQ (0x01)
devnet → Permissive (0x02) for dev workflows
Replaces the v1.22.87-era ForkClassicalCompatUnsafe (0x80) pins which
collide with v1.23.15's tightened registry (profiles collapsed to 3
canonical bundles; 0x80 is no longer in ProfileByID's switch). The
luxfi/node v1.26.17 build resolves these pins at boot and stamps the
profile through to every wire-level gate (peer handshake, mempool,
validator scheme, EVM contract auth, C-Chain plugin config).
Verified live at boot:
SECURITY PROFILE: STRICT
PROFILE HASH: cf3f2cd7b54cfc2c…
POST-QUANTUM: true
NIST-FRIENDLY: true
CLASSICAL SNARKS: forbidden
BLS FALLBACK: forbidden
Hashes computed via consensus.ChainSecurityProfile.ComputeHash() — when
consensus rolls, regenerate pins via the same path (a CI guard against
silent profile drift is the next layer).
node calls genesiscfg.GetConfig(networkID) at boot which routes through
GetConfigFromDir, NOT the configs/ embedded path. Without this shard read,
the SecurityProfile pin was silently dropped between the on-disk genesis tree
and the runtime Config — the F102 wire-up only worked for paths that went
through ConfigOutput marshal/unmarshal.
Now: shard read in GetConfigFromDir mirrors the configs/configs.go embedded
loader, so the pin flows whether node loads from embedded FS, on-disk
canonical tree, or operator override directory.
- securityProfile.json shard per network embeds the locked ChainSecurityProfile
pin (ProfileID + 48-byte hash). mainnet/testnet/devnet pin
ForkClassicalCompatUnsafe (0x80) until operators migrate validator keys;
local/localnet pin LuxStrictPQ (0x01).
- configs.go loaders (embedded + FS fallback) read the shard and stamp it into
ConfigOutput.SecurityProfile so the pin survives the marshal/unmarshal trip
into Config.
- pkg/genesis ParseConfigOutput now copies SecurityProfile through — without
this the field was silently dropped, leaving Resolve() unable to verify the
pin at boot and the node warning "genesis carries no SecurityProfile pin —
node boots in classical-compat mode" for every network.
- security_profile_test renamed to current symbol names (LuxStrictPQ,
ProfileLuxStrictPQ — consensus v1.23.5 renamed away from the StrictPQ alias).
Profile hashes computed from luxfi/consensus@v1.23.5 ChainSecurityProfile
ComputeHash():
ForkClassicalCompatUnsafe (0x80): 07fff4ce64024e22…dc61d656c
LuxStrictPQ (0x01): 93efd103aaf4b85e…323bfcd0
Mirror the LUX_DISABLE_CCHAIN three-precedence pattern for Q-Chain
(Quantum VM) and Z-Chain (ZK VM). Downstream forks that run only
P+X on the primary network (<tenant> is the first) set
LUX_DISABLE_QCHAIN=1 and LUX_DISABLE_ZCHAIN=1 to omit those entries
from the marshalled primary genesis, and builder.FromConfig's
'if config.QChainGenesis != ""' / 'ZChainGenesis' guards skip the
entries at build time.
Precedence (identical to C-Chain knob):
1. LUX_DISABLE_<X>CHAIN=1 → empty (omit from primary genesis)
2. LUX_<X>CHAIN_GENESIS_FILE=<path> → file contents verbatim
3. unset → DefaultPlaceholderGenesis
Q-Chain is the post-quantum primitives chain; Z-Chain is the
zero-knowledge primitives chain. Both are Lux Network-specific and
have no place in the primary genesis of downstream L1s.
Adds SecurityProfile to genesis.Config and ConfigOutput as an optional
pin-by-ID + pin-by-hash for the chain-wide ChainSecurityProfile. JSON
shape:
"securityProfile": {
"profileID": 1,
"profileHashHex": "<96 hex chars of SHA3-384 ComputeHash>"
}
SecurityProfile.Resolve() loads the canonical profile via
consensus/config.ProfileByID, runs Validate(), recomputes ComputeHash,
and refuses on any mismatch — a forked binary that swaps in a different
canonical profile content fails genesis boot.
This closes the genesis half of red-team F102: previously this package
never imported consensus/config.ChainSecurityProfile so no genesis
file could enforce the locked profile. The node-side wiring lands in
the follow-up commit.
Bumps luxfi/consensus v1.22.63 → v1.23.5 (the version that ships
ProfileByID, ProfileHash binding, and the closures for F96/F100/F101/
F107/F109).
Adds 6 regression tests for the load + JSON round-trip path.
luxfi/crypto v1.18.4 exposes the canonical NewKeyFromSeed entry point for
ML-DSA-65 (and mldsa44 / mldsa87) that the genesis HIP-0077 derivation
needed. Closes the TODO at the prior CIRCL stop-gap import.
The HIP-0077 SHAKE-256(label || child_seed) expansion still happens at
mldsaKeygenFromChildSeed; the resulting 32-byte xi is passed verbatim to
mldsa65.NewKeyFromSeed. At len == 32 the canonical package wires the
seed straight into FIPS 204 5.1 KeyGen, so the keypair is byte-for-byte
reproducible against the prior CIRCL call (which this package wraps
unchanged).
go.mod: luxfi/crypto v1.17.44 -> v1.18.4
go.mod: cloudflare/circl moves from direct -> indirect
Patch-bump.
First CI run flagged a second pre-existing failure: TestTransferPChain
in pkg/genesis/do_transfer_test.go makes a real outbound JSON-RPC call
to https://api.lux-dev.network/ext/bc/P, which fails in CI because
that endpoint's TLS cert is a Traefik-issued internal cert that
doesn't match the public hostname.
This is an integration test, not a unit test — it does not belong in
push/tag CI. Skipped via -skip regex in both ci.yml and release.yml
until it is either gated on testing.Short() / build tags or moved into
a dedicated integration job that has access to a real devnet.
The repo had no .github/workflows/ at all, so tag pushes never produced
release binaries.
Added:
.github/workflows/ci.yml go vet, gofmt -s -d, go test (push/PR to main)
.github/workflows/release.yml build genesis for linux|darwin|windows
amd64/arm64 + sha256, attach to GH release
CI runs `go test -skip TestGetGenesisLocalnet ./...` until the embedded
localnet genesis file is regenerated (expected 5e17 wei / 0.5 LUX, got
5e14). All other tests are green.
While here:
* go.mod bumped Go directive 1.26.1 → 1.26.2 (matches new toolchain
used by ci.yml / release.yml).
* luxfi/metric v1.4.11 → v1.4.12. v1.4.11 had a build-tag bug where
both process_metrics_other.go and process_metrics_windows.go used
//go:build windows, causing duplicate symbols on GOOS=windows. v1.4.12
fixes the tag to //go:build !unix && !windows. Required for the
windows/amd64 leg of the release matrix.
* `gofmt -s -w` on four pre-existing unformatted files so the new
gofmt CI gate is green from day one.
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.
Adds an opt-out for the embedded C-Chain that gets baked into every
primary network genesis. With LUX_DISABLE_CCHAIN=1 the cchainData byte
slice stays empty, ConfigOutput.CChainGenesis is "", and downstream
builder.FromConfig's `if config.CChainGenesis != ""` guard skips the
C-Chain entry entirely — no more chainId 31337 silently mounted at
/ext/bc/C/rpc on networks that don't want it.
Why this knob exists: forks that run their own EVM blockchain via
CreateChainTx (<tenant>, etc.) don't use the C-Chain at all — but the
embedded localnet/cchain.json was being read unconditionally, so an
SRE running `lqd --network-id=1337` with no C-Chain env vars set
still got one. That's a footgun for any service that hard-codes the C
alias and a confusion source when you grep for /bc/C and it's there.
Three tests pin the behavior:
• TestGetGenesis_DisableCChain — set knob → cChainGenesis is empty
in the marshalled primary genesis, networkID still 1337.
• TestGetGenesis_DisableCChainOverridesFile — disable wins over the
LUX_CCHAIN_GENESIS_FILE override; an SRE setting both during a
misconfig probe gets the no-C-Chain outcome rather than silently
landing the file's content on the primary.
• TestGetGenesis_DisableCChainDefault — unset knob still embeds
C-Chain (regression guard so a careless precedence edit doesn't
silently strip C-Chain from mainnet/testnet/devnet).
go.mod / go.sum churn is `go mod tidy` resolving previously-stale
entries — unrelated to the C-Chain change but required for the test
suite to build.
Adds env-driven override at the lowest layer of the genesis loader so
downstream networks (e.g. <tenant> at chainId 8675312) can reuse a
stock lqd binary without forking configs/{network}/cchain.json.
Resolution order in loadEmbeddedGenesisWithDynamic:
1. LUX_CCHAIN_GENESIS_FILE — absolute path to a JSON file. Read at
boot, used verbatim as the C-Chain genesis bytes.
2. embedded configs/{network}/cchain.json (the previous default,
immutable per-network).
Net behaviour for stock callers is unchanged: env unset → embedded
default. The override only fires when the file exists and is readable;
a misconfigured path fails loud with a wrapped error so misconfiguration
isn't silently masked by the embedded fallback.
Pairs with luxfi/node@d0a248f5 (LUX_AUTOMINE_CCHAIN_GENESIS_PATH for the
single-node automine path). The two cover both startup modes lqd uses.
Across all envs (mainnet/testnet/devnet/local/localnet) and all chains
(C-Chain primary + Zoo + SPC + Hanzo + Pars L2s), the EVM block gas
limit and the matching root genesis.gasLimit field are bumped to 1B.
targetGas tracks at half the limit (500M) to keep the existing 50%
fee-curve target ratio.
Why: 100M was the old practical ceiling and was already getting hit on
bulk security-token deploys (12k+ tokens) where each constructor burns
~1.3M gas. At 100M that's ~75 deploys/block; at 1B it's ~750.
Production note: bumping mainnet's gasLimit changes the cChain genesis
hash. Existing state imports against the old 12M-gasLimit cChain are
invalidated. The canonical hash table in the package CLAUDE.md needs to
be regenerated after this lands.
The Lux primary network on local-1337 hosts P + X + the static native chains
(A/B/D/G/K/Q/T/Z); the EVM is provisioned per-tenant as a subnet chain
(<tenant> EVM / <tenant> DEX / Liquid FHE), not baked into primary as C-Chain.
Carrying cChainGenesis here just bound the network to a 31337 stub that
nobody actually uses and confused tools that derive deployer keys from
LIGHT_MNEMONIC into thinking the local C-Chain alloc was the dev funding.
The lux/node genesis builder treats CChainGenesis as opt-in
(`if config.CChainGenesis != ""` at builder.go:618) so dropping the field
is safe — primary still starts with all required chains, just without C.
The localnet/genesis.json was generated from a different mnemonic — wallet[0]
was X-local1xhtylulkrrm... which doesn't match what BuildWalletKeyHex(0) returns
when LIGHT_MNEMONIC is set in env. This broke local-dev tools (cmd/create-evm,
cmd/deploy-dex, cmd/deploy-fhe in <tenant>/node) that derive keys from
LIGHT_MNEMONIC and try to use them against the local P-Chain — getUTXOs returned
empty, CreateChainTx failed with "couldn't get tx: not found".
Regen via:
MNEMONIC=$LIGHT_MNEMONIC go run ./cmd/genesis \
-network local \
-validators 5 \
-wallet-keys 100 \
-wallet-amount 500000000 \
-format json -output -
merged into configs/localnet/genesis.json + pchain.json (preserving the
a/b/c/d/g/k/q/t/z chain genesis fields). Now wallet[0]=P-local1jfdgmqduuyux...
matches LIGHT_MNEMONIC m/44'/9000'/0'/0/0 exactly, and indices 0..99 each carry:
- 500M LUX vested over 100 years (P-Chain main alloc)
- 500M LUX FREE/spendable (extra wallet alloc, indices 100..199)
so a fresh local 1337 cluster bootstraps with 100 fully-funded P/X wallets that
both the lux/node + <tenant>/node toolchains can derive against without any
special config.
unlockSchedule normalised to [] (was null in jq output) for strict consumers.
Same classification at the genesis layer as at the constants layer.
A user spinning up a private primary network on any non-well-known
ID gets:
IsCustom(id) == true at both layers
networkNameFromID(id) == "" (no embedded canonical genesis)
constants.GetHRP(id) == "custom" (P-custom1... addresses)
Callers must supply --genesis-file for any IsCustom() network since
there's no embedded config to load.
Mirrors the same split that just landed in luxfi/constants v1.5.1:
LocalID = 1337 (canonical local dev primary network)
LocalChainID = 31337 (canonical local C-Chain EVM ID)
CustomID = 0 (sentinel for user-defined networks)
CustomChainID = 0 (sentinel for user-defined C-Chain IDs)
LocalnetID = LocalID (deprecated alias)
LocalnetChainID = LocalChainID (deprecated alias)
Before, CustomID was an alias of LocalnetID (both 1337) which made
"this is the local dev network" indistinguishable from "this is some
other custom network the user supplied". After the split, callers can
say LocalID (1337) for the well-known local dev net, CustomID (0) for
genuinely custom networks. Addresses on a custom network use the
"custom" HRP from luxfi/constants — i.e. P-custom1..., X-custom1...
— so they're visually distinct from the local-net "X-local1..." form.
networkNameFromID switch updated to recognise LocalID + LocalChainID.
Lux EVM activation is gated by a single canonical chain config field:
EVMTimestamp / json:'evmTimestamp'
The legacy 'SubnetEVMTimestamp' / 'subnetEVMTimestamp' field was a duplicate
alias for the same activation. Removed:
- geth/params/config.go: ChainConfig.SubnetEVMTimestamp field
- geth/params/config.go: IsEVM() now checks EVMTimestamp only
- geth/params/config.go: isLuxL2Chain check uses EVMTimestamp only
- all genesis JSON configs (mainnet/testnet/devnet × all chains)
- all helm chart genesis files (charts/lux/genesis/*.json)
- all docs (LLM.md, *.mdx)
One field, one way.
Was 0x16bcc41e90000000000000000 (112.589 billion) — copy-paste error from
Lux/Zoo C-Chain 2T value. Pars EVM is its own L1 with distinct tokenomics;
deployer gets 7B PARS across all three envs (mainnet 494949, testnet 7071,
devnet 7072).
New alloc hex: 0x169e43a85eb381aa58000000 (= 7,000,000,000 × 10^18 wei).
Running chain on api.lux.network/ext/bc/pars/rpc still has stale alloc
(~1000 PARS to 0x9011); needs P-chain CreateChainTx with these bytes to
relaunch with correct genesis.
netrunner v1.15.11 references configs.CustomID which was renamed to
LocalnetID. Add CustomID = LocalnetID alias. Also regenerate go.sum
after stale checksum entries from repo rewrite.