Skip to content

Commit 386dda0

Browse files
authored
Merge pull request #27 from Firstset/relay-check-improved
Relay check improved
2 parents fd3c9b6 + 1c0f36e commit 386dda0

File tree

3 files changed

+123
-28
lines changed

3 files changed

+123
-28
lines changed

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,11 @@ The state check subcommand will retrieve all keys registered in CSM, validator c
174174

175175
The relay check subcommand will check if the validator keys are registered with all whitelisted relays in the Lido Relay Allowlist.
176176

177-
`python -m swarm relay-check`
177+
`python -m swarm relay-check [--detailed] [--pubkey <pubkey>]`
178+
179+
The `--detailed` flag will print a more detailed report with specific relay URLs per validator.
180+
181+
The `--pubkey` flag allows checking a specific validator, showing a detailed report for just that validator.
178182

179183
### Manual exit
180184

swarm/__main__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
parser_exit_monitor.set_defaults(func=exit.automated_exit)
3939

4040
parser_relay_check = subparsers.add_parser('relay-check', help='Check validator relay coverage against allowed mev-boost relays')
41+
parser_relay_check.add_argument('--detailed', action='store_true', help='Show detailed registration information')
42+
parser_relay_check.add_argument('--pubkey', type=str, help='the public key of the validator to report on')
4143
parser_relay_check.set_defaults(func=relay_check.check_relays)
4244

4345
args = parser.parse_args()

swarm/relay_check.py

Lines changed: 116 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,13 @@ async def check_validator_registration(session, relay_url, pubkey):
2121
"""Check if a validator is registered with a specific relay"""
2222
url = f"{relay_url}/relay/v1/data/validator_registration"
2323
try:
24-
async with session.get(url, params={'pubkey': pubkey}) as response:
24+
async with session.get(url, params={'pubkey': pubkey}, timeout=5) as response:
2525
if response.status == 200:
2626
return True
2727
return False
28-
except:
29-
raise RelayRequestException(f"Failed to request validator registration from {relay_url}")
28+
except Exception as e:
29+
print(f"Warning: Could not check registration status for {relay_url}: {str(e)}")
30+
return None # Return None to indicate unknown status
3031

3132
async def get_validator_keys_from_csm(config):
3233
csm = CSM(config)
@@ -39,37 +40,125 @@ async def check_relays(config, args):
3940

4041
# Get whitelisted relays
4142
relay_tuples = await get_whitelisted_relays(config)
42-
# Extract just the URLs from the relay tuples
4343
relay_urls = [relay[0] for relay in relay_tuples]
44-
print(f"Found {len(relay_urls)} whitelisted relays")
44+
45+
# Separate mandatory and optional relays
46+
mandatory_relays = [relay[0] for relay in relay_tuples if relay[2]] # relay[2] is the mandatory flag
47+
optional_relays = [relay[0] for relay in relay_tuples if not relay[2]]
48+
49+
print(f"Found {len(relay_urls)} whitelisted relays ({len(mandatory_relays)} mandatory, {len(optional_relays)} optional)")
4550

4651
# Get validator public keys from CSM
4752
validator_keys = await get_validator_keys_from_csm(config)
48-
print(f"Found {len(validator_keys)} validators in CSM")
53+
54+
# If pubkey is specified, only check that validator
55+
if hasattr(args, 'pubkey') and args.pubkey:
56+
if args.pubkey not in validator_keys:
57+
print(f"Error: Validator {args.pubkey} not found in CSM")
58+
return
59+
validator_keys = [args.pubkey]
60+
print(f"Checking specific validator: {args.pubkey}")
61+
else:
62+
print(f"Found {len(validator_keys)} validators in CSM")
4963

5064
# Check registration for each validator with each relay
5165
async with aiohttp.ClientSession() as session:
5266
results = {}
53-
for validator in validator_keys:
54-
results[validator] = {'total': 0, 'relays': []}
67+
68+
# Process validators in chunks to limit concurrency
69+
chunk_size = 10
70+
for i in range(0, len(validator_keys), chunk_size):
71+
validator_chunk = validator_keys[i:i + chunk_size]
72+
73+
tasks = []
74+
for validator in validator_chunk:
75+
results[validator] = {
76+
'mandatory_total': 0,
77+
'optional_total': 0,
78+
'mandatory_relays': [],
79+
'optional_relays': [],
80+
'unknown': []
81+
}
82+
for relay_url in relay_urls:
83+
tasks.append(check_validator_registration(session, relay_url, validator))
84+
85+
chunk_results = await asyncio.gather(*tasks)
86+
87+
for validator_idx, validator in enumerate(validator_chunk):
88+
for relay_idx, is_registered in enumerate(chunk_results[validator_idx * len(relay_urls):(validator_idx + 1) * len(relay_urls)]):
89+
relay_url = relay_urls[relay_idx]
90+
if is_registered is True:
91+
if relay_url in mandatory_relays:
92+
results[validator]['mandatory_total'] += 1
93+
results[validator]['mandatory_relays'].append(relay_url)
94+
else:
95+
results[validator]['optional_total'] += 1
96+
results[validator]['optional_relays'].append(relay_url)
97+
elif is_registered is None:
98+
results[validator]['unknown'].append(relay_url)
99+
100+
if not args.pubkey: # Only show progress for bulk checks
101+
print(f"Processed {min(i + chunk_size, len(validator_keys))}/{len(validator_keys)} validators...")
102+
103+
# Print results based on mode
104+
if args.pubkey or (hasattr(args, 'detailed') and args.detailed):
105+
print_detailed_report(results, relay_tuples, mandatory_relays, optional_relays)
106+
else:
107+
print_summary_report(results, mandatory_relays, optional_relays)
108+
109+
def print_summary_report(results, mandatory_relays, optional_relays):
110+
"""Print condensed summary of validator relay registration status"""
111+
print("\nRelay Coverage Summary:")
112+
print("-" * 50)
113+
114+
ok_count = 0
115+
not_ok_count = 0
116+
117+
for validator, data in results.items():
118+
is_ok = data['mandatory_total'] >= 2 # At least 2 mandatory relays required
119+
status = "OK" if is_ok else "NOT OK"
120+
121+
if is_ok:
122+
ok_count += 1
123+
else:
124+
not_ok_count += 1
55125

56-
for relay_url in relay_urls:
57-
is_registered = await check_validator_registration(session, relay_url, validator)
58-
if is_registered:
59-
results[validator]['total'] += 1
60-
results[validator]['relays'].append(relay_url)
126+
mandatory_coverage = f"{data['mandatory_total']}/{len(mandatory_relays)}"
127+
optional_coverage = f"{data['optional_total']}/{len(optional_relays)}"
128+
129+
print(f"Validator {validator} : {status:6} (Mandatory: {mandatory_coverage}, Optional: {optional_coverage})")
130+
131+
print(f"\nSummary: {ok_count} validators OK, {not_ok_count} validators NOT OK")
132+
133+
def print_detailed_report(results, relay_tuples, mandatory_relays, optional_relays):
134+
"""Print detailed report of validator relay registration status"""
135+
print("\nDetailed Relay Coverage Report:")
136+
print("-" * 50)
137+
138+
for validator, data in results.items():
139+
is_ok = data['mandatory_total'] >= 2
140+
status = "OK" if is_ok else "NOT OK"
141+
142+
print(f"\nValidator {validator} : {status}")
143+
print(f"Registered with {data['mandatory_total']}/{len(mandatory_relays)} mandatory relays")
144+
print(f"Registered with {data['optional_total']}/{len(optional_relays)} optional relays")
145+
146+
if data['unknown']:
147+
print(f"Could not check status for {len(data['unknown'])} relay(s):")
148+
for relay in data['unknown']:
149+
relay_info = next(r for r in relay_tuples if r[0] == relay)
150+
print(f" ? {relay} ({relay_info[1]} - {relay_info[3]})")
151+
152+
missing_mandatory = set(mandatory_relays) - set(data['mandatory_relays'])
153+
if missing_mandatory:
154+
print("Missing mandatory registrations:")
155+
for relay in missing_mandatory:
156+
relay_info = next(r for r in relay_tuples if r[0] == relay)
157+
print(f" - {relay} ({relay_info[1]} - {relay_info[3]})")
61158

62-
# Print results
63-
print("\nRelay coverage report:")
64-
print("-" * 50)
65-
for validator, data in results.items():
66-
coverage_pct = (data['total'] / len(relay_urls)) * 100
67-
print(f"\nValidator {validator[:12]}...")
68-
print(f"Registered with {data['total']}/{len(relay_urls)} relays ({coverage_pct:.1f}%)")
69-
if data['total'] < len(relay_urls):
70-
print("Missing registrations for relays:")
71-
missing_relays = set(relay_urls) - set(data['relays'])
72-
for relay in missing_relays:
73-
# Find the full relay info for prettier printing
74-
relay_info = next(r for r in relay_tuples if r[0] == relay)
75-
print(f" - {relay} ({relay_info[1]} - {relay_info[3]})")
159+
missing_optional = set(optional_relays) - set(data['optional_relays'])
160+
if missing_optional:
161+
print("Missing optional registrations:")
162+
for relay in missing_optional:
163+
relay_info = next(r for r in relay_tuples if r[0] == relay)
164+
print(f" - {relay} ({relay_info[1]} - {relay_info[3]})")

0 commit comments

Comments
 (0)