Some checks failed
Deploy to Phoenix / deploy (push) Has been cancelled
- ADD_CHAIN138_TO_LEDGER_LIVE: Ledger form done; public code review repo bis-innovations/LedgerLive; init/push commands - CONTRACT_DEPLOYMENT_RUNBOOK: Chain 138 gas price 1 gwei, 36-addr check, TransactionMirror workaround - CONTRACT_*: AddressMapper, MirrorManager deployed 2026-02-12; 36-address on-chain check - NEXT_STEPS_FOR_YOU: Ledger done; steps completable now (no LAN); run-completable-tasks-from-anywhere - MASTER_INDEX, OPERATOR_OPTIONAL, SMART_CONTRACTS_INVENTORY_SIMPLE: updates - LEDGER_BLOCKCHAIN_INTEGRATION_COMPLETE: bis-innovations/LedgerLive reference Co-authored-by: Cursor <cursoragent@cursor.com>
224 lines
9.6 KiB
Bash
224 lines
9.6 KiB
Bash
#!/usr/bin/env bash
|
|
# Collect enode from each of the 32 Besu nodes and regenerate static-nodes.json and
|
|
# permissions-nodes.toml with 32 unique entries (canonical IPs). Fixes duplicate enode (2400/2401).
|
|
#
|
|
# Usage: bash scripts/besu/collect-enodes-from-all-besu-nodes.sh [--dry-run] [--missing-only]
|
|
# --missing-only Only try to collect from VMIDs whose IP is not yet in static-nodes.json (fix failures only).
|
|
# Output: config/besu-node-lists/static-nodes.json and permissions-nodes.toml (backups as *.bak).
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
source "${PROJECT_ROOT}/config/ip-addresses.conf" 2>/dev/null || true
|
|
|
|
DRY_RUN=false
|
|
MISSING_ONLY=false
|
|
for arg in "${@:-}"; do
|
|
[[ "$arg" == "--dry-run" ]] && DRY_RUN=true
|
|
[[ "$arg" == "--missing-only" ]] && MISSING_ONLY=true
|
|
done
|
|
|
|
STATIC_FILE="${PROJECT_ROOT}/config/besu-node-lists/static-nodes.json"
|
|
PERM_FILE="${PROJECT_ROOT}/config/besu-node-lists/permissions-nodes.toml"
|
|
SSH_OPTS="-o ConnectTimeout=8 -o StrictHostKeyChecking=accept-new"
|
|
|
|
# All 32 Besu VMIDs in stable order (validators, sentries, RPCs)
|
|
BESU_VMIDS=(1000 1001 1002 1003 1004 1500 1501 1502 1503 1504 1505 1506 1507 1508 2101 2102 2201 2301 2303 2304 2305 2306 2400 2401 2402 2403 2500 2501 2502 2503 2504 2505)
|
|
|
|
# VMID -> Proxmox host
|
|
declare -A HOST_BY_VMID
|
|
for v in 1000 1001 1002 1500 1501 1502 2101 2500 2501 2502 2503 2504 2505; do HOST_BY_VMID[$v]="${PROXMOX_R630_01:-192.168.11.11}"; done
|
|
for v in 2201 2303 2401; do HOST_BY_VMID[$v]="${PROXMOX_R630_02:-192.168.11.12}"; done
|
|
for v in 1003 1004 1503 1504 1505 1506 1507 1508 2102 2301 2304 2305 2306 2400 2402 2403; do HOST_BY_VMID[$v]="${PROXMOX_ML110:-192.168.11.10}"; done
|
|
|
|
# VMID -> canonical IP (all 32)
|
|
declare -A IP_BY_VMID
|
|
IP_BY_VMID[1000]=192.168.11.100
|
|
IP_BY_VMID[1001]=192.168.11.101
|
|
IP_BY_VMID[1002]=192.168.11.102
|
|
IP_BY_VMID[1003]=192.168.11.103
|
|
IP_BY_VMID[1004]=192.168.11.104
|
|
IP_BY_VMID[1500]=192.168.11.150
|
|
IP_BY_VMID[1501]=192.168.11.151
|
|
IP_BY_VMID[1502]=192.168.11.152
|
|
IP_BY_VMID[1503]=192.168.11.153
|
|
IP_BY_VMID[1504]=192.168.11.154
|
|
IP_BY_VMID[1505]=192.168.11.213
|
|
IP_BY_VMID[1506]=192.168.11.214
|
|
IP_BY_VMID[1507]=192.168.11.244
|
|
IP_BY_VMID[1508]=192.168.11.245
|
|
IP_BY_VMID[2101]=192.168.11.211
|
|
IP_BY_VMID[2102]=192.168.11.212
|
|
IP_BY_VMID[2201]=192.168.11.221
|
|
IP_BY_VMID[2301]=192.168.11.232
|
|
IP_BY_VMID[2303]=192.168.11.233
|
|
IP_BY_VMID[2304]=192.168.11.234
|
|
IP_BY_VMID[2305]=192.168.11.235
|
|
IP_BY_VMID[2306]=192.168.11.236
|
|
IP_BY_VMID[2400]=192.168.11.240
|
|
IP_BY_VMID[2401]=192.168.11.241
|
|
IP_BY_VMID[2402]=192.168.11.242
|
|
IP_BY_VMID[2403]=192.168.11.243
|
|
IP_BY_VMID[2500]=192.168.11.172
|
|
IP_BY_VMID[2501]=192.168.11.173
|
|
IP_BY_VMID[2502]=192.168.11.174
|
|
IP_BY_VMID[2503]=192.168.11.246
|
|
IP_BY_VMID[2504]=192.168.11.247
|
|
IP_BY_VMID[2505]=192.168.11.248
|
|
|
|
get_enode() {
|
|
local vmid="$1"
|
|
local host="${HOST_BY_VMID[$vmid]:-}"
|
|
local ip="${IP_BY_VMID[$vmid]:-}"
|
|
[[ -z "$host" || -z "$ip" ]] && return 1
|
|
# 1) admin_nodeInfo (RPC)
|
|
local enode
|
|
enode=$(ssh $SSH_OPTS "root@$host" "pct exec $vmid -- curl -s -m 3 -X POST -H 'Content-Type: application/json' --data '{\"jsonrpc\":\"2.0\",\"method\":\"admin_nodeInfo\",\"params\":[],\"id\":1}' http://127.0.0.1:8545 2>/dev/null" 2>/dev/null | jq -r '.result.enode // empty' 2>/dev/null)
|
|
if [[ -n "$enode" ]]; then
|
|
echo "$enode" | sed "s/@[0-9.]*:/@$ip:/"
|
|
return 0
|
|
fi
|
|
# 2) besu public-key export: outputs "0x<128 hex>"; build enode://<128hex>@<ip>:30303
|
|
local pubkey
|
|
pubkey=$(ssh $SSH_OPTS "root@$host" "pct exec $vmid -- bash -c '/opt/besu/bin/besu public-key export --node-private-key-file=/data/besu/key 2>/dev/null || /opt/besu/bin/besu public-key export --node-private-key-file=/data/besu/nodekey 2>/dev/null'" 2>/dev/null | grep -oE '0x[0-9a-fA-F]{128}' | head -1 | sed 's/^0x//')
|
|
if [[ -n "$pubkey" && ${#pubkey} -eq 128 ]]; then
|
|
echo "enode://${pubkey}@${ip}:30303"
|
|
return 0
|
|
fi
|
|
# 3) For 1505-1508 on ml110: node may lack Besu binary; export key via helper 1504 (which has Besu)
|
|
if [[ "$host" == "${PROXMOX_ML110:-192.168.11.10}" ]] && [[ "$vmid" =~ ^(1505|1506|1507|1508)$ ]]; then
|
|
pubkey=$(ssh $SSH_OPTS "root@$host" "pct exec $vmid -- cat /data/besu/key 2>/dev/null | head -1 > /tmp/key${vmid}.$$ 2>/dev/null && pct push 1504 /tmp/key${vmid}.$$ /tmp/key${vmid} 2>/dev/null && pct exec 1504 -- /opt/besu/bin/besu public-key export --node-private-key-file=/tmp/key${vmid} 2>/dev/null; rm -f /tmp/key${vmid}.$$" 2>/dev/null | grep -oE '0x[0-9a-fA-F]{128}' | head -1 | sed 's/^0x//')
|
|
if [[ -n "$pubkey" && ${#pubkey} -eq 128 ]]; then
|
|
echo "enode://${pubkey}@${ip}:30303"
|
|
return 0
|
|
fi
|
|
fi
|
|
# 4) For 2501-2505 on r630-01: node may lack Besu binary; export key via helper 2500 (which has Besu)
|
|
if [[ "$host" == "${PROXMOX_R630_01:-192.168.11.11}" ]] && [[ "$vmid" =~ ^(2501|2502|2503|2504|2505)$ ]]; then
|
|
pubkey=$(ssh $SSH_OPTS "root@$host" "pct exec $vmid -- cat /data/besu/key 2>/dev/null | head -1 > /tmp/key${vmid}.$$ 2>/dev/null && pct push 2500 /tmp/key${vmid}.$$ /tmp/key${vmid} 2>/dev/null && pct exec 2500 -- /opt/besu/bin/besu public-key export --node-private-key-file=/tmp/key${vmid} 2>/dev/null; rm -f /tmp/key${vmid}.$$" 2>/dev/null | grep -oE '0x[0-9a-fA-F]{128}' | head -1 | sed 's/^0x//')
|
|
if [[ -n "$pubkey" && ${#pubkey} -eq 128 ]]; then
|
|
echo "enode://${pubkey}@${ip}:30303"
|
|
return 0
|
|
fi
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
# Load existing IP -> enode from current static-nodes.json (so --missing-only knows what's already there and merge uses it)
|
|
declare -A EXISTING_BY_IP
|
|
if [[ -f "$STATIC_FILE" ]]; then
|
|
while IFS= read -r enode; do
|
|
[[ -z "$enode" ]] && continue
|
|
ip=$(echo "$enode" | sed -n 's|enode://[a-fA-F0-9]*@\([0-9.]*\):.*|\1|p')
|
|
[[ -n "$ip" ]] && EXISTING_BY_IP[$ip]="$enode"
|
|
done < <(jq -r '.[]' "$STATIC_FILE" 2>/dev/null)
|
|
fi
|
|
[[ ${#EXISTING_BY_IP[@]} -eq 0 && -f "${STATIC_FILE}.bak" ]] && while IFS= read -r enode; do
|
|
[[ -z "$enode" ]] && continue
|
|
ip=$(echo "$enode" | sed -n 's|enode://[a-fA-F0-9]*@\([0-9.]*\):.*|\1|p')
|
|
[[ -n "$ip" ]] && EXISTING_BY_IP[$ip]="$enode"
|
|
done < <(jq -r '.[]' "${STATIC_FILE}.bak" 2>/dev/null)
|
|
|
|
# When --missing-only: only VMIDs whose canonical IP is not yet in the list
|
|
VMIDS_TO_TRY=()
|
|
if $MISSING_ONLY; then
|
|
for vmid in "${BESU_VMIDS[@]}"; do
|
|
ip="${IP_BY_VMID[$vmid]:-}"
|
|
[[ -z "$ip" ]] && continue
|
|
[[ -z "${EXISTING_BY_IP[$ip]:-}" ]] && VMIDS_TO_TRY+=( "$vmid" )
|
|
done
|
|
echo "Missing-only: collecting from ${#VMIDS_TO_TRY[@]} VMIDs not in current list (${VMIDS_TO_TRY[*]:-none})."
|
|
[[ ${#VMIDS_TO_TRY[@]} -eq 0 ]] && echo "All 32 IPs already present. Nothing to collect." && exit 0
|
|
else
|
|
VMIDS_TO_TRY=( "${BESU_VMIDS[@]}" )
|
|
echo "Collecting enodes from 32 Besu nodes..."
|
|
fi
|
|
|
|
declare -A COLLECTED_BY_VMID
|
|
collect_ok=0
|
|
collect_fail=0
|
|
declare -A SEEN_NODE_ID
|
|
|
|
for vmid in "${VMIDS_TO_TRY[@]}"; do
|
|
enode=$(get_enode "$vmid" 2>/dev/null) || true
|
|
if [[ -n "$enode" ]]; then
|
|
node_id=$(echo "$enode" | sed -n 's|enode://\([a-fA-F0-9]*\)@.*|\1|p')
|
|
if [[ -n "${SEEN_NODE_ID[$node_id]:-}" ]]; then
|
|
echo " VMID $vmid: duplicate node_id (same as VMID ${SEEN_NODE_ID[$node_id]}) — using anyway with IP ${IP_BY_VMID[$vmid]}" >&2
|
|
else
|
|
SEEN_NODE_ID[$node_id]=$vmid
|
|
fi
|
|
COLLECTED_BY_VMID[$vmid]="$enode"
|
|
((collect_ok++)) || true
|
|
echo " $vmid ${IP_BY_VMID[$vmid]} OK"
|
|
else
|
|
((collect_fail++)) || true
|
|
echo " $vmid ${IP_BY_VMID[$vmid]} FAIL (no enode)"
|
|
fi
|
|
done
|
|
|
|
echo ""
|
|
echo "Collected $collect_ok enodes, $collect_fail failed."
|
|
|
|
# Merge: for each of 32 VMIDs use collected (if any) else existing enode for that IP; dedupe by node_id
|
|
declare -a FINAL_ENODES
|
|
declare -a MISSING_VMIDS
|
|
declare -A USED_NODE_ID
|
|
|
|
for vmid in "${BESU_VMIDS[@]}"; do
|
|
ip="${IP_BY_VMID[$vmid]:-}"
|
|
[[ -z "$ip" ]] && continue
|
|
enode=""
|
|
if [[ -n "${COLLECTED_BY_VMID[$vmid]:-}" ]]; then
|
|
enode="${COLLECTED_BY_VMID[$vmid]}"
|
|
elif [[ -n "${EXISTING_BY_IP[$ip]:-}" ]]; then
|
|
existing="${EXISTING_BY_IP[$ip]}"
|
|
node_id=$(echo "$existing" | sed -n 's|enode://\([a-fA-F0-9]*\)@.*|\1|p')
|
|
if [[ -z "${USED_NODE_ID[$node_id]:-}" ]]; then
|
|
enode=$(echo "$existing" | sed "s/@[0-9.]*:/@$ip:/")
|
|
fi
|
|
fi
|
|
if [[ -n "$enode" ]]; then
|
|
node_id=$(echo "$enode" | sed -n 's|enode://\([a-fA-F0-9]*\)@.*|\1|p')
|
|
USED_NODE_ID[$node_id]=1
|
|
FINAL_ENODES+=( "$enode" )
|
|
else
|
|
MISSING_VMIDS+=( "$vmid" )
|
|
fi
|
|
done
|
|
|
|
echo "Merged total: ${#FINAL_ENODES[@]} unique enodes (target 32)."
|
|
|
|
if [[ ${#FINAL_ENODES[@]} -lt 32 && ${#MISSING_VMIDS[@]} -gt 0 ]]; then
|
|
echo "WARNING: ${#FINAL_ENODES[@]}/32. Missing VMIDs: ${MISSING_VMIDS[*]}. Re-run when those nodes are up or add enodes manually." >&2
|
|
fi
|
|
|
|
if [[ ${#FINAL_ENODES[@]} -eq 0 ]]; then
|
|
echo "No enodes to write. Aborting." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Build static-nodes.json (JSON array)
|
|
if ! $DRY_RUN; then
|
|
cp -a "$STATIC_FILE" "${STATIC_FILE}.bak" 2>/dev/null || true
|
|
cp -a "$PERM_FILE" "${PERM_FILE}.bak" 2>/dev/null || true
|
|
# JSON: ["enode://...", "enode://...", ...]
|
|
printf '%s\n' "${FINAL_ENODES[@]}" | jq -R . | jq -s . > "$STATIC_FILE"
|
|
# TOML: nodes-allowlist= [ "enode://...", ... ]
|
|
{
|
|
echo '# Node Permissioning — SINGLE SOURCE OF TRUTH for all Besu nodes'
|
|
echo '# Must match config/besu-node-lists/static-nodes.json and be deployed to every node.'
|
|
echo "# Generated by scripts/besu/collect-enodes-from-all-besu-nodes.sh — ${#FINAL_ENODES[@]} nodes, no duplicates."
|
|
echo ''
|
|
echo 'nodes-allowlist=['
|
|
for e in "${FINAL_ENODES[@]}"; do
|
|
echo " \"$e\","
|
|
done | sed '$ s/,$//'
|
|
echo ']'
|
|
} > "$PERM_FILE"
|
|
echo "Wrote $STATIC_FILE and $PERM_FILE (${#FINAL_ENODES[@]} entries)"
|
|
else
|
|
echo "[dry-run] Would write ${#FINAL_ENODES[@]} enodes to static-nodes.json and permissions-nodes.toml"
|
|
fi
|