mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 08:11:04 +00:00
Add --at-block option to CLI tool to download metadata at a specific block (#2079)
* Add --at-block option to CLI tool to download metadata at a specific block; useful for debugging * clippy
This commit is contained in:
@@ -137,7 +137,7 @@ async fn fetch_runtime_metadata(
|
||||
url: Url,
|
||||
version: MetadataVersion,
|
||||
) -> color_eyre::Result<Metadata> {
|
||||
let bytes = subxt_utils_fetchmetadata::from_url(url, version).await?;
|
||||
let bytes = subxt_utils_fetchmetadata::from_url(url, version, None).await?;
|
||||
let metadata = Metadata::decode(&mut &bytes[..])?;
|
||||
Ok(metadata)
|
||||
}
|
||||
|
||||
+35
-14
@@ -34,6 +34,11 @@ pub struct FileOrUrl {
|
||||
/// Defaults to asking for the latest stable metadata version.
|
||||
#[clap(long)]
|
||||
pub version: Option<MetadataVersion>,
|
||||
/// Block hash (hex encoded) to attempt to fetch the metadata from.
|
||||
/// If not provided, we default to the latest finalized block.
|
||||
/// Non-archive nodes will be unable to provide metadata from old blocks.
|
||||
#[clap(long)]
|
||||
pub at_block: Option<String>,
|
||||
}
|
||||
|
||||
impl FromStr for FileOrUrl {
|
||||
@@ -45,6 +50,7 @@ impl FromStr for FileOrUrl {
|
||||
url: None,
|
||||
file: Some(path),
|
||||
version: None,
|
||||
at_block: None,
|
||||
})
|
||||
} else {
|
||||
Url::parse(s)
|
||||
@@ -53,6 +59,7 @@ impl FromStr for FileOrUrl {
|
||||
url: Some(uri),
|
||||
file: None,
|
||||
version: None,
|
||||
at_block: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -87,19 +94,23 @@ impl FromStr for PathOrStdIn {
|
||||
impl FileOrUrl {
|
||||
/// Fetch the metadata bytes.
|
||||
pub async fn fetch(&self) -> color_eyre::Result<Vec<u8>> {
|
||||
match (&self.file, &self.url, self.version) {
|
||||
match (&self.file, &self.url, self.version, &self.at_block) {
|
||||
// Can't provide both --file and --url
|
||||
(Some(_), Some(_), _) => {
|
||||
(Some(_), Some(_), _, _) => {
|
||||
bail!("specify one of `--url` or `--file` but not both")
|
||||
}
|
||||
// --at-block must be provided with --url
|
||||
(Some(_path_or_stdin), _, _, Some(_at_block)) => {
|
||||
bail!("`--at-block` can only be used with `--url`")
|
||||
}
|
||||
// Load from --file path
|
||||
(Some(PathOrStdIn::Path(path)), None, None) => {
|
||||
(Some(PathOrStdIn::Path(path)), None, None, None) => {
|
||||
let mut file = fs::File::open(path)?;
|
||||
let mut bytes = Vec::new();
|
||||
file.read_to_end(&mut bytes)?;
|
||||
Ok(bytes)
|
||||
}
|
||||
(Some(PathOrStdIn::StdIn), None, None) => {
|
||||
(Some(PathOrStdIn::StdIn), None, None, None) => {
|
||||
let reader = std::io::BufReader::new(std::io::stdin());
|
||||
let res = reader.bytes().collect::<Result<Vec<u8>, _>>();
|
||||
|
||||
@@ -109,7 +120,7 @@ impl FileOrUrl {
|
||||
}
|
||||
}
|
||||
// Cannot load the metadata from the file and specify a version to fetch.
|
||||
(Some(_), None, Some(_)) => {
|
||||
(Some(_), None, Some(_), None) => {
|
||||
// Note: we could provide the ability to convert between metadata versions
|
||||
// but that would be involved because we'd need to convert
|
||||
// from each metadata to the latest one and from the
|
||||
@@ -117,13 +128,19 @@ impl FileOrUrl {
|
||||
bail!("`--file` is incompatible with `--version`")
|
||||
}
|
||||
// Fetch from --url
|
||||
(None, Some(uri), version) => {
|
||||
Ok(fetch_metadata::from_url(uri.clone(), version.unwrap_or_default()).await?)
|
||||
}
|
||||
(None, Some(uri), version, at_block) => Ok(fetch_metadata::from_url(
|
||||
uri.clone(),
|
||||
version.unwrap_or_default(),
|
||||
at_block.as_deref(),
|
||||
)
|
||||
.await?),
|
||||
// Default if neither is provided; fetch from local url
|
||||
(None, None, version) => {
|
||||
(None, None, version, at_block) => {
|
||||
let url = Url::parse("ws://localhost:9944").expect("Valid URL; qed");
|
||||
Ok(fetch_metadata::from_url(url, version.unwrap_or_default()).await?)
|
||||
Ok(
|
||||
fetch_metadata::from_url(url, version.unwrap_or_default(), at_block.as_deref())
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -336,7 +353,8 @@ mod tests {
|
||||
Ok(FileOrUrl {
|
||||
url: None,
|
||||
file: Some(PathOrStdIn::StdIn),
|
||||
version: None
|
||||
version: None,
|
||||
at_block: None,
|
||||
})
|
||||
),);
|
||||
|
||||
@@ -345,7 +363,8 @@ mod tests {
|
||||
Ok(FileOrUrl {
|
||||
url: None,
|
||||
file: Some(PathOrStdIn::StdIn),
|
||||
version: None
|
||||
version: None,
|
||||
at_block: None,
|
||||
})
|
||||
),);
|
||||
|
||||
@@ -354,7 +373,8 @@ mod tests {
|
||||
Ok(FileOrUrl {
|
||||
url: None,
|
||||
file: Some(PathOrStdIn::Path(_)),
|
||||
version: None
|
||||
version: None,
|
||||
at_block: None,
|
||||
})
|
||||
),);
|
||||
|
||||
@@ -365,7 +385,8 @@ mod tests {
|
||||
Ok(FileOrUrl {
|
||||
url: Some(_),
|
||||
file: None,
|
||||
version: None
|
||||
version: None,
|
||||
at_block: None,
|
||||
})
|
||||
));
|
||||
}
|
||||
|
||||
+1
-1
@@ -263,7 +263,7 @@ fn fetch_metadata(args: &RuntimeMetadataArgs) -> Result<subxt_codegen::Metadata,
|
||||
false => MetadataVersion::Latest,
|
||||
};
|
||||
|
||||
from_url_blocking(url, version)
|
||||
from_url_blocking(url, version, None)
|
||||
.map_err(|e| CodegenError::Other(e.to_string()))
|
||||
.and_then(|b| subxt_codegen::Metadata::decode(&mut &*b).map_err(Into::into))
|
||||
.map_err(|e| e.into_compile_error())?
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
//! Fetch metadata from a URL.
|
||||
|
||||
use crate::Error;
|
||||
use codec::{Decode, Encode};
|
||||
use codec::{Decode, Encode};
|
||||
use jsonrpsee::{
|
||||
core::client::ClientT, http_client::HttpClientBuilder, rpc_params, ws_client::WsClientBuilder,
|
||||
};
|
||||
@@ -44,10 +44,10 @@ impl std::str::FromStr for MetadataVersion {
|
||||
}
|
||||
|
||||
/// Returns the metadata bytes from the provided URL.
|
||||
pub async fn from_url(url: Url, version: MetadataVersion) -> Result<Vec<u8>, Error> {
|
||||
pub async fn from_url(url: Url, version: MetadataVersion, at_block_hash: Option<&str>) -> Result<Vec<u8>, Error> {
|
||||
let bytes = match url.scheme() {
|
||||
"http" | "https" => fetch_metadata_http(url, version).await,
|
||||
"ws" | "wss" => fetch_metadata_ws(url, version).await,
|
||||
"http" | "https" => fetch_metadata_http(url, version, at_block_hash).await,
|
||||
"ws" | "wss" => fetch_metadata_ws(url, version, at_block_hash).await,
|
||||
invalid_scheme => Err(Error::InvalidScheme(invalid_scheme.to_owned())),
|
||||
}?;
|
||||
|
||||
@@ -55,8 +55,8 @@ pub async fn from_url(url: Url, version: MetadataVersion) -> Result<Vec<u8>, Err
|
||||
}
|
||||
|
||||
/// Returns the metadata bytes from the provided URL, blocking the current thread.
|
||||
pub fn from_url_blocking(url: Url, version: MetadataVersion) -> Result<Vec<u8>, Error> {
|
||||
tokio_block_on(from_url(url, version))
|
||||
pub fn from_url_blocking(url: Url, version: MetadataVersion, at_block_hash: Option<&str>) -> Result<Vec<u8>, Error> {
|
||||
tokio_block_on(from_url(url, version, at_block_hash))
|
||||
}
|
||||
|
||||
// Block on some tokio runtime for sync contexts
|
||||
@@ -68,34 +68,40 @@ fn tokio_block_on<T, Fut: std::future::Future<Output = T>>(fut: Fut) -> T {
|
||||
.block_on(fut)
|
||||
}
|
||||
|
||||
async fn fetch_metadata_ws(url: Url, version: MetadataVersion) -> Result<Vec<u8>, Error> {
|
||||
async fn fetch_metadata_ws(url: Url, version: MetadataVersion, at_block_hash: Option<&str>) -> Result<Vec<u8>, Error> {
|
||||
let client = WsClientBuilder::default()
|
||||
.request_timeout(std::time::Duration::from_secs(180))
|
||||
.max_buffer_capacity_per_subscription(4096)
|
||||
.build(url)
|
||||
.await?;
|
||||
|
||||
fetch_metadata(client, version).await
|
||||
fetch_metadata(client, version, at_block_hash).await
|
||||
}
|
||||
|
||||
async fn fetch_metadata_http(url: Url, version: MetadataVersion) -> Result<Vec<u8>, Error> {
|
||||
async fn fetch_metadata_http(url: Url, version: MetadataVersion, at_block_hash: Option<&str>) -> Result<Vec<u8>, Error> {
|
||||
let client = HttpClientBuilder::default()
|
||||
.request_timeout(std::time::Duration::from_secs(180))
|
||||
.build(url)?;
|
||||
|
||||
fetch_metadata(client, version).await
|
||||
fetch_metadata(client, version, at_block_hash).await
|
||||
}
|
||||
|
||||
/// The innermost call to fetch metadata:
|
||||
async fn fetch_metadata(client: impl ClientT, version: MetadataVersion) -> Result<Vec<u8>, Error> {
|
||||
async fn fetch_metadata(client: impl ClientT, version: MetadataVersion, at_block_hash: Option<&str>) -> Result<Vec<u8>, Error> {
|
||||
const UNSTABLE_METADATA_VERSION: u32 = u32::MAX;
|
||||
|
||||
// Ensure always 0x prefix.
|
||||
let at_block_hash = at_block_hash
|
||||
.map(|hash| format!("0x{}", hash.strip_prefix("0x").unwrap_or(hash)));
|
||||
let at_block_hash = at_block_hash.as_deref();
|
||||
|
||||
// Fetch available metadata versions. If error, revert to legacy metadata code.
|
||||
async fn fetch_available_versions(
|
||||
client: &impl ClientT,
|
||||
at_block_hash: Option<&str>,
|
||||
) -> Result<Vec<u32>, Error> {
|
||||
let res: String = client
|
||||
.request("state_call", rpc_params!["Metadata_metadata_versions", "0x"])
|
||||
.request("state_call", rpc_params!["Metadata_metadata_versions", "0x", at_block_hash])
|
||||
.await?;
|
||||
let raw_bytes = hex::decode(res.trim_start_matches("0x"))?;
|
||||
Decode::decode(&mut &raw_bytes[..]).map_err(Into::into)
|
||||
@@ -106,6 +112,7 @@ async fn fetch_metadata(client: impl ClientT, version: MetadataVersion) -> Resul
|
||||
client: &impl ClientT,
|
||||
version: MetadataVersion,
|
||||
supported_versions: Vec<u32>,
|
||||
at_block_hash: Option<&str>,
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
// Return the version the user wants if it's supported:
|
||||
let version = match version {
|
||||
@@ -141,7 +148,7 @@ async fn fetch_metadata(client: impl ClientT, version: MetadataVersion) -> Resul
|
||||
let metadata_string: String = client
|
||||
.request(
|
||||
"state_call",
|
||||
rpc_params!["Metadata_metadata_at_version", &version],
|
||||
rpc_params!["Metadata_metadata_at_version", &version, at_block_hash],
|
||||
)
|
||||
.await?;
|
||||
// Decode the metadata.
|
||||
@@ -159,10 +166,11 @@ async fn fetch_metadata(client: impl ClientT, version: MetadataVersion) -> Resul
|
||||
// Fetch metadata using the "old" state_call interface
|
||||
async fn fetch_inner_legacy(
|
||||
client: &impl ClientT,
|
||||
at_block_hash: Option<&str>,
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
// Fetch the metadata.
|
||||
let metadata_string: String = client
|
||||
.request("state_call", rpc_params!["Metadata_metadata", "0x"])
|
||||
.request("state_call", rpc_params!["Metadata_metadata", "0x", at_block_hash])
|
||||
.await?;
|
||||
|
||||
// Decode the metadata.
|
||||
@@ -171,16 +179,16 @@ async fn fetch_metadata(client: impl ClientT, version: MetadataVersion) -> Resul
|
||||
Ok(metadata.0)
|
||||
}
|
||||
|
||||
match fetch_available_versions(&client).await {
|
||||
match fetch_available_versions(&client, at_block_hash).await {
|
||||
Ok(supported_versions) => {
|
||||
fetch_inner(&client, version, supported_versions).await
|
||||
fetch_inner(&client, version, supported_versions, at_block_hash).await
|
||||
},
|
||||
Err(e) => {
|
||||
// The "new" interface failed. if the user is asking for V14 or the "latest"
|
||||
// metadata then try the legacy interface instead. Else, just return the
|
||||
// reason for failure.
|
||||
if matches!(version, MetadataVersion::Version(14) | MetadataVersion::Latest) {
|
||||
fetch_inner_legacy(&client).await
|
||||
fetch_inner_legacy(&client, at_block_hash).await
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user