floor(2n/3)+1 for n=3 is 3. Sample 3 / quorum 2 lets one equivocating node finalize conflicting blocks across two 2-node partitions. Same safety law as the 5-node fleets (sample 5 / quorum 4, which is correct and must never be lowered). Co-authored-by: Hanzo Dev <[email protected]>
113 lines
4.8 KiB
YAML
113 lines
4.8 KiB
YAML
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: luxd-startup
|
|
namespace: testnet
|
|
data:
|
|
startup.sh: |
|
|
#!/bin/sh
|
|
set -e
|
|
|
|
POD_INDEX=${HOSTNAME##*-}
|
|
echo "=== luxd startup for $HOSTNAME (network-id=2, testnet) ==="
|
|
|
|
# Copy staking keys from secret
|
|
IDX=${HOSTNAME##*-}
|
|
mkdir -p /data/staking
|
|
if [ -f "/staking-keys/staker-${IDX}.crt" ]; then
|
|
cp /staking-keys/staker-${IDX}.crt /data/staking/staker.crt
|
|
cp /staking-keys/staker-${IDX}.key /data/staking/staker.key
|
|
if [ -f "/staking-keys/signer-${IDX}.key" ]; then
|
|
cp /staking-keys/signer-${IDX}.key /data/staking/signer.key
|
|
else
|
|
rm -f /data/staking/signer.key
|
|
fi
|
|
echo "[STAKING] Keys copied from secret for index ${IDX}"
|
|
elif [ -f "/data/staking/staker.crt" ]; then
|
|
rm -f /data/staking/signer.key
|
|
echo "[STAKING] Using existing keys on PVC for index ${IDX}"
|
|
else
|
|
echo "[STAKING] No keys found, generating new keypair for index ${IDX}"
|
|
openssl ecparam -genkey -name prime256v1 -out /data/staking/staker.key 2>/dev/null
|
|
printf "[req]\ndistinguished_name=dn\n[dn]\n" > /tmp/openssl.cnf
|
|
openssl req -new -x509 -key /data/staking/staker.key -out /data/staking/staker.crt \
|
|
-sha256 -days 36500 -nodes -set_serial 0 -subj "/" \
|
|
-config /tmp/openssl.cnf 2>/dev/null
|
|
rm -f /data/staking/signer.key
|
|
echo "[STAKING] Generated new keypair for index ${IDX}"
|
|
fi
|
|
echo "[STAKING] Keys ready for index ${IDX}"
|
|
|
|
# Copy plugins from image
|
|
mkdir -p /data/plugins
|
|
if [ -d /luxd/build/plugins ] && ls /luxd/build/plugins/* >/dev/null 2>&1; then
|
|
for f in /luxd/build/plugins/*; do
|
|
dest="/data/plugins/$(basename $f)"
|
|
if [ ! -f "$dest" ]; then
|
|
cp "$f" "$dest"
|
|
echo "[PLUGINS] Copied $(basename $f) from image"
|
|
else
|
|
echo "[PLUGINS] Keeping existing $(basename $f)"
|
|
fi
|
|
done
|
|
fi
|
|
ls -la /data/plugins/ 2>/dev/null || echo "[PLUGINS] No plugins directory"
|
|
|
|
# Bootstrap nodes (discover from headless service)
|
|
BOOTSTRAP_NODES="luxd-0.luxd-headless.testnet.svc.cluster.local:9651,luxd-1.luxd-headless.testnet.svc.cluster.local:9651,luxd-2.luxd-headless.testnet.svc.cluster.local:9651"
|
|
|
|
# --- Public IP: the pod IP, and only the pod IP ---
|
|
# The staking mesh is cluster-internal, so a pod IP is the only address a
|
|
# peer can dial. The k8s-API LoadBalancer-ingress lookup that used to live
|
|
# here POISONED lux-mainnet: svc/luxd-N were flipped LoadBalancer ->
|
|
# ClusterIP, but the pods that had booted while the LB ingress IPs were
|
|
# still published signed and gossiped those IPs. luxd's ipTracker replaces
|
|
# a configured bootstrap endpoint with the peer's signed IP, so every node
|
|
# then dialled a blackholed address and the mesh could not heal -- measured
|
|
# 2/3/4/1/2 of 4 peers, C-Chain accepting on ONE of five validators.
|
|
# lux-devnet and lux-testnet advertise the pod IP and hold 4/4 each.
|
|
# One way, every net.
|
|
PUBLIC_IP=$(hostname -i | awk '{print $1}')
|
|
echo "[IP] Pod: $PUBLIC_IP"
|
|
|
|
# luxd arguments
|
|
ARGS="--network-id=2"
|
|
ARGS="$ARGS --http-host=0.0.0.0 --http-port=9650 --http-allowed-hosts=*"
|
|
ARGS="$ARGS --staking-port=9651"
|
|
ARGS="$ARGS --data-dir=/data --db-type=zapdb"
|
|
ARGS="$ARGS --index-enabled=true"
|
|
ARGS="$ARGS --api-admin-enabled=true --api-metrics-enabled=true --api-keystore-enabled=true"
|
|
ARGS="$ARGS --log-level=info"
|
|
ARGS="$ARGS --plugin-dir=/data/plugins"
|
|
ARGS="$ARGS --staking-tls-cert-file=/data/staking/staker.crt"
|
|
ARGS="$ARGS --staking-tls-key-file=/data/staking/staker.key"
|
|
[ -f "/data/staking/signer.key" ] && ARGS="$ARGS --staking-signer-key-file=/data/staking/signer.key"
|
|
ARGS="$ARGS --network-compression-type=zstd"
|
|
# BFT: alpha must be a STRICT 2/3 majority — floor(2n/3)+1. For n=3 that
|
|
# is 3, not 2: quorum 2-of-3 lets two partitions finalize conflicting
|
|
# blocks with one equivocating node. Same safety law as the 5-node fleets
|
|
# (sample 5 / quorum 4). Never lower this to buy liveness.
|
|
ARGS="$ARGS --consensus-sample-size=3 --consensus-quorum-size=3"
|
|
ARGS="$ARGS --sybil-protection-enabled=true"
|
|
ARGS="$ARGS --network-require-validator-to-connect=false --network-allow-private-ips=true"
|
|
ARGS="$ARGS --genesis-file=/etc/lux/genesis/genesis.json"
|
|
ARGS="$ARGS --track-all-chains=true"
|
|
[ -n "$PUBLIC_IP" ] && ARGS="$ARGS --public-ip=$PUBLIC_IP"
|
|
[ -n "$BOOTSTRAP_NODES" ] && ARGS="$ARGS --bootstrap-nodes=$BOOTSTRAP_NODES"
|
|
|
|
echo "[START] luxd ${ARGS}" | head -c 200
|
|
echo ""
|
|
|
|
/luxd/build/luxd $ARGS &
|
|
LUXD_PID=$!
|
|
set +e
|
|
|
|
i=0
|
|
while [ "$i" -lt 180 ]; do
|
|
curl -sf -m 2 http://127.0.0.1:9650/v1/health >/dev/null 2>&1 && echo "[HEALTHY] after ${i}s" && break
|
|
i=$((i + 1))
|
|
sleep 1
|
|
done
|
|
|
|
wait "$LUXD_PID"
|