diff --git a/packaging/hive/README.md b/packaging/hive/README.md index 706f8844d..b59938a57 100644 --- a/packaging/hive/README.md +++ b/packaging/hive/README.md @@ -33,6 +33,7 @@ v1": jobs `memsweep-pc2-pv1`, `memminer-pc2-pv1` and the S_p curve, one RTX 5090 | Wallet and worker template | `0x<40 hex>.%WORKER_NAME%`: the payout address is an EVM address you hold the key for; the part after the dot labels this rig's keys | | Pool URL | `grpc://:26610` (your own igneumd, solo mining), or `local` to run the bundled node on the rig | | Pass | empty | +| OVERRIDE | the devnet's consensus override as one JSON object on its own line, needed with `local`: `OVERRIDE={"difficulty_v2_activation_daa":33000,"proving_v0_activation_daa":84100,"fees_v1_activation_daa":210000,"finality_v3_activation_daa":135200}` is the four-field object 0.3.10 shipped; after the 0.3.11 switch it has nine fields, and the downloads page carries the live one. Set OVERRIDE from the downloads page when it changes. **A rig without it is refused**: its node runs on genesis parameters, prints another digest, and every devnet peer drops it (`h-run.sh` warns in the main log) | | Extra config arguments | `DEV_FEE=1 IDENTITIES=auto WORKER=auto VOTE=1` (one per line also works); `PEERS=a:26611,b:26611` for the bundled node; `EXTRA="..."` for more miner flags | | IDENTITIES | vote keys per card: 8 for a card with 8 GB or more, else 2 (`IDENTITIES=auto` applies that rule, per card, from `nvidia-smi` or the amdgpu sysfs; 8 when neither answers; a number overrides it for every card). The rule is the app's (`app/igneum-app/src/detect.rs`) | @@ -55,8 +56,8 @@ The protocol carries no fee: this is the software's, and any other miner client | Hook | What | |---|---| -| `h-config.sh` | writes `igneum.conf` from the Flight Sheet (node URL, wallet, label, DEV_FEE, IDENTITIES, WORKER, VOTE, PEERS, EXTRA); resolves `IDENTITIES=auto` to `IDENTITIES_GPU` keys by VRAM; refuses a wallet that is not 0x + 40 hex | -| `h-run.sh` | starts the bundled node when the URL is `local`, waits for the node, exports the hourly program pack (`igneum-miner export-pack`), then one `igneum-miner` per GPU with its worker (`--identities` from `IDENTITIES_GPU`, else `IDENTITIES`); restarts a miner that exits (exit 42 = program change without prepare support: the pack is re-exported first); per-GPU logs `.gpu.log`, merged into the main log | +| `h-config.sh` | writes `igneum.conf` from the Flight Sheet (node URL, wallet, label, DEV_FEE, IDENTITIES, WORKER, VOTE, PEERS, EXTRA, OVERRIDE); refuses an OVERRIDE that is not `{...}`; resolves `IDENTITIES=auto` to `IDENTITIES_GPU` keys by VRAM; refuses a wallet that is not 0x + 40 hex | +| `h-run.sh` | starts the bundled node when the URL is `local` (writes `data/override-params.json` from OVERRIDE and passes `--override-params-file`; warns when OVERRIDE is empty; copies the node's switch lines and its `Consensus params digest` line into the main log), waits for the node, exports the hourly program pack (`igneum-miner export-pack`), then one `igneum-miner` per GPU with its worker (`--identities` from `IDENTITIES_GPU`, else `IDENTITIES`); restarts a miner that exits (exit 42 = program change without prepare support: the pack is re-exported first); per-GPU logs `.gpu.log`, merged into the main log | | `h-stats.sh` | per-GPU hash rate from each miner's last `STATUS` line (`now=`), accepted and rejected totals, dev-fee block count, temperatures and fans from Hive's `gpu-stats` (else `nvidia-smi`), uptime, version | Stats JSON (what Hive reads from `$stats`): `hs` (kH/s per GPU), `hs_units` (`khs`), `temp`, `fan`, `uptime` (s), @@ -68,7 +69,9 @@ Stats JSON (what Hive reads from `$stats`): `hs` (kH/s per GPU), `hs_units` (`kh on the library path). The worker compiles the hourly program at run time with NVRTC; without the library it says so and the miner retries. Hive images ship the driver; whether `libnvrtc.so.12` is present depends on the image, untested. - AMD: an OpenCL ICD (`libOpenCL.so.1` from ROCm or amdgpu-pro). The OpenCL worker compiles the program through the ICD. -- The bundled node (`local`) keeps its chain data under the miner folder (`data/`); a devnet chain is small today. +- The bundled node (`local`) keeps its chain data under the miner folder (`data/`); a devnet chain is small today. It + needs OVERRIDE (the Flight Sheet table): compare the `node: Consensus params digest:` line in the main log with the + digest on the downloads page; a different one means the override is stale and the node is refused. - Ports: the bundled node listens on 26611 (p2p) and answers RPC on 127.0.0.1:26610 only. ## Building the package @@ -85,5 +88,7 @@ Stats JSON (what Hive reads from `$stats`): `hs` (kH/s per GPU), `hs_units` (`kh - GPU order: the CUDA device index is assumed to follow `nvidia-smi` order and Hive's `gpu-stats` arrays (NVIDIA first); a mixed NVIDIA and AMD rig may show temperatures against the wrong card. - Each card runs its own `igneum-miner` and node connection; the node's template RPC serves them all. +- Before this change no package carried the override: `h-run.sh` started the node without `--override-params-file`, so + a `local` rig was refused by every devnet peer (release 0.3.11, section 5, C41). Still untested on a rig. - No prover in the package (the second paragraph above): the rig earns nothing from the proving share until a Linux prover build ships. diff --git a/packaging/hive/h-config.sh b/packaging/hive/h-config.sh index 3c724ae7d..2b3743754 100755 --- a/packaging/hive/h-config.sh +++ b/packaging/hive/h-config.sh @@ -16,6 +16,11 @@ # VOTE=1 sign finality checkpoints (0 = mine without voting) # PEERS=a:26611,b:26611 peers for the bundled node when CUSTOM_URL=local # EXTRA="..." appended to every igneum-miner command line +# OVERRIDE={...} the devnet's consensus override, one JSON object on its own line (single or +# double quotes around it are fine in the Hive UI); the bundled node (CUSTOM_URL=local) +# starts with --override-params-file from it. Without it the node runs on genesis +# parameters and every devnet peer refuses it. Copy the live object from the +# downloads page whenever it changes. # Hive sources h-manifest.conf before this hook; the fallback is for a run outside Hive (selftest.sh) [[ -z "$CUSTOM_CONFIG_FILENAME" ]] && . "$(dirname "${BASH_SOURCE[0]}")/h-manifest.conf" @@ -32,15 +37,26 @@ if ! [[ "$wallet" =~ ^0x[0-9a-fA-F]{40}$ ]]; then fi # defaults, then the user's KEY=VALUE lines -DEV_FEE=1; IDENTITIES=auto; WORKER=auto; VOTE=1; PEERS=""; EXTRA="" -while read -r kv; do - [[ -z "$kv" || "$kv" == \#* ]] && continue - key="${kv%%=*}"; val="${kv#*=}" - case "$key" in - DEV_FEE|IDENTITIES|WORKER|VOTE|PEERS|EXTRA) printf -v "$key" '%s' "$val" ;; - *) echo "Igneum: unknown setting '$key' ignored" ;; - esac -done < <(printf '%s\n' "$CUSTOM_USER_CONFIG" | tr ' ' '\n' | sed 's/^"//; s/"$//') +DEV_FEE=1; IDENTITIES=auto; WORKER=auto; VOTE=1; PEERS=""; EXTRA=""; OVERRIDE="" +while IFS= read -r line; do + [[ -z "$line" || "$line" == \#* ]] && continue + if [[ "$line" == OVERRIDE=* ]]; then # the whole line: JSON may carry spaces; quotes around it are stripped + val="${line#OVERRIDE=}"; val="${val#\'}"; val="${val%\'}"; val="${val#\"}"; val="${val%\"}" + OVERRIDE="$val"; continue + fi + while read -r kv; do + [[ -z "$kv" ]] && continue + key="${kv%%=*}"; val="${kv#*=}" + case "$key" in + DEV_FEE|IDENTITIES|WORKER|VOTE|PEERS|EXTRA) printf -v "$key" '%s' "$val" ;; + *) echo "Igneum: unknown setting '$key' ignored" ;; + esac + done < <(printf '%s\n' "$line" | tr ' ' '\n' | sed 's/^"//; s/"$//') +done < <(printf '%s\n' "$CUSTOM_USER_CONFIG") +if [[ -n "$OVERRIDE" && ! "$OVERRIDE" =~ ^\{.*\}$ ]]; then + echo -e "${YELLOW:-}Igneum: OVERRIDE must be one JSON object, {\"...\":N,...} from the downloads page; got '${OVERRIDE:0:40}'${NOCOLOR:-}" + return 1 +fi [[ "$DEV_FEE" =~ ^[0-9]+$ ]] || DEV_FEE=1 [[ "$IDENTITIES" =~ ^[0-9]+$ || "$IDENTITIES" == "auto" ]] || IDENTITIES=auto @@ -81,6 +97,7 @@ url="$CUSTOM_URL" [[ "$url" == "local" ]] && url="local" [[ "$url" != "local" && "$url" != grpc://* ]] && url="grpc://$url" +sq() { local v="${1//\'/\'\\\'\'}"; printf "'%s'" "$v"; } # single-quoted for sourcing: JSON and flags carry shell characters mkdir -p "$(dirname "$CUSTOM_CONFIG_FILENAME")" cat > "$CUSTOM_CONFIG_FILENAME" < "$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[@]}" \ - --nodnsseed --disable-upnp --nologfiles --yes >> "$CUSTOM_LOG_BASENAME.node.log" 2>&1 & + ${override[@]+"${override[@]}"} --nodnsseed --disable-upnp --nologfiles --yes >> "$CUSTOM_LOG_BASENAME.node.log" 2>&1 & pids+=($!) fi say "node $NODE_URL; waiting for it to answer" @@ -37,6 +48,13 @@ for _ in $(seq 1 60); do "$BIN/igneum-miner" watch 1 "$NODE_URL" >/dev/null 2>&1 && break sleep 2 done +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 # 2. the GPUs: Hive's gpu-detect when present, else the driver tools nv=0; amd=0 diff --git a/packaging/hive/selftest.sh b/packaging/hive/selftest.sh index 40fcedfe5..044442f4c 100755 --- a/packaging/hive/selftest.sh +++ b/packaging/hive/selftest.sh @@ -35,6 +35,14 @@ case "$1" in esac FAKE chmod +x "$M/bin/igneum-miner" +cat > "$M/bin/igneumd" <<'FAKE' +#!/usr/bin/env bash +echo "fake igneumd $*" +for a in "$@"; do case "$a" in --override-params-file=*) echo "Finality rule v3 from the override file: active from checkpoint DAA score 135200" ;; esac; done +echo "Consensus params digest: 0139ab9dc2992d449ec787d8f021974933631eb55740ab4b6ce9d5c226e72888 (exchanged in the p2p handshake; a peer with another digest is refused)" +sleep 600 +FAKE +chmod +x "$M/bin/igneumd" mkdir -p "$T/bin" printf '#!/bin/sh\n[ "$1" = NVIDIA ] && echo 2 || echo 0\n' > "$T/bin/gpu-detect" cat > "$T/bin/gpu-stats" <<'GS' @@ -62,8 +70,14 @@ grep -q '^IDENTITIES_GPU0=2$' "$T/auto-nv.conf" && grep -q '^IDENTITIES_GPU1=8$' # a number still overrides every card ( CUSTOM_USER_CONFIG="IDENTITIES=4" CUSTOM_CONFIG_FILENAME="$T/auto-num.conf" . "$M/h-config.sh" >/dev/null ) && grep -q '^IDENTITIES=4$' "$T/auto-num.conf" && ! grep -q '^IDENTITIES_GPU' "$T/auto-num.conf" && echo " numeric override keeps IDENTITIES=4 ok" || { echo "FAIL: numeric override"; exit 1; } rm -f "$T/bin/nvidia-smi" +echo "== h-config.sh OVERRIDE" +OV='{"difficulty_v2_activation_daa":33000,"proving_v0_activation_daa":84100,"fees_v1_activation_daa":210000,"finality_v3_activation_daa":135200}' +( CUSTOM_USER_CONFIG="DEV_FEE=0 VOTE=1"$'\n'"OVERRIDE='$OV'"$'\n'"IDENTITIES=4" CUSTOM_CONFIG_FILENAME="$T/ov.conf" . "$M/h-config.sh" >/dev/null ) || { echo "h-config.sh failed on OVERRIDE"; exit 1; } +( . "$T/ov.conf"; [[ "$OVERRIDE" == "$OV" && "$DEV_FEE" == 0 && "$IDENTITIES" == 4 ]] ) && echo " OVERRIDE (single-quoted, own line) sourced back intact beside the other keys ok" || { echo "FAIL: OVERRIDE round trip"; cat "$T/ov.conf"; exit 1; } +( CUSTOM_USER_CONFIG="OVERRIDE=notjson" CUSTOM_CONFIG_FILENAME="$T/ov-bad.conf" . "$M/h-config.sh" >/dev/null 2>&1 ) && { echo "FAIL: OVERRIDE=notjson accepted"; exit 1; } || echo " OVERRIDE that is not {...} refused ok" +( CUSTOM_USER_CONFIG="" CUSTOM_CONFIG_FILENAME="$T/ov-none.conf" . "$M/h-config.sh" | grep -q "override NONE" ) && grep -q "^OVERRIDE=''$" "$T/ov-none.conf" && echo " no OVERRIDE: empty in the conf and named in the summary ok" || { echo "FAIL: empty OVERRIDE"; exit 1; } # h-run.sh reads IDENTITIES_GPU first: write a conf with gpu1=3 and check the second miner's argv -( CUSTOM_USER_CONFIG=$'DEV_FEE=0\nIDENTITIES=auto' IGNEUM_DRM_ROOT="$T/no-drm" CUSTOM_CONFIG_FILENAME="$M/igneum.conf" . "$M/h-config.sh" >/dev/null ) && printf 'IDENTITIES_GPU1=3\n' >> "$M/igneum.conf" +( CUSTOM_USER_CONFIG=$'DEV_FEE=0\nIDENTITIES=auto\n'"OVERRIDE=$OV" IGNEUM_DRM_ROOT="$T/no-drm" CUSTOM_CONFIG_FILENAME="$M/igneum.conf" . "$M/h-config.sh" >/dev/null ) && printf 'IDENTITIES_GPU1=3\n' >> "$M/igneum.conf" echo "== h-run.sh (fake GPUs: 2 NVIDIA, fake node, fake miner)" ( cd "$M" && CUSTOM_CONFIG_FILENAME="$M/igneum.conf" CUSTOM_LOG_BASENAME="$T/log/igneum" ./h-run.sh > "$T/log/run.out" 2>&1 ) & disown @@ -73,8 +87,19 @@ _lines=$(grep -c "dev fee off (--dev-fee 0)" "$T/log/igneum.log" || true); echo grep -q -- "--dev-fee 0" "$M/argv.log" && echo " DEV_FEE=0 reached the miner as --dev-fee 0 ok" || { echo "FAIL: --dev-fee 0 missing"; exit 1; } grep -q -- "rig7-gpu0 .*--identities 8" "$M/argv.log" && echo " gpu0 took the IDENTITIES=8 fallback ok" || { echo "FAIL: --identities 8 missing on gpu0"; cat "$M/argv.log"; exit 1; } grep -q -- "rig7-gpu1 .*--identities 3" "$M/argv.log" && echo " gpu1 took IDENTITIES_GPU1=3 over the fallback ok" || { echo "FAIL: --identities 3 missing on gpu1"; cat "$M/argv.log"; exit 1; } +[[ "$(cat "$M/data/override-params.json")" == "$OV" ]] && echo " data/override-params.json written from OVERRIDE ok" || { echo "FAIL: override file"; exit 1; } +grep -q -- "--override-params-file=$M/data/override-params.json" "$T/log/igneum.node.log" && echo " the node got --override-params-file ok" || { echo "FAIL: node flag"; cat "$T/log/igneum.node.log"; exit 1; } +grep -q "node: Consensus params digest: 0139ab9d" "$T/log/igneum.log" && grep -q "node: Finality rule v3 from the override file" "$T/log/igneum.log" && echo " the node's digest and switch lines reached the main log ok" || { echo "FAIL: digest relay"; cat "$T/log/igneum.log"; exit 1; } grep -q "igneum-worker-cuda" "$M/argv.log" && echo " NVIDIA cards got the cuda worker ok" || { echo "FAIL: cuda worker missing"; exit 1; } [[ -f "$M/exited42" && $(grep -c "^mine" "$M/argv.log") -ge 3 ]] && echo " exit 42 restarted the miner and re-exported the pack ok" || { echo "FAIL: no restart after exit 42"; exit 1; } echo "== h-stats.sh (sourced)" ( . "$M/h-stats.sh"; echo " khs=$khs"; echo " stats=$stats"; python3 -c "import json,sys; s=json.loads(sys.argv[1]); assert s['hs_units']=='khs' and len(s['hs'])==2 and s['ar'][0]>0 and s['algo']=='igneum' and len(s['temp'])==2, s; print(' stats JSON ok: hs', s['hs'], 'temp', s['temp'], 'ar', s['ar'], 'bus', s['bus_numbers'])" "$stats" ) +echo "== h-run.sh without OVERRIDE (the warning)" +( CUSTOM_USER_CONFIG=$'DEV_FEE=0' IGNEUM_DRM_ROOT="$T/no-drm" CUSTOM_CONFIG_FILENAME="$T/noov.conf" . "$M/h-config.sh" >/dev/null ) +mkdir -p "$T/log2" +( cd "$M" && CUSTOM_CONFIG_FILENAME="$T/noov.conf" CUSTOM_LOG_BASENAME="$T/log2/igneum" ./h-run.sh > "$T/log2/run.out" 2>&1 ) & +disown +sleep 4 +grep -q "WARNING: no OVERRIDE in the Flight Sheet" "$T/log2/igneum.log" && ! grep -q -- "--override-params-file" "$T/log2/igneum.node.log" && echo " no OVERRIDE: warning in the main log, no flag on the node ok" || { echo "FAIL: missing warning"; cat "$T/log2/igneum.log"; exit 1; } +_p2="$(cat "$T/log2/igneum.pid" 2>/dev/null)"; [[ -n "$_p2" ]] && kill -TERM "$_p2" 2>/dev/null; sleep 1 echo "== self-test passed (scripts and stats shape; Hive itself is untested)"