Skip to content

Add experimental AnyNewEpochState parameterised on Era to eliminate partial sbeToEra conversions #1190

@carbolymer

Description

@carbolymer

Problem

AnyNewEpochState in Cardano.Api.LedgerState carries a ShelleyBasedEra era witness:

data AnyNewEpochState where
  AnyNewEpochState
    :: ShelleyBasedEra era
    -> ShelleyAPI.NewEpochState (ShelleyLedgerEra era)
    -> Ledger.LedgerTables ...
    -> AnyNewEpochState

Consumers in cardano-testnet that need EraCommonConstraints (via obtainCommonConstraints) must first convert ShelleyBasedEra era to Exp.Era era using sbeToEra. Because Exp.Era only inhabits Conway and Dijkstra, sbeToEra is partial for pre-Conway eras, forcing every call site to use a helper like:

unsafeEraFromSbe :: HasCallStack => ShelleyBasedEra era -> Era era
unsafeEraFromSbe = withFrozenCallStack $
  either (error . prettyShow . prettyError) id . sbeToEra

This appears in EpochStateProcessing.hs and is used across multiple modules (Components/Query.hs, Runtime.hs, Process/Cli/SPO.hs). Every pattern match on AnyNewEpochState in testnet code repeats the same partial conversion.

See cardano-node PR #6551 for context and the review discussion that motivated this issue.

Constraint

foldEpochState replays the chain from genesis via a pipelined chain-sync client. The callback receives blocks from all eras (Shelley through Dijkstra), so the existing AnyNewEpochState with ShelleyBasedEra must remain available for consumers that need full-era coverage (e.g. cardano-cli debug log-epoch-state).

Proposal

Add a new existential type in Cardano.Api.Experimental that carries an Era era witness instead of ShelleyBasedEra era:

-- | Like 'AnyNewEpochState' but restricted to experimental (Conway+) eras,
-- so that 'obtainCommonConstraints' is always total.
data AnyNewEpochState where
  AnyNewEpochState
    :: Era era
    -> ShelleyAPI.NewEpochState (ShelleyLedgerEra era)
    -> Ledger.LedgerTables ...
    -> AnyNewEpochState

Optionally, provide one or both of:

  1. A conversion function from the legacy type to Maybe the experimental type (fails for pre-Conway eras).
  2. A wrapper around foldEpochState that silently skips pre-Conway blocks and delivers only Exp.AnyNewEpochState to the callback, for consumers that only care about Conway+ governance state.

Migration path

  • cardano-testnet: switch to the experimental AnyNewEpochState. All unsafeEraFromSbe calls and the helper itself can be removed; obtainCommonConstraints works directly on the Era witness.
  • cardano-cli (debug log-epoch-state): continues using the legacy AnyNewEpochState with ShelleyBasedEra, unaffected by this change.

Alternatives considered

  • Make sbeToEra total: not possible without adding pre-Conway constructors to Era, which defeats the purpose of the experimental API.
  • Keep the status quo: every new testnet module repeats the same partial conversion boilerplate, and error calls in test infrastructure are fragile.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions