Skip to content

cardano-rpc: Populate transaction bodies in FetchBlock responses#1247

Draft
carbolymer wants to merge 8 commits into
masterfrom
mgalazyn/feature/fetchBlock-tx-bodies
Draft

cardano-rpc: Populate transaction bodies in FetchBlock responses#1247
carbolymer wants to merge 8 commits into
masterfrom
mgalazyn/feature/fetchBlock-tx-bodies

Conversation

@carbolymer

@carbolymer carbolymer commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Context

Part of the node kernel access work (ADR-019): FetchBlock previously returned raw block bytes, the block header and the timestamp, but left body.tx empty.

This PR populates the transaction bodies. All 14 fields of the UTxO RPC Tx message are mapped: inputs, outputs, reference inputs, certificates, withdrawals, mint, witnesses, collateral, fee, validity, successful, auxiliary data, hash and governance proposals.

  • Transactions are converted at ledger level for every Shelley-based era; era-gated fields (reference inputs, mint, collateral, proposals) stay empty where the era predates them.
  • Spending, withdrawal and certificate redeemers are wired to their entries by script witness index, and successful reflects the phase-2 IsValid flag.
  • A new Certificate module maps all 19 certificate variants (Dijkstra-safe via concrete-era dispatch); a new Governance module maps all 7 governance actions including partial parameter updates.
  • Byron transactions are converted directly from the Byron ledger types, since cardano-api's Tx era has no Byron constructor. Byron fees are implicit in the UTxO and stay unset; epoch boundary blocks yield no transactions. This makes FetchBlock serve transaction data for the full mainnet history.
  • byronBlockRaw is re-exported from Cardano.Api.Consensus.

Known limitations, deliberate and documented in code:

  • TxInput.as_output is unset (resolving it requires UTxO lookups).
  • Byron fee is unset (implicit in Byron; requires UTxO lookups).
  • The Byron positional witness-to-input pairing is not representable in the proto WitnessSet; native_bytes is the fidelity escape hatch.
  • Pre-existing bytes-field encoding divergences (TxOutput.address as text, inline Datum.hash carrying CBOR) are tracked separately in gRPC: bytes fields encoded as text or wrong payload (TxOutput.address, Datum.hash) #1246 and not changed here.

How to trust this PR

The conversion is covered at three levels:

  • Property tests (cardano-rpc-test, 61 tests): per-field projections of generated transactions against the ledger values, certificate arm routing, injected-redeemer wiring (spending/rewarding/certifying at known indices), a wire-level protobuf encode/decode roundtrip forcing total evaluation across all eras, and a forged-annotation guard pinning Byron transaction ids to the on-chain bytes rather than a re-serialisation.
  • E2E (cardano-testnet-test, cardano-node repository): submits a payment transaction and a native-script minting transaction over gRPC, locates their blocks, fetches them via FetchBlock and asserts the mapped fields (hash, fee, inputs, outputs, validity, mint, witness set contents) against the submitted values.
  • The mapping was cross-checked against other UTxO RPC implementations (Dolos/pallas, Blink Labs/gouroboros) for the byte-encoding conventions of addresses, asset names, hashes and witnesses.

Review commit by commit; the conversion lives in Cardano.Rpc.Server.Internal.UtxoRpc.{Type,Certificate,Governance,Byron} and the handler wiring in Cardano.Rpc.Server.Internal.UtxoRpc.Sync.

Run the tests:

cabal test cardano-rpc-test
TASTY_PATTERN='/RPC FetchBlock/' cabal test cardano-testnet-test

Checklist

  • Commit sequence broadly makes sense and commits have useful messages
  • New tests are added if needed and existing tests are updated. See Running tests for more details
  • Self-reviewed the diff
  • Changelog fragment added in .changes/

@carbolymer carbolymer changed the base branch from master to mgalazyn/feature/rpc-fetchblock-add-timestamp July 8, 2026 17:14
@carbolymer carbolymer changed the title Mgalazyn/feature/fetch block tx bodies cardano-rpc: Populate transaction bodies in FetchBlock responses Jul 8, 2026
Comment thread cardano-api/src/Cardano/Api/Block.hs Fixed
@carbolymer carbolymer force-pushed the mgalazyn/feature/fetchBlock-tx-bodies branch 6 times, most recently from f9cb860 to 812cb1c Compare July 9, 2026 13:34
@carbolymer carbolymer force-pushed the mgalazyn/feature/rpc-fetchblock-add-timestamp branch from 73e1a16 to 534dce1 Compare July 9, 2026 13:50
@carbolymer carbolymer force-pushed the mgalazyn/feature/fetchBlock-tx-bodies branch from 812cb1c to b0dbce2 Compare July 9, 2026 13:53
@carbolymer carbolymer changed the base branch from mgalazyn/feature/rpc-fetchblock-add-timestamp to mgalazyn/refactor/rpc-reorganise-modules July 9, 2026 13:54
The Rational to RationalNumber conversion used fromIntegral into the
int32 numerator and uint32 denominator, which wraps modulo 2^32: a pool
margin of 1 % 2^33 emitted denominator 0. The conversion now returns
the best representable approximation via a continued-fraction walk when
either component exceeds its field, and converts exactly otherwise.
The comment claiming the conversion clips was wrong and is gone.
Handler boundary types, the Inject orphans and nested fields of Proto
messages all expect Proto values, so wrapping throughout eliminates the
getProto and Proto shims at every seam. The predicate matchers now take
Proto-wrapped patterns and scriptWitnessIndexToRedeemerPurpose returns a
Proto-wrapped enum, since grapesy rewraps enum fields too.
…pers

txOutToUtxoRpcTxOutput now takes an explicit ShelleyBasedEra witness
instead of IsEra, so it can convert outputs of blocks from any
Shelley-based era rather than only the current one. Export
txoRefUtxoRpcToTxIn and add exactAddressPredicate so RPC clients and
tests build requests from library code instead of duplicating it.
fetchBlock now yields the block in era context via fromConsensusBlock
instead of just the block number, so handlers can inspect the block
content. The Sync proto wrapper re-exports the whole generated
Cardano_Fields module (hiding the four lenses that clash with
Sync_Fields), giving FetchBlock consumers access to all cardano
message field lenses.
Move the conversions between cardano-api types and their UTxO RPC
protobuf counterparts from the monolithic UtxoRpc.Type module into
dedicated submodules: BigInt, ChainPoint, PlutusData,
ProtocolParameters, Script, TxEval and TxOutput.

UtxoRpc.Type becomes an umbrella module which re-exports the same
names it exported before, so the import surface is unchanged and
existing importers keep compiling without modification.
Map fetched block transactions onto the UTxO RPC Tx message, covering
all 14 proto fields: inputs, outputs, reference inputs, certificates,
withdrawals, mint, witnesses, collateral, fee, validity, successful,
auxiliary data, hash and governance proposals.

- Convert at ledger level for every Shelley-based era, with era-gated
  fields (reference inputs, mint, collateral, proposals) empty where
  the era predates them
- Wire spending, withdrawal and certificate redeemers to their entries
  by script witness index; successful reflects the phase-2 IsValid flag
- Add Type.Certificate mapping all 19 certificate variants
  (Dijkstra-safe via concrete-era dispatch) and Type.Governance
  mapping all 7 governance actions including partial parameter updates
- Convert Byron transactions directly from the Byron ledger types
  (cardano-api's Tx has no Byron constructor); Byron fees are implicit
  and stay unset; epoch boundary blocks yield no transactions
- Re-export byronBlockRaw from Cardano.Api.Consensus
- Add property tests: per-field projections against generated
  transactions, wire-level encode/decode totality across eras,
  injected-redeemer wiring, a forged-annotation guard pinning Byron
  transaction ids to the on-chain bytes, and a golden test over a
  mainnet Byron block and epoch boundary block
@carbolymer carbolymer force-pushed the mgalazyn/refactor/rpc-reorganise-modules branch from 2daffe7 to ecc1f35 Compare July 10, 2026 09:04
@carbolymer carbolymer force-pushed the mgalazyn/feature/fetchBlock-tx-bodies branch from b0dbce2 to 0d12995 Compare July 10, 2026 09:04
@carbolymer carbolymer force-pushed the mgalazyn/refactor/rpc-reorganise-modules branch 2 times, most recently from a5413ed to 9396d3b Compare July 11, 2026 01:36
Base automatically changed from mgalazyn/refactor/rpc-reorganise-modules to master July 11, 2026 02:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants