Skip to content

gRPC: honour FieldMask in UTxO RPC requests in cardano-rpc #1273

Description

@carbolymer

Context

Every UTxO RPC service includes a google.protobuf.FieldMask in its request messages, letting clients select which response fields the server should materialise.
cardano-rpc currently ignores it everywhere; the only trace is a TODO in readParamsMethod (Cardano/Rpc/Server/Internal/UtxoRpc/Query.hs), which notes that masks need to be normalised before use, following the semantics of protobuf's FieldMaskTree.

Request messages carrying a field_mask:

Service Message Method implemented today
QueryService ReadParamsRequest yes
QueryService ReadUtxosRequest yes
QueryService SearchUtxosRequest yes
QueryService ReadGenesisRequest yes
QueryService ReadEraSummaryRequest no
QueryService ReadDataRequest no
QueryService ReadTxRequest no
SyncService FetchBlockRequest yes
SyncService FollowTipRequest yes (#1268)
SyncService DumpHistoryRequest no
SubmitService WatchMempoolRequest no

Motivation

Two distinct wins:

  1. Bandwidth: responses like AnyChainBlock carry both raw CBOR (native_bytes) and the fully parsed cardano block.
    A client interested only in headers, or only in raw bytes, currently receives everything.
    gRPC: Implement FollowTip (UTxO RPC SyncService) in cardano-rpc #1219 already flags this ("FieldMask becomes valuable for letting clients request only headers, or only transaction hashes").
  2. Server-side cost: raised in review of cardano-rpc: Implement FollowTip SyncService method #1268 (cardano-rpc: Implement FollowTip SyncService method #1268 (comment)) - FetchBlock and every FollowTip stream fetch two block components per block, GetRawBlock and GetBlock, and the GetBlock decode is paid unconditionally.
    A mask excluding the parsed cardano fields would let the server select the BlockComponent per request and skip block deserialisation entirely.

Proposed behaviour

  • An absent or empty mask means "return all fields" (standard FieldMask read semantics); current behaviour is therefore already correct for clients that send no mask.
  • A non-empty mask is normalised first (deduplicate, drop paths covered by an ancestor path), following the semantics of protobuf's FieldMaskTree linked above.
  • The mask drives response construction, not post-hoc pruning: handlers consult the mask before doing the work behind each field, so data that is masked out is never fetched, decoded, or converted in the first place.
    Building the full response and then stripping fields would deliver the bandwidth win but none of the server-side cost win, which is the harder and more valuable half.
  • Invalid paths: follow the protobuf recommendation for read masks - ignore unknown paths rather than failing the request (to be confirmed against what other UTxO RPC servers, e.g. Dolos, do).

Implementation sketch

  1. A shared mask-query helper in cardano-rpc: normalise a FieldMask into a path tree and expose "is this path (or any child of it) requested?" for handlers to branch on.
  2. Wire it into the implemented handlers (ReadParams, ReadUtxos, SearchUtxos, ReadGenesis, FetchBlock, FollowTip): each expensive field (protocol parameter conversion, UTxO parsing, tx conversion) is computed only when its path is requested.
  3. FollowTip/FetchBlock fetch-avoidance: when the mask excludes all parsed cardano.* paths, fetch only GetRawBlock and skip GetBlock; when it excludes native_bytes, fetch only GetBlock.
    This changes the BlockComponent selected in NodeKernelAccess, so the follower/fetch component becomes a function of the request.
  4. Unmentioned future handlers (ReadEraSummary, ReadData, ReadTx, DumpHistory, WatchMempool) apply the same helper when they land.

Acceptance criteria

  • Empty/absent mask returns full responses (no behaviour change for existing clients).
  • Masked unary responses contain only the requested paths, with mask normalisation applied.
  • FollowTip and FetchBlock skip the block decode when the mask excludes all parsed block fields, and skip raw-bytes fetch when the mask excludes native_bytes.
  • Unknown mask paths are handled per the documented policy (ignored or rejected - decided and documented).
  • Unit tests cover mask normalisation and masked response construction; conformance checked against another UTxO RPC server (e.g. Dolos) via the existing comparison tooling.
  • README coverage table updated to note FieldMask support.

Out of scope

  • Field masks on methods not yet implemented (they inherit the helper when implemented).
  • Write masks / update semantics (FieldMask is used purely as a read mask in UTxO RPC).

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

Status
Todo

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions