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>
172 lines
5.8 KiB
Bash
Executable File
172 lines
5.8 KiB
Bash
Executable File
#!/bin/bash
|
|
# peer-reinject.sh — generic .after-start hook: re-seed peers from peer-capture state.
|
|
# Uses admin_addStaticPeer for geth, admin_addPeer otherwise (connect-peers.sh).
|
|
# In-network vantage (rpc_chains). Idempotent; best-effort (never fails the restart).
|
|
#
|
|
# Seed order (one path for us and customers):
|
|
# 1. Own persisted peerset (peer-state/<compose>.json from peer-capture)
|
|
# 2. Public bootstrap list https://stakesquid.eu/bootstrap/<network>.json
|
|
# (third-party enodes only — same JSON customers curl)
|
|
#
|
|
# Usage: peer-reinject.sh <compose-path>
|
|
# Env: PEER_BOOTSTRAP=0 to disable bootstrap fallback
|
|
# BOOTSTRAP_BASE_URL to override (default https://stakesquid.eu/bootstrap)
|
|
set -euo pipefail
|
|
|
|
COMPOSE_PATH="${1:-}"
|
|
if [ -z "$COMPOSE_PATH" ]; then
|
|
echo "Usage: $0 <compose-path>" >&2
|
|
exit 1
|
|
fi
|
|
|
|
BASEPATH="$(cd "$(dirname "$0")" && pwd)"
|
|
STATE_DIR="$BASEPATH/peer-state"
|
|
SAFE="$(echo "$COMPOSE_PATH" | sed 's|[^a-zA-Z0-9._-]|_|g')"
|
|
STATE="$STATE_DIR/${SAFE}.json"
|
|
CURL_IMAGE="${PEER_CURL_IMAGE:-curlimages/curl:8.5.0}"
|
|
LOG="${PEER_HOOK_LOG:-/var/log/peer-hooks.log}"
|
|
USE_BOOTSTRAP="${PEER_BOOTSTRAP:-1}"
|
|
|
|
log() { echo "$(date '+%F %T') peer-reinject[$COMPOSE_PATH]: $*" | tee -a "$LOG" >&2; }
|
|
|
|
case "$COMPOSE_PATH" in
|
|
*nitro*|*zksync*|*zk-sync*)
|
|
log "skip (nitro/zksync)"
|
|
exit 0
|
|
;;
|
|
esac
|
|
|
|
CLIENT_TYPE="unknown"
|
|
ENODES=()
|
|
SOURCE="none"
|
|
|
|
if [ -f "$STATE" ]; then
|
|
CLIENT_TYPE="$(jq -r '.client_type // "unknown"' "$STATE" 2>/dev/null || echo unknown)"
|
|
mapfile -t ENODES < <(jq -r '.peers[].id // empty' "$STATE" 2>/dev/null | grep -E '^enode://' | head -n 50 || true)
|
|
if [ "${#ENODES[@]}" -gt 0 ]; then
|
|
SOURCE="peerset"
|
|
fi
|
|
fi
|
|
|
|
# Secondary seed: public bootstrap API (same files customers use).
|
|
if [ "${#ENODES[@]}" -eq 0 ] && [ "$USE_BOOTSTRAP" != "0" ]; then
|
|
if [ -x "$BASEPATH/bootstrap-peers.sh" ]; then
|
|
mapfile -t ENODES < <("$BASEPATH/bootstrap-peers.sh" --from-compose "$COMPOSE_PATH" 2>/dev/null | grep -E '^enode://' | head -n 25 || true)
|
|
if [ "${#ENODES[@]}" -gt 0 ]; then
|
|
SOURCE="bootstrap"
|
|
log "no local peerset — using bootstrap fallback (${#ENODES[@]} enodes)"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if [ "${#ENODES[@]}" -eq 0 ]; then
|
|
if [ ! -f "$STATE" ]; then
|
|
log "no capture state at $STATE and bootstrap empty — nothing to reinject"
|
|
else
|
|
log "no enodes in $STATE and bootstrap empty"
|
|
fi
|
|
exit 0
|
|
fi
|
|
|
|
BASE="$(basename "$COMPOSE_PATH" | sed 's/--.*//')"
|
|
EL=""
|
|
# wait briefly for container after recreate
|
|
for _ in 1 2 3 4 5 6 7 8 9 10; do
|
|
while read -r n; do
|
|
[ -z "$n" ] && continue
|
|
case "$n" in rpc-*) ;; *) continue ;; esac
|
|
case "$n" in *-node-*|*-relay-*) continue ;; esac
|
|
core="$(echo "$n" | sed -E 's/^rpc-(.+)-[0-9]+$/\1/')"
|
|
if [ "$BASE" = "$core" ] || [[ "$BASE" == "$core"-* ]]; then
|
|
EL="$n"
|
|
break
|
|
fi
|
|
done < <(docker ps --format '{{.Names}}')
|
|
[ -n "$EL" ] && break
|
|
sleep 3
|
|
done
|
|
|
|
if [ -z "$EL" ]; then
|
|
log "no execution container yet — giving up"
|
|
exit 0
|
|
fi
|
|
|
|
IP="$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$EL" 2>/dev/null || true)"
|
|
# Prefer rpc_chains IP when present
|
|
RPC_IP="$(docker inspect -f '{{(index .NetworkSettings.Networks "rpc_chains").IPAddress}}' "$EL" 2>/dev/null || true)"
|
|
if [ -n "$RPC_IP" ] && [ "$RPC_IP" != "<no value>" ]; then
|
|
IP="$RPC_IP"
|
|
fi
|
|
if [ -z "$IP" ]; then
|
|
log "no IP for $EL"
|
|
exit 0
|
|
fi
|
|
|
|
# wait for RPC
|
|
for _ in 1 2 3 4 5 6 7 8 9 10 11 12; do
|
|
docker rm -f peer-hook-probe >/dev/null 2>&1 || true
|
|
docker image inspect "$CURL_IMAGE" >/dev/null 2>&1 || docker pull -q "$CURL_IMAGE" >/dev/null
|
|
docker run -d --name peer-hook-probe --network rpc_chains "$CURL_IMAGE" sleep 180 >/dev/null
|
|
if docker exec peer-hook-probe curl -s --max-time 5 -X POST \
|
|
-H 'Content-Type: application/json' \
|
|
--data '{"jsonrpc":"2.0","method":"net_version","params":[],"id":1}' \
|
|
"http://${IP}:8545" 2>/dev/null | grep -q result; then
|
|
break
|
|
fi
|
|
docker rm -f peer-hook-probe >/dev/null 2>&1 || true
|
|
sleep 5
|
|
done
|
|
|
|
add_peer() {
|
|
local enode="$1"
|
|
local method
|
|
if [ "$CLIENT_TYPE" = "geth" ]; then
|
|
method="admin_addStaticPeer"
|
|
else
|
|
method="admin_addPeer"
|
|
fi
|
|
local payload
|
|
payload="$(jq -nc --arg m "$method" --arg e "$enode" \
|
|
'{jsonrpc:"2.0",method:$m,params:[$e],id:1}')"
|
|
local resp
|
|
resp="$(docker exec peer-hook-probe curl -s --max-time 8 -X POST \
|
|
-H 'Content-Type: application/json' --data "$payload" \
|
|
"http://${IP}:8545" 2>/dev/null || true)"
|
|
if echo "$resp" | grep -qi 'method not found'; then
|
|
if [ "$method" = "admin_addStaticPeer" ]; then
|
|
method="admin_addPeer"
|
|
else
|
|
method="admin_addStaticPeer"
|
|
fi
|
|
payload="$(jq -nc --arg m "$method" --arg e "$enode" \
|
|
'{jsonrpc:"2.0",method:$m,params:[$e],id:1}')"
|
|
resp="$(docker exec peer-hook-probe curl -s --max-time 8 -X POST \
|
|
-H 'Content-Type: application/json' --data "$payload" \
|
|
"http://${IP}:8545" 2>/dev/null || true)"
|
|
fi
|
|
if echo "$resp" | grep -q '"result":true'; then
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
ADDED=0
|
|
FAIL=0
|
|
for enode in "${ENODES[@]}"; do
|
|
if add_peer "$enode"; then
|
|
ADDED=$((ADDED + 1))
|
|
else
|
|
FAIL=$((FAIL + 1))
|
|
fi
|
|
done
|
|
|
|
# post count
|
|
COUNT="$(docker exec peer-hook-probe curl -s --max-time 8 -X POST \
|
|
-H 'Content-Type: application/json' \
|
|
--data '{"jsonrpc":"2.0","method":"admin_peers","params":[],"id":1}' \
|
|
"http://${IP}:8545" 2>/dev/null | jq -r '.result | length' 2>/dev/null || echo '?')"
|
|
|
|
docker rm -f peer-hook-probe >/dev/null 2>&1 || true
|
|
log "reinjected source=$SOURCE added=$ADDED fail=$FAIL now_peers=$COUNT (from ${#ENODES[@]} enodes)"
|
|
exit 0
|