Commit Graph
184 Commits
Author SHA1 Message Date
z 1e1dbbeef1 docs(brand): add hero banner 2026-06-28 20:36:29 -07:00
z 36cad5989d chore(brand): dynamic hero banner 2026-06-28 20:36:28 -07:00
zeekay 43294cc7ba configs: scrub strictPQTimestamp from all upgrade.json
The strictPQTimestamp networkUpgradeOverride drove the EVM strict-PQ
precompile-refusal gate, which is removed (luxfi/evm enable-everything:
the field no longer exists on extras.NetworkUpgrades, so it is silently
ignored). Drop it from every env's upgrade.json so the canonical config
matches the new policy — builder precompile surface enables everything;
Lux PQ security is at consensus, not by refusing EVM verifiers.
2026-06-27 21:24:09 -07:00
zeekay fcb7acd636 decomplect: retire pqcrypto bundle (0x9003) — standalone mldsa/mlkem/slhdsa is the one way
Network reboot, no backward compat: delete the AI-slop pqcrypto bundle + every
gate pinning it (pqcryptoConfig genesis activation, relaunch-safety test pin).
ML-KEM stays via standalone mlkem (0x012201). pqcryptoConfig removed from all
upgrade.json/yaml; 0x9003 retired, never reuse.
2026-06-27 16:08:48 -07:00
zeekay f88c987025 chore: kill UPGRADE_NOTES.md (AI-generated upgrade-note slop; upgrade.json is canonical) 2026-06-26 22:32:06 -07:00
zeekay 71d5afb016 fix(devnet): canonical EVM chainID 96370 -> 96367 2026-06-23 14:00:16 -07:00
zeekay 8fef3692f6 configs(mainnet): activate all precompiles as of Dec 25 2025 (1766708400) — no future dates
20 precompiles (p3qVerify, pulsar/magnetar/corona/hqc, curve25519/x25519/sr25519,
babyjubjub/pedersen/poseidon/pasta/xwing, computeMarket/bridgeRegistrar/stableSwap/
vrf/attestation/anchor/fixedPointMath) were forward-dated to 2026-07-01; realigned to
the canonical Dec-25-2025 activation (1766708400) that testnet already uses. All
networks now activate the full surface as of Dec 25 2025 — zero future-dated upgrades.
v1.13.16
2026-06-23 10:10:27 -07:00
zeekay 06f0807de6 configs: pin 17 live-at-genesis precompiles to blockTimestamp 0 (relaunch-safe)
The mainnet/testnet C-Chain upgrade.json scheduled 17 already-live
precompiles (aiMiningConfig, blake3Config, cggmp21Verify, deadZeroConfig,
deadConfig, deadFullConfig, routerConfig, fheConfig, frostVerify,
graphConfig, hpkeConfig, mldsaVerify, mlkemConfig, pqcryptoConfig,
ringConfig, slhdsaVerify, zkConfig) at blockTimestamp 1766708400 — a
future (2025-12-26) timestamp. On the running mainnet these precompiles
are active at BLOCK 0 (the deployed luxd UPGRADE_JSON in luxfi/universe
k8s/lux-mainnet/luxd-startup.yaml has always carried them at 0).

Leaving the canonical genesis file at 1766708400 is a relaunch fork /
boot-refusal hazard: a relaunch from the canonical genesis would treat
each as INACTIVE before that timestamp and diverge from the canonical
chain where it is active at block 0 — or checkPrecompileCompatible would
refuse boot ("missing PrecompileUpgrade"). Pin all 17 to blockTimestamp 0
so IsForkTransition(0, nil, genesisTime) = (0 <= genesisTime) activates
them idempotently at block 0 on any relaunch, matching the deployed set.

warpConfig is unchanged in intent: it lives in the genesis chainConfig
(cchain.json config.warpConfig @ genesis time 1730446786,
requirePrimaryNetworkSigners=false). The upgrade schedule keeps its
genuine FUTURE strict-PQ toggle (disable@1766708399, re-enable@1766708400
with quorumNumerator=67, requirePrimaryNetworkSigners=true). The array is
reordered so all blockTimestamp:0 entries lead, preserving the
non-decreasing monotonicity rule in verifyPrecompileUpgrades. Every
non-zero timestamp is strictly greater than the genesis time, so a
relaunch re-fires no already-applied activation.

UPGRADE_NOTES.md documents the live-at-0 / strict-PQ-toggle / BLS /
Quasar-Edition tiers and the relaunch-safety contract.
2026-06-21 22:22:20 -07:00
zeekay 62bf39d883 fix(configs): remove dead dexConfig precompile key from all upgrade configs
node v1.30.27/28 dropped the 0x9010 DEX precompile; its config key
`dexConfig` is no longer registered, so any C-Chain upgrade/genesis
config carrying it is rejected at boot with "unknown precompile config:
dexConfig" -> C-Chain VM init fails. Remove the dead dexConfig entry from
every per-env upgrade.json and the devnet cchain.json. 0x9999 (live DEX
settlement) is config-free, so no key is added; routerConfig and all
other registered keys are preserved. Verified each file remains valid
JSON with dexConfig absent.

Files:
- configs/{mainnet,testnet,devnet,local,localnet}/upgrade.json
- configs/devnet/cchain.json
2026-06-21 19:03:46 -07:00
zeekay 10d6203817 genesis/cmd: derivekey + derive-scan (LightMnemonic→funded localnet account, non-ewoq) v1.13.15 2026-06-17 19:29:32 -07:00
zeekay 4b9b66c14e scrub branding + corona→corona (OSS brand hygiene) 2026-06-10 23:36:12 -07:00
zeekay 7f4f3318f5 ci: route to canonical native arcd labels [self-hosted, linux, <arch>]
Replaces non-canonical scale-set / org-prefixed labels with the
existing labels every arcd host registers with. Matches evo for
amd64 and spark for arm64. No new labels added.
2026-06-10 20:22:05 -07:00
Hanzo AI 76e9d86b6e chore: update 2026-06-10 13:40:17 -07:00
Hanzo AI 0abb293d3f chore: update 2026-06-10 13:34:21 -07:00
Hanzo AI fb53885497 go: 1.26.3 → 1.26.4 (security: crypto/x509, mime, net/textproto) 2026-06-06 22:09:18 -07:00
Hanzo AI c629280b97 deps: bump luxfi/vm v1.2.0, sampler v1.1.0, staking v1.5.0 (relicense)
Picks up the 2026-06-06 relicensing:
- luxfi/vm v1.2.0 → Lux Ecosystem License v1.2 (was Lux Research v1.0).
  License-only retag of the extracted runtime; patent reservation
  preserved for runtime optimization surfaces, royalty-free for
  Descending Chains.
- luxfi/sampler v1.1.0 → BSD-3-Clause (was Lux Research v1.0).
  License restored to match the luxfi/node provenance (originally
  extracted from node/utils/sampler).
- luxfi/staking v1.5.0 → BSD-3-Clause (was Lux Research v1.0).
  License restored to match the luxfi/node provenance.

No code changes in this commit — go.mod/go.sum only.
2026-06-06 19:02:28 -07:00
Hanzo AI 9e7a67de8e terminology: brand-l1-*.json → l2-*.json + 'brand L1' → 'L2' in inventory 2026-06-06 13:44:25 -07:00
Hanzo AI e2f88fb594 go.sum: drop stale accel versions (Wave 2G-Cascade) 2026-06-06 06:35:55 -07:00
Hanzo AI d5bf6e461b deps: bump p2p v1.21.1, node v1.29.3, sdk v1.17.6, vm v1.1.11 (Wave 2G-Cascade)
cmd: also drop the local codec replace directive — codec v1.1.5 has the
canonical zapcodec extracted to its own module per Wave 2G-Archive.
2026-06-06 06:31:34 -07:00
Hanzo AI bba8e11fb4 genesis/builder: migrate from staging shim to proto/zap_codec
Wave 2G-Archive completes the genesis-builder migration: the staging
shim builder/zap_codec.go (created during Wave 2G-Genesis to mirror
proto/zap_codec's surface before it was tagged) is now deleted, and
builder.go imports proto/zap_codec directly.

Changes:
  - builder.go: pvmGenesisCodec / newXVMParserCodecs swap to
    proto/zap_codec.NewPVMGenesis / NewXVMParser
  - builder/zap_codec.go: deleted (staging shim no longer needed)
  - go.mod: bump luxfi/proto v1.1.0 → v1.3.0 (first tag that exposes
    proto/zap_codec; v1.3.0 also includes the proto/internal test
    helper migration from Wave 2G-Internal)

luxfi/codec demoted to // indirect — kept transitively via deps that
haven't re-tagged yet. Will drop on the next upstream tagging cascade.
2026-06-06 05:52:54 -07:00
Hanzo AI 8da1cca53b genesis/builder: ZAP-native genesis codec (Wave 2G-Genesis)
Migrates pvmGenesisCodec() and newXVMParserCodecs() off the legacy
linearcodec big-endian wire format onto the ZAP-native zapcodec
little-endian wire format. Aligned with LP-023 ZAP-native activation
(proto/zap_native: ZAPActivationUnix=0 — "ZAP mandatory from genesis"),
forward-only.

Wire-format verdict: BREAK.

ZAP-native bytes are NOT byte-equivalent to legacy linearcodec bytes —
endianness flips for every uint16/uint32/uint64/string-length prefix.
The canonical signature (PVM Genesis with Timestamp=0x0102030405060708,
InitialSupply=0x1112131415161718, Message="z"):

  legacy linearcodec (BE): ... 01 02 03 04 05 06 07 08  11 12 13 14 15 16 17 18 ...
  ZAP-native zapcodec (LE): ... 08 07 06 05 04 03 02 01  18 17 16 15 14 13 12 11 ...

No coordinated network upgrade needed: this is the first ZAP-native
genesis tag, and no production validator was running legacy linearcodec
in the post-LP-023 fleet. Mainnet/testnet/devnet/localnet all bootstrap
ZAP-only from genesis.

Code changes:

  - builder.go:
      Drop github.com/luxfi/codec + .../linearcodec direct imports.
      Drop math.MaxInt32 inline construction.
      pvmGenesisCodec()    calls newZapCodecPVMGenesis(CodecVersion)
                           + pchainblock.RegisterTypes(cm).
      newXVMParserCodecs() calls newZapCodecXVMParser(CodecVersion).
      Mirrors sdk/wallet/chain/p/pcodecs.NewPVMGenesisCodec and
      sdk/wallet/chain/x/constants.go::newXVMParserCodecs which already
      moved to proto/zap_codec in Wave 2G-Wallet.

  - zap_codec.go (new):
      Staging shim mirroring proto/zap_codec's public surface
      (NewPVMGenesis, NewXVMParser, Manager.{Marshal,Unmarshal,Size,
      RegisterType,SkipRegistrations}). When Wave 2G-Wallet lands and
      tags github.com/luxfi/proto/zap_codec, this file deletes and the
      two helpers in builder.go swap to that import — mechanical, no
      behavioural change because both shim and target wrap the same
      zapcodec.Codec backend.

  - wire_test.go (new):
      TestWire_PVMGenesisRoundtrip      — bytes-out, parse-back, re-marshal stable
      TestWire_PVMGenesisVersion        — LE codec-version prefix
      TestWire_PVMGenesisDeterministic  — same Config → same bytes + UTXOAssetID
      TestWire_XVMGenesisRoundtrip      — XVM ParserCodecs construction smoke
      TestWire_XVMGenesisDeterministic  — UTXOAssetID stability across builds
      TestWire_HexSignature_PVMGenesis  — mainnet 32-byte prefix tripwire
      TestWire_FormatBreak_Documented   — pinned hex wire-format signature

  - go.mod:
      luxfi/codec  v1.1.4 (indirect) → v1.1.5 (direct: zapcodec backend)
      luxfi/accel  v1.1.4 (indirect) → v1.1.8 (transitive)

Constraint-compliance:

  - NO github.com/luxfi/codec imports in builder.go (verified by grep).
  - NO snow/avalanche/subnet naming.
  - All builder tests pass (go test ./...).
  - All parent genesis + cmd modules build clean.
  - Race detector clean.

TODO (wave-2g-wallet): once luxfi/proto tags include the zap_codec
subpackage, swap zap_codec.go's shim for the canonical
github.com/luxfi/proto/zap_codec import in builder.go and delete the
shim file.
2026-06-06 04:58:12 -07:00
Hanzo AI ce38b045d1 genesis/builder: thread codec through proto/p Wave 2A genesis API
proto/p Wave 2A (#101) rips github.com/luxfi/codec from p/genesis —
genesis.New, Genesis.Bytes and genesis.Parse now take a block.Codec
parameter at the call site (the same genesis-sized codec.Manager
already constructed inline elsewhere in the wallet stack).

Adds pvmGenesisCodec() helper mirroring the existing
newXVMParserCodecs() pattern in this file. Uses math.MaxInt32 budget
because PVM genesis blobs (full set of initial validator stake txs
plus CreateChainTx entries for X/C/D/Q/A/B/T/Z/G/K) can exceed runtime
tx-size limits. block.RegisterTypes seeds the full Apricot/Banff/
Durango/Quasar block + tx type set.

Threads pvmGenesisCodec() through:
  - FromConfig:    genesis.New + Genesis.Bytes  (one codec, two uses)
  - VMGenesis:     genesis.Parse
  - Aliases:       genesis.Parse

Also fixes the X-chain genesis serialization path that was broken by
Wave 1A: xvmCodecs.GenesisCodec was used directly without going
through xchaintxs.NewParser, so the canonical XVM tx types
(BaseTx/CreateAsset/Operation/Import/Export) and fx-owned
secp256k1fx.TransferOutput were never registered. The xvm genesis now
flows through NewParser, mirroring how UTXOAssetID already uses it.

builder_test.go's TestFromConfig_ChainSetIsShardDriven gains a single
pvmGenesisCodec() construction and threads it through both pgenesis.Parse
call sites.

go build ./... + go test ./... green.

The proto pin remains at v1.1.0; the workspace go.work consumes the
local proto/ which is on branch chore/proto-p-codec-rip with the new
API. This commit goes live once proto v1.2.0 is tagged.
2026-06-06 02:07:40 -07:00
Hanzo AI fb7c92270b inventory: brand-l1 IDs after 2026-06-06 clean rebootstrap
Architecture changes:
- RESET_TOKEN env-hack killed (use kubectl delete pvc instead)
- pars supply: 2T → 7B (matches chainId 7070/7071/7072 theme)
- spc supply: unified to 1B across all envs (matches mainnet RLP)
- All 3 envs wiped via PVC delete + re-bootstrapped clean
- RLP-only state restore: zoo, spc, C-Chain (mainnet) have archives

Mainnet C-Chain RLP import (1,082,780 blocks) running across 5
validators after this commit lands.
2026-06-06 00:24:09 -07:00
Hanzo AI 0f2aa9b698 inventory: testnet brand L1 IDs after 2026-06-05 quasar-fix re-bootstrap
Re-fired CreateChainTx for hanzo/zoo/pars/spc/osage on clean P-state after
pars-testnet genesis fix (added quasarTimestamp:253399622400 to satisfy
fork-ordering check). All 5 testnet brand L1s now isBootstrapped=true.

Cross-check via api.lux-test.network/ext/bc/<brand>/rpc all returning
chainId successfully. eth_blockNumber=0x0 (BOOTSTRAP-ONLY mode — block 1
will roll on first txn).
2026-06-05 17:01:01 -07:00
Hanzo AI cfd473d50d configs/testnet: sync genesis.cChainGenesis to A-form, scrub upgrade.json
Two related fixes, both validated live on do-sfo3-lux-k8s lux-testnet:

genesis.json:
  Replaced the embedded cChainGenesis field with the canonical 2-alloc
  A-form (matches standalone configs/testnet/cchain.json per 14b85b2).
  Old: gasLimit 0x3b9aca00, warpConfig blockTimestamp:0, embedded
  genesisHash + stateRoot annotations.
  New: gasLimit 0xb71b00, warpConfig blockTimestamp:1730517266,
  skipPostMergeFields:true. Empirically produces block 0 hash
  0x1c5fe37764b8bc146dc88bc1c2e0259cd8369b07a06439bcfa1782b5d4fb0995 —
  the canonical RLP-matching hash from lux-testnet-96368.rlp.

upgrade.json:
  Dropped two entries that were breaking luxd boot:
    - warpConfig {blockTimestamp:0, requirePrimaryNetworkSigners:true}
      Fails luxd validation: "invalid precompile upgrades: PrecompileUpgrade
      (warpConfig) at [0]: disable should be [true]". Warp is already
      pre-allocated in the C-Chain genesis alloc at address 0x02..05
      (code:0x01, nonce:1), so an explicit upgrade entry is redundant and
      breaks block 0 identity if it touches state.
    - feeManagerConfig at blockTimestamp:900000000 (Sep 1998!)
      Fails luxd validation: "config block timestamp (900000000) <
      previous timestamp (1766708400)". The list is required to be
      monotonically non-decreasing; 900000000 placed mid-list between
      strictPQ entries at 1766708400 broke that. Mainnet doesn't have
      this entry; testnet inherited it as drift and it's never
      activated cleanly. Drop and re-introduce later via a properly
      ordered timestamp if/when fee admin is needed.

Live verification:
  - all 5 lux-testnet pods boot to block 0 = 0x1c5fe377…
  - RLP imports cleanly to h=218 (0xda)
  - external eth_blockNumber at api.lux-test.network/ext/bc/C/rpc → 0xda
2026-06-05 14:41:19 -07:00
Hanzo AI 26e6de4734 builder: cascade proto/x Wave 1A API changes
Wave 1A inverted the proto/x codec design — proto/x carries no codec
import; consumers supply linearcodec.NewDefault() + codec.NewManager
externally via the new xchaintxs.ParserCodecs struct.

Mirrors sdk/wallet/chain/x/constants.go::newXVMParserCodecs by adding
the equivalent helper here. Updates 3 call sites:
- xgenesis.NewGenesis(networkID, defs, codec) — new 3rd param
- xvmGenesis.Bytes(codec) — new param
- xchaintxs.NewParser(codecs, fxs) — new ParserCodecs first arg

genesis builds clean.
2026-06-05 13:37:58 -07:00
Hanzo AI 10a38d286d cmd: bump utxo to v0.3.7 + add local codec replace for zapcodec
Required for bootstrap-chain wire-compat with luxd v1.28.29. The
running node's wallet/network/primary uses utxo.ParseUTXO ZAP
dispatcher; the previous v0.3.4 emitted incompatible TypeKind/ShapeKind
discriminators (`wire: ShapeKind discriminator does not match expected
primitive shape`). Bump aligns the wire format.

Also adds local-codec replace because the published luxfi/codec@v1.1.4
does not contain the zapcodec package — that landed locally for the
2026-06 Wave 1D refactor and luxfi/node references it via the
zapcodec dispatch path.

inventory/brand-l1-2026-06-05-reset.json: post-reset canonical brand
L1 IDs. Supersedes brand-l1-2026-06-05.json (initial deploy with
orphan-prone naming).
2026-06-05 12:38:22 -07:00
Hanzo AI d02c329787 inventory: brand-genesis-homes.md — add osage row 2026-06-05 12:06:21 -07:00
Hanzo AI 6e59361e44 configs/inventory: brand genesis home inventory — one canonical per brand 2026-06-05 12:01:08 -07:00
Hanzo AI 99386d96f7 configs/_orphan-l2: tombstone — kill the AI-slop bucket
The _orphan-l2/ directory was created by #109 brand-genesis migration
as a transitional cohabitation for pars + spc (which didn't yet have
dedicated brand repos). Both brands NOW have proper homes:

  pars → ~/work/pars/genesis/networks/<env>/genesis.json
         (structured: chain.yaml + genesis.json + genesis.sha256 + precompiles.yaml)
  spc  → ~/work/spc/genesis/networks/<env>/genesis.json
         (mirrors pars layout)

Delete all 6 brand-genesis files from _orphan-l2 and the explanatory
README. Replace with a single MOVED.txt pointing readers at the brand
homes.

One way to do everything: brand genesis lives under the brand's own
workspace. State-repo at ~/work/lux/state/chains/ remains the empirical
provenance archive that brand homes track.
2026-06-05 12:00:44 -07:00
Hanzo AI de92c01c49 configs/_orphan-l2/spc-mainnet: sync to state-repo canonical
Mirrors the spc-mainnet genesis fix from
~/work/lux/state/chains/spc-mainnet/genesis.json into the deployment
mirror at configs/_orphan-l2/spc-mainnet/genesis.json. Same pattern as
zoo-universe commit 071afb4 (zoo h=799 RLP unblock).

Required so the 11-block spc-mainnet RLP archive
(b72f2602ec72360d742bf234a93f113b6614ba1f2d1935f63d8659bf61990ffc,
exported 2026-06-05) imports cleanly: the archive's block 1 parent
hash matches block 0 derived from the state-repo canonical genesis,
not the prior deployment-mirror variant.

Sync verified by `cmp` — files are now byte-identical.
2026-06-05 10:24:18 -07:00
Hanzo AI 5b6df656b1 docker: image build for cmd/rlp-{export,import} (closes #69 / #122 sidecar)
Dockerfiles for the two RLP utilities + GH workflow calling the
shared hanzoai/.github docker-build@main workflow. amd64-only,
lux-build-linux-amd64 runner pool per the cross-org isolation rule.

Consumed by the lux-operator ExportSchedules CronJob (#69) and the
TenantImports Job (#122). Both default to the in-cluster MinIO
endpoint at http://s3.lux-system.svc.cluster.local:9000.

Pre-existing modifications in builder/, configs/configs.go,
pkg/genesis/{config,networks,types,cert_policy}.go intentionally
left untouched per CTO commit-by-name discipline.
2026-06-05 09:54:56 -07:00
Hanzo AI 3294cbaa64 configs: move brand L1 genesis out of luxfi/genesis (Task #109)
luxfi/genesis only owns Lux primary-network genesis (mainnet/testnet/
devnet/localnet). Brand L1 genesis blobs now live next to the brand
that owns them, in each brand's universe repo:

  hanzo: hanzoai/universe → configs/genesis/{mainnet,testnet,devnet}/
  zoo:   zooai/universe   → configs/genesis/{mainnet,testnet,devnet}/

The pars-* and spc-* dirs have no separate universe repo yet — they
cohabitate this repo under configs/_orphan-l2/ until one is spun up,
explicitly marked as orphans (see configs/_orphan-l2/README.md).

This commit:
  - removes configs/hanzo-{mainnet,testnet,devnet}/genesis.json
    and configs/zoo-{mainnet,testnet,devnet}/genesis.{json,upgrade.json}
  - leaves MOVED.txt tombstones at each old path pointing at the
    new canonical home
  - moves configs/{pars,spc}-{mainnet,testnet,devnet}/ to
    configs/_orphan-l2/ (git mv — history preserved)
  - adds pkg/genesis/no_brand_shadow_test.go which fails if any
    genesis.json reappears under configs/hanzo-* / zoo-* / pars-* /
    spc-* at the top level
  - updates LLM.md + configs/mainnet/UPGRADE_NOTES.md doc refs to
    point at the new locations

Zero Go code references the moved paths — configs/configs.go
//go:embed only covers Lux primary (mainnet testnet devnet localnet),
not brand dirs. Verified by full pkg/genesis test pass.
2026-06-05 09:06:44 -07:00
Hanzo AI ff32c62328 cmd/rlp-export: admin_exportChain CLI wrapper (Task #118)
Symmetric with cmd/rlp-import. Thin HTTP client that:

  1. Idempotency precheck reads <output-file>.sentinel on the local PVC
     and short-circuits when Status=done + range matches — luxd is never
     contacted in the warm-cron case.
  2. Health-probes ${LuxdRPC}/ext/health (30s cap).
  3. POSTs admin_exportChain via /ext/bc/${chain}/rpc with KMS_AUTH_TOKEN
     bearer auth (same env-var contract as rlp-import).
  4. Maps Status → exit code:
       ok | noop      → 0
       interrupted    → 4 (operator re-runs with from-height=HighestExported+1)
       bad input      → 1
       luxd unreach   → 2
       admin RPC err  → 3

Lives in the cmd/ nested module per the existing rlp-import precedent.
Builds with GOWORK=off; Makefile target `make rlp-export` does this for you.

Tests cover: flag parsing, "latest" resolution, all four idempotency
fall-through branches, happy path + noop + interrupted server responses,
luxd unreachable, admin RPC error, KMS_AUTH_TOKEN bearer header
plumbing. 14 tests, all green.

Symmetric Makefile additions: `make rlp-export`, `make rlp-import`,
`make cmds`, `make test-rlp-export`.
2026-06-05 00:29:10 -07:00
Hanzo AI 6c3ba05cf8 cmd/rlp-import: operator-driven RLP import binary (Task #122)
Standalone Go binary that runs the six-step tenant RLP import lifecycle
in a single process: sentinel check → fetch → sha256 verify → wipe
chainData → POST admin.importChain → touch sentinel. Replaces the shell
loop split between luxd-startup.yaml's main container and the operator's
tenant-import init container.

The binary is invoked by luxfi/operator as a K8s Job — one per
LuxNetwork.spec.tenantImports[i]. Exit codes are wired for K8s backoff:
1=bad input/SHA mismatch/404, 2=luxd unreachable (transient retry),
3=admin.importChain RPC error. Content-hash addressing matches
TenantImportSpec.ContentHash byte-for-byte so the operator and the
binary compute identical sentinel paths.

Tests cover all six lifecycle steps + every failure exit code.
2026-06-05 00:23:27 -07:00
Hanzo AI 2773128389 mainnet/upgrade.json: cap dexConfig.maxPools 10000 → 2000
Per cryptographer review of #114 safe-subset precompile activation:
maxPools=10000 on mainnet C-Chain is a state-bloat risk (each pool
entry is permanent; 10000 slots at 32-byte keys + accounting blobs
balloons the trie). Devnet retains 10000 for fuzz / load testing.

Mainnet cap of 2000 still comfortably exceeds expected DEX-V4 pool
count (~50 canonical pairs + room for long-tail). Increase later via
forward-dated upgrade if usage approaches the cap.

routerConfig zero-address placeholders are a separate fix (#48 contract
deploy must land first; only then does it make sense to point routerConfig
at the real V3 quoter/factory + V2 router/factory addresses).

eciesConfig already excluded per #99.
2026-06-04 23:27:47 -07:00
Hanzo AI 9c1eff2804 configs/devnet/cchain.json: scrub eciesConfig per #99
ECIES on a public permissionless chain has a fatal usability flaw:
the recipient's private key must be passed in calldata so the
precompile can perform the discrete-log unwrap. Calldata is public,
so any genuinely-encrypted use leaks the private key on the first
unwrap — defeats privacy. Any cosmetic use (e.g. registering the
config but never calling decrypt) is a footgun waiting to fire.

Brand L2 cchain.json files (hanzo/zoo/pars/spc-mainnet/testnet/devnet)
were scrubbed in a prior patch; devnet/cchain.json was the lone Lux
primary copy that still carried the entry. This brings devnet in line
with mainnet (no eciesConfig in primary genesis) and matches the live
cluster CM state (verified empty across all 3 envs).

UPGRADE_NOTES.md at configs/mainnet/UPGRADE_NOTES.md documents the
exclusion rationale for posterity. Removal is final — eciesConfig
will not be re-added to any Lux-network chain.
2026-06-04 23:25:48 -07:00
Hanzo AI 38ebe7aa8b configs/inventory: brand-l1-2026-06-05.json — 12 brand chains across mainnet+testnet+devnet
Spawned via bootstrap-chain after luxfi/node v1.28.29 LockedOutput
dispatcher fix unblocked the deployer's P-chain UTXO balance
(was returning 0 because vms/components/lux had no branch for the
(TypeKindReserved, ShapeKindLockedOutput) envelope that
stakeable.LockOut.Bytes() writes for vesting allocations).

Each row records (brand, blockchain ID, per-chain network ID, EIP-155
EVM chain ID). Single source of truth for the post-deploy chain IDs;
downstream consumers (operator chain-aliases CM, explorer routes,
ingress, gateway) should read this inventory rather than re-deriving.

Deployer: BIP44 m/44'/9000'/0'/0/5 from LUX_MNEMONIC.

Bootstrap-chain run:
  /tmp/bootstrap-chain --uri=https://api.lux{,-test,-dev}.network \
    --hrp=lux|test|dev --bip44-idx=5 \
    --network-label=mainnet|testnet|devnet \
    --configs-dir=$ROOT/configs \
    --track-chain-ids=hanzo,zoo,pars,spc \
    --chain-name-override="hanzo=hanzo,zoo=zoo,pars=pars,spc=spc" \
    --skip-bootstrap-wait

(name override required because the content-hash default produced
"hanzo-b5ed62f6" which P-chain CreateChainTx rejected as "illegal
name character".)
2026-06-04 22:39:03 -07:00
Hanzo AI bc6aee460d mainnet/upgrade.json: monotone 47-entry reorder for warpConfig disable+enable
Empirical root cause of the wrong genesis block 0 hash on mainnet C-Chain
was that 18 precompiles were declared at blockTimestamp:0 — those activate
during Genesis.ToBlock() via ApplyPrecompileActivations, which mutates the
state trie (SetNonce + SetCode([0x01]) per precompile address). That state
trie mutation shifted block 0 stateRoot off the canonical 0x2d1cedac…
and consequently the canonical block 0 hash 0x3f4fa2a0… became 0x467015e9…

Fix: forward-date all 18 precompiles from blockTimestamp:0 → 1766708400
(the Quasar activation), so they activate AT the hard fork rather than
during genesis state setup. Block 0 is now byte-identical to the canonical
RLP and importChain accepts the chain on first try.

Structural reordering (47 entries total, strictly non-decreasing
blockTimestamp — validator requires monotone + warpConfig disable+enable
pair):

  [ 0] warpConfig disable @ 1766708399
  [ 1-25] aiMiningConfig … bls12381PairingConfig @ 1766708400
  [26] warpConfig enable @ 1766708400 (quorumNumerator=67,
       requirePrimaryNetworkSigners=true)
  [27-46] pulsarVerify … fixedPointMathConfig @ 1782864000 (PQ + extras)

Verified live on lux-mainnet C-Chain:
  block 0 hash = 0x3f4fa2a0b0ce089f52bf0ae9199c75ffdd76ecafc987794050cb0d286f1ec61e ✓
2026-06-04 19:15:50 -07:00
Hanzo AI 05654162de genesis: re-add 7 bls12381*Config entries to mainnet upgrade.json
Re-introduces the 7 EIP-2537 BLS12-381 precompile entries that #114
(8a40886) had to drop from mainnet because all 7 sub-configs returned
the same ConfigKey via Key() — only one could land in the registry
and DisallowUnknownFields parse rejected the other six.

Fixed upstream:
  luxfi/precompile v0.5.35 (commit 794912f) — per-sub-config Key()
  luxfi/evm        v0.19.1 — bumps precompile dep
  luxfi/node       v1.28.23 — Dockerfile EVM_VERSION=v0.19.1

Activation: Quasar horizon Unix 1766708400 (2025-12-25 16:20 PT,
already past → active on next chain restart). Matches testnet's
existing 1766708400. Devnet stays at 0 (forever-active).

Cluster apply requires luxd image rebuilt from node v1.28.23+ tag
so the C-Chain plugin (built from evm v0.19.1) accepts the parse.
2026-06-03 12:35:25 -07:00
Hanzo AI a5732479a7 configs: kill etnaTimestamp; one and only one upgrade-key namespace
Quasar Edition rip. Reverts e6cec59 (the deployment-skew bandage) now
that luxfi/evm v0.19.0 ships:
 - QuasarTimestamp Go field with json:"quasarTimestamp" tag
 - DisallowUnknownFields strict decode at upgradeBytes parse

There is one name. quasarTimestamp. The strict decoder turns any
remaining etnaTimestamp residual into a loud parse error at boot
instead of a silent fork-disable at activation.

Brand L1 EVM genesis (hanzo/pars/spc/zoo mainnet) and primary mainnet
upgrade.json: etnaTimestamp removed; quasarTimestamp: 0 retained.

New envs filled to canonical (was missing, hidden by the skew):
 - configs/devnet/upgrade.json
 - configs/local/upgrade.json
 - configs/localnet/upgrade.json
All carry quasarTimestamp:0 + every PQ precompile + cipher precompile
at blockTimestamp 0 (always-on for dev surfaces).

Mainnet keeps strictPQTimestamp:1766708400 (Dec 25 2025 16:20 PT)
as the forward-dated profile gate; PQ precompiles already active
from block 0.

Pairs with: luxfi/evm v0.19.0 + luxfi/node v1.28.22 bump.
2026-06-03 11:51:44 -07:00
Hanzo AI e6cec59f35 mainnet/upgrade.json: revert quasarTimestamp → etnaTimestamp
Commit af356f7 (Etna → Quasar Edition) renamed the field in source
but the deployed EVM plugin at s3://lux-plugins/v1.1.0/ is built
from luxfi/evm@v0.18.18 which still reads NetworkUpgrades.EtnaTimestamp
(json tag etnaTimestamp). Until a new plugin ships with the renamed
struct, mainnet upgrade.json must use the pre-rename key — otherwise
the override silently drops and the plugin's verifyNetworkUpgrades
trips "etnaTimestamp not enabled, but fortunaTimestamp enabled" for
every brand L1 EVM that inherits this override structure.

Matches the on-disk content active on lux-mainnet luxd pods
(/data/configs/chains/C/upgrade.json, applied via the luxd-startup
ConfigMap patch on 2026-06-03). When the plugin upgrade lands the
key flips back to quasarTimestamp.
2026-06-03 10:04:51 -07:00
Hanzo AI 8a40886c25 mainnet/upgrade.json: fix C-Chain warpConfig + drop bls12381 entries
Two surgical fixes that get lux-mainnet C-Chain past the
`PrecompileUpgrade verification` gate in luxd v1.28.21 (evm@v0.18.18):

1. warpConfig toggle (disable@1766708399 + enable@1766708400)
   C-Chain genesis already enables warp at blockTimestamp 1730446786
   (luxfi/genesis@v1.7.1 cchain.json). Per the validator at
   evm/params/extras/precompile_upgrade.go:133, the next upgrade for
   an enabled precompile MUST flip state — a re-enable without an
   intermediate disable trips "disable should be [true]". Inserting a
   disable upgrade 1s before Quasar (1766708399) followed by re-enable
   at Quasar (1766708400) satisfies the toggle requirement and
   activates the new warp config (requirePrimaryNetworkSigners=true)
   at the network-wide PQ rollout, consistent with #114 intent.

   Also moved the two warp entries to AFTER the at-zero precompile
   block so the monotonicity check at line 129 (timestamps must be
   non-decreasing across the entire list) passes.

2. Dropped bls12381 sub-config entries (G1Add/Mul/MSM, G2Add/Mul/MSM,
   Pairing). All 7 share a single Configurator.MakeConfig() in
   luxfi/precompile@v0.5.23/bls12381/module.go that returns a Config
   whose Key() method hard-codes G1AddConfigKey:

       func (c *Config) Key() string { return G1AddConfigKey }

   This makes the EVM plugin's verifyPrecompileUpgrades loop see all
   7 entries as "bls12381G1AddConfig", the 2nd-7th fail the
   disabled/enabled toggle check with "disable should be [true]" even
   though the entries are byte-distinct in the JSON.

   Underlying fix is in luxfi/precompile (instance-field key like
   `dead`/`graph`/etc — not a const). Until that lands and a new
   EVM plugin ships to s3://lux-plugins/, mainnet upgrade.json must
   not enumerate bls12381 sub-modules. The plugin still gates them
   correctly via strictPQTimestamp; the loss is only explicit
   activation scheduling for the bls12381 sub-keys.

After these two fixes lux-mainnet C-Chain bootstraps cleanly
(info.isBootstrapped=true, /ext/health C check OK).
2026-06-03 09:42:01 -07:00
Hanzo AI 7d4eab387a mainnet: fix C-Chain warpConfig + brand L1 fork-ordering
Surgical fixes for 4 RED chains on lux-mainnet:

1. configs/mainnet/upgrade.json — C-Chain
   warpConfig[0]: blockTimestamp 0 → 1766708400 (Quasar Edition Unix)
   luxd v1.28.18 rejected blockTimestamp:0 without disable:true at
   genesis because C-Chain genesis already configures warp. Forward-
   dating activation to Quasar (consistent with #114 intent: warp
   activates with PQ precompiles at the network-wide PQ rollout).

2. configs/{hanzo,pars,spc}-mainnet/genesis.json
   Added etnaTimestamp:0 between durangoTimestamp and quasarTimestamp.
   Deployed luxd v1.28.18 (luxfi/evm@v0.18.18) field names predate the
   Etna→Quasar rename — its NetworkUpgrades struct still uses
   EtnaTimestamp json:"etnaTimestamp". Without it, fork-ordering check
   in evm@v0.18.18 params/extras/config.go:407 fails:
   "etnaTimestamp not enabled, but fortunaTimestamp enabled at
   timestamp 0x..." Adding etnaTimestamp:0 satisfies the binary's
   ordering check; quasarTimestamp stays as forward-compat alias for
   post-rename binaries.

zoo-mainnet/genesis.json unchanged (has only evmTimestamp +
durangoTimestamp, no later forks → no ordering issue → already GREEN).
2026-06-03 09:05:20 -07:00
Hanzo AI af356f789a rename: Etna → Quasar Edition
- pkg/genesis/types.go ChainConfig.EtnaTimestamp → QuasarTimestamp
    json tag etnaTimestamp → quasarTimestamp
  - configs/{mainnet,testnet,devnet}/{cchain,upgrade}.json
    "etnaTimestamp" → "quasarTimestamp"
  - configs/{hanzo,zoo,pars,spc}-{mainnet,testnet,devnet}/genesis.json
    "etnaTimestamp" → "quasarTimestamp"
  - configs/zoo-mainnet/upgrade.json
    "etnaTimestamp" → "quasarTimestamp"
  - scripts/gen-localnet.py + LLM.md docs cleanup

No backcompat aliases — forwards-only. All embedded genesis +
upgrade JSON blobs flipped in-place.
2026-06-02 23:11:39 -07:00
Hanzo AI 8a10a3873e cmd/bootstrap-chain: read LUX_MNEMONIC env (one var, one way)
The earlier MNEMONIC unification commit (4383ad4) missed this CLI tool;
it was still reading the legacy MNEMONIC env name directly via
os.Getenv. Per the "one var, one way" rule the canonical name is
LUX_MNEMONIC across every code path in the genesis repo.

Updated in cmd/bootstrap-chain/main.go:
- Package-doc usage example: MNEMONIC="..." -> LUX_MNEMONIC="..."
- --print-addr-only flag help: scrub MNEMONIC -> LUX_MNEMONIC
- --key-file flag help + comments: MNEMONIC -> LUX_MNEMONIC
- loadKey() reads os.Getenv("LUX_MNEMONIC") instead of "MNEMONIC"
- Error message references LUX_MNEMONIC

No backcompat fallback: callers using the legacy MNEMONIC env get the
clear "LUX_MNEMONIC env var or --key-file required" error.

Verification:
- go build ./...           -> clean
- go vet ./...             -> clean
- go test ./... -count=1   -> ok configs, ok pkg/genesis
2026-06-02 22:43:57 -07:00
Hanzo AI 30f300f9ea genesis: scrub Avalanche residue from pkg + cmd + LLM.md prose
Comment-only scrub of upstream-fork residue in user-facing docstrings,
CLI help text, and LLM.md prose. No behavior change; no wire-format
change.

pkg/genesis/keys.go:
- DefaultNumAccounts doc: drop "AvalancheJS" from canonical-BIP44 wallet
  list (was "Core, MetaMask, AvalancheJS, lux key derive" -> now
  "Lux Wallet, MetaMask, lux key derive").
- LoadKeysFromMnemonic doc: drop "Avalanche/Lux BIP44 path" (just
  "Lux BIP44 path") and "Core Wallet, AvalancheJS" wallet list (now
  "MetaMask, Lux Wallet").
- LoadKeysFromMnemonic inner loop comment: same scrub
  ("Standard Avalanche/Lux secp256k1 child" -> "Standard Lux ...";
  "matches Core Wallet, MetaMask, cast" -> "matches Lux Wallet, ...").
- BuildBIP44WalletAllocations doc: drop "SLIP-0044 coin type Lux
  inherits from its upstream lineage" prose. Replaced with brand-
  neutral "BIP44 coin type Lux uses by canonical convention".

cmd/genesis/main.go:
- bip44-wallet-keys flag help: drop "SLIP-0044 coin type 9000" ->
  "BIP44 coin type 9000 canonical convention".

LLM.md:
- "Fortuna Timestamp Fix" prose: drop "Fortuna, Etna, and Granite
  upgrades" naming. Replaced with "pre-Quasar Edition upgrades"
  framing. The JSON field names (etnaTimestamp / fortunaTimestamp /
  graniteTimestamp) are preserved in the example block because they
  are the wire format consumed by luxfi/evm.

Intentionally kept (per task scope and forward-compat realities):
- params_test.go:33 + params.go:303 "avax HRP" rejection guards —
  these are protective security tests/comments, not residue. The whole
  point is to refuse "avax"-HRP bech32 addresses synthesized from a
  canonical lux validator's 20 bytes. Stripping the "Avalanche" prose
  would obscure why the guard exists.
- pkg/genesis/types.go:229 EtnaTimestamp uint64 field — this Go field
  is a downstream mirror of the wire-format identifier defined at the
  source of truth in luxfi/evm/params/extras/network_upgrades.go and
  consumed by ~95 JSON genesis blobs, K8s startup manifests, academy
  docs, and migration tooling. Renaming the genesis-repo mirror alone
  would break JSON round-trip with every other consumer in the
  ecosystem. A wire-format rename of EtnaTimestamp -> QuasarTimestamp
  must originate in luxfi/evm and migrate every embedded genesis
  config + every k8s manifest + every doc in one coordinated PR series.
  That migration is OUT OF SCOPE for this commit and is reported back
  to the dispatcher; see external-caller surprises below.

Verification:
- go build ./...           -> clean
- go vet ./...             -> clean
- go test ./... -count=1   -> ok configs, ok pkg/genesis
- grep '[Aa]valanche\|AvalancheJS\|Core Wallet\|SLIP-0044' pkg/genesis
  cmd LLM.md
    -> only 2 lines remain (params.go:303 + params_test.go:33),
       both inside the protective HRP-rejection guard prose.
2026-06-02 22:41:28 -07:00
Hanzo AI 4383ad4583 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.
2026-06-02 22:21:27 -07:00
Hanzo AI 210eb824e7 configs: remove {brand}fix-{env}/ symlinks (workaround retired)
The fix-suffix symlinks (hanzofix-testnet, zoofix-devnet, etc.) were a
workaround for P-chain CreateChainTx name-collision against stale chain
rows from earlier bootstrap attempts. They duplicated the genesis blob
under a second path per (brand, env) pair, violating the "one canonical
way" rule.

Now that bootstrap-chain derives chain.name from the canonical genesis
content (commit 6046cdb), collision is impossible and the workaround
has no reason to exist. The configs/ directory is back to one canonical
{brand}-{env}/ per pair (4 brands x 3 envs = 12 directories).

11 symlinks removed:
  hanzofix-{devnet,testnet,mainnet}
  zoofix-{devnet,mainnet}
  spcfix-{devnet,mainnet}
  parsfix-{devnet,testnet,mainnet}

(spcfix-testnet was never created; zoofix-testnet was already gone.)
2026-06-02 20:46:20 -07:00
Hanzo AI 6046cdb590 cmd/bootstrap-chain: decomplect chain.name from brand identity
The CreateChainTx `name` argument is a P-chain-unique identifier — caller's
choice. The brand alias ("hanzo", "zoo", "spc", "pars") is user-facing and
belongs at the chain-aliases ConfigMap layer. Conflating the two forced
the {brand}fix-{env}/ symlink workaround when re-bootstrapping over stale
P-chain chain rows from prior CreateChainTx attempts.

Default chain.name is now content-addressed: "<brand>-<8hex>" where
8hex = hex(SHA256("<brand>|<env>|"||genesisBytes))[:4]. Properties:

  - Idempotent: same canonical genesis → same chain.name → re-runs detect
    the existing chain via platform.getBlockchains and skip CreateChainTx.
  - Collision-free: different genesis bytes (any change to chainId, alloc,
    config) → different chain.name → CreateChainTx succeeds against
    P-chain instead of colliding with the previous stale row.
  - Brand-rooted: "hanzo-3f4a1c8b" stays humanly legible without conflating
    the chain.name with the user-facing brand alias.

Spec struct renamed: Name → Brand (user-facing) + ChainTxName (P-chain ID).
Result JSON gains "brand" and "chainTxName" fields so downstream
consumers (chain-aliases CM, explorer-chains CM, gateway routes) can pin
on the brand value without parsing the chain.name suffix.

Override path: --chain-name-override "<brand>=<name>,..." pins explicit
names for legacy reattachment scenarios.

4 new tests pin the contract:
  - Deterministic (same input → same output)
  - DifferentGenesisDifferentName (bytes change → name changes)
  - DifferentBrandDifferentName (brand-scoped)
  - DifferentEnvDifferentName (env-scoped)

All 14 tests pass.
2026-06-02 20:46:09 -07:00