Files
universe/k8s/lux-mainnet/network.yaml
T

292 lines
14 KiB
YAML

---
# LuxNetwork CR for Lux Mainnet
# Cluster: DOKS lux-k8s (do-sfo3-lux-k8s)
# luxd network ID: 1 (canonical, per luxfi/constants MainnetID).
# The C-Chain EVM chain ID is 96369 — declared on the LuxIndexer for C-chain.
# Network ID and C-Chain ID are distinct concepts (per universe CLAUDE.md).
# Consensus: Quasar (BLS12-381 + Corona lattice + ML-DSA-65, FIPS 204)
# Validator key derivation: SLIP-10 from LUX_MNEMONIC (KMSSecret),
# BIP44 coin-type 9000 for P/X-chain identities; coin-type 60 for EVM-side.
#
# WHAT ACTUALLY REACHES THE KUBELET ON THIS NAMESPACE — read before editing.
# Mainnet carries THREE declarations of the luxd image and only one is real:
# 1. StatefulSet/luxd — REAL. No ownerReferences at all, so
# nothing reconciles it; the kubelet reads
# this and only this.
# 2. LuxNetwork lux.network/v1alpha1/luxd — this file. LIVE object exists
# (Running 5/5, 153d) but its spec.image
# read v1.28.18.
# 3. LuxNetwork lux.cloud/v1/luxd — also live, spec.image read v1.34.0,
# a tag that EXISTS NOWHERE in GHCR.
# Both operators (lux-operator, lux-operator-devnet) sit at 0 replicas and
# MUST STAY THERE, so neither CR moves anything today. That also means a
# CR reading a nonexistent tag is a live landmine: scaling an operator up
# would try to pull v1.34.0 and fail the whole fleet. All three now read
# v1.36.2 — what is actually running — so the declared intent is uniform and
# pullable whichever surface someone later makes authoritative.
#
# 2026-07-28: the v1.36.35 roll was ATTEMPTED AND ABANDONED. Tag stays v1.36.2.
# TWO independent blockers were found, and the second one means mainnet
# nodes cannot currently be restarted AT ALL, on any image. Read both before
# anyone deletes another luxd pod in this namespace.
#
# BLOCKER 1 — v1.36.35 cannot read this fleet's P-Chain database.
# luxd-0 was rolled to v1.36.35 (digest sha256:6cf5356ad05559be8c4e23b21a
# 15a37bf968552c2db342e00b42166e9596c38a, GHCR three-way control
# v1.36.33=200 / v1.36.35=200 / v9.99.99=404) and exited 1 in ~3 minutes:
# P-Chain state corrupt after init — database must be wiped
# error="loadMetadata: feeState: zap: invalid magic bytes
# failed loading validator transaction txID 2qZGnjxtWeWB9o8TS1qMy6
# dtQgYNRahWc4Tn7H8t7vFU6sVBLa, zap: invalid magic bytes"
# The on-disk P-Chain is in the legacy linearcodec encoding; v1.36.35's
# reader demands ZAP magic and refuses. v1.36.2 reads it with no flag —
# there is no LUXD_ENABLE_LEGACY_CODEC in this StatefulSet's env (the only
# codec-ish var is ZAP_RPC_LISTEN=:9632). So this is a forward-incompatible
# codec change, not a config gap, and it needs a P-Chain migration or a
# legacy read path before v1.36.35 can run on mainnet. Devnet and testnet
# prove nothing here: their databases were written after the cutover.
#
# BLOCKER 2 — restarting ANY mainnet node kills its C-Chain. This is the
# serious one, and it is PRE-EXISTING, not caused by v1.36.35.
# After being reverted to v1.36.2, luxd-0 came back with no C-Chain:
# failed to repair accepted chain by height: proposervm finality index
# (height 1097880, id DNqccFRTACH7YtasxcDcf1zNhe7PfbY5XFRrosYydK6ejBK2B)
# is BEHIND the inner VM tip
# (height 1098191, id 2P9scQ3xCw7jesWDEspaSp8rVzSYaFtBi3W5i4wMfYJwj7yhoF)
# Those are the SAME two heights and the SAME two block IDs that luxd-2
# has been failing on since its own restart. Identical corruption on two
# separate disks means a shared bad lineage — the proposervm index and the
# inner EVM were restored at different heights by an earlier snapshot
# clone. The node refuses to auto-reset the finality pointer, correctly,
# because that would wedge it at the inner tip forever. It says so:
# "This cannot be repaired locally."
# The C-Chain stays alive only while the process keeps running; the damage
# is latent on disk and is discovered at boot. luxd-3 and luxd-4 are almost
# certainly carrying it too — they sit at the same inner tip 1098191 — but
# that CANNOT be confirmed without restarting them, which is precisely
# what would destroy them. Do not test it.
#
# Recovery for a node in this state is snapshot restore across BOTH the
# proposervm and EVM databases, or a full resync from peers. Note that
# CronJob/lux-export-luxd-c-chain-hourly is SUSPENDED and last ran 51 days
# ago, so there is no recent consistent snapshot to restore from.
#
# State observed before any of this, for the record: luxd-0/3/4 pinned at
# C-Chain 1098191 with a byte-identical hash 0xb5ff445a0bca1d46…, luxd-1 535
# blocks ahead at 1098726 and wedged in "failed to fetch preferred block; no
# distinct last-accepted fallback", luxd-2 with no C-Chain. NOT a fork — the
# four live C-Chains agreed exactly at the common height. The fleet was also
# not producing because the peer mesh had fragmented to 2/3/4/1/2
# connections: consensus alpha is 4-of-5 (ceil(2*5/3)+1), so luxd-3 at ONE
# peer could never be in a quorum. That fragmentation is application-level
# peer state, not networking — every pod could still open TCP to every other
# pod's 9631 (curl exit 52 = connected, non-HTTP protocol) and the namespace
# has no NetworkPolicy. Restarting is the obvious fix for a stale mesh and is
# exactly what Blocker 2 forbids.
#
# updateStrategy is OnDelete, so this tag moves nothing by itself. The two
# LuxNetwork CRs were briefly set to v1.36.35 and are back at v1.36.2; the
# v1.34.0 that lux.cloud/v1 used to carry is gone either way, which is worth
# keeping — that tag exists nowhere in GHCR.
apiVersion: lux.network/v1alpha1
kind: LuxNetwork
metadata:
name: luxd
namespace: lux-mainnet
labels:
app.kubernetes.io/part-of: lux-network
lux.network/network: mainnet
spec:
networkId: 1
validators: 5
adoptExisting: false
genesisConfigMap: luxd-genesis
image:
repository: ghcr.io/luxfi/node
# 2026-06-02: v1.28.19 — LP-023 ZAP-native activation cutover.
# ZAPActivationUnix=0 (always-on). The forward-dated 2026-07-01
# activation is dead — ZAP wire is mandatory from genesis. Legacy
# linearcodec is reachable only via LUXD_ENABLE_LEGACY_CODEC=1 for
# read-path back-compat (decoding pre-2026-06-02 archival bytes).
# New deployments, fresh syncs, and all production binaries from
# v1.28.19 onward are ZAP-only by construction.
#
# v1.28.18 (previous): bumps EVM plugin v0.18.16 → v0.18.18.
# v0.18.18 fixes the chain-create wedge on freshly-provisioned L2
# EVMs (hanzo, zoo, spc, pars), which were panicking with
# "panic in eth.New: triedb parent [<EmptyRootHash>] layer missing"
# because eth/backend.go inherited geth's path-by-default on empty
# databases while the plugin hard-refuses path mode upfront. The fix
# locks the empty StateScheme to HashScheme in plugin/evm/vm.go
# before backend.go can inherit the upstream "path" default.
#
# 2026-06-28: v1.31.2 — VERIFIED FINAL-SPEC LOCK-IN (replaces operator drift).
# Consensus v1.31.0 sibling-tolerant finality + frontier-naming fix +
# clean upgrade config. Requires the matching luxd-startup CM: the EVM
# plugin DisallowUnknownFields-rejects the retired strictPQTimestamp /
# pqcryptoConfig keys (failed to parse upgrade bytes → C-Chain can't
# init); both are dropped (standalone mldsa/slhdsa/etc verifiers cover
# PQ). C-Chain DEX (0x9999) PROVEN trading on all 3 nets (mainnet fill
# @1,082,812). Boot via universe/RUNBOOK.md, gated by universe/e2e/dex.
# 2026-06-30: v1.32.1 — incident-1082814 DURABLE FIX (QCv2 canonical-
# commitment finality + RED-round H1 verify-before-slash). Finality keys
# on the inner canonical execution commitment, NOT the proposervm
# envelope, so two envelopes wrapping the same inner block are duplicates
# not forks; equivocation evidence requires a VERIFIED alpha-of-K cert
# over a DIFFERENT canonical. Kills the false-slash fatal-exit crashloop
# that destabilized mainnet under DEX-fill load on v1.31.5. Supersedes
# v1.31.5 (QCv2 core only) and v1.32.0 (down-leader, no QCv2). Rolled
# atomic >=4-of-5 to all 3 nets; 0 equivocation post-roll.
tag: "v1.36.2"
ports:
http: 9630
staking: 9631
podAntiAffinity: true
consensus:
sybilProtectionEnabled: true
# Quasar: post-quantum hybrid finality (BLS aggregate + Corona lattice + ML-DSA-65)
type: quasar
zapPort: 9999
pqLayers:
- bls
- corona
- mldsa
staking:
secretName: luxd-staking
kms:
hostApi: http://kms.lux-system.svc.cluster.local/api
projectSlug: lux-infra
envSlug: mainnet
secretsPath: /staking
resyncInterval: 300
auth:
credentialsSecret: universal-auth-credentials
credentialsNamespace: lux-system
# Validator keys are derived deterministically from a single LUX_MNEMONIC
# held in KMS at lux-infra/mainnet/staking/lux-master-mnemonic.
# The operator (or its bootstrap init container) uses SLIP-10 / BIP44 to
# derive per-validator BLS + secp256k1 staking keys.
keyDerivation:
mnemonicSecret: lux-master-mnemonic
bip44Path: "m/9000'/96369'"
seedRestore:
enabled: true
sourceType: ObjectStore
objectStoreUrl: "https://s3.lux.cloud/lux-snapshots/mainnet/mainnet-node0-full.tar.gz"
snapshotSchedule:
enabled: true
intervalSeconds: 3600
objectStoreEndpoint: "https://s3.lux.cloud"
bucket: lux-snapshots
prefix: mainnet
accessKey: hanzo
secretKeyRef: hanzo-s3-secret
sourceNodeIndex: 0
includeRlpExports: true
maxRetained: 5
upgradeStrategy:
strategyType: RollingCanary
maxUnavailable: 1
healthCheckBetweenRestarts: true
stabilizationSeconds: 60
startupGate:
onTimeout: StartAnyway
timeoutSeconds: 30
# VM plugins fetched from S3 (built by luxfi/chains CI on push of vX.Y.Z).
# Operator emits a `plugin-fetch` init container that downloads each
# vmPlugin and creates the chainIDs symlinks. Replaces the imperative
# `cp -f /luxd/build/plugins/*` + `ln -sf` dance previously in
# luxd-startup.yaml. See luxfi/operator v0.x.x luxnetwork_plugin.go.
#
# NOTE: EVM plugin (mgj786…) is symlinked from app-chain blockchain ID
# (ag3GR…) so the C-Chain EVM binary serves the app-chain too (shared
# precompile registry). luxd-startup previously did this with
# `cp -f /data/plugins/mgj786NP… /data/plugins/ag3GReYPNuSR…`.
pluginSource:
bucket: "s3://lux-plugins/v1.1.0/"
endpoint: "https://s3.lux.cloud"
vmPlugins:
- vmID: "aivm"
objectKey: "aivm-linux-amd64"
- vmID: "bridgevm"
objectKey: "bridgevm-linux-amd64"
- vmID: "dexvm"
objectKey: "dexvm-linux-amd64"
- vmID: "graphvm"
objectKey: "graphvm-linux-amd64"
- vmID: "identityvm"
objectKey: "identityvm-linux-amd64"
- vmID: "keyvm"
objectKey: "keyvm-linux-amd64"
- vmID: "thresholdvm"
objectKey: "thresholdvm-linux-amd64"
- vmID: "oraclevm"
objectKey: "oraclevm-linux-amd64"
- vmID: "quantumvm"
objectKey: "quantumvm-linux-amd64"
- vmID: "relayvm"
objectKey: "relayvm-linux-amd64"
- vmID: "zkvm"
objectKey: "zkvm-linux-amd64"
# C-Chain EVM plugin (canonical) — symlinked from the app-chain
# blockchain ID so both share the same precompile registry binary.
- vmID: "mgj786NP7uDwBCcq6YwThhaN8FLyybkCa4zBWTQbNgmK6k9A6"
objectKey: "evm-linux-amd64"
chainIDs:
- "ag3GReYPNuSR17rUP8acMdZipQBikdXNRKDyFszAysmy3vDXE"
# Tenant RLP imports — content-hash-addressed, declarative replacement
# for the hand-edited "ONE-TIME X RLP import" shell blocks in
# luxd-startup.yaml. The operator emits a `tenant-import` init container
# that wipes chainData/<blockchainID>, downloads SourceURL, and drops a
# JSON request marker at
# /data/db/.tenant-import-request-<tenant>-<sha12>.json. The main
# container's generic loop reads each marker, calls admin_importChain on
# /v1/chain/<chainAlias>/rpc, and writes the operator-named sentinel.
# Re-firing is automatic on any spec change — no version-string bumping.
# See luxfi/operator v0.6.3+/v0.7.x manifests/luxnetwork_plugin.go.
tenantImports:
- tenant: "c-chain"
chainAlias: "C"
# C-Chain on lux-mainnet — blockchainID corrected 2026-06-01 (real
# ID is NbWZbwJjFC8...; was wrongly typed as cjhijarXP earlier).
# The content-hash sentinel re-fires automatically because the
# BlockchainID change above flips the hash; no .cchain_rlp_imported_v4
# version-bumping required.
sourceURL: "https://s3.lux.cloud/lux-snapshots/lux-mainnet-96369.rlp"
blockchainID: "NbWZbwJjFC8WWZtWpkbxvAcyQhtvkvpLLb9bcuNXBBmLuSVWJ"
- tenant: "zoo"
chainAlias: "zoo"
# Zoo Mainnet EVM (chainID 200200). luxd blockchain ID confirmed
# via chain-aliases.yaml + zoo/universe/k8s/zoo-mainnet/luxd-rlp-import.yaml.
sourceURL: "https://s3.lux.cloud/lux-snapshots/zoo-mainnet-200200.rlp"
blockchainID: "2wbPWcyuNUimWW7YMq3gymTTpHjP9Lsm7GpmcyWimEs9kyTAAH"
# SHA256 from zoo/universe luxd-rlp-import.yaml — flipping this
# value (or any other field) on a future fix produces a new content
# hash and re-fires the import. Idempotent on stable spec.
sha256: "0850841a368dd1f85ae09d40f0552aecb679225b58ce4524b2d0a3337c7afc98"
chainTracking:
trackAllChains: true
# Platform upgrade config (Durango/Etna/Fortuna/Granite active from genesis).
# EVM-side upgrades + 43 precompile activations are baked into the per-chain
# genesis JSON files at luxfi/genesis@06dc203 — no chainUpgradeConfig override
# needed at the CR layer. Boot with --genesis-file=<configs/*/genesis.json>
# and luxd reads upgrade timestamps + precompile activations from genesis.
upgradeConfig:
durangoTimestamp: 0
quasarTimestamp: 0
fortunaTimestamp: 0
graniteTimestamp: 0