Skip to content

fix: robust syncing, misc changes#591

Merged
g11tech merged 6 commits intomainfrom
sync-fixes
Feb 23, 2026
Merged

fix: robust syncing, misc changes#591
g11tech merged 6 commits intomainfrom
sync-fixes

Conversation

@anshalshukla
Copy link
Copy Markdown
Collaborator

@anshalshukla anshalshukla commented Feb 22, 2026

  • changes rocksdb back to optimize, the error earlier was probably due to invalid cache
  • cache is now invalidated on build.zig updates to prevent binding issues faced earlier
  • SSE events now have an optional node_id parameter, used to determine which node emitted (changes sync testing logic to look for finalized event being emitted from 3rd node, earlier we were just looking at the new finalized event which can be emitted event without 3rd node being able to sync), node_id helps here
  • while producing a block the node advances to the interval as per the block to prevent futureSlot errors due to ticking issues
  • target validates that it is past latest_justified as in a specific condition where the onBlock updates the beamState and the justified_block before the updateHeadUnlocked(tick2) but maybeDoAttestations happens in tick1 leading to target block for attestation < latest_justified
  • In publish block the flow has been revised to look for missing blocks even if the block is found in the fc store as there might be additional attestations which were earlier getting rejected, this might lead to stalling
  • fixes panic in process_attestations, observed in testing before

// This can happen when the updateHeadUnlocked is not yet called for the new block
// and the target is calculated before the head is updated
// OnBlock calls update the latest_justified and attestation occurs on interval before the head is updated
const justified = self.fcStore.latest_justified;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

the condition above is the same no?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

yes, I think Kai somehow removed this from devnet3 PR and I thought this condition was missing altogether, removing the redundancy here.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this condition is not needed, the previous while look should automatically take care of it


// Ensure target is not behind the justified source checkpoint
// This prevents creating invalid attestations where target slot is behind the justified source slot
// This can happen when the updateHeadUnlocked is not yet called for the new block
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

what condition is update head not called for the new block, we always call it when the new block is added no?

g11tech and others added 5 commits February 23, 2026 13:19
#593)

* chain: queue gossip blocks that arrive before forkchoice ticks to their slot

When a peer gossips a block for slot N and our local interval timer
hasn't yet advanced the forkchoice clock to N*INTERVALS_PER_SLOT,
forkChoice.onBlockUnlocked returns FutureSlot and the block is dropped.

Fix by queuing such blocks in BeamChain.pending_blocks (an ArrayList
of SSZ-cloned SignedBlockWithAttestation).  After validateBlock passes,
we check if block.slot * INTERVALS_PER_SLOT > fcStore.time; if so the
block is cloned into the queue instead of being processed immediately.

In onInterval, after forkChoice.onInterval has advanced the clock, the
new processPendingBlocks helper iterates the queue and replays every
entry whose slot is now reachable (slot * INTERVALS_PER_SLOT <= fc_time)
by calling onBlock + onBlockFollowup in the normal way.  Entries still
in the future stay in the queue for the next tick.

https://claude.ai/code/session_0116R4pk8fiu5Ve5PAfjE3op

* node: call processPendingBlocks from onInterval and fetch missing roots

Move the processPendingBlocks call out of chain.onInterval and into
node.onInterval so that any missing attestation-head roots discovered
while replaying queued blocks can be immediately fetched via
fetchBlockByRoots, mirroring the pattern used after onGossip in
handleGossipProcessingResult.

- Make processPendingBlocks pub and change its return type from void to
  []types.Root, accumulating all missing roots across every replayed
  block into a single caller-owned slice.
- Remove the processPendingBlocks call from chain.onInterval (it now
  belongs to the node layer).
- In node.onInterval, call chain.processPendingBlocks() right after
  chain.onInterval() and pass the returned roots to fetchBlockByRoots.

https://claude.ai/code/session_0116R4pk8fiu5Ve5PAfjE3op

---------

Co-authored-by: Claude <noreply@anthropic.com>
@g11tech g11tech merged commit e577fe0 into main Feb 23, 2026
23 of 29 checks passed
@g11tech g11tech deleted the sync-fixes branch February 23, 2026 15:42
@g11tech g11tech mentioned this pull request Feb 25, 2026
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.

3 participants