Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lightclient: Add support for multi-chain usecase #1238

Merged
merged 43 commits into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
4cdfe7b
lightclient: Make `smoldot::chainID` part of the RPC requests
lexnv Oct 31, 2023
962aeff
lightclient: Make `BackgroundTask` generic over `PlatformRef` and chain
lexnv Oct 31, 2023
3fd6929
lightclient: Construct from raw smoldot and target different chains
lexnv Oct 31, 2023
220bff3
testing: Update cargo lock for wasm tests
lexnv Oct 31, 2023
b50e53d
lightclient: Reuse `new_from_client` method and removed unused imports
lexnv Oct 31, 2023
4655a65
lightclient: Reexport smoldot client and RPC objects used in pub
lexnv Oct 31, 2023
f91b10e
lightclient: Adjust `new_from_client` interface
lexnv Oct 31, 2023
0616aa9
lightclient: Extend background to poll over multiple RPC objects
lexnv Nov 1, 2023
c2dba83
subxt: Build light client from raw and target different chains
lexnv Nov 1, 2023
1876b42
artifacts: Add demo chain specs
lexnv Nov 1, 2023
6d945f5
artifacts: Move artifacts to dedicated folder
lexnv Nov 2, 2023
b16c5f3
lightclient: Use SelectAll to drive all streams
lexnv Nov 2, 2023
7ef743d
lightclient: Fetch initial data from the target chain
lexnv Nov 2, 2023
659ea8b
lightclient: Reexport other smoldot objects
lexnv Nov 2, 2023
8abe5aa
subxt: Target chain with potentially different config
lexnv Nov 2, 2023
d873f94
subxt/rpc: Log chainID for debugging
lexnv Nov 2, 2023
d095a51
subxt/examples: Add smoldot client with parachain example
lexnv Nov 2, 2023
c70f655
lightclient: Propagate chain ID together with rpc responses object
lexnv Nov 2, 2023
e2b9def
lightclient: Multiplex responses by request ID and chain ID
lexnv Nov 2, 2023
acaf279
subxt: Add raw light client builder
lexnv Nov 2, 2023
e164b85
subxt: Add cargo feature flag for parachains example
lexnv Nov 2, 2023
ce56fd4
lightclient: Derive default for internal structure
lexnv Nov 2, 2023
7abaf88
lightclient: Guard reexports by std feature flag
lexnv Nov 2, 2023
1bafe50
Merge branch 'master' into lexnv/light-client-multi-chain
lexnv Nov 3, 2023
93e1260
Merge remote-tracking branch 'origin/master' into lexnv/light-client-…
lexnv Nov 6, 2023
0efba8f
Update subxt/src/client/light_client/mod.rs
lexnv Nov 9, 2023
bcbde26
lightclient: Update the builder pattern and chain targetting
lexnv Nov 9, 2023
d9bd705
tMerge remote-tracking branch 'origin/master' into lexnv/light-client…
lexnv Nov 9, 2023
c0be204
lightclient: Fix documentation
lexnv Nov 9, 2023
79dfbd1
Provide more insightful docs wrt native/wasm panics
lexnv Nov 10, 2023
43a1c77
examples: Adjust comment location
lexnv Nov 10, 2023
f7ef096
lightclient: Refactor UniqueChainId into the background task
lexnv Nov 10, 2023
2f8dd47
Update lightclient/src/background.rs
lexnv Nov 10, 2023
0ee52e6
Update subxt/src/client/light_client/builder.rs
lexnv Nov 10, 2023
da4ee7e
lightclient: Update docs wrt panics
lexnv Nov 10, 2023
deb648e
Merge remote-tracking branch 'origin/master' into lexnv/light-client-…
lexnv Nov 10, 2023
2f82e91
subxt: Update docs wrt to smoldot instance -> client
lexnv Nov 10, 2023
46a6adc
lightclient: Use IntoIter instead of Iterator
lexnv Nov 10, 2023
15811fc
subxt: Adjsut docs wrt [`Self::new_from_client`]
lexnv Nov 10, 2023
56ca927
subxt: Remove RawRpc from LightClient in favor of chainID
lexnv Nov 10, 2023
0cfe8d3
lightclient: Reexport everything under smoldot module
lexnv Nov 10, 2023
25581e4
Merge remote-tracking branch 'origin/master' into lexnv/light-client-…
lexnv Nov 16, 2023
84bf673
artifacts: Use stateRootHash instead of genesis.raw
lexnv Nov 16, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions artifacts/demo_chain_specs/polkadot.json

Large diffs are not rendered by default.

45 changes: 45 additions & 0 deletions artifacts/demo_chain_specs/polkadot_asset_hub.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"name": "Polkadot Asset Hub",
"id": "asset-hub-polkadot",
"chainType": "Live",
"bootNodes": [
"/ip4/34.65.251.121/tcp/30334/p2p/12D3KooWG3GrM6XKMM4gp3cvemdwUvu96ziYoJmqmetLZBXE8bSa",
"/ip4/34.65.35.228/tcp/30334/p2p/12D3KooWMRyTLrCEPcAQD6c4EnudL3vVzg9zji3whvsMYPUYevpq",
"/dns/polkadot-asset-hub-connect-0.polkadot.io/tcp/30334/p2p/12D3KooWLHqbcQtoBygf7GJgVjVa3TaeLuf7VbicNdooaCmQM2JZ",
"/dns/polkadot-asset-hub-connect-0.polkadot.io/tcp/443/wss/p2p/12D3KooWLHqbcQtoBygf7GJgVjVa3TaeLuf7VbicNdooaCmQM2JZ",
"/dns/polkadot-asset-hub-connect-1.polkadot.io/tcp/30334/p2p/12D3KooWNDrKSayoZXGGE2dRSFW2g1iGPq3fTZE2U39ma9yZGKd3",
"/dns/polkadot-asset-hub-connect-1.polkadot.io/tcp/443/wss/p2p/12D3KooWNDrKSayoZXGGE2dRSFW2g1iGPq3fTZE2U39ma9yZGKd3",
"/dns/polkadot-asset-hub-connect-2.polkadot.io/tcp/30334/p2p/12D3KooWApa2JW4rbLtgzuK7fjLMupLS9HZheX9cdkQKyu6AnGrP",
"/dns/polkadot-asset-hub-connect-2.polkadot.io/tcp/443/wss/p2p/12D3KooWApa2JW4rbLtgzuK7fjLMupLS9HZheX9cdkQKyu6AnGrP",
"/dns/polkadot-asset-hub-connect-3.polkadot.io/tcp/30334/p2p/12D3KooWRsVeHqRs2iKmjLiguxp8myL4G2mDAWhtX2jHwyWujseV",
"/dns/polkadot-asset-hub-connect-3.polkadot.io/tcp/443/wss/p2p/12D3KooWRsVeHqRs2iKmjLiguxp8myL4G2mDAWhtX2jHwyWujseV",
"/dns/boot.stake.plus/tcp/35333/p2p/12D3KooWFrQjYaPZSSLLxEVmoaHFcrF6VoY4awG4KRSLaqy3JCdQ",
"/dns/boot.stake.plus/tcp/35334/wss/p2p/12D3KooWFrQjYaPZSSLLxEVmoaHFcrF6VoY4awG4KRSLaqy3JCdQ",
"/dns/boot.metaspan.io/tcp/16052/p2p/12D3KooWLwiJuvqQUB4kYaSjLenFKH9dWZhGZ4qi7pSb3sUYU651",
"/dns/boot.metaspan.io/tcp/16056/wss/p2p/12D3KooWLwiJuvqQUB4kYaSjLenFKH9dWZhGZ4qi7pSb3sUYU651",
"/dns/boot-cr.gatotech.network/tcp/33110/p2p/12D3KooWKgwQfAeDoJARdtxFNNWfbYmcu6s4yUuSifnNoDgzHZgm",
"/dns/boot-cr.gatotech.network/tcp/35110/wss/p2p/12D3KooWKgwQfAeDoJARdtxFNNWfbYmcu6s4yUuSifnNoDgzHZgm",
"/dns/statemint-bootnode.turboflakes.io/tcp/30315/p2p/12D3KooWL8CyLww3m3pRySQGGYGNJhWDMqko3j5xi67ckP7hDUvo",
"/dns/statemint-bootnode.turboflakes.io/tcp/30415/wss/p2p/12D3KooWL8CyLww3m3pRySQGGYGNJhWDMqko3j5xi67ckP7hDUvo",
"/dns/boot-node.helikon.io/tcp/10220/p2p/12D3KooW9uybhguhDjVJc3U3kgZC3i8rWmAnSpbnJkmuR7C6ZsRW",
"/dns/boot-node.helikon.io/tcp/10222/wss/p2p/12D3KooW9uybhguhDjVJc3U3kgZC3i8rWmAnSpbnJkmuR7C6ZsRW",
"/dns/statemint.bootnode.amforc.com/tcp/30341/p2p/12D3KooWByohP9FXn7ao8syS167qJsbFdpa7fY2Y24xbKtt3r7Ls",
"/dns/statemint.bootnode.amforc.com/tcp/30333/wss/p2p/12D3KooWByohP9FXn7ao8syS167qJsbFdpa7fY2Y24xbKtt3r7Ls",
"/dns/statemint-boot-ng.dwellir.com/tcp/30344/p2p/12D3KooWEFrNuNk8fPdQS2hf34Gmqi6dGSvrETshGJUrqrvfRDZr",
"/dns/statemint-boot-ng.dwellir.com/tcp/443/wss/p2p/12D3KooWEFrNuNk8fPdQS2hf34Gmqi6dGSvrETshGJUrqrvfRDZr"
],
"telemetryEndpoints": null,
"protocolId": null,
"properties": {
"ss58Format": 0,
"tokenDecimals": 10,
"tokenSymbol": "DOT"
},
"relay_chain": "polkadot",
"para_id": 1000,
"consensusEngine": null,
"codeSubstitutes": {},
"genesis": {
"stateRootHash": "0xc1ef26b567de07159e4ecd415fbbb0340c56a09c4d72c82516d0f3bc2b782c80"
}
}
158 changes: 94 additions & 64 deletions lightclient/src/background.rs

Large diffs are not rendered by default.

114 changes: 105 additions & 9 deletions lightclient/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.

use std::iter;

use super::{
background::{BackgroundTask, FromSubxt, MethodResponse},
LightClientRpcError,
Expand All @@ -11,14 +13,38 @@ use tokio::sync::{mpsc, mpsc::error::SendError, oneshot};

use super::platform::build_platform;

pub const LOG_TARGET: &str = "light-client";
pub const LOG_TARGET: &str = "subxt-light-client";

/// A raw light-client RPC implementation that can connect to multiple chains.
#[derive(Clone)]
pub struct RawLightClientRpc {
/// Communicate with the backend task that multiplexes the responses
/// back to the frontend.
to_backend: mpsc::UnboundedSender<FromSubxt>,
}

impl RawLightClientRpc {
/// Construct a [`LightClientRpc`] that can communicated with the provided chain.
///
/// # Note
///
/// This uses the same underlying instance created by [`LightClientRpc::new_from_client`].
pub fn for_chain(&self, chain_id: smoldot_light::ChainId) -> LightClientRpc {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found the naming of this a bit odd,

I would prefer connect_to_chain(id: ChainId)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another question is it possible to connect the same chain more than once?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, its possible to use the same chain ID to create multiple instances of the light client

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have tested this locally with the following patch, because I was also curious if there's anything else to be done here:

    let similar_chain: LightClient<PolkadotConfig> =
        raw_light_client.for_chain(parachain_chain_id).await?;
    let mut sub = similar_chain.blocks().subscribe_all().await?;

    tokio::spawn(async move {
        while let Some(value) = sub.next().await {
            let value = value.expect("OPS");
            println!(" Background  value {:?}", value.hash());
        }
    });

This will subscribe to all the blocks of the asset-hub and indeed we get blocks a bit earlier than our parachain_sub stream

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I like for_chain better than connect_to_chain because I think it's already connected, so all we are doing I believe is "selecting" a chain from those that are available to work with. (maybe seelct_chain would also work for that reason; I'm easy either way)

LightClientRpc {
to_backend: self.to_backend.clone(),
chain_id,
}
}
}

/// The light-client RPC implementation that is used to connect with the chain.
#[derive(Clone)]
pub struct LightClientRpc {
/// Communicate with the backend task that multiplexes the responses
/// back to the frontend.
to_backend: mpsc::UnboundedSender<FromSubxt>,
/// The chain ID to target for requests.
chain_id: smoldot_light::ChainId,
}

impl LightClientRpc {
Expand All @@ -31,37 +57,97 @@ impl LightClientRpc {
///
/// ## Panics
///
/// Panics if being called outside of `tokio` runtime context.
/// The panic behaviour depends on the feature flag being used:
///
/// ### Native
///
/// Panics when called outside of a `tokio` runtime context.
///
/// ### Web
///
/// If smoldot panics, then the promise created will be leaked. For more details, see
/// https://docs.rs/wasm-bindgen-futures/latest/wasm_bindgen_futures/fn.future_to_promise.html.
pub fn new(
config: smoldot_light::AddChainConfig<'_, (), impl Iterator<Item = smoldot_light::ChainId>>,
config: smoldot_light::AddChainConfig<
'_,
(),
impl IntoIterator<Item = smoldot_light::ChainId>,
>,
) -> Result<LightClientRpc, LightClientRpcError> {
tracing::trace!(target: LOG_TARGET, "Create light client");

let mut client = smoldot_light::Client::new(build_platform());

let config = smoldot_light::AddChainConfig {
specification: config.specification,
json_rpc: config.json_rpc,
database_content: config.database_content,
potential_relay_chains: config.potential_relay_chains.into_iter(),
user_data: config.user_data,
};

let smoldot_light::AddChainSuccess {
chain_id,
json_rpc_responses,
} = client
.add_chain(config)
.map_err(|err| LightClientRpcError::AddChainError(err.to_string()))?;

let (to_backend, backend) = mpsc::unbounded_channel();

// `json_rpc_responses` can only be `None` if we had passed `json_rpc: Disabled`.
let rpc_responses = json_rpc_responses.expect("Light client RPC configured; qed");

let raw_client = Self::new_from_client(
client,
iter::once(AddedChain {
chain_id,
rpc_responses,
}),
);
Ok(raw_client.for_chain(chain_id))
}

/// Constructs a new [`RawLightClientRpc`] from the raw smoldot client.
///
/// Receives a list of RPC objects as a result of calling `smoldot_light::Client::add_chain`.
/// This [`RawLightClientRpc`] can target different chains using [`RawLightClientRpc::for_chain`] method.
///
/// ## Panics
///
/// The panic behaviour depends on the feature flag being used:
///
/// ### Native
///
/// Panics when called outside of a `tokio` runtime context.
///
/// ### Web
///
/// If smoldot panics, then the promise created will be leaked. For more details, see
/// https://docs.rs/wasm-bindgen-futures/latest/wasm_bindgen_futures/fn.future_to_promise.html.
pub fn new_from_client<TPlat>(
client: smoldot_light::Client<TPlat>,
chains: impl IntoIterator<Item = AddedChain>,
) -> RawLightClientRpc
where
TPlat: smoldot_light::platform::PlatformRef + Clone,
{
let (to_backend, backend) = mpsc::unbounded_channel();
let chains = chains.into_iter().collect();

let future = async move {
let mut task = BackgroundTask::new(client, chain_id);
task.start_task(backend, rpc_responses).await;
let mut task = BackgroundTask::new(client);
task.start_task(backend, chains).await;
};

#[cfg(feature = "native")]
tokio::spawn(future);
#[cfg(feature = "web")]
wasm_bindgen_futures::spawn_local(future);

Ok(LightClientRpc { to_backend })
RawLightClientRpc { to_backend }
}

/// Returns the chain ID of the current light-client.
pub fn chain_id(&self) -> smoldot_light::ChainId {
self.chain_id
}

/// Submits an RPC method request to the light-client.
Expand All @@ -79,6 +165,7 @@ impl LightClientRpc {
method,
params,
sender,
chain_id: self.chain_id,
})?;

Ok(receiver)
Expand Down Expand Up @@ -107,8 +194,17 @@ impl LightClientRpc {
params,
sub_id,
sender,
chain_id: self.chain_id,
})?;

Ok((sub_id_rx, receiver))
}
}

/// The added chain of the light-client.
pub struct AddedChain {
/// The id of the chain.
pub chain_id: smoldot_light::ChainId,
/// Producer of RPC responses for the chain.
pub rpc_responses: smoldot_light::JsonRpcResponses,
}
14 changes: 12 additions & 2 deletions lightclient/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,18 @@ mod platform;
#[allow(unused_imports)]
pub use getrandom as _;

pub use client::LightClientRpc;
pub use smoldot_light::{AddChainConfig, AddChainConfigJsonRpc, ChainId};
pub use client::{AddedChain, LightClientRpc, RawLightClientRpc};

/// Re-exports of the smoldot related objects.
pub mod smoldot {
pub use smoldot_light::{
platform::PlatformRef, AddChainConfig, AddChainConfigJsonRpc, ChainId, Client,
JsonRpcResponses,
};

#[cfg(feature = "native")]
pub use smoldot_light::platform::default::DefaultPlatform;
}

/// Light client error.
#[derive(Debug, thiserror::Error)]
Expand Down
5 changes: 5 additions & 0 deletions subxt/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,8 @@ tracing-subscriber = { workspace = true }
name = "unstable_light_client_tx_basic"
path = "examples/unstable_light_client_tx_basic.rs"
required-features = ["unstable-light-client", "jsonrpsee"]

[[example]]
name = "unstable_light_client_parachains"
path = "examples/unstable_light_client_parachains.rs"
required-features = ["unstable-light-client", "jsonrpsee", "native"]
101 changes: 101 additions & 0 deletions subxt/examples/unstable_light_client_parachains.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use futures::StreamExt;
use std::{iter, num::NonZeroU32};
use subxt::{
client::{LightClient, RawLightClient},
PolkadotConfig,
};

// Generate an interface that we can use from the node's metadata.
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata_small.scale")]
pub mod polkadot {}

const POLKADOT_SPEC: &str = include_str!("../../artifacts/demo_chain_specs/polkadot.json");
const ASSET_HUB_SPEC: &str =
include_str!("../../artifacts/demo_chain_specs/polkadot_asset_hub.json");

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// The smoldot logs are informative:
tracing_subscriber::fmt::init();

// Connecting to a parachain is a multi step process.

// Step 1. Construct a new smoldot client.
let mut client =
subxt_lightclient::smoldot::Client::new(subxt_lightclient::smoldot::DefaultPlatform::new(
"subxt-example-light-client".into(),
"version-0".into(),
));

// Step 2. Connect to the relay chain of the parachain. For this example, the Polkadot relay chain.
let polkadot_connection = client
.add_chain(subxt_lightclient::smoldot::AddChainConfig {
specification: POLKADOT_SPEC,
json_rpc: subxt_lightclient::smoldot::AddChainConfigJsonRpc::Enabled {
max_pending_requests: NonZeroU32::new(128).unwrap(),
max_subscriptions: 1024,
},
potential_relay_chains: iter::empty(),
database_content: "",
user_data: (),
})
.expect("Light client chain added with valid spec; qed");
let polkadot_json_rpc_responses = polkadot_connection
.json_rpc_responses
.expect("Light client configured with json rpc enabled; qed");
let polkadot_chain_id = polkadot_connection.chain_id;

// Step 3. Connect to the parachain. For this example, the Asset hub parachain.
let assethub_connection = client
.add_chain(subxt_lightclient::smoldot::AddChainConfig {
specification: ASSET_HUB_SPEC,
json_rpc: subxt_lightclient::smoldot::AddChainConfigJsonRpc::Enabled {
max_pending_requests: NonZeroU32::new(128).unwrap(),
max_subscriptions: 1024,
},
// The chain specification of the asset hub parachain mentions that the identifier
// of its relay chain is `polkadot`.
potential_relay_chains: [polkadot_chain_id].into_iter(),
database_content: "",
user_data: (),
})
.expect("Light client chain added with valid spec; qed");
let parachain_json_rpc_responses = assethub_connection
.json_rpc_responses
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to write a wrapper for this API?

It's quite annoying to do this unwrap because the RPC client should always be enabled in smoldot?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I was thinking of creating a bit of a higher level API as a followup to this.

Ideally we could provide:

  • The highest level API used for most people out there, but with some API limitations (ie multichain usage). And this is already merged in subxt. Btw we need to handle this under the hood, its just the way smoldot exposes that API at the moment depending if the RPC was enabled or not.
let api = LightClient::<PolkadotConfig>::builder()
        .bootnodes([
            "/ip4/127.0.0.1/tcp/30333/p2p/12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp",
        ])
        .build_from_url("ws://127.0.0.1:9944")
        .await?;
  • A low level API that provides the most flexibility and allows users to utilize 100% of smoldot features. This is included in this PR, although the tradeoff is that we have some extra method calls on our builder to insert the chain_id, rpc-responses objects.

  • I was also thinking of providing something in between were we tackle the multichain use-case together with parachain use-case. And sort of evolve the API in a natural way when smoldot adds additional features; or users may need something easier to use than the low level API.

/// Naming tbd, suggesting that's somewhere in the middle between super high level and super low level API.
let client: MediumLightClient  = MediumLightClient::builder()
    .add_chain(
         AddChianBuilder::with_identifier( "polkadot" )
             .with_spec( include_str!( "../assets/polkadot_chain_spec.json")
    )
    .add_chain(
         AddChainBuilder::with_identifier( "asset-hub")
            .relay_chain( "polkadot")
            .from_url("ws://127.0.0.1:9944")
     )
    .build().await;
    
let polkadot_api = client.for_chain( "polkadot");

Something like that, but I didn't put a proper thought into the API. We still need a way to identify the chains while adding them to the client. And another approach here, would be to have a fn add_chain(&mut self) -> SubxtChainDetails and abstract that bit away; as opposed to allowing users to provide strs / tokens.

Another thing worth exploring would be extending the LightClientBuilder, maybe we could have a LightClient::builder().from_token(PolkadotToken) etc

Still believe having the ability for users to turn a raw light client into something that subxt can use under the scene would cover the most complex use-cases :D

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, I'm generally for the idea too of having:

  • a simple high level API (like we do)
  • an API that allows one to manually configure a smoldot::Client or whatever however they need and connect that to subxt (like this PR)
  • maybe something inbetween which a smoldot::Client could be turned into as one way of building it, or it could be built via some nice higher level APIs that avoid the duplication of passing chain IDs around and such

But yeah it does need some thought and experimentation to find the ideal interface for this stuff I think; we shouldn't rush into it :)

.expect("Light client configured with json rpc enabled; qed");
let parachain_chain_id = assethub_connection.chain_id;

// Step 4. Turn the smoldot client into a raw client.
let raw_light_client = RawLightClient::builder()
.add_chain(polkadot_chain_id, polkadot_json_rpc_responses)
Copy link
Member

@niklasad1 niklasad1 Nov 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, so this a little complicated to first call "add_chain" and then after call "for_chain".

I don't follow why we can't hide that from this API and just provide "connect_to_chain(chain_id, rpc_stream)" to avoid having to functions to call.

Perhaps you need pass in the client as well

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rpc_stream needs to be provided while spawning the light-client at the moment.

It might be possible to introduce another subxt frontend -> subxt backend to propagate this to the smoldot client from the background task and add the rpc_stream to the streams polled in the backend. That seemed a bit more complicated than this, would love to hear your opinion on this 🙏

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's fine for now but perhaps worth writing up an issue to look into.

My point is initializing and using the API is a bit complicated but
looks to mainly because expose some of smoldots APIs.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep that's a good point, raised #1253 to handle it later, thanks!

.add_chain(parachain_chain_id, parachain_json_rpc_responses)
.build(client)
.await?;

// Step 5. Obtain a client to target the relay chain and the parachain.
let polkadot_api: LightClient<PolkadotConfig> =
lexnv marked this conversation as resolved.
Show resolved Hide resolved
raw_light_client.for_chain(polkadot_chain_id).await?;
let parachain_api: LightClient<PolkadotConfig> =
raw_light_client.for_chain(parachain_chain_id).await?;

// Step 6. Subscribe to the finalized blocks of the chains.
let polkadot_sub = polkadot_api
.blocks()
.subscribe_finalized()
.await?
.map(|block| ("Polkadot", block));
let parachain_sub = parachain_api
.blocks()
.subscribe_finalized()
.await?
.map(|block| ("AssetHub", block));
let mut stream_combinator = futures::stream::select(polkadot_sub, parachain_sub);

while let Some((chain, block)) = stream_combinator.next().await {
let block = block?;

println!(" Chain {:?} hash={:?}", chain, block.hash());
}

Ok(())
}
Loading
Loading