#!/usr/bin/env bash # Reads a node's "Consensus params digest" (the shipper and main, 7 October 2026 21:4x BST, for the manifest guard): either from a # PINNED BINARY started bare (an empty datadir, no override file, no peers, the way the canary reads it: the digest line is printed # at start, the process is ended) or from a RUNNING node's log or journal (the network's CURRENT digest; the hub dn3-g1 for Devnet 3). # # tools/digest-read.sh binary [--net-flags "--devnet --devnet-suffix=3"] [--secs 15] prints the 64-hex digest # tools/digest-read.sh log '> [--host user@box] the newest digest line in it # tools/digest-read.sh --self-test the line parser on fixtures # Exit 1 with a line on stderr when no digest is read. The ports are loopback and private to the run (26899/26898/26897), the # datadir a private temp folder removed after. Never run on a seed: it is a bare start, not a node. set -uo pipefail DIGEST_RE='Consensus params digest: ([0-9a-f]{64})' parse() { grep -oE "$DIGEST_RE" | tail -1 | awk '{print $4}'; } case "${1:-}" in binary) bin="${2:?igneumd}"; shift 2; flags="--devnet --devnet-suffix=3"; secs=15 while [ $# -gt 0 ]; do case "$1" in --net-flags) flags="$2"; shift 2 ;; --secs) secs="$2"; shift 2 ;; *) echo "digest-read: unknown $1" >&2; exit 2 ;; esac; done [ -x "$bin" ] || { echo "digest-read: $bin is not executable" >&2; exit 1; } d=$(mktemp -d); trap 'rm -rf "$d"' EXIT # shellcheck disable=SC2086 "$bin" $flags --appdir="$d/a" --nologfiles --listen=127.0.0.1:26899 --rpclisten=127.0.0.1:26898 --evm-rpclisten=127.0.0.1:26897 --outpeers=0 --nodnsseed --disable-upnp > "$d/a.log" 2>&1 & p=$!; for i in $(seq 1 "$((secs * 2))"); do grep -qE "$DIGEST_RE" "$d/a.log" 2>/dev/null && break; sleep 0.5; done kill -TERM "$p" 2>/dev/null; wait "$p" 2>/dev/null dg=$(parse < "$d/a.log"); [ -n "$dg" ] || { echo "digest-read: no digest line from $bin in $secs s (tail: $(tail -c 200 "$d/a.log" | tr '\n' ' '))" >&2; exit 1; } echo "$dg" ;; log) src="${2:?file or journal:}"; shift 2; host="" while [ $# -gt 0 ]; do case "$1" in --host) host="$2"; shift 2 ;; *) echo "digest-read: unknown $1" >&2; exit 2 ;; esac; done case "$src" in journal:*) cmd="journalctl -u ${src#journal:} --no-pager -o cat | grep -oE '$DIGEST_RE' | tail -1" ;; *) cmd="grep -aoE '$DIGEST_RE' '$src' | tail -1" ;; esac if [ -n "$host" ]; then line=$(ssh -i "$HOME/.ssh/igneum_ed25519" -o BatchMode=yes -o ConnectTimeout=8 "$host" "$cmd" 2>/dev/null); else line=$(bash -c "$cmd" 2>/dev/null); fi dg=$(printf '%s\n' "$line" | parse); [ -n "$dg" ] || { echo "digest-read: no digest line in $src${host:+ on $host}" >&2; exit 1; } echo "$dg" ;; --self-test) a=$(printf '2026-10-07 x [INFO] Consensus params digest: %s\nmore\n' "$(printf 'ab%.0s' $(seq 1 32))" | parse) [ "$a" = "$(printf 'ab%.0s' $(seq 1 32))" ] || { echo "digest-read self-test: FAIL: parse"; exit 1; } b=$(printf 'no digest here\n' | parse); [ -z "$b" ] || { echo "digest-read self-test: FAIL: parsed a digest from nothing"; exit 1; } echo "digest-read self-test: the digest line parses, an absent line reads empty" ;; *) sed -n '2,10p' "$0" | sed 's/^# \{0,1\}//'; exit 2 ;; esac