#!/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/.json # # Usage: # bootstrap-peers.sh # print enodes (one per line) # bootstrap-peers.sh --json # print full JSON # bootstrap-peers.sh --from-compose # 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 [--json] [--dry-run]" >&2 echo " $0 --from-compose [--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