Add experimental `AnyNewEpochState` parameterised on `Era` to eliminate partial `sbeToEra` conversions
## Problem
`AnyNewEpochState` in `Cardano.Api.LedgerState` carries a `ShelleyBasedEra era` witness:
```haskell
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:
```haskell
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](https://github.com/IntersectMBO/cardano-node/pull/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`:
```haskell
-- | 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.
0 条评论