Skip to content

fix(storage): store fork-choice votes independently - #552

Open
dicethedev wants to merge 4 commits into
lambdaclass:mainfrom
dicethedev:fix/fork-choice-votes
Open

fix(storage): store fork-choice votes independently#552
dicethedev wants to merge 4 commits into
lambdaclass:mainfrom
dicethedev:fix/fork-choice-votes

Conversation

@dicethedev

@dicethedev dicethedev commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

🗒️ Description / Motivation

This PR fixes fork-choice vote tracking so validator votes are stored independently from aggregated proofs and gossip signatures.

Previously, fork-choice votes were derived from bounded in-memory proof/signature buffers. When those buffers evicted old entries through FIFO cleanup, a validator’s latest vote could disappear even though the vote should still count for fork choice. This PR adds a separate proof-less vote store so fork-choice weights are not affected by proof/signature retention.

What Changed

  • Updated crates/storage/src/store.rs

    • Added an independent latest-vote store keyed by validator index.
    • Records known fork-choice votes when aggregated payloads become known.
    • Records block-included attestation votes separately from proof storage.
    • Keeps proof buffers for aggregation/proposal behavior only.
    • Prunes stored votes once their target is finalized.
  • Updated crates/blockchain/src/store.rs

    • Inserts block-included attestation votes into the independent vote store during block processing.

Correctness / Behavior Guarantees

  • Fork-choice votes no longer disappear when proof or signature buffers evict entries.
  • Existing proof/signature buffer behavior is preserved for aggregation and block proposal.
  • Latest-vote selection keeps the existing rule: higher slot wins, and same-slot ties use the canonical attestation-data root.
  • Finalized-target votes are pruned once they can no longer affect fork choice.

Tests Added / Run

  • Added regression coverage for votes surviving aggregated proof FIFO eviction.
  • Added coverage for pruning finalized-target votes from the independent vote store.

Related Issues / PRs

✅ Verification Checklist

  • Ran make fmt — clean
  • Ran make lint (clippy with -D warnings) — clean
  • Ran make test (cargo test --workspace --profile release-fast) — all passing

@greptile-apps

greptile-apps Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR separates latest fork-choice votes from bounded proof retention.

  • Adds a per-validator in-memory latest-vote store and deterministic replacement rule.
  • Records votes from known aggregates, promoted aggregates, and block-included attestations.
  • Prunes votes when finalization advances and adds FIFO-eviction and pruning tests.
  • Updates block processing to record included attestation votes independently of proofs.

Confidence Score: 4/5

This PR should not merge until vote pruning preserves latest messages whose heads remain relevant above the finalized checkpoint.

The new pruning predicate uses the attestation target, while LMD-GHOST consumes its head; after finalization this can remove live-branch voting weight from subsequent head calculations.

Files Needing Attention: crates/storage/src/store.rs

Important Files Changed

Filename Overview
crates/storage/src/store.rs Adds the independent latest-vote store and ingestion paths, but pruning by target slot can remove votes whose heads remain relevant to fork choice.
crates/blockchain/src/store.rs Records validated block-included attestation votes after persisting the imported block and state.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Known aggregate] --> V[Independent latest-vote store]
  B[Block-included attestation] --> V
  A --> P[Bounded proof buffer]
  V --> F[LMD-GHOST fork choice]
  C[Finalization advances] --> R[Prune votes by target slot]
  R --> V
Loading
Prompt To Fix All With AI
### Issue 1
crates/storage/src/store.rs:1087-1090
**Target-based pruning drops live votes**

When a validator's latest attestation targets a finalized checkpoint but its head remains on a live post-finalization branch, `prune_known_votes` removes the vote based solely on `target.slot`. LMD-GHOST weights `head.root`, so subsequent head calculations lose that validator's weight and can select the wrong branch until a replacement vote arrives.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "fix(storage): store fork-choice votes in..." | Re-trigger Greptile

Comment thread crates/storage/src/store.rs Outdated

@MegaRedHand MegaRedHand left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks good

Left some comments about pruning. The issue didn't mention it, but the main issue with how we stored votes is that proof are pruned regularly, which shouldn't affect votes: LMD-GHOST uses the latest message from each validator, so otherwise no pruning should happen.

Also, an additional PR is needed to finish addressing the issue, since we still need to replace the payloads and proofs in fork-choice with the new votes.

Comment thread crates/storage/src/store.rs Outdated
Comment thread crates/storage/src/store.rs Outdated
Comment thread crates/storage/src/store.rs Outdated
Comment thread crates/storage/src/store.rs Outdated
Comment thread crates/storage/src/store.rs Outdated
Comment thread crates/storage/src/store.rs Outdated
Comment thread crates/storage/src/store.rs Outdated
Comment thread crates/storage/src/store.rs Outdated
@MegaRedHand

Copy link
Copy Markdown
Collaborator

There are some conflicts

@dicethedev

Copy link
Copy Markdown
Contributor Author

There are some conflicts

You can review again

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.

Store fork-choice votes independently from proofs and signatures

2 participants