Skip to content

fix(consensus): apply finalized peer block from local stash#793

Merged
github-actions[bot] merged 1 commit into
fix/pass2-rollback-restore-staking-statefrom
fix/apply-finalized-peer-block-from-stash
Jun 5, 2026
Merged

fix(consensus): apply finalized peer block from local stash#793
github-actions[bot] merged 1 commit into
fix/pass2-rollback-restore-staking-statefrom
fix/apply-finalized-peer-block-from-stash

Conversation

@satyakwok

Copy link
Copy Markdown
Member

Problem

After the Pass-2 rollback fix (#792) eliminated #1e state_root mismatches,
the testnet still crawled: blocks committed only via the slow GetBlocks
sync fallback, ~1 block per several seconds, with nodes frequently stuck in
the BFT Finalize phase.

Root cause

In the peer-propose FinalizeBlock arm, the node already held the
finalized block — it had received the proposal and voted for it, the
hash-match guard confirmed proposed_block.hash == action.block_hash, and
justification carried the 2/3 precommit certificate. But the code applied
it only if the local node was the proposer; otherwise it logged
"waiting for libp2p NewBlock/sync instead of executing peer block",
triggered a sync, and broke. When NewBlock gossip missed (common after a
restart / on a small mesh), the node sat in Finalize re-requesting a block
it was holding the whole time → stall/crawl.

Fix

Apply the held block locally regardless of proposer, mirroring the
self-propose arm. The hash-match guard already proved it is the canonical
finalized block, and validate_block still re-checks structure + the
justification supermajority before the write. No change to block content or
state_root, so no fork gate — deploy fleet-wide.

Validation

Deployed to the 4-validator internal testnet alongside #792: block production
went from a crawl to ~3 blk/s (catching up the backlog), all validators
converged to the same height, and the public RPC returned to 200.

Note

Stacks on #792 (base set to that branch). A benign log artifact remains: the
redundant libp2p receive path can #1e on a block the finalize path already
committed — non-fatal (committed state_root is identical across nodes), to be
quieted as a follow-up.

On FinalizeBlock for a peer-proposed block the node already held the block
(it had voted for it; hash matches the action's block_hash, justification
carries the 2/3 precommit certificate) but refused to apply it unless it
was the local proposer — instead triggering libp2p sync and breaking. When
NewBlock gossip missed, the node sat in Finalize re-requesting a block it
already had, stalling/crawling the chain. Apply it locally like the
self-propose arm; validate_block still re-checks structure + justification
supermajority before the write. No state_root/format change.
@github-actions github-actions Bot merged commit 6783d4d into fix/pass2-rollback-restore-staking-state Jun 5, 2026
3 checks passed
@satyakwok satyakwok deleted the fix/apply-finalized-peer-block-from-stash branch June 5, 2026 20:07
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.

1 participant