210 Commits
Author SHA1 Message Date
Hanzo DevandGitHub 0c52be9d9b decomplect: move heavy-dep tests + nest builder/security modules (#12)
This is the third decomplect layer:

1. builder/ now a nested module (github.com/luxfi/genesis/builder)
   — uses luxfi/utxo, vm, database, proto
2. pkg/genesis/security/ now a nested module
   — uses luxfi/consensus for ChainSecurityProfile verification
3. do_transfer_test.go moved from pkg/genesis/ → builder/
   — it imports luxfi/utxo/secp256k1fx which was forcing the entire
   v1.27.x-era dep cascade onto downstream consumers via test-deps
   in the module graph

Result on the root module's MVS-visible graph:
- consensus      v1.25.0  → v1.22.84  (matches luxd v1.23.42 family)
- database       v1.18.3  → v1.17.44  (matches luxd v1.23.42 exactly)
- validators     v1.2.0   → v1.0.0    (matches luxd v1.23.42 exactly)
- threshold      v1.6.17  → GONE      (no longer in graph at all)
- node           v1.27.24 → v1.20.3   (transitive, harmless)

Closes the v1.23.x backport blocker for canonical evmAddr/utxoAddr.

luxd v1.23.43 (genesis bump) can now adopt the canonical genesis
schema without dragging the v1.27.x post-quantum threshold refactor
into a v1.23.x production line.

One-direction module dep:
- github.com/luxfi/genesis          (data types — minimal deps)
- github.com/luxfi/genesis/security (verification — uses consensus)
- github.com/luxfi/genesis/builder  (tx-building — uses utxo/vm/db)
- github.com/luxfi/genesis/cmd      (tools — uses node)

Never the reverse.
2026-05-30 17:38:21 -07:00
Hanzo AI 5f73f2b116 decomplect: move heavy-dep tests + nest builder/security modules
This is the third decomplect layer:

1. builder/ now a nested module (github.com/luxfi/genesis/builder)
   — uses luxfi/utxo, vm, database, proto
2. pkg/genesis/security/ now a nested module
   — uses luxfi/consensus for ChainSecurityProfile verification
3. do_transfer_test.go moved from pkg/genesis/ → builder/
   — it imports luxfi/utxo/secp256k1fx which was forcing the entire
   v1.27.x-era dep cascade onto downstream consumers via test-deps
   in the module graph

Result on the root module's MVS-visible graph:
- consensus      v1.25.0  → v1.22.84  (matches luxd v1.23.42 family)
- database       v1.18.3  → v1.17.44  (matches luxd v1.23.42 exactly)
- validators     v1.2.0   → v1.0.0    (matches luxd v1.23.42 exactly)
- threshold      v1.6.17  → GONE      (no longer in graph at all)
- node           v1.27.24 → v1.20.3   (transitive, harmless)

Closes the v1.23.x backport blocker for canonical evmAddr/utxoAddr.

luxd v1.23.43 (genesis bump) can now adopt the canonical genesis
schema without dragging the v1.27.x post-quantum threshold refactor
into a v1.23.x production line.

One-direction module dep:
- github.com/luxfi/genesis          (data types — minimal deps)
- github.com/luxfi/genesis/security (verification — uses consensus)
- github.com/luxfi/genesis/builder  (tx-building — uses utxo/vm/db)
- github.com/luxfi/genesis/cmd      (tools — uses node)

Never the reverse.
2026-05-30 17:38:01 -07:00
Hanzo DevandGitHub 0f6bec3e2f decomplect: split cmd/ to nested module — root has no luxfi/node (#11)
* decomplect: move SecurityProfile Resolve to pkg/genesis/security

pkg/genesis core types (SecurityProfile struct) stay free of
luxfi/consensus dep. The verification gate (ResolveProfile + hash
comparison) moves to pkg/genesis/security which imports both.

Result: downstream consumers that only need to parse canonical
genesis data (luxd v1.23.x line, tools, indexers) can do so without
dragging luxfi/consensus + luxfi/threshold + luxfi/validators
(Corona→Corona refactor) into their go.mod.

go list -deps ./pkg/genesis → no luxfi/consensus.
go test ./pkg/genesis/security/ → all 5 F102 verification tests pass.

Closes the dep cascade that blocked the v1.23.x backport of
evmAddr/utxoAddr canonical genesis. One and one way only:
data lives in pkg/genesis, verification lives in pkg/genesis/security,
consensus consumes both — never the reverse.

Closes #93 Phase 1.

* decomplect: split cmd/ into nested module (no luxfi/node in root go.mod)

The Phase 1 split (SecurityProfile struct in core, Resolve in
pkg/genesis/security subpackage) addressed package-level dep cycles,
but the genesis root go.mod still required luxfi/node v1.27.24
because cmd/bootstrap-l2 + cmd/derive100 import node directly for
wallet/tx-builder bits.

This commit moves cmd/ to a nested module
(github.com/luxfi/genesis/cmd) with its own go.mod that holds the
luxfi/node dep. Root go.mod no longer requires luxfi/node, breaking
the MVS cascade that was forcing luxd v1.23.x consumers to upgrade
28 packages just to bump genesis.

Result:
- go list -m all on root no longer surfaces luxfi/node directly
  (still transitively visible at v1.23.x-compatible versions, not the
  v1.27.x bloat)
- cmd/ retains the same source layout; tools still build via cmd/go.mod
- pkg/genesis core API unchanged
- pkg/genesis/security gate unchanged

Two-module separation:
- github.com/luxfi/genesis        — data types + verification primitives
- github.com/luxfi/genesis/cmd    — tools that need luxfi/node

One direction, no reverse import.

Closes the structural blocker for v1.23.x backport of canonical
evmAddr/utxoAddr.
2026-05-30 17:28:13 -07:00
Hanzo DevandGitHub d00198590e refactor: XAssetID → UTXOAssetID (genesis builder + bootstrap-l2) (#10) 2026-05-30 14:34:07 -07:00
Hanzo DevandGitHub 514e996737 refactor: XAssetID → UTXOAssetID (genesis builder + bootstrap-l2) (#9) 2026-05-30 14:30:59 -07:00
Hanzo DevandGitHub 57daf793e1 canonical evmAddr/utxoAddr only — drop legacy luxAddr/ethAddr shim (#8)
Per "one and one way only" — AllocationJSON now emits and accepts ONLY
the canonical evmAddr/utxoAddr field pair. The legacy luxAddr/ethAddr
aliases that were dual-emitted to keep luxd v1.23.x happy are gone.

Consumers of this package (luxd, genesis tool, gen-localnet) must be on
a build that reads the canonical names. Coordinated with luxd v1.23.43
which already renamed Allocation.{LUXAddr,ETHAddr} → {UTXOAddr,EVMAddr}
on main / chore/kill-fuji.

Devnet genesis: drop 3 ghost InitialStakers (LnEy5o1NPzpmSy56uavuoMDL9oRcMjHjs,
Pubc76XYx1BQCqZreJUUoAaXgmmEzapSz, N5DAqzfZpSqkthaCtdfgkyx7m8ERut16A) so
8-staker genesis matches 5-pod sybil quorum. Closes P-chain ratification
stall at numValidators=4 observed during devnet bootstrap.

Tests: pkg/genesis, configs, builder, cmd/bootstrap-l2 all pass.
2026-05-30 09:21:04 -07:00
Hanzo AI 379ff773e5 canonical evmAddr/utxoAddr only — drop legacy luxAddr/ethAddr shim
Per "one and one way only" — AllocationJSON now emits and accepts ONLY
the canonical evmAddr/utxoAddr field pair. The legacy luxAddr/ethAddr
aliases that were dual-emitted to keep luxd v1.23.x happy are gone.

Consumers of this package (luxd, genesis tool, gen-localnet) must be on
a build that reads the canonical names. Coordinated with luxd v1.23.43
which already renamed Allocation.{LUXAddr,ETHAddr} → {UTXOAddr,EVMAddr}
on main / chore/kill-fuji.

Devnet genesis: drop 3 ghost InitialStakers (LnEy5o1NPzpmSy56uavuoMDL9oRcMjHjs,
Pubc76XYx1BQCqZreJUUoAaXgmmEzapSz, N5DAqzfZpSqkthaCtdfgkyx7m8ERut16A) so
8-staker genesis matches 5-pod sybil quorum. Closes P-chain ratification
stall at numValidators=4 observed during devnet bootstrap.

Tests: pkg/genesis, configs, builder, cmd/bootstrap-l2 all pass.
2026-05-30 09:20:44 -07:00
Hanzo DevandGitHub 08e9e6904e chore: update 2026-05-29 23:33:13 -07:00
Hanzo AI 144cfed249 chore: update 2026-05-29 23:29:11 -07:00
Hanzo DevandGitHub 770c12f3ab chore(genesis): kill subnet — rename to chainID across genesis tool surface (#6)
Per CLAUDE.md "translate upstream Lux's 'subnet' out immediately":
zero remaining `subnet|Subnet|SUBNET` occurrences in lux/genesis Go +
JSON files. All eleven were comment-level cruft or one struct field
that hadn't been renamed yet.

## Changes

`cmd/bootstrap-l2/main.go`:
- Top-of-file vocabulary block updated — kills the "upstream
  CreateSubnetTx" + "ConvertSubnetToL1Tx" framing in favor of the
  canonical three-name trinity: `chainID` (chain-owner network),
  `blockchainID` (blockchain's own ID), `evmChainID` (EIP-155).
- `platformBlockchain.SubnetID` → `ChainID`; JSON tag
  `"subnetID"` → `"chainID"`.
- `resultChain.ChainOwnerID` → `ChainID`; JSON tag
  `"chainOwnerId"` → `"chainId"`.
- Inline struct in `existingByName` map: `ChainOwnerID` →
  `ChainID`, log line reads `chainID=` instead of `ownerID=`.
- All callsites updated.

`cmd/genesis/main.go`, `pkg/genesis/keys.go`,
`pkg/genesis/allocations.go`:
- Four comment-level rephrases: "subnet-bootstrap CLI" → "chain-
  bootstrap CLI", "subnet creation operations" → "chain creation
  operations".

## Output JSON wire change

The bootstrap result file emitted by `--output` now uses
`{chainId, blockchainId, evmChainId}` per chain instead of
`{chainOwnerId, blockchainId, evmChainId}`. Downstream consumers
(universe operator, chainboot, anything that reads
devnet-l2-live.json) MUST update their parsers — this is a
breaking wire change in the file format, intentional.

The platform.getBlockchains JSON-RPC method's wire format stays
upstream-shaped on the way IN; our parser now decodes from
`"chainID"` (post-rename in lux/node `APIBlockchain`). If running
against an older lqd that still emits `"subnetID"`, the field
silently zero-values — which is the correct safety behavior (the
caller will fall through to the create path and the operator gets
a re-create-attempt failure rather than a silent ghost reuse).

## Verified clean

  grep -rnE "subnet|Subnet|SUBNET" --include="*.go" --include="*.json"
  # zero matches in lux/genesis

Build clean. 9 normalizeAllocKey tests still pass.
2026-05-29 21:07:57 -07:00
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
Hanzo AI 1bf501799b feat(genesis): bake all upgrade timestamps + 43 canonical precompiles
Genesis files now self-describe the network's full capability surface.
No more CR-level chainUpgradeConfig overrides needed at runtime; if you
boot luxd with --genesis-file=<any of these>, you get Etna/Durango/
Fortuna/Granite + 43 precompiles automatically.

Activated upgrade timestamps (all set to 0 — active from genesis):
  evmTimestamp, durangoTimestamp, etnaTimestamp, fortunaTimestamp,
  graniteTimestamp

Activated precompiles (43, all at blockTimestamp 0 — sourced from
ConfigKey constants in ~/work/lux/precompile/*/module.go):
  ai, anchor, attestation, babyjubjub, blake3,
  bls12381 (G1Add, G1Mul, G1MSM, G2Add, G2Mul, G2MSM, Pairing),
  bridgeRegistrar, cggmp21, computeMarket, coronaThreshold (renamed
  from coronaThreshold), curve25519, dex, ecies, fhe, fixedPointMath,
  frost, graph, hpke, hqcEncapsulate, magnetar, mldsa, mlkem,
  p3q, pasta, pedersen, poseidon, pqcrypto, pulsar, ring, router,
  slhdsa, sr25519, stableSwap, vrf, x25519, xwing, zk

WARP config: blockTimestamp=0, quorumNumerator=67,
requirePrimaryNetworkSigners=false (sovereign signers — works pre-
and post-L1-conversion).

Applied to all 15 chain genesis files: C/hanzo/zoo/pars/spc on
mainnet/testnet/devnet. Each produces a deterministic genesis hash.
2026-05-27 01:35:15 -07:00
Hanzo AI 41cfd7827a fix(genesis): pars 7B LUX (chainId 7070), spc 11B LUX (chainId 36911)
Previous commit ca7637c incorrectly applied hanzo/zoo's 2T LUX template.
Canonical amounts per ~/work/pars/genesis/networks/mainnet/genesis.json
(7B confirmed) and product spec (spc=11B). Treasury still 0x9011E...
(LUX_MNEMONIC m/44'/9000'/0'/0/5 derived).

Re-bootstrapped on mainnet 2026-05-25 with corrected genesis:
  pars: 2M4yxnBoLi... validator-set, 2wYWjBHziT... blockchain ID (parsv4)
  spc:  2LN6THJWF1... validator-set, 2YTuJW9b1k... blockchain ID (spcv4)
2026-05-25 23:13:30 -07:00
Hanzo AI ca7637ca9a fix(genesis): pars/spc mainnet → LUX_MNEMONIC-funded treasury (0x9011E...)
Both pars-mainnet and spc-mainnet shipped with placeholder allocs:
  - pars-mainnet: chainId 494949 in file but on-chain reports 0x1b9e (7070);
    treasury was 0x12c6EE1d... w/ 16M LUX, not LUX_MNEMONIC.
  - spc-mainnet: chainId 36911 (matches), but treasury 0x12c6EE1d...
    w/ 16M LUX — also not LUX_MNEMONIC.

Both block 0 hashes are identical (0x4dc9fd5cf4...) — they were
bootstrapped from the same stub genesis.

Fix mirrors hanzo-mainnet pattern: alloc 0x9011E888251AB053B7bD1cdB598Db4f9DEd94714
(LUX_MNEMONIC treasury, derives from K8s secret lux-deployer.LUX_PRIVATE_KEY)
with 0x193e5939a08ce9dbd480000000 wei balance.

Re-bootstrap required on mainnet — both chains are at block 0x0, no
state loss. New blockchain IDs will be issued via CreateChainTx; the
chain aliases 'pars' and 'spc' will repoint via chain-aliases ConfigMap.
2026-05-25 22:56:32 -07:00
Hanzo AI 4a0f19e47e chore: update 2026-05-25 15:13:09 -07:00
Hanzo AI 4b1cdad100 keys: fix call sites — keccakBytes wraps Sum→slice properly 2026-05-23 16:27:05 -07:00
Hanzo AI 66ddf3ca26 keys: keccakBytes helper — Go disallows slicing function-returned arrays 2026-05-23 16:25:29 -07:00
Hanzo AI f6ba5aef36 consume crypto/keccak256 (was crypto/keccak) — bump crypto v1.19.14 2026-05-23 16:21:45 -07:00
Hanzo AI 8ef8e731d4 keys: use crypto/keccak directly — drop crypto.PubkeyToAddress wrapper
luxfi/crypto/keccak is the canonical Keccak-256 subpackage (parallel to
secp256k1). Replaces:

  crypto.PubkeyToAddress(pub.PublicKey)
    →
  keccakAddr(pub.PublicKey.X, pub.PublicKey.Y)

where keccakAddr is a 5-line helper that says exactly what the EVM
address derivation is:

  buf := make([]byte, 64)
  pub.X.FillBytes(buf[:32])  // big-endian, zero-padded
  pub.Y.FillBytes(buf[32:])
  h := keccak.Sum256(buf)
  return h[12:]

The local keccak256(...) helper now routes through keccak.Sum256 (one
hashing path, no sha3 import for that primitive).
2026-05-22 21:19:55 -07:00
Hanzo AI 28e520ef7e decomplect: drop evmcrypto/utxocrypto aliases — use crypto + secp256k1 by name
dropping aliases on github.com/luxfi/crypto and github.com/luxfi/crypto/secp256k1.
no name collisions with stdlib (validator_keys.go's crypto/rand is rand, not crypto).
also purges remaining ethAddr / luxAddr / luxKey local vars (renamed to addr /
utxoAddr / secpKey).
2026-05-22 21:05:17 -07:00
Hanzo AI c114107bac purge remaining eth* identifiers — single canonical EVMAddr/UTXOAddr
ParseETHAddress → ParseEVMAddress
  ValidatorKeyInfo.EthAddr → .EVMAddr
  local ethAddr / EthAddr vars → evmAddr / EVMAddr
  ethPrivKey / ethShortID / ethAddrStr → evmPrivKey / evmShortID / evmAddrStr
  privateKeyToETHAddress → privateKeyToEVMAddress
  comments: "Get ETH address" → "Get EVM address", "Ethereum address" → "EVM address (H160 hex)"
  test error strings + test loop vars also updated for consistency

The only ETH/Ethereum reference remaining is the upstream `ethcrypto`
import alias (github.com/luxfi/crypto, aliased ethcrypto) — that's an
external package name, kept as-is.
2026-05-22 20:56:48 -07:00
Hanzo AI 462fa1a206 drop ethAddr/luxAddr string tags — canonical is evmAddr/utxoAddr 2026-05-22 20:40:50 -07:00
Hanzo AI 8948b4e1cb EVMAddr (Go) + UTXOAddr (Go) — pure chain-model naming, no eth/lux branding
Renames Go struct field ETHAddr → EVMAddr across types.go, keys.go,
allocations.go, validator_keys.go, config.go, builder/builder.go,
cmd/genesis, cmd/checkkeys. JSON tags stayed evmAddr already (from
v1.12.6). Result: matched pair EVMAddr+UTXOAddr on both Go and JSON
sides.

Kept ParseETHAddress as the input-parsing function name — it describes
what it parses (an Ethereum-format hex address), not the field it
populates (an EVMAddr).

LLM.md updated to state the canonical pair plainly; no rename history.
2026-05-22 20:37:30 -07:00
Hanzo AI 2a829c0af2 JSON tags: ethAddr → evmAddr (chain-model name, paired with utxoAddr)
Go field stays ETHAddr (names the H160 byte format, which IS Ethereum's
address spec — we're wire-compatible, not branded). JSON tag changes to
evmAddr so the user-facing pair (evmAddr / utxoAddr) describes our chain
models symmetrically without branding us as Ethereum.

This is the hybrid that fell out of the linter pass: Go identifier =
format-name, JSON tag = chain-model-name. Documented in LLM.md so we
don't regress.

Touches: pkg/genesis/{types,keys,allocations,validator_keys,config}.go,
configs/*/genesis.json + pchain.json + getgenesis_test.go,
scripts/gen-localnet.py. Tests green.
2026-05-22 20:10:48 -07:00
Hanzo AI 11e221b5ce configs/devnet: align wallets to 100M/wallet — 50M initialAmount + 50M unlock@0 (matches testnet) 2026-05-22 00:06:43 -07:00
Hanzo AI 28d59d9dcc docs: refresh testnet+devnet C-Chain hashes to current canonical
Live deployed hashes verified byte-equal to current canonical cchain.json:
  testnet: 0xfc909f7e992d9cb91485f114f6d333f3823a12f2c72bbf51ed2c8eea749b2d2e
  devnet:  0x836f6053473e4331bb347afc45b641f12075c63a302f4e56e64239a3ba4acd4b

Previous values were from before commits ae1c52b (Shanghai/Cancun/Prague
at genesis) and 88b6561 (gasLimit 1B). testnet+devnet were both "block 0
fresh" with no committed historical state, so the hash update is in-spec.

Mainnet hash unchanged — its cChainGenesis is locked by the 1.08M-block
historical chain.
2026-05-21 18:43:51 -07:00
Hanzo AI 8bf3304490 configs/devnet: bump wallet unlock amount 10M → 50M to match testnet
devnet/genesis.json (the bundled blob mounted into pod ConfigMaps)
was stale relative to devnet/pchain.json — sharded source had 50M,
bundled blob had 10M. testnet/genesis.json already had 50M.

Surgical sed substitution on the wallet unlock amount; validator
stake allocations (3 unlock periods at 1B nLUX each) untouched.
2026-05-21 18:02:11 -07:00
Hanzo AI 9eed1b8791 no backcompat — canonical address fields are ethAddr + utxoAddr
Renames Allocation.EVMAddr → ETHAddr (json tag `ethAddr`) and
KeyInfo.EVMAddr → ETHAddr across pkg/genesis, builder, cmd/genesis,
cmd/checkkeys. AllocationJSON drops the legacy `evmAddr` tag entirely
— there is no aliasing, no UnmarshalJSON normalizer, no fallback.
JSON files and Go code share one canonical pair: ethAddr + utxoAddr.

configs/getgenesis_test.go updated to assert against the canonical
field. parseAllocations error message says "invalid eth address"
to match the new identifier.

Tests:
  ok  github.com/luxfi/genesis/builder
  ok  github.com/luxfi/genesis/configs
  ok  github.com/luxfi/genesis/pkg/genesis
2026-05-21 12:51:40 -07:00
Hanzo AI 526ce8b641 kill F12/F30 nid-hardened derivation paths (no slop)
Removed:
- LoadKeysFromMnemonic's `nid uint32` parameter (was ignored)
- BuildWalletAllocations (replaced by canonical BuildBIP44WalletAllocations)
- BuildWalletKeyHex (no callers after removing the nid-hardened scheme)
- deriveLuxAccount helper (m/44'/9000'/nid'/...) — only used by removed code
- genesis_hd_branches_test.go entire file (tested abandoned NID-hardening
  requirement; canonical Avalanche/Lux BIP44 is intentionally nid-stable
  so the same mnemonic gives the same addresses in every wallet)

TestTransferPChain now skips by default — opts in via LUX_LIVE_TESTS=1
(it hits a live api.lux-dev.network endpoint, not appropriate for unit-test
runs that fail when CF returns 522).

cmd/checkkeys/main.go: dropped unused NETWORK_ID env handling.
cmd/genesis/main.go: BuildWalletAllocations → BuildBIP44WalletAllocations.

All tests pass in clean offline mode now:
  configs    OK (TestPrimaryChainShards_PerChainCanonicalChainID green)
  pkg/genesis OK
  builder    OK
2026-05-21 12:30:15 -07:00
Hanzo AI 8d127d7d50 drop accidental local-backup.json 2026-05-21 10:43:50 -07:00
Hanzo AI 614a994402 kill all legacy/backward-compat surfaces
- types.go: drop AllocationJSON.UnmarshalJSON legacy alias remapping;
  drop UnparsedAllocation/UnparsedStaker type aliases; drop
  formatBech32WithChain deprecated helper.
- configs: strip ethAddr/luxAddr/avaxAddr/xAddr legacy fields from
  every shipped JSON shard; rename to canonical evmAddr/utxoAddr only.
- configs: add top-level chainId field to every letter-chain shard
  (mainnet/testnet/devnet/localnet — the test expected this).
- localnet/dev: rebuild allocations to canonical 50M LUX per account
  on X + 50M LUX per account on P (matches mainnet/testnet/devnet).
- tests: TestFormatBech32WithChain → TestChainPrefixFormat_KnownVectors,
  TestGetGenesisLocalnet expects 50M not 10M per allocation,
  field references updated to evmAddr/utxoAddr.

One-and-only-one-way enforced. No deprecated aliases. New code MUST use
EVMAddr/UTXOAddr, ChainPrefix.Format, UTXOAssetIDFor.
2026-05-21 10:43:42 -07:00
Hanzo AI 4daaa65505 configs(mainnet,testnet): add xchain.json shards (keep X-Chain baked)
Regenerate mainnet/testnet genesis.json via GetGenesis() so the merged
output now embeds xChainGenesis (the small asset descriptor shard
{"symbol":"LUX","name":"Lux","denomination":9}) alongside the existing
cChainGenesis and the seven letter-chain shards (a/b/d/g/k/q/t/z).

The per-network xchain.json shard files already existed; the on-disk
genesis.json snapshots had drifted (predated the post-decomplect
shard schema) so the xChainGenesis field was missing on mainnet and
testnet. Devnet and localnet already had it baked.

Why this matters: luxd v1.27+ X-Chain-optional builder treats absent
xChainGenesis as "skip X-Chain bake". Every existing wallet and
indexer expects X-Chain to be live on mainnet/testnet, so the shard
must remain baked.

Verification:
  $ jq -r '.xChainGenesis' configs/{mainnet,testnet}/genesis.json
  {"symbol":"LUX","name":"Lux","denomination":9}
  {"symbol":"LUX","name":"Lux","denomination":9}

Tests: TestGetGenesis_XChainShardPresentEmbedsXChainGenesis and
TestGetGenesis_AllPrimaryChainsBakedIn pass for {mainnet,testnet,
devnet,localnet}; builder/* unchanged and green.
2026-05-21 03:45:02 -07:00
Hanzo AI 419cea1b43 pkg/genesis: ChainPrefix type — decomplect chain prefix from HRP
formatBech32WithChain("P", hrp, addr) braided chain identity into bech32
formatting at every call site. Chain prefix (P-/X-) is per-chain metadata;
HRP (lux/test/dev/local) is a per-network parameter — they should be
orthogonal.

Introduce ChainPrefix as a named string type with PChainPrefix / XChainPrefix
constants and a Format(hrp, addr) method that delegates to luxfi/address.
Call sites compose by method invocation: PChainPrefix.Format(hrp, addr[:]).

The deprecated formatBech32WithChain is kept as a thin alias for any
remaining caller; output bytes are identical to the pre-decomplect form.
2026-05-21 03:36:41 -07:00
Hanzo AI 97aa05c4da pkg/genesis: ParseAddress rejects non-lux HRPs (avaxAddr footgun) 2026-05-21 03:34:15 -07:00
Hanzo AI 09558bb2e3 rename ETHAddr→EVMAddr, LUXAddr→UTXOAddr (uniform UTXO/EVM split)
Address types now reflect the actual two-address model that exists across
all chains:
  - EVMAddr (0x H160) — C-Chain and every other EVM chain
  - UTXOAddr (bech32, P-/X- prefix interchangeable) — P-Chain and X-Chain

The old names (ETHAddr / LUXAddr / avaxAddr / xAddr) confused two
orthogonal axes (Ethereum-vs-Lux brand × address-encoding-type) with
each other. UTXO/EVM is the actual technical distinction.

Backward compat:
  - JSON: AllocationJSON.UnmarshalJSON accepts evmAddr/ethAddr +
    utxoAddr/xAddr/luxAddr/avaxAddr (legacy upstream Avalanche).
  - Existing config files: bumped with both new + legacy fields present.

Builds + tests green on luxfi/genesis and luxfi/node/genesis/builder.

Also: each allocation gets 50M LUX on X (initialAmount) + 50M LUX on P
(unlockSchedule locktime=0). For 1000 accounts: 50B on each chain →
100B total per env across P+X. mainnet legacy retained at-is (1.08M
historic blocks reference original asset IDs).
2026-05-20 16:10:50 -07:00
Hanzo AI 1fdd56807b fix(testnet,devnet/pchain): migrate allocations from initialAmount-only to unlockSchedule
luxd v1.23.31 builder.go only iterates `unlockSchedule` to build P-Chain
platform allocations. Allocations with only `initialAmount` (and
`unlockSchedule: null`) are silently dropped — supply gets counted but
no UTXOs materialize at the addresses. This blocks any P-Chain operation
(staking, subnet creation, token transfers) on testnet/devnet because
the canonical deployer addresses report balance=0 despite genesis claim.

Mainnet works around this because every allocation has explicit
unlockSchedule entries (vesting locks). testnet/devnet had compact
"initialAmount: 10M, unlockSchedule: null" entries.

Fix: migrate 995 testnet + 995 devnet allocations to use
`{initialAmount: 0, unlockSchedule: [{amount: 10M, locktime: 0}]}`.
Same total amount, immediately unlocked at locktime=0, now properly
loaded into platformAllocations by the builder.

Also fixed initialStakedFunds to point at allocations[0..4].luxAddr
(the BIP44 m/44'/9000'/0'/0/{0..4} derivation) instead of the validator
reward addresses, matching the working mainnet pattern.

Verified: testnet/devnet alloc[10] now shows 10M LUX queryable balance;
4 canonical subnets deployed successfully on each (zoo 200201/200202,
hanzo 36962/36964, spc 36910/36912, pars 494950/494951).
2026-05-20 15:06:13 -07:00
Hanzo AI 9fa3c444e7 fix(local): C-Chain chainId 1337 → 31337 (canonical localnet)
Per CLAUDE.md letter-chain table localnet C-Chain uses chainId=31337
(matches geth/hardhat/foundry localdev convention while staying in
Lux's per-network 31337+110·Δ namespace). The primary network ID stays
at 1337 (P-Chain / X-Chain).

initialStakers in local/genesis.json remain a self-consistent 3-validator
template (PoPs verify); reproducible local boot is owned by netrunner /
`lux network start --local`, not by this file.
2026-05-19 21:57:19 -07:00
Hanzo AI 31c680c842 fix(testnet,devnet/pchain): match initialStakers to deployed BLS keys
Same fix as v1.11.4 (mainnet) — genesis initialStakers were pointing at
mainnet's BLS pubkeys/PoPs instead of the keys actually deployed in each
network's k8s secret luxd-staking. luxd's bootstrap health check fails
with "node has BLS key X, but is registered to the validator set with Y"
when the two diverge.

testnet: rebuilt 5 stakers from /tmp/lux-staking-keys-testnet/node{0..4}
  (NodeID-Mf3JfSY..., -2TwSZ2..., -Ld9VFB..., -8mY2fh..., -4smuFz5Z...)
devnet:  rebuilt 8 stakers from /tmp/lux-staking-keys-devnet/node{0..7}
  (NodeID-88PFzu..., -DLV6Z1..., -HVN3xX..., -6HKaoo..., -4uZ5a5...,
   -LnEy5o..., -Pubc76..., -N5DAqz...)

All pubkey+PoP pairs self-verify via bls.VerifyProofOfPossession over
luxfi/crypto's bls.SignProofOfPossession output.
2026-05-19 21:51:35 -07:00
Hanzo AI a9fdd7e07f fix(mainnet/pchain): regenerate node4 BLS keypair via bls.NewSecretKey
Previous PoP for NodeID-DwsrqSkPoE3pXWrUt9nkJ5yBycwRQ246X was generated
from random 32 bytes (openssl rand) read as a BLS secret. luxfi/crypto/bls
SecretKeyFromBytes silently accepts non-valid scalars but produces
inconsistent pubkey/signature derivations, causing the genesis loader's
VerifyProofOfPossession to fail with "invalid proof of possession".

The fix: regenerate signer.key with bls.NewSecretKey() (which runs
proper KeyGen[KeyG1SigG2] over the BLS scalar field) and derive the
PoP from that. PoP self-verifies via bls.VerifyProofOfPossession.

All 5 mainnet validators now boot, P/C bootstrap to healthy, and the
luxd health check reports "node has the correct BLS key".
2026-05-19 21:37:57 -07:00
Hanzo AI f1ed93216f mainnet: update initialStakers to match deployed validator BLS keys
The 5 deployed validators (luxd-{0..4} in lux-mainnet ns) auto-generated
their staking certs + BLS signer keys on first boot. Their actual NodeIDs
and BLS pubkeys are now baked into genesis:

  NodeID-Mf3JfSY91oDwfBqf7rCLmhg4NDtDghw1f (luxd-0)
  NodeID-2TwSZ2oyeBK2mv7JiseEQ8m74rotDj4QR (luxd-1)
  NodeID-Ld9VFBQ9zGbd79z2vzaAqkQ3jHuqbtRpo (luxd-2)
  NodeID-8mY2fhUehN27v3LCU84BnnKEoeRfd2weC (luxd-3)
  NodeID-DwsrqSkPoE3pXWrUt9nkJ5yBycwRQ246X (luxd-4, regenerated)

This unblocks consensus — validators can now sign messages with the BLS
keys that match what genesis registered them with. Previously the 5th
NodeID was `4smuFz5Z…` which had no matching live key.

Note: NodeID is derived from random ECDSA P-256 staker.crt at first
boot; it is NOT mnemonic-deterministic. The keys must be persisted to
KMS to survive future pod restarts (Task #52).
2026-05-19 21:11:16 -07:00
Hanzo AI 866052b401 fix(mainnet/cchain): align feeConfig.gasLimit with block gasLimit
Both values must match for historic RLP import. Previously:
- block gasLimit: 0xb71b00 (12,000,000)
- feeConfig.gasLimit: 1,000,000,000 (1B)

Mismatch caused admin_importChain to reject RLP blocks with:
"invalid gas limit: expected to be 1000000000 in EVM, but found 12000000"

Set feeConfig.gasLimit = 12,000,000 (matches block header + historic
RLP). Note: this changes the cChainGenesis hash → new C-Chain blockchain
ID after bootstrap, but acceptable since chain has 0 blocks currently.
2026-05-19 14:45:06 -07:00
Hanzo AI ab32126454 fix: strip bogus EVM chainId fields from non-EVM letter chains (A/B/D/G/K/Q/T/Z run their own VMs — AIVM/BridgeVM/DexVM/GraphVM/KeyVM/QuantumVM/ThresholdVM/ZKVM — not EVM). Only C-Chain has an EVM chainID (mainnet=96369, testnet=96368, devnet=96370). 2026-05-19 11:48:46 -07:00
Hanzo AI 2259308ffd feat: bake all 10 primary letter-chains into mainnet/testnet/devnet/localnet genesis
Every primary network now ships all 10 chain shards (X, C, D, Q, A, B, T, Z, G, K).
Each chain spawns at startup with zero post-launch CreateBlockchainTx — chain set
is fully data-driven by which shards the operator ships.

Per-letter EVM chainIds locked at 96369 + 100*Δ_from_C (mainnet base) with the
familiar testnet=-1 / devnet=+1 triple inherited from C-Chain:

  C 96369/96368/96370   D 96469/96468/96470   Q 96569/96568/96570
  A 96669/96668/96670   B 96769/96768/96770   T 96869/96868/96870
  Z 96969/96968/96970   G 97069/97068/97070   K 97169/97168/97170

Localnet uses a parallel 31337 + 110*Δ series.

F-Chain ≡ T-Chain (threshold FHE) and M-Chain ≡ K-Chain (KMS-MPC) wired in via
builder alias lists — letter taxonomy decomplected from canonical VM IDs.

Each shard carries:
  - chainId / networkId / timestamp (chain identity at boot)
  - vm + description (operator transparency)
  - per-VM seed extras (DEX fee config, T-Chain DKG params, B-Chain signer set,
    Q-Chain pq scheme pin, K-Chain MPC topology — all empty/default initial state)

Contract locked by configs.TestGetGenesis_AllPrimaryChainsBakedIn — every primary
network must ship all 10 letter-chain shards. Removing a chain on any network is
now a load-bearing test edit, not a silent operator switch.

Mainnet C-Chain invariants preserved: chainId=96369, genesisHash unchanged,
stateRoot=0x2d1cedac…, warp precompile in alloc, warpConfig.blockTimestamp=
1730446786, treasury 2T LUX.
2026-05-19 11:23:16 -07:00
Hanzo AI ae1c52b32a decomplect: activate Shanghai/Cancun/Prague at genesis (timestamp=0); was far-future placeholder for non-existent gating 2026-05-19 07:06:24 -07:00
Hanzo AI 8bd9aabc14 decomplect: strip upgrade timestamps from genesis JSONs (apricotPhase*/banff/cortina/durango/etna/fortuna/granite); JSON re-sorted alphabetically for determinism
Phase 4 (genesis slice) of the upstream-upgrade purge per
~/work/lux/proofs/UPGRADE_RIP.md.

Stripped 90 upgrade-name timestamp keys across 18 genesis files:
- mainnet/cchain.json (6 keys, including banffBlockTimestamp etc.)
- testnet/cchain.json (4 keys)
- devnet/cchain.json (4 keys)
- dev/cchain.json + local/cchain.json + localnet/cchain.json (4 each)
- mainnet/genesis.json (cChainGenesis-nested JSON string also stripped)
- hanzo-{mainnet,testnet,devnet}/genesis.json (4 each)
- pars-{mainnet,testnet,devnet}/genesis.json (4 each)
- spc-{mainnet,testnet,devnet}/genesis.json (4 each)
- zoo-{mainnet,testnet,devnet}/genesis.json (4 each)

Files re-sorted alphabetically by key (Python json.dump sort_keys=True)
for deterministic diff and matching the canonical jq -S layout. C-Chain
state roots are unchanged — the stripped fields are runtime activation
rules, not state inputs (state root depends on alloc + timestamp +
chainId + gasLimit + baseFee, all preserved verbatim).

Old chaindata wire compat: chain config JSON shape changes, but state
roots match. Per the activate-all-implicitly + no-compat-shims directive.
2026-05-18 22:51:21 -07:00
Hanzo AI 48bc8f91f7 rename: github.com/luxfi/protocol → github.com/luxfi/proto (cascade complete) 2026-05-18 21:29:01 -07:00
Hanzo AI b5e794f47b deps: pin luxfi/proto v1.0.0 (rename cascade complete) 2026-05-18 21:27:29 -07:00
Hanzo AI e263161762 rename: github.com/luxfi/protocol → github.com/luxfi/proto (imports + go.mod replace) 2026-05-18 21:19:09 -07:00
Hanzo DevandGitHub fe9b64885e Merge pull request #4 from luxfi/chore/bump-consensus-1.23.25
deps: bump consensus v1.23.25 + gofmt
2026-05-16 18:00:00 -07:00
Hanzo AI 037147d3bd deps: bump consensus v1.23.5 -> v1.23.25 + gofmt cleanup
Closes the v1.9.14 CI build failure: pkg/genesis/security_profile_test.go
references consensusconfig.StrictPQ()/ProfileStrictPQ which were only
introduced when the Lux prefix was dropped from profile identifiers.
That symbol first ships in consensus v1.23.25.

Also fixes the gofmt drift on builder/builder.go, cmd/genesis/main.go,
and pkg/genesis/keys.go that was failing the Test job.
2026-05-16 17:59:42 -07:00