Local State Query

 
                ┌───────────────┐
        ┌──────▶│     Idle      │⇦ START
        │       └───┬───────────┘
        │           │       ▲ 
        │   Acquire │       │ Failure 
        │           ▼       │
        │       ┌───────────┴───┐                   
Release │       │   Acquiring   │◀─────────────────┐
        │       └───┬───────────┘                  │
        │           │       ▲                      │ Result 
        │  Acquired │       │ ReAcquire            │
        │           ▼       │                      │
        │       ┌───────────┴───┐         ┌────────┴───────┐
        └───────┤   Acquired    │────────▶│    Querying    │
                └───────────────┘  Query  └────────────────┘
 

Overview

The state query protocol is likely the most versatile of the three Ouroboros mini-protocols. As a matter of fact, it allows for querying various types of information directly from the ledger. In essence, it is like a very simpler request/response pattern where the types of questions one can ask are specified by the protocols. Those questions include: information about the chain tip, information about stake pools but also the balance of a particular address.

In order to run a question by the ledger, one must first acquire a particular position on the chain, so that the node can reliably answer a few questions on a chosen, frozen state while continuing maintaining more recent version of the ledger on the side. It is important to note that:

  1. The node cannot acquire any arbitrary state. One can only rewind up to a certain point.

  2. Should a client keep a state acquired for too long, it is likely to become unreachable at some point, forcing clients to re-acquire.

How To Use

Ogmios uses a simplified version of the above state-machine. Or more exactly, it exposes a simplified version and handles some of the complexity behind the scene for you. As clients, Ogmios will give you 3 possible requests: Acquire, Query, Release. A typical sequence would be to start by Acquiring a state on a given point and then make a few queries, and then release. The release step is optional although it is a bit more polite to say goodbye at the end of a conversation.

It is also possible to submit queries directly without acquiring. As a consequence, Ogmios will acquire the tip of the chain, run the query and release it for you. This is the easiest way to send queries if you don’t care about capturing a particular state. Note however that this may create race conditions if you send multiple queries via this method. Indeed, the tip is changing quite often on the network, and two subsequent queries may actually run on two different points of the chain. While this is generally safe for most queries, it may also put your application in an unexpected state when crossing epoch boundaries or hard-forks.

Acquire

The Acquire request expect one argument named point. The point has the same format as points in the local-chain-sync protocol. That is, they can be block header hashes or the special keyword "origin" (though there’s very little chance that one will be able to acquire the origin!).

{ 
    "type": "jsonwsp/request",
    "version": "1.0",
    "servicename": "ogmios",
    "methodname": "Acquire",
    "args": { "point": "9e871633f7aa356ef11cdcabb6fdd6d8f4b00bc919c57aed71a91af8f86df590" }
}

One thing that doesn’t strike as obvious is that, as clients, you need points to query any information. There are many ways to get those hashes but in the context of Ogmios, the most logical way is via the local-chain-sync protocol.

You can acquire multiple times, the last one will prevail. If you need to re-acquire, simply send another Acquire request.

Query

There are many queries that can be sent to the ledger, and the list is growing days after days as the Cardano team implements new ones. With Ogmios, all queries follow the same pattern and use the method name Query. All of them also take one argument named query which specifies the query to run and, optionally some extra argument given to the query. For example:

{ 
    "type": "jsonwsp/request",
    "version": "1.0",
    "servicename": "ogmios",
    "methodname": "Query",
    "args": { "query": "ledgerTip" }
}

At the moment of writing this guide, the following queries are available:

QueryResult
blockHeightThe chain’s highest block number.
chainTipThe chain’s current tip.
currentEpochThe current epoch of the ledger.
currentProtocolParametersThe current protocol parameters.
delegationsAndRewardsCurrent delegation settings and rewards of given reward accounts.
eraStartThe information regarding the beginning of the current era.
eraSummariesEra bounds and slotting parameters details, required for proper slot arithmetic.
genesisConfigGet a compact version of the era’s genesis configuration.
ledgerTipThe most recent block tip known of the ledger.
nonMyopicMemberRewardsNon-myopic member rewards for each pool. Used in ranking.
poolIdsThe list of all pool identifiers currently registered and active.
poolParametersStake pool parameters submitted with registration certificates.
poolsRankingRetrieve stake pools ranking (a.k.a desirabilities).
proposedProtocolParametersThe last update proposal w.r.t. protocol parameters, if any.
rewardsProvenance'Get details about rewards calculation for the ongoing epoch.
stakeDistributionDistribution of the stake across all known stake pools.
systemStartThe chain’s start time (UTC).
utxoCurrent UTXO, possibly filtered by output reference.

Deprecated queries. Queries or functionalities listed below will be removed in the next major release of Ogmios.

QueryNoticeDeprecated Since
rewardsProvenanceSupports for this query is not longer guaranteed. The pools field in the result is no longer populated. Use rewardsProvenance' insteadcardano-node@1.33.0
utxoFiltering UTXO by address is no longer recommended with the introduction of on-disk ledger state storage. Filtering by output reference is preferred.cardano-node@1.33.0

To know more about arguments and results of each query, have a look at the API reference.

Simplified Example

In this example, we’ll consider a simple direct query on the network tip to fetch the latest protocol parameters. The next section gives a more elaborate example which shows how to acquire a specific point on chain.

const WebSocket = require('ws');
const client = new WebSocket("ws://localhost:1337");

function wsp(methodname, args) {
    client.send(JSON.stringify({
        type: "jsonwsp/request",
        version: "1.0",
        servicename: "ogmios",
        methodname,
        args
    }));
}

client.once('open', () => {
    wsp("Query", { query: "currentProtocolParameters" } );
});

client.on('message', function(msg) {
    const response = JSON.parse(msg);
    console.log(JSON.stringify(response.result, null, 4));
    client.close();
});

This little excerpt outputs the most recent protocol parameters in a nice JSON:

{
    "poolDeposit": 500000000,
    "protocolVersion": {
        "minor": 0,
        "major": 3
    },
    "minUtxoValue": 1000000,
    "minFeeConstant": 155381,
    "maxTxSize": 16384,
    "minPoolCost": 340000000,
    "maxBlockBodySize": 65536,
    "extraEntropy": "neutral",
    "minFeeCoefficient": 44,
    "poolInfluence": "3/10",
    "maxBlockHeaderSize": 1100,
    "stakeKeyDeposit": 2000000,
    "decentralizationParameter": "1/5",
    "desiredNumberOfPools": 500,
    "poolRetirementEpochBound": 18,
    "monetaryExpansion": "3/1000",
    "treasuryExpansion": "1/5"
}

Full Example

Let’s see a full example getting the stake distribution of all stake pools of the Cardano mainnet. In the example, we’ll also use the FindIntersect method from the local-chain-sync protocol to get an easy point to acquire.

const WebSocket = require('ws');
const client = new WebSocket("ws://localhost:1337");

function wsp(methodname, args) {
    client.send(JSON.stringify({
        type: "jsonwsp/request",
        version: "1.0",
        servicename: "ogmios",
        methodname,
        args
    }));
}

client.once('open', () => {
    wsp("FindIntersect", { points: ["origin"] });
});

client.on('message', function(msg) {
    const response = JSON.parse(msg);

    switch (response.methodname) {
        case "FindIntersect":
            const point = response.result.IntersectionFound.tip;
            wsp("Acquire", { point });
            break;

        case "Acquire":
            wsp("Query", { query: "stakeDistribution" });
            break;

        case "Query":
            console.log(response.result);
            client.close();
            break;
    }
});

Here’s a walk-though describing what happens when running the above script:

  1. An initial request ask to FindIntersect that is guaranteed to succeed is sent. This is a little trick in order to access the ledger tip easily. As a response, Ogmios replies with:

    IntersectionFound
  2. Using the tip from the previous response, we can now safely Acquire a state on that particular tip which we know exists and is not too old. Ogmios replies successfully with:

    AcquireSuccess
  3. Now in a position to make an actual Query, we do it and ask for the stake distribution across all stake pools. The (truncated) response from the server looks like:

    QueryResponse

Be aware that it is possible for an Acquire request to fail even if (and in particular if) made immediately after finding the ledger tip. In Ouroboros Praos frequent small rollbacks of the chain are not rare and the few last blocks of the chain can be a bit volatile. A real application may require more elaborate error handling than the toy example above.

Example Queries

currentEpoch

{
  "query": "currentEpoch"
}

currentProtocolParameters

{
  "query": "currentProtocolParameters"
}

delegationsAndRewards

{
  "query": {
    "delegationsAndRewards": [
      "7c16240714ea0e12b41a914f2945784ac494bb19573f0ca61a08afa8"
    ]
  }
}

eraStart

{
  "query": "eraStart"
}

eraSummaries

{
  "query": "eraSummaries"
}

genesisConfig

{
  "query": "genesisConfig"
}

ledgerTip

{
  "query": "ledgerTip"
}

nonMyopicMemberRewards (by credentials)

{
  "query": {
    "nonMyopicMemberRewards": [
      "7c16240714ea0e12b41a914f2945784ac494bb19573f0ca61a08afa8"
    ]
  }
}

nonMyopicMemberRewards (by amounts)

{
  "query": {
    "nonMyopicMemberRewards": [
      42000000
    ]
  }
}

poolIds

{
  "query": "poolIds"
}

poolParameters

{
  "query": {
    "poolParameters": [
      "pool1pk2wzarn9mu64eel89dtg3g8h75c84jsy0q349glpsewgd7sdls",
      "4acf2773917c7b547c576a7ff110d2ba5733c1f1ca9cdc659aea3a56"
    ]
  }
}

poolsRanking

{
  "query": "poolsRanking"
}

proposedProtocolParameters

{
  "query": "proposedProtocolParameters"
}

rewardsProvenance

{
  "query": "rewardsProvenance"
}

rewardsProvenance'

{
  "query": "rewardsProvenance'"
}

stakeDistribution

{
  "query": "stakeDistribution"
}

utxo (by Address)

{
  "query": {
    "utxo": [
      "addr1wx66ue36465w2qq40005h2hadad6pnjht8mu6sgplsfj74qhpnf3s",
      "addr1xyxefct5wvh0n2h88uu44dz9q7l6nq7k2q3uzx54ruxr9e93ddt0tmqxf0n2c09tvq67lt5xkdnvc0wy5r2hzcpawrjsjk6m63"
    ]
  }
}

utxo (by TxIn)

{
  "query": {
    "utxo": [
      {
        "txId": "ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25",
        "index": 2
      }
    ]
  }
}