# kit-common.sh - shared by validator-keys.sh and validator-candidacy.sh (sourced, not run). # # Everything here runs on the candidate's machine. It needs bash, a JDK 21 (the same one run-node.sh # requires), coreutils and curl. It holds no key and sends nothing anywhere. KIT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" BUNDLE_DIR="$(cd "$KIT_DIR/.." && pwd)" say() { printf '\n==> %s\n' "$*"; } ok() { printf ' OK %s\n' "$*"; } note() { printf ' %s\n' "$*"; } die() { printf '\nREFUSED: %s\n' "$*" >&2; exit 1; } sha256_of() { if command -v sha256sum >/dev/null 2>&1; then sha256sum "$1" | cut -d' ' -f1 elif command -v shasum >/dev/null 2>&1; then shasum -a 256 "$1" | cut -d' ' -f1 else die "no sha256sum or shasum on PATH"; fi } # mode of a file or directory, as three octal digits (GNU stat, then BSD stat) mode_of() { stat -c '%a' "$1" 2>/dev/null || stat -f '%Lp' "$1" 2>/dev/null; } # A private file must be readable by its owner only (600 or 400); a key directory 700 or 500. need_private_file() { local m; m="$(mode_of "$1")" case "$m" in 600|400) ok "$1 is owner-only ($m)";; *) die "$1 has mode ${m:-unknown}; a private key file must be owner-only. Run: chmod 600 '$1'";; esac } need_private_dir() { local m; m="$(mode_of "$1")" case "$m" in 700|500) ok "$1 is owner-only ($m)";; *) die "$1 has mode ${m:-unknown}; the key directory must be owner-only. Run: chmod 700 '$1'";; esac } # kit_find_lib : sets LIB to the lib/ of a build made from the PUBLISHED patch series, or refuses. # The registry tool and the signing helper must be the classes the fleet runs (class for class, published # 2026-10-14 for patch 0010); a build from another patch set is refused exactly as run-node.sh refuses to # reuse it. kit_find_lib() { local b="$1" command -v java >/dev/null 2>&1 || die "java not found; JDK 21 is required (the same one run-node.sh uses)" local v; v="$(java -version 2>&1 | head -1 | sed -E 's/.*version "([0-9]+).*/\1/')" [ "$v" = "21" ] || die "JDK 21 is required, found: $(java -version 2>&1 | head -1)" LIB="$b/build/install/besu/lib" [ -d "$LIB" ] || die "no build at $LIB. Build the node first: ./run-node.sh --no-start (it builds from the published patch series)" ls "$LIB"/besu-consensus-common-*.jar >/dev/null 2>&1 || die "$LIB has no besu-consensus-common jar; this is not a Besu build" local want got p # (every pipeline below that may legitimately find nothing ends in `|| true`: under pipefail an empty grep would otherwise end # the script silently, and the emptiness is then refused with a message) want="$(sed -n '/^PATCHES=(/,/^)/p' "$BUNDLE_DIR/run-node.sh" | grep -oE '"[0-9]{4}-[A-Za-z0-9._-]+\.patch"' | tr -d '"' \ | while read -r p; do printf '%s %s\n' "$(sha256_of "$BUNDLE_DIR/patches/$p" < /dev/null)" "$p"; done || true)" [ -n "$want" ] || die "cannot read the patch list from run-node.sh" got="$(cat "$b/.aere-patchset" 2>/dev/null || true)" [ "$got" = "$want" ] || die "the build at $b was not made from this bundle's patch series (or the marker .aere-patchset is missing). Rebuild with: ./run-node.sh --no-start --rebuild" ok "build from the published patch series: $LIB" local u u="$(java -Xmx256m -cp "$LIB/*" org.hyperledger.besu.consensus.common.bft.tools.PqRegistryV3Tool 2>&1 < /dev/null | head -2 || true)" case "$u" in *usage*PqRegistryV3Tool*) ok "registry tool PqRegistryV3Tool present (patch 0008)";; *) die "the build has no PqRegistryV3Tool (patch 0008); it cannot make a registry row";; esac } kit_java() { java -Xmx512m -cp "$LIB/*" "$KIT_DIR/AereCandidacy.java" "$@"; } # The private values of a key set, one per line, for a FIXED-STRING search only (never printed, never # written to disk: they go through a pipe into grep -f). Hex digits only, without 0x. # NO EMPTY LINE ever comes out: an empty fixed-string pattern matches every line, and the check would refuse everything (or, used # the other way round, find everything). kit_private_values() { # falcon-key slh-key node-key { [ -f "$1" ] && grep -E '^(f|g|F)=' "$1" | cut -d= -f2- | tr -d '\r ' [ -f "$2" ] && grep -E '^sk=' "$2" | cut -d= -f2- | tr -d '\r ' [ -f "$3" ] && { tr -d '\r\n ' < "$3" | sed 's/^0x//'; echo; } } | grep -E '^[0-9a-fA-F]{16,}$' || true } # kit_no_private_in ...: refuses if any private value appears in any of the files. kit_no_private_in() { local fk="$1" sk="$2" nk="$3"; shift 3 local n n="$(kit_private_values "$fk" "$sk" "$nk" | grep -c . || true)" [ "$n" -ge 4 ] || die "the private-value check could not read the key files (got $n values, expected at least 4); nothing is trusted" # control of the method: the search must FIND the values in the key file itself, or a zero below means nothing local self; self="$(grep -c -i -F -f <(kit_private_values "$fk" "$sk" "$nk") "$fk" || true)" [ "${self:-0}" -ge 3 ] || die "the private-value check does not find the values even in the key file (method blind); nothing is trusted" local f hits for f in "$@"; do [ -f "$f" ] || continue hits="$(grep -c -i -F -f <(kit_private_values "$fk" "$sk" "$nk") "$f" || true)" [ "${hits:-0}" = 0 ] || die "a private key value appears in $f. Do not send or publish it; delete it and report this." done ok "no private key value in: $(printf '%s ' "$@" | sed 's#[^ ]*/##g')" }