Invariants / DC-EPOCH-09

DC-EPOCH-09

DC derived enforced

The activation candidate is derived ONLY from a validated source window, bound to the TARGET-epoch context (S3f-4d-2). derive_candidate drives the reduced checkpoint + cert state forward over the (DC-EPOCH-08 validated) window's blocks (DC-EVIEW-10 drive_window_aggregate) -> per-pool stake, then binds it (DC-EVIEW-07 EpochConsensusView::bind) with the window's TARGET epoch (the Mark->Set lag, NEVER source_epoch), the window-end Point{source_window_end, lineage_pin}, the FINALIZED checkpoint commitment (the window drive clears the completeness marker; finalize re-marks + returns the commitment), the supplied network/nonce, and the window's snapshot_phase. Candidate binding happens BEFORE WAL activation; the candidate's identity is exactly what the WAL record (DC-EPOCH-04 activation_record_for) commits to and recovery (DC-EPOCH-06 recover_active_view) reproduces. The candidate contents are a pure function of (checkpoint, bootstrap cert state, the window's blocks, era, network, nonce) -- no peer/network read, wall-clock, or async side channel. A drive/checkpoint failure is fail-closed CandidateDeriveError -- no partial candidate reaches the predicate.

Source

docs/clusters/EPOCH-CONSENSUS-VIEW/SLICE-3f4d-live-flip.md (S3f-4d-2)

Introduced in
EPOCH-CONSENSUS-VIEW-S3f-4d-2

Enforcement trace

Tests 1

  • derive_candidate_binds_target_epoch_and_round_trips_through_recovery

Cross-references

Attack rationale

The candidate must be bound to the epoch whose leadership reads it (target_epoch), NOT the source epoch -- derive_candidate binds window.target_epoch (CI-pinned; binding source_epoch is rejected by the gate), so the Mark->Set off-by-one cannot leak into the candidate identity. The checkpoint commitment is the FINALIZED window-end state (the drive clears the marker; a stale/incomplete commitment is impossible because finalize re-marks + returns it). Binding happens before WAL activation, so the durable record commits to the exact candidate. The contents are pure (no peer/clock/async), so the candidate is replay-identical -- recovery re-derives the SAME view (proven: the candidate round-trips through activation_record_for + recover_active_view to Promoted). A drive/checkpoint error yields no candidate (fail closed), never a partial one.

Evidence notes

Introduced at EPOCH-CONSENSUS-VIEW S3f-4d-2 (2026-06-21), the second sub-slice of the live flip. The candidate derivation composing the proven driver (DC-EVIEW-10) + bind (DC-EVIEW-07) -- RED (drives the redb checkpoint) but pure contents. 1 hermetic proof over the REAL conway block: the candidate is bound to the target epoch + the window context AND round-trips through the WAL activation record + recovery (tying S3f-4d-2 to S3f-4a/c). ci_check_eview_candidate.sh; cargo test -p ade_node --lib (epoch_candidate 1) green. Next: S3f-4d-3 (the live orchestration + the flip) -- the ONLY remaining sub-slice, gated on the boundary-aligned stake oracle + the leadership-schedule proof.