Add runtime_metadata_url to pull metadata directly from a node (#689)

* Allow metadata to be pulled directly from a node with runtime_metadata_url

* Update docs

* https too, and abstract out block_on fn

* tweak a comment in the example
This commit is contained in:
James Wilson
2022-10-19 11:07:12 +01:00
committed by GitHub
parent d7546db843
commit d03e599684
14 changed files with 286 additions and 79 deletions
+11 -13
View File
@@ -4,12 +4,7 @@
use clap::Parser as ClapParser;
use color_eyre::eyre;
use frame_metadata::RuntimeMetadataPrefixed;
use jsonrpsee::client_transport::ws::Uri;
use scale::{
Decode,
Input,
};
use std::{
fs,
io::Read,
@@ -48,7 +43,7 @@ pub async fn run(opts: Opts) -> color_eyre::Result<()> {
let mut file = fs::File::open(file)?;
let mut bytes = Vec::new();
file.read_to_end(&mut bytes)?;
codegen(&mut &bytes[..], opts.derives, opts.crate_path)?;
codegen(&bytes, opts.derives, opts.crate_path)?;
return Ok(())
}
@@ -57,18 +52,16 @@ pub async fn run(opts: Opts) -> color_eyre::Result<()> {
.parse::<Uri>()
.expect("default url is valid")
});
let (_, bytes) = super::metadata::fetch_metadata(&url).await?;
codegen(&mut &bytes[..], opts.derives, opts.crate_path)?;
let bytes = subxt_codegen::utils::fetch_metadata_bytes(&url).await?;
codegen(&bytes, opts.derives, opts.crate_path)?;
Ok(())
}
fn codegen<I: Input>(
encoded: &mut I,
fn codegen(
metadata_bytes: &[u8],
raw_derives: Vec<String>,
crate_path: Option<String>,
) -> color_eyre::Result<()> {
let metadata = <RuntimeMetadataPrefixed as Decode>::decode(encoded)?;
let generator = subxt_codegen::RuntimeGenerator::new(metadata);
let item_mod = syn::parse_quote!(
pub mod api {}
);
@@ -82,7 +75,12 @@ fn codegen<I: Input>(
let mut derives = DerivesRegistry::new(&crate_path);
derives.extend_for_all(p.into_iter());
let runtime_api = generator.generate_runtime(item_mod, derives, crate_path);
let runtime_api = subxt_codegen::generate_runtime_api_from_bytes(
item_mod,
metadata_bytes,
derives,
crate_path,
);
println!("{}", runtime_api);
Ok(())
}
+1 -1
View File
@@ -111,7 +111,7 @@ async fn handle_full_metadata(nodes: &[Uri]) -> color_eyre::Result<()> {
}
async fn fetch_runtime_metadata(url: &Uri) -> color_eyre::Result<RuntimeMetadataV14> {
let (_, bytes) = super::metadata::fetch_metadata(url).await?;
let bytes = subxt_codegen::utils::fetch_metadata_bytes(url).await?;
let metadata = <RuntimeMetadataPrefixed as Decode>::decode(&mut &bytes[..])?;
if metadata.0 != META_RESERVED {
+8 -52
View File
@@ -5,24 +5,13 @@
use clap::Parser as ClapParser;
use color_eyre::eyre;
use frame_metadata::RuntimeMetadataPrefixed;
use jsonrpsee::{
async_client::ClientBuilder,
client_transport::ws::{
Uri,
WsTransportClientBuilder,
},
core::{
client::ClientT,
Error,
},
http_client::HttpClientBuilder,
rpc_params,
};
use jsonrpsee::client_transport::ws::Uri;
use scale::Decode;
use std::io::{
self,
Write,
};
use subxt_codegen::utils::fetch_metadata_hex;
/// Download metadata from a substrate node, for use with `subxt` codegen.
#[derive(Debug, ClapParser)]
@@ -41,10 +30,11 @@ pub struct Opts {
}
pub async fn run(opts: Opts) -> color_eyre::Result<()> {
let (hex_data, bytes) = fetch_metadata(&opts.url).await?;
let hex_data = fetch_metadata_hex(&opts.url).await?;
match opts.format.as_str() {
"json" => {
let bytes = hex::decode(hex_data.trim_start_matches("0x"))?;
let metadata = <RuntimeMetadataPrefixed as Decode>::decode(&mut &bytes[..])?;
let json = serde_json::to_string_pretty(&metadata)?;
println!("{}", json);
@@ -54,7 +44,10 @@ pub async fn run(opts: Opts) -> color_eyre::Result<()> {
println!("{}", hex_data);
Ok(())
}
"bytes" => Ok(io::stdout().write_all(&bytes)?),
"bytes" => {
let bytes = hex::decode(hex_data.trim_start_matches("0x"))?;
Ok(io::stdout().write_all(&bytes)?)
}
_ => {
Err(eyre::eyre!(
"Unsupported format `{}`, expected `json`, `hex` or `bytes`",
@@ -63,40 +56,3 @@ pub async fn run(opts: Opts) -> color_eyre::Result<()> {
}
}
}
pub async fn fetch_metadata(url: &Uri) -> color_eyre::Result<(String, Vec<u8>)> {
let hex_data = match url.scheme_str() {
Some("http") => fetch_metadata_http(url).await,
Some("ws") | Some("wss") => fetch_metadata_ws(url).await,
invalid_scheme => {
let scheme = invalid_scheme.unwrap_or("no scheme");
Err(eyre::eyre!(format!(
"`{}` not supported, expects 'http', 'ws', or 'wss'",
scheme
)))
}
}?;
let bytes = hex::decode(hex_data.trim_start_matches("0x"))?;
Ok((hex_data, bytes))
}
async fn fetch_metadata_ws(url: &Uri) -> color_eyre::Result<String> {
let (sender, receiver) = WsTransportClientBuilder::default()
.build(url.to_string().parse::<Uri>().unwrap())
.await
.map_err(|e| Error::Transport(e.into()))?;
let client = ClientBuilder::default()
.max_notifs_per_subscription(4096)
.build_with_tokio(sender, receiver);
Ok(client.request("state_getMetadata", rpc_params![]).await?)
}
async fn fetch_metadata_http(url: &Uri) -> color_eyre::Result<String> {
let client = HttpClientBuilder::default().build(url.to_string())?;
Ok(client.request::<String>("state_getMetadata", None).await?)
}