Skip to content

Commit 8e28a19

Browse files
tcoratgerclaude
andauthored
fix(fork-choice): merge aggregate pools in deterministic order (#892)
* fix(fork-choice): merge aggregate pools in deterministic order The acceptance tick merged the pending and counted aggregate pools by iterating `known.keys() | new.keys()`. A union of dict-key views is a plain set, so its iteration order follows hashing, not insertion. The LMD walk keeps the first-seen vote when two votes share an attestation slot, so the merged pool's order decides which fork an equivocating validator counts toward. A hash-ordered merge therefore made the head depend on the hash seed and diverge across clients — regressed in #888, surfaced by test_same_slot_equivocating_attesters_count_once flipping the head from slot 2 to slot 3. Iterate `{**known, **new}` instead: counted votes first in insertion order, then pending-only votes, restoring the pre-#888 ordering. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Update src/lean_spec/spec/forks/lstar/fork_choice.py --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 5ef672b commit 8e28a19

1 file changed

Lines changed: 1 addition & 1 deletion

File tree

src/lean_spec/spec/forks/lstar/fork_choice.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -830,7 +830,7 @@ def accept_new_attestations(self, store: LstarStore) -> LstarStore:
830830
merged_aggregated_payloads = {
831831
attestation_data: known_payloads.get(attestation_data, set())
832832
| new_payloads.get(attestation_data, set())
833-
for attestation_data in known_payloads.keys() | new_payloads.keys()
833+
for attestation_data in {**known_payloads, **new_payloads}
834834
}
835835

836836
# Promote into the counted pool and clear the pending one.

0 commit comments

Comments
 (0)