// Copyright 2019-2025 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! This module exposes a backend implementation based on the new APIs
//! described at . See
//! [`rpc_methods`] for the raw API calls.
//!
//! Specifically, the focus here is on the `archive` methods. These can only be used
//! to interact with archive nodes, but are less restrictive than the `chainHead` methods
//! in terms of the allowed operations.
mod storage_stream;
use crate::backend::{
Backend, BlockRef, StorageResponse, StreamOf, StreamOfResults,
TransactionStatus, utils::retry,
};
use crate::config::{Config, HashFor, RpcConfigFor};
use crate::error::BackendError;
use async_trait::async_trait;
use futures::StreamExt;
use subxt_rpcs::RpcClient;
use subxt_rpcs::methods::chain_head::{
ArchiveStorageQuery, ArchiveCallResult, StorageQueryType,
};
use storage_stream::ArchiveStorageStream;
// Expose the RPC methods.
pub use subxt_rpcs::methods::chain_head::ChainHeadRpcMethods as ArchiveRpcMethods;
/// The archive backend.
#[derive(Debug, Clone)]
pub struct ArchiveBackend {
// RPC methods we'll want to call:
methods: ArchiveRpcMethods>,
}
impl ArchiveBackend {
/// Configure and construct an [`ArchiveBackend`] and the associated [`ChainHeadBackendDriver`].
pub fn new(client: impl Into,) -> ArchiveBackend {
let methods = ArchiveRpcMethods::new(client.into());
ArchiveBackend { methods }
}
}
#[async_trait]
impl Backend for ArchiveBackend {
async fn storage_fetch_values(
&self,
keys: Vec>,
at: HashFor,
) -> Result, BackendError> {
let queries = keys.into_iter()
.map(|key| ArchiveStorageQuery {
key: key,
query_type: StorageQueryType::Value,
pagination_start_key: None,
})
.collect();
let stream = ArchiveStorageStream::new(at, self.methods.clone(), queries).map(|item| {
match item {
Err(e) => Some(Err(e)),
Ok(item) => item.value.map(|val| Ok(StorageResponse { key: item.key.0, value: val.0 }))
}
}).filter_map(async |item| item);
Ok(StreamOf(Box::pin(stream)))
}
async fn storage_fetch_descendant_keys(
&self,
key: Vec,
at: HashFor,
) -> Result>, BackendError> {
let queries = std::iter::once(ArchiveStorageQuery {
key: key,
// Just ask for the hash and then ignore it and return keys
query_type: StorageQueryType::DescendantsHashes,
pagination_start_key: None,
})
.collect();
let stream = ArchiveStorageStream::new(at, self.methods.clone(), queries).map(|item| {
match item {
Err(e) => Err(e),
Ok(item) => Ok(item.key.0)
}
});
Ok(StreamOf(Box::pin(stream)))
}
async fn storage_fetch_descendant_values(
&self,
key: Vec,
at: HashFor,
) -> Result, BackendError> {
let queries = std::iter::once(ArchiveStorageQuery {
key: key,
query_type: StorageQueryType::DescendantsValues,
pagination_start_key: None,
})
.collect();
let stream = ArchiveStorageStream::new(at, self.methods.clone(), queries).map(|item| {
match item {
Err(e) => Some(Err(e)),
Ok(item) => item.value.map(|val| Ok(StorageResponse { key: item.key.0, value: val.0 }))
}
}).filter_map(async |item| item);
Ok(StreamOf(Box::pin(stream)))
}
async fn genesis_hash(&self) -> Result, BackendError> {
retry(|| async {
let hash = self.methods.archive_v1_genesis_hash().await?;
Ok(hash)
})
.await
}
async fn block_header(&self, at: HashFor) -> Result