cardano-ledger-read guide
cardano-ledger-read is a read-only projection layer over the
cardano-ledger-* packages. It gives every on-chain concept a uniform
era index and accessors to read it. It does not build, balance,
sign, or submit transactions.
Repository map
| Path |
Purpose |
src/Cardano/Read/Ledger/Eras/KnownEras.hs |
KnownEras type-level list, Era GADT singleton, IsEra/theEra, era aliases, indexOfEra |
src/Cardano/Read/Ledger/Eras/EraValue.hs |
EraValue existential, applyEraFun, applyEraFunValue, getEra, parseEraIndex, eraValueSerialize |
src/Cardano/Read/Ledger/Eras.hs |
Re-exports KnownEras |
src/Cardano/Read/Ledger/Block/Block.hs |
ConsensusBlock, Block newtype, fromConsensusBlock, toConsensusBlock |
src/Cardano/Read/Ledger/Block/BHeader.hs |
BHeader, getEraBHeader |
src/Cardano/Read/Ledger/Block/BlockNo.hs |
BlockNo, getEraBlockNo, prettyBlockNo |
src/Cardano/Read/Ledger/Block/SlotNo.hs |
SlotNo, getEraSlotNo, from/toLedgerSlotNo, prettySlotNo |
src/Cardano/Read/Ledger/Block/HeaderHash.hs |
getEraHeaderHash, getRawHeaderHash, getEraPrevHeaderHash |
src/Cardano/Read/Ledger/Block/Txs.hs |
getEraTransactions :: Block era -> [Tx era] |
src/Cardano/Read/Ledger/Block/Gen/ |
Test block generators (mkBlockEra, BlockParameters) |
src/Cardano/Read/Ledger/Tx/Tx.hs |
Tx newtype, TxT type family |
src/Cardano/Read/Ledger/Tx/* |
Per-component accessors (see below) |
src/Cardano/Read/Ledger/{Address,Value,Hash,PParams}.hs |
Common types |
test/ |
hspec unit tests + per-era example transactions |
docs/ |
mkdocs site (index.md, architecture.md, api.md) |
.specify/memory/constitution.md |
Binding architectural rules |
The Tx/* accessor modules each export an era-indexed newtype wrapper
and a getEra<Component> function: Inputs/getEraInputs,
Outputs/getEraOutputs, Output/getEraValue,
CollateralInputs/getEraCollateralInputs,
CollateralOutputs/getEraCollateralOutputs,
ReferenceInputs/getEraReferenceInputs, Fee/getEraFee,
Validity/getEraValidity, Metadata/getEraMetadata,
Certificates/getEraCertificates, Withdrawals/getEraWithdrawals,
Mint/getEraMint, Witnesses/getEraWitnesses,
ScriptValidity/getEraScriptValidity, Integrity/getEraIntegrity,
ExtraSigs/getEraExtraSigs, TxId/getEraTxId. CBOR lives in
Tx/CBOR.hs (serializeTx, deserializeTx); Tx/Hash.hs has
getEraTxHash; Tx/Eras.hs has the onTx helper.
Build, test, run
Everything runs in the Nix dev shell. Prefer just recipes:
nix develop # haskell.nix shell: cabal, GHC 9.12, just
just build # cabal build all --enable-tests -O0
just test # cabal test all --enable-tests -O0
just format # fourmolu + nixfmt
just lint # hlint
just ci # format + lint + build + test
nix run .#cardano-ledger-read-tests # what CI runs for the test job
nix develop .#docs --command mkdocs build --strict # build docs like CI
There is no executable; the only build artifact is the library and its
unit-tests suite.
Navigating the code
- Start from the era model:
Eras/KnownEras.hs (the closed era list and
singleton) and Eras/EraValue.hs (the runtime-era existential).
- Reading entry point:
fromConsensusBlock in Block/Block.hs turns a
node's ConsensusBlock into an EraValue Block.
- To find how a component is read, open
Tx/<Component>.hs; each is a
small module: a type family <Component>Type era, a newtype wrapper,
and a getEra<Component> that does case theEra @era of ….
- Every era-dispatching
case enumerates all eras with no wildcard — to
see era coverage, grep for Dijkstra -> or case theEra.
- Tests in
test/Test/Unit/Cardano/Read/Ledger/ show real usage and
carry per-era CBOR example transactions (byronTx … dijkstraTx).
Using the library
Read a block whose era is unknown (e.g. from a node):
import Cardano.Read.Ledger.Block.Block (ConsensusBlock, fromConsensusBlock)
import Cardano.Read.Ledger.Block.Txs (getEraTransactions)
import Cardano.Read.Ledger.Tx.Hash (getEraTxHash)
import Cardano.Read.Ledger.Eras.EraValue (applyEraFun)
import Data.ByteString (ByteString)
txCount :: ConsensusBlock -> Int
txCount = applyEraFun (length . getEraTransactions) . fromConsensusBlock
txHashes :: ConsensusBlock -> [ByteString]
txHashes =
applyEraFun (map getEraTxHash . getEraTransactions)
. fromConsensusBlock
Decode a transaction at a known era and read its components:
import Cardano.Read.Ledger.Tx.CBOR (deserializeTx)
import Cardano.Read.Ledger.Tx.Tx (Tx)
import Cardano.Read.Ledger.Tx.Inputs (Inputs, getEraInputs)
import Cardano.Read.Ledger.Tx.Outputs (Outputs, getEraOutputs)
import Cardano.Read.Ledger.Eras (Conway)
import Cardano.Ledger.Binary (DecoderError)
import qualified Data.ByteString.Lazy as BL
readConway
:: BL.ByteString
-> Either DecoderError (Inputs Conway, Outputs Conway)
readConway cbor = do
tx <- deserializeTx cbor :: Either DecoderError (Tx Conway)
pure (getEraInputs tx, getEraOutputs tx)
Key facts to keep correct:
- The accessor naming is
getEra<Component>. There is no getInputs,
getOutputs, or getEraBlock — use getEraInputs, getEraOutputs,
and fromConsensusBlock.
EraValue is an existential (forall era. IsEra era => EraValue (f era)), not a GADT-syntax declaration. Consume it with applyEraFun.
- Component wrapper types are
newtypes over a type family, not
Foldable — unwrap before using length/toList.
Answering questions
- "What is this / what does it do?" — README What is this; one-line
summary in
cardano-ledger-read.cabal (synopsis/description).
- "Which eras are supported?" —
Eras/KnownEras.hs (KnownEras):
Byron, Shelley, Allegra, Mary, Alonzo, Babbage, Conway, Dijkstra.
- "How do I read X from a transaction?" — the
Tx/X.hs module's
getEraX; full map in docs/api.md.
- "How do I read a block from a node?" —
fromConsensusBlock +
applyEraFun; see README Usage and docs/index.md.
- "How is the era polymorphism built?" —
docs/architecture.md and
.specify/memory/constitution.md (closed-world eras, type families +
GADT dispatch, EraValue).
- "How do I build / test?" — README Development;
justfile;
.github/workflows/CI.yaml.
- "Can it build/sign/submit transactions?" — No. It is read-only by
design (constitution, principle I).
1---2name: cardano-ledger-read-guide3description: Guide for working in the cardano-foundation/cardano-ledger-read Haskell library: era-indexed types and accessors for READING Cardano on-chain data (blocks, transactions, inputs, outputs, fees, certificates, metadata, mint, withdrawals, witnesses). Load when a task mentions cardano-ledger-read, the module prefix Cardano.Read.Ledger, the era model (KnownEras, Era GADT, IsEra, theEra, EraValue, applyEraFun, fromConsensusBlock, ConsensusBlock), accessors named getEra* (e.g. getEraInputs, getEraOutputs, getEraTransactions, getEraBHeader, getEraTxHash), CBOR (serializeTx, deserializeTx), the closed-world era list (Byron Shelley Allegra Mary Alonzo Babbage Conway Dijkstra), or the build via nix develop + just (just build, just test, just ci) with GHC 9.12 / haskell.nix / CHaP. Use it to navigate the code, build and test, write era-polymorphic reads, or answer questions about the library.4---56# cardano-ledger-read guide78`cardano-ledger-read` is a read-only projection layer over the9`cardano-ledger-*` packages. It gives every on-chain concept a uniform10era index and accessors to read it. It does **not** build, balance,11sign, or submit transactions.1213## Repository map1415| Path | Purpose |16|------|---------|17| `src/Cardano/Read/Ledger/Eras/KnownEras.hs` | `KnownEras` type-level list, `Era` GADT singleton, `IsEra`/`theEra`, era aliases, `indexOfEra` |18| `src/Cardano/Read/Ledger/Eras/EraValue.hs` | `EraValue` existential, `applyEraFun`, `applyEraFunValue`, `getEra`, `parseEraIndex`, `eraValueSerialize` |19| `src/Cardano/Read/Ledger/Eras.hs` | Re-exports `KnownEras` |20| `src/Cardano/Read/Ledger/Block/Block.hs` | `ConsensusBlock`, `Block` newtype, `fromConsensusBlock`, `toConsensusBlock` |21| `src/Cardano/Read/Ledger/Block/BHeader.hs` | `BHeader`, `getEraBHeader` |22| `src/Cardano/Read/Ledger/Block/BlockNo.hs` | `BlockNo`, `getEraBlockNo`, `prettyBlockNo` |23| `src/Cardano/Read/Ledger/Block/SlotNo.hs` | `SlotNo`, `getEraSlotNo`, `from/toLedgerSlotNo`, `prettySlotNo` |24| `src/Cardano/Read/Ledger/Block/HeaderHash.hs` | `getEraHeaderHash`, `getRawHeaderHash`, `getEraPrevHeaderHash` |25| `src/Cardano/Read/Ledger/Block/Txs.hs` | `getEraTransactions :: Block era -> [Tx era]` |26| `src/Cardano/Read/Ledger/Block/Gen/` | Test block generators (`mkBlockEra`, `BlockParameters`) |27| `src/Cardano/Read/Ledger/Tx/Tx.hs` | `Tx` newtype, `TxT` type family |28| `src/Cardano/Read/Ledger/Tx/*` | Per-component accessors (see below) |29| `src/Cardano/Read/Ledger/{Address,Value,Hash,PParams}.hs` | Common types |30| `test/` | hspec unit tests + per-era example transactions |31| `docs/` | mkdocs site (`index.md`, `architecture.md`, `api.md`) |32| `.specify/memory/constitution.md` | Binding architectural rules |3334The `Tx/*` accessor modules each export an era-indexed `newtype` wrapper35and a `getEra<Component>` function: `Inputs`/`getEraInputs`,36`Outputs`/`getEraOutputs`, `Output`/`getEraValue`,37`CollateralInputs`/`getEraCollateralInputs`,38`CollateralOutputs`/`getEraCollateralOutputs`,39`ReferenceInputs`/`getEraReferenceInputs`, `Fee`/`getEraFee`,40`Validity`/`getEraValidity`, `Metadata`/`getEraMetadata`,41`Certificates`/`getEraCertificates`, `Withdrawals`/`getEraWithdrawals`,42`Mint`/`getEraMint`, `Witnesses`/`getEraWitnesses`,43`ScriptValidity`/`getEraScriptValidity`, `Integrity`/`getEraIntegrity`,44`ExtraSigs`/`getEraExtraSigs`, `TxId`/`getEraTxId`. CBOR lives in45`Tx/CBOR.hs` (`serializeTx`, `deserializeTx`); `Tx/Hash.hs` has46`getEraTxHash`; `Tx/Eras.hs` has the `onTx` helper.4748## Build, test, run4950Everything runs in the Nix dev shell. Prefer `just` recipes:5152```bash53nix develop # haskell.nix shell: cabal, GHC 9.12, just54just build # cabal build all --enable-tests -O055just test # cabal test all --enable-tests -O056just format # fourmolu + nixfmt57just lint # hlint58just ci # format + lint + build + test59nix run .#cardano-ledger-read-tests # what CI runs for the test job60nix develop .#docs --command mkdocs build --strict # build docs like CI61```6263There is no executable; the only build artifact is the library and its64`unit-tests` suite.6566## Navigating the code6768- Start from the era model: `Eras/KnownEras.hs` (the closed era list and69 singleton) and `Eras/EraValue.hs` (the runtime-era existential).70- Reading entry point: `fromConsensusBlock` in `Block/Block.hs` turns a71 node's `ConsensusBlock` into an `EraValue Block`.72- To find how a component is read, open `Tx/<Component>.hs`; each is a73 small module: a `type family <Component>Type era`, a `newtype` wrapper,74 and a `getEra<Component>` that does `case theEra @era of …`.75- Every era-dispatching `case` enumerates all eras with no wildcard — to76 see era coverage, grep for `Dijkstra ->` or `case theEra`.77- Tests in `test/Test/Unit/Cardano/Read/Ledger/` show real usage and78 carry per-era CBOR example transactions (`byronTx` … `dijkstraTx`).7980## Using the library8182Read a block whose era is unknown (e.g. from a node):8384```haskell85import Cardano.Read.Ledger.Block.Block (ConsensusBlock, fromConsensusBlock)86import Cardano.Read.Ledger.Block.Txs (getEraTransactions)87import Cardano.Read.Ledger.Tx.Hash (getEraTxHash)88import Cardano.Read.Ledger.Eras.EraValue (applyEraFun)89import Data.ByteString (ByteString)9091txCount :: ConsensusBlock -> Int92txCount = applyEraFun (length . getEraTransactions) . fromConsensusBlock9394txHashes :: ConsensusBlock -> [ByteString]95txHashes =96 applyEraFun (map getEraTxHash . getEraTransactions)97 . fromConsensusBlock98```99100Decode a transaction at a known era and read its components:101102```haskell103import Cardano.Read.Ledger.Tx.CBOR (deserializeTx)104import Cardano.Read.Ledger.Tx.Tx (Tx)105import Cardano.Read.Ledger.Tx.Inputs (Inputs, getEraInputs)106import Cardano.Read.Ledger.Tx.Outputs (Outputs, getEraOutputs)107import Cardano.Read.Ledger.Eras (Conway)108import Cardano.Ledger.Binary (DecoderError)109import qualified Data.ByteString.Lazy as BL110111readConway112 :: BL.ByteString113 -> Either DecoderError (Inputs Conway, Outputs Conway)114readConway cbor = do115 tx <- deserializeTx cbor :: Either DecoderError (Tx Conway)116 pure (getEraInputs tx, getEraOutputs tx)117```118119Key facts to keep correct:120121- The accessor naming is `getEra<Component>`. There is no `getInputs`,122 `getOutputs`, or `getEraBlock` — use `getEraInputs`, `getEraOutputs`,123 and `fromConsensusBlock`.124- `EraValue` is an existential (`forall era. IsEra era => EraValue (f125 era)`), not a GADT-syntax declaration. Consume it with `applyEraFun`.126- Component wrapper types are `newtype`s over a type family, not127 `Foldable` — unwrap before using `length`/`toList`.128129## Answering questions130131- "What is this / what does it do?" — README **What is this**; one-line132 summary in `cardano-ledger-read.cabal` (`synopsis`/`description`).133- "Which eras are supported?" — `Eras/KnownEras.hs` (`KnownEras`):134 Byron, Shelley, Allegra, Mary, Alonzo, Babbage, Conway, Dijkstra.135- "How do I read X from a transaction?" — the `Tx/X.hs` module's136 `getEraX`; full map in `docs/api.md`.137- "How do I read a block from a node?" — `fromConsensusBlock` +138 `applyEraFun`; see README **Usage** and `docs/index.md`.139- "How is the era polymorphism built?" — `docs/architecture.md` and140 `.specify/memory/constitution.md` (closed-world eras, type families +141 GADT dispatch, `EraValue`).142- "How do I build / test?" — README **Development**; `justfile`;143 `.github/workflows/CI.yaml`.144- "Can it build/sign/submit transactions?" — No. It is read-only by145 design (constitution, principle I).