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 thevalue
is a string containing the hexadecimal-encoded SCALE-encoded value returned by the runtime. - If the call wasn't successful,
{ "success": false, "error": ... }
where theerror
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. Theerror
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 ofdescendantsValues
anddescendantsHashes
.
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 thekey
s provided fortype
equal tovalue
,hash
orclosestDescendantMerkleValue
. For queries of typedescendantsValues
ordescendantsHashes
,key
is guaranteed to start with one of thekey
s provided. -
value
(optional): String containing the hexadecimal-encoded value of the storage entry. This field is present when thetype
of the query was"value"
or"descendantsValues"
. -
hash
(optional): String containing the hexadecimal-encoded hash of the storage entry. This field is present when thetype
of the query was"hash"
or"descendantsHashes"
. -
closestDescendantMerkleValue
(optional): String containing the hexadecimal-encoded Merkle value of the closest descendant ofkey
(including branch nodes). This field is present when thetype
of the query was"closestDescendantMerkleValue"
. The trie node whose Merkle value is indicated inclosestDescendantMerkleValue
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.
-
Call
chainHead_v1_follow
withwithRuntime: true
to obtain afollowSubscription
. ThisfollowSubscription
will need to be passed when calling most of the otherchainHead
-prefixed functions. If at any point in the future the JSON-RPC server sends back a{"event": "stop"}
notification, jump back to step 1. -
When the JSON-RPC server sends back a
{"event": "initialized"}
notification withsubscription
equal to yourfollowSubscription
, store the value offinalizedBlockHashes
found in that notification. IffinalizedBlockHashes
contains multiple values, you should use the last entry. The last entry corresponds to the current finalized block of the chain. You must then callchainHead_v1_unpin
, passing thefollowSubscription
and each of the other values infinalizedBlockHashes
. -
Make sure that the
finalizedBlockRuntime
field of the event contains a fieldtype
containingvalid
, and that thespec
->apis
object contains a key0xd2bc9897eed08f15
whose value is3
. This verifies that the runtime of the chain supports theMetadata_metadata
function that we will call below (0xd2bc9897eed08f15
is the 64bits blake2 hash of the ASCII stringMetadata
). If it is not the case, enter panic mode as the client software is incompatible with the current state of the blockchain. -
Call
chainHead_v1_call
withhash
equal to the current finalized block you've just retrieved,function
equal toMetadata_metadata
, and an emptycallParameters
. -
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. -
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.
-
In order to obtain a value in the storage, call
chainHead_v1_storage
withhash
equal to the current finalized block,key
the desired key, andtype
equal tovalue
. 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. -
You are strongly encouraged to maintain a
Set
of the blocks where the runtime changes. Whenever a{"event": "newBlock"}
notification is received withsubscription
equal to yourfollowSubcriptionId
, andnewRuntime
is non-null, store the providedblockHash
in this set. -
Whenever a
{"event": "finalized"}
notification is received withsubscription
equal to yourfollowSubcriptionId
, callchainHead_v1_unpin
with the current finalized block hash, theprunedBlockHashes
, and with each value infinalizedBlockHashes
of thefinalized
event except for the last entry. The last entry infinalizedBlockHashes
becomes your new current finalized block. If one or more entries offinalizedBlockHashes
are found in yourSet
(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 bychainHead_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 bychainHead_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 bychainHead_v1_follow
. ThewithRuntime
parameter of the call must have been equal totrue
.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 bychainHead_v1_follow
, or the block hash has been unpinned. - A JSON-RPC error with error code
-32802
is generated if thefollowSubscription
corresponds to a follow wherewithRuntime
wasfalse
. - 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 bychainHead_v1_follow
.operationId
: An opaque string that was returned bychainHead_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 thefollowSubscription
andoperationId
are valid but haven't generated aoperationWaitingForContinue
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 otherchainHead
-prefixed functions. -
Later, generates an
initialized
notification (see below) containing the hashes of the latest finalized blocks, and, ifwithRuntime
istrue
, 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 abestBlockChanged
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 abestBlockChanged
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 key
s 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 key
s 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 toMetadata_metadata
will always produce the same output as long as thespecVersion
is the same. -
implVersion
: Opaque integer. Whenever the runtime code changes in a backwards-compatible way, theimplVersion
is modified while thespecVersion
is left untouched. -
transactionVersion
: Opaque integer. Necessary when building the bytes of a transaction. Transactions that have been generated with a differenttransactionVersion
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 (usingchainHead_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 morechainHead_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 bychainHead_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 bychainHead_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 bychainHead_v1_follow
.operationId
: An opaque string that was returned bychainHead_v1_body
,chainHead_v1_call
, orchainHead_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 bychainHead_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 theitems
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 bychainHead_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 bychainHead_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 bychainHead_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 thefollowSubscription
is valid but at least one of the block hashes passed as parameter doesn't correspond to any block that has been reported bychainHead_v1_follow
, or at least one of the block hashes has been unpinned. - A JSON-RPC error with error code
-32804
is generated if thehashOrHashes
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, includingrpc_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 bysudo_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 asudo_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": ... }
wherevalue
contains a string containing the hexadecimal-encoded output of the runtime function call. - Otherwise, an object of the form
{ "success": false, "error": ... }
whereerror
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 bytransaction_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
ornot-yet
. A transaction is initiallynot-yet
validated. Avalidated
event indicates that the transaction has now been validated. After a certain number of blocks or in case of retractation, a transaction automatically becomesnot-yet
validated and needs to be validated again. No event is generated to indicate that a transaction is no longer validated, however avalidated
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. AbestChainBlockIncluded
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 thetransaction
parameter is not a valid hex string. Note that no error is produced if the bytes of thetransaction
, once decoded, are invalid. Instead, aninvalid
notification will be generated.
transactionWatch_v1_unwatch
Parameters:
subscription
: Opaque string equal to the value returned bytransactionWatch_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.