Invariants / DC-SERVEMEM-01

DC-SERVEMEM-01

DC derived enforced

Peer-driven serve range work is bounded. The --mode node serve path must not materialize an unbounded chain range, perform per-block full-index scans, or read more than MAX_SERVE_RANGE_BLOCKS blocks for a single peer request. Oversized ranges fail closed before unbounded storage/CPU work. The cap is a defensive implementation bound, not a Cardano semantic parameter, and cannot be disabled at runtime.

Source

docs/clusters/PHASE4-N-AA/cluster.md; PHASE4-N-U cross-slice security review (MEDIUM finding)

Introduced in
PHASE4-N-AA

Enforcement trace

Tests 12

  • range_bytes_capped_returns_at_most_max
  • range_bytes_capped_within_cap_not_truncated
  • range_bytes_capped_respects_bounds
  • range_bytes_capped_bytes_byte_identical
  • range_bytes_capped_inverted_range_is_empty
  • last_block_bytes_returns_highest_slot
  • serve_range_over_cap_fails_closed
  • serve_range_empty_window_is_empty_not_capexceeded
  • serve_range_undecodable_in_range_fails_closed
  • serve_range_inverted_range_fails_closed
  • served_view_projects_durable_chain
  • follower_fetches_coherent_history_incl_ingested_predecessor

Cross-references

Strengthened in

Evidence notes

Declared at cluster scoping (2026-06-05, user-confirmed). ENFORCED at the PHASE4-N-AA S2 close. Pre-RO-LIVE hardening item 1; closes the PHASE4-N-U cross-slice security review MEDIUM finding (peer-driven serve resource amplification). The serve-side analog of DC-LIVEMEM-01 (receive-side bounded memory). MAX_SERVE_RANGE_BLOCKS = 256 (symmetric with MAX_WIRE_PUMP_LOOKAHEAD; fixed/closed/non-configurable). No schema change: S1 added bounded hash-free slot-ordered ChainDb read primitives (range_bytes_capped + last_block_bytes; 5 contract tests run against BOTH PersistentChainDb + InMemoryChainDb). S2 switched ChainDbServedSource range_bytes/next_after/tip to those primitives, applied the cap (CapExceeded -> empty -> reducer NoBlocks), and derives each block's hash from its bytes via the single BLUE decode_block authority (no HASH_BY_SLOT index, no second hash authority). A closed ServeRangeOutcome enum distinguishes Served/Empty/CapExceeded/ReadError internally (all non-Served -> wire NoBlocks). Gate ci_check_serve_range_bounded.sh is non-vacuous: pre-S2 HEAD had 2 iter_from_slot + 1 chaindb.tip() serve calls (Guards 2/3 would fire); S2 has 0. Within-cap real-bytes serving (Served, byte-identical, derived hash == stored) is covered by the ade_node serve integration tests (real forged blocks). The trusted recovery/rollback iter_from_slot internals are OUT OF SCOPE (doc-fenced, not peer-driven).