diff --git a/add-peers.sh b/add-peers.sh new file mode 100755 index 00000000..67fbe325 --- /dev/null +++ b/add-peers.sh @@ -0,0 +1,148 @@ +#!/usr/bin/env bash +set -euo pipefail + +BOOTNODE_URL="${BOOTNODE_URL:-https://chains.base.org}" +NETWORK="" +EXECUTION_RPC="${EXECUTION_RPC:-http://localhost:8545}" +CONSENSUS_RPC="${CONSENSUS_RPC:-http://localhost:7545}" +EXECUTION_ONLY=false +CONSENSUS_ONLY=false +LOOP_INTERVAL=0 + +show_usage() { + cat < [options] + +Fetches public node records from chains.base.org and adds them as peers. + +Required: + --network Network name (base-mainnet, base-sepolia, base-zeronet) + +Options: + --execution-rpc Execution layer RPC endpoint (default: http://localhost:8545) + --consensus-rpc Consensus layer RPC endpoint (default: http://localhost:7545) + --bootnode-url Bootnode server URL (default: https://chains.base.org) + --execution-only Only add execution layer peers + --consensus-only Only add consensus layer peers + --loop Poll interval in seconds (0 = run once, default: 0) + --help Show this help message + +Prerequisites: + - curl and jq must be installed + - Execution client must have admin namespace enabled (--http.api includes admin) + - Consensus client must have admin RPC enabled (--rpc.enable-admin or BASE_NODE_RPC_ENABLE_ADMIN=true) + +Examples: + $(basename "$0") --network base-sepolia + $(basename "$0") --network base-sepolia --execution-rpc http://localhost:8545 --loop 300 + $(basename "$0") --network base-mainnet --consensus-only +EOF + exit 0 +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --network) NETWORK="$2"; shift 2 ;; + --execution-rpc) EXECUTION_RPC="$2"; shift 2 ;; + --consensus-rpc) CONSENSUS_RPC="$2"; shift 2 ;; + --bootnode-url) BOOTNODE_URL="$2"; shift 2 ;; + --execution-only) EXECUTION_ONLY=true; shift ;; + --consensus-only) CONSENSUS_ONLY=true; shift ;; + --loop) LOOP_INTERVAL="$2"; shift 2 ;; + --help) show_usage ;; + *) echo "Unknown option: $1"; show_usage ;; + esac +done + +if [[ -z "$NETWORK" ]]; then + echo "Error: --network is required" + show_usage +fi + +for cmd in curl jq; do + if ! command -v "$cmd" &>/dev/null; then + echo "Error: $cmd is required but not installed" + exit 1 + fi +done + +add_execution_peers() { + local nodes + nodes=$(echo "$1" | jq -r '.execution[]? // empty' 2>/dev/null) + if [[ -z "$nodes" ]]; then + echo "[$(date -Iseconds)] No execution peers found for $NETWORK" + return + fi + + local count=0 + while IFS= read -r enode; do + result=$(curl -s -X POST "$EXECUTION_RPC" \ + -H "Content-Type: application/json" \ + -d "{\"jsonrpc\":\"2.0\",\"method\":\"admin_addPeer\",\"params\":[\"$enode\"],\"id\":1}" 2>/dev/null) + success=$(echo "$result" | jq -r '.result // false' 2>/dev/null) + if [[ "$success" == "true" ]]; then + count=$((count + 1)) + else + echo "[$(date -Iseconds)] Failed to add execution peer: $enode" + fi + done <<< "$nodes" + + echo "[$(date -Iseconds)] Added $count execution peers for $NETWORK" +} + +add_consensus_peers() { + local nodes + nodes=$(echo "$1" | jq -r '.consensus[]? // empty' 2>/dev/null) + if [[ -z "$nodes" ]]; then + echo "[$(date -Iseconds)] No consensus peers found for $NETWORK" + return + fi + + local count=0 + while IFS= read -r addr; do + result=$(curl -s -X POST "$CONSENSUS_RPC" \ + -H "Content-Type: application/json" \ + -d "{\"jsonrpc\":\"2.0\",\"method\":\"opp2p_connectPeer\",\"params\":[\"$addr\"],\"id\":1}" 2>/dev/null) + error=$(echo "$result" | jq -r '.error // empty' 2>/dev/null) + if [[ -z "$error" ]]; then + count=$((count + 1)) + else + echo "[$(date -Iseconds)] Failed to add consensus peer: $addr ($error)" + fi + done <<< "$nodes" + + echo "[$(date -Iseconds)] Added $count consensus peers for $NETWORK" +} + +run_once() { + echo "[$(date -Iseconds)] Fetching peers from $BOOTNODE_URL for $NETWORK..." + + response=$(curl -sf "$BOOTNODE_URL/$NETWORK/peers" 2>/dev/null) + if [[ -z "$response" ]]; then + echo "[$(date -Iseconds)] Error: failed to fetch from $BOOTNODE_URL/$NETWORK/peers" + return 1 + fi + + if ! echo "$response" | jq -e '.execution' &>/dev/null; then + echo "[$(date -Iseconds)] Error: invalid response for network $NETWORK" + return 1 + fi + + if [[ "$CONSENSUS_ONLY" != "true" ]]; then + add_execution_peers "$response" + fi + + if [[ "$EXECUTION_ONLY" != "true" ]]; then + add_consensus_peers "$response" + fi +} + +if [[ "$LOOP_INTERVAL" -gt 0 ]]; then + echo "[$(date -Iseconds)] Running in loop mode (interval: ${LOOP_INTERVAL}s)" + while true; do + run_once || true + sleep "$LOOP_INTERVAL" + done +else + run_once +fi diff --git a/base-consensus-entrypoint b/base-consensus-entrypoint index cd2801fc..205452da 100755 --- a/base-consensus-entrypoint +++ b/base-consensus-entrypoint @@ -44,6 +44,8 @@ export BASE_NODE_P2P_ADVERTISE_IP=$PUBLIC_IP echo "$BASE_NODE_L2_ENGINE_AUTH_RAW" > "$BASE_NODE_L2_ENGINE_AUTH" +./add-peers.sh --consensus-only --network "$BASE_NODE_NETWORK" --loop 300 & + if [[ -n "${BASE_NODE_SOURCE_L2_RPC:-}" ]]; then echo "Running base-consensus in follow mode because BASE_NODE_SOURCE_L2_RPC is set" exec ./base-consensus follow diff --git a/geth/Dockerfile b/geth/Dockerfile index c3288140..876b5752 100644 --- a/geth/Dockerfile +++ b/geth/Dockerfile @@ -40,5 +40,6 @@ COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf COPY geth/geth-entrypoint ./execution-entrypoint COPY op-node-entrypoint . COPY consensus-entrypoint . +COPY add-peers.sh . CMD ["/usr/bin/supervisord"] diff --git a/geth/geth-entrypoint b/geth/geth-entrypoint index b598cb3f..b91790e0 100755 --- a/geth/geth-entrypoint +++ b/geth/geth-entrypoint @@ -50,6 +50,8 @@ if [ "${HOST_IP:+x}" = x ]; then ADDITIONAL_ARGS="$ADDITIONAL_ARGS --nat=extip:$HOST_IP" fi +./add-peers.sh --execution-only --network "$OP_NODE_NETWORK" --execution-rpc "http://localhost:${RPC_PORT}" --loop 300 & + exec ./geth \ --datadir="$GETH_DATA_DIR" \ --verbosity="$VERBOSITY" \ diff --git a/nethermind/Dockerfile b/nethermind/Dockerfile index 0ecb7360..124326f6 100644 --- a/nethermind/Dockerfile +++ b/nethermind/Dockerfile @@ -47,5 +47,6 @@ COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf COPY nethermind/nethermind-entrypoint ./execution-entrypoint COPY op-node-entrypoint . COPY consensus-entrypoint . +COPY add-peers.sh . CMD ["/usr/bin/supervisord"] diff --git a/nethermind/nethermind-entrypoint b/nethermind/nethermind-entrypoint index 66367b22..af31fbe1 100755 --- a/nethermind/nethermind-entrypoint +++ b/nethermind/nethermind-entrypoint @@ -43,6 +43,8 @@ if [[ -n "${OP_NETHERMIND_ETHSTATS_ENDPOINT:-}" ]]; then fi # Execute Nethermind +./add-peers.sh --execution-only --network "$OP_NODE_NETWORK" --execution-rpc "http://localhost:${RPC_PORT}" --loop 300 & + exec ./nethermind \ --config="$OP_NODE_NETWORK" \ --datadir="$NETHERMIND_DATA_DIR" \ diff --git a/op-node-entrypoint b/op-node-entrypoint index 89301588..859aeee5 100755 --- a/op-node-entrypoint +++ b/op-node-entrypoint @@ -46,4 +46,8 @@ export OP_NODE_P2P_ADVERTISE_IP=$PUBLIC_IP echo "$BASE_NODE_L2_ENGINE_AUTH_RAW" > "$BASE_NODE_L2_ENGINE_AUTH" export OP_NODE_L2_ENGINE_AUTH=$BASE_NODE_L2_ENGINE_AUTH +if [[ -n "${OP_NODE_NETWORK:-}" ]]; then + ./add-peers.sh --consensus-only --network "$OP_NODE_NETWORK" --loop 300 & +fi + exec ./op-node diff --git a/reth/Dockerfile b/reth/Dockerfile index 2c9eec55..04c9e197 100644 --- a/reth/Dockerfile +++ b/reth/Dockerfile @@ -71,5 +71,6 @@ COPY ./reth/reth-entrypoint ./execution-entrypoint COPY op-node-entrypoint . COPY base-consensus-entrypoint . COPY consensus-entrypoint . +COPY add-peers.sh . CMD ["/usr/bin/supervisord"] diff --git a/reth/reth-entrypoint b/reth/reth-entrypoint index 0ffebc83..7d25cff7 100755 --- a/reth/reth-entrypoint +++ b/reth/reth-entrypoint @@ -130,6 +130,10 @@ mkdir -p "$RETH_DATA_DIR" echo "Starting reth with additional args: $ADDITIONAL_ARGS" echo "$BASE_NODE_L2_ENGINE_AUTH_RAW" > "$BASE_NODE_L2_ENGINE_AUTH" +if [[ -n "${RETH_NETWORK:-}" ]]; then + ./add-peers.sh --execution-only --network "$RETH_NETWORK" --execution-rpc "http://localhost:${RPC_PORT}" --loop 300 & +fi + exec "$BINARY" node \ -$LOG_LEVEL \ --datadir="$RETH_DATA_DIR" \