mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-20 12:45:40 +00:00
rpc: stabilize chainhead backend (#1802)
* rpc: stabilize ChainHeadBackend * remove noise from example * add missing features * make tests compile * make tests compile v2 * revert stop event * feature-gate runtime * Update subxt/Cargo.toml * add docsrs feature stuff * Update subxt/src/backend/chain_head/mod.rs * Update subxt/src/backend/chain_head/mod.rs * Update subxt/src/backend/chain_head/mod.rs
This commit is contained in:
+3
-3
@@ -2,7 +2,7 @@
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use super::rpc_methods::{FollowEvent, UnstableRpcMethods};
|
||||
use super::rpc_methods::{ChainHeadRpcMethods, FollowEvent};
|
||||
use crate::config::Config;
|
||||
use crate::error::Error;
|
||||
use futures::{FutureExt, Stream, StreamExt};
|
||||
@@ -99,7 +99,7 @@ impl<Hash> FollowStream<Hash> {
|
||||
}
|
||||
|
||||
/// Create a new [`FollowStream`] given the RPC methods.
|
||||
pub fn from_methods<T: Config>(methods: UnstableRpcMethods<T>) -> FollowStream<T::Hash> {
|
||||
pub fn from_methods<T: Config>(methods: ChainHeadRpcMethods<T>) -> FollowStream<T::Hash> {
|
||||
FollowStream {
|
||||
stream_getter: Box::new(move || {
|
||||
let methods = methods.clone();
|
||||
@@ -215,7 +215,7 @@ impl<Hash> Stream for FollowStream<Hash> {
|
||||
#[cfg(test)]
|
||||
pub(super) mod test_utils {
|
||||
use super::*;
|
||||
use crate::backend::unstable::rpc_methods::{
|
||||
use crate::backend::chain_head::rpc_methods::{
|
||||
BestBlockChanged, Finalized, Initialized, NewBlock,
|
||||
};
|
||||
use crate::config::substrate::H256;
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
// see LICENSE for license details.
|
||||
|
||||
use super::follow_stream_unpin::{BlockRef, FollowStreamMsg, FollowStreamUnpin};
|
||||
use crate::backend::unstable::rpc_methods::{FollowEvent, Initialized, RuntimeEvent};
|
||||
use crate::backend::chain_head::rpc_methods::{FollowEvent, Initialized, RuntimeEvent};
|
||||
use crate::config::BlockHash;
|
||||
use crate::error::{Error, RpcError};
|
||||
use futures::stream::{Stream, StreamExt};
|
||||
+3
-3
@@ -3,8 +3,8 @@
|
||||
// see LICENSE for license details.
|
||||
|
||||
use super::follow_stream::FollowStream;
|
||||
use super::UnstableRpcMethods;
|
||||
use crate::backend::unstable::rpc_methods::{
|
||||
use super::ChainHeadRpcMethods;
|
||||
use crate::backend::chain_head::rpc_methods::{
|
||||
BestBlockChanged, Finalized, FollowEvent, Initialized, NewBlock,
|
||||
};
|
||||
use crate::config::{BlockHash, Config};
|
||||
@@ -275,7 +275,7 @@ impl<Hash: BlockHash> FollowStreamUnpin<Hash> {
|
||||
/// Create a new [`FollowStreamUnpin`] given the RPC methods.
|
||||
pub fn from_methods<T: Config>(
|
||||
follow_stream: FollowStream<T::Hash>,
|
||||
methods: UnstableRpcMethods<T>,
|
||||
methods: ChainHeadRpcMethods<T>,
|
||||
max_block_life: usize,
|
||||
) -> FollowStreamUnpin<T::Hash> {
|
||||
let unpin_method = Box::new(move |hash: T::Hash, sub_id: Arc<str>| {
|
||||
@@ -38,22 +38,22 @@ use std::task::Poll;
|
||||
use storage_items::StorageItems;
|
||||
|
||||
// Expose the RPC methods.
|
||||
pub use rpc_methods::UnstableRpcMethods;
|
||||
pub use rpc_methods::ChainHeadRpcMethods;
|
||||
|
||||
/// Configure and build an [`UnstableBackend`].
|
||||
pub struct UnstableBackendBuilder<T> {
|
||||
/// Configure and build an [`ChainHeadBackend`].
|
||||
pub struct ChainHeadBackendBuilder<T> {
|
||||
max_block_life: usize,
|
||||
_marker: std::marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Config> Default for UnstableBackendBuilder<T> {
|
||||
impl<T: Config> Default for ChainHeadBackendBuilder<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> UnstableBackendBuilder<T> {
|
||||
/// Create a new [`UnstableBackendBuilder`].
|
||||
impl<T: Config> ChainHeadBackendBuilder<T> {
|
||||
/// Create a new [`ChainHeadBackendBuilder`].
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
max_block_life: usize::MAX,
|
||||
@@ -73,15 +73,20 @@ impl<T: Config> UnstableBackendBuilder<T> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Given an [`RpcClient`] to use to make requests, this returns a tuple of an [`UnstableBackend`],
|
||||
/// which implements the [`Backend`] trait, and an [`UnstableBackendDriver`] which must be polled in
|
||||
/// order for the backend to make progress.
|
||||
/// A low-level API to build the backend and driver which requires polling the driver for the backend
|
||||
/// to make progress.
|
||||
///
|
||||
/// This is useful if you want to manage the driver yourself, for example if you want to run it in on
|
||||
/// a specific runtime.
|
||||
///
|
||||
/// If you just want to run the driver in the background until completion in on the default runtime,
|
||||
/// use [`ChainHeadBackendBuilder::build_with_background_driver`] instead.
|
||||
pub fn build(
|
||||
self,
|
||||
client: impl Into<RpcClient>,
|
||||
) -> (UnstableBackend<T>, UnstableBackendDriver<T>) {
|
||||
) -> (ChainHeadBackend<T>, ChainHeadBackendDriver<T>) {
|
||||
// Construct the underlying follow_stream layers:
|
||||
let rpc_methods = UnstableRpcMethods::new(client.into());
|
||||
let rpc_methods = ChainHeadRpcMethods::new(client.into());
|
||||
let follow_stream =
|
||||
follow_stream::FollowStream::<T::Hash>::from_methods(rpc_methods.clone());
|
||||
let follow_stream_unpin = follow_stream_unpin::FollowStreamUnpin::<T::Hash>::from_methods(
|
||||
@@ -92,26 +97,61 @@ impl<T: Config> UnstableBackendBuilder<T> {
|
||||
let follow_stream_driver = FollowStreamDriver::new(follow_stream_unpin);
|
||||
|
||||
// Wrap these into the backend and driver that we'll expose.
|
||||
let backend = UnstableBackend {
|
||||
let backend = ChainHeadBackend {
|
||||
methods: rpc_methods,
|
||||
follow_handle: follow_stream_driver.handle(),
|
||||
};
|
||||
let driver = UnstableBackendDriver {
|
||||
let driver = ChainHeadBackendDriver {
|
||||
driver: follow_stream_driver,
|
||||
};
|
||||
|
||||
(backend, driver)
|
||||
}
|
||||
|
||||
/// An API to build the backend and driver which will run in the background until completion
|
||||
/// on the default runtime.
|
||||
///
|
||||
/// - On non-wasm targets, this will spawn the driver on `tokio`.
|
||||
/// - On wasm targets, this will spawn the driver on `wasm-bindgen-futures`.
|
||||
#[cfg(feature = "runtime")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "runtime")))]
|
||||
pub fn build_with_background_driver(self, client: impl Into<RpcClient>) -> ChainHeadBackend<T> {
|
||||
fn spawn<F: std::future::Future + Send + 'static>(future: F) {
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
tokio::spawn(async move {
|
||||
future.await;
|
||||
});
|
||||
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
future.await;
|
||||
});
|
||||
}
|
||||
|
||||
let (backend, mut driver) = self.build(client);
|
||||
|
||||
spawn(async move {
|
||||
while let Some(res) = driver.next().await {
|
||||
if let Err(e) = res {
|
||||
if !e.is_disconnected_will_reconnect() {
|
||||
tracing::debug!(target: "subxt", "chainHead driver was closed: {e}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
backend
|
||||
}
|
||||
}
|
||||
|
||||
/// Driver for the [`UnstableBackend`]. This must be polled in order for the
|
||||
/// Driver for the [`ChainHeadBackend`]. This must be polled in order for the
|
||||
/// backend to make progress.
|
||||
#[derive(Debug)]
|
||||
pub struct UnstableBackendDriver<T: Config> {
|
||||
pub struct ChainHeadBackendDriver<T: Config> {
|
||||
driver: FollowStreamDriver<T::Hash>,
|
||||
}
|
||||
|
||||
impl<T: Config> Stream for UnstableBackendDriver<T> {
|
||||
impl<T: Config> Stream for ChainHeadBackendDriver<T> {
|
||||
type Item = <FollowStreamDriver<T::Hash> as Stream>::Item;
|
||||
fn poll_next(
|
||||
mut self: std::pin::Pin<&mut Self>,
|
||||
@@ -121,19 +161,19 @@ impl<T: Config> Stream for UnstableBackendDriver<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// The unstable backend.
|
||||
/// The chainHead backend.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UnstableBackend<T: Config> {
|
||||
pub struct ChainHeadBackend<T: Config> {
|
||||
// RPC methods we'll want to call:
|
||||
methods: UnstableRpcMethods<T>,
|
||||
methods: ChainHeadRpcMethods<T>,
|
||||
// A handle to the chainHead_follow subscription:
|
||||
follow_handle: FollowStreamDriverHandle<T::Hash>,
|
||||
}
|
||||
|
||||
impl<T: Config> UnstableBackend<T> {
|
||||
/// Configure and construct an [`UnstableBackend`] and the associated [`UnstableBackendDriver`].
|
||||
pub fn builder() -> UnstableBackendBuilder<T> {
|
||||
UnstableBackendBuilder::new()
|
||||
impl<T: Config> ChainHeadBackend<T> {
|
||||
/// Configure and construct an [`ChainHeadBackend`] and the associated [`ChainHeadBackendDriver`].
|
||||
pub fn builder() -> ChainHeadBackendBuilder<T> {
|
||||
ChainHeadBackendBuilder::new()
|
||||
}
|
||||
|
||||
/// Stream block headers based on the provided filter fn
|
||||
@@ -193,10 +233,10 @@ impl<Hash: BlockHash + 'static> From<follow_stream_unpin::BlockRef<Hash>> for Bl
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> super::sealed::Sealed for UnstableBackend<T> {}
|
||||
impl<T: Config> super::sealed::Sealed for ChainHeadBackend<T> {}
|
||||
|
||||
#[async_trait]
|
||||
impl<T: Config + Send + Sync + 'static> Backend<T> for UnstableBackend<T> {
|
||||
impl<T: Config + Send + Sync + 'static> Backend<T> for ChainHeadBackend<T> {
|
||||
async fn storage_fetch_values(
|
||||
&self,
|
||||
keys: Vec<Vec<u8>>,
|
||||
+13
-13
@@ -19,15 +19,15 @@ use std::task::Poll;
|
||||
/// some `T: Config` trait which determines some of the types that the RPC methods will
|
||||
/// take or hand back.
|
||||
#[derive_where(Clone, Debug)]
|
||||
pub struct UnstableRpcMethods<T> {
|
||||
pub struct ChainHeadRpcMethods<T> {
|
||||
client: RpcClient,
|
||||
_marker: std::marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Config> UnstableRpcMethods<T> {
|
||||
impl<T: Config> ChainHeadRpcMethods<T> {
|
||||
/// Instantiate the legacy RPC method interface.
|
||||
pub fn new(client: RpcClient) -> Self {
|
||||
UnstableRpcMethods {
|
||||
ChainHeadRpcMethods {
|
||||
client,
|
||||
_marker: std::marker::PhantomData,
|
||||
}
|
||||
@@ -36,15 +36,15 @@ impl<T: Config> UnstableRpcMethods<T> {
|
||||
/// Subscribe to `chainHead_v1_follow` to obtain all reported blocks by the chain.
|
||||
///
|
||||
/// The subscription ID can be used to make queries for the
|
||||
/// block's body ([`chainHead_v1_body`](UnstableRpcMethods::chainhead_v1_follow)),
|
||||
/// block's header ([`chainHead_v1_header`](UnstableRpcMethods::chainhead_v1_header)),
|
||||
/// block's storage ([`chainHead_v1_storage`](UnstableRpcMethods::chainhead_v1_storage)) and submitting
|
||||
/// runtime API calls at this block ([`chainHead_v1_call`](UnstableRpcMethods::chainhead_v1_call)).
|
||||
/// block's body ([`chainHead_v1_body`](ChainHeadRpcMethods::chainhead_v1_follow)),
|
||||
/// block's header ([`chainHead_v1_header`](ChainHeadRpcMethods::chainhead_v1_header)),
|
||||
/// block's storage ([`chainHead_v1_storage`](ChainHeadRpcMethods::chainhead_v1_storage)) and submitting
|
||||
/// runtime API calls at this block ([`chainHead_v1_call`](ChainHeadRpcMethods::chainhead_v1_call)).
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// When the user is no longer interested in a block, the user is responsible
|
||||
/// for calling the [`chainHead_v1_unpin`](UnstableRpcMethods::chainhead_v1_unpin) method.
|
||||
/// for calling the [`chainHead_v1_unpin`](ChainHeadRpcMethods::chainhead_v1_unpin) method.
|
||||
/// Failure to do so will result in the subscription being stopped by generating the `Stop` event.
|
||||
pub async fn chainhead_v1_follow(
|
||||
&self,
|
||||
@@ -106,7 +106,7 @@ impl<T: Config> UnstableRpcMethods<T> {
|
||||
/// # Note
|
||||
///
|
||||
/// The subscription ID is obtained from an open subscription created by
|
||||
/// [`chainHead_v1_follow`](UnstableRpcMethods::chainhead_v1_follow).
|
||||
/// [`chainHead_v1_follow`](ChainHeadRpcMethods::chainhead_v1_follow).
|
||||
pub async fn chainhead_v1_body(
|
||||
&self,
|
||||
subscription_id: &str,
|
||||
@@ -125,7 +125,7 @@ impl<T: Config> UnstableRpcMethods<T> {
|
||||
/// # Note
|
||||
///
|
||||
/// The subscription ID is obtained from an open subscription created by
|
||||
/// [`chainHead_v1_follow`](UnstableRpcMethods::chainhead_v1_follow).
|
||||
/// [`chainHead_v1_follow`](ChainHeadRpcMethods::chainhead_v1_follow).
|
||||
pub async fn chainhead_v1_header(
|
||||
&self,
|
||||
subscription_id: &str,
|
||||
@@ -151,7 +151,7 @@ impl<T: Config> UnstableRpcMethods<T> {
|
||||
/// # Note
|
||||
///
|
||||
/// The subscription ID is obtained from an open subscription created by
|
||||
/// [`chainHead_v1_follow`](UnstableRpcMethods::chainhead_v1_follow).
|
||||
/// [`chainHead_v1_follow`](ChainHeadRpcMethods::chainhead_v1_follow).
|
||||
pub async fn chainhead_v1_storage(
|
||||
&self,
|
||||
subscription_id: &str,
|
||||
@@ -186,7 +186,7 @@ impl<T: Config> UnstableRpcMethods<T> {
|
||||
/// # Note
|
||||
///
|
||||
/// The subscription ID is obtained from an open subscription created by
|
||||
/// [`chainHead_v1_follow`](UnstableRpcMethods::chainhead_v1_follow).
|
||||
/// [`chainHead_v1_follow`](ChainHeadRpcMethods::chainhead_v1_follow).
|
||||
pub async fn chainhead_v1_call(
|
||||
&self,
|
||||
subscription_id: &str,
|
||||
@@ -210,7 +210,7 @@ impl<T: Config> UnstableRpcMethods<T> {
|
||||
/// # Note
|
||||
///
|
||||
/// The subscription ID is obtained from an open subscription created by
|
||||
/// [`chainHead_v1_follow`](UnstableRpcMethods::chainhead_v1_follow).
|
||||
/// [`chainHead_v1_follow`](ChainHeadRpcMethods::chainhead_v1_follow).
|
||||
pub async fn chainhead_v1_unpin(
|
||||
&self,
|
||||
subscription_id: &str,
|
||||
+2
-2
@@ -5,7 +5,7 @@
|
||||
use super::follow_stream_driver::FollowStreamDriverHandle;
|
||||
use super::follow_stream_unpin::BlockRef;
|
||||
use super::rpc_methods::{
|
||||
FollowEvent, MethodResponse, StorageQuery, StorageResult, UnstableRpcMethods,
|
||||
ChainHeadRpcMethods, FollowEvent, MethodResponse, StorageQuery, StorageResult,
|
||||
};
|
||||
use crate::config::Config;
|
||||
use crate::error::{Error, RpcError};
|
||||
@@ -35,7 +35,7 @@ impl<T: Config> StorageItems<T> {
|
||||
queries: impl Iterator<Item = StorageQuery<&[u8]>>,
|
||||
at: T::Hash,
|
||||
follow_handle: &FollowStreamDriverHandle<T::Hash>,
|
||||
methods: UnstableRpcMethods<T>,
|
||||
methods: ChainHeadRpcMethods<T>,
|
||||
) -> Result<Self, Error> {
|
||||
let sub_id = super::get_subscription_id(follow_handle).await?;
|
||||
|
||||
+14
-11
@@ -6,9 +6,9 @@
|
||||
//! the necessary information (probably from a JSON-RPC API, but that's up to the
|
||||
//! implementation).
|
||||
|
||||
pub mod chain_head;
|
||||
pub mod legacy;
|
||||
pub mod rpc;
|
||||
pub mod unstable;
|
||||
pub mod utils;
|
||||
|
||||
use subxt_core::client::RuntimeVersion;
|
||||
@@ -881,18 +881,18 @@ mod test {
|
||||
OperationStorageItems, RuntimeSpec, RuntimeVersionEvent,
|
||||
};
|
||||
|
||||
use super::unstable::*;
|
||||
use super::chain_head::*;
|
||||
use super::*;
|
||||
|
||||
fn build_backend(
|
||||
rpc_client: impl RpcClientT,
|
||||
) -> (UnstableBackend<Conf>, UnstableBackendDriver<Conf>) {
|
||||
let (backend, driver): (UnstableBackend<Conf>, _) =
|
||||
UnstableBackend::builder().build(rpc_client);
|
||||
) -> (ChainHeadBackend<Conf>, ChainHeadBackendDriver<Conf>) {
|
||||
let (backend, driver): (ChainHeadBackend<Conf>, _) =
|
||||
ChainHeadBackend::builder().build(rpc_client);
|
||||
(backend, driver)
|
||||
}
|
||||
|
||||
fn build_backend_spawn_background(rpc_client: impl RpcClientT) -> UnstableBackend<Conf> {
|
||||
fn build_backend_spawn_background(rpc_client: impl RpcClientT) -> ChainHeadBackend<Conf> {
|
||||
let (backend, mut driver) = build_backend(rpc_client);
|
||||
tokio::spawn(async move {
|
||||
while let Some(val) = driver.next().await {
|
||||
@@ -931,7 +931,7 @@ mod test {
|
||||
serde_json::from_value(spec).unwrap()
|
||||
}
|
||||
|
||||
type FollowEvent = unstable::rpc_methods::FollowEvent<<Conf as Config>::Hash>;
|
||||
type FollowEvent = chain_head::rpc_methods::FollowEvent<<Conf as Config>::Hash>;
|
||||
|
||||
fn setup_mock_rpc_client(cycle_ids: bool) -> MockRpcBuilder {
|
||||
let hash = random_hash();
|
||||
@@ -993,13 +993,16 @@ mod test {
|
||||
operation_id: id.to_owned(),
|
||||
})
|
||||
}
|
||||
fn storage_result(key: &str, value: &str) -> unstable::rpc_methods::StorageResult {
|
||||
unstable::rpc_methods::StorageResult {
|
||||
fn storage_result(key: &str, value: &str) -> chain_head::rpc_methods::StorageResult {
|
||||
chain_head::rpc_methods::StorageResult {
|
||||
key: Bytes(key.to_owned().into()),
|
||||
result: rpc_methods::StorageResultType::Value(Bytes(value.to_owned().into())),
|
||||
}
|
||||
}
|
||||
fn storage_items(id: &str, items: &[unstable::rpc_methods::StorageResult]) -> FollowEvent {
|
||||
fn storage_items(
|
||||
id: &str,
|
||||
items: &[chain_head::rpc_methods::StorageResult],
|
||||
) -> FollowEvent {
|
||||
FollowEvent::OperationStorageItems(OperationStorageItems {
|
||||
operation_id: id.to_owned(),
|
||||
items: VecDeque::from(items.to_owned()),
|
||||
@@ -1400,7 +1403,7 @@ mod test {
|
||||
.add_mock_data(mock_data)
|
||||
.add_mock_data(response_data)
|
||||
.build();
|
||||
let (backend, mut driver): (UnstableBackend<Conf>, _) = build_backend(rpc_client);
|
||||
let (backend, mut driver): (ChainHeadBackend<Conf>, _) = build_backend(rpc_client);
|
||||
|
||||
let _ = driver.next().await.unwrap();
|
||||
let _ = driver.next().await.unwrap();
|
||||
|
||||
Reference in New Issue
Block a user