From ef139f90c41b19112edffcdd7cab089e1ca37f5c Mon Sep 17 00:00:00 2001 From: Santiago Date: Mon, 27 Apr 2026 11:28:42 -0300 Subject: [PATCH 1/4] feat: add extensible QueryLedgerState RPC with stake pool distribution Introduces a chain-agnostic QueryLedgerState method on the v1beta QueryService backed by AnyChainLedgerQuery / AnyChainLedgerQueryResult envelopes. Cardano-side queries live in cardano.proto under LedgerStateQuery / LedgerStateQueryResult oneof envelopes, mirroring the Ouroboros node-to-client LocalStateQuery mini-protocol. Future queries can be added as new oneof variants without changing the service surface. The first concrete query is GetStakePoolDistribution (with optional pool_keyhashes filter), returning PoolStakeShare entries that match Ouroboros GetPoolDistr / IndividualPoolStake. Co-Authored-By: Claude Opus 4.7 (1M context) --- proto/utxorpc/v1beta/cardano/cardano.proto | 40 ++++++++++++++++++++++ proto/utxorpc/v1beta/query/query.proto | 27 +++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/proto/utxorpc/v1beta/cardano/cardano.proto b/proto/utxorpc/v1beta/cardano/cardano.proto index 24e54fa..226c548 100644 --- a/proto/utxorpc/v1beta/cardano/cardano.proto +++ b/proto/utxorpc/v1beta/cardano/cardano.proto @@ -539,6 +539,46 @@ message UpdateDRepCert { Anchor anchor = 2; } +// LEDGER-STATE QUERIES +// ==================== +// +// Cardano-specific queries that mirror the Ouroboros node-to-client +// LocalStateQuery mini-protocol. The oneof envelope lets new queries be +// added later without changing the chain-agnostic QueryService surface. + +// Envelope of a Cardano ledger-state query. +message LedgerStateQuery { + oneof query { + GetStakePoolDistribution stake_pool_distribution = 1; // Active stake distribution across pools. + } +} + +// Envelope of a Cardano ledger-state query result. +message LedgerStateQueryResult { + oneof result { + StakePoolDistribution stake_pool_distribution = 1; // Result of a stake pool distribution query. + } +} + +// Stake pool distribution query. Mirrors Ouroboros GetPoolDistr / GetFilteredPoolDistr. +message GetStakePoolDistribution { + // If non-empty, restrict the result to the listed pool key hashes. + // If empty, return the distribution for every pool. + repeated bytes pool_keyhashes = 1; +} + +// Per-pool stake share. Mirrors Ouroboros IndividualPoolStake. +message PoolStakeShare { + bytes pool_keyhash = 1; // Pool key hash (pool id). + RationalNumber stake_fraction = 2; // Fraction of total active stake delegated to this pool. + bytes vrf_keyhash = 3; // Pool's VRF key hash, as reported by the node. +} + +// Result of a stake pool distribution query. +message StakePoolDistribution { + repeated PoolStakeShare pools = 1; // One entry per pool present in the snapshot. +} + // PATTERN MATCHING // ================ diff --git a/proto/utxorpc/v1beta/query/query.proto b/proto/utxorpc/v1beta/query/query.proto index 8e83fa0..507b148 100644 --- a/proto/utxorpc/v1beta/query/query.proto +++ b/proto/utxorpc/v1beta/query/query.proto @@ -72,6 +72,32 @@ message ReadParamsResponse { ChainPoint ledger_tip = 2; // The chain point that represent the ledger current position. } +// An envelope that wraps a chain-specific ledger-state query. +message AnyChainLedgerQuery { + oneof query { + utxorpc.v1beta.cardano.LedgerStateQuery cardano = 1; // A Cardano ledger-state query. + } +} + +// An envelope that wraps a chain-specific ledger-state query result. +message AnyChainLedgerQueryResult { + oneof result { + utxorpc.v1beta.cardano.LedgerStateQueryResult cardano = 1; // A Cardano ledger-state query result. + } +} + +// Request to run a chain-specific ledger-state query against the current ledger snapshot. +message QueryLedgerStateRequest { + AnyChainLedgerQuery query = 1; // The chain-specific query to evaluate. + google.protobuf.FieldMask field_mask = 2; // Field mask to selectively return fields. +} + +// Response carrying the ledger-state query result. +message QueryLedgerStateResponse { + AnyChainLedgerQueryResult result = 1; // The query result. + ChainPoint ledger_tip = 2; // Chain point representing the snapshot the query was evaluated against. +} + // An evenlope that holds an UTxO patterns from any of compatible chains message AnyUtxoPattern { oneof utxo_pattern { @@ -175,6 +201,7 @@ service QueryService { rpc ReadTx(ReadTxRequest) returns (ReadTxResponse); // Get Txs by chain-specific criteria. rpc ReadGenesis(ReadGenesisRequest) returns (ReadGenesisResponse); // Get the genesis configuration rpc ReadEraSummary(ReadEraSummaryRequest) returns (ReadEraSummaryResponse); // Get the chain era summary + rpc QueryLedgerState(QueryLedgerStateRequest) returns (QueryLedgerStateResponse); // Run a chain-specific ledger-state query (e.g. stake pool distribution). // TODO: decide if we want to expand the scope // rpc DumpUtxos(ReadUtxosRequest) returns (stream ReadUtxosResponse); // Dump all available utxos From 14acc2b0656bf1841cfc366145a6bb35a6dc4c3d Mon Sep 17 00:00:00 2001 From: Santiago Date: Mon, 27 Apr 2026 11:31:07 -0300 Subject: [PATCH 2/4] refactor: rename QueryLedgerState to ReadState Aligns the new RPC with the existing Read* naming convention on QueryService (ReadParams, ReadUtxos, ReadData, ReadTx, ReadGenesis, ReadEraSummary). Co-Authored-By: Claude Opus 4.7 (1M context) --- proto/utxorpc/v1beta/query/query.proto | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proto/utxorpc/v1beta/query/query.proto b/proto/utxorpc/v1beta/query/query.proto index 507b148..2871f84 100644 --- a/proto/utxorpc/v1beta/query/query.proto +++ b/proto/utxorpc/v1beta/query/query.proto @@ -87,13 +87,13 @@ message AnyChainLedgerQueryResult { } // Request to run a chain-specific ledger-state query against the current ledger snapshot. -message QueryLedgerStateRequest { +message ReadStateRequest { AnyChainLedgerQuery query = 1; // The chain-specific query to evaluate. google.protobuf.FieldMask field_mask = 2; // Field mask to selectively return fields. } // Response carrying the ledger-state query result. -message QueryLedgerStateResponse { +message ReadStateResponse { AnyChainLedgerQueryResult result = 1; // The query result. ChainPoint ledger_tip = 2; // Chain point representing the snapshot the query was evaluated against. } @@ -201,7 +201,7 @@ service QueryService { rpc ReadTx(ReadTxRequest) returns (ReadTxResponse); // Get Txs by chain-specific criteria. rpc ReadGenesis(ReadGenesisRequest) returns (ReadGenesisResponse); // Get the genesis configuration rpc ReadEraSummary(ReadEraSummaryRequest) returns (ReadEraSummaryResponse); // Get the chain era summary - rpc QueryLedgerState(QueryLedgerStateRequest) returns (QueryLedgerStateResponse); // Run a chain-specific ledger-state query (e.g. stake pool distribution). + rpc ReadState(ReadStateRequest) returns (ReadStateResponse); // Run a chain-specific ledger-state query (e.g. stake pool distribution). // TODO: decide if we want to expand the scope // rpc DumpUtxos(ReadUtxosRequest) returns (stream ReadUtxosResponse); // Dump all available utxos From db7c0f1d5c7f011c12378ebeae5c267362bd4f53 Mon Sep 17 00:00:00 2001 From: Santiago Date: Fri, 1 May 2026 09:08:27 -0300 Subject: [PATCH 3/4] refactor: shorten ledger-state query type names - AnyChainLedgerQuery -> AnyChainStateQuery - AnyChainLedgerQueryResult -> AnyChainStateResult - LedgerStateQuery -> StateQuery - LedgerStateQueryResult -> StateResult Aligns with the ReadState RPC name and trims redundancy in the type names while keeping the envelope structure intact. Co-Authored-By: Claude Opus 4.7 (1M context) --- proto/utxorpc/v1beta/cardano/cardano.proto | 4 ++-- proto/utxorpc/v1beta/query/query.proto | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/proto/utxorpc/v1beta/cardano/cardano.proto b/proto/utxorpc/v1beta/cardano/cardano.proto index 226c548..46022e7 100644 --- a/proto/utxorpc/v1beta/cardano/cardano.proto +++ b/proto/utxorpc/v1beta/cardano/cardano.proto @@ -547,14 +547,14 @@ message UpdateDRepCert { // added later without changing the chain-agnostic QueryService surface. // Envelope of a Cardano ledger-state query. -message LedgerStateQuery { +message StateQuery { oneof query { GetStakePoolDistribution stake_pool_distribution = 1; // Active stake distribution across pools. } } // Envelope of a Cardano ledger-state query result. -message LedgerStateQueryResult { +message StateResult { oneof result { StakePoolDistribution stake_pool_distribution = 1; // Result of a stake pool distribution query. } diff --git a/proto/utxorpc/v1beta/query/query.proto b/proto/utxorpc/v1beta/query/query.proto index 2871f84..8515d01 100644 --- a/proto/utxorpc/v1beta/query/query.proto +++ b/proto/utxorpc/v1beta/query/query.proto @@ -73,28 +73,28 @@ message ReadParamsResponse { } // An envelope that wraps a chain-specific ledger-state query. -message AnyChainLedgerQuery { +message AnyChainStateQuery { oneof query { - utxorpc.v1beta.cardano.LedgerStateQuery cardano = 1; // A Cardano ledger-state query. + utxorpc.v1beta.cardano.StateQuery cardano = 1; // A Cardano ledger-state query. } } // An envelope that wraps a chain-specific ledger-state query result. -message AnyChainLedgerQueryResult { +message AnyChainStateResult { oneof result { - utxorpc.v1beta.cardano.LedgerStateQueryResult cardano = 1; // A Cardano ledger-state query result. + utxorpc.v1beta.cardano.StateResult cardano = 1; // A Cardano ledger-state query result. } } // Request to run a chain-specific ledger-state query against the current ledger snapshot. message ReadStateRequest { - AnyChainLedgerQuery query = 1; // The chain-specific query to evaluate. + AnyChainStateQuery query = 1; // The chain-specific query to evaluate. google.protobuf.FieldMask field_mask = 2; // Field mask to selectively return fields. } // Response carrying the ledger-state query result. message ReadStateResponse { - AnyChainLedgerQueryResult result = 1; // The query result. + AnyChainStateResult result = 1; // The query result. ChainPoint ledger_tip = 2; // Chain point representing the snapshot the query was evaluated against. } From dc03e37f2bd33fed87e91367e7d2dabcf57c20e3 Mon Sep 17 00:00:00 2001 From: Santiago Date: Fri, 1 May 2026 09:10:55 -0300 Subject: [PATCH 4/4] refactor: rename StateResult to StateData Continues the type-name shortening; "Data" reads more naturally than "Result" since the response carries the state payload itself rather than a yes/no outcome. - AnyChainStateResult -> AnyChainStateData - StateResult -> StateData Co-Authored-By: Claude Opus 4.7 (1M context) --- proto/utxorpc/v1beta/cardano/cardano.proto | 2 +- proto/utxorpc/v1beta/query/query.proto | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/proto/utxorpc/v1beta/cardano/cardano.proto b/proto/utxorpc/v1beta/cardano/cardano.proto index 46022e7..2584373 100644 --- a/proto/utxorpc/v1beta/cardano/cardano.proto +++ b/proto/utxorpc/v1beta/cardano/cardano.proto @@ -554,7 +554,7 @@ message StateQuery { } // Envelope of a Cardano ledger-state query result. -message StateResult { +message StateData { oneof result { StakePoolDistribution stake_pool_distribution = 1; // Result of a stake pool distribution query. } diff --git a/proto/utxorpc/v1beta/query/query.proto b/proto/utxorpc/v1beta/query/query.proto index 8515d01..a1f7909 100644 --- a/proto/utxorpc/v1beta/query/query.proto +++ b/proto/utxorpc/v1beta/query/query.proto @@ -80,9 +80,9 @@ message AnyChainStateQuery { } // An envelope that wraps a chain-specific ledger-state query result. -message AnyChainStateResult { +message AnyChainStateData { oneof result { - utxorpc.v1beta.cardano.StateResult cardano = 1; // A Cardano ledger-state query result. + utxorpc.v1beta.cardano.StateData cardano = 1; // A Cardano ledger-state query result. } } @@ -94,7 +94,7 @@ message ReadStateRequest { // Response carrying the ledger-state query result. message ReadStateResponse { - AnyChainStateResult result = 1; // The query result. + AnyChainStateData result = 1; // The query result. ChainPoint ledger_tip = 2; // Chain point representing the snapshot the query was evaluated against. }