This release improves duty scheduling reliability (proposer duty rework, late voluntary exit fix, multi-CL init resilience), strengthens consensus and message validation, adds detection/recovery for stale attestation data and silently-dropped EL logs, brings P2P performance and stability improvements, plus extensive test coverage, refactors, and dependency updates.
š Upgrade Priority
| Network | Priority |
|---|---|
| Mainnet | š Recommended |
| Hoodi | š Recommended |
ā ļø Always ensure your Execution + Consensus clients and any remote signers (web3signer, external signers) are updated, SSV strives to support latest released versions of these clients.
ā ļø Always update testnet and check performance before updating mainnet nodes.
š³ Docker image
docker pull ssvlabs/ssv-node:v2.4.3New features
Configurable SSV API listen address
Adds a new SSVAPIAddress (env: SSV_API_ADDRESS) config option so operators can bind the SSV HTTP API to 127.0.0.1 (or another specific interface) instead of all interfaces. The default behavior is intentionally non-breaking, but the node now logs a warning recommending SSVAPIAddress: 127.0.0.1 whenever SSVAPIPort is enabled without an explicit address.
Validators-list API pagination
Adds optional pagination (page, per_page) to the exporter /v1/validators HTTP endpoint. Existing clients work unchanged.
Stale attestation data detection & recovery
When the beacon node returns attestation data with a beacon_block_root that doesn't match the latest cached HeadEvent, the SSV node waits briefly (50ms) and refetches to get fresh data ā falling back gracefully if still mismatched or if insufficient time remains before the deadline. Stage validation over 5 days showed 100% recovery on detected mismatches with no negative side effects. New ssv_cl_attestation_data_* metrics track verification, mismatches, and refetch outcomes; proposal parent-root mismatches are also tracked for observability.
Execution-layer bloom filter cross-check
Adds a bloom-filter cross-check in fetchLogsInBatches to detect events silently dropped by execution clients (known Geth v1.17.1 bug: eth_getLogs occasionally returns [] for blocks that actually contain SSV events). For each block with no logs near chain tip, the node fetches the block header, checks if its bloom matches the SSV contract address, and retries FilterLogs up to 3 times if it does. Scoped to the streaming path to keep historical-sync RPC cost flat. Surfaces via the ssv.el.bloom.checks counter (recovery / false_positive outcome).
What's Changed
Duty scheduling: proposer rework + late-exit fix + scheduler hardening
Reworks the proposer duty-fetching path with better retries and epoch-transition reorg handling (mirroring the AttesterHandler simplification), and fixes voluntary exits being scheduled too close to the EL follow distance (which caused them to land already-late). Also buffers scheduler reorg events and handler channels for robustness under bursty load.
PRs: #2756, #2851, #2857, #2808, #2794, #2712, #2801
Beacon: multi-client init resilience + attestation recovery + domain caching
Tolerates transient CL inactivity during multi-client startup (WithAllowDelayedStart) so brief CL blips no longer kill SSV nodes during the existing 60s init window. Adds the stale attestation detection feature highlighted above, and caches DomainData per epoch to avoid duplicate RPCs.
Execution layer: bloom cross-check + contract ABI v2.0.0
Adds the bloom-filter EL log cross-check highlighted above, updates the contract ABI to v2.0.0 (fixing failed to find event by ID log noise from newer event types), updates the Hoodi stage contract address, and improves event-handler / event-syncer test coverage.
PRs: #2722, #2719, #2747, #2793, #2701
Message validation
Re-enables the lowest-allowed-round check for non-decided QBFT messages (clamped at FirstRound to avoid unsigned underflow), and fixes a nil SSVMessage panic in the Validate defer path.
QBFT: per-duty timers + lifecycle cleanup
Refactors RoundTimer to be per-duty (eliminating cross-duty timer state), stops a timer leak in TimeoutForRound, defers arming until callbacks register (avoids races), and clarifies/simplifies the QBFT Instance lifecycle and ProcessMsg paths. Adds missing unit tests for the QBFT instance.
PRs: #2775, #2754, #2755, #2826, #2830, #2741
Runners: unification, helpers, and fixes
Handles unexpected/mismatched messages gracefully, removes the BaseRunner.mtx (no longer needed), clarifies queue state management, unifies runner constructor arguments, extracts GetShare() and signAndBroadcastPartialSigMsgs() helpers, refactors the blind package, removes delegation duplication, fixes sync-committee contribution ordering, and adds tests for the committee and proposer-block-proposal flows.
PRs: #2723, #2771, #2777, #2804, #2805, #2806, #2802, #2762, #2781, #2814, #2745, #2725, #2824
P2P: stability, OOM hardening, faster peer scoring, fewer fatals
Improves peer scoring from O(n²) to O(n log n), caps stream reads to prevent OOM, preserves write errors and handles short writes, replaces fatals with graceful error handling, rejects bad peers earlier (InterceptPeerDial), uses exact peer subnets (rather than advertised), prevents msg-ID handler overflow from dropping peers, reduces discv5 polling, registers discovery health with the node prober, adds a topic-validator timeout, fixes various host/topic/subnet races, reduces router fan-out, fixes a TTL-map goroutine leak / interval-timer leak / p2p context cancellations, and adds connection-management and discovery test coverage.
PRs: #2727, #2735, #2734, #2730, #2773, #2744, #2810, #2809, #2748, #2787, #2813, #2818, #2820, #2782, #2779, #2746, #2786, #2823, #2731, #2726, #2789, #2764, #2860
Performance & memory
Reduces default validator queue capacity from 1000 to 128 (~76 MB/node heap savings, telemetry-validated), adds an in-memory pubkey index for O(1) operator lookup, uses [32]byte keys in PartialSigContainer, optimizes validator queue message keys, and caches inbox-metric attributes on the queue push path.
PRs: #2797, #2774, #2776, #2796, #2788
Queue: pressure admission + drop metrics
Improves pressure admission and drop-metric accounting in the validator queue, plus additional test coverage for the dropped-message metric and concurrent drops.
Exporter / HTTP API
Adds the validators-list pagination feature highlighted above, disables non-essential services in exporter mode, adds more logs around partial-signature verifications, and adds regression tests for the exporter network key.
PRs: #2703, #2711, #2718, #2815
CLI / operator config
Adds the configurable SSVAPIAddress highlighted above (with a default-binding warning when unset).
PRs: #2743
Fee-recipient registrations
Self-throttles on-demand validator-registrations during fee-recipient updates to avoid bursting downstream services.
PRs: #2608
ssvsigner
Breaks the circular module dependency between ssvsigner and the SSV node and adds a replace directive for local development.
Identity (stage-only rollback)
The persisted P2P network key encryption (#2765) that landed earlier on stage was rolled back to plaintext via a safe-downgrade bridge (#2838). Net behavior on this release matches v2.4.2 (plaintext storage); no operator action is required.
Observability
Shuts observability down with the correct ctx (no more "shutting down" log after process exit signal).
PRs: #2721
Build / CI / dependencies
Updates to Go 1.26, speeds up test pipelines, fixes the lint workflow's module/build cache, updates GH actions refs for the stage branch, and applies security updates to docker, otel, and postcss.
PRs: #2697, #2686, #2704, #2759, #2215, #2832, #2213
Spec & unit-test improvements
Enables the spec-test msg-processing-root check, runs unit tests concurrently (revert + re-roll: #2693 ā #2715 ā #2716), adds tests for aggregate selection-proof fork handling, dutystore independent locks, concurrent beacon head events, and replaces flaky time.Sleep-based test waits with explicit synchronization across the suite. Also improves beacon mock error-path coverage and avoids assertions inside worker goroutines.
PRs: #2692, #2693, #2715, #2716, #2816, #2785, #2817, #2833, #2834, #2822
Cleanup & refactors
Switches to reflect.TypeFor, built-in max/min, and stdlib errors (dropping pkg/errors); removes unused public methods/types, commented-out code, dead p2p code, unused constants, and stale TODOs; renames health-prober to match its responsibilities; fixes a misleading error message in event_parser; corrects swapped error messages in DropRegistryData; replaces an error-swallowing Get with read-only Lookup; replaces panics with errors in eventparser; and clarifies slashing-protection docs.
PRs: #2671, #2687, #2825, #2803, #2791, #2792, #2800, #2763, #2772, #2742, #2729, #2732, #2739, #2733, #2667, #2644