Summary
Every signature verification resolves the eth2 signing domain by fetching BN config endpoints
at call time, with no caching: an attester partial-sig verification costs 4 sequential HTTP
calls (spec ×2 + genesis + fork_schedule — crates/eth2util/src/signing.rs:105-117,
crates/eth2api/src/extensions.rs:287,352,411); the aggregator fetch path adds another spec lookup.
On a non-local BN link this eats the 4s duty budget, and a single stalled request fails the
operation. Worse, the failure is misreported as a signature failure — the VC gets HTTP 400
partial signature verification failed when no signature was ever checked.
Charon has the same call shape but its go-eth2-client fork caches
Spec /
Genesis /
ForkSchedule
— on identical infra (BN behind a ~670ms tunnel), charon ran a clean 14-epoch hoodi window while
pluto missed 3/14.
Evidence (hoodi, 2026-07-22/23, 2×pluto+2×charon, validator 1454246)
Transport failures confirmed live in three verification paths (sigagg 4×, parsigex external store
2×, vapi 400 1×); likely contributed to ≥1 attester miss and to aggregator starvation (2/22
aggregator duties succeeded; direct log: fetch aggregator data → GetSpec(… operation timed out)).
ERROR … pluto_core::sigagg: verify failed error=aggregate signature verification failed:
Request error: error sending request for url (…/eth/v1/beacon/genesis)
lodestar VC: submitPoolAttestationsV2 failed with status 400: partial signature verification failed
Summary
Every signature verification resolves the eth2 signing domain by fetching BN config endpoints
at call time, with no caching: an attester partial-sig verification costs 4 sequential HTTP
calls (spec ×2 + genesis + fork_schedule —
crates/eth2util/src/signing.rs:105-117,crates/eth2api/src/extensions.rs:287,352,411); the aggregator fetch path adds another spec lookup.On a non-local BN link this eats the 4s duty budget, and a single stalled request fails the
operation. Worse, the failure is misreported as a signature failure — the VC gets HTTP 400
partial signature verification failedwhen no signature was ever checked.Charon has the same call shape but its go-eth2-client fork caches
Spec/Genesis/ForkSchedule— on identical infra (BN behind a ~670ms tunnel), charon ran a clean 14-epoch hoodi window while
pluto missed 3/14.
Evidence (hoodi, 2026-07-22/23, 2×pluto+2×charon, validator 1454246)
Transport failures confirmed live in three verification paths (sigagg 4×, parsigex external store
2×, vapi 400 1×); likely contributed to ≥1 attester miss and to aggregator starvation (2/22
aggregator duties succeeded; direct log:
fetch aggregator data → GetSpec(… operation timed out)).