-
Notifications
You must be signed in to change notification settings - Fork 254
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
cli: Command to fetch chainSpec and optimise its size (#1278)
* cli: Add chainSpec command Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * cli/chainSpec: Move to dedicated module Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * cli: Compute the state root hash Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * cli: Remove code substitutes Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * artifacts: Update polkadot.json Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * scripts: Generate the chain spec Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * cli: Remove testing artifacts Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * cli: Fix clippy Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * cli: Apply rustfmt Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * cli: Introduce feature flag for smoldot dependency Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * cli: Rename chain-spec to chain-spec-pruning Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * scripts: Update chain-spec command Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> --------- Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
- Loading branch information
1 parent
44c42c3
commit 0724735
Showing
8 changed files
with
270 additions
and
46 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
// 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 jsonrpsee::{ | ||
async_client::ClientBuilder, | ||
client_transport::ws::WsTransportClientBuilder, | ||
core::{client::ClientT, Error}, | ||
http_client::HttpClientBuilder, | ||
}; | ||
use std::time::Duration; | ||
use subxt_codegen::fetch_metadata::Url; | ||
|
||
/// Returns the node's chainSpec from the provided URL. | ||
pub async fn fetch_chain_spec(url: Url) -> Result<serde_json::Value, FetchSpecError> { | ||
async fn fetch_ws(url: Url) -> Result<serde_json::Value, Error> { | ||
let (sender, receiver) = WsTransportClientBuilder::default() | ||
.build(url) | ||
.await | ||
.map_err(|e| Error::Transport(e.into()))?; | ||
|
||
let client = ClientBuilder::default() | ||
.request_timeout(Duration::from_secs(180)) | ||
.max_buffer_capacity_per_subscription(4096) | ||
.build_with_tokio(sender, receiver); | ||
|
||
inner_fetch(client).await | ||
} | ||
|
||
async fn fetch_http(url: Url) -> Result<serde_json::Value, Error> { | ||
let client = HttpClientBuilder::default() | ||
.request_timeout(Duration::from_secs(180)) | ||
.build(url)?; | ||
|
||
inner_fetch(client).await | ||
} | ||
|
||
async fn inner_fetch(client: impl ClientT) -> Result<serde_json::Value, Error> { | ||
client | ||
.request("sync_state_genSyncSpec", jsonrpsee::rpc_params![true]) | ||
.await | ||
} | ||
|
||
let spec = match url.scheme() { | ||
"http" | "https" => fetch_http(url).await.map_err(FetchSpecError::RequestError), | ||
"ws" | "wss" => fetch_ws(url).await.map_err(FetchSpecError::RequestError), | ||
invalid_scheme => Err(FetchSpecError::InvalidScheme(invalid_scheme.to_owned())), | ||
}?; | ||
|
||
Ok(spec) | ||
} | ||
|
||
/// Error attempting to fetch chainSpec. | ||
#[derive(Debug, thiserror::Error)] | ||
#[non_exhaustive] | ||
pub enum FetchSpecError { | ||
/// JSON-RPC error fetching metadata. | ||
#[error("Request error: {0}")] | ||
RequestError(#[from] jsonrpsee::core::Error), | ||
/// URL scheme is not http, https, ws or wss. | ||
#[error("'{0}' not supported, supported URI schemes are http, https, ws or wss.")] | ||
InvalidScheme(String), | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 clap::Parser as ClapParser; | ||
#[cfg(feature = "chain-spec-pruning")] | ||
use serde_json::Value; | ||
use std::{io::Write, path::PathBuf}; | ||
use subxt_codegen::fetch_metadata::Url; | ||
|
||
mod fetch; | ||
|
||
/// Download chainSpec from a substrate node. | ||
#[derive(Debug, ClapParser)] | ||
pub struct Opts { | ||
/// The url of the substrate node to query for metadata for codegen. | ||
#[clap(long)] | ||
url: Url, | ||
/// Write the output of the command to the provided file path. | ||
#[clap(long, short, value_parser)] | ||
output_file: Option<PathBuf>, | ||
/// Replaced the genesis raw entry with a stateRootHash to optimize | ||
/// the spec size and avoid the need to calculate the genesis storage. | ||
/// | ||
/// This option is enabled with the `chain-spec-pruning` feature. | ||
/// | ||
/// Defaults to `false`. | ||
#[cfg(feature = "chain-spec-pruning")] | ||
#[clap(long)] | ||
state_root_hash: bool, | ||
/// Remove the `codeSubstitutes` entry from the chain spec. | ||
/// This is useful when wanting to store a smaller chain spec. | ||
/// At this moment, the light client does not utilize this object. | ||
/// | ||
/// Defaults to `false`. | ||
#[clap(long)] | ||
remove_substitutes: bool, | ||
} | ||
|
||
/// Error attempting to fetch chainSpec. | ||
#[derive(Debug, thiserror::Error)] | ||
#[non_exhaustive] | ||
pub enum ChainSpecError { | ||
/// Failed to fetch the chain spec. | ||
#[error("Failed to fetch the chain spec: {0}")] | ||
FetchError(#[from] fetch::FetchSpecError), | ||
|
||
#[cfg(feature = "chain-spec-pruning")] | ||
/// The provided chain spec is invalid. | ||
#[error("Error while parsing the chain spec: {0})")] | ||
ParseError(String), | ||
|
||
#[cfg(feature = "chain-spec-pruning")] | ||
/// Cannot compute the state root hash. | ||
#[error("Error computing state root hash: {0})")] | ||
ComputeError(String), | ||
|
||
/// Other error. | ||
#[error("Other: {0})")] | ||
Other(String), | ||
} | ||
|
||
#[cfg(feature = "chain-spec-pruning")] | ||
fn compute_state_root_hash(spec: &Value) -> Result<[u8; 32], ChainSpecError> { | ||
let chain_spec = smoldot::chain_spec::ChainSpec::from_json_bytes(spec.to_string().as_bytes()) | ||
.map_err(|err| ChainSpecError::ParseError(err.to_string()))?; | ||
|
||
let genesis_chain_information = chain_spec.to_chain_information().map(|(ci, _)| ci); | ||
|
||
let state_root = match genesis_chain_information { | ||
Ok(genesis_chain_information) => { | ||
let header = genesis_chain_information.as_ref().finalized_block_header; | ||
*header.state_root | ||
} | ||
// From the smoldot code this error is encountered when the genesis already contains the | ||
// state root hash entry instead of the raw entry. | ||
Err(smoldot::chain_spec::FromGenesisStorageError::UnknownStorageItems) => *chain_spec | ||
.genesis_storage() | ||
.into_trie_root_hash() | ||
.ok_or_else(|| { | ||
ChainSpecError::ParseError( | ||
"The chain spec does not contain the proper shape for the genesis.raw entry" | ||
.to_string(), | ||
) | ||
})?, | ||
Err(err) => return Err(ChainSpecError::ComputeError(err.to_string())), | ||
}; | ||
|
||
Ok(state_root) | ||
} | ||
|
||
pub async fn run(opts: Opts, output: &mut impl Write) -> color_eyre::Result<()> { | ||
let url = opts.url; | ||
|
||
let mut spec = fetch::fetch_chain_spec(url).await?; | ||
|
||
let mut output: Box<dyn Write> = match opts.output_file { | ||
Some(path) => Box::new(std::fs::File::create(path)?), | ||
None => Box::new(output), | ||
}; | ||
|
||
#[cfg(feature = "chain-spec-pruning")] | ||
if opts.state_root_hash { | ||
let state_root_hash = compute_state_root_hash(&spec)?; | ||
let state_root_hash = format!("0x{}", hex::encode(state_root_hash)); | ||
|
||
if let Some(genesis) = spec.get_mut("genesis") { | ||
let object = genesis.as_object_mut().ok_or_else(|| { | ||
ChainSpecError::Other("The genesis entry must be an object".to_string()) | ||
})?; | ||
|
||
object.remove("raw").ok_or_else(|| { | ||
ChainSpecError::Other("The genesis entry must contain a raw entry".to_string()) | ||
})?; | ||
|
||
object.insert("stateRootHash".to_string(), Value::String(state_root_hash)); | ||
} | ||
} | ||
|
||
if opts.remove_substitutes { | ||
let object = spec | ||
.as_object_mut() | ||
.ok_or_else(|| ChainSpecError::Other("The chain spec must be an object".to_string()))?; | ||
|
||
object.remove("codeSubstitutes"); | ||
} | ||
|
||
let json = serde_json::to_string_pretty(&spec)?; | ||
write!(output, "{json}")?; | ||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters