igneum/packaging/hive/h-run.sh

124 lines
7.7 KiB
Bash
Executable file

#!/usr/bin/env bash
# Igneum Miner for HiveOS: starts the bundled node when asked, exports the hourly program pack, then one igneum-miner
# per GPU with the matching worker (igneum-worker-cuda on NVIDIA, igneum-worker-opencl on AMD). Each miner mines
# against the node for its own payout address; the software dev fee (1 template in 100) is the miner's default and
# DEV_FEE=0 in the Flight Sheet's extra config turns it off. Per-GPU logs: $CUSTOM_LOG_BASENAME.gpu<N>.log; the main
# log is their merged tail. Run by Hive with the working directory at the miner folder.
cd "$(dirname "$0")" || exit 1
HERE="$(pwd)"
[[ -z "$CUSTOM_LOG_BASENAME" || -z "$CUSTOM_CONFIG_FILENAME" ]] && . "$HERE/h-manifest.conf"
[[ -z "$CUSTOM_LOG_BASENAME" ]] && echo "No CUSTOM_LOG_BASENAME is set" && exit 1
[[ -f "$CUSTOM_CONFIG_FILENAME" ]] || { echo "No config at $CUSTOM_CONFIG_FILENAME (apply the Flight Sheet first)"; exit 1; }
. "$CUSTOM_CONFIG_FILENAME"
BIN="${IGNEUM_BIN:-$HERE/bin}"
mkdir -p "$(dirname "$CUSTOM_LOG_BASENAME")" "$HERE/packs" "$HERE/data"
date +%s > "$CUSTOM_LOG_BASENAME.started"
MAIN="$CUSTOM_LOG_BASENAME.log"
: > "$MAIN"
say() { printf '%s %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$*" | tee -a "$MAIN"; }
echo $$ > "$CUSTOM_LOG_BASENAME.pid"
pids=()
cleanup() { say "stopping"; for p in "${pids[@]}"; do kill "$p" 2>/dev/null; done; pkill -TERM -P $$ 2>/dev/null; sleep 2; for p in "${pids[@]}"; do kill -9 "$p" 2>/dev/null; done; pkill -KILL -P $$ 2>/dev/null; exit 0; }
trap cleanup INT TERM
# 1. the node (in pool mode, NODE_URL is "none" unless the Flight Sheet asked for a verifier node with NODE=)
POOL_URL="${POOL_URL:-}"
if [[ "$NODE_URL" == "local" ]]; then
NODE_URL="grpc://127.0.0.1:26610"
peers=()
if [[ -n "$PEERS" ]]; then IFS=',' read -r -a plist <<< "$PEERS"; for p in "${plist[@]}"; do peers+=("--addpeer=$p"); done
else peers+=("--addpeer=188.245.5.161:26611"); fi # the public devnet seed (app/igneum-app/src/config.rs)
# the consensus override (OVERRIDE in the Flight Sheet's extra config, written to igneum.conf by h-config.sh): the
# devnet's activation heights. A node without it runs on genesis parameters, prints another digest in the p2p
# handshake and is refused by every devnet peer (release 0.3.11, section 5, C41).
override=()
if [[ -n "${OVERRIDE:-}" ]]; then
printf '%s\n' "$OVERRIDE" > "$HERE/data/override-params.json"
override=("--override-params-file=$HERE/data/override-params.json")
say "consensus override from the Flight Sheet: $OVERRIDE"
else
say "WARNING: no OVERRIDE in the Flight Sheet; the node runs on genesis parameters and every devnet peer will refuse it. Set OVERRIDE from the downloads page."
fi
say "starting the bundled node (devnet, data $HERE/data, peers ${peers[*]#--addpeer=})"
"$BIN/igneumd" --devnet "--appdir=$HERE/data" --rpclisten=127.0.0.1:26610 --listen=0.0.0.0:26611 "${peers[@]}" \
${override[@]+"${override[@]}"} --nodnsseed --disable-upnp --nologfiles --yes >> "$CUSTOM_LOG_BASENAME.node.log" 2>&1 &
pids+=($!)
fi
if [[ "$NODE_URL" != "none" ]]; then
say "node $NODE_URL; waiting for it to answer"
for _ in $(seq 1 60); do
"$BIN/igneum-miner" watch 1 "$NODE_URL" >/dev/null 2>&1 && break
sleep 2
done
fi
if [[ -f "$CUSTOM_LOG_BASENAME.node.log" ]]; then
# the switch lines and the digest the node printed at start, so the operator can compare the digest with the one
# on the downloads page (another digest = refused by every peer)
n=0
while IFS= read -r l; do say "node: $l"; n=$((n + 1)); done < <(grep -E 'override params file|from the override file|Consensus params digest' "$CUSTOM_LOG_BASENAME.node.log" | head -12)
[[ $n == 0 ]] && say "node: no digest line yet in $CUSTOM_LOG_BASENAME.node.log (an older node, or not started); check it by hand"
fi
[[ -n "$POOL_URL" ]] && say "pool mode: shares to $POOL_URL, payout $WALLET, worker $LABEL-gpuN, verifier node $NODE_URL (the vote key stays this rig's; no software dev fee)"
# 2. the GPUs: Hive's gpu-detect when present, else the driver tools
nv=0; amd=0
if command -v gpu-detect >/dev/null 2>&1; then nv=$(gpu-detect NVIDIA 2>/dev/null || echo 0); amd=$(gpu-detect AMD 2>/dev/null || echo 0)
else
command -v nvidia-smi >/dev/null 2>&1 && nv=$(nvidia-smi -L 2>/dev/null | grep -c '^GPU')
[[ -d /sys/bus/pci/drivers/amdgpu ]] && amd=$(ls /sys/bus/pci/drivers/amdgpu 2>/dev/null | grep -c '^0000:')
fi
nv=${nv:-0}; amd=${amd:-0}
say "GPUs: $nv NVIDIA, $amd AMD (worker setting: $WORKER)"
[[ "$nv" == "0" && "$amd" == "0" ]] && say "no GPU found; nothing to mine with" && sleep 30 && exit 1
# 3. the hourly program pack from the node (the workers read it with --pack; the miner writes the next one to packs/prepare)
export_pack() { [[ "$NODE_URL" == "none" ]] && return 0; rm -rf "$HERE/packs/devnet"; "$BIN/igneum-miner" export-pack "$NODE_URL" "$HERE/packs/devnet" >> "$MAIN" 2>&1; }
# a shipped pack (packs/<name>/program.json, from make-hive-package.sh --kit; 0.3.22) seeds packs/devnet ONLY when the node's own
# export left nothing: the rig's first-start convenience, never the authority (a rig starting after epoch 0 re-exports from its node;
# the miner's --prepare-packs and exit 42 keep it on the chain's program as before). SHIPPED_PACK names the directory under packs/
# (h-config.sh or the Flight Sheet; default v4-devnet3-epoch0 when it exists).
seed_pack() {
[[ -d "$HERE/packs/devnet" && -f "$HERE/packs/devnet/program.json" ]] && return 0
local sp="${SHIPPED_PACK:-v4-devnet3-epoch0}"
if [[ -f "$HERE/packs/$sp/program.json" ]]; then rm -rf "$HERE/packs/devnet"; cp -R "$HERE/packs/$sp" "$HERE/packs/devnet"; say "first start: packs/devnet seeded from the shipped pack $sp (program_id $(sed -n 's/.*"program_id" *: *"\{0,1\}\([0-9a-fx]*\)"\{0,1\}.*/\1/p' "$HERE/packs/$sp/program.json" | head -1)); the node's export replaces it"; fi
}
export_pack || say "pack export failed; the miners retry"
seed_pack
# 4. one miner per GPU, restarted on exit (exit 42 = the program changed and the worker cannot prepare: re-export the pack)
run_gpu() {
local idx="$1" worker="$2" dev="$3" tag="$4"
local log="$CUSTOM_LOG_BASENAME.gpu$idx.log"
local args=(mine "$NODE_URL" 1 100000000 "$LABEL-gpu$idx" --worker "$BIN/$worker" --worker-args "--device $dev --pack packs/devnet"
--prepare-packs packs/prepare --exit-on-seed-change --evm-address "$WALLET" --payout-label "$LABEL-gpu$idx" --status-secs 30)
if [[ -n "$POOL_URL" ]]; then
# pool mode: one vote key per operator (spec 9.6), the worker name labels the card, no software dev fee
args+=(--pool "$POOL_URL" --worker-name "$LABEL-gpu$idx")
else
local identv="IDENTITIES_GPU$idx" ident="$IDENTITIES"; [[ -n "${!identv:-}" ]] && ident="${!identv}" # per-card from h-config.sh, else the fallback
[[ "$ident" -gt 1 ]] && args+=(--identities "$ident")
[[ "$DEV_FEE" != "1" ]] && args+=(--dev-fee "$DEV_FEE")
fi
[[ "$VOTE" == "0" ]] && args+=(--no-vote)
[[ -n "$EXTRA" ]] && args+=($EXTRA)
while :; do
echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) gpu$idx ($tag $dev): igneum-miner ${args[*]}" >> "$log"
"$BIN/igneum-miner" "${args[@]}" >> "$log" 2>&1
rc=$?
if [[ $rc == 42 ]]; then export_pack; else sleep 5; fi
done
}
idx=0
if [[ "$nv" -gt 0 && "$WORKER" != "opencl" ]]; then
for ((d = 0; d < nv; d++)); do run_gpu "$idx" igneum-worker-cuda "$d" nvidia & pids+=($!); idx=$((idx + 1)); done
fi
if [[ "$amd" -gt 0 && "$WORKER" != "cuda" ]]; then
for ((d = 0; d < amd; d++)); do run_gpu "$idx" igneum-worker-opencl "$d" amd & pids+=($!); idx=$((idx + 1)); done
fi
say "$idx miner(s) started; dev fee ${DEV_FEE}% (the miner prints its own dev fee line below)"
# 5. the main log: every GPU's lines, merged
sleep 1
tail -q -n +1 -F "$CUSTOM_LOG_BASENAME".gpu*.log 2>/dev/null | tee -a "$MAIN" &
pids+=($!)
wait