Files
node/docker/docker-entrypoint.sh
T
zeekayandHanzo Dev 51a304804c chore: migrate luxd HTTP routes /ext -> /v1
Drop the Avalanche-heritage /ext prefix; /v1 is the single canonical route
surface (one way, no backward compat). The node's baseURL is the source of
truth; clients, SDKs, CLI, indexer, maker, genesis, netrunner, and the
k8s/compose/gateway/explorer configs are updated to match.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-01 11:40:13 -07:00

297 lines
10 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
set -e
echo "🚀 Lux Node Production Entrypoint"
echo "================================="
# Setup data directories
mkdir -p /data/{db,logs,chains,staking,configs/chains/{P,X,C}}
# Function to derive keys from mnemonic or secret
derive_keys_from_secret() {
local secret="$1"
echo "🔐 Deriving staking keys from secret..."
# Use the secret to generate deterministic keys
# This ensures the same secret always generates the same node ID
local key_material
key_material=$(echo -n "$secret" | sha256sum | cut -d' ' -f1)
# Generate deterministic private key
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 \
-pass pass:"$key_material" \
-out /data/staking/staker.key 2>/dev/null
# Generate certificate with the key
openssl req -new -x509 -key /data/staking/staker.key \
-out /data/staking/staker.crt -days 3650 \
-passin pass:"$key_material" -nodes \
-subj "/C=US/ST=State/L=City/O=LuxNode/CN=node.lux.network" 2>/dev/null
chmod 600 /data/staking/*
# Calculate and print Node ID from certificate
# The node ID is the SHA256 hash of the DER-encoded certificate
CERT_DER=$(openssl x509 -in /data/staking/staker.crt -outform DER 2>/dev/null | sha256sum | cut -d' ' -f1)
echo "✅ Keys derived from secret"
echo "📋 Certificate SHA256: $CERT_DER"
}
# Handle staking keys with priority order
KEYS_CONFIGURED=false
# 1. Priority: Mnemonic or secret for deterministic generation
if [ -n "$STAKING_MNEMONIC" ] || [ -n "$STAKING_SECRET" ]; then
SECRET="${STAKING_MNEMONIC:-$STAKING_SECRET}"
derive_keys_from_secret "$SECRET"
KEYS_CONFIGURED=true
# 2. Direct environment variables
elif [ -n "$STAKING_KEY" ] && [ -n "$STAKING_CERT" ]; then
echo "🔑 Using environment staking keys..."
echo "$STAKING_KEY" > /data/staking/staker.key
echo "$STAKING_CERT" > /data/staking/staker.crt
chmod 600 /data/staking/*
KEYS_CONFIGURED=true
# 3. Mounted volume
elif [ -d "/keys/staking" ] && [ -f "/keys/staking/staker.crt" ] && [ -f "/keys/staking/staker.key" ]; then
echo "🔑 Using mounted staking keys..."
cp /keys/staking/staker.* /data/staking/ 2>/dev/null
chmod 600 /data/staking/*
KEYS_CONFIGURED=true
# 4. Check for numbered staker files (staker1.crt, etc)
elif [ -d "/keys/staking" ] && [ -f "/keys/staking/staker1.crt" ] && [ -f "/keys/staking/staker1.key" ]; then
echo "🔑 Using mounted staking keys (numbered)..."
cp /keys/staking/staker1.crt /data/staking/staker.crt
cp /keys/staking/staker1.key /data/staking/staker.key
chmod 600 /data/staking/*
KEYS_CONFIGURED=true
fi
# If no keys configured, generate ephemeral ones (for testing only)
if [ "$KEYS_CONFIGURED" = "false" ]; then
if [ "$ALLOW_EPHEMERAL_KEYS" = "true" ]; then
echo "⚠️ WARNING: Generating ephemeral certificate (testing only)..."
openssl req -x509 -newkey rsa:4096 -keyout /data/staking/staker.key \
-out /data/staking/staker.crt -days 365 -nodes \
-subj "/C=US/ST=State/L=City/O=EphemeralNode/CN=ephemeral.local" 2>/dev/null
chmod 600 /data/staking/*
else
echo "❌ ERROR: No staking keys configured!"
echo ""
echo "Please provide staking keys using one of these methods:"
echo " 1. Set STAKING_MNEMONIC or STAKING_SECRET environment variable"
echo " 2. Mount keys to /keys/staking/staker.{crt,key}"
echo " 3. Set STAKING_KEY and STAKING_CERT environment variables"
echo " 4. Set ALLOW_EPHEMERAL_KEYS=true for testing (not recommended)"
exit 1
fi
fi
# Try to get node ID - first attempt with luxd
NODE_ID=$(/app/luxd --version --staking-tls-cert-file=/data/staking/staker.crt --staking-tls-key-file=/data/staking/staker.key 2>&1 | grep -oP 'node ID: \K[^\s]+' || echo "")
# If that fails, calculate it from the certificate
if [ -z "$NODE_ID" ]; then
echo "📋 Calculating Node ID from certificate..."
# Extract the public key from certificate and hash it to get node ID
# This is a simplified version - in production, use the proper tool
# Note: CERT_HASH calculated for logging only
_cert_hash=$(openssl x509 -in /data/staking/staker.crt -pubkey -noout | openssl sha256 | cut -d' ' -f2)
echo "📋 Certificate hash: $_cert_hash"
# Use a default for now, but in production this should be properly calculated
NODE_ID="NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg"
echo "⚠️ Using default Node ID (certificate hash calculation simplified)"
fi
echo "📋 Node ID: $NODE_ID"
# Setup blockchain data if provided
if [ -d "/blockchain-data" ]; then
echo "📊 Loading blockchain data..."
mkdir -p /data/chains/C
if [ ! -d "/data/chains/C/db" ]; then
cp -r /blockchain-data /data/chains/C/db
BLOCK_COUNT=$(find /data/chains/C/db -name "*.sst" 2>/dev/null | wc -l || echo "0")
echo "✅ Loaded blockchain data ($BLOCK_COUNT SST files)"
else
echo "️ Blockchain data already exists, skipping copy"
fi
fi
# Configure network
NETWORK_ID=${NETWORK_ID:-96369}
HTTP_HOST=${HTTP_HOST:-0.0.0.0}
HTTP_PORT=${HTTP_PORT:-9630}
STAKING_PORT=${STAKING_PORT:-9631}
LOG_LEVEL=${LOG_LEVEL:-info}
# Use provided genesis or create default
if [ -d "/genesis" ] && [ -f "/genesis/P/genesis.json" ]; then
echo "📜 Using provided genesis files..."
cp -r /genesis/* /data/configs/chains/
else
echo "📝 Creating genesis files with current node as validator..."
# P-chain genesis with current node
cat > /data/configs/chains/P/genesis.json << EOF
{
"allocations": [
{
"utxoAddr": "P-local1jfdgmqduuyuxjp9sq7szp08xpynav88sd7qxvv",
"evmAddr": "0x9011E888251AB053B7bD1cdB598Db4f9DEd94714",
"initialAmount": 1000000000000000000,
"unlockSchedule": []
}
],
"startTime": 1640995200,
"initialStakeDuration": 31536000,
"initialStakeDurationOffset": 5400,
"initialStakedFunds": ["P-local1jfdgmqduuyuxjp9sq7szp08xpynav88sd7qxvv"],
"initialStakers": [
{
"nodeID": "$NODE_ID",
"rewardAddress": "P-local1jfdgmqduuyuxjp9sq7szp08xpynav88sd7qxvv",
"delegationFee": 20000
}
],
"networkID": $NETWORK_ID,
"message": "Lux et Libertas"
}
EOF
# X-chain genesis
cat > /data/configs/chains/X/genesis.json << EOF
{
"allocations": [
{
"utxoAddr": "X-local1jfdgmqduuyuxjp9sq7szp08xpynav88sd7qxvv",
"evmAddr": "0x9011E888251AB053B7bD1cdB598Db4f9DEd94714",
"initialAmount": 1000000000000000000,
"unlockSchedule": []
}
],
"startTime": 1640995200,
"initialStakeDuration": 31536000,
"initialStakeDurationOffset": 5400,
"initialStakedFunds": [],
"initialStakers": [],
"networkID": $NETWORK_ID,
"message": "Lux et Libertas"
}
EOF
# C-chain genesis
cat > /data/configs/chains/C/genesis.json << EOF
{
"config": {
"chainId": $NETWORK_ID,
"homesteadBlock": 0,
"eip150Block": 0,
"eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0",
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"petersburgBlock": 0,
"istanbulBlock": 0,
"muirGlacierBlock": 0,
"berlinBlock": 0,
"londonBlock": 0,
"apricotPhase1BlockTimestamp": 0,
"apricotPhase2BlockTimestamp": 0,
"apricotPhase3BlockTimestamp": 0,
"apricotPhase4BlockTimestamp": 0,
"apricotPhase5BlockTimestamp": 0,
"durangoBlockTimestamp": 0,
"quasarTimestamp": 0,
"feeConfig": {
"gasLimit": 20000000,
"minBaseFee": 1000000000,
"targetGas": 100000000,
"baseFeeChangeDenominator": 36,
"minBlockGasCost": 0,
"maxBlockGasCost": 10000000,
"targetBlockRate": 2,
"blockGasCostStep": 500000
}
},
"alloc": {
"9011E888251AB053B7bD1cdB598Db4f9DEd94714": {
"balance": "0x1B1AE4D6E2EF500000"
}
},
"nonce": "0x0",
"timestamp": "0x0",
"extraData": "0x00",
"gasLimit": "0x1312D00",
"difficulty": "0x0",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase": "0x0000000000000000000000000000000000000000",
"number": "0x0",
"gasUsed": "0x0",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}
EOF
fi
echo "✅ Genesis files ready"
# Build command
CMD="/app/luxd"
CMD="$CMD --network-id=$NETWORK_ID"
CMD="$CMD --data-dir=/data"
CMD="$CMD --http-host=$HTTP_HOST"
CMD="$CMD --http-port=$HTTP_PORT"
CMD="$CMD --staking-port=$STAKING_PORT"
CMD="$CMD --staking-tls-cert-file=/data/staking/staker.crt"
CMD="$CMD --staking-tls-key-file=/data/staking/staker.key"
CMD="$CMD --log-level=$LOG_LEVEL"
CMD="$CMD --log-dir=/data/logs"
# Handle bootstrap mode
BOOTSTRAP_MODE=${BOOTSTRAP_MODE:-auto}
if [ "$BOOTSTRAP_MODE" = "self" ] || [ "$BOOTSTRAP_MODE" = "solo" ]; then
echo "🔄 Self-bootstrap mode (single node)"
CMD="$CMD --bootstrap-ips="
CMD="$CMD --bootstrap-ids="
# Single node consensus settings
CMD="$CMD --consensus-sample-size=${CONSENSUS_SAMPLE_SIZE:-1}"
CMD="$CMD --consensus-quorum-size=${CONSENSUS_QUORUM_SIZE:-1}"
elif [ "$BOOTSTRAP_MODE" = "network" ] && [ -n "$BOOTSTRAP_IPS" ] && [ -n "$BOOTSTRAP_IDS" ]; then
echo "🌐 Network bootstrap mode"
CMD="$CMD --bootstrap-ips=$BOOTSTRAP_IPS"
CMD="$CMD --bootstrap-ids=$BOOTSTRAP_IDS"
else
echo "🤖 Auto-detect bootstrap mode (defaulting to self)"
CMD="$CMD --bootstrap-ips="
CMD="$CMD --bootstrap-ids="
# Default to single node settings for self-bootstrap
CMD="$CMD --consensus-sample-size=${CONSENSUS_SAMPLE_SIZE:-1}"
CMD="$CMD --consensus-quorum-size=${CONSENSUS_QUORUM_SIZE:-1}"
fi
# Add any extra arguments
if [ -n "$EXTRA_ARGS" ]; then
CMD="$CMD $EXTRA_ARGS"
fi
echo ""
echo "📝 Starting with command:"
echo " $CMD"
echo ""
echo "📡 API Endpoints:"
echo " - JSON-RPC: http://$HTTP_HOST:$HTTP_PORT/v1/bc/C/rpc"
echo " - WebSocket: ws://$HTTP_HOST:$HTTP_PORT/v1/bc/C/ws"
echo " - Health: http://$HTTP_HOST:$HTTP_PORT/v1/health"
echo " - Info: http://$HTTP_HOST:$HTTP_PORT/v1/info"
echo ""
# Execute
exec $CMD