Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add option to output metadata to a file #61

Merged
merged 13 commits into from
Feb 7, 2023
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ data
.env
scripts/out
*.gz
metadata.*
21 changes: 1 addition & 20 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

50 changes: 42 additions & 8 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
mod opts;

use std::io::Write;

use clap::{crate_name, crate_version, Parser};
// use color_eyre::owo_colors::OwoColorize;
use env_logger::Env;
Expand All @@ -20,6 +22,8 @@ macro_rules! noquiet {
fn main() -> color_eyre::Result<()> {
env_logger::Builder::from_env(Env::default().default_filter_or("none")).init();
let opts: Opts = Opts::parse();
color_eyre::install()?;

noquiet!(opts, println!("Running {} v{}", crate_name!(), crate_version!()));

match opts.subcmd {
Expand Down Expand Up @@ -57,13 +61,43 @@ fn main() -> color_eyre::Result<()> {
info!("⏱️ Loading WASM from {:?}", &source);
let subwasm = Subwasm::new(&source);

if let Some(filter) = meta_opts.module {
subwasm.display_module(filter);
} else if opts.json {
subwasm.display_metadata_json()
} else {
subwasm.display_modules_list()
let mut fmt: OutputFormat = meta_opts.format.unwrap_or_else(|| "human".into()).into();
if opts.json {
eprintln!("--json is DEPRECATED, use --format=json instead");
fmt = OutputFormat::Json;
}

let mut output = meta_opts.output;
if let Some(out) = &output {
if out.is_empty() || out == "auto" {
match fmt {
OutputFormat::Human => output = Some("metadata.txt".into()),
OutputFormat::Json => output = Some("metadata.json".into()),
OutputFormat::Scale => output = Some("metadata.scale".into()),
OutputFormat::HexScale => output = Some("metadata.hex".into()),
OutputFormat::JsonScale => output = Some("metadata.jscale".into()),
}
}
}

let mut out: Box<dyn Write> = if let Some(output) = &output {
Box::new(std::fs::File::create(output)?)
} else {
Box::new(std::io::stdout())
};

match subwasm.write_metadata(fmt, meta_opts.module, &mut out) {
Ok(_) => Ok(()),
Err(e) => {
if let Some(e) = e.root_cause().downcast_ref::<std::io::Error>() {
if e.kind() == std::io::ErrorKind::BrokenPipe {
log::debug!("ignoring broken pipe error: {:?}", e);
return Ok(());
}
}
Err(e)
}
}?
}

SubCommand::Diff(diff_opts) => {
Expand All @@ -77,11 +111,11 @@ fn main() -> color_eyre::Result<()> {
}

SubCommand::Compress(copts) => {
compress(copts.input, copts.output).unwrap();
compress(copts.input, copts.output)?;
}

SubCommand::Decompress(dopts) => {
decompress(dopts.input, dopts.output).unwrap();
decompress(dopts.input, dopts.output)?;
}
};

Expand Down
9 changes: 9 additions & 0 deletions cli/src/opts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,15 @@ pub struct MetaOpts {
/// Currently, you must pass a block hash. Passing the block numbers is not supported.
#[clap(short, long)]
pub block: Option<String>, // TODO: can do better...

/// You may specifiy the output format. One of "human", "scale", "json", "json+scale", "hex+scale"
#[clap(long, short, default_value = "human")]
pub format: Option<String>,

/// You may specifiy the output filename where the metadata will be saved.
/// Alternatively, you may use `auto` and an appropriate name will be generated according to the `format` your chose.
#[clap(short, long)]
pub output: Option<String>,
}

/// Compare 2 runtimes
Expand Down
20 changes: 10 additions & 10 deletions cli/tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ mod cli_tests {
fn it_gets_a_runtime() {
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap();

let assert = cmd.args(&["get", "--output", "runtime.wasm", "wss://rpc.polkadot.io:443"]).assert();
let assert = cmd.args(["get", "--output", "runtime.wasm", "wss://rpc.polkadot.io:443"]).assert();
assert.success().code(0);
assert!(Path::new("runtime.wasm").exists());
}
Expand All @@ -41,7 +41,7 @@ mod cli_tests {
fn it_fails_on_bad_chain() {
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap();

let assert = cmd.args(&["get", "--chain", "foobar"]).assert();
let assert = cmd.args(["get", "--chain", "foobar"]).assert();
assert.failure().code(101);
}
}
Expand All @@ -53,11 +53,11 @@ mod cli_tests {
#[test]
fn it_shows_metadata() {
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap();
let assert = cmd.args(&["get", "wss://rpc.polkadot.io:443", "--output", "runtime.wasm"]).assert();
let assert = cmd.args(["get", "wss://rpc.polkadot.io:443", "--output", "runtime.wasm"]).assert();
assert.success().code(0);

let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap();
let assert = cmd.args(&["meta", "runtime.wasm"]).assert();
let assert = cmd.args(["meta", "runtime.wasm"]).assert();
assert.success().code(0);
}
}
Expand All @@ -69,27 +69,27 @@ mod cli_tests {
#[test]
fn it_does_basic_compress_decompress() {
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap();
let assert = cmd.args(&["get", "wss://rpc.polkadot.io:443", "--output", "compressed.wasm"]).assert();
let assert = cmd.args(["get", "wss://rpc.polkadot.io:443", "--output", "compressed.wasm"]).assert();
assert.success().code(0);

let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap();
cmd.args(&["decompress", "compressed.wasm", "decompressed.wasm"]).assert().success().code(0);
cmd.args(["decompress", "compressed.wasm", "decompressed.wasm"]).assert().success().code(0);

let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap();
cmd.args(&["compress", "decompressed.wasm", "new_compressed.wasm"]).assert().success().code(0);
cmd.args(["compress", "decompressed.wasm", "new_compressed.wasm"]).assert().success().code(0);
}

#[test]
fn it_does_decompress_on_already() {
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap();
let assert = cmd.args(&["get", "wss://rpc.polkadot.io:443", "--output", "compressed.wasm"]).assert();
let assert = cmd.args(["get", "wss://rpc.polkadot.io:443", "--output", "compressed.wasm"]).assert();
assert.success().code(0);

let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap();
cmd.args(&["decompress", "compressed.wasm", "decompressed.wasm"]).assert().success().code(0);
cmd.args(["decompress", "compressed.wasm", "decompressed.wasm"]).assert().success().code(0);

let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap();
cmd.args(&["decompress", "decompressed.wasm", "new_decompressed.wasm"]).assert().success().code(0);
cmd.args(["decompress", "decompressed.wasm", "new_decompressed.wasm"]).assert().success().code(0);
}
}
}
2 changes: 2 additions & 0 deletions doc/usage_meta.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,7 @@ Options:
-j, --json Output as json
-m, --module <MODULE> Without this flag, the metadata command display the list of all modules. Using this flag, you will only see the module of your choice and a few details about it
-b, --block <BLOCK> The optional block where to fetch the runtime. That allows fetching older runtimes but you will need to connect to archive nodes. Currently, you must pass a block hash. Passing the block numbers is not supported
-f, --format <FORMAT> You may specifiy the output format. One of "human", "scale", "json", "json+scale", "hex+scale" [default: human]
-o, --output <OUTPUT> You may specifiy the output filename where the metadata will be saved. Alternatively, you may use `auto` and an appropriate name will be generated according to the `format` you choose
-h, --help Print help information
-V, --version Print version information
2 changes: 1 addition & 1 deletion lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ repository = "https://github.com/chevdor/subwasm"
version = "0.18.0"

[dependencies]
calm_io = "0.1"
color-eyre = "0.6"
frame-metadata = { version = "15", package = "frame-metadata", features = [
"v12",
Expand All @@ -39,3 +38,4 @@ substrate-differ = { version = "0.18.0", path = "../libs/substrate-differ" }
wasm-loader = { version = "0.18.0", path = "../libs/wasm-loader" }
wasm-testbed = { version = "0.18.0", path = "../libs/wasm-testbed" }
sp-version = { tag = "monthly-2023-01", git = "https://github.com/paritytech/substrate" }
hex = "0.4"
26 changes: 14 additions & 12 deletions lib/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use color_eyre::eyre::eyre;
use std::path::Path;
use std::{fs::File, path::PathBuf};
use std::{io::prelude::*, str::FromStr};
Expand All @@ -17,6 +18,7 @@ mod subwasm;
mod types;
pub use chain_info::*;
use log::{debug, info};
pub use metadata_wrapper::OutputFormat;
pub use runtime_info::*;
pub use subwasm::*;
pub use types::*;
Expand Down Expand Up @@ -67,7 +69,7 @@ pub fn download_runtime(url: &str, block_ref: Option<BlockRef>, output: Option<P
let url = match url {
url if url.starts_with("ws") => NodeEndpoint::WebSocket(url.to_string()),
url if url.starts_with("http") => NodeEndpoint::Http(url.to_string()),
_ => panic!("The url should either start with http or ws"),
_ => return Err(eyre!("The url should either start with http or ws")),
};

let reference = OnchainBlock { endpoint: url, block_ref };
Expand Down Expand Up @@ -129,41 +131,41 @@ pub fn diff(src_a: Source, src_b: Source) {

/// Compress a given runtime into a new file. You cannot compress
/// a runtime that is already compressed.
pub fn compress(input: PathBuf, output: PathBuf) -> Result<(), String> {
let wasm = WasmLoader::load_from_source(&Source::File(input)).unwrap();
pub fn compress(input: PathBuf, output: PathBuf) -> color_eyre::Result<()> {
let wasm = WasmLoader::load_from_source(&Source::File(input))?;

if wasm.compression().compressed() {
return Err("The input is already compressed".into());
return Err(eyre!("The input is already compressed"));
}

let bytes_compressed = Compression::compress(wasm.original_bytes()).unwrap();
let bytes_compressed = Compression::compress(wasm.original_bytes()).map_err(|e| eyre!(e))?;

debug!("original = {:?}", wasm.original_bytes().len());
debug!("compressed = {:?}", bytes_compressed.len());
info!("Saving compressed runtime to {:?}", output);

let mut buffer = File::create(output).unwrap();
buffer.write_all(&bytes_compressed.to_vec()).unwrap();
let mut buffer = File::create(output)?;
buffer.write_all(&bytes_compressed.to_vec())?;

Ok(())
}

/// Decompress a given runtime file. It is fine decompressing an already
/// decompressed runtime, you will just get the same.
pub fn decompress(input: PathBuf, output: PathBuf) -> Result<(), String> {
let wasm = WasmLoader::load_from_source(&Source::File(input)).unwrap();
pub fn decompress(input: PathBuf, output: PathBuf) -> color_eyre::Result<()> {
let wasm = WasmLoader::load_from_source(&Source::File(input))?;

let bytes_decompressed = match wasm.compression().compressed() {
false => wasm.original_bytes().clone(),
true => Compression::decompress(wasm.original_bytes()).unwrap(),
true => Compression::decompress(wasm.original_bytes()).map_err(|e| eyre!(e))?,
};

debug!("original = {:?}", wasm.original_bytes().len());
debug!("decompressed = {:?}", bytes_decompressed.len());

info!("Saving decompressed runtime to {:?}", output);
let mut buffer = File::create(output).unwrap();
buffer.write_all(&bytes_decompressed.to_vec()).unwrap();
let mut buffer = File::create(output)?;
buffer.write_all(&bytes_decompressed.to_vec())?;

Ok(())
}
Loading