Files

203 lines
6.5 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# Initialize a 3-of-5 MPC cluster for Customer + Lux topology.
#
# This script:
# 1. Generates Ed25519 identity keypairs for all 5 nodes
# 2. Creates key directories for compose.yml volume mounts
# 3. Generates a peer configuration file
# 4. Prints instructions for DKG and KMS registration
#
# Prerequisites:
# - Go toolchain (for keygen via mpcd)
# - openssl (for Ed25519 key generation)
#
# Usage:
# ./init.sh # Interactive: generates all keys locally
# ./init.sh --lux-only # Generate only Lux node keys (customer generates theirs)
# ./init.sh --customer-only # Generate only customer node keys
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
KEYS_DIR="${SCRIPT_DIR}/keys"
NODES_LUX=(lux-0 lux-1 lux-2)
NODES_CUSTOMER=(cust-0 cust-1)
THRESHOLD=3
TOTAL=5
MODE="${1:-all}"
log() { echo "[init] $*"; }
err() { echo "[init] ERROR: $*" >&2; exit 1; }
# Generate an Ed25519 keypair and write to directory.
# Args: $1 = node ID, $2 = output directory
generate_identity() {
local node_id="$1"
local key_dir="$2"
mkdir -p "${key_dir}"
# Generate Ed25519 private key (64 bytes: seed + public)
openssl genpkey -algorithm Ed25519 -out "${key_dir}/identity.pem" 2>/dev/null
# Extract raw private key (32-byte seed)
openssl pkey -in "${key_dir}/identity.pem" -outform DER 2>/dev/null | \
tail -c 32 | xxd -p -c 64 > "${key_dir}/identity.key"
# Extract public key (32 bytes)
openssl pkey -in "${key_dir}/identity.pem" -pubout -outform DER 2>/dev/null | \
tail -c 32 | xxd -p -c 64 > "${key_dir}/identity.pub"
# Write node ID
echo "${node_id}" > "${key_dir}/node-id"
local pubkey
pubkey=$(cat "${key_dir}/identity.pub")
log " ${node_id}: pubkey=${pubkey}"
}
# Generate ZapDB password (32 bytes, hex-encoded for AES-256)
generate_zapdb_password() {
local node_id="$1"
local key_dir="$2"
openssl rand -hex 32 > "${key_dir}/zapdb-password"
log " ${node_id}: zapdb password generated"
}
# Write .env file for compose.yml
write_env_file() {
local env_file="${SCRIPT_DIR}/.env"
log "Writing ${env_file}"
{
echo "# Generated by init.sh -- DO NOT COMMIT"
echo "# Store these values in KMS for production."
for node in "${NODES_LUX[@]}" "${NODES_CUSTOMER[@]}"; do
local var_name
var_name="ZAPDB_PASSWORD_$(echo "${node}" | tr '[:lower:]-' '[:upper:]_')"
echo "${var_name}=$(cat "${KEYS_DIR}/${node}/zapdb-password")"
done
echo ""
echo "# Set these manually:"
echo "MPC_API_TOKEN="
echo "MPC_VAULT_ID="
} > "${env_file}"
chmod 600 "${env_file}"
}
# Write peer registry JSON (consumed by mpcd for initial peer discovery)
write_peer_config() {
local config_file="${SCRIPT_DIR}/peers.json"
log "Writing ${config_file}"
echo "{" > "${config_file}"
echo ' "threshold": '"${THRESHOLD}"',' >> "${config_file}"
echo ' "peers": {' >> "${config_file}"
local all_nodes=("${NODES_LUX[@]}" "${NODES_CUSTOMER[@]}")
local count=${#all_nodes[@]}
for i in "${!all_nodes[@]}"; do
local node="${all_nodes[$i]}"
local pubkey
pubkey=$(cat "${KEYS_DIR}/${node}/identity.pub")
local comma=","
if [ "$((i + 1))" -eq "${count}" ]; then
comma=""
fi
echo " \"${node}\": {\"pubkey\": \"${pubkey}\"}${comma}" >> "${config_file}"
done
echo ' }' >> "${config_file}"
echo '}' >> "${config_file}"
}
# Main
log "Initializing 3-of-5 MPC cluster (threshold=${THRESHOLD}, total=${TOTAL})"
log "Mode: ${MODE}"
log "Keys directory: ${KEYS_DIR}"
echo ""
case "${MODE}" in
all|--all)
log "Generating Lux node identities..."
for node in "${NODES_LUX[@]}"; do
generate_identity "${node}" "${KEYS_DIR}/${node}"
generate_zapdb_password "${node}" "${KEYS_DIR}/${node}"
done
echo ""
log "Generating customer node identities..."
for node in "${NODES_CUSTOMER[@]}"; do
generate_identity "${node}" "${KEYS_DIR}/${node}"
generate_zapdb_password "${node}" "${KEYS_DIR}/${node}"
done
;;
--lux-only)
log "Generating Lux node identities only..."
for node in "${NODES_LUX[@]}"; do
generate_identity "${node}" "${KEYS_DIR}/${node}"
generate_zapdb_password "${node}" "${KEYS_DIR}/${node}"
done
;;
--customer-only)
log "Generating customer node identities only..."
for node in "${NODES_CUSTOMER[@]}"; do
generate_identity "${node}" "${KEYS_DIR}/${node}"
generate_zapdb_password "${node}" "${KEYS_DIR}/${node}"
done
;;
*)
err "Unknown mode: ${MODE}. Use: all, --lux-only, --customer-only"
;;
esac
echo ""
# Write configs if all keys exist
if [ -d "${KEYS_DIR}/lux-0" ] && [ -d "${KEYS_DIR}/cust-0" ]; then
write_peer_config
write_env_file
fi
echo ""
log "========================================="
log "Initialization complete."
log ""
log "Next steps:"
log ""
log " 1. Start the cluster:"
log " docker compose up -d"
log ""
log " 2. Verify all nodes are healthy:"
log " for port in 9800 9801 9802 9810 9811; do"
log " echo \"Port \${port}: \$(curl -s http://localhost:\${port}/health)\""
log " done"
log ""
log " 3. Run DKG (distributed key generation):"
log " curl -X POST http://localhost:9800/keygen \\"
log " -H 'Content-Type: application/json' \\"
log " -d '{\"keyId\":\"main\",\"threshold\":3,\"parties\":5,\"curve\":\"secp256k1\"}'"
log ""
log " 4. Register the generated key in KMS:"
log " curl -X POST http://localhost:8080/keys \\"
log " -H 'Content-Type: application/json' \\"
log " -d '{\"name\":\"customer-main\",\"type\":\"mpc\",\"keyId\":\"main\"}'"
log ""
log " 5. For Safe integration, generate 3 FROST keys:"
log " for key in safe-owner-ops safe-owner-dao safe-owner-recovery; do"
log " curl -X POST http://localhost:9800/keygen \\"
log " -H 'Content-Type: application/json' \\"
log " -d \"{\\\"keyId\\\":\\\"\${key}\\\",\\\"threshold\\\":3,\\\"parties\\\":5,\\\"curve\\\":\\\"secp256k1\\\",\\\"protocol\\\":\\\"frost\\\"}\""
log " done"
log ""
log " IMPORTANT: Store ZapDB passwords and identity keys in KMS."
log " The .env file and keys/ directory contain secrets."
log " DO NOT commit them to git."
log "========================================="