Jun 03, 12-1 PM (93)
Jun 03, 1-2 PM (28)
Jun 03, 2-3 PM (62)
Jun 03, 3-4 PM (26)
Jun 03, 4-5 PM (24)
Jun 03, 5-6 PM (23)
Jun 03, 6-7 PM (15)
Jun 03, 7-8 PM (17)
Jun 03, 8-9 PM (19)
Jun 03, 9-10 PM (9)
Jun 03, 10-11 PM (31)
Jun 03, 11-12 AM (14)
Jun 04, 12-1 AM (12)
Jun 04, 1-2 AM (4)
Jun 04, 2-3 AM (1)
Jun 04, 3-4 AM (5)
Jun 04, 4-5 AM (1)
Jun 04, 5-6 AM (0)
Jun 04, 6-7 AM (14)
Jun 04, 7-8 AM (10)
Jun 04, 8-9 AM (11)
Jun 04, 9-10 AM (19)
Jun 04, 10-11 AM (11)
Jun 04, 11-12 PM (14)
Jun 04, 12-1 PM (53)
Jun 04, 1-2 PM (39)
Jun 04, 2-3 PM (60)
Jun 04, 3-4 PM (12)
Jun 04, 4-5 PM (4)
Jun 04, 5-6 PM (7)
Jun 04, 6-7 PM (46)
Jun 04, 7-8 PM (27)
Jun 04, 8-9 PM (4)
Jun 04, 9-10 PM (2)
Jun 04, 10-11 PM (24)
Jun 04, 11-12 AM (7)
Jun 05, 12-1 AM (6)
Jun 05, 1-2 AM (8)
Jun 05, 2-3 AM (1)
Jun 05, 3-4 AM (1)
Jun 05, 4-5 AM (1)
Jun 05, 5-6 AM (5)
Jun 05, 6-7 AM (9)
Jun 05, 7-8 AM (12)
Jun 05, 8-9 AM (8)
Jun 05, 9-10 AM (11)
Jun 05, 10-11 AM (12)
Jun 05, 11-12 PM (8)
Jun 05, 12-1 PM (52)
Jun 05, 1-2 PM (61)
Jun 05, 2-3 PM (26)
Jun 05, 3-4 PM (24)
Jun 05, 4-5 PM (17)
Jun 05, 5-6 PM (7)
Jun 05, 6-7 PM (14)
Jun 05, 7-8 PM (12)
Jun 05, 8-9 PM (6)
Jun 05, 9-10 PM (2)
Jun 05, 10-11 PM (20)
Jun 05, 11-12 AM (9)
Jun 06, 12-1 AM (6)
Jun 06, 1-2 AM (0)
Jun 06, 2-3 AM (3)
Jun 06, 3-4 AM (4)
Jun 06, 4-5 AM (0)
Jun 06, 5-6 AM (24)
Jun 06, 6-7 AM (1)
Jun 06, 7-8 AM (2)
Jun 06, 8-9 AM (3)
Jun 06, 9-10 AM (0)
Jun 06, 10-11 AM (3)
Jun 06, 11-12 PM (6)
Jun 06, 12-1 PM (2)
Jun 06, 1-2 PM (2)
Jun 06, 2-3 PM (2)
Jun 06, 3-4 PM (18)
Jun 06, 4-5 PM (1)
Jun 06, 5-6 PM (6)
Jun 06, 6-7 PM (0)
Jun 06, 7-8 PM (6)
Jun 06, 8-9 PM (0)
Jun 06, 9-10 PM (1)
Jun 06, 10-11 PM (27)
Jun 06, 11-12 AM (9)
Jun 07, 12-1 AM (14)
Jun 07, 1-2 AM (2)
Jun 07, 2-3 AM (0)
Jun 07, 3-4 AM (0)
Jun 07, 4-5 AM (1)
Jun 07, 5-6 AM (1)
Jun 07, 6-7 AM (3)
Jun 07, 7-8 AM (0)
Jun 07, 8-9 AM (0)
Jun 07, 9-10 AM (1)
Jun 07, 10-11 AM (2)
Jun 07, 11-12 PM (2)
Jun 07, 12-1 PM (5)
Jun 07, 1-2 PM (35)
Jun 07, 2-3 PM (2)
Jun 07, 3-4 PM (4)
Jun 07, 4-5 PM (2)
Jun 07, 5-6 PM (4)
Jun 07, 6-7 PM (0)
Jun 07, 7-8 PM (0)
Jun 07, 8-9 PM (17)
Jun 07, 9-10 PM (1)
Jun 07, 10-11 PM (21)
Jun 07, 11-12 AM (9)
Jun 08, 12-1 AM (9)
Jun 08, 1-2 AM (5)
Jun 08, 2-3 AM (3)
Jun 08, 3-4 AM (4)
Jun 08, 4-5 AM (2)
Jun 08, 5-6 AM (9)
Jun 08, 6-7 AM (5)
Jun 08, 7-8 AM (25)
Jun 08, 8-9 AM (36)
Jun 08, 9-10 AM (40)
Jun 08, 10-11 AM (24)
Jun 08, 11-12 PM (22)
Jun 08, 12-1 PM (40)
Jun 08, 1-2 PM (48)
Jun 08, 2-3 PM (33)
Jun 08, 3-4 PM (27)
Jun 08, 4-5 PM (12)
Jun 08, 5-6 PM (23)
Jun 08, 6-7 PM (14)
Jun 08, 7-8 PM (3)
Jun 08, 8-9 PM (6)
Jun 08, 9-10 PM (19)
Jun 08, 10-11 PM (29)
Jun 08, 11-12 AM (8)
Jun 09, 12-1 AM (5)
Jun 09, 1-2 AM (3)
Jun 09, 2-3 AM (1)
Jun 09, 3-4 AM (3)
Jun 09, 4-5 AM (26)
Jun 09, 5-6 AM (5)
Jun 09, 6-7 AM (23)
Jun 09, 7-8 AM (50)
Jun 09, 8-9 AM (35)
Jun 09, 9-10 AM (45)
Jun 09, 10-11 AM (51)
Jun 09, 11-12 PM (46)
Jun 09, 12-1 PM (86)
Jun 09, 1-2 PM (67)
Jun 09, 2-3 PM (36)
Jun 09, 3-4 PM (38)
Jun 09, 4-5 PM (16)
Jun 09, 5-6 PM (17)
Jun 09, 6-7 PM (18)
Jun 09, 7-8 PM (19)
Jun 09, 8-9 PM (16)
Jun 09, 9-10 PM (16)
Jun 09, 10-11 PM (28)
Jun 09, 11-12 AM (10)
Jun 10, 12-1 AM (11)
Jun 10, 1-2 AM (16)
Jun 10, 2-3 AM (11)
Jun 10, 3-4 AM (7)
Jun 10, 4-5 AM (5)
Jun 10, 5-6 AM (2)
Jun 10, 6-7 AM (46)
Jun 10, 7-8 AM (82)
Jun 10, 8-9 AM (18)
Jun 10, 9-10 AM (59)
Jun 10, 10-11 AM (45)
Jun 10, 11-12 PM (53)
Jun 10, 12-1 PM (0)
2,792 commits this week Jun 03, 2026 - Jun 10, 2026
praos: fix stuck-rollup parent check + clean up per-peer state
- maybe_emit_stuck_warning was counting any peer-chain parent missing from
  adopted_ancestors as "unreachable", but the diagnostic doc says "neither
  in chain_tree nor block_cache".  A peer chain that forks off into a
  branch we hold but haven't adopted would false-positive the WARN.  Check
  the whole chain_tree (every fork we know about) instead.

- record_peer_disconnected wasn't removing the per-peer last_gap_warning_at
  throttle entry, so the map grew without bound under reconnect churn if
  PeerIds are monotonically assigned.  Same lifecycle as the orphan
  cooldown that already gets cleared here.

- Drop a duplicate `#[allow(clippy::too_many_arguments)]` on
  on_tip_advanced (one was enough).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
behaviour: short-circuit DropInboundPeers at probability >= 1.0
`u64 / u64::MAX as f64` lands in [0, 1] inclusive: `u64::MAX as f64` rounds
to 2^64, and a hash equal to u64::MAX yields a draw of exactly 1.0.  The
`draw < probability` test then refused to drop on that one-in-2^64 hash
even when the operator asked for "always drop" semantics with
probability=1.0.

Mirror the `probability <= 0.0` short-circuit at the top of the function
so probability=1.0 unconditionally returns true, and fix the comment to
say [0, 1] rather than [0, 1).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
sim-core: clear pre-existing clippy 1.92 lints
- tcp_connection.rs: `mem::replace(_, Default::default())` → `mem::take`.
- config.rs: `for (_, spec) in &out` → `for spec in out.values()`.
- network/connection.rs: allow `clippy::large_enum_variant` on
  `ConnectionKind` — the two variants share a uniform interface and
  Box-ing the TCP variant would add an indirection on every connection
  access in the sim hot path.  Also allow `clippy::items_after_test_module`
  on the file's `mod tests` block; `ConnectionKind` legitimately lives
  after it and moving the 400-line test module to the file end would be
  pure churn.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
net-cluster: clear pre-existing clippy 1.92 lints
- aggregator.rs: drop `len.clone()` on a `usize` (Copy).
- server.rs: allow `clippy::too_many_arguments` on `start()`; the nine
  args are all distinct top-level cluster wiring (port, channels, shared
  state) and grouping them into a config struct would be churn for
  marginal benefit.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
shared-consensus: clear pre-existing clippy 1.92 lints
- t22.rs: dedent inline list items to 2 spaces (overindented); drop the
  redundant `let decision = …; decision` binding (`needless_return`).
- praos.rs: rewrap PraosStateSizes equivocation_bytes_estimate doc so the
  list bullet sits flush with the prose.
- behaviour/selection.rs: replace `out.get(&2).is_none()` with
  `!out.contains_key(&2)` (`unnecessary_get_then_check`).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
praos: evict abandoned suffix from block cache on force_rollback
`force_rollback` re-anchored chain state and pruned the chain tree
suffix, but left the abandoned blocks in `block_cache` / `validated` /
`in_flight_validation`.  The dedup at the top of `on_block_received`
short-circuits when a hash is in any of those, so a peer re-offering an
abandoned block after a deliberate self-reorg was silently dropped —
`chain_tree` never re-acquired it and the node was pinned on its dead
fork, defeating post-chaos recovery.

Mirror the k-prune retention pattern from `on_block_applied` in the
opposite direction: retain `block_cache` to entries with `block_no <=
target_bn`, then drop `validated` / `in_flight_validation` /
`header_first_seen` to hashes still in `block_cache`.

Test extended to assert the suffix is gone from cache + validated.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
feat(grpc): expose configurable HTTP/2 transport tuning via GrpcConfig
The gRPC server built its tonic transport with no HTTP/2 tuning, inheriting
hyper/h2 defaults (64 KiB per-stream flow-control window, adaptive windowing
off) with no way for operators to influence them.

Add optional HTTP/2 fields to GrpcConfig (http2_adaptive_window,
http2_initial_stream_window_size, http2_initial_connection_window_size,
http2_max_frame_size, http2_max_concurrent_streams), all backwards-compatible
optional fields, and plumb them into the server builder via a dedicated
apply_http2_tuning helper.

Adaptive windowing defaults to on. NOTE: this is a behavior change for existing
deployments (windows now auto-size to the BDP instead of being pinned at
64 KiB). Adaptive and explicit fixed windows are mutually exclusive in hyper;
setting both logs a warning rather than silently dropping the fixed sizes.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
praos: surface upstream gappy ChainSync via two complementary WARNs
Diagnosing a wedged catch-up against the public Leios dev relay
required grepping across the orphan / fork-mismatch INFO traffic and
inferring the cause from cache state.  Two new WARNs hand the
diagnosis directly to an operator skimming logs:

- **ChainSync ingress contiguity check** in `record_peer_tip`: when an
  arriving header's `prev_hash` doesn't match the previously-announced
  one's hash, log the (block_no, hash) pair on each side and the
  implied skipped-block count.  Throttled per peer
  (`GAP_WARNING_INTERVAL = 10 s`) so a sustained non-contiguous forward
  doesn't flood the log.  This is the direct signal — the WARN fires
  the moment upstream commits the offence.

- **Stuck-validation rollup** in `retry_select_chain`: when validation
  has been frozen for `STUCK_THRESHOLD = 30 s` and some peer offers a
  strictly-better tip, emit one rollup line summarising stuck duration,
  adopted vs best-peer block_no, the count of entries in that peer's
  replay whose parent_hash we don't have locally, and the peer-chain
  size.  Throttled to one fire per `STUCK_WARNING_INTERVAL = 60 s`.
  This covers the general "stuck for any reason" case and stays
  informative when the ingress check has gone quiet under its
  per-peer cooldown.

Both lines were verified against the dev relay: ingress fires within
~30 s of catch-up reaching the wedge boundary (with the exact missing
block hash prefix in the message), and the rollup fires 30 s later
with `unreachable_parent_hashes > 0`, both throttled correctly under
sustained wedge load.

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