Introduction

This document contains the specification of the JSON-RPC interface exposed by blockchain nodes in the Substrate ecosystem.

This specification aims at establishing a lingua franca between UIs, tools, etc. that would like to access a Substrate-based blockchain, and node client implementations that actually connect to the blockchain. Please note that it is, however, in no way mandatory for nodes to implement this specification in order to access a Substrate-based blockchain. As such, this specification is out of scope of, say, the Polkadot host specification.

Objectives

The objective of this JSON-RPC interface is to accomodate multiple kinds of audiences:

  • End-user-facing applications that want to read and interact with the blockchain.
  • Node operators to want to make sure that their node is operating correctly, and perform some administrative operations such as rotating keys.
  • Core/parachain developers that want to manually look at the storage and figure out what is happening on the blockchain.
  • Oracles or bridges need to be able to read and interface with the blockchain.
  • Archivers want to look at past data.

End-user-facing applications

End-user-facing applications, such as a wallet or an unstoppable application, need to be able to read the storage of the blockchain and submit transactions.

These applications perform JSON-RPC function calls either against a node run locally by the end-user, or against a trusted JSON-RPC server. The locally-run node solution is strictly better for security and decentralization reasons, and we would like to encourage this. The trusted JSON-RPC server should be used as a back-up solution only in case it is not possible to run a node locally.

In order to be more user-friendly, a node run locally is very often a light client that doesn't hold the storage of the blockchain in its memory. The JSON-RPC functions are designed having in mind the fact that the target of the function calls might be a light client that doesn't have all the needed information immediately available.

End-user-facing applications generally rarely need to access older blocks. They usually care only about the storage of the finalized block and of the best block.

End-user-facing applications would normally not directly use the JSON-RPC interface, but an intermediary layer library built on top of the JSON-RPC interface. The JSON-RPC interface is a bit complicated to use, in a exchange for making functions more explicit and predictable.

An end-user-facing application is typically a website that the end-user visits. Both in the case of a locally-run node and in the case of a remote node, the JSON-RPC server is subject to attacks by malicious applications as an application can ask the end-user's browser to send millions of requests to the server. For this reason, it is important for the JSON-RPC server to resist to some degree to attacks (both DoS attacks and vulnerabilities), and thus for the JSON-RPC interface to not require behaviors that contradict DoS resilience.

When calls are made against a node run locally, the bandwidth consumption and latency of the JSON-RPC functions isn't very important. However, the JSON-RPC functions have a very precise behavior, in order to avoid situations where an ambiguity in what the JSON-RPC client desires leads the JSON-RPC server to use more bandwidth than is strictly required.

When calls are made against a trusted JSON-RPC server, the bandwidth consumption and latency are more important. However, one should keep in mind that we would like to discourage trusted JSON-RPC servers.

Node operators

DevOps that are administering a node (be it a full node, a validator, or an archive node) want to be able to know whether their node is operating properly, and might want to change some configuration options while the node is running.

DevOps are usually familiar with bash scripts. In order to make their life easier, the functions in the JSON-RPC interface that are relevant to them are usable with just a few CLI tools. The websocat CLI tool is probably the easiest way to communicate over a WebSocket connection at the time of writing of this document.

DevOps shouldn't have to use unstable functions when writing bash scripts. They want to be able to run scripts automatically in the background without them breaking. As such, the functions that they need are stable.

Since scripts usually run on the same machine or a machine in the same data center as the target of the JSON-RPC function calls, the bandwidth consumption and latency of the JSON-RPC functions isn't very important for this usage.

Debugging developers

Core/parachain developers want to be able to look at the on-chain storage or the internal state of a node, for debugging purposes.

When debugging the chain or the node implementation, developers rarely care about the stability of the function they use. They perform manual function calls using some tools (either a UI or a CLI tool) and throw away the function call once they're finished debugging.

In order to accomodate this audience, it should be relatively easy to perform a JSON-RPC function call manually, and developers should be able to easily add to the interface new semi-temporary JSON-RPC functions specific to their debugging needs.

The target of the JSON-RPC function calls is either their own node, or a specific node that has encountered an issue, and the bandwidth consumption and latency of the JSON-RPC functions isn't very important for this usage.

Oracles and bridges

Oracles and bridges consist for example in cryptocurencies exchanges or any software that programmatically interacts with a chain.

Contrary to end-user-facing applications, no human is interfacing directly or indirectly with the JSON-RPC client. Everything is automatic. However, everything said in the section about end-user-facing applications also applies here.

Archivers

Archivers are websites or applications that want to look at the past state of the chain.

In order to accomodate this audience, the JSON-RPC interface should provide functions that lets you access any block of the chain, and the storage of the chain at any block. Note that pruned blocks, in other words blocks that aren't descendants of the latest finalized blocks, are out of scope of this use case as inspecting them isn't considered useful for archiving purposes.

Apart from this difference, this category is a mix between "end-user-facing applications" and "node operators".

Archivers expect the JSON-RPC function calls to be stable and easy to use. They normally don't pay too much attention to performances and don't optimize their code, as long as everything is relatively fast.

Why JSON-RPC?

The JSON-RPC protocol has been chosen over other protocols such as gRPC due to its simplicity.

Note: Protocol specification formats such as https://open-rpc.org/ have been considered, but are unfortunately lacking the capabilities to describe subscriptions, and their interest is therefore limited.

Grouping functions and node capabilities

All JSON-RPC functions in this interface are distributed between so-called groups.

The group a JSON-RPC function belongs to is indicated by the prefix_ in its name. This prefix includes a version number. For example, in foo_v1_bar the prefix is foo_v1.

Node capabilities

This JSON-RPC interface is intended to be implemented on various types of nodes:

  • Full nodes, which hold in their database all block headers and bodies, plus the storage of recent blocks.
  • Light clients, which only know the headers of recent blocks.
  • Archive nodes, which hold in their database all block headers, bodies, and storage of every past block.
  • Plain databases that aren't actually connected to the peer-to-peer network of the blockchain.

These various node implementations have different capabilities, and it is normal for some implementations to only support some functions and not others. It doesn't make sense for example for a light client to support a function that rotates keys.

JSON-RPC server must always support the rpc_methods function, and clients should use this function to determine which other functions are supported.

In order to not introducing too much confusion and complexity, supporting a group of functions is all or nothing. Either all functions in a group are supported, or none of them. This rule reduces the complexity that a JSON-RPC client has to face to determine what a JSON-RPC server is capable of.

Upgradability

A group name must always include a version number. This version number is part of the group name it self, and consequently foo_v1 and foo_v2 should be considered as two completely separate, unrelated, groups.

As explained in the previous section, some nodes might support some groups and not others. For example, some nodes might support only foo_v1, some others only foo_v2, some others both, and some others neither.

Each group must be self-contained, and not build on top of functions of a different group. For example, foo_v1_start can only be stopped with foo_v1_stop and foo_v2_start can only be stopped with foo_v2_stop. foo_v1_stop must not be able to stop foo_v2_start and vice versa. For this reason, if the version number of a group is increased, all functions that are still relevant should be duplicated in the new group.

Having a clear version number makes it clear for developers that some functions (with a higher version number) are preferred over some others. For example, when foo_v2 is introduced, developers automatically understand that foo_v1 is soft-deprecated.

Unstable functions

No guarantee is offered as to the stability of functions with unstable as their version number. They can disappear, get renamed, change parameters, or change return value without any warning.

For obvious reasons, higher-level applications should not rely on unstable functions. Please open an issue in this repository if some critical feature is missing or is only covered by an unstable function.

The presence of unstable functions in the interface exposed by a JSON-RPC server is not a problem by itself. Not all functions are destined to be stabilized, and it is normal for some functions to remain unstable forever.

Unstable functions are especially useful for core/parachain developers to add debugging utilities to their client implementation. For example, if a developer wants to investigate the list of Grandpa votes, they could introduce a function named grandpa_unstable_votes.

It is expected that all new functions added to this interface go through a testing phase during which they're unstable.

DoS attacks resilience

This page is at the same time a small guide explaining how to handle DoS attacks, and explains the motivation behind some design decisions.

A denial-of-service (DoS) attack consists in consuming all the resources (bandwidth, disk I/O, CPU, or memory) of the target in order to prevent the target from properly serving legitimate users. It is possible to mitigate a DoS attack by identifying the attackers and filtering them out, but this is never done by the final JSON-RPC server that actually processes requests. From the point of view of the final JSON-RPC server, a DoS attack is the same thing as being under very heavy load.

Because the resources available to the target are bounded, it is fundamentally impossible to claim to be able to resist to all DoS attacks. It is, however, possible to optimize and properly distribute resources consumption in order to increase the load that the target is capable of handling.

Additionally, and maybe more importantly, it is important to guarantee a good quality of service even when under heavy load.

Bounded queues

In order to provide a good quality of service, the time between the moment a request is received by the server and the moment the response is sent back must be short. In order to achieve this, the CPU operations and I/O operations needed to handle the request must not take too much time. The only way to guarantee this is by limiting the number of CPU or I/O operations that can be performed concurrently. As an example, if you try to read 5000 files at once, some of these file reads will take a long time, and the only way to guarantee that any file read is always short is to not start too many at the same time.

This is generally achieved by creating a certain number of threads (lightweight or not) dedicated to handling a specific CPU or I/O operation. By doing so, you are guaranteed that the number of these CPU or I/O operations that are being executed simultaneously will never exceed the number of threads dedicated to them. When an operation needs to be performed, it is put in a FIFO queue in order to be picked up by these threads. It is important for the sizes of all queues to be bounded, in order for memory usage to be bounded as well, and as otherwise operations could take a long time being stuck in a queue.

Queues should be bounded such that, even when they are nearly full, the time between the moment when an operation enters the queue and the moment when it is processed is reasonable. A bound that is too low can potentially lead to a decrease in performance due to an excess of context switches, but this is in practice rarely a problem.

When a queue of operations is full, the dispatcher should simply wait for some space to be available. This is called back-pressure, as the dispatcher is intentionally slowed down as well. If the dispatcher is itself responsible for processing some operations in a queue, this processing is paused, and that queue is slowed down as well, which might cause more back-pressure to be propagated elsewhere.

This back-pressure must be propagated all the way to the TCP socket. When the code receiving requests from the TCP socket is unable to move forward because a queue is full, it should in turn stop receiving data from the TCP socket. This will in turn cause the Linux kernel to not increase the TCP window size, which propagates the back-pressure to the JSON-RPC client, slowing down this JSON-RPC client.

In multiplexing situations, where multiple threads (lightweight or not) all try to insert elements in a single bounded queue, it is important for the distribution of items to have an acceptable distribution. It must not happen that some threads are able to insert elements very quickly while some others need to sleep for a long time before their element is inserted.

Thanks to all these mechanisms, the amount of CPU and I/O consumed by all JSON-RPC clients connected to the server combined is bounded and uniformly distributed between each JSON-RPC client, guaranteeing that they are equally slowed down. In order to guarantee a good quality of service to these clients, the final step is simply to limit the number of JSON-RPC clients that are allowed to connect. The precise limit depends on the capabilities of the machine running the JSON-RPC server.

Dispatching a notification shouldn't wait

Some JSON-RPC functions in the API let clients subscribe to some events, such as a new block, meaning that the server must send a notification to the client when that specific event happens.

One important thing to keep in mind is that these events, generated by the blockchain node, shouldn't be back-pressured. We do not want to slow down for example the reception of blocks by the node because the JSON-RPC server isn't capable of following the rhythm.

The events coming from the blockchain node can be seen as a stream. This stream can briefly be back-pressured, but ideally as little as possible. Whatever threads are on the receiving side of this stream of events should therefore consist only of CPU-only non-blocking operations.

However, sending a message to a JSON-RPC client might take a long time, in case the client has (intentionally or not) little bandwidth. The threads that are receiving the stream of events should never wait for a client to be ready to accept more data before sending a notification to it. If the client isn't ready, then the notification must either be added to a send queue or simply discarded. Because queues must be bounded, it is unavoidable to sometimes have to discard some notifications.

Consequently, all functions that consist in sending notifications must be designed having in mind that the queue of notifications to send out must be bounded to a certain value. For example, the queue of notifications for transactionWatch_v1_submitAndWatch must have a size of 3. When the queue is full, new notifications must overwrite the notifications already in the queue. The design of all JSON-RPC functions should take into account the fact that this shouldn't result in a loss of important information for the JSON-RPC client.

Distinguishing between light and heavy calls

When implementing bounded queues, one should avoid a situation where some elements in the queue are very quick to be processed while some others very slow.

During normal operations one will decide on the size of the queue and number of processing threads based on the average time it takes to process a queue element, but during a DoS attack the attacker can deliberately push a larger number of elements that are heavy to process. For example, with a single queue of incoming JSON-RPC requests, an attacker can intentionally submit requests that take a long time to process, in order to fill the queue more quickly.

For this reason, it is a good idea to sometimes split some queues, where light operations go on one queue and heavy operations on a different one.

This is part of the reason why JSON-RPC functions are split by groups. In particular, the archive-prefixed JSON-RPC functions are, due to their disk access, considered as heavy and should be processed on a different queue than the other JSON-RPC functions. Under heavy load, it is likely that the processing of archive-prefixed JSON-RPC functions will be slowed down a lot, while the processing of other functions will be less impacted.

Enforced limits

In order to limit the memory consumption and the latency of processing requests, it is important that requests that involve storing some state on the JSON-RPC server in the long term are subject to limits.

In particular:

  • The number of JSON-RPC clients simultaneously connected must be bounded.
  • The number of active subscriptions (i.e. where the server sends a notification to the client when something happens) must be bounded.
  • The number of pinned blocks (in the context of chainHead_v1_follow) must be bounded.

The limits on the number of active subscriptions and pinned block should be enforced per client, as it would be undesirable to limit the number of subscriptions/pinned blocks available to some clients just because other clients are using a lot of them. Since the number of clients is bounded, enforcing these limits per client also automatically enforces these limits globally.

Note that the limit on the number of JSON-RPC clients simultaneously connected should be enforced by rejecting new clients with a 503 status code, rather than for example closing the TCP socket altogether. This makes it possible for other software in front of the JSON-RPC server, such as a load balancer, to understand what the problem is.

Load balancers

In case of a publicly-accessible JSON-RPC server, it is unlikely for a single server to be able to handle the load of all the end users running JSON-RPC clients.

For this reason, the JSON-RPC interface is suitable for load balancer. A single load balancer can serve all the requests and dispatch them to multiple nodes.

With the exception of the JSON-RPC functions prefixed with ̀sudo, none of the JSON-RPC functions require stick sessions. In other words, if a JSON-RPC client disconnects from the load balancer, then reconnects, the load balancer can wire the connection to a different underlying server than the one it previously used. However, as long as a JSON-RPC client is connected, all its messages must be redirected to the same underlying JSON-RPC server. The underlying server can change only after a disconnect/reconnect.

Unsafe JSON-RPC functions

In the legacy JSON-RPC interface, a flag on the server renders some JSON-RPC functions inaccessible. This is important in order to prevent the general public from accessing the node's configuration and sensitive data. In this new JSON-RPC interface, all the functions that shouldn't be publicly-accessible are prefixed with sudo.

This makes it possible to insert proxies that filter incoming requests. All such proxy has to do is parse JSON-RPC requests and detect whether the function name starts with a prefix that isn't sudo_.

In practice, however, such proxy doesn't seem to exist, and JSON-RPC server implementations should continue to provide a configuration option (such as a CLI flag) to disable sudo-prefixed functions.

API specification

Note that all parameters are mandatory unless specified otherwise. All functions support two ways of calling them: either by passing an array with an ordered list of parameters, or by passing an object where parameters are named.

Any missing parameter, or parameter with an invalid format, should result in a JSON-RPC error being returned, as described in the JSON-RPC specification.

Any function returning an opaque subscription or operation ID ensures that this is returned before any related notifications are generated.

The functions within each respective category must be called from the same connection in order to work together.

Glossary

  • "hexadecimal-encoded" designates a binary value encoded as hexadecimal. The value must either be empty, or start with "0x" and contain an even number of characters.
  • "SCALE-encoded" designates a value encoded using the SCALE codec.
  • "Merkle value" is described in the Polkadot specification.

Introduction

Functions with the archive prefix allow obtaining the state of the chain at any point in the present or in the past.

These functions are meant to be used to inspect the history of a chain. They can be used to access recent information as well, but JSON-RPC clients should keep in mind that the chainHead functions could be more appropriate.

These functions are typically expensive for a JSON-RPC server, because they likely have to perform either disk accesses or network requests. Consequently, JSON-RPC servers are encouraged to put a global limit on the number of concurrent calls to archive-prefixed functions.

Usage

The JSON-RPC server exposes a finalized block height, which can be retrieved by calling archive_unstable_finalizedHeight.

Call archive_unstable_hashByHeight in order to obtain the hash of a block by its height.

If the height passed to archive_unstable_hashByHeight is inferior or equal to the value returned by archive_unstable_finalizedHeight, then it is always guaranteed that there is exactly one block with this hash. The JSON-RPC client can then call archive_unstable_header, archive_unstable_body, archive_unstable_storage, and archive_unstable_call in order to obtain details about the block with this hash. It is always guaranteed to return a value.

If the height passed to archive_unstable_hashByHeight is strictly superior to the value returned by archive_unstable_finalizedHeight, then archive_unstable_hashByHeight might return zero, one, or more blocks. Furthermore, the list of blocks being returned can change at any point. It is also possible to call archive_unstable_header, archive_unstable_body, archive_unstable_storage, and archive_unstable_call on these blocks, but these functions might return null even if their hash was previously returned by archive_unstable_hashByHeight.

archive_unstable_body

Parameters:

  • hash: String containing a hexadecimal-encoded hash of the header of the block whose body will be retrieved.

Return value: If a block with that hash is found, an array of strings containing the hexadecimal-encoded SCALE-codec-encoded transactions in that block. If no block with that hash is found, null.

If the block was previously returned by archive_unstable_hashByHeight at a height inferior or equal to the current finalized block height (as indicated by archive_unstable_finalizedHeight), then calling this method multiple times is guaranteed to always return non-null and always the same result.

If the block was previously returned by archive_unstable_hashByHeight at a height strictly superior to the current finalized block height (as indicated by archive_unstable_finalizedHeight), then the block might "disappear" and calling this function might return null at any point.

archive_unstable_call

Parameters:

  • hash: String containing the hexadecimal-encoded hash of the header of the block to make the call against.
  • function: Name of the runtime entry point to call as a string.
  • callParameters: Hexadecimal-encoded SCALE-encoded value to pass as input to the runtime function.

Return value:

  • If no block with that hash exists, null.
  • If the call was successful, { "success": true, "value": ... } where the value is a string containing the hexadecimal-encoded SCALE-encoded value returned by the runtime.
  • If the call wasn't successful, { "success": false, "error": ... } where the error is a human-readable message indicating the problem.

The JSON-RPC server must invoke the entry point of the runtime of the given block using the storage of the given block.

Note: The runtime is still allowed to call host functions with side effects, however these side effects must be discarded. For example, a runtime function call can try to modify the storage of the chain, but this modification must not be actually applied. The only motivation for performing a call is to obtain the return value.

If the height of the block hash provided is less than or equal to the current finalized block height (which can be obtained via archive_unstable_finalizedHeight), then calling this method multiple times is guaranteed to always return non-null and always the same result (except for the error message which is allowed to change).

If the height of the block hash provided is greater than the current finalized block height, then the block might be pruned at any time and calling this method may return null.

Possible errors

  • A JSON-RPC error if the provided parameters are invalid.
  • { "success": false, "error": ... } is returned if a problem happens during the call, such as a Wasm trap, runtime panics, function not supported etc. The error isn't meant to be shown to end users, but is for developers to understand the problem.

archive_unstable_finalizedHeight

Parameters: none

Return value: An integer height of the current finalized block of the chain.

The value returned by this function must only ever increase over time. In other words, if calling this function returns N, then calling it again later must return a value superior or equal to N.

When implemented on a load balancer, keep in mind that the other functions of the archive namespace must always return the same value when the block's height is inferior or equal to the finalized block height indicated by this function. One possible implementation strategy is for this function to call archive_unstable_finalizedHeight on every node being load balanced and return the smallest value.

This function is expected to be called rarely by the JSON-RPC client. This function exists in order to give an indication of which blocks are accessible, and not for JSON-RPC clients to follow the finalized block. The archive namespace isn't meant to follow the head of the chain, and chainHead should be used instead in that situation.

archive_unstable_genesisHash

Parameters: none

Return value: String containing the hexadecimal-encoded hash of the genesis block of the chain.

This function is a simple getter. The JSON-RPC server is expected to keep in its memory the hash of the genesis block.

The value returned by this function must never change for the lifetime of the connection between the JSON-RPC client and server.

archive_unstable_hashByHeight

Parameters:

  • height: Integer representing the height of the block.

Return value: Array (possibly empty) of strings containing a hexadecimal-encoded hash of a block header.

The JSON-RPC client must find the blocks (zero, one, or more) whose height is the one passed as parameter. If the height is inferior or equal to the finalized block height, then only finalized blocks must be fetched and returned.

It returns an array of hexadecimal-encoded hashes corresponding to the blocks of this height that are known to the node. If the height is inferior or equal to the finalized block height, the array must contain exactly one entry. Furthermore, calling this function multiple times with the same height inferior or equal to the finalized block height must always return the same result.

archive_unstable_header

Parameters:

  • hash: String containing the hexadecimal-encoded hash of the header to retreive.

Return value: If a block with that hash is found, a string containing the hexadecimal-encoded SCALE-codec encoding header of the block. If no block with that hash is found, null.

If the block was previously returned by archive_unstable_hashByHeight at a height inferior or equal to the current finalized block height (as indicated by archive_unstable_finalizedHeight), then calling this method multiple times is guaranteed to always return non-null and always the same result.

If the block was previously returned by archive_unstable_hashByHeight at a height strictly superior to the current finalized block height (as indicated by archive_unstable_finalizedHeight), then the block might "disappear" and calling this function might return null at any point.

archive_unstable_storage

Parameters:

  • hash: String containing a hexadecimal-encoded hash of the header of the block whose storage to fetch.
  • items: Array of objects. The structure of these objects is found below.
  • childTrie: null for main storage look-ups, or a string containing the hexadecimal-encoded key of the child trie of the "default" namespace.

Each element in items must be an object containing the following fields:

  • key: String containing the hexadecimal-encoded key to fetch in the storage.
  • type: String equal to one of: value, hash, closestDescendantMerkleValue, descendantsValues, descendantsHashes.
  • paginationStartKey: This parameter is optional and should be a string containing the hexadecimal-encoded key from which the storage iteration should resume. This parameter is only valid in the context of descendantsValues and descendantsHashes.

Return value: String containing an opaque value representing the operation.

Notifications format

This function will later generate one or more notifications in the following format:

{
    "jsonrpc": "2.0",
    "method": "archive_unstable_storageEvent",
    "params": {
        "subscription": "...",
        "result": ...
    }
}

Where subscription is the value returned by this function, and result can be one of:

storage

{
    "event": "storage",
    "key": "0x...",
    "value": "0x...",
    "hash": "0x...",
    "closestDescendantMerkleValue": "0x...",
    "childTrieKey": "0x...",
}

The notification corresponds to a storage response for one of the requested items.

  • key: String containing the hexadecimal-encoded key of the storage entry. This is guaranteed to be equal to one of the keys provided for type equal to value, hash or closestDescendantMerkleValue. For queries of type descendantsValues or descendantsHashes, key is guaranteed to start with one of the keys provided.

  • value (optional): String containing the hexadecimal-encoded value of the storage entry. This field is present when the type of the query was "value" or "descendantsValues".

  • hash (optional): String containing the hexadecimal-encoded hash of the storage entry. This field is present when the type of the query was "hash" or "descendantsHashes".

  • closestDescendantMerkleValue (optional): String containing the hexadecimal-encoded Merkle value of the closest descendant of key (including branch nodes). This field is present when the type of the query was "closestDescendantMerkleValue". The trie node whose Merkle value is indicated in closestDescendantMerkleValue is not indicated, as determining the key of this node might incur an overhead for the JSON-RPC server. The Merkle value is equal to either the node value or the hash of the node value, as defined in the Polkadot specification.

  • childTrieKey (optional): String containing the hexadecimal-encoded key of the child trie of the "default" namespace if the storage entry is part of a child trie. If the storage entry is part of the main trie, this field is not present.

storageDone

{
    "event": "storageDone",
}

This event is always generated after all storage events have been generated.

No more events will be generated after a storageDone event.

storageError

{
    "event": "storageError",
    "error": "...",
}

error is a human-readable error message indicating why the call has failed. This string isn't meant to be shown to end users, but is for developers to understand the problem.

No more events will be generated after a storageError event.

Overview

For each item in items, the JSON-RPC server must start obtaining the value of the entry with the given key from the storage, either from the main trie or from childTrie. If type is descendantsValues or descendantsHashes, then it must also obtain the values of all the descendants of the entry.

For the purpose of storage requests, the trie root hash of the child tries of the storage can be found in the main trie at keys starting the bytes of the ASCII string :child_storage:. This behaviour is consistent with all the other storage-request-alike mechanisms of Polkadot and Substrate-based chains, such as host functions or libp2p network requests.

If the height of the block hash provided is less than or equal to the current finalized block height (which can be obtained via archive_unstable_finalizedHeight), then calling this method with the same parameters will always return the same response. If the height of the block hash provided is greater than the current finalized block height, then the block might be pruned at any time and calling this method may return null.

This function should be used when the target block is older than the blocks reported by chainHead_v1_follow. Use chainHead_v1_storage if instead you want to retrieve the storage of a block obtained by the chainHead_v1_follow.

If items contains multiple identical or overlapping queries, the JSON-RPC server can choose whether to merge or not the items in the result. For example, if the request contains two items with the same key, one with hash and one with value, the JSON-RPC server can choose whether to generate two item objects, one with the value and one with the hash, or only a single item object with both hash and value set.

It is allowed (but discouraged) for the JSON-RPC server to provide the same information multiple times in the result, for example providing the value field of the same key twice. Forcing the JSON-RPC server to de-duplicate items in the result might lead to unnecessary overhead.

Possible errors

  • A JSON-RPC error can be generated if the JSON-RPC client has to many active calls to archive_unstable_storageDiff.
  • A JSON-RPC error with error code -32602 is generated if one of the parameters doesn't correspond to the expected type (similarly to a missing parameter or an invalid parameter type).

Introduction

Functions with the chainHead prefix allow tracking the head of the chain (in other words, the latest new and finalized blocks) and their storage.

The most important function in this category is chainHead_v1_follow. It is the first function that is the user is expected to call, before (almost) any other. chainHead_v1_follow returns the current list of blocks that are near the head of the chain, and generates notifications about new blocks. The chainHead_v1_body, chainHead_v1_call, chainHead_v1_header and chainHead_v1_storage functions can be used to obtain more details about the blocks that have been reported by chainHead_v1_follow.

These functions are the functions most of the JSON-RPC clients will most commonly use. A JSON-RPC server implementation is encouraged to prioritize serving these functions over other functions, and to put pinned blocks in a quickly-accessible cache.

Usage

This section contains a small beginner guide destined for JSON-RPC client users.

This beginner guide shows how to use the chainHead functions in order to know the value of a certain storage item.

  1. Call chainHead_v1_follow with withRuntime: true to obtain a followSubscription. This followSubscription will need to be passed when calling most of the other chainHead-prefixed functions. If at any point in the future the JSON-RPC server sends back a {"event": "stop"} notification, jump back to step 1.

  2. When the JSON-RPC server sends back a {"event": "initialized"} notification with subscription equal to your followSubscription, store the value of finalizedBlockHashes found in that notification. If finalizedBlockHashes contains multiple values, you should use the last entry. The last entry corresponds to the current finalized block of the chain. You must then call chainHead_v1_unpin, passing the followSubscription and each of the other values in finalizedBlockHashes.

  3. Make sure that the finalizedBlockRuntime field of the event contains a field type containing valid, and that the spec -> apis object contains a key 0xd2bc9897eed08f15 whose value is 3. This verifies that the runtime of the chain supports the Metadata_metadata function that we will call below (0xd2bc9897eed08f15 is the 64bits blake2 hash of the ASCII string Metadata). If it is not the case, enter panic mode as the client software is incompatible with the current state of the blockchain.

  4. Call chainHead_v1_call with hash equal to the current finalized block you've just retrieved, function equal to Metadata_metadata, and an empty callParameters.

  5. If the JSON-RPC server sends back a {"event": "operationInaccessible"} notification, jump back to step 4 to try again. If the JSON-RPC server sends back a {"event": "operationError"} notification, enter panic mode. If the JSON-RPC server instead sends back a {"event": "operationCallDone"} notification, save the return value.

  6. The return value you've just saved is called the metadata, prefixed with its SCALE-compact-encoded length. You must decode and parse this metadata. How to do this is out of scope of this small guide. The metadata contains information about the layout of the storage of the chain. Inspect it to determine how to find the storage item you're looking for.

  7. In order to obtain a value in the storage, call chainHead_v1_storage with hash equal to the current finalized block, key the desired key, and type equal to value. If the JSON-RPC server instead sends back a {"event": "operationInaccessible"} notification, the value you're looking for is unfortunately inaccessible and you can either try again or give up. If the JSON-RPC server instead sends back a {"event": "operationStorageItems"} notification, you can find the desired value inside.

  8. You are strongly encouraged to maintain a Set of the blocks where the runtime changes. Whenever a {"event": "newBlock"} notification is received with subscription equal to your followSubcriptionId, and newRuntime is non-null, store the provided blockHash in this set.

  9. Whenever a {"event": "finalized"} notification is received with subscription equal to your followSubcriptionId, call chainHead_v1_unpin with the current finalized block hash, the prunedBlockHashes, and with each value in finalizedBlockHashes of the finalized event except for the last entry. The last entry in finalizedBlockHashes becomes your new current finalized block. If one or more entries of finalizedBlockHashes are found in your Set (see step 7), remove them from the set and jump to step 3 as the metadata has likely been modified. Otherwise, jump to step 7.

Note that these steps are a bit complicated. Any serious user of the JSON-RPC interface is expected to implement high-level wrappers around the various JSON-RPC functions.

For example, if multiple storage values are desired, only step 7 should be repeated once per storage item. All other steps are application-wide.

chainHead_v1_body

Parameters:

  • followSubscription: An opaque string that was returned by chainHead_v1_follow.
  • hash: String containing a hexadecimal-encoded hash of the header of the block whose body will be retrieved.

Return value: A JSON object.

The JSON object returned by this function has one the following formats:

Started

{
    "result": "started",
    "operationId": ...
}

This return value indicates that the request has successfully started.

operationId is a string containing an opaque value representing the operation.

LimitReached

{
    "result": "limitReached"
}

This return value indicates the request couldn't be started because the server is overloaded, or that the followSubscription is invalid or stale.

The JSON-RPC client should try again after an on-going chainHead_v1_storage, chainHead_v1_body, or chainHead_v1_call operation finishes.

The JSON-RPC server must accept at least 16 concurrent operations for any given chainHead_v1_follow subscription. In other words, as long as the JSON-RPC client makes sure that no more than 16 operations are in progress at any given item, it is guaranteed that all of its operations will be accepted by the JSON-RPC server. For this purpose, each item requested through chainHead_v1_storage counts as one operation, and each call to chainHead_v1_body and chainHead_v1_call counts as one operation.

Overview

The JSON-RPC server must start obtaining the body (in other words the list of transactions) of the given block.

The progress of the operation is indicated through operationBodyDone, operationInaccessible, or operationError notifications generated on the corresponding chainHead_v1_follow subscription.

The operation continues even if the target block is unpinned with chainHead_v1_unpin.

This function should be seen as a complement to chainHead_v1_follow, allowing the JSON-RPC client to retrieve more information about a block that has been reported. Use archive_unstable_body if instead you want to retrieve the body of an arbitrary block.

Possible errors

  • If the networking part of the behaviour fails, then a {"event": "operationInaccessible"} notification is generated (as explained above).
  • If the followSubscription is invalid or stale, then "result": "limitReached" is returned (as explained above).
  • A JSON-RPC error with error code -32801 is generated if the block hash passed as parameter doesn't correspond to any block that has been reported by chainHead_v1_follow, or the block hash has been unpinned.
  • A JSON-RPC error with error code -32602 is generated if one of the parameters doesn't correspond to the expected type (similarly to a missing parameter or an invalid parameter type).
  • A JSON-RPC error with error code -32603 is generated if the JSON-RPC server cannot interpret the block (hardware issues, corrupted database, disk failure etc).

chainHead_v1_call

Parameters:

  • followSubscription: An opaque string that was returned by chainHead_v1_follow. The withRuntime parameter of the call must have been equal to true.
  • hash: String containing the hexadecimal-encoded hash of the header of the block to make the call against.
  • function: Name of the runtime entry point to call as a string.
  • callParameters: Hexadecimal-encoded SCALE-encoded value to pass as input to the runtime function.

Return value: A JSON object.

The JSON object returned by this function has one the following formats:

Started

{
    "result": "started",
    "operationId": ...
}

This return value indicates that the request has successfully started.

operationId is a string containing an opaque value representing the operation.

LimitReached

{
    "result": "limitReached"
}

This return value indicates the call couldn't be started because the server is overloaded, or that the followSubscription is invalid or stale.

The JSON-RPC client should try again after an on-going chainHead_v1_storage, chainHead_v1_body, or chainHead_v1_call operation finishes.

The JSON-RPC server must accept at least 16 concurrent operations for any given chainHead_v1_follow subscription. In other words, as long as the JSON-RPC client makes sure that no more than 16 operations are in progress at any given item, it is guaranteed that all of its operations will be accepted by the JSON-RPC server. For this purpose, each item requested through chainHead_v1_storage counts as one operation, and each call to chainHead_v1_body and chainHead_v1_call counts as one operation.

Overview

The JSON-RPC server must invoke the entry point of the runtime of the given block using the storage of the given block.

Note: The runtime is still allowed to call host functions with side effects, however these side effects must be discarded. For example, a runtime function call can try to modify the storage of the chain, but this modification must not be actually applied. The only motivation for performing a call is to obtain the return value.

The progress of the operation is indicated through operationCallDone, operationInaccessible, or operationError notifications generated on the corresponding chainHead_v1_follow subscription.

The operation continues even if the target block is unpinned with chainHead_v1_unpin.

This function should be seen as a complement to chainHead_v1_follow, allowing the JSON-RPC client to retrieve more information about a block that has been reported. Use archive_unstable_call if instead you want to call the runtime of an arbitrary block.

Note: This can be used as a replacement for the legacy state_getMetadata, system_accountNextIndex, and payment_queryInfo functions.

Possible errors

  • If the networking part of the behaviour fails, then an {"event": "operationInaccessible"} notification is generated (as explained above).
  • If the followSubscription is invalid or stale, then "result": "limitReached" is returned (as explained above).
  • If the method to call doesn't exist in the Wasm runtime of the chain, then an {"event": "operationError"} notification is generated.
  • If the runtime call fails (e.g. because it triggers a panic in the runtime, running out of memory, etc., or if the runtime call takes too much time), then an {"event": "operationError"} notification is generated.
  • A JSON-RPC error with error code -32801 is generated if the block hash passed as parameter doesn't correspond to any block that has been reported by chainHead_v1_follow, or the block hash has been unpinned.
  • A JSON-RPC error with error code -32802 is generated if the followSubscription corresponds to a follow where withRuntime was false.
  • A JSON-RPC error with error code -32602 is generated if one of the parameters doesn't correspond to the expected type (similarly to a missing parameter or an invalid parameter type).

About callParameters

Runtime entry points typically accept multiple input parameters, but this JSON-RPC function accepts as parameter a single hexadecimal-encoded value. The reason is that all the parameters are concatenated together before performing the call anyway. There is no reason to force JSON-RPC clients to provide a proper division between the various parameters if they are all concatenated in the implementation anyway.

chainHead_v1_continue

Parameters:

  • followSubscription: An opaque string that was returned by chainHead_v1_follow.
  • operationId: An opaque string that was returned by chainHead_v1_storage.

Return value: null

Resumes a storage fetch started with chainHead_v1_storage after it has generated an operationWaitingForContinue event.

Has no effect if the operationId is invalid or refers to an operation that has emitted a {"event": "operationInaccessible"} event, or if the followSubscription is invalid or stale.

Possible errors

  • A JSON-RPC error with error code -32803 is generated if the followSubscription and operationId are valid but haven't generated a operationWaitingForContinue event.
  • A JSON-RPC error with error code -32602 is generated if one of the parameters doesn't correspond to the expected type (similarly to a missing parameter or an invalid parameter type).

chainHead_v1_follow

Parameters:

  • withRuntime: A boolean indicating whether the events should report changes to the runtime.

Return value: String containing an opaque value representing the operation.

Usage

This functions lets the JSON-RPC client track the state of the head of the chain: the finalized, non-finalized, and best blocks.

This function works as follows:

  • When called, returns an opaque followSubscription that can used to match events and in various other chainHead-prefixed functions.

  • Later, generates an initialized notification (see below) containing the hashes of the latest finalized blocks, and, if withRuntime is true, the runtime specification of the runtime of the current finalized block.

  • Afterwards, generates one newBlock notification (see below) for each non-finalized block currently in the node's memory (including all forks), then a bestBlockChanged notification. The notifications must be sent in an ordered way such that the parent of each block either can be found in an earlier notification or is the current finalized block.

  • When a new block arrives, generates a newBlock notification. If the new block is also the new best block of the node, also generates a bestBlockChanged notification.

  • When the node finalizes a block, generates a finalized notification indicating which blocks have been finalized and which blocks have been pruned.

  • If the node is overloaded and cannot avoid a gap in the notifications, or in case of a warp syncing, or if the maximum number of pinned blocks is reached (see below), generates a stop notification indicating that the subscription is now dead and must be re-created. No more notifications will be sent out on this subscription.

Note: This list of notifications makes it very easy for a JSON-RPC client to follow just the best block updates (listening to just bestBlockChanged events) or follow just the finalized block updates (listening to just initialized and finalized events). It is however not possible to easily figure out whether the runtime has been modified when these updates happen. This is not problematic, as anyone using the JSON-RPC interface naively propably doesn't need to account for runtime changes anyway.

Additionally, the chainHead_v1_body, chainHead_v1_call, and chainHead_v1_storage JSON-RPC function might cause the subscription to produce additional notifications.

The withRuntime parameter

If the withRuntime parameter is true, then blocks shouldn't (and can't) be reported to JSON-RPC clients before the JSON-RPC server has finished obtaining the runtime specification of the blocks that it reports. This includes the finalized blocks reported in the initialized event.

If withRuntime is false, then the initialized event must be sent back quickly after the function returns. If withRuntime is true, then the JSON-RPC server can take as much time as it wants to send back the initialized event.

For this reason, blocks might be reported more quickly when withRuntime is false.

Note: It is unlikely that high-level UIs built on top of a JSON-RPC client can do anything before the JSON-RPC server has access to the runtime. Consequently, they should consider the time before the initialized event is generated as a loading time. During this loading time, the JSON-RPC server might be performing downloads and CPU-intensive operations. This loading time can be considered as a replacement for the isSyncing field of the legacy system_health JSON-RPC call.

If a JSON-RPC client wants to be sure to receive an initialized event quickly but is also interested in the runtime, it is encouraged to create two subscriptions: one with withRuntime: true and one with withRuntime: false.

Notifications format

This function will later generate one or more notifications in the following format:

{
    "jsonrpc": "2.0",
    "method": "chainHead_v1_followEvent",
    "params": {
        "subscription": "...",
        "result": ...
    }
}

Where subscription is the value returned by this function, and result can be one of:

initialized

{
    "event": "initialized",
    "finalizedBlockHashes": [
        "0x0000000000000000000000000000000000000000000000000000000000000000",
        "0x0000000000000000000000000000000000000000000000000000000000000000"
    ],
    "finalizedBlockRuntime": ...
}

The initialized event is always the first event to be sent back, and is only ever sent back once per subscription.

finalizedBlockHashes contains a list of finalized blocks, of at least one element, ordered by increasing block number. The last element in this list is the current finalized block.

Note: The RPC server is encouraged to include around 1 minute of finalized blocks in this list. These blocks are also pinned, and the user is responsible for unpinning them. This information is useful for the JSON-RPC clients that resubscribe to the chainHead_v1_follow function after a disconnection.

finalizedBlockRuntime is present if and only if withRuntime, the parameter to this function, is true. It corresponds to the last finalized block from the finalizedBlockHashes list.

The format of finalizedBlockRuntime is described later down this page.

newBlock

{
    "event": "newBlock",
    "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    "parentBlockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    "newRuntime": ...
}

The newBlock indicates a new non-finalized block.

parentBlockHash is guaranteed to be equal either to the current finalized block hash, or to a block reported in an earlier newBlock event.

newRuntime must not be present if withRuntime, the parameter to this function, is false. newRuntime must be null if the runtime hasn't changed compared to its parent.

If present and non-null, the format of newRuntime is the same as the finalizedBlockRuntime field in the initialized event and is explained later down this page.

bestBlockChanged

{
    "event": "bestBlockChanged",
    "bestBlockHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}

The bestBlockChanged event indicates that the given block is now considered to be the best block.

bestBlockHash is guaranteed to be equal either to the current finalized block hash, or to a block reported in an earlier newBlock event.

finalized

{
    "event": "finalized",
    "finalizedBlockHashes": [
        "0x0000000000000000000000000000000000000000000000000000000000000000",
        "0x0000000000000000000000000000000000000000000000000000000000000000"
    ],
    "prunedBlockHashes": [
        "0x0000000000000000000000000000000000000000000000000000000000000000",
        "0x0000000000000000000000000000000000000000000000000000000000000000"
    ]
}

The finalized event indicates that some of the blocks that have earlier been reported with newBlock events are now either finalized or no longer relevant.

finalizedBlockHashes contains a list of blocks that are now part of the finalized block, ordered by increasing block number. The last element in this list is the current finalized block.

prunedBlockHashes contains, in no particular order, a list of blocks that are not descendants of the latest finalized block. These blocks will never be finalized and can be discarded.

All items in finalizedBlockHashes and prunedBlockHashes are guaranteed to have been reported through earlier newBlock events.

The current best block, in other words the last block reported through a bestBlockChanged event, is guaranteed to either be the last item in finalizedBlockHashes, or to not be present in either finalizedBlockHashes or prunedBlockHashes.

operationBodyDone

{
    "event": "operationBodyDone",
    "operationId": ...,
    "value": [...]
}

operationId is a string returned by chainHead_v1_body.

The operationBodyDone event indicates that an operation started with chainHead_v1_body was successful.

value is an array of strings containing the hexadecimal-encoded SCALE-encoded extrinsics found in the block.

Note: Note that the order of extrinsics is important. Extrinsics in the chain are uniquely identified by a (blockHash, index) tuple.

No more event will be generated with this operationId.

operationCallDone

{
    "event": "operationCallDone",
    "operationId": ...,
    "output": "0x0000000..."
}

operationId is a string returned bychainHead_v1_call.

The operationCallDone event indicates that an operation started with chainHead_v1_call was successful.

output is the hexadecimal-encoded output of the runtime function call.

No more event will be generated with this operationId.

operationStorageItems

{
    "event": "operationStorageItems",
    "operationId": ...,
    "items": [
        {
            "key": "0x0000000...",
            "value": "0x0000000...",
            "hash": "0x0000000...",
            "closestDescendantMerkleValue": "0x000000..."
        },
        ...
    ]
}

operationId is a string returned by chainHead_v1_storage.

Yields one or more items that were found in the storage.

The key field is a string containing the hexadecimal-encoded key of the item. This key is guaranteed to start with one of the keys provided as parameter to chainHead_v1_storage. If the type parameter was "value", "hash", "closestDescendantMerkleValue", then it is also guaranteed to be equal to one of the keys provided as parameter to chainHead_v1_storage.

In the situation where the type parameter was "closestDescendantMerkleValue", the fact that key is equal to a key that was provided as parameter is necessary in order to avoid ambiguities when multiple items of type "closestDescendantMerkleValue" were requested.

The value field is set if this item corresponds to one of the requested items whose type was "value" or "descendantsValues". The value field is a string containing the hexadecimal-encoded value of the storage entry.

The hash field is set if this item corresponds to one of the requested items whose type was "hash" or "descendantsHashes". The hash field is a string containing the hexadecimal-encoded hash of the storage entry.

The closestDescendantMerkleValue field is set if this item corresponds to one of the requested items whose type was "closestDescendantMerkleValue". The trie node whose Merkle value is indicated in closestDescendantMerkleValue is not indicated, as determining the key of this node might incur an overhead for the JSON-RPC server. The Merkle value is equal to either the node value or the hash of the node value, as defined in the Polkadot specification.

operationWaitingForContinue

{
    "event": "operationWaitingForContinue",
    "operationId": ...
}

operationId is a string returned by chainHead_v1_storage.

The waitingForContinue event is generated after at least one "operationStorageItems" event has been generated, and indicates that the JSON-RPC client must call chainHead_v1_continue before more events are generated.

This event only ever happens if the type of one of the items provided as a parameter to chainHead_v1_storage was descendantsValues or descendantsHashes.

While the JSON-RPC server is waiting for a call to chainHead_v1_continue, it can generate an operationInaccessible event in order to indicate that it can no longer proceed with the operation. If that is the case, the JSON-RPC client can simply try again.

operationStorageDone

{
    "event": "operationStorageDone",
    "operationId": ...
}

operationId is a string returned by chainHead_v1_storage.

The operationStorageDone event indicates that an operation started with chainHead_v1_storage went well and all result has been provided through operationStorageItems events in the past.

If no operationStorageItems event was yielded for this operationId, then the storage doesn't contain a value at the given key.

No more event will be generated with this operationId.

operationInaccessible

{
    "event": "operationInaccessible",
    "operationId": ...
}

operationId is a string returned by chainHead_v1_body, chainHead_v1_call, or chainHead_v1_storage.

The operationInaccessible event is produced if the JSON-RPC server fails to retrieve either the block body or the necessary storage items for the given operation.

Contrary to the operationError event, repeating the same operation in the future might succeed.

No more event will be generated about this operationId.

operationError

{
    "event": "operationError",
    "operationId": ...,
    "error": "..."
}

operationId is a string returned by chainHead_v1_body, chainHead_v1_call, or chainHead_v1_storage.

The operationError event indicates a problem during the operation. In the case of chainHead_v1_call, this can include the function missing or a runtime panic. In the case of chainHead_v1_body or chainHead_v1_storage, this includes failing to parse the block header to obtain the extrinsics root hash or state root hash.

Contrary to the operationInaccessible event, repeating the same call in the future will not succeed.

error is a human-readable error message indicating why the call has failed. This string isn't meant to be shown to end users, but is for developers to understand the problem.

No more event will be generated about this operationId.

stop

{
    "event": "stop"
}

The stop event indicates that the JSON-RPC server was unable to provide a consistent list of the blocks at the head of the chain. This can happen because too many blocks have been pinned, because doing so would use an unreasonable amount of memory, or because a consensus mechanism creates a gap in the chain.

Note: In particular, warp syncing algorithms create a "jump" in the chain from a block to a much later block. Any subscription that is active when the warp syncing happens will receive a stop event.

No more event will be generated with this subscription.

Calling chainHead_v1_unfollow on a subscription that has produced a stop event is optional.

Pinning

The finalized blocks reported in the initialized event, and each subsequent block reported with a newBlock event, are automatically considered by the JSON-RPC server as pinned. A block is guaranteed to not leave the node's memory for as long as it is pinned, making it possible to call functions such as chainHead_v1_header on it. Blocks must be unpinned by the JSON-RPC client by calling chainHead_v1_unpin.

When a block is unpinned, on-going calls to chainHead_v1_body, chainHead_v1_call and chainHead_v1_storage against this block will still finish normally.

A block is pinned only in the context of a specific subscription. If multiple chainHead_v1_follow subscriptions exist, then each (subscription, block) tuple must be unpinned individually. Blocks stay pinned even if they have been pruned from the chain of blocks, and must always be unpinned by the JSON-RPC client.

The JSON-RPC server is strongly encouraged to enforce a limit to the maximum number of pinned blocks. If this limit is reached, it should then stop the subscription by emitting a stop event. This specification does not mention any specific limit, but it must be large enough for clients to be able to pin all existing non-finalized blocks and the finalized blocks that have been reported in the previous few seconds or minutes.

Note: A JSON-RPC client should call chainHead_v1_unpin only after it is sure to no longer be interested in a certain block. This typically happens after the block has been finalized or pruned. There is no requirement to call chainHead_v1_unpin as quickly as possible.

Note: JSON-RPC server implementers should be aware that the number of non-finalized blocks might grow to become very large, for example in the case where the finality mechanism of the chain has an issue. When enforcing a limit to the number of pinned blocks, care must be taken to not prevent the API from being unusable in that situation. A good way to implement this limit is to limit only the number of pinned finalized blocks.

Multiple subscriptions

The JSON-RPC server must accept at least 2 chainHead_v1_follow subscriptions per JSON-RPC client. Trying to open more might lead to a JSON-RPC error when calling chainHead_v1_follow. In other words, as long as a JSON-RPC client starts 2 or fewer chainHead_v1_follow subscriptions, it is guaranteed that this return value will never happen.

If a JSON-RPC client maintains mutiple chainHead_v1_follow subscriptions at the same time, it has no guarantee that the blocks reported by the various subscriptions are the same. While the finalized blocks reported should eventually be the same, it is possible that in the short term some subscriptions lag behind others.

Note: For example, imagine there exists two active chainHead_v1_follow subscriptions named A and B. Block N is announced on the peer-to-peer network and is announced to A. But then a sibling of block N gets finalized, leading to block N being pruned. Block N might never be announced to B.

About the runtime

The format of the finalizedBlockRuntime and newRuntime fields can be one of:

valid

{
    "type": "valid",
    "spec": {
        "specName": ...,
        "implName": ...,
        "specVersion": ...,
        "implVersion": ...,
        "transactionVersion": ...,
        "apis": [...],
    }
}

In normal situations, the type is valid.

The fields of spec are:

  • specName: Opaque string indicating the name of the chain.

  • implName: Opaque string indicating the name of the implementation of the chain.

  • specVersion: Opaque integer. The JSON-RPC client can assume that the call to Metadata_metadata will always produce the same output as long as the specVersion is the same.

  • implVersion: Opaque integer. Whenever the runtime code changes in a backwards-compatible way, the implVersion is modified while the specVersion is left untouched.

  • transactionVersion: Opaque integer. Necessary when building the bytes of a transaction. Transactions that have been generated with a different transactionVersion are incompatible.

  • apis: Object containing a list of "entry point APIs" supported by the runtime. Each key is an opaque string indicating the API, and each value is an integer version number. Before making a runtime call (using chainHead_v1_call), you should make sure that this list contains the entry point API corresponding to the call and with a known version number.

Note: In Substrate, the keys in the apis field consists of the hexadecimal-encoded 8-bytes blake2 hash of the name of the API. For example, the TaggedTransactionQueue API is 0xd2bc9897eed08f15.

Note: The format of apis is not the same as in the legacy JSON-RPC API.

Note: The list of fields is only a subset of the list of so-called "runtime specification" found in the runtime. The fields that aren't useful from a JSON-RPC client perspective are intentionally not included.

Example value

{
    "specName": "westend",
    "implName": "parity-westend",
    "specVersion": 9122,
    "implVersion": 0,
    "transactionVersion": 7,
    "apis": {
        "0xdf6acb689907609b": 3
        "0x37e397fc7c91f5e4": 1,
        "0x40fe3ad401f8959a": 5,
        "0xd2bc9897eed08f15": 3,
        "0xf78b278be53f454c": 2,
        "0xaf2c0297a23e6d3d": 1,
        "0x49eaaf1b548a0cb0": 1,
        "0x91d5df18b0d2cf58": 1,
        "0xed99c5acb25eedf5": 3,
        "0xcbca25e39f142387": 2,
        "0x687ad44ad37f03c2": 1,
        "0xab3c0572291feb8b": 1,
        "0xbc9d89904f5b923f": 1,
        "0x37c8bb1350a9a2a8": 1
    }
}

invalid

{
    "type": "invalid",
    "error": "..."
}

The runtime is of type invalid if the JSON-RPC server considers the runtime as invalid, for example because the WebAssembly runtime code doesn't match its expectations.

error is a human-readable string indicating why the node considers it as invalid. This string isn't meant to be shown to end users, but is for developers to understand the problem.

Note: The typical situation where a node could consider the runtime as invalid is a light client after a warp syncing. The light client knows that it's its fault for considering the runtime as invalid, but it has no better way to handle this situation than to return an error through the JSON-RPC interface for the error to get shown to the user.

Possible errors

  • A JSON-RPC error with error code -32800 can be generated if the JSON-RPC client has already opened 2 or more chainHead_v1_follow subscriptions.
  • A JSON-RPC error with error code -32602 is generated if one of the parameters doesn't correspond to the expected type (similarly to a missing parameter or an invalid parameter type).

chainHead_v1_header

Parameters:

  • followSubscription: An opaque string that was returned by chainHead_v1_follow.
  • hash: String containing the hexadecimal-encoded hash of the header to retrieve.

Return value:

  • If the followSubscription is still alive (the vast majority of the time), the hexadecimal-encoded SCALE-encoded header of the block.
  • If the followSubscription is invalid or stale, null.

Retrieves the header of a pinned block.

This function should be seen as a complement to chainHead_v1_follow, allowing the JSON-RPC client to retrieve more information about a block that has been reported. Use archive_unstable_header if instead you want to retrieve the header of an arbitrary block.

Note: As explained in the documentation of chainHead_v1_follow, the JSON-RPC server reserves the right to kill an existing subscription and unpin all its blocks at any moment in case it is overloaded or incapable of following the chain. If that happens, chainHead_v1_header will return null.

Possible errors

  • A JSON-RPC error with error code -32801 is generated if the block hash passed as parameter doesn't correspond to any block that has been reported by chainHead_v1_follow, or the block hash has been unpinned.
  • A JSON-RPC error with error code -32602 is generated if one of the parameters doesn't correspond to the expected type (similarly to a missing parameter or an invalid parameter type).
  • A JSON-RPC error with error code -32603 is generated if the JSON-RPC server cannot interpret the block (hardware issues, corrupted database, disk failure etc).

chainHead_v1_stopOperation

Parameters:

  • followSubscription: An opaque string that was returned by chainHead_v1_follow.
  • operationId: An opaque string that was returned by chainHead_v1_body, chainHead_v1_call, or chainHead_v1_storage.

Return value: null

Stops an operation started with chainHead_v1_body, chainHead_v1_call, or chainHead_v1_storage. If the operation was still in progress, this interrupts it. If the operation was already finished, this call has no effect.

Has no effect if the followSubscription is invalid or stale.

JSON-RPC client implementations must be aware that, due to the asynchronous nature of JSON-RPC client <-> server communication, they might still receive notifications about the operation, for example because a notification was already in the process of being sent back by the JSON-RPC server.

chainHead_v1_storage

Parameters:

  • followSubscription: An opaque string that was returned by chainHead_v1_follow.
  • hash: String containing a hexadecimal-encoded hash of the header of the block whose storage to fetch.
  • items: Array of objects. The structure of these objects is found below.
  • childTrie: null for main storage look-ups, or a string containing the hexadecimal-encoded key of the child trie of the "default" namespace.

Each element in items must be an object containing the following fields:

  • key: String containing the hexadecimal-encoded key to fetch in the storage.
  • type: String equal to one of: value, hash, closestDescendantMerkleValue, descendantsValues, descendantsHashes.

Return value: A JSON object.

The JSON object returned by this function has one the following formats:

Started

{
    "result": "started",
    "operationId": ...
    "discardedItems": ...
}

This return value indicates that the request has successfully started.

Where:

  • operationId is a string containing an opaque value representing the operation.
  • discardedItems is an integer indicating the number of items at the back of the array of the items parameters that couldn't be processed.

If the list the JSON-RPC server is overloaded, it might refuse to accept new storage requests. In that situation, the JSON-RPC server will discard some or all the items passed as parameter. The number of items discarded is indicated in discardedItems. When that happens, the JSON-RPC client should try again after an on-going chainHead_v1_storage, chainHead_v1_body, or chainHead_v1_call operation finishes.

The JSON-RPC server must accept at least 16 concurrent operations for any given chainHead_v1_follow subscription. In other words, as long as the JSON-RPC client makes sure that no more than 16 operations are in progress at any given item, it is guaranteed that all of its operations will be accepted by the JSON-RPC server. For this purpose, each item requested through chainHead_v1_storage counts as one operation, and each call to chainHead_v1_body and chainHead_v1_call counts as one operation.

LimitReached

{
    "result": "limitReached"
}

This return value indicates that all the items would be discarded, or that the provided followSubscription is invalid or stale.

Overview

For each item in items, the JSON-RPC server must start obtaining the value of the entry with the given key from the storage, either from the main trie or from childTrie. If type is descendantsValues or descendantsHashes, then it must also obtain the values of all the descendants of the entry.

For the purpose of storage requests, the trie root hash of the child tries of the storage can be found in the main trie at keys starting the bytes of the ASCII string :child_storage:. This behaviour is consistent with all the other storage-request-alike mechanisms of Polkadot and Substrate-based chains, such as host functions or libp2p network requests.

The progress of the operation is indicated through operationStorageItems, operationWaitingForContinue, operationStorageDone, operationInaccessible, or operationError notifications generated on the corresponding chainHead_v1_follow subscription.

The operation continues even if the target block is unpinned with chainHead_v1_unpin.

This function should be seen as a complement to chainHead_v1_follow, allowing the JSON-RPC client to retrieve more information about a block that has been reported. Use archive_unstable_storage if instead you want to retrieve the storage of an arbitrary block.

{"event": "operationStorageItems"} notifications will be generated. Each notification contains a list of items. The list of items, concatenated together, forms the result.

If the type of an item is value, and key is associated with a storage value in the trie, then the result will include an item that contains this storage value. If key is not associated with a storage value in the trie, then the result will not include such item.

If the type of an item is hash, the behavior is similar to a type equal to value, except that the cryptographic hash of the value is included in the result rather than the value itself. The hashing algorithm used is the one of the chain, which is typically blake2b. This can lead to significantly less bandwidth usage and can be used in order to compare the value of an item with a known hash and querying the full value only if it differs.

If the type of an item is descendantsValues or descendantsHashes, then the result will contain zero or more items whose key starts with the key of this item.

If the type of an item is closestDescendantMerkleValue, then the so-called trie Merkle value of the key can be found in the result. If key doesn't exist in the trie, then the Merkle value of the closest descendant of key (including branch nodes) is provided. If key doesn't have any descendant in the trie, then the result will not contain any relevant item.

If items contains multiple identical or overlapping queries, the JSON-RPC server can choose whether to merge or not the items in the result. For example, if the request contains two items with the same key, one with hash and one with value, the JSON-RPC server can choose whether to generate two item objects, one with the value and one with the hash, or only a single item object with both hash and value set. The JSON-RPC server is encouraged to notify as soon as possible of the information at its disposal, without waiting for missing information.

It is allowed (but discouraged) for the JSON-RPC server to provide the same information multiple times in the result, for example providing the value field of the same key twice. Forcing the JSON-RPC server to de-duplicate items in the result might lead to unnecessary overhead.

If a {"event": "operationWaitingForContinue"} notification is generated, the subscription will not generate any more notification unless the JSON-RPC client calls the chainHead_v1_continue JSON-RPC function. The JSON-RPC server is encouraged to generate this event after having sent a certain number of bytes to the JSON-RPC client in order to avoid head-of-line-blocking issues.

Possible errors

  • If the networking part of the behaviour fails, then a {"event": "operationInaccessible"} notification is generated (as explained above).

  • If the followSubscription is invalid or stale, then "result": "limitReached" is returned (as explained above).

  • A JSON-RPC error with error code -32801 is generated if the block hash passed as parameter doesn't correspond to any block that has been reported by chainHead_v1_follow, or the block hash has been unpinned.

  • A JSON-RPC error with error code -32602 is generated if one of the parameters doesn't correspond to the expected type (similarly to a missing parameter or an invalid parameter type).

chainHead_v1_unfollow

Parameters:

  • followSubscription: An opaque string that was returned by chainHead_v1_follow.

Return value: null

Stops a subscription started with chainHead_v1_follow. Has no effect if the followSubscription is invalid or refers to a subscription that has already emitted a {"event": "stop"} event.

JSON-RPC client implementations must be aware that, due to the asynchronous nature of JSON-RPC client <-> server communication, they might still receive chain updates notifications, for example because these notifications were already in the process of being sent back by the JSON-RPC server.

chainHead_v1_unpin

Parameters:

  • followSubscription: An opaque string that was returned by chainHead_v1_follow.
  • hashOrHashes: String or array of unique strings containing the hexadecimal-encoded hash of the header of the block to unpin.

Return value: null

See explanations in the documentation of chainHead_v1_follow.

On-going calls to chainHead_v1_body, chainHead_v1_call and chainHead_v1_storage against this block will still finish normally.

Has no effect if the followSubscription is invalid or stale.

If this function returns an error, then no block has been unpinned. An JSON-RPC server implementation is expected to start unpinning the blocks only after it has made sure that all the blocks could be unpinned.

Possible errors

  • A JSON-RPC error with error code -32801 is generated if the followSubscription is valid but at least one of the block hashes passed as parameter doesn't correspond to any block that has been reported by chainHead_v1_follow, or at least one of the block hashes has been unpinned.
  • A JSON-RPC error with error code -32804 is generated if the hashOrHashes parameter is an array and at least one of the block hashes is duplicated.
  • A JSON-RPC error with error code -32602 is generated if one of the parameters doesn't correspond to the expected type (similarly to a missing parameter or an invalid parameter type).
  • No error is generated if the followSubscription is invalid or stale. The call is simply ignored.

Introduction

The functions with the chainSpec prefix allow inspecting the content of the specification of the chain a JSON-RPC server is targeting.

Because the chain specification never changes while a JSON-RPC server is running, the return value of all these functions must never change and can be cached by the JSON-RPC client.

chainSpec_v1_chainName

Parameters: none

Return value: String containing the human-readable name of the chain.

The value returned by this function must never change for the lifetime of the connection between the JSON-RPC client and server.

chainSpec_v1_genesisHash

Parameters: none

Return value: String containing the hexadecimal-encoded hash of the header of the genesis block of the chain.

This function is a simple getter. The JSON-RPC server is expected to keep in its memory the hash of the genesis block.

The value returned by this function must never change for the lifetime of the connection between the JSON-RPC client and server.

chainSpec_v1_properties

Parameters: none

Return value: any

Returns the JSON payload found in the chain specification under the key properties. No guarantee is offered about the content of this object.

The JSON-RPC server is allowed to re-format the JSON payload found in the chain specification. In other words, whitespaces aren't necessarily preserved.

The value returned by this function must never change for the lifetime of the connection between the JSON-RPC client and server.

Usage

Because no guarantee is offered about the nature of the returned value, this JSON-RPC function is expected to be used in contexts where the JSON-RPC client knows what the properties field contains.

The properties field is a useful way for a chain developer to store important information about their chain, such as the name of the token or the number of decimals, in the chain specification. Without this field, important constants would need to be copy-pasted across all UIs that connect to said chain, potentially leading to mistakes.

Introduction

The rpc_methods function is the only one without a version number. It can be used by JSON-RPC clients in order to determine which functions the JSON-RPC server supports.

rpc_methods

Parameters: none

Return value: A JSON object.

The JSON object returned by this function has the following format:

{
    "methods": [...]
}

Where:

  • methods contains an array of strings indicating the names of all the JSON-RPC functions supported by the JSON-RPC server, including rpc_methods itself.

Introduction

The functions with the sudo prefix are targeted at blockchain node operators who want to inspect the state of their blockchain node.

Contrary to functions with other prefixes, functions with the sudo prefix are meant to be called on a specific JSON-RPC server, and not for example on a load balancer. When implementing a load balancer in front of multiple JSON-RPC servers, functions with the sudo prefix should be forbidden.

sudo_unstable_p2pDiscover

Parameters:

  • multiaddr: String containing a text representation of a multiaddress that ends with /p2p/....

Return value: none

Adds an entry to the address book of peer-to-peer nodes of the JSON-RPC server. The multiaddress passed as parameter should contain the address and identity of a node serving the libp2p protocol.

The JSON-RPC server might start connecting to this node, but it is also free to entirely ignore it.

An example of a valid multiaddress is /ip4/10.2.83.208/tcp/30333/p2p/12D3KooWSNvfxTYrtxqAGmYM1VAtg6YMuAGWvjQ28UvoYoBBgANr.

A JSON-RPC error should be returned if the JSON-RPC server doesn't support the protocols in the address. In this example, the JSON-RPC server should return an error if it doesn't support plain TCP connections.

Because a JSON-RPC server is also free to completely ignore the address, it is not strictly mandatory to return a JSON-RPC error when its protocols are not supported.

However, a JSON-RPC server must always return a JSON-RPC error if it couldn't parse the address. A JSON-RPC client can rely on this behavior to validate user-provided multiaddresses.

Possible errors

  • A JSON-RPC error is generated if the JSON-RPC server couldn't parse multiaddr.
  • A JSON-RPC error is generated if the JSON-RPC server doesn't support some of the protocols in the multiaddr.

About errors

It could be useful for a JSON-RPC client to be able to distinguish between addresses that are completely malformed, and would return an error on all JSON-RPC servers, and addresses that contain unsupported protocols, which could be supported by other JSON-RPC servers.

A better API for this function would consist in returning a JSON-RPC error only if the address if malformed, and a successful response containing a boolean equal to false if the address contains unsupported protocols.

However, this would force JSON-RPC servers to support parsing all the protocols currently defined in the multiaddress specification. Because the multiaddress specification doesn't use proper versioning and is constantly getting new protocol additions, this would be tedious to enforce.

Instead, an invalid multiaddress and an unsupported protocol lead to the same JSON-RPC error so that JSON-RPC servers only need to be able to parse the protocols they support.

sudo_unstable_pendingTransactions

Parameters: none

Return value: Array of string containing hexadecimal-encoded SCALE-encoded transactions that are in the transactions pool of the node.

TODO: is this function actually necessary?

sudo_unstable_version

Parameters: none

Return value: String containing a human-readable name and version of the implementation of the JSON-RPC server.

The return value shouldn't be parsed by a program. It is purely meant to be shown to a human.

Note: Example return values: "polkadot 0.9.12-ec34cf7e0-x86_64-linux-gnu", "smoldot-light 0.5.4"

sudo_network_unstable_unwatch

Parameters:

  • subscription: An opaque string that was returned by sudo_network_unstable_watch.

Return value: null

JSON-RPC client implementations must be aware that, due to the asynchronous nature of JSON-RPC client <-> server communication, they might still receive notifications concerning this subscription, for example because these notifications were already in the process of being sent back by the JSON-RPC server.

Possible errors

A JSON-RPC error is generated if the subscription doesn't correspond to any active subscription.

sudo_network_unstable_watch

Parameters: none

Return value: String containing an opaque value representing the subscription.

This functions lets the JSON-RPC client track the state of the peer-to-peer networking of the blockchain node associated to the JSON-RPC server.

The subscription can later be stopped by calling sudo_network_unstable_unwatch.

When this function is called, a connectionState event is generated for each connection that already exists, and a substreamState event is generated for each substream that already exists. In other words, the JSON-RPC server must send its current networking state to the JSON-RPC client. In addition, the JSON-RPC server is encouraged to notify the JSON-RPC client of connections and substreams that have recently been closed.

The JSON-RPC server must accept at least one sudo_network_unstable_watch subscriptions per JSON-RPC client. Trying to open more might lead to a JSON-RPC error when calling sudo_network_unstable_watch. In other words, as long as a JSON-RPC client only starts one sudo_network_unstable_watch subscriptions, it is guaranteed that this return value will never happen.

Notifications format

This function will later generate one or more notifications in the following format:

{
    "jsonrpc": "2.0",
    "method": "sudo_networkState_event",
    "params": {
        "subscription": "...",
        "result": ...
    }
}

Where subscription is the value returned by this function, and result can be one of:

connectionState

{
    "event": "connectionState",
    "connectionId": "...",
    "targetPeerId": "...",
    "targetMultiaddr": "...",
    "status": "connecting" | "open" | "closed",
    "direction": "in" | "out",
    "when": ...
}

A connectionState event is generated when a new connection attempt is started, when a connection has finished its handshaking phase, or when a connection is terminated.

connectionId is an opaque string representing this specific connection.

status indicates the state of the connection: connecting if the connection hasn't finished its handshake phase (including the libp2p-specific handshakes), open if the connection is fully established and can open substreams, or closed if the connection is now dead.

Each connectionId must follow one of the follow status transitions: connecting then open then closed, or connecting then closed (if an error happend during the handshake). The JSON-RPC server is not allowed to omit events such as the connecting event.

Once a connectionState event with status equal to closed is generated, the connectionId is unallocated. Any further usage of the same connectionId designates a different connection instead.

If status is closed, the connection must not have any associated substream still alive. A substreamEvent of status equal to closed must have been generated earlier for each substream that corresponds to this connection.

If status is open or closed, the targetPeerId is a string containing the string representation of the PeerId of the remote side of the connection. If status is connecting and direction is in, the targetPeerId must be omitted. If status is connecting, the targetPeerId contains the string representation of the PeerId that the remote is expected to have, which might end up being different from the actual PeerId.

targetMultiaddr is a string containing the string representation of the multiaddress of the remote side of the connection. The value in the targetMultiaddr field must always be the same for all the events related to a specific connection. In other words, a the multiaddress of the remote never changes during the lifetime of the connection.

direction indicates whether the connection was initiated locally (out) or by the remote (in). The value in the direction field must always be the same for all the events related to a specific connection. In other words, a connection never changes direction during its lifetime.

when is an integer containing the UNIX timestamp in milliseconds, in other words the number of milliseconds since the UNIX epoch ignoring leap seconds.

substreamState

{
    "event": "substreamState",
    "connectionId": "...",
    "substreamId": "...",
    "status": "open" | "closed",
    "protocolName": "...",
    "direction": "in" | "out",
    "when": ...
}

A substreamState event is generated when a new connection attempt is started, when a connection has finished its handshaking phase, or when a connection is terminated.

connectionId is an opaque string representing this specific connection. It must always correspond to a connection whose latest status is open.

substreamId is an opaque string representing this specific substream within the connection. Each substream is identified by the connectionId + substreamId tuple rather than just the substreamId alone. The JSON-RPC server is allowed to use the same value of substreamId for two different substreams belonging to two different connections.

status indicates the state of the substream: open if the substream is "alive", or closed if the substream is dead. A substream is considered "alive" if the JSON-RPC server allocates resources for this substream, even if the remote isn't aware of said substream.

Each substreamState event where status equal to closed must follow a previous substreamState even for that same substream where status was open. In other words, the JSON-RPC server is not allowed to omit event the open event.

Once a substreamState event with status equal to closed is generated, the substreamId is unallocated. Any further usage of the same substreamId in the context of that connectionId designates a different substream instead.

protocolName is a string indicating the multistream-select protocol name that was negotiated.

direction indicates whether the substream was initiated locally (out) or by the remote (in). Note that this is not the same thing as the direction of the corresponding connection. The value in the direction field must always be the same for all the events related to a specific substream. In other words, a substream never changes direction during its lifetime.

when is an integer containing the UNIX timestamp in milliseconds, in other words the number of milliseconds since the UNIX epoch ignoring leap seconds.

missedEvents

{
    "event": "missedEvents"
}

The missedEvents event is generated in order to inform the JSON-RPC client that it has not been informed of the existence of all connections or substreams due to it being too slow to pull JSON-RPC notifications from the JSON-RPC server.

See the Guarantee of delivery section for more details.

Guarantee of delivery

JSON-RPC server implementations must be aware of the fact that JSON-RPC clients might pull notifications from the JSON-RPC server at a slower rate than networking events are generated. If this function is implemented naively, a slow or malicious JSON-RPC client can cause the JSON-RPC server to allocate ever-increasing buffers, which could in turn lead to a DoS attack on the JSON-RPC server.

JSON-RPC server implementations are also allowed to completely omit events about connections and substreams whose existence is unknown to the JSON-RPC client. For example, when a connection gets closed, the JSON-RPC server is allowed to not notify the JSON-RPC client if the client wasn't yet notified of the fact that the new-closed connection existed. When that happens, the JSON-RPC server must send a missedEvents event to the JSON-RPC client in the nearby future.

JSON-RPC clients must be aware that they aren't guaranteed to see the list of all connections and all substreams that the peer-to-peer endpoint of the node associated to the JSON-RPC server performs. The JSON-RPC client is only guaranteed to be aware of what is currently happening.

Assuming that the number of total active sudo_network_unstable_watch subscriptions on any given JSON-RPC server is bounded, and that the number of total active connections and substreams is bounded, the size of the buffer of notifications to send back to JSON-RPC clients is also bounded.

About timestamps

The connectionState and substreamState events contain a when field indicating when the event happened.

The JSON-RPC server isn't required to order events by the value in their when field. The JSON-RPC is only required to order events so that they don't lose their logical meaning. For example, when two different connections open, the JSON-RPC server can send the two connectionState events in any order. When a connection opens then closes, the JSON-RPC server must send a connectionState with a status equal to open before the connectionState with a status equal to closed.

Possible errors

  • A JSON-RPC error with error code -32100 can be generated if the JSON-RPC client has already opened a sudo_network_unstable_watch subscription.

sudo_sessionKeys_unstable_generate

Parameters:

  • seed (optional): TODO: what is that? I mean, it's the param for the function, but it doesn't explain

Return value:

  • If the runtime supports the function call (see below), an object of the form { "success": true, "value": ... } where value contains a string containing the hexadecimal-encoded output of the runtime function call.
  • Otherwise, an object of the form { "success": false, "error": ... } where error is a human-readable error message indicating the problem. This string isn't meant to be shown to end users, but is for developers to understand the problem.

The JSON-RPC server must check that the runtime supports the SessionKeys API (64bits blake2 hash: 0xab3c0572291feb8b) at version 1, and call the SessionKeys_generate_session_keys runtime function. The runtime call is done against the current best block of the chain.

If there is no SessionKeys API being supported, or if it is not at version 1, the JSON-RPC server is allowed to call an alternative version of this function if it is sensible to do so. For example, if the SessionKeys API is updated to version 2 without a substantial change in the logic of SessionKeys_generate_session_keys, then the JSON-RPC server is allowed to call it as well. This specification should be updated if that happens.

Contrary to most other JSON-RPC functions that perform runtime function calls where side-effects are forbidden, this runtime must be allowed to call host functions that access the keys of the node (e.g. ext_crypto_sr25519_generate_version_1, ext_crypto_ed25519_public_keys_version_1, etc.).

Note: This can be used as a replacement for the legacy author_rotateKeys function.

Possible errors

  • { "success": false, "error": ... } is returned if the runtime doesn't support the given API.
  • { "success": false, "error": ... } is returned if a problem happens during the call, such as a Wasm trap.
  • { "success": false, "error": ... } is returned if the runtime attempts to modify the storage of the block.

About the behavior of SessionKeys_generate_session_keys

The objective of this JSON-RPC function is to call the SessionKeys_generate_session_keys runtime function. This paragraph describes how this runtime function behaves.

The SessionKeys_generate_session_keys runtime function generates a serie of keys, inserts these keys in the so-called keystore, and returns all the public keys concatenated together.

Because the newly-generated keys are inserted in the keystore of the JSON-RPC server, it will automatically start performing duties such as authoring blocks or emitting Grandpa votes if one of the generated public keys corresponds to a key that is given the rights by the blockchain to do so. Most of the time, the keystore is configured to write the keys on disk, meaning that these newly-generated keys remain in the keystore even after the JSON-RPC server has been restarted.

The value returned by this function, which is the concatenation of all newly-generated public keys, is called the session keys. The session keys are meant to be submitted to the blockchain via a transaction by the JSON-RPC client or its user. Before this is done, the newly-generated keys normally don't automatically obtain the right to, for example, generate blocks. Submitting the session keys to the blockchain is out of scope of this function.

Introduction

The transaction functions allow broadcasting a transaction for inclusion in the chain.

transaction_v1_broadcast

Parameters:

  • transaction: String containing the hexadecimal-encoded SCALE-encoded transaction to try to include in a block.

Return value: String representing the operation, or null if the maximum number of broadcasted transactions has been reached.

The string returned by this function is opaque and its meaning can't be interpreted by the JSON-RPC client.

Once this function has been called, the JSON-RPC server will try to propagate this transaction over the peer-to-peer network until transaction_v1_stop is called.

The JSON-RPC server must allow at least 4 transactions being broadcasted at the same time per JSON-RPC client. Any attempt to broadcast more than 4 transactions simultaneously might result in null being returned.

The JSON-RPC server might check whether the transaction is valid before broadcasting it. If it does so and if the transaction is invalid, the server should silently do nothing and the JSON-RPC client is not informed of the problem. Invalid transactions should still count towards the limit to the number of simultaneously broadcasted transactions.

transaction_v1_stop

Parameters:

  • operationId: Opaque string equal to the value returned by transaction_v1_broadcast

Return value: null

The node will no longer try to broadcast the transaction over the peer-to-peer network.

Possible errors

A JSON-RPC error is generated if the operationId doesn't correspond to any active transaction_v1_broadcast operation.

Introduction

The transactionWatch functions allow submitting a transaction for inclusion in the chain.

transactionWatch_v1_submitAndWatch

Parameters:

  • transaction: String containing the hexadecimal-encoded SCALE-encoded transaction to try to include in a block.

Return value: String representing the subscription.

The string returned by this function is opaque and its meaning can't be interpreted by the JSON-RPC client. It is only meant to be matched with the subscription field of events and potentially passed to transactionWatch_v1_unwatch.

Once this function has been called, the server will try to propagate this transaction over the peer-to-peer network and/or include it onto the chain even if transactionWatch_v1_unwatch is called or that the JSON-RPC client disconnects. In other words, it is not possible to cancel submitting a transaction.

Notifications format

This function will later generate one or more notifications in the following format:

{
    "jsonrpc": "2.0",
    "method": "transactionWatch_v1_watchEvent",
    "params": {
        "subscription": "...",
        "result": ...
    }
}

Where subscription is the value returned by this function, and result can be one of:

validated

{
    "event": "validated"
}

The validated event indicates that this transaction has been checked and is considered as valid by the runtime.

This transaction might still become invalid in the future, for example because a conflicting transaction is included in the chain in-between.

Multiple validated events can be generated during the lifetime of a transaction. If multiple validated events happen in a row, the JSON-RPC server is allowed to skip all but the last one.

Note: In theory, this event could include a field indicating the block against which this transaction was validated. It has been decided to not include this field for pragmatic reasons: implementing it might be complicated, and it is not very useful for a JSON-RPC client to know this information.

bestChainBlockIncluded

{
    "event": "bestChainBlockIncluded",
    "block": {
        "hash": "...",
        "index": ...
    }
}

Or

{
    "event": "bestChainBlockIncluded",
    "block": null
}

The bestChainBlockIncluded event indicates which block of the best chain the transaction is included in.

null can be sent back in case the block is no longer in any block of the best chain. This is the state a transaction starts in.

hash is a string containing the hexadecimal-encoded hash of the header of the block. index is an integer indicating the 0-based index of this transaction within the body of this block.

If multiple bestChainBlockIncluded events happen in a row, the JSON-RPC server is allowed to skip all but the last.

Note: Please note that these is no guarantee that the mentioned block matches any of the blocks returned by chainHead_v1_follow.

finalized

{
    "event": "finalized",
    "block": {
        "hash": "...",
        "index": ...
    }
}

The finalized event indicates that this transaction is present in a block of the chain that is finalized.

hash is a string containing the hexadecimal-encoded hash of the header of the block. index is an integer indicating the 0-based index of this transaction within the body of this block.

No more event will be generated about this transaction.

error

{
    "event": "error",
    "error": "..."
}

The error event indicates that an internal error within the client has happened.

Examples include: the runtime crashes, the runtime is missing the function to validate a transaction, the format of the value returned by the runtime is invalid, etc.

This typically indicates a bug in the runtime of the chain or an incompatibility between the client and the runtime of the chain, and there is nothing the end user can do to fix the problem.

The transaction that has been submitted will not be included in the chain by the local node, but it could be included by sending it via a different client implementation.

error is a human-readable error message indicating what happened. This string isn't meant to be shown to end users, but is for developers to understand the problem.

No more event will be generated about this transaction.

invalid

{
    "event": "invalid",
    "error": "..."
}

The invalid event indicates that the runtime has marked the transaction as invalid.

This can happen for a variety of reasons specific to the chain, such as a bad signature, bad nonce, not enough balance for fees, invalid decoded transaction bytes etc.

error is a human-readable error message indicating why the transaction is invalid. This string isn't meant to be shown to end users, but is for developers to understand the problem.

No more event will be generated about this transaction.

dropped

{
    "event": "dropped",
    "error": "..."
}

The dropped event indicates that the client wasn't capable of keeping track of this transaction.

This can happen for example if the JSON-RPC server's transactions pool is full, if the JSON-RPC server's resources have reached their limit, if the block the transaction is included in takes too long to be finalized, or the syncing requires a gap in the chain that prevents the JSON-RPC server from knowing whether the transaction has been included and/or finalized.

error is a human-readable error message indicating why the transaction has been dropped. This string isn't meant to be shown to end users, but is for developers to understand the problem.

No more event will be generated about this transaction.

Transaction state

One can build a mental model in order to understand which events can be generated. While a transaction is being watched, it has the following properties:

  • isValidated: yes or not-yet. A transaction is initially not-yet validated. A validated event indicates that the transaction has now been validated. After a certain number of blocks or in case of retractation, a transaction automatically becomes not-yet validated and needs to be validated again. No event is generated to indicate that a transaction is no longer validated, however a validated event will be generated again when a transaction is validated again.

  • bestChainBlockIncluded: an optional block hash and index. A transaction is initially included in no block. It can automatically become included in a block of the best chain. A bestChainBlockIncluded event reports updates to this property.

Note that these two properties are orthogonal. In particular, a transaction can be included in a block before being validated.

The finalized, error, invalid, and dropped event indicate that the transaction is no longer being watched. The state of the transaction is entirely discarded.

JSON-RPC servers are allowed to skip sending events as long as it properly keeps the JSON-RPC client up to date with the state of the transaction. In other words, multiple validated or bestChainBlockIncluded events in a row might be merged into one.

Note: In order to implement this properly, JSON-RPC servers should maintain a buffer of three notifications (one for each property), and overwrite any unsent notification with a more recent status update.

Possible errors

  • A JSON-RPC error with error code -32602 is generated if the transaction parameter is not a valid hex string. Note that no error is produced if the bytes of the transaction, once decoded, are invalid. Instead, an invalid notification will be generated.

transactionWatch_v1_unwatch

Parameters:

  • subscription: Opaque string equal to the value returned by transactionWatch_v1_submitAndWatch

Return value: null

Note: This function does not remove the transaction from the pool. In other words, the node will still try to include the transaction in the chain. Having a function that removes the transaction from the pool would be almost useless, as the node might have already gossiped it to the rest of the network.

Possible errors

A JSON-RPC error is generated if the subscription doesn't correspond to any active subscription.