181 lines
6.6 KiB
Python
181 lines
6.6 KiB
Python
|
|
#!/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()
|