fix(lean): stop treating slot starts as proposals - #1541
Conversation
|
Rust / test devnet5 / serial (test_lean_node_finalizes) (pull_request) is passed when re-run on https://github.com/ream-collective/ream/actions/runs/30417417753/job/90471621711?pr=7 Rust / test devnet5 / serial (test_lean_node_checkpoint_sync_from_fresh_source) (pull_request)'s still fail with same error as previous PR: #1540 |
|
what is this PR fixing exactly? |
|
Hi @KolbyML, I described it on the What's wrong. tick_interval(true, ...)because For easier review, it is this part
changing |
What is the bug though? I can read what the PR is doing |
|
@KolbyML Correct me if i'm wrong, I think the bug is the case that a non-proposer can skip valid votes when computing its safe target - this can happen when aggregation does not finish before interval 4. The aggregation job starts at interval 2, but proof generation runs asynchronously and can take long enough to finish after interval 4. On a non-proposer node, the next slot-start tick was incorrectly treated as |
|
Just paste a case that it can be viewed on ci that i believe it comes from the bug i described above - https://github.com/ReamLabs/ream/actions/runs/30422733109/job/90482826834?pr=1542: Thank you for taking time reviewing it |



What was wrong?
LeanChainServicepassedis_slot_startas thehas_proposalargument totick_interval:At the start of a slot,
is_slot_startistrueon every synced node, not just the proposer.On a normal non-proposer node,
clock_prebuilt_forremainsNone, so the prebuilt-block guard evaluates tofalseand execution enters theelsebranch.It then calls
tick_interval(true, ...), where thattrueis interpreted ashas_proposal. This causes the node to callaccept_new_attestations()at interval 0 and move its new attestations into the known pool too early.This will move the attestation from
latest_new_aggregated_payloads_providertolatest_known_aggregated_payloads_provider.As a result,
update_safe_target()can miss votes thatupdate_head()still sees, allowing the head to advance while safe-target and finality progress lag.For example, suppose the safe-target threshold is 67 out of 100 validators:
latest_new_aggregated_payloadscontains 70 votes.has_proposal = true.accept_new_attestations()drains those 70 votes intolatest_known_aggregated_payloads.update_safe_target()reads only the new pool and can no longer see those votes.update_head()still reads the known pool, so the head can advance while safe-target and finality progress lag or stall.Besides, actual proposal handling already accepts new attestations through
get_proposal_head(). Therefore, slot start and proposal production must be signalled independently.How was it fixed?
has_proposal = false.get_proposal_head().Verification performed:
To-Do