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).