Invariants / DC-NODE-37

DC-NODE-37

DC derived enforced

Fork-switch never-abandon (PHASE4-N-AO, SELECT primary invariant; the H-1 class at fork-choice scale). When select_best_chain picks a winner that forks BELOW Ade's current durable tip, Ade MUST NEVER commit a rollback of its current durable chain until the replacement branch's bodies are FETCHED, LINKED, and VALIDATED as a complete candidate branch (block-fetched via BlockFetch RequestRange anchor->tip from the winning peer; the fork anchor canonically bound to Ade's durable STORED slot+hash per DC-NODE-29, never peer-supplied / mixed authority). A failed / lying / Byzantine / incomplete replacement branch leaves ChainDb + LedgerState + PraosChainDepState UNCHANGED. Adoption then proceeds ONLY via the already-enforced authorities: RolledBack(fork_anchor) through materialize_rolled_back_state (CN-STORE-07) + the lockstep receive reducer (DC-CONS-20), then ChainSelected(body) x N through pump_block (DC-NODE-05/12) -- a fork-choice win is provisional, durably adopted ONLY when its BODIES validate+apply (no header-only tip advance). The reselection is recorded as the durable append-only WalEntry::RollBack{ForkChoiceWin} and is replay-equivalent (DC-NODE-27, extended) -- NOT a second rollback implementation. No forge across the pending reselection (DC-NODE-28).

Source

docs/planning/phase4-select-multicandidate-fork-choice-invariants.md (FC-5/FC-6/FC-7) + docs/clusters/PHASE4-N-AO/cluster.md

Introduced in
PHASE4-N-AO

Enforcement trace

Tests 7

  • empty_branch_fails_closed_before_any_apply
  • null_source_serves_nothing
  • fork_switch_win_adopts_via_rolledback_then_chainselected
  • body_hash_mismatch_leaves_chain_unchanged
  • broken_parent_link_leaves_chain_unchanged
  • selected_peer_missing_body_leaves_chain_unchanged_fence_held
  • proof_failure_holds_fence_then_resolves_when_caught_up

Cross-references

Attack rationale

A fork-choice win over peer-supplied/unvalidated data that triggered a rollback BEFORE validating the replacement would let a lying/Byzantine peer induce Ade to abandon a valid durable chain for one that then fails pump_block -- the H-1 truncation class (PHASE4-N-AI AI-S6 / DC-NODE-29) generalized from a peer RollBackward to an Ade-initiated reselection. Never-abandon-until-validated + the DC-NODE-29 stored-slot+hash anchor binding close it.

Evidence notes

Declared at PHASE4-N-AO cluster-doc; enforced at S4 close (CE-AO-4) via ci_check_fork_switch_never_abandons.sh + the reused ci_check_rollback_target_canonical_binding.sh + ci_check_live_fork_choice_apply.sh, including a negative-corpus test (invalid/incomplete winner leaves the durable chain unchanged). The security-critical slice -- its own focused per-cluster security review (S4 is the expected HIGH surface). Resolves OQ-AO-3 (selected-range fetch driver/peer/anchor/failure).