mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-22 09:08:06 +00:00
light-client: Add experimental light-client support (#965)
* rpc/types: Decode `SubstrateTxStatus` for substrate and smoldot Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * lightclient: Add light client Error Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * lightclient: Add background task to manage RPC responses Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * lightclient: Implement the light client RPC in subxt Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * subxt: Expose light client under experimental feature-flag Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * artifacts: Add development chain spec for local nodes Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Update cargo lock Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * examples: Add light client example Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Update sp-* crates and smoldot to use git with branch / rev Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Apply cargo fmt Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Fix clippy Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Import hashmap entry Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * lightclient: Fetch spec only if jsonrpsee feature is enabled Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Update subxt/src/rpc/lightclient/background.rs Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com> * Fix typo Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * artifacts: Update dev chain spec Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * types: Handle storage replies from chainHead_storage Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * artifacts: Add polkadot spec Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * lightclient: Handle RPC error responses Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * examples: Tx basic with light client for local nodes Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * example: Light client coprehensive example for live chains Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * examples: Remove prior light client example Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * feature: Rename experimental to unstable Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * book: Add light client section Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * testing: Fix clippy Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * lightclient: Ignore validated events Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Adjust tests for light-clients and normal clients Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * testing: Keep lightclient variant Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Remove support for chainHead_storage for light client Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Update light client to point to crates.io Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Update sp-crates from crates.io Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Replace Atomic with u64 Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Add LightClientBuilder Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Adjust chainspec with provided bootnodes Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Add potential_relay_chains to light client builder Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Move the light-client to the background task Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Adjust tracing logs Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Update book and example Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Apply cargo fmt Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Remove dev_spec.json artifact Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Examples fix duplicate Cargo.toml Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Use tracing_subscriber crate Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Fix clippy for different features Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Add comment about bootNodes Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Add comment about tracing-sub dependency Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Run integration-tests with light-client Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Feature guard some incompatible tests Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * ci: Enable light-client tests under feature flag Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * ci: Fix git step name Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Adjust flags for testing Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Adjust warnings Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Rename feature flag jsonrpsee-ws to jsonrpsee Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Fix cargo check Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * ci: Run tests on just 2 threads Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Move light-client to subxt/src/client Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Adjust LightClientBuilder Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Use ws_url to construct light client for testing Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Refactor background Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Address feedback Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Remove polkadot.spec and trim sub_id Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Wait for substrate to produce block before connecting light client Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Adjust builder and tests Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Apply fmt Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * ci: Use release for light client testing Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Add single test for light-client Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Wait for more blocks Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Use polkadot endpoint for testing Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Adjust cargo check Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * examples: Remove light client chain connection example Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Adjust cargo.toml section for the old example Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Adjust background task to use usize for subscription Id Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Build bootnodes with serde_json::Value directly Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Make channel between subxt user and subxt background unbounded Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Update subxt/src/client/lightclient/builder.rs Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com> * Switch to smoldot 0.6.0 from 0.5.0 Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Move testing to `full_client` and `light_client` higher modules Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Remove subscriptionID type Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Remove subxt/integration-testing feature flag Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Adjust wait_for_blocks documentation Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Adjust utils import for testing Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Remove into_iter from builder construction Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> --------- Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com>
This commit is contained in:
@@ -6,6 +6,8 @@
|
||||
pub enum Error {
|
||||
Io(std::io::Error),
|
||||
CouldNotExtractPort,
|
||||
CouldNotExtractP2pAddress,
|
||||
CouldNotExtractP2pPort,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Error {
|
||||
@@ -16,6 +18,14 @@ impl std::fmt::Display for Error {
|
||||
f,
|
||||
"could not extract port from running substrate node's stdout"
|
||||
),
|
||||
Error::CouldNotExtractP2pAddress => write!(
|
||||
f,
|
||||
"could not extract p2p address from running substrate node's stdout"
|
||||
),
|
||||
Error::CouldNotExtractP2pPort => write!(
|
||||
f,
|
||||
"could not extract p2p port from running substrate node's stdout"
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ impl SubstrateNodeBuilder {
|
||||
pub fn spawn(self) -> Result<SubstrateNode, Error> {
|
||||
let mut cmd = Command::new(self.binary_path);
|
||||
|
||||
cmd.env("RUST_LOG", "info")
|
||||
cmd.env("RUST_LOG", "info,libp2p_tcp=debug")
|
||||
.stdout(process::Stdio::piped())
|
||||
.stderr(process::Stdio::piped())
|
||||
.arg("--dev")
|
||||
@@ -74,16 +74,26 @@ impl SubstrateNodeBuilder {
|
||||
|
||||
// Wait for RPC port to be logged (it's logged to stderr).
|
||||
let stderr = proc.stderr.take().unwrap();
|
||||
let ws_port =
|
||||
try_find_substrate_port_from_output(stderr).ok_or(Error::CouldNotExtractPort)?;
|
||||
let (ws_port, p2p_address, p2p_port) = try_find_substrate_port_from_output(stderr);
|
||||
|
||||
Ok(SubstrateNode { proc, ws_port })
|
||||
let ws_port = ws_port.ok_or(Error::CouldNotExtractPort)?;
|
||||
let p2p_address = p2p_address.ok_or(Error::CouldNotExtractP2pAddress)?;
|
||||
let p2p_port = p2p_port.ok_or(Error::CouldNotExtractP2pPort)?;
|
||||
|
||||
Ok(SubstrateNode {
|
||||
proc,
|
||||
ws_port,
|
||||
p2p_address,
|
||||
p2p_port,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SubstrateNode {
|
||||
proc: process::Child,
|
||||
ws_port: u16,
|
||||
p2p_address: String,
|
||||
p2p_port: u32,
|
||||
}
|
||||
|
||||
impl SubstrateNode {
|
||||
@@ -102,6 +112,16 @@ impl SubstrateNode {
|
||||
self.ws_port
|
||||
}
|
||||
|
||||
/// Return the libp2p address of the running node.
|
||||
pub fn p2p_address(&self) -> String {
|
||||
self.p2p_address.clone()
|
||||
}
|
||||
|
||||
/// Return the libp2p port of the running node.
|
||||
pub fn p2p_port(&self) -> u32 {
|
||||
self.p2p_port
|
||||
}
|
||||
|
||||
/// Kill the process.
|
||||
pub fn kill(&mut self) -> std::io::Result<()> {
|
||||
self.proc.kill()
|
||||
@@ -116,28 +136,63 @@ impl Drop for SubstrateNode {
|
||||
|
||||
// Consume a stderr reader from a spawned substrate command and
|
||||
// locate the port number that is logged out to it.
|
||||
fn try_find_substrate_port_from_output(r: impl Read + Send + 'static) -> Option<u16> {
|
||||
BufReader::new(r).lines().take(50).find_map(|line| {
|
||||
fn try_find_substrate_port_from_output(
|
||||
r: impl Read + Send + 'static,
|
||||
) -> (Option<u16>, Option<String>, Option<u32>) {
|
||||
let mut port = None;
|
||||
let mut p2p_address = None;
|
||||
let mut p2p_port = None;
|
||||
|
||||
for line in BufReader::new(r).lines().take(50) {
|
||||
let line = line.expect("failed to obtain next line from stdout for port discovery");
|
||||
|
||||
// does the line contain our port (we expect this specific output from substrate).
|
||||
let line_end = line
|
||||
// Parse the port lines
|
||||
let line_port = line
|
||||
// oldest message:
|
||||
.rsplit_once("Listening for new connections on 127.0.0.1:")
|
||||
// slightly newer message:
|
||||
.or_else(|| line.rsplit_once("Running JSON-RPC WS server: addr=127.0.0.1:"))
|
||||
// newest message (jsonrpsee merging http and ws servers):
|
||||
.or_else(|| line.rsplit_once("Running JSON-RPC server: addr=127.0.0.1:"))
|
||||
.map(|(_, port_str)| port_str)?;
|
||||
.map(|(_, port_str)| port_str);
|
||||
|
||||
// trim non-numeric chars from the end of the port part of the line.
|
||||
let port_str = line_end.trim_end_matches(|b: char| !b.is_ascii_digit());
|
||||
if let Some(line_port) = line_port {
|
||||
// trim non-numeric chars from the end of the port part of the line.
|
||||
let port_str = line_port.trim_end_matches(|b: char| !b.is_ascii_digit());
|
||||
|
||||
// expect to have a number here (the chars after '127.0.0.1:') and parse them into a u16.
|
||||
let port_num = port_str
|
||||
.parse()
|
||||
.unwrap_or_else(|_| panic!("valid port expected for log line, got '{port_str}'"));
|
||||
// expect to have a number here (the chars after '127.0.0.1:') and parse them into a u16.
|
||||
let port_num = port_str
|
||||
.parse()
|
||||
.unwrap_or_else(|_| panic!("valid port expected for log line, got '{port_str}'"));
|
||||
port = Some(port_num);
|
||||
}
|
||||
|
||||
Some(port_num)
|
||||
})
|
||||
// Parse the p2p address line
|
||||
let line_address = line
|
||||
.rsplit_once("Local node identity is: ")
|
||||
.map(|(_, address_str)| address_str);
|
||||
|
||||
if let Some(line_address) = line_address {
|
||||
let address = line_address.trim_end_matches(|b: char| b.is_ascii_whitespace());
|
||||
p2p_address = Some(address.into());
|
||||
}
|
||||
|
||||
// Parse the p2p port line (present in debug logs)
|
||||
let p2p_port_line = line
|
||||
.rsplit_once("libp2p_tcp: New listen address: /ip4/127.0.0.1/tcp/")
|
||||
.map(|(_, address_str)| address_str);
|
||||
|
||||
if let Some(line_port) = p2p_port_line {
|
||||
// trim non-numeric chars from the end of the port part of the line.
|
||||
let port_str = line_port.trim_end_matches(|b: char| !b.is_ascii_digit());
|
||||
|
||||
// expect to have a number here (the chars after '127.0.0.1:') and parse them into a u16.
|
||||
let port_num = port_str
|
||||
.parse()
|
||||
.unwrap_or_else(|_| panic!("valid port expected for log line, got '{port_str}'"));
|
||||
p2p_port = Some(port_num);
|
||||
}
|
||||
}
|
||||
|
||||
(port, p2p_address, p2p_port)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user