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:
James Wilson
2025-09-02 17:19:39 +01:00
committed by GitHub
parent 07ed8bad33
commit a79c531a82
4 changed files with 62 additions and 33 deletions
+25 -17
View File
@@ -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)
}