Add bootstrap-peers.sh and fall back to stakesquid.eu/bootstrap when local peerset capture is empty; optional --seed-bootstrap on connect-peers. Co-authored-by: Cursor <cursoragent@cursor.com>
103 lines
3.2 KiB
Bash
Executable File
103 lines
3.2 KiB
Bash
Executable File
#!/bin/bash
|
|
# bootstrap-peers.sh — fetch curated third-party bootstrap enodes for a network.
|
|
#
|
|
# Secondary seed source for peer-reinject / restore flows. Primary source remains
|
|
# the node's own peer-capture state. Same public JSON customers curl:
|
|
# https://stakesquid.eu/bootstrap/<network>.json
|
|
#
|
|
# Usage:
|
|
# bootstrap-peers.sh <network> # print enodes (one per line)
|
|
# bootstrap-peers.sh <network> --json # print full JSON
|
|
# bootstrap-peers.sh --from-compose <path> # derive network from compose path
|
|
# bootstrap-peers.sh ... --dry-run # show URL + count, no failure on empty
|
|
#
|
|
# Env:
|
|
# BOOTSTRAP_BASE_URL default https://stakesquid.eu/bootstrap
|
|
# BOOTSTRAP_MAX max enodes to emit (default 25)
|
|
set -euo pipefail
|
|
|
|
BASE_URL="${BOOTSTRAP_BASE_URL:-https://stakesquid.eu/bootstrap}"
|
|
MAX="${BOOTSTRAP_MAX:-25}"
|
|
DRY_RUN=false
|
|
JSON_OUT=false
|
|
NETWORK=""
|
|
COMPOSE=""
|
|
|
|
usage() {
|
|
echo "Usage: $0 <network> [--json] [--dry-run]" >&2
|
|
echo " $0 --from-compose <compose-path> [--json] [--dry-run]" >&2
|
|
exit 1
|
|
}
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--from-compose)
|
|
COMPOSE="${2:-}"; shift 2 || usage
|
|
;;
|
|
--json) JSON_OUT=true; shift ;;
|
|
--dry-run) DRY_RUN=true; shift ;;
|
|
-h|--help) usage ;;
|
|
-*)
|
|
echo "Unknown option: $1" >&2
|
|
usage
|
|
;;
|
|
*)
|
|
NETWORK="$1"; shift
|
|
;;
|
|
esac
|
|
done
|
|
|
|
network_from_compose() {
|
|
local path="$1"
|
|
local base
|
|
base="$(basename "$path" | sed 's/--.*//')"
|
|
# strip client/variant suffixes (same idea as fleet-peers network_from_path)
|
|
for sep in -op-reth -op-geth -op-erigon -op-nethermind \
|
|
-reth -geth -erigon -erigon3 -besu -nethermind \
|
|
-bsc -nitro -l2geth -bor -external-node -gaiad \
|
|
-juno -rskj -avalanche -maru; do
|
|
case "$base" in
|
|
*"$sep"*) base="${base%%$sep*}"; break ;;
|
|
esac
|
|
done
|
|
# common leftover: -pruned / -archive / -pebble / -path / -trace / -bonsai
|
|
base="$(echo "$base" | sed -E 's/-(pruned|archive|full|pebble|path|trace|bonsai|snap|hash).*//')"
|
|
echo "$base"
|
|
}
|
|
|
|
if [[ -n "$COMPOSE" ]]; then
|
|
NETWORK="$(network_from_compose "$COMPOSE")"
|
|
fi
|
|
if [[ -z "$NETWORK" ]]; then
|
|
usage
|
|
fi
|
|
|
|
URL="${BASE_URL%/}/${NETWORK}.json"
|
|
TMP="$(mktemp)"
|
|
trap 'rm -f "$TMP"' EXIT
|
|
|
|
if ! curl -fsS --max-time 15 "$URL" -o "$TMP" 2>/dev/null; then
|
|
if [[ "$DRY_RUN" == true ]]; then
|
|
echo "bootstrap-peers: dry-run fetch failed for $URL" >&2
|
|
exit 0
|
|
fi
|
|
echo "bootstrap-peers: fetch failed: $URL" >&2
|
|
exit 1
|
|
fi
|
|
|
|
COUNT="$(jq -r '.peers | length // 0' "$TMP" 2>/dev/null || echo 0)"
|
|
UPDATED="$(jq -r '.updated // empty' "$TMP" 2>/dev/null || true)"
|
|
|
|
if [[ "$DRY_RUN" == true ]]; then
|
|
echo "bootstrap-peers: network=$NETWORK url=$URL peers=$COUNT updated=$UPDATED"
|
|
jq -r '.peers[:3][]? | " \(.stability.tier // "?") score=\(.stability.score // "?") n_vantage=\(.stability.n_vantage_nodes // "?") \(.enode[0:60])…"' "$TMP" 2>/dev/null || true
|
|
exit 0
|
|
fi
|
|
|
|
if [[ "$JSON_OUT" == true ]]; then
|
|
cat "$TMP"
|
|
exit 0
|
|
fi
|
|
|
|
jq -r --argjson m "$MAX" '.peers[:$m][]?.enode // empty' "$TMP" 2>/dev/null | grep -E '^enode://' || true
|