WIP: Backends added, Archive backend created

This commit is contained in:
James Wilson
2025-11-28 12:35:33 +00:00
parent 4c27bd8062
commit fbde20cb0e
24 changed files with 5155 additions and 186 deletions
+27 -139
View File
@@ -15,9 +15,9 @@ use serde::{Deserialize, Deserializer, Serialize};
use std::collections::{HashMap, VecDeque};
use std::task::Poll;
/// An interface to call the unstable RPC methods. This interface is instantiated with
/// some `T: Config` trait which determines some of the types that the RPC methods will
/// take or hand back.
/// An interface to call the new ["chainHead" RPC methods](https://paritytech.github.io/json-rpc-interface-spec/).
/// This interface is instantiated with some `T: RpcConfig` trait which determines some of the types that
/// the RPC methods will take or hand back.
#[derive_where(Clone, Debug)]
pub struct ChainHeadRpcMethods<T> {
client: RpcClient,
@@ -386,14 +386,15 @@ impl<T: RpcConfig> ChainHeadRpcMethods<T> {
pub async fn archive_v1_storage(
&self,
block_hash: T::Hash,
items: impl IntoIterator<Item = StorageQuery<&[u8]>>,
items: impl IntoIterator<Item = ArchiveStorageQuery<&[u8]>>,
child_key: Option<&[u8]>,
) -> Result<ArchiveStorageSubscription<T::Hash>, Error> {
let items: Vec<StorageQuery<String>> = items
let items: Vec<ArchiveStorageQuery<String>> = items
.into_iter()
.map(|item| StorageQuery {
.map(|item| ArchiveStorageQuery {
key: to_hex(item.key),
query_type: item.query_type,
pagination_start_key: item.pagination_start_key.map(|k| to_hex(k)),
})
.collect();
@@ -408,137 +409,6 @@ impl<T: RpcConfig> ChainHeadRpcMethods<T> {
Ok(ArchiveStorageSubscription { sub, done: false })
}
// Dev note: we continue to support the latest "unstable" archive methods because
// they will be around for a while before the stable ones make it into a release.
// The below are just a copy-paste of the v1 methods, above, but calling the
// "unstable" RPCs instead. Eventually we'll remove them.
/// Fetch the block body (ie the extrinsics in the block) given its hash.
///
/// Returns an array of the hexadecimal-encoded scale-encoded extrinsics found in the block,
/// or `None` if the block wasn't found.
pub async fn archive_unstable_body(
&self,
block_hash: T::Hash,
) -> Result<Option<Vec<Bytes>>, Error> {
self.client
.request("archive_unstable_body", rpc_params![block_hash])
.await
}
/// Call the `archive_unstable_call` method and return the response.
pub async fn archive_unstable_call(
&self,
block_hash: T::Hash,
function: &str,
call_parameters: &[u8],
) -> Result<ArchiveCallResult, Error> {
use serde::de::Error as _;
// We deserialize to this intermediate shape, since
// we can't have a boolean tag to denote variants.
#[derive(Deserialize)]
struct Response {
success: bool,
value: Option<Bytes>,
error: Option<String>,
// This was accidentally used instead of value in Substrate,
// so to support those impls we try it here if needed:
result: Option<Bytes>,
}
let res: Response = self
.client
.request(
"archive_unstable_call",
rpc_params![block_hash, function, to_hex(call_parameters)],
)
.await?;
let value = res.value.or(res.result);
match (res.success, value, res.error) {
(true, Some(value), _) => Ok(ArchiveCallResult::Success(value)),
(false, _, err) => Ok(ArchiveCallResult::Error(err.unwrap_or(String::new()))),
(true, None, _) => {
let m = "archive_unstable_call: 'success: true' response should have `value: 0x1234` alongside it";
Err(Error::Deserialization(serde_json::Error::custom(m)))
}
}
}
/// Return the finalized block height of the chain.
pub async fn archive_unstable_finalized_height(&self) -> Result<usize, Error> {
self.client
.request("archive_unstable_finalizedHeight", rpc_params![])
.await
}
/// Return the genesis hash.
pub async fn archive_unstable_genesis_hash(&self) -> Result<T::Hash, Error> {
self.client
.request("archive_unstable_genesisHash", rpc_params![])
.await
}
/// Given a block height, return the hashes of the zero or more blocks at that height.
/// For blocks older than the latest finalized block, only one entry will be returned. For blocks
/// newer than the latest finalized block, it's possible to have 0, 1 or multiple blocks at
/// that height given that forks could occur.
pub async fn archive_unstable_hash_by_height(
&self,
height: usize,
) -> Result<Vec<T::Hash>, Error> {
self.client
.request("archive_unstable_hashByHeight", rpc_params![height])
.await
}
/// Fetch the header for a block with the given hash, or `None` if no block with that hash exists.
pub async fn archive_unstable_header(
&self,
block_hash: T::Hash,
) -> Result<Option<T::Header>, Error> {
let maybe_encoded_header: Option<Bytes> = self
.client
.request("archive_unstable_header", rpc_params![block_hash])
.await?;
let Some(encoded_header) = maybe_encoded_header else {
return Ok(None);
};
let header =
<T::Header as codec::Decode>::decode(&mut &*encoded_header.0).map_err(Error::Decode)?;
Ok(Some(header))
}
/// Query the node storage and return a subscription which streams corresponding storage events back.
pub async fn archive_unstable_storage(
&self,
block_hash: T::Hash,
items: impl IntoIterator<Item = StorageQuery<&[u8]>>,
child_key: Option<&[u8]>,
) -> Result<ArchiveStorageSubscription<T::Hash>, Error> {
let items: Vec<StorageQuery<String>> = items
.into_iter()
.map(|item| StorageQuery {
key: to_hex(item.key),
query_type: item.query_type,
})
.collect();
let sub = self
.client
.subscribe(
"archive_unstable_storage",
rpc_params![block_hash, items, child_key.map(to_hex)],
"archive_unstable_stopStorage",
)
.await?;
Ok(ArchiveStorageSubscription { sub, done: false })
}
}
/// This represents events generated by the `follow` method.
@@ -849,6 +719,24 @@ pub struct StorageQuery<Key> {
pub query_type: StorageQueryType,
}
/// The storage item received as parameter. This is used archive storage queries, and
/// unlike [`StorageQuery`] also contains `paginationStartKey` to define where iteration
/// should begin.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ArchiveStorageQuery<Key> {
/// The provided key.
pub key: Key,
/// The type of the storage query.
#[serde(rename = "type")]
pub query_type: StorageQueryType,
/// This parameter is optional and should be a string containing the hexadecimal-encoded key
/// from which the storage iteration should resume. This parameter is only valid in the context
/// of `descendantsValues` and `descendantsHashes`.
#[serde(skip_serializing_if = "Option::is_none")]
pub pagination_start_key: Option<Key>,
}
/// The type of the storage query.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
@@ -1104,7 +992,7 @@ impl<Hash> ArchiveStorageEvent<Hash> {
}
}
/// Something went wrong during the [`ChainHeadRpcMethods::archive_unstable_storage()`] subscription.
/// Something went wrong during the [`ChainHeadRpcMethods::archive_v1_storage()`] subscription.
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ArchiveStorageEventError {
@@ -1112,7 +1000,7 @@ pub struct ArchiveStorageEventError {
pub error: String,
}
/// A storage item returned from the [`ChainHeadRpcMethods::archive_unstable_storage()`] subscription.
/// A storage item returned from the [`ChainHeadRpcMethods::archive_v1_storage()`] subscription.
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ArchiveStorageEventItem<Hash> {
+1 -1
View File
@@ -13,7 +13,7 @@ use primitive_types::U256;
use serde::{Deserialize, Serialize};
/// An interface to call the legacy RPC methods. This interface is instantiated with
/// some `T: Config` trait which determines some of the types that the RPC methods will
/// some `T: RpcConfig` trait which determines some of the types that the RPC methods will
/// take or hand back.
#[derive_where(Clone, Debug)]
pub struct LegacyRpcMethods<T> {