chainHead based backend implementation (#1161)

* add follow_stream impl

* follow_stream_unpin first draft

* add tests for follow_stream_unpin

* more tests and fixes for follow_stream_unpin

* first pass follow_stream_driver

* follow_stream_driver: add tests, fix things, buffer events from last finalized

* First pass finishing Backend impl

* Fix test compile issues

* clippy fixes

* clippy fix and consistify light_client

* revert lightclient tweak

* revert other lightclient thing

* cargo fmt

* start testing unstable backend behind feature flag

* more test fixes and move test-runtime metadata path just incase

* fix compile error

* ensure transaction progress stream actually used and fix another test

* cargo fmt

* CI tweak

* improve some comments and address some feedback bits

* update CI to use our own nightly binary

* wait for finalized block perhaps
This commit is contained in:
James Wilson
2023-09-26 16:58:30 +01:00
committed by GitHub
parent 00cce68371
commit cf7e2db1b7
43 changed files with 2682 additions and 250 deletions
+131
View File
@@ -0,0 +1,131 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use super::LightClientError;
use crate::{
backend::rpc::{RawRpcFuture, RawRpcSubscription, RpcClientT},
error::{Error, RpcError},
};
use futures::StreamExt;
use serde_json::value::RawValue;
use subxt_lightclient::{AddChainConfig, ChainId, LightClientRpcError};
use tokio_stream::wrappers::UnboundedReceiverStream;
pub const LOG_TARGET: &str = "light-client";
/// The light-client RPC implementation that is used to connect with the chain.
#[derive(Clone)]
pub struct LightClientRpc(subxt_lightclient::LightClientRpc);
impl LightClientRpc {
/// Constructs a new [`LightClientRpc`], providing the chain specification.
///
/// The chain specification can be downloaded from a trusted network via
/// the `sync_state_genSyncSpec` RPC method. This parameter expects the
/// chain spec in text format (ie not in hex-encoded scale-encoded as RPC methods
/// will provide).
///
/// ## Panics
///
/// Panics if being called outside of `tokio` runtime context.
pub fn new(
config: AddChainConfig<'_, (), impl Iterator<Item = ChainId>>,
) -> Result<LightClientRpc, Error> {
let rpc = subxt_lightclient::LightClientRpc::new(config)
.map_err(|err| LightClientError::Rpc(err))?;
Ok(LightClientRpc(rpc))
}
}
impl RpcClientT for LightClientRpc {
fn request_raw<'a>(
&'a self,
method: &'a str,
params: Option<Box<RawValue>>,
) -> RawRpcFuture<'a, Box<RawValue>> {
let client = self.clone();
Box::pin(async move {
let params = match params {
Some(params) => serde_json::to_string(&params).map_err(|_| {
RpcError::ClientError(Box::new(LightClientError::InvalidParams))
})?,
None => "[]".into(),
};
// Fails if the background is closed.
let rx = client
.0
.method_request(method.to_string(), params)
.map_err(|_| RpcError::ClientError(Box::new(LightClientError::BackgroundClosed)))?;
// Fails if the background is closed.
let response = rx
.await
.map_err(|_| RpcError::ClientError(Box::new(LightClientError::BackgroundClosed)))?;
tracing::trace!(target: LOG_TARGET, "RPC response {:?}", response);
response.map_err(|err| RpcError::ClientError(Box::new(err)))
})
}
fn subscribe_raw<'a>(
&'a self,
sub: &'a str,
params: Option<Box<RawValue>>,
_unsub: &'a str,
) -> RawRpcFuture<'a, RawRpcSubscription> {
let client = self.clone();
Box::pin(async move {
tracing::trace!(
target: LOG_TARGET,
"Subscribe to {:?} with params {:?}",
sub,
params
);
let params = match params {
Some(params) => serde_json::to_string(&params).map_err(|_| {
RpcError::ClientError(Box::new(LightClientError::InvalidParams))
})?,
None => "[]".into(),
};
// Fails if the background is closed.
let (sub_id, notif) = client
.0
.subscription_request(sub.to_string(), params)
.map_err(|_| RpcError::ClientError(Box::new(LightClientError::BackgroundClosed)))?;
// Fails if the background is closed.
let result = sub_id
.await
.map_err(|_| RpcError::ClientError(Box::new(LightClientError::BackgroundClosed)))?
.map_err(|err| {
RpcError::ClientError(Box::new(LightClientError::Rpc(
LightClientRpcError::Request(err.to_string()),
)))
})?;
let sub_id = result
.get()
.trim_start_matches('"')
.trim_end_matches('"')
.to_string();
tracing::trace!(target: LOG_TARGET, "Received subscription ID: {}", sub_id);
let stream = UnboundedReceiverStream::new(notif);
let rpc_subscription = RawRpcSubscription {
stream: Box::pin(stream.map(Ok)),
id: Some(sub_id),
};
Ok(rpc_subscription)
})
}
}