diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 10b7fdc..0fc3ee7 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -33,6 +33,10 @@ path = "stack_height/main.rs" name = "wasm-pack" path = "pack/main.rs" +[[bin]] +name = "wasm-check" +path = "check/main.rs" + [dependencies] parity-wasm = "0.31" pwasm-utils = { path = "..", version = "0.3" } diff --git a/cli/check/main.rs b/cli/check/main.rs new file mode 100644 index 0000000..fafffb8 --- /dev/null +++ b/cli/check/main.rs @@ -0,0 +1,102 @@ +extern crate parity_wasm; +extern crate pwasm_utils as utils; +extern crate pwasm_utils_cli as logger; +extern crate clap; + +use clap::{App, Arg}; +use parity_wasm::elements; + +fn fail(msg: &str) -> ! { + eprintln!("{}", msg); + std::process::exit(1) +} + +const ALLOWED_IMPORTS: &'static [&'static str] = &[ + "ret", + "storage_read", + "storage_write", + "balance", + "sender", + "origin", + "fetch_input", + "input_length", + "ccall", + "dcall", + "scall", + "create", + "balance", + "blockhash", + "blocknumber", + "coinbase", + "timestamp", + "difficulty", + "gaslimit", + "address", + "value", + "suicide", + "panic", + "elog", + "abort" +]; + +fn main() { + logger::init_log(); + + let matches = App::new("wasm-check") + .arg(Arg::with_name("input") + .index(1) + .required(true) + .help("Input WASM file")) + .get_matches(); + + let input = matches.value_of("input").expect("is required; qed"); + + let module = parity_wasm::deserialize_file(&input).expect("Input module deserialization failed"); + + for section in module.sections() { + match *section { + elements::Section::Import(ref import_section) => { + let mut has_imported_memory_properly_named = false; + for entry in import_section.entries() { + if entry.module() != "env" { + fail("All imports should be from env"); + } + match *entry.external() { + elements::External::Function(_) => { + if !ALLOWED_IMPORTS.contains(&entry.field()) { + fail(&format!("'{}' is not supported by the runtime", entry.field())); + } + }, + elements::External::Memory(m) => { + if entry.field() == "memory" { + has_imported_memory_properly_named = true; + } + + let max = if let Some(max) = m.limits().maximum() { + max + } else { + fail("There is a limit on memory in Parity runtime, and this program does not limit memory"); + }; + + if max > 16 { + fail(&format!( + "Parity runtime has 1Mb limit (16 pages) on max contract memory, this program speicifies {}", + max + )); + } + }, + elements::External::Global(_) => { + fail("Parity runtime does not provide any globals") + }, + _ => { continue; } + } + } + + if !has_imported_memory_properly_named { + fail("No imported memory from env::memory in the contract"); + } + } + _ => { continue; } + } + } +}