Apr 04, 5-6 PM (9)
Apr 04, 6-7 PM (2)
Apr 04, 7-8 PM (3)
Apr 04, 8-9 PM (5)
Apr 04, 9-10 PM (17)
Apr 04, 10-11 PM (21)
Apr 04, 11-12 AM (23)
Apr 05, 12-1 AM (2)
Apr 05, 1-2 AM (0)
Apr 05, 2-3 AM (2)
Apr 05, 3-4 AM (1)
Apr 05, 4-5 AM (1)
Apr 05, 5-6 AM (9)
Apr 05, 6-7 AM (13)
Apr 05, 7-8 AM (4)
Apr 05, 8-9 AM (1)
Apr 05, 9-10 AM (0)
Apr 05, 10-11 AM (7)
Apr 05, 11-12 PM (7)
Apr 05, 12-1 PM (5)
Apr 05, 1-2 PM (2)
Apr 05, 2-3 PM (3)
Apr 05, 3-4 PM (3)
Apr 05, 4-5 PM (18)
Apr 05, 5-6 PM (3)
Apr 05, 6-7 PM (2)
Apr 05, 7-8 PM (2)
Apr 05, 8-9 PM (0)
Apr 05, 9-10 PM (5)
Apr 05, 10-11 PM (19)
Apr 05, 11-12 AM (18)
Apr 06, 12-1 AM (4)
Apr 06, 1-2 AM (6)
Apr 06, 2-3 AM (12)
Apr 06, 3-4 AM (11)
Apr 06, 4-5 AM (5)
Apr 06, 5-6 AM (7)
Apr 06, 6-7 AM (4)
Apr 06, 7-8 AM (8)
Apr 06, 8-9 AM (24)
Apr 06, 9-10 AM (15)
Apr 06, 10-11 AM (9)
Apr 06, 11-12 PM (2)
Apr 06, 12-1 PM (38)
Apr 06, 1-2 PM (49)
Apr 06, 2-3 PM (27)
Apr 06, 3-4 PM (8)
Apr 06, 4-5 PM (32)
Apr 06, 5-6 PM (18)
Apr 06, 6-7 PM (3)
Apr 06, 7-8 PM (11)
Apr 06, 8-9 PM (6)
Apr 06, 9-10 PM (9)
Apr 06, 10-11 PM (29)
Apr 06, 11-12 AM (16)
Apr 07, 12-1 AM (8)
Apr 07, 1-2 AM (8)
Apr 07, 2-3 AM (3)
Apr 07, 3-4 AM (4)
Apr 07, 4-5 AM (1)
Apr 07, 5-6 AM (17)
Apr 07, 6-7 AM (6)
Apr 07, 7-8 AM (14)
Apr 07, 8-9 AM (35)
Apr 07, 9-10 AM (38)
Apr 07, 10-11 AM (25)
Apr 07, 11-12 PM (63)
Apr 07, 12-1 PM (38)
Apr 07, 1-2 PM (56)
Apr 07, 2-3 PM (54)
Apr 07, 3-4 PM (24)
Apr 07, 4-5 PM (36)
Apr 07, 5-6 PM (19)
Apr 07, 6-7 PM (22)
Apr 07, 7-8 PM (21)
Apr 07, 8-9 PM (20)
Apr 07, 9-10 PM (16)
Apr 07, 10-11 PM (41)
Apr 07, 11-12 AM (21)
Apr 08, 12-1 AM (13)
Apr 08, 1-2 AM (6)
Apr 08, 2-3 AM (9)
Apr 08, 3-4 AM (9)
Apr 08, 4-5 AM (4)
Apr 08, 5-6 AM (21)
Apr 08, 6-7 AM (40)
Apr 08, 7-8 AM (72)
Apr 08, 8-9 AM (41)
Apr 08, 9-10 AM (24)
Apr 08, 10-11 AM (56)
Apr 08, 11-12 PM (43)
Apr 08, 12-1 PM (36)
Apr 08, 1-2 PM (64)
Apr 08, 2-3 PM (45)
Apr 08, 3-4 PM (17)
Apr 08, 4-5 PM (16)
Apr 08, 5-6 PM (17)
Apr 08, 6-7 PM (27)
Apr 08, 7-8 PM (12)
Apr 08, 8-9 PM (11)
Apr 08, 9-10 PM (6)
Apr 08, 10-11 PM (50)
Apr 08, 11-12 AM (18)
Apr 09, 12-1 AM (7)
Apr 09, 1-2 AM (5)
Apr 09, 2-3 AM (2)
Apr 09, 3-4 AM (4)
Apr 09, 4-5 AM (6)
Apr 09, 5-6 AM (15)
Apr 09, 6-7 AM (36)
Apr 09, 7-8 AM (22)
Apr 09, 8-9 AM (25)
Apr 09, 9-10 AM (33)
Apr 09, 10-11 AM (20)
Apr 09, 11-12 PM (60)
Apr 09, 12-1 PM (68)
Apr 09, 1-2 PM (43)
Apr 09, 2-3 PM (74)
Apr 09, 3-4 PM (22)
Apr 09, 4-5 PM (51)
Apr 09, 5-6 PM (26)
Apr 09, 6-7 PM (23)
Apr 09, 7-8 PM (21)
Apr 09, 8-9 PM (39)
Apr 09, 9-10 PM (18)
Apr 09, 10-11 PM (29)
Apr 09, 11-12 AM (14)
Apr 10, 12-1 AM (5)
Apr 10, 1-2 AM (4)
Apr 10, 2-3 AM (4)
Apr 10, 3-4 AM (12)
Apr 10, 4-5 AM (3)
Apr 10, 5-6 AM (9)
Apr 10, 6-7 AM (29)
Apr 10, 7-8 AM (45)
Apr 10, 8-9 AM (47)
Apr 10, 9-10 AM (20)
Apr 10, 10-11 AM (61)
Apr 10, 11-12 PM (70)
Apr 10, 12-1 PM (46)
Apr 10, 1-2 PM (23)
Apr 10, 2-3 PM (36)
Apr 10, 3-4 PM (33)
Apr 10, 4-5 PM (38)
Apr 10, 5-6 PM (30)
Apr 10, 6-7 PM (11)
Apr 10, 7-8 PM (13)
Apr 10, 8-9 PM (7)
Apr 10, 9-10 PM (25)
Apr 10, 10-11 PM (47)
Apr 10, 11-12 AM (18)
Apr 11, 12-1 AM (6)
Apr 11, 1-2 AM (4)
Apr 11, 2-3 AM (2)
Apr 11, 3-4 AM (0)
Apr 11, 4-5 AM (8)
Apr 11, 5-6 AM (4)
Apr 11, 6-7 AM (5)
Apr 11, 7-8 AM (7)
Apr 11, 8-9 AM (3)
Apr 11, 9-10 AM (0)
Apr 11, 10-11 AM (10)
Apr 11, 11-12 PM (4)
Apr 11, 12-1 PM (7)
Apr 11, 1-2 PM (3)
Apr 11, 2-3 PM (7)
Apr 11, 3-4 PM (10)
Apr 11, 4-5 PM (17)
Apr 11, 5-6 PM (0)
3,203 commits this week Apr 04, 2026 - Apr 11, 2026
net-rs: contiguity walk falls back to block_cache
select_chain_once's contiguity guard called chain_tree.ancestors(last_hash)
to verify that a peer's replay chain reaches the picked common ancestor.
The walk terminates at the first block whose parent is not in chain_tree.

On_block_received inserts every fetched block into both chain_tree and
block_cache — but the chain_tree insert is skipped when the header has no
parsed info AND chain_tree doesn't already know the block (so block_no=0).
That leaves the block in block_cache without a chain_tree entry, and the
next contiguity walk terminates early, firing `fork mismatch (replay
doesn't reach ancestor)` → OrphanCandidate. With the cooldown cap this no
longer infects other peers, but individual nodes under sustained fork
load can slowly get stuck on this false mismatch.

Fix: add a hybrid walker that follows prev_hash links using chain_tree
first and block_cache as a fallback. The walk terminates at a genuine
gap (neither store has the parent) or at a genesis child (prev_hash=None)
— both distinguished via a new HybridWalk.reached_origin flag so the
genesis-reached check in select_chain_once still works.

The walk is a new private method on PraosConsensus in selection.rs:
walk_ancestors_hybrid(start_hash) -> HybridWalk.

4 new unit tests exercise:
- chain_tree-only case (back-compat with pre-fix behaviour)
- block_cache fallback (tree has tip + anchor, middle only in cache)
- gap termination (parent in neither store → reached_origin=false)
- start_only_in_cache (start block only in block_cache)

Cluster verification at p=0.2: 24/25 nodes stayed healthy for ~55 min
(vs previous build which had 4 stuck by T+60min). The one stuck node
(node-4) hit a separate mux-level ingress-overflow bug during catch-up
fetches, not the contiguity walk — tracked separately.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>