Files
proxmox/scripts/fix-enode-config.py

181 lines
6.6 KiB
Python
Executable File

#!/usr/bin/env python3
"""
Fix enode URLs in static-nodes.json and permissions-nodes.toml
This script fixes the critical issues identified:
1. Invalid enode public key length (trailing zeros padding)
2. IP address mismatches between static-nodes.json and permissions-nodes.toml
3. Ensures all enode IDs are exactly 128 hex characters
Usage:
python3 fix-enode-config.py [container_vmid]
If vmid provided, fixes files in that container.
Otherwise, generates corrected files for all containers.
"""
import json
import re
import sys
import subprocess
# Container IP mapping (VMID -> IP)
CONTAINER_IPS = {
106: '192.168.11.13', # besu-validator-1
107: '192.168.11.14', # besu-validator-2
108: '192.168.11.15', # besu-validator-3
109: '192.168.11.16', # besu-validator-4
110: '192.168.11.18', # besu-validator-5
111: '192.168.11.19', # besu-sentry-2
112: '192.168.11.20', # besu-sentry-3
113: '192.168.11.21', # besu-sentry-4
114: '192.168.11.22', # besu-sentry-5
115: '192.168.11.23', # besu-rpc-1
116: '192.168.11.24', # besu-rpc-2
117: '192.168.11.25', # besu-rpc-3
}
def normalize_node_id(node_id_hex):
"""
Normalize node ID to exactly 128 hex characters.
Ethereum node IDs must be exactly 128 hex chars (64 bytes).
This function:
- Removes trailing zeros (padding)
- Pads with leading zeros if needed to reach 128 chars
- Truncates to 128 chars if longer
"""
# Remove '0x' prefix if present
node_id_hex = node_id_hex.lower().replace('0x', '')
# Remove trailing zeros (these are invalid padding)
node_id_hex = node_id_hex.rstrip('0')
# Pad with leading zeros to reach 128 chars if needed
if len(node_id_hex) < 128:
node_id_hex = '0' * (128 - len(node_id_hex)) + node_id_hex
elif len(node_id_hex) > 128:
# If longer, take first 128 (shouldn't happen, but handle it)
node_id_hex = node_id_hex[:128]
return node_id_hex
def extract_node_id_from_enode(enode_url):
"""Extract and normalize node ID from enode URL."""
match = re.search(r'enode://([a-fA-F0-9]+)@', enode_url)
if not match:
return None
node_id_raw = match.group(1)
return normalize_node_id(node_id_raw)
def create_enode_url(node_id, ip, port=30303):
"""Create properly formatted enode URL."""
if len(node_id) != 128:
raise ValueError(f"Node ID must be exactly 128 chars, got {len(node_id)}: {node_id[:32]}...")
return f"enode://{node_id}@{ip}:{port}"
def fix_static_nodes_json(current_file_content, validator_ips):
"""
Fix static-nodes.json to contain only validator enodes with correct IPs.
Args:
current_file_content: Current static-nodes.json content (JSON string)
validator_ips: List of (node_id, ip) tuples for validators
"""
enodes = []
for node_id, ip in validator_ips:
enode = create_enode_url(node_id, ip)
enodes.append(enode)
return json.dumps(enodes, indent=2)
def fix_permissions_nodes_toml(current_file_content, all_node_ips):
"""
Fix permissions-nodes.toml to contain all nodes with correct IPs.
Args:
current_file_content: Current permissions-nodes.toml content
all_node_ips: List of (node_id, ip) tuples for all nodes
"""
# Extract the header/comment
lines = current_file_content.split('\n')
header_lines = []
for line in lines:
if line.strip().startswith('#') or line.strip() == '':
header_lines.append(line)
elif 'nodes-allowlist' in line:
break
# Build the allowlist
allowlist_lines = ['nodes-allowlist=[']
for node_id, ip in all_node_ips:
enode = create_enode_url(node_id, ip)
allowlist_lines.append(f' "{enode}",')
# Remove trailing comma from last entry
if allowlist_lines:
allowlist_lines[-1] = allowlist_lines[-1].rstrip(',')
allowlist_lines.append(']')
return '\n'.join(header_lines) + '\n' + '\n'.join(allowlist_lines)
# Node IDs from source static-nodes.json (these are the 5 validators)
# Note: These may need adjustment - one appears to be 126 chars, we'll normalize
SOURCE_VALIDATOR_NODE_IDS = [
'889ba317e10114a035ef82248a26125fbc00b1cd65fb29a2106584dddd025aa3dda14657bc423e5e8bf7d91a9858e85a',
'2a827fcff14e548b761d18d0d7177745799d880be5ac54fb17d73aa06b105559527c97fec09005ac050e1363f16cb052',
'aeec2f2f7ee15da9bdbf11261d1d1e5526d2d1ca03d66393e131cc70dcea856a9a01ef3488031b769025447e36e14f4e',
'0f647faab18eb3cd1a334ddf397011af768b3311400923b670d9536f5a937aa04071801de095100142da03b233adb5db',
'037c0feeb799e7e98bc99f7c21b8993254cc48f3251c318b211a76aa40d9c373da8c0a1df60804b327b43a222940ebf', # 126 chars - needs padding
]
def main():
print("=" * 70)
print("Fixing Enode URLs in Besu Configuration Files")
print("=" * 70)
print()
# Normalize validator node IDs
normalized_validators = []
for i, node_id_raw in enumerate(SOURCE_VALIDATOR_NODE_IDS):
node_id = normalize_node_id(node_id_raw)
vmid = 106 + i # Validators are 106-110
if vmid in CONTAINER_IPS:
ip = CONTAINER_IPS[vmid]
normalized_validators.append((node_id, ip))
print(f"Validator {vmid} ({ip}):")
print(f" Original: {node_id_raw[:64]}... (len={len(node_id_raw)})")
print(f" Normalized: {node_id[:64]}... (len={len(node_id)})")
print(f" Enode: {create_enode_url(node_id, ip)}")
print()
# For now, we'll use the same 5 validators for static-nodes.json
# and permissions-nodes.toml (this is a simplified version)
# In production, you'd want all nodes in permissions-nodes.toml
# Generate corrected static-nodes.json
static_nodes_json = fix_static_nodes_json("", normalized_validators)
print("Generated static-nodes.json:")
print(static_nodes_json)
print()
# For permissions-nodes.toml, we need all nodes
# For now, use the same validators (you may want to add more)
permissions_toml = fix_permissions_nodes_toml(
"# Node Permissioning Configuration\n# Lists nodes that are allowed to connect to this node\n\n",
normalized_validators
)
print("Generated permissions-nodes.toml:")
print(permissions_toml[:500] + "..." if len(permissions_toml) > 500 else permissions_toml)
print()
print("=" * 70)
print("IMPORTANT: This is a template. You need to:")
print("1. Verify node IDs match actual Besu node keys")
print("2. Add all nodes (validators, sentries, RPC) to permissions-nodes.toml")
print("3. Copy corrected files to all containers")
print("=" * 70)
if __name__ == "__main__":
main()