DC-EVIEW-03
DC derived enforcedEra-parameterized pointer decoding + pre-Conway resolution, matching cardano-ledger EXACTLY (the wire authority -- CIP-19 is silent on canonicality, so the cardano-ledger implementation is the sole rule). A pointer address (header type 4/5) is header(1) | payment(28) | 3 base-128 big-endian varints (slot, txIx, certIx); the stored shape is (u32 slot, u16 txIx, u16 certIx). decode_pointer_address / decode_pointer_tail take a TYPED CardanoEra bound to the block (era_schedule.locate(slot).era -- never inferred from bytes / config / clock) and are ERA-GATED: (a) Conway (PV9+, era>=CardanoEra::Conway) -- STRICT: width-bounded varints (decode_width_bounded: at most ceil(bits/7) groups; a continuation past the max group count or a most-significant group whose surplus data bits exceed the field width is OverWidth), and trailing bytes are rejected. (b) Babbage (era==CardanoEra::Babbage) -- NORMALIZE: each coord decodes as a WRAPPING u64 (decode_u64_wrapping, bits past 64 dropped), then mkPtrNormalized clamps the WHOLE 3-tuple to (0,0,0) if ANY coord overflows its field width (not per-field masking, not wrapping the field); trailing bytes rejected. (c) <=Alonzo (era<CardanoEra::Babbage) -- NORMALIZE + crop trailing. In EVERY era a bounded leading-zero / non-minimal encoding (e.g. [0x80,0x01]==[0x01]) is ACCEPTED (the strict check is a WIDTH check, not a minimal-form check) -- reject-all-non-canonical would FALSE-REJECT txs cardano-node accepts. The parser exists in every era (pointers stay spendable post-Conway); only its strictness is era-gated; stake retirement (PV9 -> Null) is the SEPARATE Slice-2 rule. RESOLUTION (PointerMap, ade_ledger): a decoded Ptr resolves to the credential REGISTERED by the StakeRegistration cert at exactly (slot,txIx,certIx); pre-Conway only; fail-closed -- an unregistered coordinate -> None (no stake, never a fabricated credential), a duplicate coordinate -> rejected (no overwrite). Total, pure, deterministic. No live wiring, no aggregation.
- Source
docs/clusters/EPOCH-CONSENSUS-VIEW/SLICE-3a-pointer-decode-resolution.md; docs/clusters/EPOCH-CONSENSUS-VIEW/SLICE-3-scope.md
- Introduced in
- EPOCH-CONSENSUS-VIEW-S3a
Enforcement trace
Code
Tests 20
- bounded_leading_zero_alias_accepted_all_eras
- conway_decodes_in_range
- conway_rejects_txix_over_u16
- conway_rejects_width_overflow_within_max_groups
- conway_rejects_slot_over_u32
- conway_rejects_trailing_bytes
- conway_accepts_max_width_boundary
- babbage_normalizes_overflow_to_zero_tuple
- babbage_in_range_kept_unmodified
- babbage_rejects_trailing_bytes
- alonzo_normalizes_overflow_to_zero_tuple
- alonzo_crops_trailing_bytes
- truncated_varint_is_error
- decode_pointer_address_validates_header_and_tail
- decode_is_deterministic
- resolves_a_registered_pointer
- unregistered_pointer_is_none_fail_closed
- duplicate_position_is_rejected_fail_closed
- distinct_coordinates_resolve_independently
- resolution_is_deterministic
Cross-references
Attack rationale
The decoder must not (a) substitute a cleaner parser rule that diverges from network semantics -- bounded leading-zero aliasing is ACCEPTED in every era (a width check, not a minimal-form check); reject-all-non-canonical would FALSE-REJECT a tx cardano-node accepts (a consensus split); (b) infer the era from anything but a bound consensus context (a typed CardanoEra param), so the SAME bytes decode identically on every node; (c) mask/truncate/wrap an over-width coordinate -- Conway hard-rejects it, Babbage/<=Alonzo clamp the WHOLE 3-tuple to (0,0,0) (mkPtrNormalized), never a per-field silent wrap that would mis-resolve; (d) accept trailing bytes where the ledger rejects them (Conway/Babbage) -- a lenient extra byte that cardano-node rejects is a split; or (e) fabricate a credential for an unresolvable or duplicate pointer -- resolution is fail-closed (None / reject), never a guess. The era-strictness boundary is at protocol major 9 (Conway), confirmed stable on the pinned cardano-node 11.0.1 (still Conway, PV9-11).
Evidence notes
Introduced at EPOCH-CONSENSUS-VIEW S3a (2026-06-20), the first sub-slice of Slice 3, scoped + implemented ALONE (the varint/PV behavior is too load-bearing to bundle with materialization or aggregation). Grounded vs cardano-ledger master + its tests (Address.hs decodePtr/decodePtrLenient, Credential.hs mkPtrNormalized, BaseTypes.hs integralToBounded, AddressSpec.hs propDecompactErrors/propDecompactAddrWithJunk/RoundTrip-invalid): the strict check is a WIDTH check not a minimal-form check; Babbage normalizes by clamping the whole 3-tuple to zero; the strict-vs-normalize boundary is protocol major 9 (Conway), NOT 7 (Babbage tightened only trailing-bytes). cardano-node 11.0.1 keeps the ledger in Conway (PV9-11 all strict; Preview on PV11 since 2026-05-08), so the strict decoder is the live-relevant path. 20 hermetic tests (15 decoder: alias-accepted-all-eras, Conway in-range/width-reject x3/trailing-reject/max-width-boundary, Babbage normalize/in-range/trailing-reject, Alonzo normalize/crop-trailing, truncation, address-header validation, determinism; 5 resolution: resolve-registered, unregistered-None, duplicate-rejected, distinct-coords, determinism). cargo test -p ade_codec + -p ade_ledger green. This REPLACES Slice-2's stake_ref::decode_varint (which diverges from both regimes -- rejects >u64) for the S3c resolution path; Slice-2's classifier stays frozen (pre-Conway, resolves nothing). Decoder home = ade_codec::address (typed CardanoEra, reachable by tx_validity + S3c); resolution = ade_ledger (yields StakeCredential, needs the map). The PointerMap is POPULATED by S3b (windowed cert accumulation at each StakeRegistration's (slot,txIx,certIx) position); S3a is the type + algorithm, tested against a synthetic map. NO live wiring, NO aggregation, NO snapshot/emission, NO track_utxo, NO leader/header use (S3b-e + the DC-EVIEW-08 activation). At S3b/S3c, cross-check the golden hex vectors against cardano-ledger AddressSpec.hs and pin the exact ProtVer constants from 11.0.1's cardano-ledger dep.