fix(blockchain): make proof selection tie-breaking deterministic - #590
Conversation
extend_proofs_greedily kept its remaining candidate proofs in a HashSet<usize> and picked the best-coverage proof with max_by_key over the set's randomized iteration order, so equal-coverage ties were broken arbitrarily per process: the same store state could produce blocks with different aggregation bits from one run to the next. Iterate candidates in index order and break coverage ties toward the lowest index (pool insertion order), making block building reproducible for a given pool. The regression test builds a six-way coverage tie, so an arbitrary order cannot reproduce pool order by luck; against the previous code it fails on most runs. Found while building an offline block-building benchmark, whose same-seed determinism check reported differing block roots across runs of an identical workload.
🤖 Kimi Code ReviewOverall Assessment: This PR correctly fixes a consensus-critical determinism bug. The change ensures that block construction is deterministic across all nodes by replacing hash-iteration-order dependency with explicit index-based tie-breaking. Critical Consensus Fix (Positive)
Code Correctness
Performance
Testing
Nitpick
Verdict: Approve after verifying Automated review by Kimi (Moonshot AI) · kimi-k2.5 · custom prompt |
🤖 Codex Code Review
I did not find a correctness, security, or consensus-layer bug in the production change itself. The Validation note: I could not run Automated review by OpenAI Codex · gpt-5.4 · custom prompt |
🤖 Claude Code ReviewI now have enough to write the review. Review:
|
🗒️ Description / Motivation
extend_proofs_greedilykept its remaining candidate proofs in aHashSet<usize>andpicked the best-coverage proof with
max_by_keyover the set's randomized iterationorder, so equal-coverage ties were broken arbitrarily per process: the same store state
could produce blocks with different aggregation bits from one run to the next.
Found while building the offline block-building benchmark (#497), whose same-seed
determinism check reported differing block roots across runs of an identical workload.
Split out of that PR because it is a standalone node-behavior fix, unrelated to the
harness.
What Changed
crates/blockchain/src/block_builder.rsremaining_indicesis aVec<usize>iterated in index order; coverage ties break toward the lowest index (pool insertion order) viamax_by_key((count, Reverse(idx)))Correctness / Behavior Guarantees
every round. Only the choice among equal-coverage candidates changes, and that
choice was previously random.
baseline-vs-optimized benchmark comparison meaningful.
Tests Added / Run
extend_proofs_greedily_breaks_coverage_ties_by_pool_order: six disjoint proofs ofidentical coverage, so every round is again a six-way tie and selection order is
decided purely by the tie-break. An arbitrary order cannot match pool order by luck
(1 in 720); against the previous code the test fails on most runs.
make fmt,make lint,make test— all clean.Related Issues / PRs
✅ Verification Checklist
make fmt— cleanmake lint(clippy with-D warnings) — cleanmake test(cargo test --workspace --profile release-fast) — all passing