aere-node/validator/kit-common.sh

91 lines
5.3 KiB
Bash

# 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 <build dir>: 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 <falcon-key> <slh-key> <node-key> <file>...: 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')"
}