The two mains shared no history, so this is a per-file three-way merge:
for each file the 65 forge-only commits touched (by patch-id), the base is
the newest version both histories held. 105 files merged or took one side
cleanly, 48 were already identical, and the 45 true conflicts and 31
modify/delete pairs were resolved by what runs on the cluster:
- The forge wins where it is live: explorer 1.5.56 (digest 45db11ff) and
its archive readers, explore-fe-hanzo's ConfigMap env (AI coin,
/v1/indexer/hanzo, /v1/chain/hanzo/rpc), exchange-api reading
luxd-archive, and explore-fe-{lux,zoo}-testnet, pinned to what they run
(v2.8.5-lux / v2.8.4-lux).
- GitHub wins where it is live: explore-fe-lux v2.8.5-lux, explore-fe-zoo
v2.8.4-lux, bankd v1.0.18, lux-build's routes.
- The forge's retirement of workloads that run nowhere stands: lux-mainnet
dex, lux-hb2, lux-market, traffic, wallet-web; the lux-testnet frontends,
indexer, exchange and gateways; lux-operator. deploy/lux-testnet/luxd.yaml
stays, since roll_luxd.sh reads it.
- GitHub's deletions stand: lux-finance and lux-financial (static sites
now), the k8s/ operator manifests, .hanzo/workflows/cicd.yml.
- Paths are /v1/chain/C/rpc where the forge had written /v1/chain/c. All
four spellings answer; this one is the documented form. The forge's
/{network}/v1 doc lines are dropped: a network is a host, and the prefix
404s.
Dry-run against the running objects (server-side, as CD applies), the
merged explorer shows no change where GitHub's showed 152 lines, and no
other live workload moves further from what runs. Every deploy/*/*.yaml
renders with charts/app; price.yaml, which failed on GitHub, now renders.
Claude-Session: https://claude.ai/code/session_01TzirYkGmD88w4GipqivUtV
815 lines
85 KiB
YAML
815 lines
85 KiB
YAML
# luxd — mainnet's five-validator CD-owned StatefulSet.
|
|
# OnDelete and Retain/Retain preserve quorum and ledger data during releases.
|
|
# The pod anti-affinity selector is mandatory: all five votes need independent hosts.
|
|
affinity:
|
|
podAntiAffinity:
|
|
# PREFERRED, not required. Required means one luxd per host, and this cluster
|
|
# has two nodes -- so four of five were permanently Unschedulable, "node(s)
|
|
# didn't match pod anti-affinity rules", while the set reported 5 desired.
|
|
# Spread them if there is room and co-locate when there is not; the hanzod
|
|
# validators next door have run five this way all along.
|
|
preferredDuringSchedulingIgnoredDuringExecution:
|
|
- weight: 100
|
|
podAffinityTerm:
|
|
labelSelector:
|
|
matchExpressions:
|
|
- key: app.kubernetes.io/name
|
|
operator: In
|
|
values:
|
|
- luxd
|
|
topologyKey: kubernetes.io/hostname
|
|
# evo carries hanzo.ai/no-ci=true:NoSchedule so build work stays off the node that
|
|
# serves production. A validator is not build work, and without this line the only
|
|
# amd64 node refuses it outright. hanzod carries the same toleration.
|
|
tolerations:
|
|
- key: hanzo.ai/no-ci
|
|
operator: Exists
|
|
effect: NoSchedule
|
|
command:
|
|
- /bin/sh
|
|
- /scripts/startup.sh
|
|
component: validator
|
|
configMaps:
|
|
- data:
|
|
brand-upgrade.json: "{\n \"networkUpgradeOverrides\": {\n \"evmTimestamp\": 0,\n \"durangoTimestamp\"\
|
|
: 0,\n \"quasarTimestamp\": 1766708400,\n \"fortunaTimestamp\": 1766708400,\n \"graniteTimestamp\"\
|
|
: 1766708400\n },\n \"precompileUpgrades\": [\n {\n \"aiMiningConfig\": {\n \"\
|
|
blockTimestamp\": 1766708400\n }\n },\n {\n \"blake3Config\": {\n \"blockTimestamp\"\
|
|
: 1766708400\n }\n },\n {\n \"cggmp21Verify\": {\n \"blockTimestamp\": 1766708400\n\
|
|
\ }\n },\n {\n \"deadZeroConfig\": {\n \"blockTimestamp\": 1766708400\n \
|
|
\ }\n },\n {\n \"deadConfig\": {\n \"blockTimestamp\": 1766708400\n }\n\
|
|
\ },\n {\n \"deadFullConfig\": {\n \"blockTimestamp\": 1766708400\n }\n \
|
|
\ },\n {\n \"routerConfig\": {\n \"blockTimestamp\": 1766708400,\n \"v3QuoterAddress\"\
|
|
: \"0x0000000000000000000000000000000000000000\",\n \"v3FactoryAddress\": \"0x0000000000000000000000000000000000000000\"\
|
|
,\n \"v2RouterAddress\": \"0x0000000000000000000000000000000000000000\",\n \"v2FactoryAddress\"\
|
|
: \"0x0000000000000000000000000000000000000000\"\n }\n },\n {\n \"fheConfig\": {\n\
|
|
\ \"blockTimestamp\": 1766708400\n }\n },\n {\n \"frostVerify\": {\n \
|
|
\ \"blockTimestamp\": 1766708400\n }\n },\n {\n \"graphConfig\": {\n \"\
|
|
blockTimestamp\": 1766708400\n }\n },\n {\n \"hpkeConfig\": {\n \"blockTimestamp\"\
|
|
: 1766708400\n }\n },\n {\n \"mldsaVerify\": {\n \"blockTimestamp\": 1766708400\n\
|
|
\ }\n },\n {\n \"mlkemConfig\": {\n \"blockTimestamp\": 1766708400\n \
|
|
\ }\n },\n {\n \"ringConfig\": {\n \"blockTimestamp\": 1766708400\n }\n\
|
|
\ },\n {\n \"slhdsaVerify\": {\n \"blockTimestamp\": 1766708400\n }\n \
|
|
\ },\n {\n \"zkConfig\": {\n \"blockTimestamp\": 1766708400\n }\n },\n \
|
|
\ {\n \"bls12381G1AddConfig\": {\n \"blockTimestamp\": 1766708400\n }\n },\n\
|
|
\ {\n \"bls12381G1MulConfig\": {\n \"blockTimestamp\": 1766708400\n }\n },\n\
|
|
\ {\n \"bls12381G1MSMConfig\": {\n \"blockTimestamp\": 1766708400\n }\n },\n\
|
|
\ {\n \"bls12381G2AddConfig\": {\n \"blockTimestamp\": 1766708400\n }\n },\n\
|
|
\ {\n \"bls12381G2MulConfig\": {\n \"blockTimestamp\": 1766708400\n }\n },\n\
|
|
\ {\n \"bls12381G2MSMConfig\": {\n \"blockTimestamp\": 1766708400\n }\n },\n\
|
|
\ {\n \"bls12381PairingConfig\": {\n \"blockTimestamp\": 1766708400\n }\n \
|
|
\ },\n {\n \"pulsarVerify\": {\n \"blockTimestamp\": 1766708400\n }\n },\n\
|
|
\ {\n \"magnetarVerify\": {\n \"blockTimestamp\": 1766708400\n }\n },\n \
|
|
\ {\n \"p3qVerify\": {\n \"blockTimestamp\": 1766708400\n }\n },\n {\n\
|
|
\ \"coronaThreshold\": {\n \"blockTimestamp\": 1766708400\n }\n },\n {\n\
|
|
\ \"hqcEncapsulate\": {\n \"blockTimestamp\": 1766708400\n }\n },\n {\n \
|
|
\ \"curve25519Config\": {\n \"blockTimestamp\": 1766708400\n }\n },\n {\n\
|
|
\ \"x25519Config\": {\n \"blockTimestamp\": 1766708400\n }\n },\n {\n \
|
|
\ \"sr25519Verify\": {\n \"blockTimestamp\": 1766708400\n }\n },\n {\n \
|
|
\ \"babyjubjubConfig\": {\n \"blockTimestamp\": 1766708400\n }\n },\n {\n \
|
|
\ \"pedersenConfig\": {\n \"blockTimestamp\": 1766708400\n }\n },\n {\n \
|
|
\ \"poseidonConfig\": {\n \"blockTimestamp\": 1766708400\n }\n },\n {\n \"\
|
|
pastaConfig\": {\n \"blockTimestamp\": 1766708400\n }\n },\n {\n \"xwingConfig\"\
|
|
: {\n \"blockTimestamp\": 1766708400\n }\n },\n {\n \"computeMarketConfig\"\
|
|
: {\n \"blockTimestamp\": 1766708400\n }\n },\n {\n \"bridgeRegistrarConfig\"\
|
|
: {\n \"blockTimestamp\": 1766708400\n }\n },\n {\n \"stableSwapConfig\"\
|
|
: {\n \"blockTimestamp\": 1766708400\n }\n },\n {\n \"vrfConfig\": {\n \
|
|
\ \"blockTimestamp\": 1766708400\n }\n },\n {\n \"attestationConfig\": {\n\
|
|
\ \"blockTimestamp\": 1766708400\n }\n },\n {\n \"anchorConfig\": {\n \
|
|
\ \"blockTimestamp\": 1766708400\n }\n },\n {\n \"fixedPointMathConfig\": {\n\
|
|
\ \"blockTimestamp\": 1766708400\n }\n },\n {\n \"dexPositionManagerConfig\"\
|
|
: {\n \"blockTimestamp\": 1766708400\n }\n },\n {\n \"dexStateViewConfig\"\
|
|
: {\n \"blockTimestamp\": 1766708400\n }\n },\n {\n \"dexQuoterConfig\":\
|
|
\ {\n \"blockTimestamp\": 1766708400\n }\n },\n {\n \"dexSettleConfig\":\
|
|
\ {\n \"blockTimestamp\": 1766708400\n }\n }\n ],\n \"stateUpgrades\": []\n}"
|
|
cchain-upgrade.json: '{"networkUpgradeOverrides": {"evmTimestamp": 0, "durangoTimestamp": 0, "quasarTimestamp":
|
|
1766708400, "fortunaTimestamp": 1766708400, "graniteTimestamp": 1766708400}, "precompileUpgrades": [{"aiMiningConfig":
|
|
{"blockTimestamp": 1766708400}}, {"blake3Config": {"blockTimestamp": 1766708400}}, {"cggmp21Verify":
|
|
{"blockTimestamp": 1766708400}}, {"deadZeroConfig": {"blockTimestamp": 1766708400}}, {"deadConfig":
|
|
{"blockTimestamp": 1766708400}}, {"deadFullConfig": {"blockTimestamp": 1766708400}}, {"routerConfig":
|
|
{"blockTimestamp": 1766708400, "v3QuoterAddress": "0x0000000000000000000000000000000000000000", "v3FactoryAddress":
|
|
"0x0000000000000000000000000000000000000000", "v2RouterAddress": "0x0000000000000000000000000000000000000000",
|
|
"v2FactoryAddress": "0x0000000000000000000000000000000000000000"}}, {"fheConfig": {"blockTimestamp":
|
|
1766708400}}, {"frostVerify": {"blockTimestamp": 1766708400}}, {"graphConfig": {"blockTimestamp": 1766708400}},
|
|
{"hpkeConfig": {"blockTimestamp": 1766708400}}, {"mldsaVerify": {"blockTimestamp": 1766708400}}, {"mlkemConfig":
|
|
{"blockTimestamp": 1766708400}}, {"ringConfig": {"blockTimestamp": 1766708400}}, {"slhdsaVerify": {"blockTimestamp":
|
|
1766708400}}, {"zkConfig": {"blockTimestamp": 1766708400}}, {"bls12381G1AddConfig": {"blockTimestamp":
|
|
1766708400}}, {"bls12381G1MulConfig": {"blockTimestamp": 1766708400}}, {"bls12381G1MSMConfig": {"blockTimestamp":
|
|
1766708400}}, {"bls12381G2AddConfig": {"blockTimestamp": 1766708400}}, {"bls12381G2MulConfig": {"blockTimestamp":
|
|
1766708400}}, {"bls12381G2MSMConfig": {"blockTimestamp": 1766708400}}, {"bls12381PairingConfig": {"blockTimestamp":
|
|
1766708400}}, {"dexPositionManagerConfig": {"blockTimestamp": 1766708400}}, {"dexStateViewConfig": {"blockTimestamp":
|
|
1766708400}}, {"dexQuoterConfig": {"blockTimestamp": 1766708400}}, {"dexSettleConfig": {"blockTimestamp":
|
|
1766708400}}, {"pulsarVerify": {"blockTimestamp": 1782864000}}, {"magnetarVerify": {"blockTimestamp":
|
|
1782864000}}, {"p3qVerify": {"blockTimestamp": 1782864000}}, {"coronaThreshold": {"blockTimestamp":
|
|
1782864000}}, {"hqcEncapsulate": {"blockTimestamp": 1782864000}}, {"curve25519Config": {"blockTimestamp":
|
|
1782864000}}, {"x25519Config": {"blockTimestamp": 1782864000}}, {"sr25519Verify": {"blockTimestamp":
|
|
1782864000}}, {"babyjubjubConfig": {"blockTimestamp": 1782864000}}, {"pedersenConfig": {"blockTimestamp":
|
|
1782864000}}, {"poseidonConfig": {"blockTimestamp": 1782864000}}, {"pastaConfig": {"blockTimestamp":
|
|
1782864000}}, {"xwingConfig": {"blockTimestamp": 1782864000}}, {"computeMarketConfig": {"blockTimestamp":
|
|
1782864000}}, {"bridgeRegistrarConfig": {"blockTimestamp": 1782864000}}, {"stableSwapConfig": {"blockTimestamp":
|
|
1782864000}}, {"vrfConfig": {"blockTimestamp": 1782864000}}, {"attestationConfig": {"blockTimestamp":
|
|
1782864000}}, {"anchorConfig": {"blockTimestamp": 1782864000}}, {"fixedPointMathConfig": {"blockTimestamp":
|
|
1782864000}}], "stateUpgrades": [{"blockTimestamp": 1782953071, "accounts": {"0x80bBc7C4C7a59C899D1B37BC14539A22D5830a84":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000003": "0x00000000000000000000000051284dc2133e8d3a8e213dca6a6fa768cfdfcce2"}},
|
|
"0xD173926A10A0C4eCd3A51B1422270b65Df0551c1": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000001":
|
|
"0x00000000000000000000000051284dc2133e8d3a8e213dca6a6fa768cfdfcce2"}}, "0xeac0a50112b5ee20cc18e42ba4d37777012afd0d":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000051284dc2133e8d3a8e213dca6a6fa768cfdfcce2"}},
|
|
"0xaa6a41cacb18bed5b98059a5fa30f9dbabe0cc64": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000001":
|
|
"0x00000000000000000000000051284dc2133e8d3a8e213dca6a6fa768cfdfcce2"}}, "0xdf1de693c31e2a5eb869c329529623556b20abf3":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005": "0x000000000000000000000051284dc2133e8d3a8e213dca6a6fa768cfdfcce212"}},
|
|
"0x8031e9b0d02a792cfefaa2bdca6e1289d385426f": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005":
|
|
"0x000000000000000000000051284dc2133e8d3a8e213dca6a6fa768cfdfcce212"}}, "0x79608e442d046ea6d3125931a33ce971ad99c6f9":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005": "0x000000000000000000000051284dc2133e8d3a8e213dca6a6fa768cfdfcce212"}},
|
|
"0xa85eb8e163f4d70bfc43a4f3fcc9a8dfc42b1ae4": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005":
|
|
"0x000000000000000000000051284dc2133e8d3a8e213dca6a6fa768cfdfcce212"}}, "0x724f4ef0400386fc8c1011788af733bd367ac00f":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005": "0x000000000000000000000051284dc2133e8d3a8e213dca6a6fa768cfdfcce212"}},
|
|
"0x3569c06a80c148d7b4b67f8a06125f6d59774243": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005":
|
|
"0x000000000000000000000051284dc2133e8d3a8e213dca6a6fa768cfdfcce212"}}, "0x60E0a8167FC13dE89348978860466C9ceC24B9ba":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005": "0x00000000000000000000000051284dc2133e8d3a8e213dca6a6fa768cfdfcce2"}},
|
|
"0x1E48D32a4F5e9f08DB9aE4959163300FaF8A6C8e": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005":
|
|
"0x00000000000000000000000051284dc2133e8d3a8e213dca6a6fa768cfdfcce2"}}, "0x848Cff46eb323f323b6Bbe1Df274E40793d7f2c2":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005": "0x00000000000000000000000051284dc2133e8d3a8e213dca6a6fa768cfdfcce2"}},
|
|
"0x5E5290f350352768bD2bfC59c2DA15DD04A7cB88": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005":
|
|
"0x00000000000000000000000051284dc2133e8d3a8e213dca6a6fa768cfdfcce2"}}, "0x26B40f650156C7EbF9e087Dd0dca181Fe87625B7":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005": "0x00000000000000000000000051284dc2133e8d3a8e213dca6a6fa768cfdfcce2"}},
|
|
"0x6EdcF3645DeF09DB45050638c41157D8B9FEa1cf": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005":
|
|
"0x00000000000000000000000051284dc2133e8d3a8e213dca6a6fa768cfdfcce2"}}, "0x28BfC5DD4B7E15659e41190983e5fE3df1132bB9":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005": "0x00000000000000000000000051284dc2133e8d3a8e213dca6a6fa768cfdfcce2"}},
|
|
"0x3078847F879A33994cDa2Ec1540ca52b5E0eE2e5": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005":
|
|
"0x00000000000000000000000051284dc2133e8d3a8e213dca6a6fa768cfdfcce2"}}, "0x8B982132d639527E8a0eAAD385f97719af8f5e04":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005": "0x00000000000000000000000051284dc2133e8d3a8e213dca6a6fa768cfdfcce2"}},
|
|
"0x3141b94b89691009b950c96e97Bff48e0C543E3C": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005":
|
|
"0x00000000000000000000000051284dc2133e8d3a8e213dca6a6fa768cfdfcce2"}}, "0x0e4bd0dd67c15DECFbBBDBbE07Fc9d51D737693D":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005": "0x00000000000000000000000051284dc2133e8d3a8e213dca6a6fa768cfdfcce2"}},
|
|
"0xf5a313885832d4fc71d1ef80115197c4479b58c8": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005":
|
|
"0x00000000000000000000000051284dc2133e8d3a8e213dca6a6fa768cfdfcce2"}}, "0x1af00a2590a834d14f4a8a26d1b03ebba8cf7961":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005": "0x00000000000000000000000051284dc2133e8d3a8e213dca6a6fa768cfdfcce2"}}}},
|
|
{"blockTimestamp": 1783923968, "accounts": {"0x80bBc7C4C7a59C899D1B37BC14539A22D5830a84": {"storage":
|
|
{"0x0000000000000000000000000000000000000000000000000000000000000003": "0x000000000000000000000000e54caf7e0c04e0ec69be9302b97613de89745ddc"}},
|
|
"0xD173926A10A0C4eCd3A51B1422270b65Df0551c1": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000001":
|
|
"0x000000000000000000000000e54caf7e0c04e0ec69be9302b97613de89745ddc"}}, "0xeac0a50112b5ee20cc18e42ba4d37777012afd0d":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000001": "0x000000000000000000000000e54caf7e0c04e0ec69be9302b97613de89745ddc"}},
|
|
"0xaa6a41cacb18bed5b98059a5fa30f9dbabe0cc64": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000001":
|
|
"0x000000000000000000000000e54caf7e0c04e0ec69be9302b97613de89745ddc"}}, "0xdf1de693c31e2a5eb869c329529623556b20abf3":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005": "0x0000000000000000000000e54caf7e0c04e0ec69be9302b97613de89745ddc12"}},
|
|
"0x8031e9b0d02a792cfefaa2bdca6e1289d385426f": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005":
|
|
"0x0000000000000000000000e54caf7e0c04e0ec69be9302b97613de89745ddc12"}}, "0x79608e442d046ea6d3125931a33ce971ad99c6f9":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005": "0x0000000000000000000000e54caf7e0c04e0ec69be9302b97613de89745ddc12"}},
|
|
"0xa85eb8e163f4d70bfc43a4f3fcc9a8dfc42b1ae4": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005":
|
|
"0x0000000000000000000000e54caf7e0c04e0ec69be9302b97613de89745ddc12"}}, "0x724f4ef0400386fc8c1011788af733bd367ac00f":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005": "0x0000000000000000000000e54caf7e0c04e0ec69be9302b97613de89745ddc12"}},
|
|
"0x3569c06a80c148d7b4b67f8a06125f6d59774243": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005":
|
|
"0x0000000000000000000000e54caf7e0c04e0ec69be9302b97613de89745ddc12"}}, "0x60E0a8167FC13dE89348978860466C9ceC24B9ba":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005": "0x000000000000000000000000e54caf7e0c04e0ec69be9302b97613de89745ddc"}},
|
|
"0x1E48D32a4F5e9f08DB9aE4959163300FaF8A6C8e": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005":
|
|
"0x000000000000000000000000e54caf7e0c04e0ec69be9302b97613de89745ddc"}}, "0x848Cff46eb323f323b6Bbe1Df274E40793d7f2c2":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005": "0x000000000000000000000000e54caf7e0c04e0ec69be9302b97613de89745ddc"}},
|
|
"0x5E5290f350352768bD2bfC59c2DA15DD04A7cB88": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005":
|
|
"0x000000000000000000000000e54caf7e0c04e0ec69be9302b97613de89745ddc"}}, "0x26B40f650156C7EbF9e087Dd0dca181Fe87625B7":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005": "0x000000000000000000000000e54caf7e0c04e0ec69be9302b97613de89745ddc"}},
|
|
"0x6EdcF3645DeF09DB45050638c41157D8B9FEa1cf": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005":
|
|
"0x000000000000000000000000e54caf7e0c04e0ec69be9302b97613de89745ddc"}}, "0x28BfC5DD4B7E15659e41190983e5fE3df1132bB9":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005": "0x000000000000000000000000e54caf7e0c04e0ec69be9302b97613de89745ddc"}},
|
|
"0x3078847F879A33994cDa2Ec1540ca52b5E0eE2e5": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005":
|
|
"0x000000000000000000000000e54caf7e0c04e0ec69be9302b97613de89745ddc"}}, "0x8B982132d639527E8a0eAAD385f97719af8f5e04":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005": "0x000000000000000000000000e54caf7e0c04e0ec69be9302b97613de89745ddc"}},
|
|
"0x3141b94b89691009b950c96e97Bff48e0C543E3C": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005":
|
|
"0x000000000000000000000000e54caf7e0c04e0ec69be9302b97613de89745ddc"}}, "0x0e4bd0dd67c15DECFbBBDBbE07Fc9d51D737693D":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005": "0x000000000000000000000000e54caf7e0c04e0ec69be9302b97613de89745ddc"}},
|
|
"0xf5a313885832d4fc71d1ef80115197c4479b58c8": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005":
|
|
"0x000000000000000000000000e54caf7e0c04e0ec69be9302b97613de89745ddc"}}, "0x1af00a2590a834d14f4a8a26d1b03ebba8cf7961":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005": "0x000000000000000000000000e54caf7e0c04e0ec69be9302b97613de89745ddc"}}}},
|
|
{"blockTimestamp": 1785944022, "accounts": {"0x80bBc7C4C7a59C899D1B37BC14539A22D5830a84": {"storage":
|
|
{"0x0000000000000000000000000000000000000000000000000000000000000003": "0x000000000000000000000000f0d19cccb8e33663e9bdee6e40474f1e3fd1f2bd"}},
|
|
"0xD173926A10A0C4eCd3A51B1422270b65Df0551c1": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000001":
|
|
"0x000000000000000000000000f0d19cccb8e33663e9bdee6e40474f1e3fd1f2bd"}}, "0xeac0a50112b5ee20cc18e42ba4d37777012afd0d":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000001": "0x000000000000000000000000f0d19cccb8e33663e9bdee6e40474f1e3fd1f2bd"}},
|
|
"0xaa6a41cacb18bed5b98059a5fa30f9dbabe0cc64": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000001":
|
|
"0x000000000000000000000000f0d19cccb8e33663e9bdee6e40474f1e3fd1f2bd"}}, "0xdf1de693c31e2a5eb869c329529623556b20abf3":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005": "0x000000000000000000000000f0d19cccb8e33663e9bdee6e40474f1e3fd1f2bd"}},
|
|
"0x8031e9b0d02a792cfefaa2bdca6e1289d385426f": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005":
|
|
"0x000000000000000000000000f0d19cccb8e33663e9bdee6e40474f1e3fd1f2bd"}}, "0x79608e442d046ea6d3125931a33ce971ad99c6f9":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005": "0x000000000000000000000000f0d19cccb8e33663e9bdee6e40474f1e3fd1f2bd"}},
|
|
"0xa85eb8e163f4d70bfc43a4f3fcc9a8dfc42b1ae4": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005":
|
|
"0x000000000000000000000000f0d19cccb8e33663e9bdee6e40474f1e3fd1f2bd"}}, "0x724f4ef0400386fc8c1011788af733bd367ac00f":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005": "0x000000000000000000000000f0d19cccb8e33663e9bdee6e40474f1e3fd1f2bd"}},
|
|
"0x3569c06a80c148d7b4b67f8a06125f6d59774243": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005":
|
|
"0x000000000000000000000000f0d19cccb8e33663e9bdee6e40474f1e3fd1f2bd"}}, "0x60E0a8167FC13dE89348978860466C9ceC24B9ba":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005": "0x000000000000000000000000f0d19cccb8e33663e9bdee6e40474f1e3fd1f2bd"}},
|
|
"0x1E48D32a4F5e9f08DB9aE4959163300FaF8A6C8e": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005":
|
|
"0x000000000000000000000000f0d19cccb8e33663e9bdee6e40474f1e3fd1f2bd"}}, "0x848Cff46eb323f323b6Bbe1Df274E40793d7f2c2":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005": "0x000000000000000000000000f0d19cccb8e33663e9bdee6e40474f1e3fd1f2bd"}},
|
|
"0x5E5290f350352768bD2bfC59c2DA15DD04A7cB88": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005":
|
|
"0x000000000000000000000000f0d19cccb8e33663e9bdee6e40474f1e3fd1f2bd"}}, "0x26B40f650156C7EbF9e087Dd0dca181Fe87625B7":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005": "0x000000000000000000000000f0d19cccb8e33663e9bdee6e40474f1e3fd1f2bd"}},
|
|
"0x6EdcF3645DeF09DB45050638c41157D8B9FEa1cf": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005":
|
|
"0x000000000000000000000000f0d19cccb8e33663e9bdee6e40474f1e3fd1f2bd"}}, "0x28BfC5DD4B7E15659e41190983e5fE3df1132bB9":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005": "0x000000000000000000000000f0d19cccb8e33663e9bdee6e40474f1e3fd1f2bd"}},
|
|
"0x3078847F879A33994cDa2Ec1540ca52b5E0eE2e5": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005":
|
|
"0x000000000000000000000000f0d19cccb8e33663e9bdee6e40474f1e3fd1f2bd"}}, "0x8B982132d639527E8a0eAAD385f97719af8f5e04":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005": "0x000000000000000000000000f0d19cccb8e33663e9bdee6e40474f1e3fd1f2bd"}},
|
|
"0x3141b94b89691009b950c96e97Bff48e0C543E3C": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005":
|
|
"0x000000000000000000000000f0d19cccb8e33663e9bdee6e40474f1e3fd1f2bd"}}, "0x0e4bd0dd67c15DECFbBBDBbE07Fc9d51D737693D":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005": "0x000000000000000000000000f0d19cccb8e33663e9bdee6e40474f1e3fd1f2bd"}},
|
|
"0xf5a313885832d4fc71d1ef80115197c4479b58c8": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005":
|
|
"0x000000000000000000000000f0d19cccb8e33663e9bdee6e40474f1e3fd1f2bd"}}, "0x1af00a2590a834d14f4a8a26d1b03ebba8cf7961":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005": "0x000000000000000000000000f0d19cccb8e33663e9bdee6e40474f1e3fd1f2bd"}}}},
|
|
{"blockTimestamp": 1785949619, "accounts": {"0x3569c06a80c148d7b4b67f8a06125f6d59774243": {"storage":
|
|
{"0x0000000000000000000000000000000000000000000000000000000000000005": "0x0000000000000000000000f0d19cccb8e33663e9bdee6e40474f1e3fd1f2bd12"}},
|
|
"0x724f4ef0400386fc8c1011788af733bd367ac00f": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005":
|
|
"0x0000000000000000000000f0d19cccb8e33663e9bdee6e40474f1e3fd1f2bd12"}}, "0x79608e442d046ea6d3125931a33ce971ad99c6f9":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005": "0x0000000000000000000000f0d19cccb8e33663e9bdee6e40474f1e3fd1f2bd12"}},
|
|
"0x8031e9b0d02a792cfefaa2bdca6e1289d385426f": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005":
|
|
"0x0000000000000000000000f0d19cccb8e33663e9bdee6e40474f1e3fd1f2bd12"}}, "0xa85eb8e163f4d70bfc43a4f3fcc9a8dfc42b1ae4":
|
|
{"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005": "0x0000000000000000000000f0d19cccb8e33663e9bdee6e40474f1e3fd1f2bd12"}},
|
|
"0xdf1de693c31e2a5eb869c329529623556b20abf3": {"storage": {"0x0000000000000000000000000000000000000000000000000000000000000005":
|
|
"0x0000000000000000000000f0d19cccb8e33663e9bdee6e40474f1e3fd1f2bd12"}}}}]}'
|
|
startup.sh: "#!/bin/sh\nset -e\nexport HOME=/data\n\n# The hourly C-Chain export writes SERVER-SIDE into this node own /data.\n# A replaced PVC has no such directory and admin_exportChain does not create\n# one - it fails \"no such file or directory\", which is how the archive died\n# again the moment luxd-0 was rebuilt. The node owns its filesystem.\nmkdir -p /data/exports\n\nPOD_INDEX=${HOSTNAME##*-}\necho \"=== luxd startup\
|
|
\ for $HOSTNAME (network-id=1) ===\"\n\n# Tenant RLP imports are owned by the operator's tenant-import\
|
|
\ init\n# container; the wipe + download + JSON marker drop happen before this\n# script runs. Below\
|
|
\ we only:\n# 1. detect whether any request markers are pending,\n# 2. enable --api-admin-enabled\
|
|
\ if so (luxd needs the admin API to\n# service admin_importChain),\n# 3. after luxd is healthy,\
|
|
\ loop over pending markers, call\n# admin_importChain via /v1/chain/<chainAlias>/rpc, then write\
|
|
\ the\n# operator-named sentinel and remove the marker.\n# Re-firing after a spec fix is automatic\
|
|
\ \u2014 the operator's content-hash\n# addressing emits a new request marker on the next reconcile.\n\
|
|
TENANT_REQ_GLOB=\"/data/db/.tenant-import-request-*.json\"\nif ls $TENANT_REQ_GLOB >/dev/null 2>&1;\
|
|
\ then\n ADMIN_NEEDED=\"true\"\n echo \"[IMPORT] Pending tenant-import request markers found;\
|
|
\ admin API will be enabled.\"\nelse\n ADMIN_NEEDED=\"false\"\n echo \"[IMPORT] No pending tenant-import\
|
|
\ request markers.\"\nfi\n\n# DB wipe disabled - preserve chain state across pod restarts.\n# EXCEPTION\
|
|
\ - per-pod clean rejoin. `.wipe-state` discards ALL local chain\n# state so this node re-syncs\
|
|
\ from healthy peers the normal way, keeping only\n# node identity (/data/staking) and operator\
|
|
\ markers. A PARTIAL wipe cannot\n# work: the proposervm finality index lives in /data/db while\
|
|
\ the inner EVM\n# lives in /data/chainData, so removing one without the other only inverts the\n\
|
|
# skew (\"index is BEHIND the inner VM tip\" becomes \"index is AHEAD\"). No chain\n# ID is named\
|
|
\ here on purpose - the previous hardcoded C-Chain ID drifted after\n# the re-genesis and silently\
|
|
\ made the wipe a no-op for weeks.\nif [ -f /data/db/.wipe-state ]; then\n # \u2500\u2500 SAFETY\
|
|
\ JOURNAL MUST SURVIVE THE WIPE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\
|
|
\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\
|
|
\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n # The vote-guard is this validator's ANTI-EQUIVOCATION\
|
|
\ memory: which block it\n # already signed at each height. It lives INSIDE disposable chain state\
|
|
\ at\n # /data/chainData/network-1/<chainID>/vote-guard, so a naive `rm -rf\n # /data/chainData`\
|
|
\ silently destroys it. An RLP import then rebuilds every\n # block and index but CANNOT rebuild\
|
|
\ the guard -- producing a validator with\n # the SAME KEY that has forgotten its prior votes.\
|
|
\ That validator can sign a\n # different block at a height it already signed: equivocation, with\
|
|
\ a valid\n # signature, from an identity the network still trusts. Losing the guard is\n # strictly\
|
|
\ worse than losing the chain data.\n # Preserve every vote-guard (any chain), wipe, restore, and\
|
|
\ verify byte-for-\n # byte. If restore fails we leave a QUARANTINE marker rather than starting\
|
|
\ a\n # validator that could double-sign.\n SAFE=/data/safety\n mkdir -p \"$SAFE\"\n echo \"\
|
|
[WIPE] preserving safety journals before wipe\"\n for VG in /data/chainData/*/*/vote-guard; do\n\
|
|
\ [ -f \"$VG\" ] || continue\n CID=$(basename \"$(dirname \"$VG\")\"); NETD=$(basename \"\
|
|
$(dirname \"$(dirname \"$VG\")\")\")\n cp -p \"$VG\" \"$SAFE/vote-guard.$NETD.$CID\"\n echo\
|
|
\ \"[WIPE] saved $NETD/$CID vote-guard ($(stat -c%s \"$VG\") bytes)\"\n done\n rm -f /data/db/.wipe-state\n\
|
|
\ rm -rf /data/chainData\n find /data/db -mindepth 1 -maxdepth 1 -type d -exec rm -rf {} +\n \
|
|
\ echo \"[WIPE] discarded /data/chainData + every /data/db database; /data/staking and markers preserved\"\
|
|
\n RESTORE_FAIL=0\n for SV in \"$SAFE\"/vote-guard.*; do\n [ -f \"$SV\" ] || continue\n \
|
|
\ B=$(basename \"$SV\"); REST=${B#vote-guard.}\n NETD=${REST%%.*}; CID=${REST#*.}\n D=\"/data/chainData/$NETD/$CID\"\
|
|
\n mkdir -p \"$D\"\n cp -p \"$SV\" \"$D/vote-guard\"\n if cmp -s \"$SV\" \"$D/vote-guard\"\
|
|
; then\n echo \"[WIPE] restored $NETD/$CID vote-guard, byte-identical\"\n else\n \
|
|
\ echo \"[WIPE] ERROR: vote-guard restore MISMATCH for $NETD/$CID\" >&2\n RESTORE_FAIL=1\n\
|
|
\ fi\n done\n if [ \"$RESTORE_FAIL\" = \"1\" ]; then\n touch /data/db/.quarantine-no-sign\n\
|
|
\ echo \"[WIPE] SAFETY JOURNAL NOT RESTORED -> wrote /data/db/.quarantine-no-sign\" >&2\n fi\n\
|
|
fi\n\n\n# \u2500\u2500 One-shot vote-guard compaction to the CERTIFIED floor \u2500\u2500\u2500\u2500\
|
|
\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\
|
|
\n# `.guard-reset` holds a base64 vote-guard blob. Applied HERE, before luxd\n# starts, so a graceful\
|
|
\ shutdown cannot overwrite it and no SIGKILL/torn-write\n# is needed to install it.\n#\n# WHY THIS\
|
|
\ EXISTS. The guard binds this validator to ONE block per height and\n# refuses to sign a different\
|
|
\ one there. That is correct and must not be\n# weakened. But the fleet deadlocked at 1098192: each\
|
|
\ node was bound to a\n# DIFFERENT block ID at that height (luxd-3 -> J42iGXKm\u2026, luxd-4 ->\
|
|
\ HnK3hsbV\u2026)\n# while the builder proposed a THIRD (sYFeHHe9\u2026). Peers received it, verified\n\
|
|
# it, processed it through consensus \u2014 and no one could sign, so no certificate\n# ever assembled.\
|
|
\ Rebuilt and re-pushed every ~16s, forever.\n#\n# Those bindings are votes for blocks that NEVER\
|
|
\ CERTIFIED: the durable floor\n# is 1098191 on every node, so nothing at 1098192+ is finalized\
|
|
\ history.\n# Compacting bindings above the floor discards only uncertified votes; the\n# floor\
|
|
\ itself is preserved verbatim and never lowered. The prior guard is kept\n# alongside as `.pre-reset`\
|
|
\ \u2014 nothing is deleted.\nif [ -f /data/db/.guard-reset ]; then\n for VG in /data/chainData/*/*/vote-guard;\
|
|
\ do\n [ -f \"$VG\" ] || continue\n cp -p \"$VG\" \"$VG.pre-reset\"\n if base64 -d < /data/db/.guard-reset\
|
|
\ > \"$VG.tmp\" && [ -s \"$VG.tmp\" ]; then\n mv \"$VG.tmp\" \"$VG\"\n echo \"[GUARD-RESET]\
|
|
\ compacted $VG to $(stat -c%s \"$VG\") bytes; prior guard kept at $VG.pre-reset\"\n else\n \
|
|
\ rm -f \"$VG.tmp\"\n echo \"[GUARD-RESET] ERROR decoding blob; left $VG untouched\" >&2\n\
|
|
\ fi\n done\n mv /data/db/.guard-reset /data/db/.guard-reset.done\n echo \"[GUARD-RESET] marker\
|
|
\ consumed -> .guard-reset.done\"\nfi\n\n# Write genesis from base64; translate legacy alloc field\
|
|
\ names\n# (ethAddr/luxAddr) \u2192 canonical luxfi/genesis v1.12.19+ (evmAddr/utxoAddr).\n# Embedded\
|
|
\ blob is the source of truth for amounts/addresses; the sed\n# is purely a rename for forward-compat\
|
|
\ with the current luxd binary.\necho \"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\"\
|
|
\ | base64 -d | gunzip > /data/genesis.json\n# Translate legacy alloc field names \u2192 canonical\
|
|
\ luxfi/genesis v1.12.19+.\n# Idempotent: re-running over already-translated content is a no-op\n\
|
|
# (the new field names won't match the old patterns).\nsed -i 's/\"ethAddr\":/\"evmAddr\":/g; s/\"\
|
|
luxAddr\":/\"utxoAddr\":/g' /data/genesis.json\necho \"[GENESIS] Written genesis.json (alloc field\
|
|
\ names translated to v1.12.19+)\"\n\n# Copy staking keys (skip if already on PVC and secret is\
|
|
\ empty)\nIDX=${HOSTNAME##*-}\nmkdir -p /data/staking\nif [ -f \"/staking-keys/staker-${IDX}.crt\"\
|
|
\ ]; then\n cp /staking-keys/staker-${IDX}.crt /data/staking/staker.crt\n cp /staking-keys/staker-${IDX}.key\
|
|
\ /data/staking/staker.key\n if [ -f \"/staking-keys/signer-${IDX}.key\" ]; then\n cp /staking-keys/signer-${IDX}.key\
|
|
\ /data/staking/signer.key\n else\n rm -f /data/staking/signer.key\n fi\n echo \"[STAKING]\
|
|
\ Keys copied from secret for index ${IDX}\"\nelif [ -f /data/staking/staker.crt ] && [ -f /data/staking/staker.key\
|
|
\ ]; then\n echo \"[STAKING] Using existing keys on PVC for index ${IDX}\"\nelse\n echo \"[STAKING]\
|
|
\ ERROR: No staking keys available!\" >&2\n exit 1\nfi\n\n# Copy plugins from image (always overwrite\
|
|
\ \u2014 image builds from source with full precompile registry)\nmkdir -p /data/plugins\nif [ -d\
|
|
\ /luxd/build/plugins ] && ls /luxd/build/plugins/* >/dev/null 2>&1; then\n for f in /luxd/build/plugins/*;\
|
|
\ do\n dest=\"/data/plugins/$(basename $f)\"\n # Compare CONTENT, not size. This was `stat\
|
|
\ -c%s` on both sides, and the\n # stale dexvm on the PVC and the corrected one in the image\
|
|
\ are BOTH\n # exactly 15917218 bytes. So the copy was skipped on every start from\n # 2026-07-14\
|
|
\ to 2026-08-04 while logging \"unchanged\", D-Chain died in the\n # ZAP init handshake on all\
|
|
\ five validators, and the node restarted\n # forever. A size is not an identity.\n if ! cmp\
|
|
\ -s \"$f\" \"$dest\"; then\n cp \"$f\" \"$dest\"\n chmod +x \"$dest\"\n echo \"\
|
|
[PLUGINS] Updated $(basename $f) from image\"\n else\n echo \"[PLUGINS] $(basename $f) unchanged\"\
|
|
\n fi\n done\nelse\n echo \"[PLUGINS] No plugins in image, using existing PVC plugins\"\nfi\n\
|
|
ls -la /data/plugins/ 2>/dev/null || echo \"[PLUGINS] No plugins directory\"\n\n# Bootstrap nodes\
|
|
\ (exclude self)\nDNS_PREFIX=\"luxd\"\nDNS_SUFFIX=\"luxd-headless.lux-mainnet.svc.cluster.local\"\
|
|
\n# Default beacon set = every peer but self. A rejoining node names the frontier\n# by \u2154-of-BEACON\
|
|
\ supermajority (consensus engine/chain/bootstrap/bootstrapper.go\n# passNoQuorum), so when the\
|
|
\ fleet is SPLIT across tips no frontier can be named\n# and the node sits at height 0 forever.\
|
|
\ `.bootstrap-from` (content: comma- or\n# space-separated pod indices) narrows the beacon set to\
|
|
\ nodes known to hold the\n# tip being adopted, which lets the \u2154 vote resolve. Use ONLY to\
|
|
\ fast-forward onto\n# a strictly-extending tip that no other branch competes with - narrowing beacons\n\
|
|
# narrows who gets to vote, so it is an operator assertion about which tip is\n# canonical, not\
|
|
\ a consensus result.\nBOOTSTRAP_IDX=\"0 1 2 3 4\"\nif [ -f /data/db/.bootstrap-from ]; then\n \
|
|
\ BOOTSTRAP_IDX=$(tr ',' ' ' < /data/db/.bootstrap-from)\n echo \"[BOOTSTRAP] .bootstrap-from present\
|
|
\ -> beacon set narrowed to pod indices: $BOOTSTRAP_IDX\"\nfi\nBOOTSTRAP_NODES=\"\"\nfor j in $BOOTSTRAP_IDX;\
|
|
\ do\n if [ \"$POD_INDEX\" != \"$j\" ]; then\n EP=\"${DNS_PREFIX}-${j}.${DNS_SUFFIX}:9631\"\n\
|
|
\ BOOTSTRAP_NODES=\"${BOOTSTRAP_NODES:+$BOOTSTRAP_NODES,}$EP\"\n fi\ndone\n\n# --- Public IP:\
|
|
\ the pod IP, and only the pod IP ---\n# The staking mesh is cluster-internal: svc/luxd-N are ClusterIP\
|
|
\ and the\n# staking port is published nowhere, so a pod IP is the only address a peer\n# can dial.\
|
|
\ The k8s-API LoadBalancer-ingress lookup that used to live here\n# POISONED the mesh. The luxd-N\
|
|
\ Services were flipped LoadBalancer ->\n# ClusterIP (their DO load-balancer-id annotations are\
|
|
\ still on them) but the\n# pods that had booted while the LB ingress IPs were still published signed\n\
|
|
# and gossiped those IPs. luxd's ipTracker replaces a configured bootstrap\n# endpoint with the\
|
|
\ peer's signed IP, so every node then dialled a blackholed\n# address: measured lux-mainnet mesh\
|
|
\ 2/3/4/1/2 of 4 peers, under\n# --consensus-quorum-size=4, which turns every consensus poll into\
|
|
\ a ~16s\n# timeout and stretches C-Chain inclusion to 100-185s. lux-devnet and\n# lux-testnet\
|
|
\ advertise the pod IP and hold 4/4 each. One way, every net.\nPUBLIC_IP=$(hostname -i | awk '{print\
|
|
\ $1}')\necho \"[IP] Pod: $PUBLIC_IP\"\n\n# luxd arguments\nARGS=\"--network-id=1\"\n# Enable admin\
|
|
\ API during import\nif [ \"$ADMIN_NEEDED\" = \"true\" ]; then\n ADMIN_ENABLED=\"true\"\nelse\n\
|
|
\ ADMIN_ENABLED=\"false\"\nfi\nARGS=\"$ARGS --http-host=0.0.0.0 --http-port=9630 --http-allowed-hosts=*\"\
|
|
\nARGS=\"$ARGS --staking-port=9631\"\nARGS=\"$ARGS --data-dir=/data --db-type=zapdb\"\nARGS=\"$ARGS\
|
|
\ --index-enabled=true\"\nARGS=\"$ARGS --api-admin-enabled=$ADMIN_ENABLED --api-metrics-enabled=true\
|
|
\ --api-keystore-enabled=false\"\nARGS=\"$ARGS --log-level=info\"\nARGS=\"$ARGS --plugin-dir=/data/plugins\"\
|
|
\nARGS=\"$ARGS --staking-tls-cert-file=/data/staking/staker.crt\"\nARGS=\"$ARGS --staking-tls-key-file=/data/staking/staker.key\"\
|
|
\n[ -f \"/data/staking/signer.key\" ] && ARGS=\"$ARGS --staking-signer-key-file=/data/staking/signer.key\"\
|
|
\nARGS=\"$ARGS --network-compression-type=zstd\"\nARGS=\"$ARGS --consensus-sample-size=6 --consensus-quorum-size=4\
|
|
\ --consensus-commit-threshold=2\"\nARGS=\"$ARGS --sybil-protection-enabled=true\"\nSKIP_BS=false\n\
|
|
if [ -f /data/db/.allow-bootstrap ]; then SKIP_BS=false; echo \"[BOOTSTRAP] .allow-bootstrap present\
|
|
\ -> --skip-bootstrap=false (native peer-sync catchup enabled for this pod)\"; fi\nARGS=\"$ARGS --network-require-validator-to-connect=true --network-allow-private-ips=true\"\
|
|
\nARGS=\"$ARGS --genesis-file=/data/genesis.json\"\n# Track every CreateChainTx-registered EVM in\
|
|
\ P-state. Was a stale pin\n# to 5 pre-1a recovery chain IDs; switched to --track-all-chains on\n\
|
|
# 2026-06-04 L2 spin-up (hanzo+zoo+pars+spc fresh chain IDs\n# never made the old --track-chains\
|
|
\ list, so they 404'd at the gateway).\nARGS=\"$ARGS --track-all-chains\"\nARGS=\"$ARGS --dex-validator=true\"\
|
|
\n[ -n \"$PUBLIC_IP\" ] && ARGS=\"$ARGS --public-ip=$PUBLIC_IP\"\n[ -n \"$BOOTSTRAP_NODES\" ] &&\
|
|
\ ARGS=\"$ARGS --bootstrap-nodes=$BOOTSTRAP_NODES\"\n\n# \u2500\u2500 Activate Cancun (MCOPY etc)\
|
|
\ + canonical precompile schedule \u2500\u2500\n# The canonical lux-mainnet upgrade.json (luxfi/genesis\n\
|
|
# configs/mainnet/upgrade.json) is bundled in THIS ConfigMap under\n# the `cchain-upgrade.json`\
|
|
\ data key and projected at\n# /scripts/cchain-upgrade.json. We seed it ONLY to the primary\n# C-Chain's\
|
|
\ per-chain config dir.\n#\n# SCOPE \u2014 primary C-Chain ONLY. L2 (hanzo/zoo/pars/spc)\n# carry\
|
|
\ their precompile activations AND network-upgrade timestamps\n# in their own per-chain genesis\
|
|
\ (configs/{brand}-mainnet/genesis.json),\n# baked at block 0. Overwriting their /data/configs/chains/<bid>/\n\
|
|
# upgrade.json with the C-Chain schedule would either be a redundant\n# no-op or \u2014 worse \u2014\
|
|
\ re-schedule an already-live precompile to a\n# forward timestamp and brick the chain via\n# extras.ChainConfig.checkPrecompileCompatible.\
|
|
\ See Red review\n# (vector 5, HIGH) for the rationale.\nCHAIN_CONFIG_DIR=\"/data/configs/chains\"\
|
|
\nCCHAIN_ALIAS_DIR=\"${CHAIN_CONFIG_DIR}/C\"\nmkdir -p \"${CCHAIN_ALIAS_DIR}\"\nif [ -f /scripts/cchain-upgrade.json\
|
|
\ ]; then\n cp /scripts/cchain-upgrade.json \"${CCHAIN_ALIAS_DIR}/upgrade.json\"\n echo \"[UPGRADE]\
|
|
\ canonical upgrade.json seeded at ${CCHAIN_ALIAS_DIR}/upgrade.json (C-Chain only)\"\nelse\n echo\
|
|
\ \"[UPGRADE] WARN: /scripts/cchain-upgrade.json missing \u2014 luxd will boot WITHOUT the canonical\
|
|
\ upgrade schedule\" >&2\nfi\n\n# Per-pod clean-rejoin: enable EVM state-sync so a wiped/behind\
|
|
\ node syncs\n# state from healthy peers instead of re-executing ~1M blocks (OOM-safe and\n# the\
|
|
\ correct native rejoin mechanism). Gated on the same .allow-bootstrap\n# marker that flips skip-bootstrap=false;\
|
|
\ removed for normal pods.\nif [ -f /data/db/.allow-bootstrap ]; then\n printf '{\"state-sync-enabled\"\
|
|
:true,\"state-sync-min-blocks\":300000,\"trie-clean-cache\":256,\"trie-dirty-cache\":256,\"snapshot-cache\"\
|
|
:128}' > \"${CCHAIN_ALIAS_DIR}/config.json\"\n echo \"[STATE-SYNC] wrote ${CCHAIN_ALIAS_DIR}/config.json\
|
|
\ state-sync-enabled=true + mem-bound coreth caches clean=256/dirty=256/snap=128 MB (native peer\
|
|
\ state-sync rejoin, OOM-bounded)\"\nelse\n # DB previously ran archive (pruning disabled) during\
|
|
\ incident mitigation, so\n # it is archive-tagged. allow-missing-tries=true lets it run with pruning\n\
|
|
\ # ENABLED again (canonical, bounded disk) without tripping coreth's\n # missing-tries shutdown\
|
|
\ guard. v1.34.25's head-state pin protects the tip.\n printf '{\"allow-missing-tries\":true,\"\
|
|
trie-clean-cache\":256,\"trie-dirty-cache\":256,\"snapshot-cache\":128}' > \"${CCHAIN_ALIAS_DIR}/config.json\"\
|
|
\n echo \"[HEAD-PIN] wrote ${CCHAIN_ALIAS_DIR}/config.json allow-missing-tries=true + mem-bound\
|
|
\ coreth caches clean=256/dirty=256/snap=128 MB (pruning enabled; archive-tagged DB tolerated; head-state\
|
|
\ pin via v1.34.25; OOM-bounded)\"\nfi\n\n# \u2500\u2500 L2 EVM precompile activation (task-114\
|
|
\ #114) \u2500\u2500\n# L2 spawned at 2026-06-05 reset have `quasarTimestamp=year-9999`\n# in their\
|
|
\ per-chain genesis = precompile registry effectively disabled.\n# Seed brand-upgrade.json (canonical\
|
|
\ 42 precompiles, forward-dated to\n# 2026-08-07 UTC = unix 1766708400) into each brand alias dir.\
|
|
\ luxd\n# resolves config via alias before chain-ID, so seeding to the alias\n# dir activates the\
|
|
\ schedule for that L1 EVM.\n#\n# Source-of-truth: brand-upgrade.json data key below; mirror at\n\
|
|
# ~/work/<brand>/{universe/configs/genesis,genesis/networks}/mainnet/upgrade.json.\n# Safety: the\
|
|
\ chains have NO active precompile state to break (everything\n# is currently gated past year 9999),\
|
|
\ so checkPrecompileCompatible cannot\n# fail when the upgrade.json drops in. The forward-dated\
|
|
\ 2026-08-07\n# activation gives 60+ days of pod rollout + smoke tests before crypto\n# surfaces\
|
|
\ flip live.\nif [ -f /scripts/brand-upgrade.json ]; then\n for BRAND_ALIAS in hanzo zoo pars spc\
|
|
\ osage; do\n BRAND_DIR=\"${CHAIN_CONFIG_DIR}/${BRAND_ALIAS}\"\n mkdir -p \"${BRAND_DIR}\"\
|
|
\n cp /scripts/brand-upgrade.json \"${BRAND_DIR}/upgrade.json\"\n echo \"[UPGRADE] brand-upgrade.json\
|
|
\ seeded at ${BRAND_DIR}/upgrade.json\"\n done\nelse\n echo \"[UPGRADE] WARN: /scripts/brand-upgrade.json\
|
|
\ missing \u2014 L2 EVMs will boot WITHOUT precompile schedule\" >&2\nfi\n\nARGS=\"$ARGS --chain-config-dir=${CHAIN_CONFIG_DIR}\"\
|
|
\n\necho \"\n# Ensure app chain EVM plugin uses our custom EVM build (with all precompiles)\nCCHAIN_PLUGIN=\"\
|
|
mgj786NP7uDwBCcq6YwThhaN8FLyybkCa4zBWTQbNgmK6k9A6\"\nAPPCHAIN_PLUGIN=\"ag3GReYPNuSR17rUP8acMdZipQBikdXNRKDyFszAysmy3vDXE\"\
|
|
\nif [ -f \"/data/plugins/$CCHAIN_PLUGIN\" ]; then\n echo \"[PLUGINS] Copying C-Chain EVM as app\
|
|
\ chain plugin - shared precompile registry\"\n cp -f \"/data/plugins/$CCHAIN_PLUGIN\" \"/data/plugins/$APPCHAIN_PLUGIN\"\
|
|
\nfi\n\n[START] luxd ${ARGS}\" | head -c 200\necho \"\"\n\n# Canonical chain aliases (single source\
|
|
\ of truth: clean brand names).\n# 2026-06-05 mainnet reset: full P-state wipe + re-bootstrap dropped\n\
|
|
# all accumulated orphan CreateChainTx (hanzo, zoo, zoov5, pars, spc,\n# spcv5, spcv6). Fresh bootstrap-chain\
|
|
\ with --chain-name-override\n# creates each L2 with its CLEAN name (hanzo/zoo/pars/spc/osage)\n\
|
|
# \u2014 no version suffixes, no gateway rewrites. The chain-aliases CM\n# mirror lives at chain-aliases.yaml;\
|
|
\ this inline copy ensures the\n# file is present on PVC before luxd reads it.\nmkdir -p /data/configs/chains\n\
|
|
cat > /data/configs/chains/aliases.json <<'ALIASES'\n{\n \"2TdhxqC31SuwuCoaucATqA2vBB9pgXEuMHA5157oUmshN1xEH1\"\
|
|
: [\"hanzo\"],\n \"tQzPnApH1xouMVfvt9hXYTcSiSePLpPymLpp4Sztc5BRoNo28\": [\"zoo\"],\n \"SsbCrV3AC3ZnbEsb1Uz7BWsJzLe54wJU3tAWt4mbooF2GLU4J\"\
|
|
: [\"pars\"],\n \"yJ9tBVTrvsG2jycf5cNRZshGdPo9Dfb8snST3tsTWDa9i8ctC\": [\"spc\"],\n \"2FtVprwJcS2y7fqSwncdeJcRny1niRragrFtGiC7WQrgDJLc87\"\
|
|
: [\"osage\"]\n}\nALIASES\n\n# \u2500\u2500 FIX #1 (P0 root cause): signal-forwarding trap + guarded\
|
|
\ double-wait \u2500\u2500\n# On `kubectl delete pod`, containerd sends SIGTERM to PID 1 (this shell).\n\
|
|
# luxd installs its OWN SIGINT/SIGTERM handler (app/app.go -> node.Shutdown)\n# that runs graceful\
|
|
\ shutdown() and DELETES the `ungracefulShutdown` key\n# before DB.Close(). But luxd is BACKGROUNDED\
|
|
\ here (we do post-launch work,\n# so a bare `exec` is impossible). A bare `wait` with no trap SWALLOWS\n\
|
|
# SIGTERM: the shell never forwards it, luxd runs until the cgroup SIGKILLs\n# it at grace expiry\
|
|
\ -> shutdown() never runs -> the non-atomic\n# proposervm/inner-EVM commit is torn mid-write ->\
|
|
\ \"proposervm state\n# truncated/inconsistent\" -> C-VM refuses to init. Install a TERM/INT trap\n\
|
|
# that forwards to luxd + a guarded double-wait (end of script) that blocks\n# PID 1 until luxd\
|
|
\ has actually flushed and exited.\nTERM_RECEIVED=\"\"\nterm() {\n TERM_RECEIVED=1\n echo \"[SIGNAL]\
|
|
\ SIGTERM/INT received -> forwarding to luxd (pid ${LUXD_PID:-?}) for graceful shutdown\"\n kill\
|
|
\ -TERM \"$LUXD_PID\" 2>/dev/null || true\n}\ntrap term TERM INT\n\n/luxd/build/luxd $ARGS &\nLUXD_PID=$!\n\
|
|
set +e\n\ni=0\nwhile [ \"$i\" -lt 180 ]; do\n [ -n \"$TERM_RECEIVED\" ] && break\n # FIX #3: liveness\
|
|
\ over /v1 (was /v1/health -> 404 forever on v1.36.x; the\n # node serves baseURL=\"/v1\" only,\
|
|
\ server/http/server.go:33). Liveness (200\n # the moment the HTTP server is up) is the correct\
|
|
\ gate for admin RPC below.\n curl -sf -m 2 http://127.0.0.1:9630/v1/health/ops/liveness >/dev/null\
|
|
\ 2>&1 && echo \"[HEALTHY] after ${i}s\" && break\n i=$((i + 1))\n sleep 1\ndone\n\n# \u2500\u2500\
|
|
\ FIX #2: frontier-gated self-consume of the .allow-bootstrap marker \u2500\u2500\n# The marker\
|
|
\ (an operator drops it to force a one-shot re-sync) already\n# flipped --skip-bootstrap=false +\
|
|
\ wrote the state-sync config.json above for\n# THIS boot. Consume it (RENAME, never delete) ONLY\
|
|
\ after the local C-Chain\n# tip actually reaches the fleet frontier (max peer height) \u2014 gating\
|
|
\ on TIP\n# HEIGHT, not mere health/Ready, because the skip-bootstrap wedge can report\n# Ready=true\
|
|
\ while frozen (lux-skipbootstrap-frontier-wedge-rootcause).\n# Never-consumed => every reboot re-runs\
|
|
\ state-sync and a crash mid-sync\n# drops back into the wedge. Backgrounded so it never blocks\
|
|
\ PID 1's path to\n# the guarded double-wait; renamed marker (.done) is idempotent + auditable.\n\
|
|
if [ -f /data/db/.allow-bootstrap ]; then\n (\n LOCAL_RPC=\"http://127.0.0.1:9630/v1/chain/C/rpc\"\
|
|
\n # RED #7 frontier-floor: never consume the marker on a REF below a known-good\n # frontier.\
|
|
\ Defeats the all-peers-stale edge \u2014 if EVERY peer we can reach is\n # itself wedged/behind\
|
|
\ (e.g. a whole-fleet sub-quorum halt), REF (=max peer\n # height) can be LOWER than the true\
|
|
\ frontier; consuming here would strand\n # this node skip-bootstrap=false-then-true at a stale\
|
|
\ tip and re-wedge it.\n # Floor is a LOWER bound only (safe): once the chain advances past it,\
|
|
\ REF\n # naturally exceeds it. Operator SHOULD raise it to the current confirmed\n # tip\
|
|
\ at canary time: FRONTIER_FLOOR=<tip> before the restart.\n # The floor MUST be <= the live\
|
|
\ tip. 1098191 was the pre-regenesis tip; the\n # 2026-08-04 RLP re-genesis reset the chain to\
|
|
\ 1082780, leaving the old floor\n # permanently unreachable — an armed .allow-bootstrap could\
|
|
\ then NEVER be\n # consumed. 1082780 is the certified imported history every node holds.\n \
|
|
\ FRONTIER_FLOOR=\"${FRONTIER_FLOOR:-1082780}\"\
|
|
\n hex2dec() { printf '%d' \"$1\" 2>/dev/null; }\n evm_height() {\n _r=$(curl -sf -m\
|
|
\ 3 -X POST -H 'Content-Type: application/json' \\\n -d '{\"jsonrpc\":\"2.0\",\"method\"\
|
|
:\"eth_blockNumber\",\"params\":[],\"id\":1}' \"$1\" 2>/dev/null)\n _h=$(printf '%s' \"$_r\"\
|
|
\ | sed -n 's/.*\"result\":\"\\(0x[0-9a-fA-F]*\\)\".*/\\1/p')\n [ -n \"$_h\" ] && hex2dec \"\
|
|
$_h\"\n }\n j=0\n while [ \"$j\" -lt 3600 ]; do\n [ -n \"$TERM_RECEIVED\" ] && exit\
|
|
\ 0\n if curl -sf -m 2 http://127.0.0.1:9630/v1/health/ops/liveness >/dev/null 2>&1; then\n \
|
|
\ LOCAL=$(evm_height \"$LOCAL_RPC\")\n REF=\"\"\n for HOST in $(printf '%s' \"\
|
|
$BOOTSTRAP_NODES\" | tr ',' ' '); do\n PEER_HP=${HOST%%:*}\n PH=$(evm_height \"\
|
|
http://${PEER_HP}:9630/v1/chain/C/rpc\")\n [ -n \"$PH\" ] && { [ -z \"$REF\" ] && REF=\"$PH\"\
|
|
\ || { [ \"$PH\" -gt \"$REF\" ] && REF=\"$PH\"; }; }\n done\n if [ -n \"$LOCAL\" ]\
|
|
\ && [ -n \"$REF\" ] && [ \"$LOCAL\" -ge \"$REF\" ] && [ \"$REF\" -ge \"$FRONTIER_FLOOR\" ]; then\n\
|
|
\ mv /data/db/.allow-bootstrap /data/db/.allow-bootstrap.done 2>/dev/null \\\n \
|
|
\ && echo \"[BOOTSTRAP] fleet frontier reached (local=$LOCAL >= ref=$REF >= floor=$FRONTIER_FLOOR)\
|
|
\ \u2014 marker consumed -> .allow-bootstrap.done\"\n exit 0\n fi\n if [\
|
|
\ -n \"$REF\" ] && [ \"$REF\" -lt \"$FRONTIER_FLOOR\" ]; then\n echo \"[BOOTSTRAP] ALL\
|
|
\ reachable peers stale (ref=$REF < floor=$FRONTIER_FLOOR) \u2014 REFUSING to consume marker (would\
|
|
\ strand at stale tip); preserved\" >&2\n else\n echo \"[BOOTSTRAP] catching up:\
|
|
\ local=${LOCAL:-?} ref=${REF:-?} floor=$FRONTIER_FLOOR \u2014 marker preserved\"\n fi\n\
|
|
\ fi\n j=$((j + 5)); sleep 5\n done\n echo \"[BOOTSTRAP] WARN: frontier not confirmed\
|
|
\ within window \u2014 marker PRESERVED for next boot\" >&2\n ) &\nfi\n\n\n# Generic tenant-import\
|
|
\ loop. Iterates every operator-written request\n# marker, calls admin_importChain, then writes\
|
|
\ the matching sentinel.\n# No hardcoded tenant names \u2014 the marker JSON carries chainAlias,\n\
|
|
# rlpPath, and sentinelPath, all derived from LuxNetwork.spec.tenantImports[]\n# by luxfi/operator's\
|
|
\ BuildTenantImportInitContainer.\nif [ \"$ADMIN_NEEDED\" = \"true\" ]; then\n for MARKER in $TENANT_REQ_GLOB;\
|
|
\ do\n [ -n \"$TERM_RECEIVED\" ] && break\n [ -f \"$MARKER\" ] || continue\n # Minimal\
|
|
\ JSON field extraction without jq dependency. sh-only.\n ALIAS=$(sed -n 's/.*\"chainAlias\"\
|
|
:\"\\([^\"]*\\)\".*/\\1/p' \"$MARKER\")\n RLP_PATH=$(sed -n 's/.*\"rlpPath\":\"\\([^\"]*\\)\"\
|
|
.*/\\1/p' \"$MARKER\")\n SENT_PATH=$(sed -n 's/.*\"sentinelPath\":\"\\([^\"]*\\)\".*/\\1/p' \"\
|
|
$MARKER\")\n TENANT=$(sed -n 's/.*\"tenant\":\"\\([^\"]*\\)\".*/\\1/p' \"$MARKER\")\n HASH=$(sed\
|
|
\ -n 's/.*\"contentHash\":\"\\([^\"]*\\)\".*/\\1/p' \"$MARKER\")\n if [ -z \"$ALIAS\" ] || [\
|
|
\ -z \"$RLP_PATH\" ] || [ -z \"$SENT_PATH\" ]; then\n echo \"[IMPORT] WARN: marker $MARKER\
|
|
\ missing required fields; skipping\" >&2\n continue\n fi\n echo \"[IMPORT] tenant=$TENANT\
|
|
\ alias=$ALIAS hash=$HASH rlp=$RLP_PATH\"\n # RED #4: run the import in the BACKGROUND so PID\
|
|
\ 1's `wait` is interruptible\n # by the TERM trap. A foreground $(curl --max-time 21600) blocks\
|
|
\ the shell up\n # to 6h; a Phase-2 SIGTERM during it is DEFERRED (dash runs a caught-signal\n\
|
|
\ # trap only after the foreground utility returns) past the 150s grace ->\n # kubelet SIGKILLs\
|
|
\ luxd mid-import (mid-write) -> the non-atomic\n # proposervm/inner-EVM commit tears -> \"proposervm\
|
|
\ state truncated\" -> C-VM\n # won't init. Backgrounded, `wait` returns the instant TERM fires,\
|
|
\ the trap\n # forwards TERM to luxd, and luxd aborts the import via graceful shutdown().\n \
|
|
\ IMPORT_OUT=\"/data/db/.tenant-import.out.$$\"\n curl -s --max-time 21600 -X POST -H \"Content-Type:\
|
|
\ application/json\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"method\\\":\\\"admin_importChain\\\
|
|
\",\\\"params\\\":[\\\"$RLP_PATH\\\"],\\\"id\\\":1}\" \\\n \"http://127.0.0.1:9630/v1/chain/$ALIAS/rpc\"\
|
|
\ > \"$IMPORT_OUT\" 2>&1 &\n IMPORT_PID=$!\n wait \"$IMPORT_PID\"; CURL_RC=$?\n if [ -n\
|
|
\ \"$TERM_RECEIVED\" ]; then\n kill \"$IMPORT_PID\" 2>/dev/null || true\n rm -f \"$IMPORT_OUT\"\
|
|
\n echo \"[IMPORT] SIGTERM during import -> aborted; luxd shutting down gracefully; marker\
|
|
\ PRESERVED for retry on next boot\"\n break\n fi\n RESULT=$(cat \"$IMPORT_OUT\" 2>/dev/null)\n\
|
|
\ rm -f \"$IMPORT_OUT\"\n echo \"[IMPORT] $TENANT/$ALIAS result (rc=$CURL_RC): $RESULT\"\n\
|
|
\ # Sentinel only on real success. Two failure surfaces:\n # 1. curl rc != 0 \u2014 transport/protocol\
|
|
\ fail (luxd not ready,\n # network issue). Leave marker so next reconcile retries.\n \
|
|
\ # 2. curl rc == 0 BUT body carries `\"error\":` \u2014 \"silent success\"\n # bug the\
|
|
\ old `curl -sf` check missed. HTTP 200 + JSON-RPC\n # error was treated as import success\
|
|
\ and the sentinel got\n # written even though no blocks were loaded. Most common\n #\
|
|
\ cause: \"block N parent mismatch\" \u2014 the RLP archive's genesis\n # hash doesn't\
|
|
\ match the running chain's block 0 (structural,\n # retrying won't help; the genesis is\
|
|
\ wrong for this RLP).\n # Distinguish: rc!=0 retries automatically; the JSON-RPC error\n \
|
|
\ # case logs LOUDLY and preserves the marker so an operator\n # notices and decides whether\
|
|
\ to fix the genesis or accept loss.\n if [ \"$CURL_RC\" != \"0\" ]; then\n echo \"[IMPORT]\
|
|
\ $TENANT/$ALIAS curl failed (rc=$CURL_RC); marker preserved for retry on next reconcile.\"\n \
|
|
\ elif echo \"$RESULT\" | grep -q '\"error\"'; then\n ERR_MSG=$(echo \"$RESULT\" | sed -n\
|
|
\ 's/.*\"message\":\"\\([^\"]*\\)\".*/\\1/p')\n echo \"[IMPORT] $TENANT/$ALIAS JSON-RPC ERROR:\
|
|
\ ${ERR_MSG:-unknown}\" >&2\n echo \"[IMPORT] $TENANT/$ALIAS marker PRESERVED; operator must\
|
|
\ triage. Common cause: genesis-hash mismatch (cmd/rlp-verify in luxfi/genesis surfaces this).\"\
|
|
\ >&2\n else\n touch \"$SENT_PATH\"\n rm -f \"$MARKER\"\n echo \"[IMPORT] $TENANT/$ALIAS\
|
|
\ sentinel $SENT_PATH written; marker removed.\"\n fi\n done\nfi\n\n# \u2500\u2500 FIX #1 (continued):\
|
|
\ guarded double-wait \u2500\u2500\n# First wait returns the instant the TERM trap fires (luxd got\
|
|
\ SIGTERM) OR\n# luxd self-exits. If luxd is still alive (signal path), block again until it\n#\
|
|
\ has truly flushed and exited \u2014 otherwise PID 1 returns, the cgroup is torn\n# down, and luxd\
|
|
\ is SIGKILLed mid-flush (exactly the failure we eliminate).\n# The kill -0 guard is robust across\
|
|
\ ash/dash/bash reaping differences and\n# captures luxd's real exit code.\nwait \"$LUXD_PID\";\
|
|
\ RC=$?\nwhile kill -0 \"$LUXD_PID\" 2>/dev/null; do\n wait \"$LUXD_PID\"; RC=$?\ndone\necho \"\
|
|
[EXIT] luxd exited rc=$RC\"\nexit \"$RC\""
|
|
name: luxd-startup
|
|
containerName: luxd
|
|
env:
|
|
- name: GOMEMLIMIT
|
|
value: 6GiB
|
|
- name: ZAP_RPC_LISTEN
|
|
value: :9632
|
|
- name: LUX_NETWORK_ID
|
|
value: '1'
|
|
- name: POD_NAME
|
|
valueFrom:
|
|
fieldRef:
|
|
apiVersion: v1
|
|
fieldPath: metadata.name
|
|
- name: LUX_CONSENSUS_VIEW_CHANGE
|
|
value: 'true'
|
|
fsGroup: 1000
|
|
image:
|
|
# Semver and immutable digest move together. OnDelete permits a
|
|
# one-validator-at-a-time promotion after CD updates this template.
|
|
digest: sha256:3ff4841e76b407e774b59ea33cdb691bc0b5b6dfdebc0e5ea71bd1119d059cad
|
|
pullPolicy: Always
|
|
repository: ghcr.io/luxfi/node
|
|
tag: 1.36.181
|
|
initContainers:
|
|
- command:
|
|
- sh
|
|
- -c
|
|
- f=/data/configs/chains/C/config.json; if [ -f "$f" ] && [ ! -w "$f" ]; then chown 1000:1000 "$f" 2>/dev/null
|
|
|| rm -f "$f"; fi; chown -R 1000:1000 /data/configs 2>/dev/null || true; rm -f /data/configs/chains/C/config.json.pre-flagday;
|
|
echo '[fix-cfg-perms] config ownership normalised'
|
|
image: busybox:1.36
|
|
imagePullPolicy: IfNotPresent
|
|
name: fix-cfg-perms
|
|
securityContext:
|
|
runAsUser: 0
|
|
volumeMounts:
|
|
- mountPath: /data
|
|
name: data
|
|
- args:
|
|
- "#!/bin/sh\nset -e\n\nPOD_INDEX=${HOSTNAME##*-}\necho \"[GATE] Startup gate for $HOSTNAME (luxd, network-id=1)\"\
|
|
\nDNS_PREFIX=\"luxd\"\nDNS_SUFFIX=\"luxd-headless.lux-mainnet.svc.cluster.local\"\nGATE_MIN_PEERS=2\n\
|
|
GATE_TIMEOUT=60\nGATE_INTERVAL=5\nGATE_START=$(date +%s)\necho \"[GATE] Waiting for $GATE_MIN_PEERS\
|
|
\ peers (timeout ${GATE_TIMEOUT}s)...\"\n\nwhile true; do\n REACHABLE=0\n if [ \"$POD_INDEX\" !=\
|
|
\ \"0\" ]; then\n if nc -z -w 2 \"${DNS_PREFIX}-0.${DNS_SUFFIX}\" 9631 2>/dev/null; then\n \
|
|
\ REACHABLE=$((REACHABLE + 1))\n fi\n fi\n if [ \"$POD_INDEX\" != \"1\" ]; then\n if nc -z\
|
|
\ -w 2 \"${DNS_PREFIX}-1.${DNS_SUFFIX}\" 9631 2>/dev/null; then\n REACHABLE=$((REACHABLE + 1))\n\
|
|
\ fi\n fi\n if [ \"$POD_INDEX\" != \"2\" ]; then\n if nc -z -w 2 \"${DNS_PREFIX}-2.${DNS_SUFFIX}\"\
|
|
\ 9631 2>/dev/null; then\n REACHABLE=$((REACHABLE + 1))\n fi\n fi\n if [ \"$POD_INDEX\"\
|
|
\ != \"3\" ]; then\n if nc -z -w 2 \"${DNS_PREFIX}-3.${DNS_SUFFIX}\" 9631 2>/dev/null; then\n \
|
|
\ REACHABLE=$((REACHABLE + 1))\n fi\n fi\n if [ \"$POD_INDEX\" != \"4\" ]; then\n if nc\
|
|
\ -z -w 2 \"${DNS_PREFIX}-4.${DNS_SUFFIX}\" 9631 2>/dev/null; then\n REACHABLE=$((REACHABLE +\
|
|
\ 1))\n fi\n fi\n\n if [ \"$REACHABLE\" -ge \"$GATE_MIN_PEERS\" ]; then\n echo \"[GATE] $REACHABLE\
|
|
\ peers reachable, proceeding with startup\"\n break\n fi\n\n ELAPSED=$(( $(date +%s) - GATE_START\
|
|
\ ))\n if [ \"$ELAPSED\" -ge \"$GATE_TIMEOUT\" ]; then\n echo \"[GATE] WARNING: Timeout after\
|
|
\ ${ELAPSED}s with only $REACHABLE peers. Proceeding (onTimeout=StartAnyway).\"\n break\n fi\n\
|
|
\n echo \"[GATE] $REACHABLE/$GATE_MIN_PEERS peers reachable (${ELAPSED}/${GATE_TIMEOUT}s), waiting\
|
|
\ ${GATE_INTERVAL}s...\"\n sleep \"$GATE_INTERVAL\"\ndone\n\necho \"[GATE] Gate passed, luxd may\
|
|
\ start.\"\n"
|
|
command:
|
|
- /bin/sh
|
|
- -c
|
|
env:
|
|
- name: LUX_CONSENSUS_VIEW_CHANGE
|
|
value: 'true'
|
|
imagePullPolicy: IfNotPresent
|
|
name: startup-gate
|
|
resources: {}
|
|
terminationMessagePath: /dev/termination-log
|
|
terminationMessagePolicy: File
|
|
livenessProbe:
|
|
failureThreshold: 10
|
|
httpGet:
|
|
path: /v1/health/ops/liveness
|
|
port: 9630
|
|
scheme: HTTP
|
|
initialDelaySeconds: 300
|
|
periodSeconds: 15
|
|
successThreshold: 1
|
|
timeoutSeconds: 5
|
|
nameOverride: luxd
|
|
partOf: lux
|
|
persistence:
|
|
enabled: false
|
|
persistentVolumeClaimRetentionPolicy:
|
|
whenDeleted: Retain
|
|
whenScaled: Retain
|
|
podAnnotations:
|
|
forceRestart: '1780781328'
|
|
podSecurityContext:
|
|
runAsGroup: 1000
|
|
runAsUser: 1000
|
|
ports:
|
|
- containerPort: 9632
|
|
name: zap-rpc
|
|
protocol: TCP
|
|
- containerPort: 9630
|
|
name: http
|
|
protocol: TCP
|
|
- containerPort: 9631
|
|
name: staking
|
|
protocol: TCP
|
|
- containerPort: 9090
|
|
name: metrics
|
|
protocol: TCP
|
|
# hanzo-infra-critical, the cluster's existing top tier (1000000) and what the
|
|
# hanzod validators already run under. luxd-validator-critical was named here and
|
|
# never defined -- no PriorityClass of that name exists in this repo or the
|
|
# cluster -- so the API refused every pod with "no PriorityClass with name
|
|
# luxd-validator-critical was found" and this StatefulSet sat at 0/5 for fourteen
|
|
# hours with no pod ever created. A second name at the same value would buy
|
|
# nothing over the one the working chain uses.
|
|
priorityClassName: hanzo-infra-critical
|
|
readinessProbe:
|
|
exec:
|
|
command:
|
|
- /bin/sh
|
|
- -c
|
|
# Readiness is whether this process SERVES the C-Chain. It deliberately does
|
|
# not consult info.isBootstrapped: that flag is a one-time transition a node
|
|
# skips entirely when it starts already AT the tip, so a healthy validator
|
|
# reports false forever and drops out of luxd-rpc. Freshness belongs to
|
|
# rpc-tip-gate, which compares heights live at MAX_LAG_BLOCKS and owns the
|
|
# lux.network/rpc=fresh label this Service also selects on.
|
|
- 'curl -sf -m 4 -X POST http://127.0.0.1:9630/v1/chain/C/rpc -H ''Content-Type: application/json''
|
|
-d ''{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}'' | grep -q ''"result":"0x''
|
|
|| exit 1'
|
|
failureThreshold: 4
|
|
initialDelaySeconds: 30
|
|
periodSeconds: 15
|
|
successThreshold: 1
|
|
timeoutSeconds: 6
|
|
# Back to five while luxd-5's chain data is seeded.
|
|
#
|
|
# luxd-5 is SDM's own node — identity `staker-5` in luxd-staking, minted into
|
|
# KMS under env mainnet path staking, NodeID-4Wijnqm4v83VdYtBWLPNUVtM3bf1J6UDK.
|
|
# Its PVC (data-luxd-5) and identity are kept; only the pod is stood down.
|
|
#
|
|
# Raising this to 6 is safe for the RUNNING pods — OnDelete means the existing
|
|
# five are not restarted. What is not safe is what the new pod then does: from
|
|
# an empty database it asks its peers to serve the whole chain, and serving
|
|
# that ancestry OOM-killed three of the five validators inside ten minutes.
|
|
# On N=5 with quorum 4, two down is a halt.
|
|
#
|
|
# A from-genesis node cannot close this gap on its own. The bootstrapper
|
|
# refuses any gap over MaxBuffer (65,536 blocks) with ErrGapTooLarge and does
|
|
# not retry it, and `state-sync-enabled` in the C-Chain config is inert — the
|
|
# consensus engine has no state-sync phase, the chain handler no-ops the six
|
|
# state-sync wire ops, and the ZAP plugin exposes no state-sync methods.
|
|
#
|
|
# So seed the chain data to within ~65k blocks of tip FIRST (--import-chain-data,
|
|
# or a block-level snapshot of a quiesced donor — luxd-3/4, never luxd-1, and
|
|
# never copy /data/staking with it: two pods on one identity is equivocation,
|
|
# not a peering bug). Then set this back to 6.
|
|
# 0, not 5. The canonical lux-mainnet C-Chain is the RLP archive, served by
|
|
# deploy/lux-mainnet/luxd-archive.yaml. These validators start from a genesis the
|
|
# archive does not chain to and import nothing, so any block they make is a
|
|
# fork of it. They return once luxfi/evm#152 is fixed (a validator that
|
|
# imports and then restarts cannot initialize its C-Chain) and they run the
|
|
# canonical genesis with the archive imported.
|
|
replicas: 0
|
|
resources:
|
|
limits:
|
|
cpu: '4'
|
|
memory: 12Gi
|
|
requests:
|
|
cpu: 100m
|
|
# 7Gi apiece meant five validators reserved 35Gi -- a third of the only amd64
|
|
# node -- before one of them had served a block. Measured on the luxd that does
|
|
# run: 185Mi resident on lux-local, 41Mi on testnet. 1536Mi is eight times the
|
|
# larger of those and the 12Gi limit above is untouched, so a syncing node can
|
|
# still take the node's memory if it genuinely needs it.
|
|
memory: 1536Mi
|
|
service:
|
|
headless: false
|
|
type: ClusterIP
|
|
startupProbe:
|
|
failureThreshold: 90
|
|
initialDelaySeconds: 5
|
|
periodSeconds: 10
|
|
successThreshold: 1
|
|
tcpSocket:
|
|
port: 9630
|
|
timeoutSeconds: 1
|
|
strategy: Recreate
|
|
terminationGracePeriodSeconds: 150
|
|
topologySpread:
|
|
enabled: false
|
|
volumeClaimTemplates:
|
|
- apiVersion: v1
|
|
kind: PersistentVolumeClaim
|
|
metadata:
|
|
name: data
|
|
spec:
|
|
accessModes:
|
|
- ReadWriteOnce
|
|
resources:
|
|
requests:
|
|
storage: 200Gi
|
|
volumeMode: Filesystem
|
|
volumeMounts:
|
|
- mountPath: /data
|
|
name: data
|
|
- mountPath: /scripts
|
|
name: startup-script
|
|
- mountPath: /staking-keys
|
|
name: staking-keys
|
|
readOnly: true
|
|
volumes:
|
|
- configMap:
|
|
defaultMode: 420
|
|
name: luxd-config
|
|
name: config
|
|
- configMap:
|
|
defaultMode: 493
|
|
name: luxd-startup
|
|
name: startup-script
|
|
- name: staking-keys
|
|
secret:
|
|
defaultMode: 420
|
|
secretName: luxd-staking
|
|
workload:
|
|
kind: StatefulSet
|
|
podManagementPolicy: Parallel
|
|
serviceName: luxd-headless
|
|
# ⛔ Deliberately NOT using the chart's `pdb:` here. Its selector is
|
|
# selectorLabels (name+instance), which on this fleet ALSO matches the hourly
|
|
# luxd-export-c-chain job pods — 8 pods on lux-mainnet, not 5. A budget that
|
|
# counts finished Job pods as unhealthy drives disruptionsAllowed to 0 and
|
|
# blocks legitimate node drains, which is the failure this was meant to prevent.
|
|
# The validators are pinned by `app.kubernetes.io/component: validator`, a key
|
|
# the chart's PDB template cannot express, so `luxd-pdb` stays hand-applied
|
|
# until the chart grows an extra-selector option.
|
|
#
|
|
# The long-standing luxd-pdb was INERT: it demanded
|
|
# `app.kubernetes.io/managed-by: lux-operator`, a fossil from before this fleet
|
|
# moved to the chart (pods now say `Helm`), so it matched ZERO pods and reported
|
|
# expectedPods=0 for 162 days. Corrected 2026-08-06 to
|
|
# {name: luxd, component: validator} — verified selecting exactly 5.
|
|
|
|
# api.lux.network. Both rules send to luxd-rpc, the Service that selects the
|
|
# sync node -- the one holding the real chain -- and not to `lux-chain`, a
|
|
# selector-less Service whose hand-made EndpointSlice named the five docker
|
|
# validators on evo. Those five were a fork: same genesis, different block 1,
|
|
# real transactions on both sides; the divergence only shows above block 0.
|
|
# They are stopped and stay stopped.
|
|
#
|
|
# bc-path exists because this node is on luxd v1.36.148, which serves the EVM
|
|
# at /v1/bc/C/rpc, while everything that calls api.lux.network -- the gateway's
|
|
# route table, the explorer, the traffic sender -- speaks /v1/chain/c/rpc, the
|
|
# path v1.36.181 serves. The rewrite holds the public contract steady until the
|
|
# node moves to .181; delete bc-path on that day and nothing else changes.
|
|
middlewares:
|
|
- name: bc-path
|
|
spec:
|
|
replacePathRegex:
|
|
regex: "^/v1/chain/[cC](/.*)?$"
|
|
replacement: "/v1/bc/C$1"
|
|
- name: c-chain
|
|
spec:
|
|
replacePath:
|
|
path: /v1/bc/C/rpc
|
|
routes:
|
|
- name: lux-api
|
|
spec:
|
|
entryPoints: [websecure]
|
|
routes:
|
|
- match: (Host(`api.lux.network`) || Host(`api.lux.cloud`)) && PathPrefix(`/v1/chain`)
|
|
kind: Rule
|
|
priority: 300
|
|
middlewares:
|
|
- name: bc-path
|
|
services:
|
|
- name: luxd-rpc
|
|
port: 9630
|
|
# v1.lux.exchange is built against /v1/bc/C/rpc, the path this node serves
|
|
# natively on v1.36.148, so it passes through untouched. When the node moves
|
|
# to .181 this rule needs the reverse rewrite (/v1/bc/C -> /v1/chain/c), or
|
|
# v1 gets rebuilt against /v1/chain -- the second is the one-way answer.
|
|
- match: (Host(`api.lux.network`) || Host(`api.lux.cloud`)) && PathPrefix(`/v1/bc`)
|
|
kind: Rule
|
|
priority: 300
|
|
services:
|
|
- name: luxd-rpc
|
|
port: 9630
|
|
- match: (Host(`api.lux.network`) || Host(`api.lux.cloud`)) && Path(`/`)
|
|
kind: Rule
|
|
priority: 310
|
|
middlewares:
|
|
- name: c-chain
|
|
services:
|
|
- name: luxd-rpc
|
|
port: 9630
|
|
tls: {}
|