owned this note
owned this note
Published
Linked with GitHub
# JSON-RPC interface rework
This document is about improving the current JSON-RPC API of Substrate.
**Definition**: what is called "the JSON-RPC API" in this document is the list of JSON-RPC functions that can be invoked by connecting to the ports 9933 or 9944 (by default) of a Substrate node.
In particular, we're not talking about specific implementation details of this API in Rust or JavaScript. This document is strictly about the functions themselves.
The current API is not properly specified, but can be found here:
- Roughly documented here: https://polkadot.js.org/docs/substrate/rpc/. This documentation unfortunately doesn't go in the details of the behaviour of the functions.
- In the various sub-directories of https://github.com/paritytech/substrate/tree/master/client/rpc-api/src
- Partially described in one file here: https://github.com/paritytech/smoldot/blob/d66b13a41c5e4cf98666bc21644785d90267625f/src/json_rpc/methods.rs#L275-L341
## Problems with the current API
Here is a list of problems with the current situation, and the reasons why a change is needed. In no specific order:
- Most functions are very badly documented, in particular when it comes to corner cases. Example:
- The format of many parameters and return values are not documented anywhere. The source code of Substrate defines `struct`s or `enum`s that derive the `serde` `Serialize` and `Deserialize` traits, and it is tedious and difficult to predict just by reading the source code how exactly they are serialized, most especially for people who aren't deeply familiar with Rust or `serde`. [Example](https://github.com/paritytech/substrate/blob/2ab769819e8a3ee869459b78cbb98cc749728cc3/client/rpc-api/src/chain/mod.rs#L43).
- Some parameters and return values are passed a strings, in which case it is unclear what format is accepted. This is the case for example for `system_localListenAddresses`, `system_localPeerId`, `system_addReservedPeer`, `system_removeReservedPeer`.
- The format of subscription notifications is completely undocumented and hard to find, especially considering that notifications are a custom addition that are not part of the official JSON-RPC specification. [Example here](https://github.com/paritytech/substrate/blob/2ab769819e8a3ee869459b78cbb98cc749728cc3/client/rpc-api/src/author/mod.rs#L87). The source code indicates that notifications will be sent back and that contain a `TransactionStatus`, but it is almost impossible to determine which fields the JSON payload will contain without retro-engineering a node.
- Some behaviours are simply not documented. For example, calling `state_subscribeStorage` with an empty list of keys implicitly subscribes to *all* keys. This isn't explained anywhere, and it is unclear how many undocumented behaviours exist.
- It is unclear what value is returned by `author_submitAndWatchExtrinsic` or `author_submitExtrinsic` if called with an invalid extrinsic.
- It is unclear what value is returned by `chain_getBlock`, `chain_getHeader`, `state_getStorage`, `state_getMetadata`, etc. if called with an unknown block hash (it has been noticed that some functions return an error while some others return `null`).
- It is unclear what value is returned by `chain_getBlockHash` if called with a height that is too high.
- It is unclear what value is returned by `system_accountNextIndex` if passed a non-existing account.
- It is unclear what value is returned by `system_addReservedPeer` and `system_removeReservedPeer` if called with an invalid address.
- It is unclear how `system_removeReservedPeer` behaves if the peer passed as parameter wasn't reserved.
- The difference between `author_hasKey` and `author_hasSessionKeys` is unclear.
- The difference between `state_call` and `system_dryRun` is unclear.
- The list returned by `author_pendingExtrinsics` has an unclear meaning.
- The value returned by `author_submitExtrinsic` is unclear.
- It is unclear whether `chain_subscribeAllHeads`, `chain_subscribeFinalizedHeads` and `chain_subscribeNewHeads` are allowed to have gaps in the blocks they report. Gaps might be necessary in order to avoid buffering too much data in case the node is overloaded.
- Functions such as `state_getKeysPaged` or `state_queryStorageAt` are very complex, and a brief summary is far from enough for a reader to even have an idea of how they behave.
- It is unclear whether the ordering of notifications between `chain_subscribeNewHeads` and `state_subscribeRuntimeVersion` has to be respected. In other words, when the user receives a notification that the runtime version has changed, can they assume that this change happened in the latest best block, or is the server implementation allowed more freedom?
- It is unclear whether the node is allowed to reject incoming requests due to being overloaded, and if yes with which error. In particular, being able to reject new subscriptions is important to avoid DoS attacks where a client subscribes thousands of times, and contrary to non-subscriptions it cannot be done by back-pressuring the TCP connection on which requests are received.
- The format of the parameters and return value of some functions is weird.
- This is most likely the consequence of the lack of documentation (see previous point).
- `chain_getHeader` and `chain_getBlock` functions and the `chain_subscribe*` subscription notifications return the header of a block. This header is a JSON object whose fields are the fields of the header, except that the block number is converted to hexadecimal, and except that digest items are left undecoded. This means that a JSON-RPC client, if they want to compute the hash of a block received through a notification, have to do a lot of complicated operations.
- The return value of `state_queryStorageAt` is over-complicated.
- The format of the parameters and return values of some functions depends on the runtime of the chain.
- The function `system_accountNextIndex` has an `AccountId` as parameter, and the `payment_queryInfo` function returns a `RuntimeDispatchInfo`. These two types are defined in the runtime of the chain that the node is connected to.
- The `number` field of the header returned by `chain_getHeader` and `chain_getBlock` depends on the runtime as well, but this can be neglected as in practice everyone uses integers.
- This means that a JSON-RPC client cannot reasonably call these functions and a JSON-RPC server cannot accept incoming requests without hardcoding the types of the chain or having parsed the metadata of the chain, which considerably adds complexity to the implementation of both the JSON-RPC client and server.
- This also means that, in theory, the JSON-RPC API can change after a runtime upgrade. In practice, Substrate will not parse the incoming JSON-RPC requests differently after a runtime upgrade, meaning that it can become incompatible with JSON-RPC clients.
- This more generally prevents de-coupling the Substrate client from the Substrate runtime. Sorry but magically using types defined in the runtime from the client was a really really bad idea.
- Many JSON-RPC functions assume full node capabilities.
- All functions that access the storage of a block have been designed with the assumption that the storage is either available or not. A light client, however, accesses the storage by performing network requests. In the context of light clients, there are more things to consider:
- The storage might temporarily not be available due to a networking outage or because all peers have refused our storage request.
- Retrieving the storage can take a long time (due to potentially having to perform multiple network queries), and it is unclear how long the client is willing to wait. The JSON-RPC API doesn't provide any way to provide a timeout, and doesn't provide any way to cancel an ongoing query, meaning that the server has to hardcode an arbitrary timeout after which the query has failed.
- A JSON-RPC client might want to query the value of a storage item, parse it, then perform more storage item requests based on the outcome. Due to the necessity for networking round-trips, doing so is slow and inefficient. The JSON-RPC API could, in theory, provide ways to perform the two queries at once.
- Functions that access the history of changes the storage (`state_queryStorageAt`, despite its name, returns a list of all changes that have happened to this storage item in the past) can't reasonably be implemented by a light client, as it would need to perform hundreds of heavy network requests. The mechanism that had been planned in order to make it possible to prove changes, the changes trie, has been removed due to slow performances.
- In the case of a light client performing a warp sync, it is unclear whether `chain_subscribeAllHeads` is allowed to have a gap at the moment when the warp sync happens. Furthermore the warp syncing process, as it results most of the time in a change in the runtime version, highlights the question of the ordering of notifications between `chain_subscribeNewHeads` and `state_subscribeRuntimeVersion` discussed above.
- It is unclear whether a node is supposed to support all functions, or if it can only support a subset of functions.
- As explained in the previous point, functions such as `state_queryStorageAt` can't be implemented by a light client. Should a light client provide these functions nonetheless and return an error?
- In the past, we have introduced the notion of a "safe" subset of functions. This subset is the subset of the functions that can be called by an anonymous person, without the risk of compromising the security of the node. The public JSON-RPC nodes only give access to this subset, but the list of function that is in this subset isn't clearly documented.
- Note that the `rpc_methods` function can be used to determine which other JSON-RPC functions are supported. However, the unsafe subset described in the previous point is implemented by returning errors rather than filtering them from the list of available functions, meaning that `rpc_methods` always indicates that all functions are available.
- The functions are not adapted to the unstoppable apps use-case.
- The existing JSON-RPC functions have been designed with the use case of a node operator wanting to know if their node is working well, and with the use case of a blockchain developer wanting to look at the state of the chain. It hasn't been designed with the use-case of a dapp/uapp UI displaying information found on the chain.
- An unstoppable app should only show as authoritative the storage of the finalized block. However the `state_subscribeStorage` function can only apply on the best block. All the storage-related functions target the best block by default if not specified otherwise.
- Some functions are redundant.
- Functions such as `state_getMetadata`, `system_accountNextIndex` and `payment_queryInfo` could be implemented on top of `state_call`.
- `state_call` and `system_dryRun` could be one function.
- `chain_subscribeAllHeads`, `chain_subscribeFinalizedHeads`, `chain_subscribeNewHeads` could be combined into one.
- Functions that start a subscription always return a first notification containing the first value. As such, `chain_getBlockHash` could be implemented on top of `chain_subscribeNewHeads`, `chain_getFinalizedHead` on top of `chain_subscribeFinalizedHeads`, `state_getStorage` on top of `state_subscribeStorage`, and `state_getRuntimeVersion` on top of `state_subscribeRuntimeVersion`.
- The functions that fetch the child trie storage (`child_*`) could be provided by passing an additional parameter to the functions that fetch the storage.
## Upgrade path
All the problems enumerated in the previous section can be solved by introducing new JSON-RPC functions or modifying existing JSON-RPC functions.
But in order to do this, we need an upgrade path.
It would be unwise to silently modify the behaviour of only some functions in a non-backwards-compatible way. If backwards compatibility is broken, then it needs to be completely broken so as to not give the impression that an application or a tool communicating with JSON-RPC is still working even when in reality it's half-broken.
Two questions arise:
- Should we keep using the JSON-RPC protocol (with notifications extension)? Or replace it with a different protocol?
- If we keep using JSON-RPC, do we fix the situation by adding/modifying functions in the existing API? Or do we start from scratch by listening on a separate port and serving a different API?
The rest of this document assumes that we keep using JSON-RPC but start a brand new API from scratch, as this seems to be the consensus.
Using alternative protocols, such as gRPC, has been considered. However, the primary goal of the JSON-RPC interface is not to be the most optimal but to be simple to understand. JSON-RPC is based on the well-known WebSocket protocol and JSON encoding, and nothing comes close to it in terms of simplicity.
**TODO**: get consensus on this topic ^
## Writing a proper specification of this interface
In order for JSON-RPC client developers and JSON-RPC server developers to be able to properly share assumptions, it is important for a proper specification to written.
Using the Substrate implementation of this API as a reference **is not** a good solution, as it is impossible to differentiate intentional behaviour from bugs (including bugs accidentally introduced later, which is likely to happen considering that the types and behaviours are spread out throughout a lot of the code base, and developers might modify these types/behaviours without being aware of the implications).
This is especially important because JSON-RPC client developers and server developers generally don't work closely with each other. Typical server developers work for Parity and code in Rust, while typical client developers are not necessarily working for Parity and code in JavaScript or other.
Once this document has been fleshed out, a repository should be created, and the specification of the new JSON-RPC API should be put there. This repository can then be used in order to propose changes, report issues about the API, and will serve as a reference point to make sure that clients and server conform to it.
## Grouping functions and node capabilities
Before going further, let's examine *why* we're building a JSON-RPC interface at all:
- For end-user-facing applications to be able to read and interact with a blockchain.
- For node operators to make sure that their node is operating correctly, and perform some operations such as rotating keys.
- For core/parachain developers to manually look at the state of the blockchain and figure out what is on the chain.
These three audiences have different needs, and it doesn't make sense for example for a light client to support a function that rotates keys.
It is already the case today that light clients only support a subset of all JSON-RPC functions, and publicly-accessible JSON-RPC full nodes also only support a subset of all JSON-RPC functions.
In order to do this properly, we suggest to distribute all JSON-RPC functions between groups, and nodes are allowed to support only a certain subset of groups. However, each group, if it is supported, must be supported entirely. The group a JSON-RPC function belongs to is indicated by a `prefix_` in its name.
The groups must also include a version number. For example, each function prefixed with `foo_v1_` belong to version 1 of the group `foo`. Multiple versions of the same group name might co-exist in the future. Remember that some servers might support `foo_v2_` and not `foo_v1_`, or vice versa. As such, each group+version must be "self-contained".
Functions from multiple different group+version should never be mixed. 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.
Functions that are unstable should always use version `unstable` of a group. For instance, `foo_unstable_`. Functions must be stable only if we are forever ready to commit to their stability. Unstable functions can break at any time, and thus more freedom is given to them.
We understand that developers want to be able to add RPC functions for various reasons, such as debugging. When doing so, they are strongly encouraged to assign functions to a group with the `_unstable` prefix.
It is completely fine to leave functions as unstable forever and never try to stabilize them. In particular, there is no drawback in leaving as unstable functions that aren't meant to be called programmatically.
JSON-RPC server should always support the `rpc_methods` function, and clients should use this function to determine which other functions are supported.
**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.
## Guiding principles for new functions
All the functions in the new API should conform to the following:
- camelCase and not snake_case, as this is the norm in JavaScript.
- Function names must start with a prefix indicating the category the function belongs to.
- Functions should keep in mind the fact that they might be called on a load balancer, and that requests could be distributed between multiple nodes. For example, after submitting a transaction, retrieving the list of pending transactions might return an empty list, because the submission was sent to a different node than the one the list was retrieved from.
- Functions should only generate errors in case of a communication issue between the client and server (e.g. missing parameter, unknown function), or in case of a problem with the node (e.g. processing the request triggers a panic, or the node detected that it wasn't compatible with the chain it is supposed to connect to). Functions shouldn't generate JSON-RPC errors in circumstances that can happen normally, for example the impossibility to access a storage item due to lack of connectivity. This lets a JSON-RPC client implementation treat all JSON-RPC errors as critical problems, rather than having to interpret these errors.
- Functions that produce notifications should keep in mind that it must be possible for a JSON-RPC server to drop notifications in case the client isn't processing them quickly enough. Example: let's take the `chain_subscribeNewHeads` function that reports updates about the new best block. A server can implement this by detecting when a new best block happens then trying to send a notification. After the notification has been sent, the server then checks whether the best block is still the same as the one that was notified, and if not sends a notification again. This implementation uses a fixed amount of memory, which is a good thing, but might lead to some best block updates being missed (e.g. if the best block changes multiple times while trying to send the notification). If it was mandatory for the server to report every single best block update, it would have no choice but to buffer all the updates that happen while sending a notification, which could lead to an infinite amount of memory being used. For this reason, it must not be mandatory to report every single best block update.
- No JSON-RPC function should use up a disproportionate amount of CPU power in order to be answered compared to the other functions, in order to avoid a situation where a few expensive calls in front of the queue are blocking cheap callers behind them.
- Implementations of this API should enforce a limit to the number of simultaneous subscriptions, meaning that all active subscriptions should have roughly the same CPU/memory cost for the implementation.
- It is **not** an objective to optimize the bandwidth usage of JSON-RPC client <-> server communication, as all the idiomatic usages of this API involve communicating through `localhost`. However it should still be realistic to use TCP/IP as a quick solution. Similarly, it is not an objective to minimize the number of round-trips necessary between the JSON-RPC client and server.
- The objective of this interface is to give clear, explicit, and direct access to a node's internal state, and **not** to be convenient to use. Functions that require, for example, some post-processing on the data should be avoided, and caches should preferably be found on the client side rather than the server side. High-level developers are not expected to directly use the client side of this interface, but instead to use an intermediary layer on top of this client side.
## The new API
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.
List of prefixes:
- `archive_v1`: Functions that allow examining the history of the chain, in other words the latest finalized block and its ancestors.
- `chainHead_v1`: Functions that allow tracking the head of the chain (in other words the latest new and finalized blocks) and their storage.
- `chainSpec_v1`: Functions that return information found or calculated from the chain specification.
- `extrinsic_v1`: Functions that allow submitting a transaction to the chain. **TODO**: what about `transaction` instead?
- `sudo_v1`: Functions that manage the node client itself and should only ever be called by the owner of the node. These functions are expected to be called on a specific node, and not on a load-balanced endpoint.
### The `networkConfig` parameter
Many functions below have an optional parameter named `networkConfig`. This parameter is used when a function needs to request an information from its peers.
Requesting an information from a peer involves sending a request to this peer. This request can fail for various reasons: the peer is non responsive, the peer is overwhelmed, the peer is malicious, the peer has a bug in its implementation, etc. Because the request can fail, multiple requests should be attempted towards different peers before the information retrieval fails altogether.
The passed passed for the `networkConfig` parameter is a struct defined as:
```json
{
"totalAttempts": ...,
"maxParallel": ...,
"timeoutMs": ...
}
```
- `totalAttempts` is an integer indicating the total number of peers to request for the information.
- `maxParallel` is an integer indicating the maximum number of requests to perform in parallel. A value of 0 is illegal.
- `timeoutMs` is an integer indicating the time, in milliseconds, after which a single request (towards one peer) is considered unsuccessful.
These values can be tweaked depending on the urgency of the JSON-RPC function call. An important information should be queried with a low timeout and high number of `maxParallel`, while a low priority information should be queried with `maxParallel` equal to 1.
A JSON-RPC function call that is passed a `networkConfig` can take, in the worst case scenario, a bit more than `timeoutMs * totalAttempts / maxParallel` milliseconds.
The JSON-RPC server implementation is allowed and strongly encouraged to put hard a limit on the values in the `networkConfig`.
### archive_v1_body
**TODO**
### archive_v1_genesisHash
**Parameters**: *none*
**Return value**: String containing the hex-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.
### archive_v1_hashByHeight
**Parameters**:
- `height`: String containing an hexadecimal-encoded integer.
- `networkConfig` (optional): Object containing the configuration of the networking part of the function. See above for details. Ignored if the JSON-RPC server doesn't need to perform a network request. Sensible defaults are used if not provided.
**Return value**: An opaque string that identifies the query in progress.
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.
This function will later generate a notification looking like this:
```json
{
"jsonrpc": "2.0",
"method": "archive_v1_hashByHeightEvent",
"params": {
"subscriptionId": "...",
"result": ...
}
}
```
Where `result` can be:
```json
{
"event": "done",
"output": [...]
}
```
Where `output` is 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 either zero or one entry.
Only one notification will ever be generated.
**Note**: Other events might be added in the future, such as reports on the progress of the query.
**Important implementation note**: While it is possible for a light client to ask its peers which block hash corresponds to a certain height, it is at the moment impossible to obtain a proof of this. If a light client implements this JSON-RPC function, it must only look in its internal memory and **not** ask the network. Consequently, calling this function with a height more than a few blocks away from the finalized block height will always return zero blocks. Despite being currently useless, the `networkConfig` parameter is kept for the future.
### archive_v1_header
**Parameters**:
- `hash`: String containing the hexadecimal-encoded hash of the header to retreive.
- `networkConfig` (optional): Object containing the configuration of the networking part of the function. See above for details. Ignored if the JSON-RPC server doesn't need to perform a network request. Sensible defaults are used if not provided.
**Return value**: An opaque string that identifies the query in progress.
This function will later generate a notification looking like this:
```json
{
"jsonrpc": "2.0",
"method": "archive_v1_headerEvent",
"params":{
"subscriptionId": "...",
"result": ...
}
}
```
Where `result` can be:
```json
{
"event": "done",
"output": ...
}
```
Where `output` is a string containing the hexadecimal-encoded SCALE-codec encoding of the header of the block.
Alternatively, `result` can also be:
```json
{
"event": "failed"
}
```
Only one notification will ever be generated.
**Note**: Other events might be added in the future, such as reports on the progress of the call.
#### Possible errors
- If the block hash passed as parameter doesn't correspond to any known block, then a `{"event": "failed"}` notification is generated (as explained above).
- If the networking part of the behaviour fails, then a `{"event": "failed"}` notification is generated (as explained above).
Due to the way blockchains work, it is never possible to be certain that a block doesn't exist. For this reason, networking-related errors and unknown block errors are reported in the same way.
### archive_v1_stopBody
**TODO**
### archive_v1_stopHashByHeight
**Parameters**:
- `subscriptionId`: An opaque string that was returned by `archive_v1_hashByHeight`.
**Return value**: *null*
Stops a query started with `archive_v1_hashByHeight`. If the query was still in progress, this interrupts it. If the query was already finished, this call has no effect.
JSON-RPC client implementations must be aware that, due to the asynchronous nature of JSON-RPC client <-> server communication, they might still receive a notification about this call, for example because this notification was already in the process of being sent back by the JSON-RPC server.
### archive_v1_stopHeader
**Parameters**:
- `subscriptionId`: An opaque string that was returned by `archive_v1_header`.
**Return value**: *null*
Stops a query started with `archive_v1_header`. If the query was still in progress, this interrupts it. If the query was already finished, this call has no effect.
JSON-RPC client implementations must be aware that, due to the asynchronous nature of JSON-RPC client <-> server communication, they might still receive a notification about this call, for example because this notification was already in the process of being sent back by the JSON-RPC server.
### archive_v1_stopStorage
**Parameters**:
- `subscriptionId`: An opaque string that was returned by `archive_v1_storage`.
**Return value**: *null*
Stops a storage fetch started with `archive_v1_storage`. If the storage fetch was still in progress, this interrupts it. If the storage fetch was already finished, this call has no effect.
JSON-RPC client implementations must be aware that, due to the asynchronous nature of JSON-RPC client <-> server communication, they might still receive a notification about this storage fetch, for example because this notification was already in the process of being sent back by the JSON-RPC server.
### archive_v1_storage
**Parameters**:
- `hash`: String containing an hexadecimal-encoded hash of the header of the block whose storage to fetch.
- `key`: String containing the hexadecimal-encoded key to fetch in the storage.
- `childKey`: `null` for main storage look-ups, or a string containing the hexadecimal-encoded key of the trie key of the trie that `key` refers to. **TODO**: I don't know enough about child tries to design this properly
- `type`: String that must be equal to one of: `value`, `hash`, or `size`.
- `networkConfig` (optional): Object containing the configuration of the networking part of the function. See above for details. Ignored if the JSON-RPC server doesn't need to perform a network request. Sensible defaults are used if not provided.
**Return value**: An opaque string that identifies the storage fetch in progress.
This function works the same way as `chainHead_v1_storage`, except that it is not connected to a chain head follow, and no `disjoint` event can be generated.
Note that `chainHead_v1_storage` and `archive_v1_storage` should be treated as two completely separate functions. It is forbidden to call `archive_v1_stopStorage` with a storage fetch started with `chainHead_v1_storage`, and vice versa. Some JSON-RPC servers might support only one of these functions.
### chainHead_v1_body
**Parameters**:
- `followSubscriptionId`: An opaque string that was returned by `chainHead_v1_follow`.
- `hash`: String containing an hexadecimal-encoded hash of the header of the block whose body to fetch.
- `networkConfig` (optional): Object containing the configuration of the networking part of the function. See above for details. Ignored if the JSON-RPC server doesn't need to perform a network request. Sensible defaults are used if not provided.
**Return value**: An opaque string that identifies the body fetch in progress.
The JSON-RPC server must start obtaining the body (in other words the list of transactions) of the given block.
This function will later generate notifications looking like this:
```json
{
"jsonrpc": "2.0",
"method": "chainHead_v1_bodyEvent",
"params": {
"subscriptionId": "...",
"result": ...
}
}
```
If everything is successful, `result` will be:
```json
{
"event": "done",
"value": [...]
}
```
Where `value` is an array of strings containing the hex-encoded SCALE-encoded extrinsics found in this block.
Alternatively, `result` can also be:
```json
{
"event": "failed"
}
```
Which indicates that the body has failed to be retrieved from the network.
Alternatively, if the `followSubscriptionId` is dead, then `result` can also be:
```json
{
"event": "disjoint"
}
```
After an `"event": "done"`, `"event": "failed"`, or `"event": "disjoint"` is received, no more notification will be generated.
**Note**: Other events might be added in the future, such as reports on the progress of the fetch.
#### Possible errors
- If the networking part of the behaviour fails, then a `{"event": "failed"}` notification is generated (as explained above).
- A JSON-RPC error is generated if the `followSubscriptionId` is invalid.
- If the `followSubscriptionId` is dead, then a `{"event": "disjoint"}` notification is generated (as explained above).
- A JSON-RPC error is generated if the block hash passed as parameter doesn't correspond to any block that has been reported by `chainHead_v1_follow`.
- A JSON-RPC error is generated if the `followSubscriptionId` is valid but the block hash passed as parameter has already been unpinned.
### chainHead_v1_call
**Parameters**:
- `followSubscriptionId`: An opaque string that was returned by `chainHead_v1_follow`.
- `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`: Array containing a list of hexadecimal-encoded SCALE-encoded parameters to pass to the runtime function.
- `networkConfig` (optional): Object containing the configuration of the networking part of the function. See above for details. Ignored if the JSON-RPC server doesn't need to perform a network request. Sensible defaults are used if not provided.
**Return value**: An opaque string that identifies the call in progress.
**TODO**: in order to perform the runtime call, the implementation of this function will simply concatenate all the parameters (without any separator), so does it make sense for the JSON-RPC function to require to split them into an array?
This function will later generate a notification looking like this:
```json
{
"jsonrpc": "2.0",
"method": "chainHead_v1_callEvent",
"params": {
"subscriptionId": "...",
"result": ...
}
}
```
Where `result` can be:
```json
{
"event": "done",
"output": "0x0000000..."
}
```
Where `output` is the hex-encoded output of the runtime function call.
Alternatively, `result` can also be:
```json
{
"event": "failed",
"error": "..."
}
```
Where `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.
Alternatively, if the `followSubscriptionId` is dead, then `result` can also be:
```json
{
"event": "disjoint"
}
```
Only one notification will ever be generated.
**Note**: Other events might be added in the future, such as reports on the progress of the call.
**Note**: This can be used as a replacement for `state_getMetadata`, `system_accountNextIndex`, and `payment_queryInfo`.
#### Possible errors
**TODO**: more precise
- If the block hash passed as parameter doesn't correspond to any known block, then a `{"event":"failed","error":"..."}` notification is generated (as explained above).
- If the JSON-RPC server is incapable of executing the Wasm runtime of the given block, a JSON-RPC error should be returned.
- If the method to call doesn't exist in the Wasm runtime of the chain, **TODO**.
- 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 **TODO**.
- If the networking part of the behaviour fails, then a `{"event":"failed","error":"..."}` notification is generated (as explained above).
### chainHead_v1_follow
**Parameters**:
- `runtimeUpdates`: A boolean indicating whether the events should report changes to the runtime.
**Return value**: String containing an opaque value representing the subscription.
This function works as follows:
- When called, returns a subscription id.
- Later, generates an `initialized` notification containing the hash of the current finalized block, and if `runtimeUpdates` is `true` the runtime specification of the runtime of the current finalized block.
- Afterwards, generates one `newBlock` notification (see below) for each non-finalized block currently in the node's memory (including all forks), then a `bestBlockChanged` notification. The notifications must be sent in an ordered way such that the parent of each block either can be found in an earlier notification or is the current finalized block.
- When a new block arrives, generates a `newBlock` notification. If the new block is also the new best block of the node, also generates a `bestBlockChanged` notification.
- When the node finalizes a block, generates a `finalized` notification indicating which blocks have been finalized (both directly and indirectly) ordered by ascending height, and which blocks have been pruned (without any ordering). The latest notified best block must *not* be in the list of pruned blocks. If that would happen, a `bestBlockChanged` notification needs to be generated beforehand.
- 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.
If `runtimeUpdates` 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 new block. This means that blocks might be reported more quickly when `runtimeUpdates` is `false`.
If `runtimeUpdates` is `false`, then the `initialized` event must be sent back quickly after the function returns. If `runtimeUpdates` is `true`, then the JSON-RPC server can take as much time as it wants to send back the `initialized` event.
Notifications format:
```json
{
"jsonrpc": "2.0",
"method": "chainHead_v1_followEvent",
"params": {
"subscriptionId": "...",
"result": ...
}
}
```
Where `result` can be one of:
```json
{
"event": "initialized",
"finalizedBlockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"finalizedBlockRuntime": {
"type": "valid",
"spec": {
"specName": ...,
"implName": ...,
"authoringVersion": ...,
"specVersion": ...,
"implVersion": ...,
"transactionVersion": ...,
"apis": [...],
}
}
}
```
The `initialized` event is always the first event to be sent back, and is only ever sent back once per subscription.
`finalizedBlockRuntime` is present if and only if `runtimeUpdates`, the parameter to this function, is `true`.
```json
{
"event": "newBlock",
"blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"parentBlockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"newRuntime": ...
}
```
`newRuntime` must not be present if `runtimeUpdates`, the parameter to this function, is `false`. `newRuntime` must be `null` if the runtime hasn't changed compared to its parent, or . Its format is the same as the `finalizedBlockRuntime` field in the `initialized` event.
```json
{
"event": "bestBlockChanged",
"bestBlockHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}
```
```json
{
"event": "finalized",
"finalizedBlocksHashes": [
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000"
],
"prunedBlocksHashes": [
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000"
]
}
```
```json
{
"event": "stop"
}
```
**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.
The current finalized block reported in the `initialized` event, and each subsequent block reported with a `newBlock` event, is 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`.
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 blockchain, 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 should be large enough for clients to be able to pin all existing non-finalized blocks and a few finalized blocks.
**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.
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 various fields of `spec` are:
- `specVersion`: Opaque version number. The JSON-RPC client can assume that the call to `Metadata_metadata` will always produce the same output as long as the `specVersion` is the same.
- `transactionVersion`: Opaque version number. Necessary when building the bytes of an extrinsic. Extrinsics that have been generated with a different `transactionVersion` are incompatible.
- `apis`: Object containing a list of "entry point APIs" supported by the runtime. Each key is the 8-bytes blake2 hash of the name of the API, and each value is a version number. Before making a runtime call (using `chainHead_v1_call`), you should make sure that this list contains the entry point API corresponding to the call and with a known version number.
**TODO**: detail the other fields
Example value:
```json
{
"specName": "westend",
"implName": "parity-westend",
"authoringVersion": 2,
"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
}
}
```
**Note**: The format of `apis` is not the same as in the current JSON-RPC API.
If the node fails to compile the Wasm runtime blob of a block, `finalizedBlockRuntime` or `newRuntime` can be of the format `{"type": "invalid", "error": "..."}` where `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.
### chainHead_v1_genesisHash
**Parameters**: *none*
**Return value**: String containing the hex-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.
### chainHead_v1_header
**Parameters**:
- `followSubscriptionId`: An opaque string that was returned by `chainHead_v1_follow`.
- `hash`: String containing the hexadecimal-encoded hash of the header to retrieve.
**Return value**:
- If the `followSubscriptionId` is still alive (the vast majority of the time), the hexadecimal-encoded SCALE-encoded header of the block.
- If the `followSubscriptionId` is dead, *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_header_v1` if instead you want to retrieve the header of an arbitrary block.
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 is generated if the `followSubscriptionId` is invalid.
- A JSON-RPC error is generated if the block hash passed as parameter doesn't correspond to any block that has been reported by `chainHead_v1_follow`.
- A JSON-RPC error is generated if the `followSubscriptionId` is valid but the block hash passed as parameter has already been unpinned.
### chainHead_v1_stopBody
**Parameters**:
- `subscriptionId`: An opaque string that was returned by `chainHead_v1_body`.
**Return value**: *null*
Stops a body fetch started with `chainHead_v1_body`. If the body fetch was still in progress, this interrupts it. If the body fetch was already finished, this call has no effect.
JSON-RPC client implementations must be aware that, due to the asynchronous nature of JSON-RPC client <-> server communication, they might still receive a notification about this body fetch, for example because this notification was already in the process of being sent back by the JSON-RPC server.
#### Possible errors
A JSON-RPC error is generated if the `subscriptionId` doesn't correspond to any active subscription.
### chainHead_v1_stopCall
**Parameters**:
- `subscriptionId`: An opaque string that was returned by `chainHead_v1_call`.
**Return value**: *null*
Stops a call started with `chainHead_v1_call`. If the call was still in progress, this interrupts it. If the call was already finished, this call has no effect.
JSON-RPC client implementations must be aware that, due to the asynchronous nature of JSON-RPC client <-> server communication, they might still receive a notification about this call, for example because this notification was already in the process of being sent back by the JSON-RPC server.
### chainHead_v1_stopStorage
**Parameters**:
- `followSubscriptionId`: An opaque string that was returned by `chainHead_v1_storage`.
**Return value**: *null*
Stops a storage fetch started with `chainHead_v1_storage`. If the storage fetch was still in progress, this interrupts it. If the storage fetch was already finished, this call has no effect.
JSON-RPC client implementations must be aware that, due to the asynchronous nature of JSON-RPC client <-> server communication, they might still receive a notification about this storage fetch, for example because this notification was already in the process of being sent back by the JSON-RPC server.
### chainHead_v1_storage
**Parameters**:
- `followSubscriptionId`: An opaque string that was returned by `chainHead_v1_follow`.
- `hash`: String containing an hexadecimal-encoded hash of the header of the block whose storage to fetch.
- `key`: String containing the hexadecimal-encoded key to fetch in the storage.
- `childKey`: `null` for main storage look-ups, or a string containing the hexadecimal-encoded key of the trie key of the trie that `key` refers to. **TODO**: I don't know enough about child tries to design this properly
- `type`: String that must be equal to one of: `value`, `hash`, or `size`.
- `networkConfig` (optional): Object containing the configuration of the networking part of the function. See above for details. Ignored if the JSON-RPC server doesn't need to perform a network request. Sensible defaults are used if not provided.
**Return value**: An opaque string that identifies the storage fetch in progress.
The JSON-RPC server must start obtaining the value of the entry with the given `key` (and possibly `childKey`) from the storage.
For optimization purposes, the JSON-RPC server is allowed to wait a little bit (e.g. up to 100ms) before starting to try fulfill the storage request, in order to batch multiple storage requests together.
This function will later generate notifications looking like this:
```json
{
"jsonrpc": "2.0",
"method": "chainHead_v1_storageEvent",
"params": {
"subscriptionId": "...",
"result": ...
}
}
```
If everything is successful, `result` will be:
```json
{
"event": "done",
"value": "0x0000000..."
}
```
Where `value` is:
- If `type` was `value`, either `null` if the storage doesn't contain a value at the given key, or a string containing the hex-encoded value of the storage entry.
- If `type` was `hash`, either `null` if the storage doesn't contain a value at the given key, or a string containing the hex-encoded hash of the value of the storage item. The hashing algorithm is the same as the one used by the trie of the chain.
- If `type` was `size`, either `null` if the storage doesn't contain a value at the given key, or a string containing the number of bytes of the storage entry. Note that a string is used rather than a number in order to prevent JavaScript clients from accidentally rounding the value.
Alternatively, if `result` can also be:
```json
{
"event": "failed"
}
```
Which indicates that the storage value has failed to be retrieved from the network.
Alternatively, if the `followSubscriptionId` is dead, then `result` can also be:
```json
{
"event": "disjoint"
}
```
After an `"event": "done"`, `"event": "failed"`, or `"event": "disjoint"` is received, no more notification will be generated.
**Note**: Other events might be added in the future, such as reports on the progress of the fetch.
#### Possible errors
- A JSON-RPC error is generated if `type` isn't one of the allowed values (similarly to a missing parameter or an invalid parameter type).
- If the networking part of the behaviour fails, then a `{"event": "failed"}` notification is generated (as explained above).
- A JSON-RPC error is generated if the `followSubscriptionId` is invalid.
- If the `followSubscriptionId` is dead, then a `{"event": "disjoint"}` notification is generated (as explained above).
- A JSON-RPC error is generated if the block hash passed as parameter doesn't correspond to any block that has been reported by `chainHead_v1_follow`.
- A JSON-RPC error is generated if the `followSubscriptionId` is valid but the block hash passed as parameter has already been unpinned.
### chainHead_v1_unfollow
**Parameters**:
- `followSubscriptionId`: An opaque string that was returned by `chainHead_v1_follow`.
**Return value**: *null*
Stops a subscription started with `chainHead_v1_follow`.
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**:
- `followSubscriptionId`: An opaque string that was returned by `chainHead_v1_follow`.
- `hash`: String 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`.
#### Possible errors
- A JSON-RPC error is generated if the `followSubscriptionId` doesn't correspond to any active subscription.
- A JSON-RPC error is generated if the block hash passed as parameter doesn't correspond to any block that has been reported by `chainHead_v1_follow`.
- A JSON-RPC error is generated if the `followSubscriptionId` is valid but the block hash passed as parameter has already been unpinned.
- No error is generated if the `followSubscriptionId` is dead. The call is simply ignored.
### chainHead_unstable_wasmQuery
**TODO**: allow passing a Wasm blob that is executed by a remote
### 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.
### chainSpec_v1_genesisHash
**Parameters**: *none*
**Return value**: String containing the hex-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.
### 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 value returned by this function must never change.
**TODO**: is that bad? stronger guarantees?
### extrinsic_v1_submitAndWatch
**Parameters**:
- `extrinsic`: A hexadecimal-encoded SCALE-encoded extrinsic to try to include in a block.
**Return value**: An opaque string representing the subscription.
This function is similar to the current `author_submitAndWatchExtrinsic`. Note that `author_submitExtrinsic` is gone because it seems not useful.
Notifications format:
```json
{
"jsonrpc": "2.0",
"method": "extrinsic_watchEvent_v1",
"params": {
"subscriptionId": "...",
"result": ...
}
}
```
Where `result` is:
**TODO**: roughly the same as author_submitAndWatchExtrinsic, but needs to be written down
The node can drop a transaction (i.e. send back a `dropped` event extrinsic and discard the extrinsic altogether) if the transaction is invalid, if the JSON-RPC server's transactions pool is full, if the JSON-RPC server's resources have reached their limit, 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.
The JSON-RPC client should unconditionally call `extrinsic_v1_unwatch`, even if **TODO**.
A JSON-RPC error is generated if the `extrinsic` parameter has an invalid format, but no error is produced if the bytes of the `extrinsic`, once decoded, are invalid. Instead, a `dropped` notification will be generated.
### extrinsic_v1_unwatch
**Parameters**:
- `subscription`: An opaque string equal to the value returned by `extrinsic_v1_submitAndWatch`
**Return value**: *null*
**Note**: This function does not remove the extrinsic from the pool. In other words, the node will still try to include the extrinsic in the chain. Having a function that removes the extrinsic 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 `subscriptionId` doesn't correspond to any active subscription.
### rpc_methods
**Parameters**: *none*
**Return value**: an array of strings indicating the names of all the JSON-RPC functions supported by the JSON-RPC server.
### sudo_v1_addReservedPeer
**TODO**: same as `system_addReservedPeer` but properly defined
**TODO**: is this function actually necessary?
### sudo_v1_pendingExtrinsics
**Parameters**: *none*
**Return value**: an array of hexadecimal-encoded SCALE-encoded extrinsics that are in the transactions pool of the node
**TODO**: is this function actually necessary?
### sudo_v1_rotateKeys
**TODO**: same as `author_rotateKeys`
### sudo_v1_removeReservedPeer
**TODO**: same as `system_removeReservedPeer` but properly defined
**TODO**: is this function actually necessary?
### sudo_v1_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"