292 lines
14 KiB
YAML
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
|