packaging/linux: preflight (Ubuntu 24.04, NVIDIA driver 580 floor with libcuda and libnvrtc.so.12, AMD ROCm OpenCL ICD, Intel compute runtime, nvidia-smi and the OpenCL list printed, RAM, free disk, ports, ufw), the wallet and rig name asked once, the igneum system user, the Ed25519 check of dl/public/igneum-app-latest.json with the OTA public key (OpenSSL 3 pkeyutl -rawin, python3 cryptography fallback, never skipped), consensus.override written from the verified manifest and refreshed hourly (the HiveOS override rule without a package republish), the HiveOS package downloaded with size and sha256 checked (the signed linux entry when the manifest has one, else the .sha256 sidecar behind --allow-sidecar-sha256, said in capitals), releases under /opt/igneum with a current symlink and a 90-s rollback, one miner unit per card with CUDA_DEVICE_ORDER=PCI_BUS_ID and the OpenCL ordinal mapping, the integrated GPU and the BMC VGA excluded by the inventory, the prover unit as the proving-v1 loop in bash (12 GB gate, 20 GB mine-and-prove line with the card's miner paused per shard through a sudoers rule, idles in state setup while no Linux prover binary is published), telemetry in the app's line shapes with the relay upload under nodelog-linux/miner-<vendor>/linux labels, the identities rule with its 8 GiB threshold. Tested on the Mac: shellcheck -x -S style clean, bash -n, check-units.sh (6 units, the systemd-analyze stand-in; no systemd or Docker here), the inventory on a fake sysfs tree, the rules, the live manifest verified by both verifiers and tampered copies refused, the installer dry run with the 0.3.9 package downloaded and verified. Untested until a rig exists: listed in README.md. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
307 lines
24 KiB
Bash
Executable file
307 lines
24 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Igneum rig installer for Ubuntu 24.04: an 8-GPU mining and proving rig (NVIDIA through CUDA, AMD and Intel through
|
|
# OpenCL) as systemd units, built from the HiveOS package (packaging/hive) and its lessons of 5 October 2026.
|
|
#
|
|
# sudo packaging/linux/install-rig.sh --wallet 0x<40 hex> --name rig1 [--network devnet|testnet] [--prover auto|on|off]
|
|
# [--relay-key-file ~/log-intake-key] [--peers host:port,...] [--dev-fee 1] [--identities auto|N]
|
|
# [--allow-sidecar-sha256] [--package-url URL --package-sha256 HEX --package-size N] [--no-start] [--yes]
|
|
# packaging/linux/install-rig.sh --preflight-only the checks alone, nothing written (root not needed)
|
|
# packaging/linux/install-rig.sh --dry-run ... every step printed, the download and the manifest
|
|
# verification run for real into a scratch folder, nothing
|
|
# under / is touched (runs on a Mac too)
|
|
#
|
|
# What it does, in order: preflight (Ubuntu 24.04, drivers and compute runtimes per vendor, nvidia-smi and the OpenCL
|
|
# device list printed, RAM, free disk, ports); the wallet and the rig name, asked once; the igneum system user and the
|
|
# folders; the signed manifest (Ed25519, the OTA public key) for the consensus override and the package entry; the
|
|
# package download with size and sha256 checked; the units; enable and start. Re-running is safe: it keeps rig.conf
|
|
# and the machine id, replaces the scripts and units, and installs a newer package only when the manifest names one.
|
|
set -euo pipefail
|
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
. "$HERE/bin/igneum-rig-lib.sh"
|
|
|
|
WALLET_ARG=""; NAME_ARG=""; NETWORK_ARG="devnet"; PROVER_ARG="auto"; RELAY_KEY_ARG=""; PEERS_ARG=""; DEV_FEE_ARG=1; IDENT_ARG=auto
|
|
ALLOW_SIDECAR=0; PKG_URL=""; PKG_SHA=""; PKG_SIZE=""; NO_START=0; YES=0; DRY=0; PREFLIGHT_ONLY=0; FORCE=0
|
|
MIN_NVIDIA_DRIVER="${MIN_NVIDIA_DRIVER:-580}" # the project lead, 5 October 2026: driver 580 or newer for the RTX 50 series
|
|
MIN_FREE_GB=20
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--wallet) WALLET_ARG="$2"; shift 2 ;;
|
|
--name) NAME_ARG="$2"; shift 2 ;;
|
|
--network) NETWORK_ARG="$2"; shift 2 ;;
|
|
--prover) PROVER_ARG="$2"; shift 2 ;;
|
|
--relay-key-file) RELAY_KEY_ARG="$2"; shift 2 ;;
|
|
--peers) PEERS_ARG="$2"; shift 2 ;;
|
|
--dev-fee) DEV_FEE_ARG="$2"; shift 2 ;;
|
|
--identities) IDENT_ARG="$2"; shift 2 ;;
|
|
--allow-sidecar-sha256) ALLOW_SIDECAR=1; shift ;;
|
|
--package-url) PKG_URL="$2"; shift 2 ;;
|
|
--package-sha256) PKG_SHA="$2"; shift 2 ;;
|
|
--package-size) PKG_SIZE="$2"; shift 2 ;;
|
|
--no-start) NO_START=1; shift ;;
|
|
--yes) YES=1; shift ;;
|
|
--dry-run) DRY=1; shift ;;
|
|
--preflight-only) PREFLIGHT_ONLY=1; shift ;;
|
|
--force) FORCE=1; shift ;;
|
|
-h|--help) sed -n '2,20p' "$0"; exit 0 ;;
|
|
*) die "unknown argument: $1 (see --help)" ;;
|
|
esac
|
|
done
|
|
run() { if [[ $DRY == 1 ]]; then printf '[dry-run] %s\n' "$*"; else "$@"; fi; }
|
|
step() { printf '\n== %s\n' "$*"; }
|
|
ok() { printf ' ok %s\n' "$*"; }
|
|
bad() { printf ' FAIL %s\n' "$*"; FAILS=$((FAILS + 1)); }
|
|
soft() { printf ' warn %s\n' "$*"; WARNS=$((WARNS + 1)); }
|
|
FAILS=0; WARNS=0
|
|
if [[ $DRY == 0 && $PREFLIGHT_ONLY == 0 && $(id -u) != 0 ]]; then die "run as root (sudo); --dry-run and --preflight-only need no root"; fi
|
|
|
|
# ---- 1. preflight -------------------------------------------------------------------------------------------------
|
|
step "preflight"
|
|
# shellcheck disable=SC1091
|
|
os_id="$(. /etc/os-release 2>/dev/null && echo "${ID:-?} ${VERSION_ID:-?}" || echo "unknown")"
|
|
if [[ "$os_id" == "ubuntu 24.04" ]]; then ok "Ubuntu 24.04"; else bad "not Ubuntu 24.04 ($os_id); the package was built for glibc 2.27 and newer, the units for systemd (--force to go on)"; fi
|
|
if [[ "$(uname -m)" == x86_64 ]]; then ok "x86_64"; else bad "architecture $(uname -m): the package is x86_64 only"; fi
|
|
if have systemctl; then ok "systemd $(systemctl --version 2>/dev/null | head -1 | awk '{print $2}')"; else bad "no systemctl"; fi
|
|
for t in curl python3 tar sha256sum flock awk sed; do if have "$t"; then ok "$t"; else bad "$t is missing (apt install curl python3 tar coreutils util-linux)"; fi; done
|
|
have lspci || soft "lspci is missing (apt install pciutils): Intel card names fall back to the PCI id"
|
|
if have openssl && openssl pkeyutl -help 2>&1 | grep -q -- '-rawin'; then ok "openssl $(openssl version | awk '{print $2}') verifies Ed25519 (pkeyutl -rawin)"
|
|
elif python3 -c 'import cryptography' 2>/dev/null; then ok "python3 cryptography verifies Ed25519 (openssl here lacks pkeyutl -rawin)"
|
|
else bad "no Ed25519 verifier: OpenSSL 3 or python3-cryptography is needed to check the signed manifest"; fi
|
|
kernel="$(uname -r)"; ok "kernel $kernel"
|
|
mem_kb="$(awk '/MemTotal/ {print $2}' /proc/meminfo 2>/dev/null || echo 0)"; mem_gb=$((mem_kb / 1048576))
|
|
if [[ "$mem_gb" -ge 16 ]]; then ok "RAM $mem_gb GB"; elif [[ "$mem_gb" -ge 8 ]]; then soft "RAM $mem_gb GB: mining is fine; the SP1 prover's host side wants more (2.3 GB measured inside WSL2 on 5 October 2026, plus the node); 16 GB or more for proving"; else bad "RAM $mem_gb GB is under 8 GB"; fi
|
|
free_gb() { df -Pk "$1" 2>/dev/null | awk 'NR==2 {printf "%d", $4/1048576}'; }
|
|
for p in /var/lib /opt; do
|
|
g="$(free_gb "$p")"; [[ -n "$g" ]] || g=0
|
|
if [[ "$g" -ge "$MIN_FREE_GB" ]]; then ok "$g GB free on $p"; else bad "$g GB free on $p, under $MIN_FREE_GB GB (the chain data and the package)"; fi
|
|
done
|
|
# ports: the node's P2P port must be free (and open inbound for peers to dial in); RPC and EVM RPC are loopback only
|
|
NETWORK="$NETWORK_ARG"
|
|
case "$NETWORK" in devnet) P2P_PORT=26611; RPC_PORT=26610 ;; testnet) P2P_PORT=26811; RPC_PORT=26810 ;; *) die "--network must be devnet or testnet" ;; esac
|
|
EVM_PORT=$((RPC_PORT + 180))
|
|
if have ss; then
|
|
for port in "$P2P_PORT" "$RPC_PORT" "$EVM_PORT"; do
|
|
if ss -ltn 2>/dev/null | awk '{print $4}' | grep -q ":$port\$"; then
|
|
if systemctl is-active igneum-node >/dev/null 2>&1; then ok "port $port is held by the running igneum-node (re-install)"; else bad "port $port is in use by something else"; fi
|
|
else ok "port $port free"; fi
|
|
done
|
|
else soft "ss is missing (iproute2); ports not checked"; fi
|
|
if have ufw && ufw status 2>/dev/null | grep -q '^Status: active'; then
|
|
if ufw status 2>/dev/null | grep -qE "^$P2P_PORT(/tcp)? +ALLOW"; then ok "ufw allows $P2P_PORT/tcp inbound"; else soft "ufw is active and $P2P_PORT/tcp is not allowed: the installer adds the rule (peers dial in on it; mining works without it)"; UFW_OPEN=1; fi
|
|
fi
|
|
UFW_OPEN="${UFW_OPEN:-0}"
|
|
|
|
# the cards
|
|
step "cards (igneum-gpus.sh)"
|
|
INV="$("$HERE/bin/igneum-gpus.sh" 2> >(sed 's/^/ note /' >&2) || true)"
|
|
if [[ -z "$INV" ]]; then bad "no NVIDIA, AMD or Intel discrete GPU found in /sys/bus/pci (on a Mac this is expected)"; else printf '%s\n' "$INV" | sed 's/^/ card /'; fi
|
|
NV_COUNT="$(printf '%s\n' "$INV" | grep -c '^nvidia' || true)"; AMD_COUNT="$(printf '%s\n' "$INV" | grep -c '^amd' || true)"; INTEL_COUNT="$(printf '%s\n' "$INV" | grep -c '^intel' || true)"
|
|
AMD_OK=1; INTEL_OK=1
|
|
if [[ "$NV_COUNT" -gt 0 ]]; then
|
|
step "NVIDIA ($NV_COUNT cards): driver, CUDA, NVRTC"
|
|
if have nvidia-smi; then
|
|
drv="$(nvidia-smi --query-gpu=driver_version --format=csv,noheader 2>/dev/null | head -1)"
|
|
cuda="$(nvidia-smi 2>/dev/null | sed -n 's/.*CUDA Version: \([0-9.]*\).*/\1/p' | head -1)"
|
|
if [[ "${drv%%.*}" -ge "$MIN_NVIDIA_DRIVER" ]] 2>/dev/null; then ok "driver $drv (at least $MIN_NVIDIA_DRIVER)"; else bad "driver ${drv:-unknown}: $MIN_NVIDIA_DRIVER or newer is required (the RTX 50 series needs a Blackwell-capable driver; 580 is the floor the project lead set)"; fi
|
|
if [[ -n "$cuda" && "${cuda%%.*}" -ge 12 ]]; then ok "CUDA runtime $cuda reported by the driver (12 or newer)"; else soft "nvidia-smi reports CUDA '${cuda:-?}'; the worker needs a CUDA 12 driver"; fi
|
|
if ldconfig -p 2>/dev/null | grep -q 'libcuda\.so\.1'; then ok "libcuda.so.1 on the library path"; else bad "libcuda.so.1 is not on the library path (the driver's CUDA library)"; fi
|
|
# the worker compiles the hourly program with NVRTC 12 (dlopen libnvrtc.so.12; infra/cross/build-workers-linux.sh);
|
|
# a CUDA 13 toolkit ships libnvrtc.so.13, which does not satisfy it
|
|
if ldconfig -p 2>/dev/null | grep -q 'libnvrtc\.so\.12'; then ok "libnvrtc.so.12 on the library path"
|
|
else bad "libnvrtc.so.12 is missing: install a CUDA 12.x toolkit or the NVRTC 12 redistributable (apt install cuda-nvrtc-12-8 from NVIDIA's Ubuntu 24.04 repository, approximate package name) and run ldconfig"; fi
|
|
printf ' nvidia-smi:\n'; nvidia-smi 2>/dev/null | sed 's/^/ | /' || true
|
|
else bad "nvidia-smi is missing: no NVIDIA driver (install the $MIN_NVIDIA_DRIVER series or newer from NVIDIA's Ubuntu 24.04 repository)"; fi
|
|
fi
|
|
if [[ "$AMD_COUNT" -gt 0 ]]; then
|
|
step "AMD ($AMD_COUNT cards): amdgpu, ROCm OpenCL"
|
|
printf '%s\n' "$INV" | awk '$2 == "amd" {print $3}' | while read -r bus; do if [[ -d "/sys/bus/pci/drivers/amdgpu/$bus" ]]; then ok "amdgpu bound to $bus"; else soft "amdgpu is not bound to $bus"; fi; done
|
|
if [[ -f /opt/rocm/.info/version ]]; then ok "ROCm $(cat /opt/rocm/.info/version) (RDNA 4, gfx1201, needs ROCm 6.4 or newer: approximate, from AMD's ROCm release notes)"; else soft "no /opt/rocm/.info/version: ROCm is not installed the usual way (RDNA 4 needs ROCm 6.4 or newer, approximate)"; fi
|
|
if ldconfig -p 2>/dev/null | grep -q 'libOpenCL\.so\.1'; then ok "libOpenCL.so.1 on the library path"; else bad "libOpenCL.so.1 is missing (apt install rocm-opencl-runtime or ocl-icd-libopencl1 plus AMD's ICD)"; AMD_OK=0; fi
|
|
if ls /etc/OpenCL/vendors/*.icd >/dev/null 2>&1 && grep -qil 'amdocl\|rocm' /etc/OpenCL/vendors/*.icd 2>/dev/null; then ok "AMD OpenCL ICD in /etc/OpenCL/vendors"; else bad "no AMD ICD file in /etc/OpenCL/vendors (rocm-opencl-runtime writes one)"; AMD_OK=0; fi
|
|
if [[ "${kernel%%.*}" -ge 7 || ( "${kernel%%.*}" -eq 6 && "$(echo "$kernel" | cut -d. -f2)" -ge 11 ) ]] 2>/dev/null; then ok "kernel $kernel for RDNA 4 (6.11 or newer, approximate)"; else soft "kernel $kernel: RDNA 4 support landed in 6.11 (approximate; Ubuntu 24.04's HWE kernel is newer than its GA 6.8)"; fi
|
|
fi
|
|
if [[ "$INTEL_COUNT" -gt 0 ]]; then
|
|
step "Intel ($INTEL_COUNT cards): xe driver, compute runtime"
|
|
printf '%s\n' "$INV" | awk '$2 == "intel" {print $3}' | while read -r bus; do if [[ -d "/sys/bus/pci/drivers/xe/$bus" || -d "/sys/bus/pci/drivers/i915/$bus" ]]; then ok "xe or i915 bound to $bus"; else soft "neither xe nor i915 is bound to $bus"; fi; done
|
|
if ls /etc/OpenCL/vendors/*.icd >/dev/null 2>&1 && grep -qil 'intel\|igdrcl' /etc/OpenCL/vendors/*.icd 2>/dev/null; then ok "Intel OpenCL ICD in /etc/OpenCL/vendors ($(dpkg-query -W -f='${Version}' intel-opencl-icd 2>/dev/null || echo 'version unknown'))"; else bad "no Intel ICD (apt install intel-opencl-icd from Intel's compute-runtime repository; the Arc B580 needs a 2025 release, approximate)"; INTEL_OK=0; fi
|
|
if [[ "${kernel%%.*}" -ge 7 || ( "${kernel%%.*}" -eq 6 && "$(echo "$kernel" | cut -d. -f2)" -ge 12 ) ]] 2>/dev/null; then ok "kernel $kernel for Battlemage (6.12 or newer, approximate)"; else soft "kernel $kernel: the Arc B580 (Battlemage) needs the xe driver of 6.12 or newer (approximate)"; fi
|
|
fi
|
|
if have clinfo; then step "OpenCL devices (clinfo -l)"; clinfo -l 2>/dev/null | sed 's/^/ | /' || true; fi
|
|
printf '\n preflight: %d failure(s), %d warning(s)\n' "$FAILS" "$WARNS"
|
|
if [[ $PREFLIGHT_ONLY == 1 ]]; then exit $(( FAILS > 0 ? 1 : 0 )); fi
|
|
if [[ $FAILS -gt 0 && $FORCE == 0 && $DRY == 0 ]]; then die "preflight failed; fix the items above or pass --force"; fi
|
|
[[ $FAILS -gt 0 && $DRY == 1 ]] && printf ' (dry run: going on despite the failures)\n'
|
|
|
|
# ---- 2. what the project lead types: the wallet and the rig name ---------------------------------------------------------------
|
|
step "wallet and name"
|
|
if [[ -f "$RIG_CONF" ]]; then load_conf; [[ -n "$WALLET" && -z "$WALLET_ARG" ]] && WALLET_ARG="$WALLET"; [[ -n "$RIG_NAME" && -z "$NAME_ARG" ]] && NAME_ARG="$RIG_NAME"; printf ' existing %s read (wallet %s..., name %s)\n' "$RIG_CONF" "${WALLET_ARG:0:8}" "$NAME_ARG"; fi
|
|
ask() { local v; if [[ $YES == 1 ]]; then die "$1 is required with --yes"; fi; read -r -p " $2: " v; printf '%s' "$v"; }
|
|
[[ -n "$WALLET_ARG" ]] || WALLET_ARG="$(ask --wallet 'payout wallet (0x followed by 40 hex, an EVM address you hold the key for)')"
|
|
[[ "$WALLET_ARG" =~ ^0x[0-9a-fA-F]{40}$ ]] || die "the wallet must be 0x followed by 40 hex characters, got '$WALLET_ARG'"
|
|
WALLET_ARG="$(printf '%s' "$WALLET_ARG" | tr 'A-F' 'a-f')"
|
|
[[ -n "$NAME_ARG" ]] || NAME_ARG="$(ask --name 'rig name (letters, digits, - and _; it labels this rig in payouts and on the console)')"
|
|
NAME_ARG="$(printf '%s' "$NAME_ARG" | tr -c 'A-Za-z0-9_-' '-' | cut -c1-32)"
|
|
[[ -n "$NAME_ARG" ]] || die "the rig name is empty"
|
|
case "$PROVER_ARG" in auto|on|off) ;; *) die "--prover must be auto, on or off" ;; esac
|
|
[[ "$DEV_FEE_ARG" =~ ^[0-9]+$ ]] || die "--dev-fee must be a whole percent"
|
|
[[ "$IDENT_ARG" == auto || "$IDENT_ARG" =~ ^[0-9]+$ ]] || die "--identities must be auto or a number"
|
|
if [[ -n "$RELAY_KEY_ARG" ]]; then [[ -s "$RELAY_KEY_ARG" ]] || die "no relay key file at $RELAY_KEY_ARG"; fi
|
|
ok "wallet ${WALLET_ARG:0:10}...${WALLET_ARG: -4}, name $NAME_ARG, network $NETWORK, prover $PROVER_ARG, dev fee ${DEV_FEE_ARG}%, identities $IDENT_ARG, relay upload $( [[ -n "$RELAY_KEY_ARG" ]] && echo on || echo off )"
|
|
|
|
# ---- 3. user, folders, machine id -----------------------------------------------------------------------------------
|
|
step "system user and folders"
|
|
if id "$IGNEUM_USER" >/dev/null 2>&1; then ok "user $IGNEUM_USER exists"
|
|
else run useradd --system --home-dir "$IGNEUM_VAR" --shell /usr/sbin/nologin --user-group "$IGNEUM_USER"; fi
|
|
for g in video render systemd-journal; do if getent group "$g" >/dev/null 2>&1; then run usermod -a -G "$g" "$IGNEUM_USER"; else soft "group $g does not exist here (GPU device access on Ubuntu is through video and render)"; fi; done
|
|
run mkdir -p "$IGNEUM_ROOT/bin" "$IGNEUM_ROOT/releases" "$IGNEUM_ETC" "$IGNEUM_VAR/node" "$IGNEUM_VAR/packs" "$IGNEUM_VAR/proving" "$IGNEUM_VAR/updates" "$IGNEUM_RUN"
|
|
if [[ -s "$IGNEUM_ETC/machine-id" ]]; then ok "machine id $(cut -c1-8 "$IGNEUM_ETC/machine-id") kept"
|
|
else
|
|
mid="$(head -c 8 /dev/urandom | od -An -tx1 | tr -d ' \n')"
|
|
if [[ $DRY == 1 ]]; then printf '[dry-run] write %s/machine-id = %s\n' "$IGNEUM_ETC" "$mid"; else printf '%s\n' "$mid" > "$IGNEUM_ETC/machine-id"; fi
|
|
ok "machine id ${mid:0:8} (the console's id8; labels are miner-<vendor>-${mid:0:8}-<n>)"
|
|
fi
|
|
|
|
# ---- 4. the signed manifest: the consensus override and the package ------------------------------------------------
|
|
step "signed manifest ($MANIFEST_URL)"
|
|
WORK="$(mktemp -d)"; trap 'rm -rf "$WORK"' EXIT
|
|
if fetch_manifest "$WORK"; then
|
|
m="$WORK/igneum-app-latest.json"
|
|
ok "signature verifies with the OTA public key ${OTA_PUBLIC_KEY_HEX:0:16}... ($MANIFEST_VERIFIER); version $(manifest_field "$m" 'm.get("version")'), published $(manifest_field "$m" 'm.get("published_at")'), channel $(manifest_field "$m" 'm.get("channel")')"
|
|
OVERRIDE="$(manifest_override "$m")"
|
|
if [[ -n "$OVERRIDE" ]]; then ok "consensus.override $OVERRIDE (the node's --override-params-file; refreshed hourly by igneum-update)"; else soft "the manifest carries no consensus.override (a fresh testnet needs none; the devnet refuses a node without it)"; fi
|
|
ENTRY="$(manifest_package "$m")"
|
|
else
|
|
bad "the manifest could not be fetched or verified; without it there is no consensus override and no package entry"
|
|
[[ $DRY == 1 ]] || die "stopped: the signed manifest is required"
|
|
OVERRIDE=""; ENTRY=""; m=""
|
|
fi
|
|
PACKAGE_SOURCE=signed
|
|
if [[ -n "$PKG_URL" ]]; then
|
|
[[ "$PKG_SHA" =~ ^[0-9a-fA-F]{64}$ && "$PKG_SIZE" =~ ^[0-9]+$ ]] || die "--package-url needs --package-sha256 (64 hex) and --package-size (bytes)"
|
|
ENTRY="$PKG_URL $(printf '%s' "$PKG_SHA" | tr 'A-F' 'a-f') $PKG_SIZE $(basename "$PKG_URL" | sed 's/^igneum-hive-//; s/\.tar\.gz$//')"; PACKAGE_SOURCE=given
|
|
ok "package given on the command line (its sha256 is yours to vouch for)"
|
|
elif [[ -n "$ENTRY" ]]; then ok "the signed manifest names the Linux package: $ENTRY"
|
|
else
|
|
soft "the signed manifest names no linux package (5 October 2026: it carries mac and windows only)"
|
|
if [[ $ALLOW_SIDECAR == 1 ]]; then
|
|
if ENTRY="$(sidecar_package)"; then PACKAGE_SOURCE=sidecar; soft "using the UNSIGNED igneum-downloads.json entry and the .sha256 sidecar over TLS (--allow-sidecar-sha256): $ENTRY"; else bad "igneum-downloads.json names no miner-hive package"; fi
|
|
else
|
|
bad "no signed package entry; pass --allow-sidecar-sha256 to accept the unsigned .sha256 sidecar from $DL_HOST, or --package-url/--package-sha256/--package-size"
|
|
[[ $DRY == 1 ]] || die "stopped: no package to install"
|
|
fi
|
|
fi
|
|
|
|
step "package download and verification"
|
|
VERSION=""; PKG=""
|
|
if [[ -n "$ENTRY" ]]; then
|
|
read -r url sha size VERSION <<< "$ENTRY"
|
|
[[ -n "$VERSION" ]] || VERSION="$(basename "$url" | sed 's/^igneum-hive-//; s/\.tar\.gz$//')"
|
|
dl_dir="$IGNEUM_VAR/updates"; [[ $DRY == 1 ]] && dl_dir="$WORK/updates"
|
|
mkdir -p "$dl_dir"
|
|
PKG="$dl_dir/$(basename "$url")"
|
|
if download_verified "$url" "$PKG" "$sha" "$size"; then
|
|
ok "$(basename "$PKG"): $size bytes, sha256 $sha ($PACKAGE_SOURCE)"
|
|
if [[ $PACKAGE_SOURCE == sidecar ]]; then if sidecar_matches "$url" "$PKG"; then ok ".sha256 sidecar agrees"; else bad ".sha256 sidecar disagrees with the download"; PKG=""; fi; fi
|
|
else bad "download or verification failed"; PKG=""; fi
|
|
fi
|
|
if [[ -n "$PKG" ]]; then
|
|
stage="$WORK/stage"; mkdir -p "$stage"
|
|
tar -C "$stage" --strip-components=1 -xzf "$PKG"
|
|
for b in igneumd igneum-miner igneum-worker-cuda igneum-worker-opencl; do if [[ -x "$stage/bin/$b" ]]; then ok "bin/$b $(stat -c %s "$stage/bin/$b" 2>/dev/null || stat -f %z "$stage/bin/$b") bytes"; else soft "bin/$b is not in the package"; fi; done
|
|
[[ -s "$stage/override-params.json" ]] && ok "override-params.json in the package (offline fallback): $(tr -d ' \n' < "$stage/override-params.json")"
|
|
# the glibc floor every ELF wants against this system's glibc (packaging/hive/make-hive-package.sh's gate, read here)
|
|
sys_glibc="$(ldd --version 2>/dev/null | head -1 | grep -o '[0-9]*\.[0-9]*$' || echo '?')"
|
|
for b in "$stage"/bin/*; do
|
|
want="$(python3 -c 'import re,sys; d=open(sys.argv[1],"rb").read(); vs=sorted(set(m.decode() for m in re.findall(rb"GLIBC_\d+\.\d+", d)), key=lambda v: tuple(int(x) for x in v[6:].split("."))); print(vs[-1][6:] if vs else "none")' "$b")"
|
|
printf ' glibc %s wants GLIBC_%s (system %s)\n' "$(basename "$b")" "$want" "$sys_glibc"
|
|
done
|
|
if [[ "$(uname -s)" == Linux && ( "$AMD_COUNT" -gt 0 || "$INTEL_COUNT" -gt 0 ) ]]; then
|
|
printf ' OpenCL device list (igneum-worker-opencl --list):\n'; "$stage/bin/igneum-worker-opencl" --list 2>&1 | sed 's/^/ | /' | head -60 || true
|
|
fi
|
|
REL="$IGNEUM_ROOT/releases/$VERSION"
|
|
if [[ -x "$REL/bin/igneumd" ]]; then ok "release $VERSION already unpacked at $REL"
|
|
else
|
|
run rm -rf "$REL.new"; run mkdir -p "$REL.new"
|
|
if [[ $DRY == 1 ]]; then printf '[dry-run] tar -C %s.new --strip-components=1 -xzf %s; write %s.new/version = %s; mv to %s\n' "$REL" "$PKG" "$REL" "$VERSION" "$REL"
|
|
else tar -C "$REL.new" --strip-components=1 -xzf "$PKG"; printf '%s\n' "$VERSION" > "$REL.new/version"; chmod -R a+rX "$REL.new"; mv "$REL.new" "$REL"; fi
|
|
ok "release $VERSION unpacked at $REL"
|
|
fi
|
|
run ln -sfn "$REL" "$IGNEUM_ROOT/current"
|
|
fi
|
|
if [[ -n "$OVERRIDE" ]]; then
|
|
if [[ $DRY == 1 ]]; then printf '[dry-run] write %s/override-params.json = %s\n' "$IGNEUM_ETC" "$OVERRIDE"; else printf '%s\n' "$OVERRIDE" > "$IGNEUM_ETC/override-params.json"; fi
|
|
fi
|
|
|
|
# ---- 5. configuration -----------------------------------------------------------------------------------------------
|
|
step "configuration ($RIG_CONF)"
|
|
conf="$(cat <<CONF
|
|
# Igneum rig, written by install-rig.sh on $(ts). Edit, then: systemctl restart igneum-node (the miners follow).
|
|
WALLET=$WALLET_ARG
|
|
RIG_NAME=$NAME_ARG
|
|
NETWORK=$NETWORK
|
|
PEERS=$PEERS_ARG
|
|
DEV_FEE=$DEV_FEE_ARG
|
|
IDENTITIES=$IDENT_ARG
|
|
VOTE=1
|
|
EXTRA=
|
|
PROVER=$PROVER_ARG
|
|
PROVER_MIN_VRAM_MB=11776
|
|
PROVER_MINE_AND_PROVE_MB=20480
|
|
PROVER_PAUSE_MINER=auto
|
|
PROVER_CARD=
|
|
SYNC_WAIT=3600
|
|
TELEMETRY_SECS=5
|
|
RELAY_INTAKE_URL=$( [[ -n "$RELAY_KEY_ARG" ]] && printf '%s' "$DEFAULT_INTAKE_URL" )
|
|
RELAY_KEY_FILE=$IGNEUM_ETC/log-intake-key
|
|
PACKAGE_SOURCE=$PACKAGE_SOURCE
|
|
CONF
|
|
)"
|
|
if [[ $DRY == 1 ]]; then printf '[dry-run] write %s:\n%s\n' "$RIG_CONF" "$(printf '%s\n' "$conf" | sed "s/^WALLET=.*/WALLET=${WALLET_ARG:0:10}.../; s/^/ | /")"
|
|
else printf '%s\n' "$conf" > "$RIG_CONF"; chown "root:$IGNEUM_USER" "$RIG_CONF"; chmod 0640 "$RIG_CONF"; fi
|
|
if [[ -n "$RELAY_KEY_ARG" ]]; then run install -o root -g "$IGNEUM_USER" -m 0640 "$RELAY_KEY_ARG" "$IGNEUM_ETC/log-intake-key"; ok "relay upload key installed; the console shows this rig as $NAME_ARG-<id8>"; fi
|
|
ok "config written"
|
|
|
|
# ---- 6. scripts, units, sudoers ---------------------------------------------------------------------------------------
|
|
step "scripts and units"
|
|
for f in "$HERE"/bin/*; do run install -o root -g root -m 0755 "$f" "$IGNEUM_ROOT/bin/$(basename "$f")"; done
|
|
run install -o root -g root -m 0755 "$HERE/bin/rig-status" /usr/local/bin/rig-status
|
|
run install -o root -g root -m 0644 "$HERE/README.md" "$IGNEUM_ROOT/README.md"
|
|
for u in "$HERE"/units/*; do run install -o root -g root -m 0644 "$u" "/etc/systemd/system/$(basename "$u")"; done
|
|
sudoers="$IGNEUM_USER ALL=(root) NOPASSWD: /usr/bin/systemctl stop igneum-miner@*, /usr/bin/systemctl start igneum-miner@*"
|
|
if [[ $DRY == 1 ]]; then printf '[dry-run] write /etc/sudoers.d/igneum-rig: %s\n' "$sudoers"; else printf '%s\n' "$sudoers" > /etc/sudoers.d/igneum-rig; chmod 0440 /etc/sudoers.d/igneum-rig; fi
|
|
run chown -R "$IGNEUM_USER:$IGNEUM_USER" "$IGNEUM_VAR" "$IGNEUM_RUN"
|
|
[[ "$UFW_OPEN" == 1 ]] && run ufw allow "$P2P_PORT/tcp" comment 'igneum p2p'
|
|
run systemctl daemon-reload
|
|
ok "units installed"
|
|
|
|
# the instances: one miner per usable card; the prover by the default rule
|
|
step "enable"
|
|
INSTANCES=()
|
|
while read -r line; do
|
|
[[ -z "$line" ]] && continue
|
|
c="$(awk '{print $1}' <<< "$line")"; v="$(awk '{print $2}' <<< "$line")"
|
|
case "$v" in amd) [[ $AMD_OK == 1 ]] || { soft "$c skipped: no AMD OpenCL runtime"; continue; } ;; intel) [[ $INTEL_OK == 1 ]] || { soft "$c skipped: no Intel OpenCL runtime"; continue; } ;; esac
|
|
INSTANCES+=("igneum-miner@$c.service")
|
|
done <<< "$INV"
|
|
run systemctl enable igneum-node.service igneum-telemetry.service igneum-update.timer
|
|
[[ ${#INSTANCES[@]} -gt 0 ]] && run systemctl enable "${INSTANCES[@]}"
|
|
if [[ $DRY == 0 ]]; then
|
|
PROVER="$PROVER_ARG"; load_conf; decision="$(prover_decision)"
|
|
else decision="(decided on the rig from its cards)"; fi
|
|
case "$decision" in on*) run systemctl enable igneum-prover.service; ok "prover: $decision" ;; *) run systemctl disable igneum-prover.service 2>/dev/null || true; ok "prover: $decision" ;; esac
|
|
ok "enabled: igneum-node, ${#INSTANCES[@]} miner instance(s) (${INSTANCES[*]:-none}), igneum-telemetry, igneum-update.timer"
|
|
if [[ $NO_START == 0 ]]; then
|
|
step "start"
|
|
run systemctl start igneum-node.service igneum-telemetry.service igneum-update.timer
|
|
[[ ${#INSTANCES[@]} -gt 0 ]] && run systemctl start "${INSTANCES[@]}"
|
|
case "$decision" in on*) run systemctl start igneum-prover.service ;; esac
|
|
ok "started; the miners wait for the node to sync (SYNC_WAIT 3600 s) before they mine"
|
|
fi
|
|
step "done"
|
|
cat <<TXT
|
|
rig-status per-card hash rate, watts, temperature, MH/W; node height and peers; prover
|
|
journalctl -fu igneum-node the node
|
|
journalctl -fu igneum-miner@nvidia0 one card (STATUS every 30 s: now=<MH/s>, accepted, rejected)
|
|
journalctl -fu igneum-telemetry one line per card every ${TELEMETRY_SECS:-5} s, status every 30 s, stability every 5 min
|
|
systemctl start igneum-update an update check now (hourly otherwise, at a safe moment)
|
|
$IGNEUM_ROOT/README.md what is assumed and what is untested until a rig exists
|
|
TXT
|