DC-EVIEW-07
DC derived enforcedThe bound, immutable EpochConsensusView (S3e). EpochConsensusView::bind emits the compact next-epoch consensus view from the finalized snapshot (S3d), BOUND to all of: network_magic, era, epoch, source_point (slot+hash), checkpoint_commitment (the reduced-UTxO checkpoint fingerprint, DC-EVIEW-04/04b), nonce (eta0), snapshot_phase (DC-EVIEW-06), plus the stake distribution payload (stake_by_pool + total_active_stake, S3c). The canonical_hash (blake2b over the canonical encoding of EVERY binding + the stake distribution, fixed field order, BTreeMap in sorted PoolId order) is the view's self-describing identity. A view is INERT -- it may NOT be activated -- unless ALL bindings match the activation context AND verify_canonical_hash() holds (recompute == stored): matches(&ViewBindings) checks both. The canonical encoding round-trips (canonical_bytes -> the same hash), so a WAL-recorded view replays byte-identically (the replay-equivalence the activation relies on). Pure, total, deterministic. ECA-0b strengthens the view to be LEADERSHIP-COMPLETE: the canonical_hash + canonical encoding now ALSO cover pool_vrf_keyhashes (the effective per-pool VRF) + protocol_params_commitment (the FULL consensus-profile commitment), and matches() additionally requires is_leadership_complete() (equal stake/VRF key sets) + the protocol-params commitment, so an incomplete or wrong-profile view is INERT (DC-EVIEW-12). OBSERVE-ONLY: the rewire into the live boundary authority, the WAL activation variant, and feeding the view to live leader election are the activation slice (DC-EVIEW-08); no live-path change.
- Source
docs/clusters/EPOCH-CONSENSUS-VIEW/SLICE-3-scope.md (S3e); docs/clusters/EPOCH-CONSENSUS-VIEW/EPOCH-CONSENSUS-VIEW-design-analysis.md (Deliverable 5; the bound-activation prohibition)
- Introduced in
- EPOCH-CONSENSUS-VIEW-S3e
Enforcement trace
Tests 6
- bind_is_deterministic_and_self_verifies
- matches_exact_bindings_and_rejects_mismatch
- canonical_hash_is_binding_sensitive
- canonical_bytes_reproduce_the_hash
- tampered_view_fails_verification
- leadership_complete_required_for_matches
Cross-references
Strengthened in
Attack rationale
The view must not (a) be activatable while unbound or mis-bound -- matches() requires ALL of network/era/epoch/point/checkpoint/nonce/phase to equal the activation context, so a view from the wrong fork (point), wrong epoch, wrong nonce, or wrong snapshot phase is INERT (the design record's bound-activation prohibition: never combine a correct pool distribution with the wrong fork/epoch/nonce); (b) be silently tampered -- the canonical_hash is recomputed over every field in verify_canonical_hash, so any mutated binding or stake value (without rebinding) fails verification and is inert (tampered_view_fails_verification); (c) be non-deterministic / non-replayable -- the canonical encoding is a fixed field order with the BTreeMap in sorted PoolId order, so the hash is deterministic and the bytes round-trip (canonical_bytes -> the same hash), which a WAL activation record requires for replay-equivalence; the hash is binding-sensitive (any binding or stake change changes the identity). Observe-only: no live decision consumes the view until DC-EVIEW-08.
Evidence notes
Introduced at EPOCH-CONSENSUS-VIEW S3e (2026-06-20), the last LOGIC sub-slice. The EpochConsensusView TYPE was absent; modelled on SeedEpochConsensusInputs (the bound-record + canonical encode/decode pattern). Pure BLUE, observe-only. 5 hermetic tests (deterministic bind + self-verify, matches-exact / rejects-mismatch, canonical-hash binding-sensitive across network/stake/phase, canonical-bytes reproduce the hash, tamper fails verification + makes the view inert). This completes the S3a-S3e LOGIC chain: classify (S3a/Slice-2) -> reduced checkpoint (S3b-1) -> windowed advance (S3b-2) -> per-pool aggregate (S3c) -> snapshot + k-stability (S3d) -> bound immutable view (S3e). cargo test -p ade_ledger green. NO live wiring, NO leader/header use, NO track_utxo=true on live. REMAINING: DC-EVIEW-08 -- the ONLY activation slice, and the ONLY live-path change: rewire apply_epoch_boundary's new_mark stub (rules.rs:1098) to consume the S3c aggregate, consult the S3d stability gate, add a distinct WAL activation variant (preserving the bootstrap single-import), add the new ledger_view rebind seam in run_relay_loop_with_sched (node_sync.rs:1544/1560, which today borrows ledger_view immutably with no rebind), and feed the bound view to live leader election -- gated on TWO LIVE proofs: the differential oracle (stake_by_pool == cardano-cli stake-snapshot stakeSet, DC-EVIEW-05 open_obligation) + the leadership-schedule live proof (ADE1's derived schedule across a real boundary matches cardano-cli leadership-schedule + the forge produces on the new epoch). Those are NOT pure coding tasks (a live cardano-node run) -- the honest stopping point for 'finish the cluster'.