Skip to content

eth: sync finality#2695

Open
nkryuchkov wants to merge 2 commits intostagefrom
finality-sync-2
Open

eth: sync finality#2695
nkryuchkov wants to merge 2 commits intostagefrom
finality-sync-2

Conversation

@nkryuchkov
Copy link
Contributor

Supersedes #2184

SIP: ssvlabs/SIPs#67

@codecov
Copy link

codecov bot commented Feb 19, 2026

Codecov Report

❌ Patch coverage is 56.58915% with 56 lines in your changes missing coverage. Please review.
✅ Project coverage is 56.3%. Comparing base (82f70ef) to head (cb2e0dc).
⚠️ Report is 1 commits behind head on stage.

Files with missing lines Patch % Lines
eth/executionclient/execution_client.go 57.6% 29 Missing and 7 partials ⚠️
eth/executionclient/multi_client.go 25.0% 9 Missing ⚠️
cli/generate_config.go 0.0% 4 Missing ⚠️
cli/operator/node.go 0.0% 3 Missing ⚠️
eth/eventsyncer/event_syncer.go 85.7% 3 Missing ⚠️
networkconfig/network.go 0.0% 1 Missing ⚠️

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@nkryuchkov nkryuchkov marked this pull request as ready for review February 20, 2026 13:20
@nkryuchkov nkryuchkov requested review from a team as code owners February 20, 2026 13:20
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 20, 2026

Greptile Summary

This PR implements finality-based event syncing for the execution client, transitioning from a follow-distance approach to using the execution layer's finalized blocks after a configurable fork epoch.

Key Changes:

  • Pre-fork behavior: Uses follow-distance (current block - offset) to determine safe blocks
  • Post-fork behavior: Uses eth_getBlockByNumber("finalized") to sync up to finalized blocks
  • Fork activation controlled by SSVForks.FinalityConsensus epoch in network config
  • All networks currently set to math.MaxUint64 (not yet activated), following Ethereum conventions
  • Fork detection based on estimated epoch from block timestamp
  • Health checks updated to compare against finalized block time instead of current time in post-fork mode

Implementation Details:

  • Added networkConfig parameter to ExecutionClient.New() and MultiClient.NewMulti()
  • New methods: IsFinalizedFork(), getFinalizedBlock(), epochFromBlockHeader()
  • Event syncer staleness checks now fork-aware
  • Comprehensive test coverage for pre/post-fork switching and finalized block usage

The implementation is clean, well-tested, and follows project conventions.

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk.
  • The implementation is thorough, well-tested, and backward compatible. Fork activation is disabled on all networks (math.MaxUint64), allowing safe deployment before activation. Comprehensive test coverage verifies both pre-fork and post-fork behavior, including boundary conditions. Code follows established patterns and project conventions.
  • No files require special attention

Important Files Changed

Filename Overview
eth/executionclient/execution_client.go Adds finality-based event syncing. Pre-fork uses follow-distance, post-fork uses EL finalized blocks via rpc.FinalizedBlockNumber. Properly handles epoch calculation and fork detection.
eth/eventsyncer/event_syncer.go Updates health checks for finality fork. Pre-fork checks for stale processing, post-fork compares against finalized block time instead of current time.
eth/executionclient/multi_client.go Passes networkConfig to single clients and adds IsFinalizedFork() method to check fork status across multi-client setup.
networkconfig/ssv.go Adds SSVForks struct with FinalityConsensus epoch field to store fork activation epoch.
eth/executionclient/execution_client_test.go Comprehensive tests for finality fork behavior, including pre/post-fork switching and finalized block usage verification.
eth/eventsyncer/event_syncer_test.go Adds tests for finalized fork health checks, verifying behavior when comparing against finalized blocks.
cli/operator/node.go Passes networkConfig to execution client constructors, adds missing WithFollowDistanceMulti option.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    Start([Event Sync Request]) --> GetHead[Get current head block]
    GetHead --> CalcEpoch[Calculate current epoch from block timestamp]
    CalcEpoch --> CheckFork{Current epoch >= FinalityConsensus?}
    
    CheckFork -->|No - Pre-fork| CheckDistance{Current block >= follow distance?}
    CheckDistance -->|No| NothingToSync[Return ErrNothingToSync]
    CheckDistance -->|Yes| CalcPreFork[toBlock = currentBlock - followDistance]
    
    CheckFork -->|Yes - Post-fork| GetFinalized[Get finalized block via rpc.FinalizedBlockNumber]
    GetFinalized --> CalcPostFork[toBlock = finalized block number]
    
    CalcPreFork --> CheckRange{toBlock >= fromBlock?}
    CalcPostFork --> CheckRange
    
    CheckRange -->|No| NothingToSync
    CheckRange -->|Yes| FetchLogs[Fetch logs in batches from fromBlock to toBlock]
    FetchLogs --> Return([Return logs channel])
    
    NothingToSync --> End([End])
    Return --> End
    
    style CheckFork fill:#e1f5ff
    style GetFinalized fill:#d4edda
    style CalcPreFork fill:#fff3cd
    style FetchLogs fill:#f8d7da
Loading

Last reviewed commit: cb2e0dc

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

25 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

@y0sher
Copy link
Contributor

y0sher commented Feb 22, 2026

As mentioned in the SIP comments, we are still debating whether this feature will need a fork or not. Also, we are discussing adding a "special treatment" for some events like exits/removals to allow a nice UX. So I think this pends some discussion with spec (CC: @GalRogozinski @MatheusFranco99 ).

@nkryuchkov
Copy link
Contributor Author

nkryuchkov commented Feb 22, 2026

As mentioned in the SIP comments, we are still debating whether this feature will need a fork or not. Also, we are discussing adding a "special treatment" for some events like exits/removals to allow a nice UX. So I think this pends some discussion with spec (CC: @GalRogozinski @MatheusFranco99 ).

Yes, I wanted the first version to be ported from the original PR as it is, just with minor changes. I tested it on stage, and it worked fine. I have also been thinking about the fork, and I don't see the reason why we'd need it. So, I want to remove the fork code, but I decided to keep it until the discussion on it has been finalized.

I'm currently working on the special treatment of ValidatorExited, I'll likely push it in the next commit

Copy link
Contributor

@GalRogozinski GalRogozinski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There could be issues where views differ of registered validators. So some operators may send messages that will be rejected by others. This can cause temporary scoring issues...

I think ValidatorExits may have issues, but it is not so important since this can be activated via EL

@MatheusFranco99
Copy link
Contributor

Overall I like moving event sync from 8 EL blocks to CL finality, but we need to be sure that this has no impact on duty loss, or signing anything on behalf of exited validators.

Also, I wanted to clarify something about the fork transition.
Suppose $F$ is the fork epoch.
Let $f.s$ be the slot $s$ (1 to 32) of epoch $f$.

  • During slot $F-1.32$, operators will sync with events of block with slot up to $F-1.24$.
  • Then, at slot $F.1$ it won't sync anymore with events of slot $F-1.25$ since it's already applying the new rule, and it will only sync with all events of epoch $F-1$ once (at best) it's in $F+1$?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants