lightclient: Add support for multi-chain usecase (#1238)

* lightclient: Make `smoldot::chainID` part of the RPC requests

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* lightclient: Make `BackgroundTask` generic over `PlatformRef` and chain

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* lightclient: Construct from raw smoldot and target different chains

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* testing: Update cargo lock for wasm tests

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* lightclient: Reuse `new_from_client` method and removed unused imports

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* lightclient: Reexport smoldot client and RPC objects used in pub
interface

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* lightclient: Adjust `new_from_client` interface

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* lightclient: Extend background to poll over multiple RPC objects

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt: Build light client from raw and target different chains

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* artifacts: Add demo chain specs

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* artifacts: Move artifacts to dedicated folder

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* lightclient: Use SelectAll to drive all streams

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* lightclient: Fetch initial data from the target chain

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* lightclient: Reexport other smoldot objects

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt: Target chain with potentially different config

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt/rpc: Log chainID for debugging

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt/examples: Add smoldot client with parachain example

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* lightclient: Propagate chain ID together with rpc responses object

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* lightclient: Multiplex responses by request ID and chain ID

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt: Add raw light client builder

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt: Add cargo feature flag for parachains example

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* lightclient: Derive default for internal structure

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* lightclient: Guard reexports by std feature flag

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Update subxt/src/client/light_client/mod.rs

Co-authored-by: James Wilson <james@jsdw.me>

* lightclient: Update the builder pattern and chain targetting

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* lightclient: Fix documentation

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Provide more insightful docs wrt native/wasm panics

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* examples: Adjust comment location

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* lightclient: Refactor UniqueChainId into the background task

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Update lightclient/src/background.rs

Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com>

* Update subxt/src/client/light_client/builder.rs

Co-authored-by: James Wilson <james@jsdw.me>

* lightclient: Update docs wrt panics

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt: Update docs wrt to smoldot instance -> client

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* lightclient: Use IntoIter instead of Iterator

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt: Adjsut docs wrt [`Self::new_from_client`]

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt: Remove RawRpc from LightClient in favor of chainID

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* lightclient: Reexport everything under smoldot module

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* artifacts: Use stateRootHash instead of genesis.raw

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

---------

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
Co-authored-by: James Wilson <james@jsdw.me>
Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com>
This commit is contained in:
Alexandru Vasile
2023-11-16 18:29:00 +02:00
committed by GitHub
parent 7b210f5a8e
commit 34ff3da37d
11 changed files with 606 additions and 104 deletions
+62 -6
View File
@@ -8,6 +8,7 @@ mod builder;
mod rpc;
use crate::{
backend::rpc::RpcClient,
blocks::BlocksClient,
client::{OfflineClientT, OnlineClientT},
config::Config,
@@ -19,10 +20,13 @@ use crate::{
tx::TxClient,
OnlineClient,
};
pub use builder::LightClientBuilder;
pub use builder::{LightClientBuilder, RawLightClientBuilder};
use derivative::Derivative;
use subxt_lightclient::LightClientRpcError;
// Re-export smoldot related objects.
pub use subxt_lightclient::smoldot;
/// Light client error.
#[derive(Debug, thiserror::Error)]
pub enum LightClientError {
@@ -51,10 +55,57 @@ pub enum LightClientError {
Handshake,
}
/// The raw light-client RPC implementation that is used to connect with the chain.
#[derive(Clone)]
pub struct RawLightClient {
raw_rpc: rpc::RawLightClientRpc,
}
impl RawLightClient {
/// Construct a [`RawLightClient`] using its builder interface.
///
/// The raw builder is utilized for constructing light-clients from a low
/// level smoldot client.
///
/// This is especially useful when you want to gain access to the smoldot client.
/// For example, you may want to connect to multiple chains and/or parachains while reusing the
/// same smoldot client under the hood. Or you may want to configure different values for
/// smoldot internal buffers, number of subscriptions and relay chains.
///
/// # Note
///
/// If you are unsure, please use [`LightClient::builder`] instead.
pub fn builder() -> RawLightClientBuilder {
RawLightClientBuilder::default()
}
/// Target a different chain identified by the provided chain ID for requests.
///
/// The provided chain ID is provided by the `smoldot_light::Client::add_chain` and it must
/// match one of the `smoldot_light::JsonRpcResponses` provided in [`RawLightClientBuilder::add_chain`].
///
/// # Note
///
/// This uses the same underlying instance spawned by the builder.
pub async fn for_chain<TChainConfig: Config>(
&self,
chain_id: smoldot::ChainId,
) -> Result<LightClient<TChainConfig>, crate::Error> {
let raw_rpc = self.raw_rpc.for_chain(chain_id);
let rpc_client = RpcClient::new(raw_rpc.clone());
let client = OnlineClient::<TChainConfig>::from_rpc_client(rpc_client).await?;
Ok(LightClient { client, chain_id })
}
}
/// The light-client RPC implementation that is used to connect with the chain.
#[derive(Derivative)]
#[derivative(Clone(bound = ""))]
pub struct LightClient<T: Config>(OnlineClient<T>);
pub struct LightClient<T: Config> {
client: OnlineClient<T>,
chain_id: smoldot::ChainId,
}
impl<T: Config> LightClient<T> {
/// Construct a [`LightClient`] using its builder interface.
@@ -68,17 +119,17 @@ impl<T: Config> LightClient<T> {
/// Return the [`crate::Metadata`] used in this client.
fn metadata(&self) -> crate::Metadata {
self.0.metadata()
self.client.metadata()
}
/// Return the genesis hash.
fn genesis_hash(&self) -> <T as Config>::Hash {
self.0.genesis_hash()
self.client.genesis_hash()
}
/// Return the runtime version.
fn runtime_version(&self) -> crate::backend::RuntimeVersion {
self.0.runtime_version()
self.client.runtime_version()
}
/// Work with transactions.
@@ -115,11 +166,16 @@ impl<T: Config> LightClient<T> {
pub fn runtime_api(&self) -> RuntimeApiClient<T, Self> {
<Self as OfflineClientT<T>>::runtime_api(self)
}
/// Returns the chain ID of the current light-client.
pub fn chain_id(&self) -> smoldot::ChainId {
self.chain_id
}
}
impl<T: Config> OnlineClientT<T> for LightClient<T> {
fn backend(&self) -> &dyn crate::backend::Backend<T> {
self.0.backend()
self.client.backend()
}
}