From 13f311382683a84e485fe6f1ef8a5779f0b0f41d Mon Sep 17 00:00:00 2001 From: NikVolf Date: Sat, 25 Nov 2017 23:27:58 +0300 Subject: [PATCH] pwasm-run-test utility --- Cargo.lock | 16 ++ Cargo.toml | 2 +- ethcore/wasm/run/Cargo.toml | 19 +++ ethcore/wasm/run/res/sample-fixture.json | 31 ++++ ethcore/wasm/run/res/sample1.wasm | Bin 0 -> 125 bytes ethcore/wasm/run/res/sample2.wasm | Bin 0 -> 15410 bytes ethcore/wasm/run/src/fixture.rs | 49 ++++++ ethcore/wasm/run/src/main.rs | 46 +++++ ethcore/wasm/run/src/runner.rs | 204 +++++++++++++++++++++++ 9 files changed, 366 insertions(+), 1 deletion(-) create mode 100644 ethcore/wasm/run/Cargo.toml create mode 100644 ethcore/wasm/run/res/sample-fixture.json create mode 100644 ethcore/wasm/run/res/sample1.wasm create mode 100644 ethcore/wasm/run/res/sample2.wasm create mode 100644 ethcore/wasm/run/src/fixture.rs create mode 100644 ethcore/wasm/run/src/main.rs create mode 100644 ethcore/wasm/run/src/runner.rs diff --git a/Cargo.lock b/Cargo.lock index 89cf7413d..f9063a4e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2477,6 +2477,22 @@ dependencies = [ "getopts 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "pwasm-run-test" +version = "0.1.0" +dependencies = [ + "clap 2.26.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethcore-bigint 0.2.1", + "ethcore-logger 1.9.0", + "ethjson 0.1.0", + "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "vm 0.1.0", + "wasm 0.1.0", +] + [[package]] name = "quasi" version = "0.32.0" diff --git a/Cargo.toml b/Cargo.toml index 36d8255fc..17250c32c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -116,4 +116,4 @@ lto = false panic = "abort" [workspace] -members = ["ethstore/cli", "ethkey/cli", "evmbin", "whisper", "chainspec", "dapps/js-glue"] +members = ["ethstore/cli", "ethkey/cli", "evmbin", "whisper", "chainspec", "dapps/js-glue", "ethcore/wasm/run"] diff --git a/ethcore/wasm/run/Cargo.toml b/ethcore/wasm/run/Cargo.toml new file mode 100644 index 000000000..a739d72e2 --- /dev/null +++ b/ethcore/wasm/run/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "pwasm-run-test" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +serde = "1" +serde_json = "1" +serde_derive = "1" +ethcore-bigint = { path = "../../../util/bigint" } +ethjson = { path = "../../../json" } +vm = { path = "../../vm" } +wasm = { path = "../" } +clap = "2.24" +ethcore-logger = { path = "../../../logger" } +rustc-hex = "1" + +[features] +default = ["ethcore-bigint/std"] \ No newline at end of file diff --git a/ethcore/wasm/run/res/sample-fixture.json b/ethcore/wasm/run/res/sample-fixture.json new file mode 100644 index 000000000..4f53eaa38 --- /dev/null +++ b/ethcore/wasm/run/res/sample-fixture.json @@ -0,0 +1,31 @@ +[ + { + "caption": "Sample test", + "wasm": "./res/sample1.wasm", + "address": "0x1000000000000000000000000000000000000001", + "sender": "0x1000000000000000000000000000000000000002", + "value": "0x0000000000000000000000000000000000000000000000000000000000000000", + "gasLimit": 100000, + "payload": "0x", + "asserts": [ + { "Return": "0x01" }, + { "UsedGas": 17 }, + { "HasCall": { "codeAddress": "0x1000000000000000000000000000000000000002" }}, + { "HasStorage": + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000001", + "value": "0x0000000000000000000000000000000000000000000000000000000000000002" + } + } + ] + }, + { + "caption": "Keccak test", + "wasm": "./res/sample2.wasm", + "payload": "0x736f6d657468696e67", + "gasLimit": 100000, + "asserts": [ + { "Return": "0x68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87" } + ] + } +] \ No newline at end of file diff --git a/ethcore/wasm/run/res/sample1.wasm b/ethcore/wasm/run/res/sample1.wasm new file mode 100644 index 0000000000000000000000000000000000000000..6ea0c58cc77bec3f6ccec1a8d3616e28d48df29c GIT binary patch literal 125 zcmW-YK@Ng26b0Y=4QOdX+_?7y-hqYZfKnSbEfA{nL-{7x0(pYN9dJ=*qckFEHlHgH8L0 NOWJR4p3-Vv`T}Mx8bkm9 literal 0 HcmV?d00001 diff --git a/ethcore/wasm/run/res/sample2.wasm b/ethcore/wasm/run/res/sample2.wasm new file mode 100644 index 0000000000000000000000000000000000000000..baf9d17aa98fd185167448c58cc0388993dfab0a GIT binary patch literal 15410 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 Tf_{90=6Z$qRJMAI-uqtxfELf` literal 0 HcmV?d00001 diff --git a/ethcore/wasm/run/src/fixture.rs b/ethcore/wasm/run/src/fixture.rs new file mode 100644 index 000000000..0fd3a08e1 --- /dev/null +++ b/ethcore/wasm/run/src/fixture.rs @@ -0,0 +1,49 @@ +use std::borrow::Cow; +use ethjson::uint::Uint; +use ethjson::hash::{Address, H256}; +use ethjson::bytes::Bytes; + +#[derive(Deserialize)] +pub struct Fixture { + pub caption: Cow<'static, String>, + #[serde(rename="wasm")] + pub wasm_file: Cow<'static, String>, + pub address: Option
, + pub sender: Option
, + pub value: Option, + #[serde(rename="gasLimit")] + pub gas_limit: Option, + pub payload: Option, + pub storage: Option>, + pub asserts: Vec, +} + +#[derive(Deserialize, Debug)] +pub struct StorageEntry { + pub key: Uint, + pub value: Uint, +} + +#[derive(Deserialize, Debug, Clone)] +pub struct CallLocator { + pub sender: Option
, + pub receiver: Option
, + pub value: Option, + pub data: Option, + #[serde(rename="codeAddress")] + pub code_address: Option
, +} + +#[derive(Deserialize, Debug)] +pub struct StorageAssert { + pub key: H256, + pub value: H256, +} + +#[derive(Deserialize, Debug)] +pub enum Assert { + HasCall(CallLocator), + HasStorage(StorageAssert), + UsedGas(u64), + Return(Bytes), +} \ No newline at end of file diff --git a/ethcore/wasm/run/src/main.rs b/ethcore/wasm/run/src/main.rs new file mode 100644 index 000000000..1cb38cdf9 --- /dev/null +++ b/ethcore/wasm/run/src/main.rs @@ -0,0 +1,46 @@ +extern crate serde; +extern crate serde_json; +#[macro_use] extern crate serde_derive; +extern crate ethcore_bigint; +extern crate ethjson; +extern crate wasm; +extern crate vm; +extern crate clap; +extern crate ethcore_logger; +extern crate rustc_hex; + +mod fixture; +mod runner; + +use fixture::Fixture; +use clap::{App, Arg}; +use std::fs; + +fn main() { + ::ethcore_logger::init_log(); + + let matches = App::new("pwasm-run-test") + .arg(Arg::with_name("target") + .index(1) + .required(true) + .multiple(true) + .help("JSON fixture")) + .get_matches(); + + let mut exit_code = 0; + + for target in matches.values_of("target").expect("No target parameter") { + let mut f = fs::File::open(target).expect("Failed to open file"); + let fixtures: Vec = serde_json::from_reader(&mut f).expect("Failed to deserialize json"); + + for fixture in fixtures.into_iter() { + let fails = runner::run_fixture(&fixture); + for fail in fails.iter() { + exit_code = 1; + println!("Failed assert in test \"{}\" ('{}'): {}", fixture.caption.as_ref(), target, fail); + } + } + } + + std::process::exit(exit_code); +} \ No newline at end of file diff --git a/ethcore/wasm/run/src/runner.rs b/ethcore/wasm/run/src/runner.rs new file mode 100644 index 000000000..edfc1ee4b --- /dev/null +++ b/ethcore/wasm/run/src/runner.rs @@ -0,0 +1,204 @@ +use fixture::{Fixture, Assert, CallLocator}; +use wasm::WasmInterpreter; +use vm::{self, Vm, GasLeft, ActionParams, ActionValue}; +use vm::tests::FakeExt; +use std::io::{self, Read}; +use std::{fs, path, fmt}; +use std::sync::Arc; +use ethcore_bigint::prelude::{U256, H256, H160}; +use rustc_hex::ToHex; + +fn load_code>(p: P) -> io::Result> { + let mut result = Vec::new(); + let mut f = fs::File::open(p)?; + f.read_to_end(&mut result)?; + Ok(result) +} + +fn wasm_interpreter() -> WasmInterpreter { + WasmInterpreter::new().expect("wasm interpreter to create without errors") +} + +#[derive(Debug)] +pub enum Fail { + Return { expected: Vec, actual: Vec }, + UsedGas { expected: u64, actual: u64 }, + Runtime(String), + Load(io::Error), + NoCall(CallLocator), + StorageMismatch { key: H256, expected: H256, actual: Option }, +} + +impl Fail { + fn runtime(err: vm::Error) -> Vec { + vec![Fail::Runtime(format!("{}", err))] + } + + fn load(err: io::Error) -> Vec { + vec![Fail::Load(err)] + } +} + +impl fmt::Display for Fail { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::Fail::*; + match *self { + Return { ref expected, ref actual } => + write!( + f, + "Expected to return result: 0x{} ({} bytes), but got 0x{} ({} bytes)", + expected.to_hex(), + expected.len(), + actual.to_hex(), + actual.len() + ), + UsedGas { expected, actual } => + write!(f, "Expected to use gas: {}, but got actual gas used: {}", expected, actual), + + Runtime(ref s) => + write!(f, "WASM Runtime error: {}", s), + + Load(ref e) => + write!(f, "Load i/o error: {}", e), + + NoCall(ref call) => + write!(f, "Call not found: {:?}", call), + + StorageMismatch { ref key, ref expected, actual: Some(ref actual)} => + write!( + f, + "Storage key {} value mismatch, expected {}, got: {}", + key.as_ref().to_vec().to_hex(), + expected.as_ref().to_vec().to_hex(), + actual.as_ref().to_vec().to_hex(), + ), + + StorageMismatch { ref key, ref expected, actual: None} => + write!( + f, + "No expected storage value for key {} found, expected {}", + key.as_ref().to_vec().to_hex(), + expected.as_ref().to_vec().to_hex(), + ), + } + } +} + +pub fn run_fixture(fixture: &Fixture) -> Vec { + let mut params = ActionParams::default(); + + params.code = Some(Arc::new( + match load_code(fixture.wasm_file.as_ref()) { + Ok(code) => code, + Err(e) => { return Fail::load(e); }, + } + )); + + if let Some(ref address) = fixture.address { + params.address = address.clone().into(); + } + + if let Some(gas_limit) = fixture.gas_limit { + params.gas = U256::from(gas_limit); + } + + if let Some(ref data) = fixture.payload { + params.data = Some(data.clone().into()) + } + + if let Some(value) = fixture.value { + params.value = ActionValue::Transfer(value.clone().into()) + } + + let mut ext = FakeExt::new(); + if let Some(ref storage) = fixture.storage { + for storage_entry in storage.iter() { + let key: U256 = storage_entry.key.into(); + let val: U256 = storage_entry.value.into(); + ext.store.insert(key.into(), val.into()); + } + } + + let mut interpreter = wasm_interpreter(); + + let interpreter_return = match interpreter.exec(params, &mut ext) { + Ok(ret) => ret, + Err(e) => { return Fail::runtime(e); } + }; + let (gas_left, result) = match interpreter_return { + GasLeft::Known(gas) => { (gas, Vec::new()) }, + GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), + }; + + let mut fails = Vec::new(); + + for assert in fixture.asserts.iter() { + match *assert { + Assert::Return(ref data) => { + if &data[..] != &result[..] { + fails.push(Fail::Return { expected: (&data[..]).to_vec(), actual: (&result[..]).to_vec() }) + } + }, + Assert::UsedGas(gas) => { + let used_gas = fixture.gas_limit.unwrap_or(0) - gas_left.low_u64(); + if gas != used_gas { + fails.push(Fail::UsedGas { expected: gas, actual: used_gas }); + } + }, + Assert::HasCall(ref locator) => { + let mut found = false; + + for fake_call in ext.calls.iter() { + let mut match_ = true; + if let Some(ref data) = locator.data { + if data.as_ref() != &fake_call.data[..] { match_ = false; } + } + + if let Some(ref code_addr) = locator.code_address { + if fake_call.code_address.unwrap_or(H160::zero()) != code_addr.clone().into() { match_ = false } + } + + if let Some(ref sender) = locator.sender { + if fake_call.sender_address.unwrap_or(H160::zero()) != sender.clone().into() { match_ = false } + } + + if let Some(ref receiver) = locator.receiver { + if fake_call.receive_address.unwrap_or(H160::zero()) != receiver.clone().into() { match_ = false } + } + + if match_ { + found = true; + break; + } + } + + if !found { + fails.push(Fail::NoCall(locator.clone())) + } + }, + Assert::HasStorage(ref storage_entry) => { + let expected_storage_key: H256 = storage_entry.key.clone().into(); + let expected_storage_value: H256 = storage_entry.value.clone().into(); + let val = ext.store.get(&expected_storage_key); + + if let Some(val) = val { + if val != &expected_storage_value { + fails.push(Fail::StorageMismatch { + key: expected_storage_key, + expected: expected_storage_value, + actual: Some(val.clone()) + }) + } + } else { + fails.push(Fail::StorageMismatch { + key: expected_storage_key, + expected: expected_storage_value, + actual: None, + }) + } + + }, + } + } + fails +} \ No newline at end of file