mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 13:31:10 +00:00
Add support for ws in subxt-cli (#579)
* cli: Support ws or wss connection via jsonrpsee Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * cli: Handle `http` connections via `ureq` Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * cli: Use Jsonrpsee HTTP client Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * cli: Remove `node.to_string()` calls Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
This commit is contained in:
+71
-42
@@ -26,6 +26,19 @@ use frame_metadata::{
|
||||
RuntimeMetadataV14,
|
||||
META_RESERVED,
|
||||
};
|
||||
use jsonrpsee::{
|
||||
async_client::ClientBuilder,
|
||||
client_transport::ws::{
|
||||
Uri,
|
||||
WsTransportClientBuilder,
|
||||
},
|
||||
core::{
|
||||
client::ClientT,
|
||||
Error,
|
||||
},
|
||||
http_client::HttpClientBuilder,
|
||||
rpc_params,
|
||||
};
|
||||
use scale::{
|
||||
Decode,
|
||||
Input,
|
||||
@@ -70,7 +83,7 @@ enum Command {
|
||||
parse(try_from_str),
|
||||
default_value = "http://localhost:9933"
|
||||
)]
|
||||
url: url::Url,
|
||||
url: Uri,
|
||||
/// The format of the metadata to display: `json`, `hex` or `bytes`.
|
||||
#[structopt(long, short, default_value = "bytes")]
|
||||
format: String,
|
||||
@@ -83,7 +96,7 @@ enum Command {
|
||||
Codegen {
|
||||
/// The url of the substrate node to query for metadata for codegen.
|
||||
#[structopt(name = "url", long, parse(try_from_str))]
|
||||
url: Option<url::Url>,
|
||||
url: Option<Uri>,
|
||||
/// The path to the encoded metadata file.
|
||||
#[structopt(short, long, parse(from_os_str))]
|
||||
file: Option<PathBuf>,
|
||||
@@ -95,7 +108,7 @@ enum Command {
|
||||
Compatibility {
|
||||
/// Urls of the substrate nodes to verify for metadata compatibility.
|
||||
#[structopt(name = "nodes", long, use_delimiter = true, parse(try_from_str))]
|
||||
nodes: Vec<url::Url>,
|
||||
nodes: Vec<Uri>,
|
||||
/// Check the compatibility of metadata for a particular pallet.
|
||||
///
|
||||
/// ### Note
|
||||
@@ -105,13 +118,14 @@ enum Command {
|
||||
},
|
||||
}
|
||||
|
||||
fn main() -> color_eyre::Result<()> {
|
||||
#[tokio::main]
|
||||
async fn main() -> color_eyre::Result<()> {
|
||||
color_eyre::install()?;
|
||||
let args = Opts::from_args();
|
||||
|
||||
match args.command {
|
||||
Command::Metadata { url, format } => {
|
||||
let (hex_data, bytes) = fetch_metadata(&url)?;
|
||||
let (hex_data, bytes) = fetch_metadata(&url).await?;
|
||||
|
||||
match format.as_str() {
|
||||
"json" => {
|
||||
@@ -148,22 +162,26 @@ fn main() -> color_eyre::Result<()> {
|
||||
}
|
||||
|
||||
let url = url.unwrap_or_else(|| {
|
||||
url::Url::parse("http://localhost:9933").expect("default url is valid")
|
||||
"http://localhost:9933"
|
||||
.parse::<Uri>()
|
||||
.expect("default url is valid")
|
||||
});
|
||||
let (_, bytes) = fetch_metadata(&url)?;
|
||||
let (_, bytes) = fetch_metadata(&url).await?;
|
||||
codegen(&mut &bytes[..], derives)?;
|
||||
Ok(())
|
||||
}
|
||||
Command::Compatibility { nodes, pallet } => {
|
||||
match pallet {
|
||||
Some(pallet) => handle_pallet_metadata(nodes.as_slice(), pallet.as_str()),
|
||||
None => handle_full_metadata(nodes.as_slice()),
|
||||
Some(pallet) => {
|
||||
handle_pallet_metadata(nodes.as_slice(), pallet.as_str()).await
|
||||
}
|
||||
None => handle_full_metadata(nodes.as_slice()).await,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_pallet_metadata(nodes: &[url::Url], name: &str) -> color_eyre::Result<()> {
|
||||
async fn handle_pallet_metadata(nodes: &[Uri], name: &str) -> color_eyre::Result<()> {
|
||||
#[derive(Serialize, Deserialize, Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct CompatibilityPallet {
|
||||
@@ -173,28 +191,22 @@ fn handle_pallet_metadata(nodes: &[url::Url], name: &str) -> color_eyre::Result<
|
||||
|
||||
let mut compatibility: CompatibilityPallet = Default::default();
|
||||
for node in nodes.iter() {
|
||||
let metadata = fetch_runtime_metadata(node)?;
|
||||
let metadata = fetch_runtime_metadata(node).await?;
|
||||
|
||||
match metadata.pallets.iter().find(|pallet| pallet.name == name) {
|
||||
Some(pallet_metadata) => {
|
||||
let hash = get_pallet_hash(&metadata.types, pallet_metadata);
|
||||
let hex_hash = hex::encode(hash);
|
||||
println!(
|
||||
"Node {:?} has pallet metadata hash {:?}",
|
||||
node.as_str(),
|
||||
hex_hash
|
||||
);
|
||||
println!("Node {:?} has pallet metadata hash {:?}", node, hex_hash);
|
||||
|
||||
compatibility
|
||||
.pallet_present
|
||||
.entry(hex_hash)
|
||||
.or_insert_with(Vec::new)
|
||||
.push(node.as_str().to_string());
|
||||
.push(node.to_string());
|
||||
}
|
||||
None => {
|
||||
compatibility
|
||||
.pallet_not_found
|
||||
.push(node.as_str().to_string());
|
||||
compatibility.pallet_not_found.push(node.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -208,18 +220,18 @@ fn handle_pallet_metadata(nodes: &[url::Url], name: &str) -> color_eyre::Result<
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_full_metadata(nodes: &[url::Url]) -> color_eyre::Result<()> {
|
||||
async fn handle_full_metadata(nodes: &[Uri]) -> color_eyre::Result<()> {
|
||||
let mut compatibility_map: HashMap<String, Vec<String>> = HashMap::new();
|
||||
for node in nodes.iter() {
|
||||
let metadata = fetch_runtime_metadata(node)?;
|
||||
let metadata = fetch_runtime_metadata(node).await?;
|
||||
let hash = get_metadata_hash(&metadata);
|
||||
let hex_hash = hex::encode(hash);
|
||||
println!("Node {:?} has metadata hash {:?}", node.as_str(), hex_hash,);
|
||||
println!("Node {:?} has metadata hash {:?}", node, hex_hash,);
|
||||
|
||||
compatibility_map
|
||||
.entry(hex_hash)
|
||||
.or_insert_with(Vec::new)
|
||||
.push(node.as_str().to_string());
|
||||
.push(node.to_string());
|
||||
}
|
||||
|
||||
println!(
|
||||
@@ -231,14 +243,14 @@ fn handle_full_metadata(nodes: &[url::Url]) -> color_eyre::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fetch_runtime_metadata(url: &url::Url) -> color_eyre::Result<RuntimeMetadataV14> {
|
||||
let (_, bytes) = fetch_metadata(url)?;
|
||||
async fn fetch_runtime_metadata(url: &Uri) -> color_eyre::Result<RuntimeMetadataV14> {
|
||||
let (_, bytes) = fetch_metadata(url).await?;
|
||||
|
||||
let metadata = <RuntimeMetadataPrefixed as Decode>::decode(&mut &bytes[..])?;
|
||||
if metadata.0 != META_RESERVED {
|
||||
return Err(eyre::eyre!(
|
||||
"Node {:?} has invalid metadata prefix: {:?} expected prefix: {:?}",
|
||||
url.as_str(),
|
||||
url,
|
||||
metadata.0,
|
||||
META_RESERVED
|
||||
))
|
||||
@@ -249,28 +261,45 @@ fn fetch_runtime_metadata(url: &url::Url) -> color_eyre::Result<RuntimeMetadataV
|
||||
_ => {
|
||||
Err(eyre::eyre!(
|
||||
"Node {:?} with unsupported metadata version: {:?}",
|
||||
url.as_str(),
|
||||
url,
|
||||
metadata.1
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fetch_metadata(url: &url::Url) -> color_eyre::Result<(String, Vec<u8>)> {
|
||||
let resp = ureq::post(url.as_str())
|
||||
.set("Content-Type", "application/json")
|
||||
.send_json(ureq::json!({
|
||||
"jsonrpc": "2.0",
|
||||
"method": "state_getMetadata",
|
||||
"id": 1
|
||||
}))
|
||||
.context("error fetching metadata from the substrate node")?;
|
||||
let json: serde_json::Value = resp.into_json()?;
|
||||
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?)
|
||||
}
|
||||
|
||||
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 hex_data = json["result"]
|
||||
.as_str()
|
||||
.map(ToString::to_string)
|
||||
.ok_or_else(|| eyre::eyre!("metadata result field should be a string"))?;
|
||||
let bytes = hex::decode(hex_data.trim_start_matches("0x"))?;
|
||||
|
||||
Ok((hex_data, bytes))
|
||||
|
||||
Reference in New Issue
Block a user