Invariants / DC-CINPUT-05

DC-CINPUT-05

DC true enforced

Venue epoch geometry is DURABLE REPLAY AUTHORITY. A recovered store MUST replay using the epoch geometry (epoch_start_slot + epoch_length_slots) persisted with the seed/import that CREATED the store -- NEVER re-derived from whatever genesis/CLI a restart happens to supply. The seed-epoch sidecar (SeedEpochConsensusInputs, schema v4 since ECA-2-pre) carries the venue epoch_start_slot + epoch_length_slots, populated at import from the cardano-cli-reported epoch window (epoch_length = epoch_end_slot - epoch_start_slot + 1: preview 86400, preprod 432000, ...). warm_start_recovery + the live-follow/forge era-schedule rebuild make_node_schedule from THOSE durable values; there is NO hardcoded epoch length, NO venue-name switch, NO implicit default, NO hidden fallback to 432000. A restart-supplied --genesis-file is ONLY a consistency check (RestartGenesisGeometryMismatch fail-closed on disagreement), never a re-derivation source. An old v2 sidecar (no geometry) fails closed at decode (UnknownVersion) -- a store must be re-seeded, never silently re-geometried.

Source

docs/active/warmstart-era-schedule-venue-slice.md (live C2-PREVIEW forge SlotBeforeSystemStart finding: warm-start hardcoded the preprod epoch length 432000, breaking preview replay)

Introduced in
WARMSTART-ERA-SCHEDULE-VENUE

Enforcement trace

Tests 5

  • warm_start_schedule_locates_block_by_venue_geometry_not_hardcoded_432000
  • merge_persists_venue_epoch_geometry_preview_and_preprod
  • restart_genesis_epoch_length_mismatch_fails_closed
  • seed_epoch_consensus_inputs_round_trips_byte_identical
  • seed_cinput_decode_rejects_unknown_version

CI 0

no CI script — gap

Cross-references

Strengthened in

Attack rationale

The pre-fix warm-start hardcoded the preprod epoch length (432000) in make_node_schedule + recomputed the era start as epoch_no*432000. On preview (epoch length 86400) the recovered schedule placed epoch 1331's start at 574992000 -- AFTER the followed block at slot 115030409 -- so forward-replay rejected the block SlotBeforeSystemStart and the forge could not recover its own durably-admitted chain. More dangerously, re-deriving epoch geometry from the restart CLI would let an operator (or an attacker with CLI access) 'repair' or REWRITE a store's replay behavior by passing a different venue's genesis -- a durable-authority split where the same store + WAL + checkpoints replays differently depending on the restart args. Persisting the geometry in the sidecar makes the durable store self-describing and replay-deterministic (T-REC-05); the CLI is demoted to a fail-closed consistency check.

Evidence notes

WARMSTART-ERA-SCHEDULE-VENUE (2026-06-17). Surfaced when the DURABLE-ADMISSION-BYTES forge resume got PAST the block-bytes stage (durable-bytes fix proven) and reached forward-replay of the followed preview block at slot 115030409, failing Materialize(ReplayFailedAt{ Header(HFC(SlotBeforeSystemStart{ first_era_start: 574992000 = 1331*432000 })) }). The admission path was already venue-correct (it built its schedule from the canonical bundle's epoch_start_slot); only the WARM-START path recomputed with the hardcoded preprod 432000. Fix: persist the venue geometry (epoch_start_slot + epoch_length_slots) in the sidecar at import (schema v2->v3, old sidecars fail closed UnknownVersion), and rebuild every recovered/import era-schedule from those durable values -- make_node_schedule takes epoch_length_slots explicitly with NO hardcoded constant. The restart --genesis-file is a fail-closed consistency check only (RestartGenesisGeometryMismatch). Proven: warm_start_schedule_locates_block_by_venue_geometry_not_hardcoded_432000 (preview 86400 LOCATES slot 115030409; the wrong preprod-length geometry rejects it SlotBeforeSystemStart@574992000 -- the EXACT live failure; preprod 432000 also locates -- the prior warm-start tests all used snapshot-at-tip degenerate replay and never called locate(), which is why they missed the bug); merge_persists_venue_epoch_geometry_preview_and_preprod (both venues + degenerate-window fail-closed); restart_genesis_epoch_length_mismatch_fails_closed. 68 test binaries green across ade_ledger + ade_runtime + ade_node. The live forge re-run (fresh admission writes a v3 preview-geometry sidecar -> warm-start builds the 86400 schedule -> forward-replay accepts the followed block) is the C2-PREVIEW-BA02 forge resume.