91 lines
5.3 KiB
Bash
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')"
|
|
}
|