Hanzo DevandGitHub 45008b7205 fix(genesis): canonical 0x prefix on all devnet alloc keys + fail-loud bootstrap-l2 (#72) (#5)
* feat(genesis): bootstrap-l2 — durable replacement for /tmp script

Recreates the v1.27.x ConvertSubnetToL1 / CreateChain pipeline previously
maintained as /tmp/bootstrap-l2-v127. New tool at cmd/bootstrap-l2/:

- Reads MNEMONIC from env, derives BIP44 m/44'/9000'/0'/0/<idx>
- For each chain in --chains: IssueCreateNetworkTx → wallet re-sync →
  IssueCreateChainTx (vmID nyGCobireNhxFB7iM5bxV74hAY6j9nQX6wizxfWomnMMtztkr)
  → IssueAddChainValidatorTx for every primary validator
- Probes info.isBootstrapped(chain=<bcID>) + eth_blockNumber>0 per chain;
  if either misses the deadline the whole tool exits non-zero (no partial
  state propagated to YAML/CMs)
- Emits a single JSON document {chains:[{name, subnetId, blockchainId,
  evmChainId, firstBlockHex, bootstrappedAt}, ...]} for downstream
  YAML/CM updates
- --print-addr-only mode to verify funding before spending

go.mod: bump luxfi/node v1.23.36 → v1.27.21 to match cluster runtime
(devnet runs luxd v1.27.21; pre-rename wallet codecs would produce txs the
nodes can't decode). Only existing genesis use of luxfi/node is the
address formatter — no behavior change for other cmds.

Designed for the four canonical Lux L2 EVMs (hanzo, zoo, pars, spc)
but the chain list is data-driven via --chains and --configs-dir.

* feat(genesis): bootstrap-l2 default VM ID → mgj786NP... (native subnet-evm)

Devnet luxd-0 /data/plugins contains exactly two subnet-evm plugin IDs
(mgj786NP7uDwBCcq6YwThhaN8FLyybkCa4zBWTQbNgmK6k9A6 and
ag3GReYPNuSR17rUP8acMdZipQBikdXNRKDyFszAysmy3vDXE), neither matching the
brand-namespaced alias nyGCobireNhxFB7iM5bxV74hAY6j9nQX6wizxfWomnMMtztkr
recorded in the 2026-05-27 chain-aliases ConfigMap annotation.

That alias required a runtime symlink (created post-startup, lost on PVC
remount) — the same fragile path that's been the historical source of
L2-EVM bootstrap failures. Mainnet's lux-mainnet/luxd-startup.yaml uses
mgj786NP... directly and `cp`'s it as the ag3GR... duplicate; we mirror
that pattern here so the plugin is *always* present without a startup
script writing into a PVC.

The chain-aliases CM annotation about nyGCobir... is now stale and will be
rewritten in the post-bootstrap update.

* feat(genesis): bootstrap-l2 — --existing-subnet-ids to resume after partial bootstrap

When CreateNetworkTx succeeds but the subsequent CreateChainTx fails (or
the tool gets killed mid-flight), the subnet still exists on-chain and
the fee was burned. Re-running the tool without --existing-subnet-ids
would burn another CreateNetwork fee.

Format: --existing-subnet-ids=hanzo:2PkWqv...,zoo:abc...

For each named chain, skip IssueCreateNetworkTx and pass the supplied
subnet ID straight into IssueCreateChainTx. Chains not in the map
continue to create a fresh subnet.

Also surfaces a real operational pattern: load-balanced services like
svc/luxd-headless can pin a kubectl port-forward to a lagging pod whose
state is behind the chain head; the tool must still complete by
re-using the subnet visible on the leader pod.

* feat(genesis): bootstrap-l2 — EVM heartbeat tx + decoupled bootstrap probe

Acceptance criterion 'eth_blockNumber > 0' on a fresh subnet-evm chain
requires injecting the first tx (subnet-evm only seals blocks when a tx
arrives). Adding two new flags:

  --evm-heartbeat-key=<hex>   LUX_PRIVATE_KEY hex (0x9011E888...).
                               Sends a 0-value self-tx after the chain
                               reports info.isBootstrapped=true; the first
                               tx rolls block 1.

  --probe-bootstrap-only      Skip the eth_blockNumber>0 wait. Used when
                               an out-of-band heartbeat (chain-heartbeat
                               CronJob) will roll block 1 later.

The probe is also split into waitBootstrap + probeChain so the heartbeat
fires after isBootstrapped but before eth_blockNumber checks (otherwise
eth_sendRawTransaction returns 'chain not bootstrapped').

Signing path: luxfi/crypto/secp256k1 → ecdsa.PrivateKey → geth/core/types
SignTx with EIP-155 signer scoped to each chain's evmChainID. EVM address
derivation goes through luxfi/crypto/secp256k1.PubkeyToAddress so we never
import an upstream go-ethereum crypto package.

* feat(genesis): bootstrap-l2 — preset chain ID (chain:subnetID:chainID) for resume

Extends --existing-subnet-ids parser to accept an optional third field
<chainID>. When present, both IssueCreateNetworkTx and IssueCreateChainTx
are skipped; the tool only waits for isBootstrapped, sends the heartbeat
(if --evm-heartbeat-key is set), and probes eth_blockNumber.

This is the post-partial-failure resume pattern: if hanzo's subnet+chain
were created successfully in a prior run but zoo onwards failed (or were
never attempted), pass

  --existing-subnet-ids=hanzo:2PkWqv...:rHiiTB...,zoo:abc...

and the tool will skip the hanzo P-chain spend, treat hanzo as already
created, and continue with the next chain.

Also gates the AddChainValidatorTx and post-chain wallet re-sync on
presetChainID being unset, since both are unnecessary in resume mode.

* fix(genesis): canonical 0x prefix on all devnet alloc keys + fail-loud bootstrap-l2 (#72)

Two-part fix for the alloc-key shape that bootstrap-l2 consumes:

## 1. Genesis configs patched at source (4 files)

Every alloc key in:
  - configs/hanzo-devnet/genesis.json
  - configs/pars-devnet/genesis.json
  - configs/spc-devnet/genesis.json
  - configs/zoo-devnet/genesis.json

now matches the canonical /^0x[0-9a-fA-F]{40}$/ shape. The EVM
genesis loader rejects unprefixed keys; previously bootstrap-l2's
in-memory repair was masking the issue. Fixing at source makes the
configs self-describing and stops the "works for me but won't load
on a fresh node" failure mode.

## 2. bootstrap-l2 normalization is now fail-loud (cmd/bootstrap-l2/main.go)

Pre-flight repair upgrades from silent fix to:

  - Normalize 0X → 0x (preserve body case for EIP-55 checksum signal)
  - Add missing 0x prefix (repair, log count)
  - Reject anything that doesn't match /^0x[0-9a-fA-F]{40}$/ AFTER
    repair via log.Fatalf with up to 5 sample keys

Per the user's directive:
  > "normalize alloc keys on load/write or fail loudly before chain
  >  creation. I prefer normalization plus a validation log, not
  >  silent mutation with no signal."

Log line now shows both healthy and repaired counts:
  [devnet] hanzo: alloc keys ok=2 0x-repaired=0 (in-memory only, file unchanged)

A genuinely malformed key (non-hex, wrong length) aborts the entire
bootstrap before any CreateNetworkTx burns LUX:
  [devnet] hanzo: 1 malformed alloc keys (require canonical
                   /^0x[0-9a-fA-F]{40}$/); sample: ["0xZZZ..."]

## 3. Single source of truth: normalizeAllocKey() helper

New pure function returns `(canonical, repaired, valid)`. Every
consumer (the in-memory repair, future validation tooling, any
on-disk rewriter) routes through this one function. Routes:

  - body-case PRESERVED (EIP-55 checksum)
  - prefix-case NORMALIZED (`0X` → `0x`)
  - shape-validated (length 40, hex only)
  - idempotent

## Tests

9 new tests in cmd/bootstrap-l2/main_test.go:

  - already-canonical roundtrip
  - missing-prefix repair flag
  - 0X-prefix normalization
  - body-case preservation (EIP-55 invariant)
  - short body rejection (39/38/empty)
  - long body rejection (41)
  - non-hex rejection (g, _, space)
  - empty string rejection
  - idempotency under repeated normalize calls

All pass. Build clean.
2026-05-29 20:02:37 -07:00
2026-05-25 15:13:09 -07:00
2026-05-25 15:13:09 -07:00

Lux Network Genesis Configurations

This repository contains the canonical genesis configurations for all Lux networks.

Directory Structure

genesis/
├── mainnet/           # LUX Mainnet (Chain ID: 96369)
│   ├── cchain.json    # C-Chain genesis (EVM compatible)
│   ├── pchain.json    # P-Chain genesis (ProtocolVM)
│   ├── xchain.json    # X-Chain genesis (Exchange chain)
│   ├── network.json   # Network configuration
│   └── genesis.json   # Full network genesis
├── testnet/           # LUX Testnet (Chain ID: 96368)
│   ├── cchain.json    # C-Chain genesis (EVM compatible)
│   └── genesis.json   # Full testnet genesis
├── devnet/            # LUX Devnet (Chain ID: 96370) - Fast iteration network
│   └── cchain.json    # C-Chain genesis (EVM compatible)
├── local/             # Local development network (Chain ID: 1337)
│   ├── cchain.json    # C-Chain genesis (EVM compatible)
│   └── genesis.json   # Local network genesis
├── bootstrappers.json # Network bootstrapper nodes
└── checkpoints.json   # Network checkpoints

Mainnet C-Chain Genesis

The mainnet C-Chain was initialized with a single genesis allocation:

Address Balance
0x9011e888251ab053b7bd1cdb598db4f9ded94714 2,000,000,000,000,000,000,000,000,000,000 wei (2T LUX)

Key Parameters

  • Chain ID: 96369
  • Gas Limit: 30,000,000 (0x1C9C380)
  • Min Base Fee: 25 gwei
  • Block Rate: 2 seconds

Network IDs

Network Chain ID Network ID Purpose
LUX Mainnet 96369 96369 Production network
LUX Testnet 96368 96368 Public test network
LUX Devnet 96370 96370 Fast iteration dev network
Local 1337 1337 Local development

Network Configurations

Mainnet (96369)

  • Block Rate: 2 seconds
  • Gas Limit: 30M
  • Min Base Fee: 25 gwei
  • Genesis Account: 0x9011... with 2T LUX

Testnet (96368)

  • Block Rate: 2 seconds
  • Gas Limit: 12M
  • Min Base Fee: 25 gwei
  • Genesis Account: 0x9011... with 2T LUX (same as mainnet)

Devnet (96370)

  • Block Rate: 1 second (faster)
  • Gas Limit: 20M
  • Min Base Fee: 1 gwei (cheaper)
  • Genesis Account: 0x9011... with 2T LUX
  • All upgrades enabled at genesis (Shanghai, Cancun, etc.)

Local (1337)

  • Block Rate: 1 second
  • Gas Limit: 15M
  • Min Base Fee: 1 gwei
  • Genesis Account: 0x9011... with 2T LUX
  • All upgrades enabled at genesis

Usage

These genesis files are used by:

  • luxd node for network initialization
  • coreth for C-Chain configuration
  • Block explorers for genesis block verification
  • Migration tools for state reconstruction

RPC Endpoints

  • Mainnet: http://localhost:9630/ext/bc/C/rpc
  • Testnet: http://localhost:9630/ext/bc/C/rpc (testnet node)
  • Devnet: http://localhost:9630/ext/bc/C/rpc (devnet node)

Genesis Account

All networks use the same production genesis account:

Account Balance Networks
0x9011e888251ab053b7bd1cdb598db4f9ded94714 2T LUX All networks

State Data & RLP Exports

Blockchain state exports and RLP-encoded blocks are maintained in a separate repository:

Repository: github.com/luxfi/state

Available Data

Network Chain ID Blocks RLP Location
Lux Mainnet 96369 1,082,780 rlp/lux-mainnet/lux-mainnet-96369.rlp
Lux Testnet 96368 219 rlp/lux-testnet/lux-testnet-96368.rlp
Zoo Mainnet 200200 799 rlp/zoo-mainnet/zoo-mainnet-200200.rlp
Zoo Testnet 200201 85 rlp/zoo-testnet/zoo-testnet-200201.rlp

Import Workflow

# Clone repos
git clone https://github.com/luxfi/genesis
git clone https://github.com/luxfi/state

# Initialize with genesis
geth init --datadir /path/to/db genesis/mainnet/cchain.json

# Import blocks from state repo
geth import --datadir /path/to/db state/rlp/lux-mainnet/lux-mainnet-96369.rlp

Source Data

The state repo also contains:

  • pebbledb/ - Original SubnetEVM PebbleDB databases
  • docs/ - State verification and recovery documentation
  • pkg/ - Go tools for archaeology, bridge, and scanning
S
Description
Lux Network genesis for Mainnet, Testnet, Devnet, Local/Custom.
Readme BSD-3-Clause
20 MiB
Languages
Go 96.6%
Python 3%
Makefile 0.4%