From 87bf2d498ac2b0c1037133c56a2cefe1cb1ea6c6 Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Fri, 18 Oct 2019 16:28:19 +0200 Subject: [PATCH 01/26] executor: Use non wasmi-specific execution in tests. --- core/executor/src/sandbox.rs | 117 +++++++++++--- core/executor/src/wasmi_execution.rs | 219 ++++++++++++++++++++------- 2 files changed, 258 insertions(+), 78 deletions(-) diff --git a/core/executor/src/sandbox.rs b/core/executor/src/sandbox.rs index e1e9e0db95263..482c81fa5ed12 100644 --- a/core/executor/src/sandbox.rs +++ b/core/executor/src/sandbox.rs @@ -586,32 +586,20 @@ impl Store { #[cfg(test)] mod tests { use super::*; - use primitives::{Blake2Hasher, traits::Externalities}; - use crate::wasm_runtime::WasmRuntime; - use crate::wasmi_execution; + use primitives::Blake2Hasher; + use crate::{WasmExecutionMethod, call_in_wasm}; use state_machine::TestExternalities as CoreTestExternalities; use wabt; use runtime_test::WASM_BINARY; type TestExternalities = CoreTestExternalities; - fn call_wasm( - ext: &mut E, - heap_pages: u64, - code: &[u8], - method: &str, - data: &[u8], - ) -> Result> { - let mut instance = wasmi_execution::create_instance(ext, code, heap_pages) - .map_err(|err| err.to_string())?; - instance.call(ext, method, data) - } - #[test] fn sandbox_should_work() { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; let code = wabt::wat2wasm(r#" (module @@ -635,7 +623,14 @@ mod tests { "#).unwrap().encode(); assert_eq!( - call_wasm(&mut ext, 8, &test_code[..], "test_sandbox", &code).unwrap(), + call_in_wasm( + "test_sandbox", + &code, + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), true.encode(), ); } @@ -645,6 +640,7 @@ mod tests { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; let code = wabt::wat2wasm(r#" (module @@ -657,7 +653,14 @@ mod tests { "#).unwrap(); assert_eq!( - call_wasm(&mut ext, 8, &test_code[..], "test_sandbox", &code).unwrap(), + call_in_wasm( + "test_sandbox", + &code, + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), vec![0], ); } @@ -667,6 +670,7 @@ mod tests { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; let code = wabt::wat2wasm(r#" (module @@ -678,7 +682,14 @@ mod tests { ) "#).unwrap().encode(); - let res = call_wasm(&mut ext, 8, &test_code[..], "test_exhaust_heap", &code); + let res = call_in_wasm( + "test_exhaust_heap", + &code, + wasm_method, + &mut ext, + &test_code[..], + 8, + ); assert_eq!(res.is_err(), true); if let Err(err) = res { assert_eq!( @@ -696,6 +707,7 @@ mod tests { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; let code = wabt::wat2wasm(r#" (module @@ -725,7 +737,14 @@ mod tests { "#).unwrap().encode(); assert_eq!( - call_wasm(&mut ext, 8, &test_code[..], "test_sandbox", &code).unwrap(), + call_in_wasm( + "test_sandbox", + &code, + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), true.encode(), ); } @@ -735,6 +754,7 @@ mod tests { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; let code = wabt::wat2wasm(r#" (module @@ -760,7 +780,14 @@ mod tests { "#).unwrap().encode(); assert_eq!( - call_wasm(&mut ext, 8, &test_code[..], "test_sandbox_args", &code).unwrap(), + call_in_wasm( + "test_sandbox_args", + &code, + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), true.encode(), ); } @@ -770,6 +797,7 @@ mod tests { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; let code = wabt::wat2wasm(r#" (module @@ -783,7 +811,14 @@ mod tests { "#).unwrap().encode(); assert_eq!( - call_wasm(&mut ext, 8, &test_code[..], "test_sandbox_return_val", &code).unwrap(), + call_in_wasm( + "test_sandbox_return_val", + &code, + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), true.encode(), ); } @@ -793,6 +828,7 @@ mod tests { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; let code = wabt::wat2wasm(r#" (module @@ -804,7 +840,14 @@ mod tests { "#).unwrap().encode(); assert_eq!( - call_wasm(&mut ext, 8, &test_code[..], "test_sandbox_instantiate", &code).unwrap(), + call_in_wasm( + "test_sandbox_instantiate", + &code, + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), 1u8.encode(), ); } @@ -814,12 +857,20 @@ mod tests { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; // Corrupted wasm file let code = vec![0u8, 0, 0, 0, 1, 0, 0, 0].encode(); assert_eq!( - call_wasm(&mut ext, 8, &test_code[..], "test_sandbox_instantiate", &code).unwrap(), + call_in_wasm( + "test_sandbox_instantiate", + &code, + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), 1u8.encode(), ); } @@ -829,6 +880,7 @@ mod tests { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; let code = wabt::wat2wasm(r#" (module @@ -843,7 +895,14 @@ mod tests { "#).unwrap().encode(); assert_eq!( - call_wasm(&mut ext, 8, &test_code[..], "test_sandbox_instantiate", &code).unwrap(), + call_in_wasm( + "test_sandbox_instantiate", + &code, + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), 0u8.encode(), ); } @@ -853,6 +912,7 @@ mod tests { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; let code = wabt::wat2wasm(r#" (module @@ -868,7 +928,14 @@ mod tests { "#).unwrap().encode(); assert_eq!( - call_wasm(&mut ext, 8, &test_code[..], "test_sandbox_instantiate", &code).unwrap(), + call_in_wasm( + "test_sandbox_instantiate", + &code, + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), 2u8.encode(), ); } diff --git a/core/executor/src/wasmi_execution.rs b/core/executor/src/wasmi_execution.rs index 2b832b490641d..903ed3345c55f 100644 --- a/core/executor/src/wasmi_execution.rs +++ b/core/executor/src/wasmi_execution.rs @@ -663,28 +663,25 @@ mod tests { use substrate_offchain::testing; use trie::{TrieConfiguration, trie_types::Layout}; use codec::{Encode, Decode}; + use crate::{WasmExecutionMethod, call_in_wasm}; type TestExternalities = CoreTestExternalities; - fn call( - ext: &mut E, - heap_pages: u64, - code: &[u8], - method: &str, - data: &[u8], - ) -> Result, Error> { - let mut instance = create_instance(ext, code, heap_pages) - .map_err(|err| err.to_string())?; - instance.call(ext, method, data) - } - #[test] fn returning_should_work() { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; - - let output = call(&mut ext, 8, &test_code[..], "test_empty_return", &[]).unwrap(); + let wasm_method = WasmExecutionMethod::Interpreted; + + let output = call_in_wasm( + "test_empty_return", + &[], + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(); assert_eq!(output, vec![0u8; 0]); } @@ -693,32 +690,56 @@ mod tests { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; - - let output = call(&mut ext, 8, &test_code[..], "test_panic", &[]); + let wasm_method = WasmExecutionMethod::Interpreted; + + let output = call_in_wasm( + "test_panic", + &[], + wasm_method, + &mut ext, + &test_code[..], + 8, + ); assert!(output.is_err()); - let output = call(&mut ext, 8, &test_code[..], "test_conditional_panic", &[0]); + let output = call_in_wasm( + "test_conditional_panic", + &[0], + wasm_method, + &mut ext, + &test_code[..], + 8, + ); assert_eq!(Decode::decode(&mut &output.unwrap()[..]), Ok(Vec::::new())); - let output = call(&mut ext, 8, &test_code[..], "test_conditional_panic", &vec![2].encode()); + let output = call_in_wasm( + "test_conditional_panic", + &vec![2].encode(), + wasm_method, + &mut ext, + &test_code[..], + 8, + ); assert!(output.is_err()); } #[test] fn storage_should_work() { let mut ext = TestExternalities::default(); + let wasm_method = WasmExecutionMethod::Interpreted; { let mut ext = ext.ext(); ext.set_storage(b"foo".to_vec(), b"bar".to_vec()); let test_code = WASM_BINARY; - let output = call( - &mut ext, - 8, - &test_code[..], + let output = call_in_wasm( "test_data_in", &b"Hello world".to_vec().encode(), + wasm_method, + &mut ext, + &test_code[..], + 8, ).unwrap(); assert_eq!(output, b"all ok!".to_vec().encode()); @@ -735,6 +756,7 @@ mod tests { #[test] fn clear_prefix_should_work() { let mut ext = TestExternalities::default(); + let wasm_method = WasmExecutionMethod::Interpreted; { let mut ext = ext.ext(); ext.set_storage(b"aaa".to_vec(), b"1".to_vec()); @@ -745,12 +767,13 @@ mod tests { let test_code = WASM_BINARY; // This will clear all entries which prefix is "ab". - let output = call( - &mut ext, - 8, - &test_code[..], + let output = call_in_wasm( "test_clear_prefix", &b"ab".to_vec().encode(), + wasm_method, + &mut ext, + &test_code[..], + 8, ).unwrap(); assert_eq!(output, b"all ok!".to_vec().encode()); @@ -769,17 +792,26 @@ mod tests { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; assert_eq!( - call(&mut ext, 8, &test_code[..], "test_blake2_256", &[0]).unwrap(), + call_in_wasm( + "test_blake2_256", + &[0], + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), blake2_256(&b""[..]).to_vec().encode(), ); assert_eq!( - call( - &mut ext, - 8, - &test_code[..], + call_in_wasm( "test_blake2_256", &b"Hello world!".to_vec().encode(), + wasm_method, + &mut ext, + &test_code[..], + 8, ).unwrap(), blake2_256(&b"Hello world!"[..]).to_vec().encode(), ); @@ -790,17 +822,26 @@ mod tests { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; assert_eq!( - call(&mut ext, 8, &test_code[..], "test_blake2_128", &[0]).unwrap(), + call_in_wasm( + "test_blake2_128", + &[0], + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), blake2_128(&b""[..]).to_vec().encode(), ); assert_eq!( - call( - &mut ext, - 8, - &test_code[..], + call_in_wasm( "test_blake2_128", &b"Hello world!".to_vec().encode(), + wasm_method, + &mut ext, + &test_code[..], + 8, ).unwrap(), blake2_128(&b"Hello world!"[..]).to_vec().encode(), ); @@ -811,19 +852,28 @@ mod tests { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; assert_eq!( - call(&mut ext, 8, &test_code[..], "test_twox_256", &[0]).unwrap(), + call_in_wasm( + "test_twox_256", + &[0], + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), hex!( "99e9d85137db46ef4bbea33613baafd56f963c64b1f3685a4eb4abd67ff6203a" ).to_vec().encode(), ); assert_eq!( - call( - &mut ext, - 8, - &test_code[..], + call_in_wasm( "test_twox_256", &b"Hello world!".to_vec().encode(), + wasm_method, + &mut ext, + &test_code[..], + 8, ).unwrap(), hex!( "b27dfd7f223f177f2a13647b533599af0c07f68bda23d96d059da2b451a35a74" @@ -836,17 +886,26 @@ mod tests { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; assert_eq!( - call(&mut ext, 8, &test_code[..], "test_twox_128", &[0]).unwrap(), + call_in_wasm( + "test_twox_128", + &[0], + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), hex!("99e9d85137db46ef4bbea33613baafd5").to_vec().encode(), ); assert_eq!( - call( - &mut ext, - 8, - &test_code[..], + call_in_wasm( "test_twox_128", &b"Hello world!".to_vec().encode(), + wasm_method, + &mut ext, + &test_code[..], + 8, ).unwrap(), hex!("b27dfd7f223f177f2a13647b533599af").to_vec().encode(), ); @@ -857,6 +916,7 @@ mod tests { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; let key = ed25519::Pair::from_seed(&blake2_256(b"test")); let sig = key.sign(b"all ok!"); let mut calldata = vec![]; @@ -864,7 +924,14 @@ mod tests { calldata.extend_from_slice(sig.as_ref()); assert_eq!( - call(&mut ext, 8, &test_code[..], "test_ed25519_verify", &calldata.encode()).unwrap(), + call_in_wasm( + "test_ed25519_verify", + &calldata.encode(), + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), true.encode(), ); @@ -874,7 +941,14 @@ mod tests { calldata.extend_from_slice(other_sig.as_ref()); assert_eq!( - call(&mut ext, 8, &test_code[..], "test_ed25519_verify", &calldata.encode()).unwrap(), + call_in_wasm( + "test_ed25519_verify", + &calldata.encode(), + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), false.encode(), ); } @@ -884,6 +958,7 @@ mod tests { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; let key = sr25519::Pair::from_seed(&blake2_256(b"test")); let sig = key.sign(b"all ok!"); let mut calldata = vec![]; @@ -891,7 +966,14 @@ mod tests { calldata.extend_from_slice(sig.as_ref()); assert_eq!( - call(&mut ext, 8, &test_code[..], "test_sr25519_verify", &calldata.encode()).unwrap(), + call_in_wasm( + "test_sr25519_verify", + &calldata.encode(), + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), true.encode(), ); @@ -901,7 +983,14 @@ mod tests { calldata.extend_from_slice(other_sig.as_ref()); assert_eq!( - call(&mut ext, 8, &test_code[..], "test_sr25519_verify", &calldata.encode()).unwrap(), + call_in_wasm( + "test_sr25519_verify", + &calldata.encode(), + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), false.encode(), ); } @@ -912,8 +1001,16 @@ mod tests { let mut ext = ext.ext(); let trie_input = vec![b"zero".to_vec(), b"one".to_vec(), b"two".to_vec()]; let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; assert_eq!( - call(&mut ext, 8, &test_code[..], "test_ordered_trie_root", &[0]).unwrap(), + call_in_wasm( + "test_ordered_trie_root", + &[0], + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), Layout::::ordered_trie_root(trie_input.iter()).as_bytes().encode(), ); } @@ -926,9 +1023,17 @@ mod tests { let (offchain, state) = testing::TestOffchainExt::new(); ext.register_extension(OffchainExt::new(offchain)); let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; let mut ext = ext.ext(); assert_eq!( - call(&mut ext, 8, &test_code[..], "test_offchain_local_storage", &[0]).unwrap(), + call_in_wasm( + "test_offchain_local_storage", + &[0], + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), true.encode(), ); assert_eq!(state.read().persistent_storage.get(b"", b"test"), Some(vec![])); @@ -954,9 +1059,17 @@ mod tests { ); let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; let mut ext = ext.ext(); assert_eq!( - call(&mut ext, 8, &test_code[..], "test_offchain_http", &[0]).unwrap(), + call_in_wasm( + "test_offchain_http", + &[0], + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), true.encode(), ); } From f37b9d833ad29981d24d216da5f3ab0572f9bf6c Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Fri, 18 Oct 2019 16:33:00 +0200 Subject: [PATCH 02/26] executor: Move all runtime execution tests into tests file. --- core/executor/src/integration_tests/mod.rs | 441 ++++++++++++++++++ .../executor/src/integration_tests/sandbox.rs | 369 +++++++++++++++ core/executor/src/lib.rs | 2 + core/executor/src/sandbox.rs | 358 -------------- core/executor/src/wasmi_execution.rs | 425 ----------------- 5 files changed, 812 insertions(+), 783 deletions(-) create mode 100644 core/executor/src/integration_tests/mod.rs create mode 100644 core/executor/src/integration_tests/sandbox.rs diff --git a/core/executor/src/integration_tests/mod.rs b/core/executor/src/integration_tests/mod.rs new file mode 100644 index 0000000000000..ca3e36461b98a --- /dev/null +++ b/core/executor/src/integration_tests/mod.rs @@ -0,0 +1,441 @@ +// Copyright 2017-2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +mod sandbox; + +use codec::{Encode, Decode}; +use hex_literal::hex; +use primitives::{ + Blake2Hasher, blake2_128, blake2_256, ed25519, sr25519, map, Pair, offchain::OffchainExt, + traits::Externalities, +}; +use runtime_test::WASM_BINARY; +use state_machine::TestExternalities as CoreTestExternalities; +use substrate_offchain::testing; +use trie::{TrieConfiguration, trie_types::Layout}; + +use crate::{WasmExecutionMethod, call_in_wasm}; +use crate::error::Error; + +pub type TestExternalities = CoreTestExternalities; + +#[test] +fn returning_should_work() { + let mut ext = TestExternalities::default(); + let mut ext = ext.ext(); + let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; + + let output = call_in_wasm( + "test_empty_return", + &[], + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(); + assert_eq!(output, vec![0u8; 0]); +} + +#[test] +fn panicking_should_work() { + let mut ext = TestExternalities::default(); + let mut ext = ext.ext(); + let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; + + let output = call_in_wasm( + "test_panic", + &[], + wasm_method, + &mut ext, + &test_code[..], + 8, + ); + assert!(output.is_err()); + + let output = call_in_wasm( + "test_conditional_panic", + &[0], + wasm_method, + &mut ext, + &test_code[..], + 8, + ); + assert_eq!(Decode::decode(&mut &output.unwrap()[..]), Ok(Vec::::new())); + + let output = call_in_wasm( + "test_conditional_panic", + &vec![2].encode(), + wasm_method, + &mut ext, + &test_code[..], + 8, + ); + assert!(output.is_err()); +} + +#[test] +fn storage_should_work() { + let mut ext = TestExternalities::default(); + let wasm_method = WasmExecutionMethod::Interpreted; + + { + let mut ext = ext.ext(); + ext.set_storage(b"foo".to_vec(), b"bar".to_vec()); + let test_code = WASM_BINARY; + + let output = call_in_wasm( + "test_data_in", + &b"Hello world".to_vec().encode(), + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(); + + assert_eq!(output, b"all ok!".to_vec().encode()); + } + + let expected = TestExternalities::new((map![ + b"input".to_vec() => b"Hello world".to_vec(), + b"foo".to_vec() => b"bar".to_vec(), + b"baz".to_vec() => b"bar".to_vec() + ], map![])); + assert_eq!(ext, expected); +} + +#[test] +fn clear_prefix_should_work() { + let mut ext = TestExternalities::default(); + let wasm_method = WasmExecutionMethod::Interpreted; + { + let mut ext = ext.ext(); + ext.set_storage(b"aaa".to_vec(), b"1".to_vec()); + ext.set_storage(b"aab".to_vec(), b"2".to_vec()); + ext.set_storage(b"aba".to_vec(), b"3".to_vec()); + ext.set_storage(b"abb".to_vec(), b"4".to_vec()); + ext.set_storage(b"bbb".to_vec(), b"5".to_vec()); + let test_code = WASM_BINARY; + + // This will clear all entries which prefix is "ab". + let output = call_in_wasm( + "test_clear_prefix", + &b"ab".to_vec().encode(), + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(); + + assert_eq!(output, b"all ok!".to_vec().encode()); + } + + let expected = TestExternalities::new((map![ + b"aaa".to_vec() => b"1".to_vec(), + b"aab".to_vec() => b"2".to_vec(), + b"bbb".to_vec() => b"5".to_vec() + ], map![])); + assert_eq!(expected, ext); +} + +#[test] +fn blake2_256_should_work() { + let mut ext = TestExternalities::default(); + let mut ext = ext.ext(); + let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; + assert_eq!( + call_in_wasm( + "test_blake2_256", + &[0], + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + blake2_256(&b""[..]).to_vec().encode(), + ); + assert_eq!( + call_in_wasm( + "test_blake2_256", + &b"Hello world!".to_vec().encode(), + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + blake2_256(&b"Hello world!"[..]).to_vec().encode(), + ); +} + +#[test] +fn blake2_128_should_work() { + let mut ext = TestExternalities::default(); + let mut ext = ext.ext(); + let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; + assert_eq!( + call_in_wasm( + "test_blake2_128", + &[0], + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + blake2_128(&b""[..]).to_vec().encode(), + ); + assert_eq!( + call_in_wasm( + "test_blake2_128", + &b"Hello world!".to_vec().encode(), + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + blake2_128(&b"Hello world!"[..]).to_vec().encode(), + ); +} + +#[test] +fn twox_256_should_work() { + let mut ext = TestExternalities::default(); + let mut ext = ext.ext(); + let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; + assert_eq!( + call_in_wasm( + "test_twox_256", + &[0], + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + hex!( + "99e9d85137db46ef4bbea33613baafd56f963c64b1f3685a4eb4abd67ff6203a" + ).to_vec().encode(), + ); + assert_eq!( + call_in_wasm( + "test_twox_256", + &b"Hello world!".to_vec().encode(), + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + hex!( + "b27dfd7f223f177f2a13647b533599af0c07f68bda23d96d059da2b451a35a74" + ).to_vec().encode(), + ); +} + +#[test] +fn twox_128_should_work() { + let mut ext = TestExternalities::default(); + let mut ext = ext.ext(); + let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; + assert_eq!( + call_in_wasm( + "test_twox_128", + &[0], + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + hex!("99e9d85137db46ef4bbea33613baafd5").to_vec().encode(), + ); + assert_eq!( + call_in_wasm( + "test_twox_128", + &b"Hello world!".to_vec().encode(), + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + hex!("b27dfd7f223f177f2a13647b533599af").to_vec().encode(), + ); +} + +#[test] +fn ed25519_verify_should_work() { + let mut ext = TestExternalities::default(); + let mut ext = ext.ext(); + let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; + let key = ed25519::Pair::from_seed(&blake2_256(b"test")); + let sig = key.sign(b"all ok!"); + let mut calldata = vec![]; + calldata.extend_from_slice(key.public().as_ref()); + calldata.extend_from_slice(sig.as_ref()); + + assert_eq!( + call_in_wasm( + "test_ed25519_verify", + &calldata.encode(), + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + true.encode(), + ); + + let other_sig = key.sign(b"all is not ok!"); + let mut calldata = vec![]; + calldata.extend_from_slice(key.public().as_ref()); + calldata.extend_from_slice(other_sig.as_ref()); + + assert_eq!( + call_in_wasm( + "test_ed25519_verify", + &calldata.encode(), + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + false.encode(), + ); +} + +#[test] +fn sr25519_verify_should_work() { + let mut ext = TestExternalities::default(); + let mut ext = ext.ext(); + let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; + let key = sr25519::Pair::from_seed(&blake2_256(b"test")); + let sig = key.sign(b"all ok!"); + let mut calldata = vec![]; + calldata.extend_from_slice(key.public().as_ref()); + calldata.extend_from_slice(sig.as_ref()); + + assert_eq!( + call_in_wasm( + "test_sr25519_verify", + &calldata.encode(), + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + true.encode(), + ); + + let other_sig = key.sign(b"all is not ok!"); + let mut calldata = vec![]; + calldata.extend_from_slice(key.public().as_ref()); + calldata.extend_from_slice(other_sig.as_ref()); + + assert_eq!( + call_in_wasm( + "test_sr25519_verify", + &calldata.encode(), + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + false.encode(), + ); +} + +#[test] +fn ordered_trie_root_should_work() { + let mut ext = TestExternalities::default(); + let mut ext = ext.ext(); + let trie_input = vec![b"zero".to_vec(), b"one".to_vec(), b"two".to_vec()]; + let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; + assert_eq!( + call_in_wasm( + "test_ordered_trie_root", + &[0], + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + Layout::::ordered_trie_root(trie_input.iter()).as_bytes().encode(), + ); +} + +#[test] +fn offchain_local_storage_should_work() { + use substrate_client::backend::OffchainStorage; + + let mut ext = TestExternalities::default(); + let (offchain, state) = testing::TestOffchainExt::new(); + ext.register_extension(OffchainExt::new(offchain)); + let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; + let mut ext = ext.ext(); + assert_eq!( + call_in_wasm( + "test_offchain_local_storage", + &[0], + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + true.encode(), + ); + assert_eq!(state.read().persistent_storage.get(b"", b"test"), Some(vec![])); +} + +#[test] +fn offchain_http_should_work() { + let mut ext = TestExternalities::default(); + let (offchain, state) = testing::TestOffchainExt::new(); + ext.register_extension(OffchainExt::new(offchain)); + state.write().expect_request( + 0, + testing::PendingRequest { + method: "POST".into(), + uri: "http://localhost:12345".into(), + body: vec![1, 2, 3, 4], + headers: vec![("X-Auth".to_owned(), "test".to_owned())], + sent: true, + response: Some(vec![1, 2, 3]), + response_headers: vec![("X-Auth".to_owned(), "hello".to_owned())], + ..Default::default() + }, + ); + + let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; + let mut ext = ext.ext(); + assert_eq!( + call_in_wasm( + "test_offchain_http", + &[0], + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + true.encode(), + ); +} + diff --git a/core/executor/src/integration_tests/sandbox.rs b/core/executor/src/integration_tests/sandbox.rs new file mode 100644 index 0000000000000..29aab8dddb8ae --- /dev/null +++ b/core/executor/src/integration_tests/sandbox.rs @@ -0,0 +1,369 @@ +// Copyright 2018-2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +use codec::Encode; +use runtime_test::WASM_BINARY; +use wabt; + +use crate::{WasmExecutionMethod, call_in_wasm}; +use crate::error::Error; +use crate::integration_tests::TestExternalities; + +#[test] +fn sandbox_should_work() { + let mut ext = TestExternalities::default(); + let mut ext = ext.ext(); + let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; + + let code = wabt::wat2wasm(r#" + (module + (import "env" "assert" (func $assert (param i32))) + (import "env" "inc_counter" (func $inc_counter (param i32) (result i32))) + (func (export "call") + (drop + (call $inc_counter (i32.const 5)) + ) + + (call $inc_counter (i32.const 3)) + ;; current counter value is on the stack + + ;; check whether current == 8 + i32.const 8 + i32.eq + + call $assert + ) + ) + "#).unwrap().encode(); + + assert_eq!( + call_in_wasm( + "test_sandbox", + &code, + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + true.encode(), + ); +} + +#[test] +fn sandbox_trap() { + let mut ext = TestExternalities::default(); + let mut ext = ext.ext(); + let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; + + let code = wabt::wat2wasm(r#" + (module + (import "env" "assert" (func $assert (param i32))) + (func (export "call") + i32.const 0 + call $assert + ) + ) + "#).unwrap(); + + assert_eq!( + call_in_wasm( + "test_sandbox", + &code, + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + vec![0], + ); +} + +#[test] +fn sandbox_should_trap_when_heap_exhausted() { + let mut ext = TestExternalities::default(); + let mut ext = ext.ext(); + let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; + + let code = wabt::wat2wasm(r#" + (module + (import "env" "assert" (func $assert (param i32))) + (func (export "call") + i32.const 0 + call $assert + ) + ) + "#).unwrap().encode(); + + let res = call_in_wasm( + "test_exhaust_heap", + &code, + wasm_method, + &mut ext, + &test_code[..], + 8, + ); + assert_eq!(res.is_err(), true); + if let Err(err) = res { + assert_eq!( + format!("{}", err), + format!( + "{}", + wasmi::Error::Trap(Error::FunctionExecution("AllocatorOutOfSpace".into()).into()), + ), + ); + } +} + +#[test] +fn start_called() { + let mut ext = TestExternalities::default(); + let mut ext = ext.ext(); + let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; + + let code = wabt::wat2wasm(r#" + (module + (import "env" "assert" (func $assert (param i32))) + (import "env" "inc_counter" (func $inc_counter (param i32) (result i32))) + + ;; Start function + (start $start) + (func $start + ;; Increment counter by 1 + (drop + (call $inc_counter (i32.const 1)) + ) + ) + + (func (export "call") + ;; Increment counter by 1. The current value is placed on the stack. + (call $inc_counter (i32.const 1)) + + ;; Counter is incremented twice by 1, once there and once in `start` func. + ;; So check the returned value is equal to 2. + i32.const 2 + i32.eq + call $assert + ) + ) + "#).unwrap().encode(); + + assert_eq!( + call_in_wasm( + "test_sandbox", + &code, + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + true.encode(), + ); +} + +#[test] +fn invoke_args() { + let mut ext = TestExternalities::default(); + let mut ext = ext.ext(); + let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; + + let code = wabt::wat2wasm(r#" + (module + (import "env" "assert" (func $assert (param i32))) + + (func (export "call") (param $x i32) (param $y i64) + ;; assert that $x = 0x12345678 + (call $assert + (i32.eq + (get_local $x) + (i32.const 0x12345678) + ) + ) + + (call $assert + (i64.eq + (get_local $y) + (i64.const 0x1234567887654321) + ) + ) + ) + ) + "#).unwrap().encode(); + + assert_eq!( + call_in_wasm( + "test_sandbox_args", + &code, + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + true.encode(), + ); +} + +#[test] +fn return_val() { + let mut ext = TestExternalities::default(); + let mut ext = ext.ext(); + let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; + + let code = wabt::wat2wasm(r#" + (module + (func (export "call") (param $x i32) (result i32) + (i32.add + (get_local $x) + (i32.const 1) + ) + ) + ) + "#).unwrap().encode(); + + assert_eq!( + call_in_wasm( + "test_sandbox_return_val", + &code, + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + true.encode(), + ); +} + +#[test] +fn unlinkable_module() { + let mut ext = TestExternalities::default(); + let mut ext = ext.ext(); + let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; + + let code = wabt::wat2wasm(r#" + (module + (import "env" "non-existent" (func)) + + (func (export "call") + ) + ) + "#).unwrap().encode(); + + assert_eq!( + call_in_wasm( + "test_sandbox_instantiate", + &code, + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + 1u8.encode(), + ); +} + +#[test] +fn corrupted_module() { + let mut ext = TestExternalities::default(); + let mut ext = ext.ext(); + let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; + + // Corrupted wasm file + let code = vec![0u8, 0, 0, 0, 1, 0, 0, 0].encode(); + + assert_eq!( + call_in_wasm( + "test_sandbox_instantiate", + &code, + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + 1u8.encode(), + ); +} + +#[test] +fn start_fn_ok() { + let mut ext = TestExternalities::default(); + let mut ext = ext.ext(); + let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; + + let code = wabt::wat2wasm(r#" + (module + (func (export "call") + ) + + (func $start + ) + + (start $start) + ) + "#).unwrap().encode(); + + assert_eq!( + call_in_wasm( + "test_sandbox_instantiate", + &code, + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + 0u8.encode(), + ); +} + +#[test] +fn start_fn_traps() { + let mut ext = TestExternalities::default(); + let mut ext = ext.ext(); + let test_code = WASM_BINARY; + let wasm_method = WasmExecutionMethod::Interpreted; + + let code = wabt::wat2wasm(r#" + (module + (func (export "call") + ) + + (func $start + unreachable + ) + + (start $start) + ) + "#).unwrap().encode(); + + assert_eq!( + call_in_wasm( + "test_sandbox_instantiate", + &code, + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + 2u8.encode(), + ); +} diff --git a/core/executor/src/lib.rs b/core/executor/src/lib.rs index ac98388cd7bbe..f83f9202f59bb 100644 --- a/core/executor/src/lib.rs +++ b/core/executor/src/lib.rs @@ -38,6 +38,8 @@ mod sandbox; mod allocator; mod host_interface; mod wasm_runtime; +#[cfg(test)] +mod integration_tests; pub mod error; pub use wasmi; diff --git a/core/executor/src/sandbox.rs b/core/executor/src/sandbox.rs index 482c81fa5ed12..87edae8c3037d 100644 --- a/core/executor/src/sandbox.rs +++ b/core/executor/src/sandbox.rs @@ -582,361 +582,3 @@ impl Store { instance_idx as u32 } } - -#[cfg(test)] -mod tests { - use super::*; - use primitives::Blake2Hasher; - use crate::{WasmExecutionMethod, call_in_wasm}; - use state_machine::TestExternalities as CoreTestExternalities; - use wabt; - use runtime_test::WASM_BINARY; - - type TestExternalities = CoreTestExternalities; - - #[test] - fn sandbox_should_work() { - let mut ext = TestExternalities::default(); - let mut ext = ext.ext(); - let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; - - let code = wabt::wat2wasm(r#" - (module - (import "env" "assert" (func $assert (param i32))) - (import "env" "inc_counter" (func $inc_counter (param i32) (result i32))) - (func (export "call") - (drop - (call $inc_counter (i32.const 5)) - ) - - (call $inc_counter (i32.const 3)) - ;; current counter value is on the stack - - ;; check whether current == 8 - i32.const 8 - i32.eq - - call $assert - ) - ) - "#).unwrap().encode(); - - assert_eq!( - call_in_wasm( - "test_sandbox", - &code, - wasm_method, - &mut ext, - &test_code[..], - 8, - ).unwrap(), - true.encode(), - ); - } - - #[test] - fn sandbox_trap() { - let mut ext = TestExternalities::default(); - let mut ext = ext.ext(); - let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; - - let code = wabt::wat2wasm(r#" - (module - (import "env" "assert" (func $assert (param i32))) - (func (export "call") - i32.const 0 - call $assert - ) - ) - "#).unwrap(); - - assert_eq!( - call_in_wasm( - "test_sandbox", - &code, - wasm_method, - &mut ext, - &test_code[..], - 8, - ).unwrap(), - vec![0], - ); - } - - #[test] - fn sandbox_should_trap_when_heap_exhausted() { - let mut ext = TestExternalities::default(); - let mut ext = ext.ext(); - let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; - - let code = wabt::wat2wasm(r#" - (module - (import "env" "assert" (func $assert (param i32))) - (func (export "call") - i32.const 0 - call $assert - ) - ) - "#).unwrap().encode(); - - let res = call_in_wasm( - "test_exhaust_heap", - &code, - wasm_method, - &mut ext, - &test_code[..], - 8, - ); - assert_eq!(res.is_err(), true); - if let Err(err) = res { - assert_eq!( - format!("{}", err), - format!( - "{}", - wasmi::Error::Trap(Error::FunctionExecution("AllocatorOutOfSpace".into()).into()), - ), - ); - } - } - - #[test] - fn start_called() { - let mut ext = TestExternalities::default(); - let mut ext = ext.ext(); - let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; - - let code = wabt::wat2wasm(r#" - (module - (import "env" "assert" (func $assert (param i32))) - (import "env" "inc_counter" (func $inc_counter (param i32) (result i32))) - - ;; Start function - (start $start) - (func $start - ;; Increment counter by 1 - (drop - (call $inc_counter (i32.const 1)) - ) - ) - - (func (export "call") - ;; Increment counter by 1. The current value is placed on the stack. - (call $inc_counter (i32.const 1)) - - ;; Counter is incremented twice by 1, once there and once in `start` func. - ;; So check the returned value is equal to 2. - i32.const 2 - i32.eq - call $assert - ) - ) - "#).unwrap().encode(); - - assert_eq!( - call_in_wasm( - "test_sandbox", - &code, - wasm_method, - &mut ext, - &test_code[..], - 8, - ).unwrap(), - true.encode(), - ); - } - - #[test] - fn invoke_args() { - let mut ext = TestExternalities::default(); - let mut ext = ext.ext(); - let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; - - let code = wabt::wat2wasm(r#" - (module - (import "env" "assert" (func $assert (param i32))) - - (func (export "call") (param $x i32) (param $y i64) - ;; assert that $x = 0x12345678 - (call $assert - (i32.eq - (get_local $x) - (i32.const 0x12345678) - ) - ) - - (call $assert - (i64.eq - (get_local $y) - (i64.const 0x1234567887654321) - ) - ) - ) - ) - "#).unwrap().encode(); - - assert_eq!( - call_in_wasm( - "test_sandbox_args", - &code, - wasm_method, - &mut ext, - &test_code[..], - 8, - ).unwrap(), - true.encode(), - ); - } - - #[test] - fn return_val() { - let mut ext = TestExternalities::default(); - let mut ext = ext.ext(); - let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; - - let code = wabt::wat2wasm(r#" - (module - (func (export "call") (param $x i32) (result i32) - (i32.add - (get_local $x) - (i32.const 1) - ) - ) - ) - "#).unwrap().encode(); - - assert_eq!( - call_in_wasm( - "test_sandbox_return_val", - &code, - wasm_method, - &mut ext, - &test_code[..], - 8, - ).unwrap(), - true.encode(), - ); - } - - #[test] - fn unlinkable_module() { - let mut ext = TestExternalities::default(); - let mut ext = ext.ext(); - let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; - - let code = wabt::wat2wasm(r#" - (module - (import "env" "non-existent" (func)) - - (func (export "call") - ) - ) - "#).unwrap().encode(); - - assert_eq!( - call_in_wasm( - "test_sandbox_instantiate", - &code, - wasm_method, - &mut ext, - &test_code[..], - 8, - ).unwrap(), - 1u8.encode(), - ); - } - - #[test] - fn corrupted_module() { - let mut ext = TestExternalities::default(); - let mut ext = ext.ext(); - let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; - - // Corrupted wasm file - let code = vec![0u8, 0, 0, 0, 1, 0, 0, 0].encode(); - - assert_eq!( - call_in_wasm( - "test_sandbox_instantiate", - &code, - wasm_method, - &mut ext, - &test_code[..], - 8, - ).unwrap(), - 1u8.encode(), - ); - } - - #[test] - fn start_fn_ok() { - let mut ext = TestExternalities::default(); - let mut ext = ext.ext(); - let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; - - let code = wabt::wat2wasm(r#" - (module - (func (export "call") - ) - - (func $start - ) - - (start $start) - ) - "#).unwrap().encode(); - - assert_eq!( - call_in_wasm( - "test_sandbox_instantiate", - &code, - wasm_method, - &mut ext, - &test_code[..], - 8, - ).unwrap(), - 0u8.encode(), - ); - } - - #[test] - fn start_fn_traps() { - let mut ext = TestExternalities::default(); - let mut ext = ext.ext(); - let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; - - let code = wabt::wat2wasm(r#" - (module - (func (export "call") - ) - - (func $start - unreachable - ) - - (start $start) - ) - "#).unwrap().encode(); - - assert_eq!( - call_in_wasm( - "test_sandbox_instantiate", - &code, - wasm_method, - &mut ext, - &test_code[..], - 8, - ).unwrap(), - 2u8.encode(), - ); - } -} diff --git a/core/executor/src/wasmi_execution.rs b/core/executor/src/wasmi_execution.rs index 903ed3345c55f..b2c72041cace1 100644 --- a/core/executor/src/wasmi_execution.rs +++ b/core/executor/src/wasmi_execution.rs @@ -649,428 +649,3 @@ fn extract_data_segments(wasm_code: &[u8]) -> Result, WasmError .to_vec(); Ok(segments) } - -#[cfg(test)] -mod tests { - use super::*; - - use state_machine::TestExternalities as CoreTestExternalities; - use hex_literal::hex; - use primitives::{ - Blake2Hasher, blake2_128, blake2_256, ed25519, sr25519, map, Pair, offchain::OffchainExt, - }; - use runtime_test::WASM_BINARY; - use substrate_offchain::testing; - use trie::{TrieConfiguration, trie_types::Layout}; - use codec::{Encode, Decode}; - use crate::{WasmExecutionMethod, call_in_wasm}; - - type TestExternalities = CoreTestExternalities; - - #[test] - fn returning_should_work() { - let mut ext = TestExternalities::default(); - let mut ext = ext.ext(); - let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; - - let output = call_in_wasm( - "test_empty_return", - &[], - wasm_method, - &mut ext, - &test_code[..], - 8, - ).unwrap(); - assert_eq!(output, vec![0u8; 0]); - } - - #[test] - fn panicking_should_work() { - let mut ext = TestExternalities::default(); - let mut ext = ext.ext(); - let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; - - let output = call_in_wasm( - "test_panic", - &[], - wasm_method, - &mut ext, - &test_code[..], - 8, - ); - assert!(output.is_err()); - - let output = call_in_wasm( - "test_conditional_panic", - &[0], - wasm_method, - &mut ext, - &test_code[..], - 8, - ); - assert_eq!(Decode::decode(&mut &output.unwrap()[..]), Ok(Vec::::new())); - - let output = call_in_wasm( - "test_conditional_panic", - &vec![2].encode(), - wasm_method, - &mut ext, - &test_code[..], - 8, - ); - assert!(output.is_err()); - } - - #[test] - fn storage_should_work() { - let mut ext = TestExternalities::default(); - let wasm_method = WasmExecutionMethod::Interpreted; - - { - let mut ext = ext.ext(); - ext.set_storage(b"foo".to_vec(), b"bar".to_vec()); - let test_code = WASM_BINARY; - - let output = call_in_wasm( - "test_data_in", - &b"Hello world".to_vec().encode(), - wasm_method, - &mut ext, - &test_code[..], - 8, - ).unwrap(); - - assert_eq!(output, b"all ok!".to_vec().encode()); - } - - let expected = TestExternalities::new((map![ - b"input".to_vec() => b"Hello world".to_vec(), - b"foo".to_vec() => b"bar".to_vec(), - b"baz".to_vec() => b"bar".to_vec() - ], map![])); - assert_eq!(ext, expected); - } - - #[test] - fn clear_prefix_should_work() { - let mut ext = TestExternalities::default(); - let wasm_method = WasmExecutionMethod::Interpreted; - { - let mut ext = ext.ext(); - ext.set_storage(b"aaa".to_vec(), b"1".to_vec()); - ext.set_storage(b"aab".to_vec(), b"2".to_vec()); - ext.set_storage(b"aba".to_vec(), b"3".to_vec()); - ext.set_storage(b"abb".to_vec(), b"4".to_vec()); - ext.set_storage(b"bbb".to_vec(), b"5".to_vec()); - let test_code = WASM_BINARY; - - // This will clear all entries which prefix is "ab". - let output = call_in_wasm( - "test_clear_prefix", - &b"ab".to_vec().encode(), - wasm_method, - &mut ext, - &test_code[..], - 8, - ).unwrap(); - - assert_eq!(output, b"all ok!".to_vec().encode()); - } - - let expected = TestExternalities::new((map![ - b"aaa".to_vec() => b"1".to_vec(), - b"aab".to_vec() => b"2".to_vec(), - b"bbb".to_vec() => b"5".to_vec() - ], map![])); - assert_eq!(expected, ext); - } - - #[test] - fn blake2_256_should_work() { - let mut ext = TestExternalities::default(); - let mut ext = ext.ext(); - let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; - assert_eq!( - call_in_wasm( - "test_blake2_256", - &[0], - wasm_method, - &mut ext, - &test_code[..], - 8, - ).unwrap(), - blake2_256(&b""[..]).to_vec().encode(), - ); - assert_eq!( - call_in_wasm( - "test_blake2_256", - &b"Hello world!".to_vec().encode(), - wasm_method, - &mut ext, - &test_code[..], - 8, - ).unwrap(), - blake2_256(&b"Hello world!"[..]).to_vec().encode(), - ); - } - - #[test] - fn blake2_128_should_work() { - let mut ext = TestExternalities::default(); - let mut ext = ext.ext(); - let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; - assert_eq!( - call_in_wasm( - "test_blake2_128", - &[0], - wasm_method, - &mut ext, - &test_code[..], - 8, - ).unwrap(), - blake2_128(&b""[..]).to_vec().encode(), - ); - assert_eq!( - call_in_wasm( - "test_blake2_128", - &b"Hello world!".to_vec().encode(), - wasm_method, - &mut ext, - &test_code[..], - 8, - ).unwrap(), - blake2_128(&b"Hello world!"[..]).to_vec().encode(), - ); - } - - #[test] - fn twox_256_should_work() { - let mut ext = TestExternalities::default(); - let mut ext = ext.ext(); - let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; - assert_eq!( - call_in_wasm( - "test_twox_256", - &[0], - wasm_method, - &mut ext, - &test_code[..], - 8, - ).unwrap(), - hex!( - "99e9d85137db46ef4bbea33613baafd56f963c64b1f3685a4eb4abd67ff6203a" - ).to_vec().encode(), - ); - assert_eq!( - call_in_wasm( - "test_twox_256", - &b"Hello world!".to_vec().encode(), - wasm_method, - &mut ext, - &test_code[..], - 8, - ).unwrap(), - hex!( - "b27dfd7f223f177f2a13647b533599af0c07f68bda23d96d059da2b451a35a74" - ).to_vec().encode(), - ); - } - - #[test] - fn twox_128_should_work() { - let mut ext = TestExternalities::default(); - let mut ext = ext.ext(); - let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; - assert_eq!( - call_in_wasm( - "test_twox_128", - &[0], - wasm_method, - &mut ext, - &test_code[..], - 8, - ).unwrap(), - hex!("99e9d85137db46ef4bbea33613baafd5").to_vec().encode(), - ); - assert_eq!( - call_in_wasm( - "test_twox_128", - &b"Hello world!".to_vec().encode(), - wasm_method, - &mut ext, - &test_code[..], - 8, - ).unwrap(), - hex!("b27dfd7f223f177f2a13647b533599af").to_vec().encode(), - ); - } - - #[test] - fn ed25519_verify_should_work() { - let mut ext = TestExternalities::default(); - let mut ext = ext.ext(); - let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; - let key = ed25519::Pair::from_seed(&blake2_256(b"test")); - let sig = key.sign(b"all ok!"); - let mut calldata = vec![]; - calldata.extend_from_slice(key.public().as_ref()); - calldata.extend_from_slice(sig.as_ref()); - - assert_eq!( - call_in_wasm( - "test_ed25519_verify", - &calldata.encode(), - wasm_method, - &mut ext, - &test_code[..], - 8, - ).unwrap(), - true.encode(), - ); - - let other_sig = key.sign(b"all is not ok!"); - let mut calldata = vec![]; - calldata.extend_from_slice(key.public().as_ref()); - calldata.extend_from_slice(other_sig.as_ref()); - - assert_eq!( - call_in_wasm( - "test_ed25519_verify", - &calldata.encode(), - wasm_method, - &mut ext, - &test_code[..], - 8, - ).unwrap(), - false.encode(), - ); - } - - #[test] - fn sr25519_verify_should_work() { - let mut ext = TestExternalities::default(); - let mut ext = ext.ext(); - let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; - let key = sr25519::Pair::from_seed(&blake2_256(b"test")); - let sig = key.sign(b"all ok!"); - let mut calldata = vec![]; - calldata.extend_from_slice(key.public().as_ref()); - calldata.extend_from_slice(sig.as_ref()); - - assert_eq!( - call_in_wasm( - "test_sr25519_verify", - &calldata.encode(), - wasm_method, - &mut ext, - &test_code[..], - 8, - ).unwrap(), - true.encode(), - ); - - let other_sig = key.sign(b"all is not ok!"); - let mut calldata = vec![]; - calldata.extend_from_slice(key.public().as_ref()); - calldata.extend_from_slice(other_sig.as_ref()); - - assert_eq!( - call_in_wasm( - "test_sr25519_verify", - &calldata.encode(), - wasm_method, - &mut ext, - &test_code[..], - 8, - ).unwrap(), - false.encode(), - ); - } - - #[test] - fn ordered_trie_root_should_work() { - let mut ext = TestExternalities::default(); - let mut ext = ext.ext(); - let trie_input = vec![b"zero".to_vec(), b"one".to_vec(), b"two".to_vec()]; - let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; - assert_eq!( - call_in_wasm( - "test_ordered_trie_root", - &[0], - wasm_method, - &mut ext, - &test_code[..], - 8, - ).unwrap(), - Layout::::ordered_trie_root(trie_input.iter()).as_bytes().encode(), - ); - } - - #[test] - fn offchain_local_storage_should_work() { - use substrate_client::backend::OffchainStorage; - - let mut ext = TestExternalities::default(); - let (offchain, state) = testing::TestOffchainExt::new(); - ext.register_extension(OffchainExt::new(offchain)); - let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; - let mut ext = ext.ext(); - assert_eq!( - call_in_wasm( - "test_offchain_local_storage", - &[0], - wasm_method, - &mut ext, - &test_code[..], - 8, - ).unwrap(), - true.encode(), - ); - assert_eq!(state.read().persistent_storage.get(b"", b"test"), Some(vec![])); - } - - #[test] - fn offchain_http_should_work() { - let mut ext = TestExternalities::default(); - let (offchain, state) = testing::TestOffchainExt::new(); - ext.register_extension(OffchainExt::new(offchain)); - state.write().expect_request( - 0, - testing::PendingRequest { - method: "POST".into(), - uri: "http://localhost:12345".into(), - body: vec![1, 2, 3, 4], - headers: vec![("X-Auth".to_owned(), "test".to_owned())], - sent: true, - response: Some(vec![1, 2, 3]), - response_headers: vec![("X-Auth".to_owned(), "hello".to_owned())], - ..Default::default() - }, - ); - - let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; - let mut ext = ext.ext(); - assert_eq!( - call_in_wasm( - "test_offchain_http", - &[0], - wasm_method, - &mut ext, - &test_code[..], - 8, - ).unwrap(), - true.encode(), - ); - } -} From 36bdc5c89d196a14a28a46415627581a14f5668d Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Fri, 18 Oct 2019 16:40:33 +0200 Subject: [PATCH 03/26] executor: Use test_case macro to easily execute tests with different Wasm execution methods. --- Cargo.lock | 20 ++++++ core/executor/Cargo.toml | 1 + core/executor/src/integration_tests/mod.rs | 66 ++++++++----------- .../executor/src/integration_tests/sandbox.rs | 51 ++++++-------- 4 files changed, 69 insertions(+), 69 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4bade6122576a..e190c012a519c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5221,6 +5221,7 @@ dependencies = [ "substrate-state-machine 2.0.0", "substrate-trie 2.0.0", "substrate-wasm-interface 2.0.0", + "test-case 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "wabt 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", "wasmi 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5969,6 +5970,18 @@ dependencies = [ "wincolor 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "test-case" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "textwrap" version = "0.11.0" @@ -6485,6 +6498,11 @@ name = "version_check" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "version_check" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "void" version = "1.0.2" @@ -7262,6 +7280,7 @@ dependencies = [ "checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" "checksum termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e" +"checksum test-case 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a605baa797821796a751f4a959e1206079b24a4b7e1ed302b7d785d81a9276c9" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e2f0c90a5f3459330ac8bc0d2f879c693bb7a2f59689c1083fc4ef83834da865" @@ -7315,6 +7334,7 @@ dependencies = [ "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" "checksum vergen 3.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6aba5e34f93dc7051dfad05b98a18e9156f27e7b431fe1d2398cb6061c0a1dba" "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" +"checksum version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum wabt 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3c5c5c1286c6e578416982609f47594265f9d489f9b836157d403ad605a46693" "checksum wabt-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "af5d153dc96aad7dc13ab90835b892c69867948112d95299e522d370c4e13a08" diff --git a/core/executor/Cargo.toml b/core/executor/Cargo.toml index cf3ea4ff7a6e7..74ef40292faca 100644 --- a/core/executor/Cargo.toml +++ b/core/executor/Cargo.toml @@ -31,6 +31,7 @@ runtime-test = { package = "substrate-runtime-test", path = "runtime-test" } substrate-client = { path = "../client" } substrate-offchain = { path = "../offchain/" } state_machine = { package = "substrate-state-machine", path = "../state-machine" } +test-case = "0.3.3" [features] default = [] diff --git a/core/executor/src/integration_tests/mod.rs b/core/executor/src/integration_tests/mod.rs index ca3e36461b98a..8d769bb677701 100644 --- a/core/executor/src/integration_tests/mod.rs +++ b/core/executor/src/integration_tests/mod.rs @@ -25,6 +25,7 @@ use primitives::{ use runtime_test::WASM_BINARY; use state_machine::TestExternalities as CoreTestExternalities; use substrate_offchain::testing; +use test_case::test_case; use trie::{TrieConfiguration, trie_types::Layout}; use crate::{WasmExecutionMethod, call_in_wasm}; @@ -32,12 +33,11 @@ use crate::error::Error; pub type TestExternalities = CoreTestExternalities; -#[test] -fn returning_should_work() { +#[test_case(WasmExecutionMethod::Interpreted)] +fn returning_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; let output = call_in_wasm( "test_empty_return", @@ -50,12 +50,11 @@ fn returning_should_work() { assert_eq!(output, vec![0u8; 0]); } -#[test] -fn panicking_should_work() { +#[test_case(WasmExecutionMethod::Interpreted)] +fn panicking_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; let output = call_in_wasm( "test_panic", @@ -88,10 +87,9 @@ fn panicking_should_work() { assert!(output.is_err()); } -#[test] -fn storage_should_work() { +#[test_case(WasmExecutionMethod::Interpreted)] +fn storage_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); - let wasm_method = WasmExecutionMethod::Interpreted; { let mut ext = ext.ext(); @@ -118,10 +116,9 @@ fn storage_should_work() { assert_eq!(ext, expected); } -#[test] -fn clear_prefix_should_work() { +#[test_case(WasmExecutionMethod::Interpreted)] +fn clear_prefix_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); - let wasm_method = WasmExecutionMethod::Interpreted; { let mut ext = ext.ext(); ext.set_storage(b"aaa".to_vec(), b"1".to_vec()); @@ -152,12 +149,11 @@ fn clear_prefix_should_work() { assert_eq!(expected, ext); } -#[test] -fn blake2_256_should_work() { +#[test_case(WasmExecutionMethod::Interpreted)] +fn blake2_256_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; assert_eq!( call_in_wasm( "test_blake2_256", @@ -182,12 +178,11 @@ fn blake2_256_should_work() { ); } -#[test] -fn blake2_128_should_work() { +#[test_case(WasmExecutionMethod::Interpreted)] +fn blake2_128_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; assert_eq!( call_in_wasm( "test_blake2_128", @@ -212,12 +207,11 @@ fn blake2_128_should_work() { ); } -#[test] -fn twox_256_should_work() { +#[test_case(WasmExecutionMethod::Interpreted)] +fn twox_256_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; assert_eq!( call_in_wasm( "test_twox_256", @@ -246,12 +240,11 @@ fn twox_256_should_work() { ); } -#[test] -fn twox_128_should_work() { +#[test_case(WasmExecutionMethod::Interpreted)] +fn twox_128_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; assert_eq!( call_in_wasm( "test_twox_128", @@ -276,12 +269,11 @@ fn twox_128_should_work() { ); } -#[test] -fn ed25519_verify_should_work() { +#[test_case(WasmExecutionMethod::Interpreted)] +fn ed25519_verify_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; let key = ed25519::Pair::from_seed(&blake2_256(b"test")); let sig = key.sign(b"all ok!"); let mut calldata = vec![]; @@ -318,12 +310,11 @@ fn ed25519_verify_should_work() { ); } -#[test] -fn sr25519_verify_should_work() { +#[test_case(WasmExecutionMethod::Interpreted)] +fn sr25519_verify_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; let key = sr25519::Pair::from_seed(&blake2_256(b"test")); let sig = key.sign(b"all ok!"); let mut calldata = vec![]; @@ -360,13 +351,12 @@ fn sr25519_verify_should_work() { ); } -#[test] -fn ordered_trie_root_should_work() { +#[test_case(WasmExecutionMethod::Interpreted)] +fn ordered_trie_root_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let trie_input = vec![b"zero".to_vec(), b"one".to_vec(), b"two".to_vec()]; let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; assert_eq!( call_in_wasm( "test_ordered_trie_root", @@ -380,15 +370,14 @@ fn ordered_trie_root_should_work() { ); } -#[test] -fn offchain_local_storage_should_work() { +#[test_case(WasmExecutionMethod::Interpreted)] +fn offchain_local_storage_should_work(wasm_method: WasmExecutionMethod) { use substrate_client::backend::OffchainStorage; let mut ext = TestExternalities::default(); let (offchain, state) = testing::TestOffchainExt::new(); ext.register_extension(OffchainExt::new(offchain)); let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; let mut ext = ext.ext(); assert_eq!( call_in_wasm( @@ -404,8 +393,8 @@ fn offchain_local_storage_should_work() { assert_eq!(state.read().persistent_storage.get(b"", b"test"), Some(vec![])); } -#[test] -fn offchain_http_should_work() { +#[test_case(WasmExecutionMethod::Interpreted)] +fn offchain_http_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let (offchain, state) = testing::TestOffchainExt::new(); ext.register_extension(OffchainExt::new(offchain)); @@ -424,7 +413,6 @@ fn offchain_http_should_work() { ); let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; let mut ext = ext.ext(); assert_eq!( call_in_wasm( diff --git a/core/executor/src/integration_tests/sandbox.rs b/core/executor/src/integration_tests/sandbox.rs index 29aab8dddb8ae..e6f9f217a238d 100644 --- a/core/executor/src/integration_tests/sandbox.rs +++ b/core/executor/src/integration_tests/sandbox.rs @@ -16,18 +16,18 @@ use codec::Encode; use runtime_test::WASM_BINARY; +use test_case::test_case; use wabt; use crate::{WasmExecutionMethod, call_in_wasm}; use crate::error::Error; use crate::integration_tests::TestExternalities; -#[test] -fn sandbox_should_work() { +#[test_case(WasmExecutionMethod::Interpreted)] +fn sandbox_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; let code = wabt::wat2wasm(r#" (module @@ -63,12 +63,11 @@ fn sandbox_should_work() { ); } -#[test] -fn sandbox_trap() { +#[test_case(WasmExecutionMethod::Interpreted)] +fn sandbox_trap(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; let code = wabt::wat2wasm(r#" (module @@ -93,12 +92,11 @@ fn sandbox_trap() { ); } -#[test] -fn sandbox_should_trap_when_heap_exhausted() { +#[test_case(WasmExecutionMethod::Interpreted)] +fn sandbox_should_trap_when_heap_exhausted(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; let code = wabt::wat2wasm(r#" (module @@ -130,12 +128,11 @@ fn sandbox_should_trap_when_heap_exhausted() { } } -#[test] -fn start_called() { +#[test_case(WasmExecutionMethod::Interpreted)] +fn start_called(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; let code = wabt::wat2wasm(r#" (module @@ -177,12 +174,11 @@ fn start_called() { ); } -#[test] -fn invoke_args() { +#[test_case(WasmExecutionMethod::Interpreted)] +fn invoke_args(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; let code = wabt::wat2wasm(r#" (module @@ -220,12 +216,11 @@ fn invoke_args() { ); } -#[test] -fn return_val() { +#[test_case(WasmExecutionMethod::Interpreted)] +fn return_val(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; let code = wabt::wat2wasm(r#" (module @@ -251,12 +246,11 @@ fn return_val() { ); } -#[test] -fn unlinkable_module() { +#[test_case(WasmExecutionMethod::Interpreted)] +fn unlinkable_module(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; let code = wabt::wat2wasm(r#" (module @@ -280,12 +274,11 @@ fn unlinkable_module() { ); } -#[test] -fn corrupted_module() { +#[test_case(WasmExecutionMethod::Interpreted)] +fn corrupted_module(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; // Corrupted wasm file let code = vec![0u8, 0, 0, 0, 1, 0, 0, 0].encode(); @@ -303,12 +296,11 @@ fn corrupted_module() { ); } -#[test] -fn start_fn_ok() { +#[test_case(WasmExecutionMethod::Interpreted)] +fn start_fn_ok(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; let code = wabt::wat2wasm(r#" (module @@ -335,12 +327,11 @@ fn start_fn_ok() { ); } -#[test] -fn start_fn_traps() { +#[test_case(WasmExecutionMethod::Interpreted)] +fn start_fn_traps(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); let test_code = WASM_BINARY; - let wasm_method = WasmExecutionMethod::Interpreted; let code = wabt::wat2wasm(r#" (module From 727ce438950ac0ad4cd21836de6ff473ea61b97f Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Mon, 21 Oct 2019 11:55:30 +0200 Subject: [PATCH 04/26] executor: Convert errors to strings with Display, not Debug. --- core/executor/src/integration_tests/mod.rs | 1 - .../executor/src/integration_tests/sandbox.rs | 11 ++-------- core/executor/src/wasmi_execution.rs | 20 +++++++++---------- 3 files changed, 12 insertions(+), 20 deletions(-) diff --git a/core/executor/src/integration_tests/mod.rs b/core/executor/src/integration_tests/mod.rs index 8d769bb677701..4041fe5cf88ba 100644 --- a/core/executor/src/integration_tests/mod.rs +++ b/core/executor/src/integration_tests/mod.rs @@ -29,7 +29,6 @@ use test_case::test_case; use trie::{TrieConfiguration, trie_types::Layout}; use crate::{WasmExecutionMethod, call_in_wasm}; -use crate::error::Error; pub type TestExternalities = CoreTestExternalities; diff --git a/core/executor/src/integration_tests/sandbox.rs b/core/executor/src/integration_tests/sandbox.rs index e6f9f217a238d..621635e6b2dc4 100644 --- a/core/executor/src/integration_tests/sandbox.rs +++ b/core/executor/src/integration_tests/sandbox.rs @@ -20,7 +20,6 @@ use test_case::test_case; use wabt; use crate::{WasmExecutionMethod, call_in_wasm}; -use crate::error::Error; use crate::integration_tests::TestExternalities; #[test_case(WasmExecutionMethod::Interpreted)] @@ -116,15 +115,9 @@ fn sandbox_should_trap_when_heap_exhausted(wasm_method: WasmExecutionMethod) { &test_code[..], 8, ); - assert_eq!(res.is_err(), true); + assert!(res.is_err()); if let Err(err) = res { - assert_eq!( - format!("{}", err), - format!( - "{}", - wasmi::Error::Trap(Error::FunctionExecution("AllocatorOutOfSpace".into()).into()), - ), - ); + assert!(err.to_string().contains("Allocator ran out of space")); } } diff --git a/core/executor/src/wasmi_execution.rs b/core/executor/src/wasmi_execution.rs index b2c72041cace1..eae54625c8509 100644 --- a/core/executor/src/wasmi_execution.rs +++ b/core/executor/src/wasmi_execution.rs @@ -110,24 +110,24 @@ impl sandbox::SandboxCapabilities for FunctionExecutor { impl FunctionContext for FunctionExecutor { fn read_memory_into(&self, address: Pointer, dest: &mut [u8]) -> WResult<()> { - self.memory.get_into(address.into(), dest).map_err(|e| format!("{:?}", e)) + self.memory.get_into(address.into(), dest).map_err(|e| e.to_string()) } fn write_memory(&mut self, address: Pointer, data: &[u8]) -> WResult<()> { - self.memory.set(address.into(), data).map_err(|e| format!("{:?}", e)) + self.memory.set(address.into(), data).map_err(|e| e.to_string()) } fn allocate_memory(&mut self, size: WordSize) -> WResult> { let heap = &mut self.heap; self.memory.with_direct_access_mut(|mem| { - heap.allocate(mem, size).map_err(|e| format!("{:?}", e)) + heap.allocate(mem, size).map_err(|e| e.to_string()) }) } fn deallocate_memory(&mut self, ptr: Pointer) -> WResult<()> { let heap = &mut self.heap; self.memory.with_direct_access_mut(|mem| { - heap.deallocate(mem, ptr).map_err(|e| format!("{:?}", e)) + heap.deallocate(mem, ptr).map_err(|e| e.to_string()) }) } @@ -144,7 +144,7 @@ impl Sandbox for FunctionExecutor { buf_ptr: Pointer, buf_len: WordSize, ) -> WResult { - let sandboxed_memory = self.sandbox_store.memory(memory_id).map_err(|e| format!("{:?}", e))?; + let sandboxed_memory = self.sandbox_store.memory(memory_id).map_err(|e| e.to_string())?; match MemoryInstance::transfer( &sandboxed_memory, @@ -165,7 +165,7 @@ impl Sandbox for FunctionExecutor { val_ptr: Pointer, val_len: WordSize, ) -> WResult { - let sandboxed_memory = self.sandbox_store.memory(memory_id).map_err(|e| format!("{:?}", e))?; + let sandboxed_memory = self.sandbox_store.memory(memory_id).map_err(|e| e.to_string())?; match MemoryInstance::transfer( &self.memory, @@ -180,7 +180,7 @@ impl Sandbox for FunctionExecutor { } fn memory_teardown(&mut self, memory_id: MemoryId) -> WResult<()> { - self.sandbox_store.memory_teardown(memory_id).map_err(|e| format!("{:?}", e)) + self.sandbox_store.memory_teardown(memory_id).map_err(|e| e.to_string()) } fn memory_new( @@ -188,7 +188,7 @@ impl Sandbox for FunctionExecutor { initial: u32, maximum: u32, ) -> WResult { - self.sandbox_store.new_memory(initial, maximum).map_err(|e| format!("{:?}", e)) + self.sandbox_store.new_memory(initial, maximum).map_err(|e| e.to_string()) } fn invoke( @@ -209,7 +209,7 @@ impl Sandbox for FunctionExecutor { .map(Into::into) .collect::>(); - let instance = self.sandbox_store.instance(instance_id).map_err(|e| format!("{:?}", e))?; + let instance = self.sandbox_store.instance(instance_id).map_err(|e| e.to_string())?; let result = instance.invoke(export_name, &args, self, state); match result { @@ -229,7 +229,7 @@ impl Sandbox for FunctionExecutor { } fn instance_teardown(&mut self, instance_id: u32) -> WResult<()> { - self.sandbox_store.instance_teardown(instance_id).map_err(|e| format!("{:?}", e)) + self.sandbox_store.instance_teardown(instance_id).map_err(|e| e.to_string()) } fn instance_new( From 1bcb472d39da39bd6abef8097f09becd50450356 Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Mon, 21 Oct 2019 12:55:07 +0200 Subject: [PATCH 05/26] node-executor: Rewrite benchmarks with criterion. They were not passing compilation before and criterion seems to be more widely used in Substrate. --- Cargo.lock | 1 + node/executor/Cargo.toml | 7 +- node/executor/benches/bench.rs | 194 +++++++++++++++++++++++++++++++++ node/executor/src/lib.rs | 21 ---- 4 files changed, 201 insertions(+), 22 deletions(-) create mode 100644 node/executor/benches/bench.rs diff --git a/Cargo.lock b/Cargo.lock index e190c012a519c..2ebf9284b8bdc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2432,6 +2432,7 @@ dependencies = [ name = "node-executor" version = "2.0.0" dependencies = [ + "criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "node-primitives 2.0.0", "node-runtime 2.0.0", "node-testing 2.0.0", diff --git a/node/executor/Cargo.toml b/node/executor/Cargo.toml index 1908443ca9519..ebde7f434bffb 100644 --- a/node/executor/Cargo.toml +++ b/node/executor/Cargo.toml @@ -31,7 +31,12 @@ contracts = { package = "srml-contracts", path = "../../srml/contracts" } grandpa = { package = "srml-grandpa", path = "../../srml/grandpa" } indices = { package = "srml-indices", path = "../../srml/indices" } wabt = "0.9.2" +criterion = "0.3.0" [features] -benchmarks = [] stress-test = [] + +[[bench]] +name = "bench" +harness = false + diff --git a/node/executor/benches/bench.rs b/node/executor/benches/bench.rs new file mode 100644 index 0000000000000..19c53b2428f4a --- /dev/null +++ b/node/executor/benches/bench.rs @@ -0,0 +1,194 @@ +// Copyright 2018-2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +use codec::{Decode, Encode}; +use criterion::{BatchSize, Criterion, criterion_group, criterion_main}; +use node_executor::Executor; +use node_primitives::{BlockNumber, Hash}; +use node_runtime::{ + Block, BuildStorage, Call, CheckedExtrinsic, GenesisConfig, Header, UncheckedExtrinsic, +}; +use node_runtime::constants::currency::*; +use node_testing::keyring::*; +use primitives::{Blake2Hasher, NativeOrEncoded, NeverNativeValue}; +use primitives::storage::well_known_keys; +use primitives::traits::CodeExecutor; +use runtime_support::Hashable; +use state_machine::TestExternalities as CoreTestExternalities; +use substrate_executor::{NativeExecutor, RuntimeInfo, WasmExecutionMethod, Externalities}; + +criterion_group!(benches, bench_execute_block); +criterion_main!(benches); + +/// The wasm runtime code. +const COMPACT_CODE: &[u8] = node_runtime::WASM_BINARY; + +const GENESIS_HASH: [u8; 32] = [69u8; 32]; + +const VERSION: u32 = node_runtime::VERSION.spec_version; + +const HEAP_PAGES: u64 = 20; + +type TestExternalities = CoreTestExternalities; + +#[derive(Debug)] +enum ExecutionMethod { + Native, + Wasm(WasmExecutionMethod), +} + +fn sign(xt: CheckedExtrinsic) -> UncheckedExtrinsic { + node_testing::keyring::sign(xt, VERSION, GENESIS_HASH) +} + +fn new_test_ext(genesis_config: &GenesisConfig) -> TestExternalities { + let mut test_ext = TestExternalities::new_with_code( + COMPACT_CODE, + genesis_config.build_storage().unwrap(), + ); + test_ext.ext().place_storage(well_known_keys::HEAP_PAGES.to_vec(), Some(HEAP_PAGES.encode())); + test_ext +} + +fn construct_block( + executor: &NativeExecutor, + ext: &mut E, + number: BlockNumber, + parent_hash: Hash, + extrinsics: Vec, +) -> (Vec, Hash) { + use trie::{TrieConfiguration, trie_types::Layout}; + + // sign extrinsics. + let extrinsics = extrinsics.into_iter().map(sign).collect::>(); + + // calculate the header fields that we can. + let extrinsics_root = Layout::::ordered_trie_root( + extrinsics.iter().map(Encode::encode) + ).to_fixed_bytes() + .into(); + + let header = Header { + parent_hash, + number, + extrinsics_root, + state_root: Default::default(), + digest: Default::default(), + }; + + // execute the block to get the real header. + executor.call::<_, NeverNativeValue, fn() -> _>( + ext, + "Core_initialize_block", + &header.encode(), + true, + None, + ).0.unwrap(); + + for i in extrinsics.iter() { + executor.call::<_, NeverNativeValue, fn() -> _>( + ext, + "BlockBuilder_apply_extrinsic", + &i.encode(), + true, + None, + ).0.unwrap(); + } + + let header = match executor.call::<_, NeverNativeValue, fn() -> _>( + ext, + "BlockBuilder_finalize_block", + &[0u8;0], + true, + None, + ).0.unwrap() { + NativeOrEncoded::Native(_) => unreachable!(), + NativeOrEncoded::Encoded(h) => Header::decode(&mut &h[..]).unwrap(), + }; + + let hash = header.blake2_256(); + (Block { header, extrinsics }.encode(), hash.into()) +} + + +fn test_blocks(genesis_config: &GenesisConfig, executor: &NativeExecutor) + -> Vec<(Vec, Hash)> +{ + let mut test_ext = new_test_ext(genesis_config); + let mut block1_extrinsics = vec![ + CheckedExtrinsic { + signed: None, + function: Call::Timestamp(timestamp::Call::set(42 * 1000)), + }, + ]; + block1_extrinsics.extend((0..20).map(|i| { + CheckedExtrinsic { + signed: Some((alice(), signed_extra(i, 0))), + function: Call::Balances(balances::Call::transfer(bob().into(), 1 * DOLLARS)), + } + })); + let block1 = construct_block( + executor, + &mut test_ext.ext(), + 1, + GENESIS_HASH.into(), + block1_extrinsics, + ); + + vec![block1] +} + +fn bench_execute_block(c: &mut Criterion) { + c.bench_function_over_inputs( + "execute blocks", + |b, strategy| { + let genesis_config = node_testing::genesis::config(false, Some(COMPACT_CODE)); + let (use_native, wasm_method) = match strategy { + ExecutionMethod::Native => (true, WasmExecutionMethod::Interpreted), + ExecutionMethod::Wasm(wasm_method) => (false, *wasm_method), + }; + let executor = NativeExecutor::new(wasm_method, None); + + // Get the runtime version to initialize the runtimes cache. + { + let mut test_ext = new_test_ext(&genesis_config); + executor.runtime_version(&mut test_ext.ext()); + } + + let blocks = test_blocks(&genesis_config, &executor); + + b.iter_batched_ref( + || new_test_ext(&genesis_config), + |test_ext| { + for block in blocks.iter() { + executor.call::<_, NeverNativeValue, fn() -> _>( + &mut test_ext.ext(), + "Core_execute_block", + &block.0, + use_native, + None, + ).0.unwrap(); + } + }, + BatchSize::LargeInput, + ); + }, + vec![ + ExecutionMethod::Native, + ExecutionMethod::Wasm(WasmExecutionMethod::Interpreted), + ], + ); +} diff --git a/node/executor/src/lib.rs b/node/executor/src/lib.rs index 39727d1d5503e..fe43fc8ff0ba4 100644 --- a/node/executor/src/lib.rs +++ b/node/executor/src/lib.rs @@ -17,10 +17,6 @@ //! A `CodeExecutor` specialization which uses natively compiled runtime when the wasm to be //! executed is equivalent to the natively compiled code. -#![cfg_attr(feature = "benchmarks", feature(test))] - -#[cfg(feature = "benchmarks")] extern crate test; - pub use substrate_executor::NativeExecutor; use substrate_executor::native_executor_instance; @@ -1208,21 +1204,4 @@ mod tests { block_number += 1; } } - - #[cfg(feature = "benchmarks")] - mod benches { - use super::*; - use test::Bencher; - - #[bench] - fn wasm_execute_block(b: &mut Bencher) { - let (block1, block2) = blocks(); - - b.iter(|| { - let mut t = new_test_ext(COMPACT_CODE, false); - WasmExecutor::new().call(&mut t, "Core_execute_block", &block1.0).unwrap(); - WasmExecutor::new().call(&mut t, "Core_execute_block", &block2.0).unwrap(); - }); - } - } } From 3908f766ffe85fe312b29d382933613ea54073e1 Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Mon, 21 Oct 2019 15:37:41 +0200 Subject: [PATCH 06/26] executor: Begin implementation of Wasm runtime. The implementation demonstrates the outline of the execution, but does not link against the external host functions. --- Cargo.lock | 445 ++++++++++++++++++++++++++ core/executor/Cargo.toml | 19 ++ core/executor/src/error.rs | 17 +- core/executor/src/lib.rs | 2 + core/executor/src/wasmtime/mod.rs | 22 ++ core/executor/src/wasmtime/runtime.rs | 244 ++++++++++++++ core/executor/src/wasmtime/util.rs | 88 +++++ 7 files changed, 836 insertions(+), 1 deletion(-) create mode 100644 core/executor/src/wasmtime/mod.rs create mode 100644 core/executor/src/wasmtime/runtime.rs create mode 100644 core/executor/src/wasmtime/util.rs diff --git a/Cargo.lock b/Cargo.lock index 2ebf9284b8bdc..6ad0cff53bdc6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -188,6 +188,16 @@ dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "bincode" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "bindgen" version = "0.47.3" @@ -244,6 +254,16 @@ dependencies = [ "constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "blake2b_simd" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "block-buffer" version = "0.2.0" @@ -505,6 +525,89 @@ name = "core-foundation-sys" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "cranelift-bforest" +version = "0.46.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cranelift-entity 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cranelift-codegen" +version = "0.46.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cranelift-bforest 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-codegen-meta 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-codegen-shared 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-entity 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "target-lexicon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.46.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cranelift-codegen-shared 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-entity 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.46.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cranelift-entity" +version = "0.46.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cranelift-frontend" +version = "0.46.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cranelift-codegen 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "target-lexicon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cranelift-native" +version = "0.46.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cranelift-codegen 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "raw-cpuid 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "target-lexicon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cranelift-wasm" +version = "0.46.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cranelift-codegen 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-entity 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-frontend 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.39.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "crc32fast" version = "1.2.0" @@ -759,6 +862,26 @@ dependencies = [ "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "directories" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "dirs-sys" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_users 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "dns-parser" version = "0.8.0" @@ -847,6 +970,25 @@ dependencies = [ "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "errno" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "errno-dragonfly 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "error-chain" version = "0.12.1" @@ -865,6 +1007,20 @@ dependencies = [ "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "faerie" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "goblin 0.0.24 (registry+https://github.com/rust-lang/crates.io-index)", + "indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "scroll 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "string-interner 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "target-lexicon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "failure" version = "0.1.6" @@ -890,6 +1046,11 @@ name = "fake-simd" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "fdlimit" version = "0.1.1" @@ -898,6 +1059,15 @@ dependencies = [ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "file-per-thread-logger" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "finality-grandpa" version = "0.9.0" @@ -1155,6 +1325,18 @@ dependencies = [ "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "gimli" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "fallible-iterator 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "glob" version = "0.2.11" @@ -1177,6 +1359,16 @@ dependencies = [ "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "goblin" +version = "0.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "scroll 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "h2" version = "0.1.26" @@ -2186,6 +2378,14 @@ dependencies = [ "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "mach" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "malloc_size_of_derive" version = "0.1.0" @@ -3023,6 +3223,11 @@ name = "pkg-config" version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "ppv-lite86" version = "0.2.5" @@ -3396,6 +3601,16 @@ dependencies = [ "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "raw-cpuid" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rayon" version = "1.2.0" @@ -3431,6 +3646,17 @@ name = "redox_syscall" version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "redox_users" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-argon2 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ref_thread_local" version = "0.0.0" @@ -3460,6 +3686,17 @@ name = "regex-syntax" version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "region" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "mach 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "remove_dir_all" version = "0.5.2" @@ -3510,6 +3747,16 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rust-argon2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rustc-demangle" version = "0.1.16" @@ -3622,6 +3869,25 @@ name = "scopeguard" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "scroll" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "scroll_derive 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "scroll_derive" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "sct" version = "0.5.0" @@ -4713,6 +4979,14 @@ dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "string-interner" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "strsim" version = "0.8.0" @@ -5202,6 +5476,11 @@ name = "substrate-executor" version = "2.0.0" dependencies = [ "assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-codegen 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-entity 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-frontend 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-native 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-wasm 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", "derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", "hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5226,6 +5505,9 @@ dependencies = [ "tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "wabt 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", "wasmi 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmtime-environ 0.2.0 (git+https://github.com/jimpo/wasmtime.git?branch=context-compile)", + "wasmtime-jit 0.2.0 (git+https://github.com/jimpo/wasmtime.git?branch=context-compile)", + "wasmtime-runtime 0.2.0 (git+https://github.com/jimpo/wasmtime.git?branch=context-compile)", ] [[package]] @@ -5936,6 +6218,16 @@ name = "take_mut" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "target-lexicon" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "target_info" version = "0.1.0" @@ -6675,6 +6967,95 @@ dependencies = [ "parity-wasm 0.40.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "wasmparser" +version = "0.39.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "wasmtime-debug" +version = "0.2.0" +source = "git+https://github.com/jimpo/wasmtime.git?branch=context-compile#754dd01be66d58fff6b352f1c212e8986e143f1e" +dependencies = [ + "cranelift-codegen 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-entity 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-wasm 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "faerie 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "gimli 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", + "target-lexicon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.39.2 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmtime-environ 0.2.0 (git+https://github.com/jimpo/wasmtime.git?branch=context-compile)", +] + +[[package]] +name = "wasmtime-environ" +version = "0.2.0" +source = "git+https://github.com/jimpo/wasmtime.git?branch=context-compile#754dd01be66d58fff6b352f1c212e8986e143f1e" +dependencies = [ + "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-codegen 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-entity 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-wasm 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "directories 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "file-per-thread-logger 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "zstd 0.4.28+zstd.1.4.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasmtime-jit" +version = "0.2.0" +source = "git+https://github.com/jimpo/wasmtime.git?branch=context-compile#754dd01be66d58fff6b352f1c212e8986e143f1e" +dependencies = [ + "cranelift-codegen 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-entity 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-frontend 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-wasm 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "region 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "target-lexicon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.39.2 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmtime-debug 0.2.0 (git+https://github.com/jimpo/wasmtime.git?branch=context-compile)", + "wasmtime-environ 0.2.0 (git+https://github.com/jimpo/wasmtime.git?branch=context-compile)", + "wasmtime-runtime 0.2.0 (git+https://github.com/jimpo/wasmtime.git?branch=context-compile)", +] + +[[package]] +name = "wasmtime-runtime" +version = "0.2.0" +source = "git+https://github.com/jimpo/wasmtime.git?branch=context-compile#754dd01be66d58fff6b352f1c212e8986e143f1e" +dependencies = [ + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-codegen 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-entity 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-wasm 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "region 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmtime-environ 0.2.0 (git+https://github.com/jimpo/wasmtime.git?branch=context-compile)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "web-sys" version = "0.3.28" @@ -6877,6 +7258,33 @@ dependencies = [ "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "zstd" +version = "0.4.28+zstd.1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "zstd-safe 1.4.13+zstd.1.4.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "zstd-safe" +version = "1.4.13+zstd.1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "zstd-sys 1.4.13+zstd.1.4.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "zstd-sys" +version = "1.4.13+zstd.1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", + "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", +] + [metadata] "checksum adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" "checksum aes-ctr 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d2e5b0458ea3beae0d1d8c0f3946564f8e10f90646cf78c06b4351052058d1ee" @@ -6901,12 +7309,14 @@ dependencies = [ "checksum base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83" "checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" "checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" +"checksum bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8ab639324e3ee8774d296864fbc0dbbb256cf1a41c490b94cba90c082915f92" "checksum bindgen 0.47.3 (registry+https://github.com/rust-lang/crates.io-index)" = "df683a55b54b41d5ea8ebfaebb5aa7e6b84e3f3006a78f010dadc9ca88469260" "checksum bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a606a02debe2813760609f57a64a2ffd27d9fdf5b2f133eaca0b248dd92cdd2" "checksum bitmask 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5da9b3d9f6f585199287a473f4f8dfab6566cf827d15c00c219f53c645687ead" "checksum bitvec 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9633b74910e1870f50f5af189b08487195cdb83c0e27a71d6f64d5e09dd0538b" "checksum blake2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "94cb07b0da6a73955f8fb85d24c466778e70cda767a568229b104f0264089330" "checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" +"checksum blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "5850aeee1552f495dd0250014cf64b82b7c8879a89d83b33bbdace2cc4f63182" "checksum block-buffer 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1339a1042f5d9f295737ad4d9a6ab6bf81c84a933dba110b9200cd6d1448b814" "checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" "checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" @@ -6939,6 +7349,14 @@ dependencies = [ "checksum constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "995a44c877f9212528ccc74b21a232f66ad69001e40ede5bcee2ac9ef2657120" "checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" "checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" +"checksum cranelift-bforest 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)" = "18c97588946d3e5fe11f8e34ebf8cc65fd3fda50f3ffa2e80c98b2748058f00f" +"checksum cranelift-codegen 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3255935da50302bcb0f7109f2fef27f44b46f1c797dfa7db971379261023adcd" +"checksum cranelift-codegen-meta 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd57265ef5e6ff253c378b6261ed8c2e6cb1b15e91624540dbd09b1e5a40e9ca" +"checksum cranelift-codegen-shared 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c093398d21f9493ab29445191362592ef621f497e56a8efb15bdf80471978b7a" +"checksum cranelift-entity 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e915fa58d2a75e3c4b768b7e4760282889915c3fcd9ccb2ad2b3ebec99654a78" +"checksum cranelift-frontend 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)" = "46963952cda267bd0177b3f036e50038cd56e7b4c5b09a455b02df727e0f2a16" +"checksum cranelift-native 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7ba8a2d69ddd4729199a321bc2f4020e1969a088b468ed6a29dc7a69350be76e" +"checksum cranelift-wasm 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5a802357a6a016bf4c1dcdc6d73a650640eb3b613cc098a1a044a6c3731ca264" "checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" "checksum criterion 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0363053954f3e679645fc443321ca128b7b950a6fe288cf5f9335cc22ee58394" "checksum criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "938703e165481c8d612ea3479ac8342e5615185db37765162e762ec3523e2fc6" @@ -6964,6 +7382,8 @@ dependencies = [ "checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" "checksum digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e5b29bf156f3f4b3c4f610a25ff69370616ae6e0657d416de22645483e72af0a" "checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +"checksum directories 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "551a778172a450d7fc12e629ca3b0428d00f6afa9a43da1b630d54604e97371c" +"checksum dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" "checksum dns-parser 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c4d33be9473d06f75f58220f71f7a9317aca647dc061dbd3c361b0bef505fbea" "checksum doc-comment 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "923dea538cea0aa3025e8685b20d6ee21ef99c4f77e954a30febbaac5ec73a97" "checksum ed25519-dalek 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d07e8b8a8386c3b89a7a4b329fdfa4cb545de2545e9e2ebbc3dd3929253e426" @@ -6974,12 +7394,17 @@ dependencies = [ "checksum env_logger 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "39ecdb7dd54465526f0a56d666e3b2dd5f3a218665a030b6e4ad9e70fa95d8fa" "checksum environmental 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "34f8467a0284de039e6bd0e25c14519538462ba5beb548bb1f03e645097837a8" "checksum erased-serde 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3beee4bc16478a1b26f2e80ad819a52d24745e292f521a63c16eea5f74b7eb60" +"checksum errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2a071601ed01b988f896ab14b95e67335d1eeb50190932a1320f7fe3cadc84e" +"checksum errno-dragonfly 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "14ca354e36190500e1e1fb267c647932382b54053c50b14970856c0b00a35067" "checksum error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3ab49e9dcb602294bc42f9a7dfc9bc6e936fca4418ea300dbfb84fe16de0b7d9" "checksum exit-future 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d8013f441e38e31c670e7f34ec8f1d5d3a2bd9d303c1ff83976ca886005e8f48" +"checksum faerie 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "875d78b92b2a4d9e1e2c7eeccfa30a327d2ee6434db3beb8fd6fd92f41898bc4" "checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" "checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" +"checksum fallible-iterator 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" "checksum fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1ee15a7050e5580b3712877157068ea713b245b080ff302ae2ca973cfcd9baa" +"checksum file-per-thread-logger 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8505b75b31ef7285168dd237c4a7db3c1f3e0927e7d314e670bc98e854272fe9" "checksum finality-grandpa 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9681c1f75941ea47584573dd2bc10558b2067d460612945887e00744e43393be" "checksum fixed-hash 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "516877b7b9a1cc2d0293cbce23cd6203f0edbfd4090e6ca4489fecb5aa73050e" "checksum fixed-hash 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6357b15872f8126e4ea7cf79d579473f132ccd2de239494ad1bf4aa892faea68" @@ -7009,9 +7434,11 @@ dependencies = [ "checksum get_if_addrs 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "abddb55a898d32925f3148bd281174a68eeb68bbfd9a5938a57b18f506ee4ef7" "checksum get_if_addrs-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0d04f9fb746cf36b191c00f3ede8bde9c8e64f9f4b05ae2694a9ccf5e3f5ab48" "checksum getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "473a1265acc8ff1e808cd0a1af8cee3c2ee5200916058a2ca113c29f2d903571" +"checksum gimli 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "162d18ae5f2e3b90a993d202f1ba17a5633c2484426f8bcae201f86194bacd00" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" "checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" "checksum globset 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "925aa2cac82d8834e2b2a4415b6f6879757fb5c0928fc445ae76461a12eed8f2" +"checksum goblin 0.0.24 (registry+https://github.com/rust-lang/crates.io-index)" = "e3fa261d919c1ae9d1e4533c4a2f99e10938603c4208d56c05bec7a872b661b0" "checksum h2 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462" "checksum hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" "checksum hash256-std-hasher 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "92c171d55b98633f4ed3860808f004099b36c1cc29c42cfc53aa8591b21efcf2" @@ -7100,6 +7527,7 @@ dependencies = [ "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +"checksum mach 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "86dd2487cdfea56def77b88438a2c915fb45113c5319bfe7e14306ca4cd0b0e1" "checksum malloc_size_of_derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "35adee9ed962cf7d07d62cb58bc45029f3227f5b5b86246caa8632f06c187bc3" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" @@ -7163,6 +7591,7 @@ dependencies = [ "checksum petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f" "checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" "checksum pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "72d5370d90f49f70bd033c3d75e87fc529fbfff9d6f7cccef07d6170079d91ea" +"checksum plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" "checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b" "checksum pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427" "checksum primitive-types 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "83ef7b3b965c0eadcb6838f34f827e1dfb2939bdd5ebd43f9647e009b12b0371" @@ -7203,19 +7632,23 @@ dependencies = [ "checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" "checksum rand_xoshiro 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "03b418169fb9c46533f326efd6eed2576699c44ca92d3052a066214a8d828929" "checksum rand_xoshiro 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e18c91676f670f6f0312764c759405f13afb98d5d73819840cf72a518487bff" +"checksum raw-cpuid 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "30a9d219c32c9132f7be513c18be77c9881c7107d2ab5569d205a6a0f0e6dc7d" "checksum rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83a27732a533a1be0a0035a111fe76db89ad312f6f0347004c220c57f209a123" "checksum rayon-core 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "98dcf634205083b17d0861252431eb2acbfb698ab7478a2d20de07954f47ec7b" "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +"checksum redox_users 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ecedbca3bf205f8d8f5c2b44d83cd0690e39ee84b951ed649e9f1841132b66d" "checksum ref_thread_local 0.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d813022b2e00774a48eaf43caaa3c20b45f040ba8cbf398e2e8911a06668dbe6" "checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd" "checksum regex-automata 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "92b73c2a1770c255c240eaa4ee600df1704a38dc3feaa6e949e7fcd4f8dc09f9" "checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" +"checksum region 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "448e868c6e4cfddfa49b6a72c95906c04e8547465e9536575b95c70a4044f856" "checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" "checksum rhododendron 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "36542aafc2429a4c010fafa079a20dee953b663cb2427f51d86cf1d436846b4d" "checksum ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)" = "426bc186e3e95cac1e4a4be125a4aca7e84c2d616ffc02244eef36e2a60a093c" "checksum rocksdb 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f1651697fefd273bfb4fd69466cc2a9d20de557a0213b97233b22b5e95924b5e" "checksum rpassword 4.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f072d931f11a96546efd97642e1e75e807345aced86b947f9239102f262d0fcd" +"checksum rust-argon2 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ca4eaef519b494d1f2848fc602d18816fed808a981aedf4f1f00ceb7c9d32cf" "checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" "checksum rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "403bb3a286107a04825a5f82e1270acc1e14028d3d554d7a1e08914549575ab8" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" @@ -7230,6 +7663,8 @@ dependencies = [ "checksum schnorrkel 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)" = "eacd8381b3c37840c9c9f40472af529e49975bdcbc24f83c31059fd6539023d3" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" "checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" +"checksum scroll 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f84d114ef17fd144153d608fba7c446b0145d038985e7a8cc5d08bb0ce20383" +"checksum scroll_derive 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8f1aa96c45e7f5a91cb7fabe7b279f02fea7126239fc40b732316e8b6a2d0fcb" "checksum sct 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f5adf8fbd58e1b1b52699dc8bed2630faecb6d8c7bee77d009d6bbe4af569b9" "checksum security-framework 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eee63d0f4a9ec776eeb30e220f0bc1e092c3ad744b2a379e3993070364d3adc2" "checksum security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9636f8989cbf61385ae4824b98c1aaa54c994d7d8b41f11c601ed799f0549a56" @@ -7262,6 +7697,7 @@ dependencies = [ "checksum static_slice 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "92a7e0c5e3dfb52e8fbe0e63a1b947bbb17b4036408b151353c4491374931362" "checksum stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8131256a5896cabcf5eb04f4d6dacbe1aefda854b0d9896e09cb58829ec5638c" "checksum string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" +"checksum string-interner 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd710eadff449a1531351b0e43eb81ea404336fa2f56c777427ab0e32a4cf183" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" "checksum structopt 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4f66a4c0ddf7aee4677995697366de0749b0139057342eccbb609b12d0affc" "checksum structopt-derive 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8fe0c13e476b4e21ff7f5c4ace3818b6d7bdc16897c31c73862471bc1663acae" @@ -7277,6 +7713,7 @@ dependencies = [ "checksum synstructure 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f085a5855930c0441ca1288cf044ea4aecf4f43a91668abdb870b4ba546a203" "checksum sysinfo 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d5bd3b813d94552a8033c650691645f8dd5a63d614dddd62428a95d3931ef7b6" "checksum take_mut 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" +"checksum target-lexicon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7975cb2c6f37d77b190bc5004a2bb015971464756fde9514651a525ada2a741a" "checksum target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c63f48baada5c52e65a29eef93ab4f8982681b67f9e8d29c7b05abcfec2b9ffe" "checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" @@ -7353,6 +7790,11 @@ dependencies = [ "checksum wasm-timer 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "aa3e01d234bb71760e685cfafa5e2c96f8ad877c161a721646356651069e26ac" "checksum wasmi 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f31d26deb2d9a37e6cfed420edce3ed604eab49735ba89035e13c98f9a528313" "checksum wasmi-validation 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6bc0356e3df56e639fc7f7d8a99741915531e27ed735d911ed83d7e1339c8188" +"checksum wasmparser 0.39.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e5083b449454f7de0b15f131eee17de54b5a71dcb9adcf11df2b2f78fad0cd82" +"checksum wasmtime-debug 0.2.0 (git+https://github.com/jimpo/wasmtime.git?branch=context-compile)" = "" +"checksum wasmtime-environ 0.2.0 (git+https://github.com/jimpo/wasmtime.git?branch=context-compile)" = "" +"checksum wasmtime-jit 0.2.0 (git+https://github.com/jimpo/wasmtime.git?branch=context-compile)" = "" +"checksum wasmtime-runtime 0.2.0 (git+https://github.com/jimpo/wasmtime.git?branch=context-compile)" = "" "checksum web-sys 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)" = "c84440699cd02ca23bed6f045ffb1497bc18a3c2628bd13e2093186faaaacf6b" "checksum webpki 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4f7e1cd7900a3a6b65a3e8780c51a3e6b59c0e2c55c6dc69578c288d69f7d082" "checksum webpki-roots 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c10fa4212003ba19a564f25cd8ab572c6791f99a03cc219c13ed35ccab00de0e" @@ -7375,3 +7817,6 @@ dependencies = [ "checksum zeroize 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4090487fa66630f7b166fba2bbb525e247a5449f41c468cc1d98f8ae6ac03120" "checksum zeroize 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "45af6a010d13e4cf5b54c94ba5a2b2eba5596b9e46bf5875612d332a1f2b3f86" "checksum zeroize_derive 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "080616bd0e31f36095288bb0acdf1f78ef02c2fa15527d7e993f2a6c7591643e" +"checksum zstd 0.4.28+zstd.1.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f4e716acaad66f2daf2526f37a1321674a8814c0b37a366ebe6c97a699f85ddc" +"checksum zstd-safe 1.4.13+zstd.1.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bfe4d3b26a0790201848865663e8ffabf091e126e548bc9710ccfa95621ece48" +"checksum zstd-sys 1.4.13+zstd.1.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fadc8ebe858f056ab82dffb9d93850b841603bdf663db7cf5e3dbd7f34cc55b2" diff --git a/core/executor/Cargo.toml b/core/executor/Cargo.toml index 74ef40292faca..ab2fa131e762a 100644 --- a/core/executor/Cargo.toml +++ b/core/executor/Cargo.toml @@ -23,6 +23,15 @@ log = "0.4.8" libsecp256k1 = "0.3.0" tiny-keccak = "1.5.0" +cranelift-codegen = { version = "0.46.1", optional = true } +cranelift-entity = { version = "0.46.1", optional = true } +cranelift-frontend = { version = "0.46.1", optional = true } +cranelift-native = { version = "0.46.1", optional = true } +cranelift-wasm = { version = "0.46.1", optional = true } +wasmtime-environ = { version = "0.2", optional = true, git = "https://github.com/jimpo/wasmtime.git", branch = "context-compile" } +wasmtime-jit = { version = "0.2", optional = true, git = "https://github.com/jimpo/wasmtime.git", branch = "context-compile" } +wasmtime-runtime = { version = "0.2", optional = true, git = "https://github.com/jimpo/wasmtime.git", branch = "context-compile" } + [dev-dependencies] assert_matches = "1.3.0" wabt = "0.9.2" @@ -36,3 +45,13 @@ test-case = "0.3.3" [features] default = [] wasm-extern-trace = [] +wasmtime = [ + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "cranelift-native", + "cranelift-wasm", + "wasmtime-environ", + "wasmtime-jit", + "wasmtime-runtime", +] diff --git a/core/executor/src/error.rs b/core/executor/src/error.rs index e1221bea54aef..52fd68ff8411e 100644 --- a/core/executor/src/error.rs +++ b/core/executor/src/error.rs @@ -18,6 +18,8 @@ use serializer; use wasmi; +#[cfg(feature = "wasmtime")] +use wasmtime_jit::{ActionError, SetupError}; /// Result type alias. pub type Result = std::result::Result; @@ -31,6 +33,9 @@ pub enum Error { Trap(wasmi::Trap), /// Wasmi loading/instantiating error Wasmi(wasmi::Error), + /// Wasmtime action error + #[cfg(feature = "wasmtime")] + Wasmtime(ActionError), /// Error in the API. Parameter is an error message. ApiError(String), /// Method is not found @@ -75,7 +80,7 @@ pub enum Error { /// Someone tried to allocate more memory than the allowed maximum per allocation. #[display(fmt="Requested allocation size is too large")] RequestedAllocationTooLarge, - /// Executing the given function failed with the given error. + /// Execution of a host function failed. #[display(fmt="Function execution failed with: {}", _0)] FunctionExecution(String), } @@ -116,6 +121,16 @@ pub enum WasmError { InvalidModule, /// Wasm code could not be deserialized. CantDeserializeWasm, + /// The module does not export a linear memory named `memory`. + InvalidMemory, + /// The number of heap pages requested is disallowed by the module. + InvalidHeapPages, /// Instantiation error. Instantiation(Error), + /// The compiler does not support the host machine as a target. + #[cfg(feature = "wasmtime")] + MissingCompilerSupport(&'static str), + /// Wasmtime setup error. + #[cfg(feature = "wasmtime")] + WasmtimeSetup(SetupError), } diff --git a/core/executor/src/lib.rs b/core/executor/src/lib.rs index f83f9202f59bb..f053718b3aef5 100644 --- a/core/executor/src/lib.rs +++ b/core/executor/src/lib.rs @@ -38,6 +38,8 @@ mod sandbox; mod allocator; mod host_interface; mod wasm_runtime; +#[cfg(feature = "wasmtime")] +mod wasmtime; #[cfg(test)] mod integration_tests; diff --git a/core/executor/src/wasmtime/mod.rs b/core/executor/src/wasmtime/mod.rs new file mode 100644 index 0000000000000..1cef04188dbd3 --- /dev/null +++ b/core/executor/src/wasmtime/mod.rs @@ -0,0 +1,22 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +///! Defines a `WasmRuntime` that uses the Wasmtime JIT to execute. + +mod runtime; +mod util; + +pub use runtime::create_instance; diff --git a/core/executor/src/wasmtime/runtime.rs b/core/executor/src/wasmtime/runtime.rs new file mode 100644 index 0000000000000..db1769d3a03b9 --- /dev/null +++ b/core/executor/src/wasmtime/runtime.rs @@ -0,0 +1,244 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Defines the compiled Wasm runtime that uses Wasmtime internally. + +use crate::error::{Error, Result, WasmError}; +use crate::wasm_runtime::WasmRuntime; +use crate::wasmtime::util::read_memory_into; +use crate::{Externalities, RuntimeVersion}; + +use codec::Decode; +use cranelift_codegen::isa::TargetIsa; +use std::collections::HashMap; +use std::convert::TryFrom; +use wasm_interface::Pointer; +use wasmtime_jit::{ + ActionOutcome, ActionError, CompilationStrategy, CompiledModule, Context, + SetupError, RuntimeValue, +}; +use wasmtime_runtime::{Export, InstanceHandle}; + +/// A `WasmRuntime` implementation using the Wasmtime JIT to compile the runtime module to native +/// and execute the compiled code. +pub struct WasmtimeRuntime { + module: CompiledModule, + context: Context, + max_heap_pages: Option, + heap_pages: u32, + version: Option, +} + +impl WasmRuntime for WasmtimeRuntime { + fn update_heap_pages(&mut self, heap_pages: u64) -> bool { + match heap_pages_valid(heap_pages, self.max_heap_pages) { + Some(heap_pages) => { + self.heap_pages = heap_pages; + true + } + None => false, + } + } + + fn call(&mut self, ext: &mut dyn Externalities, method: &str, data: &[u8]) -> Result> { + call_method( + &mut self.context, + &mut self.module, + ext, + method, + data, + self.heap_pages, + ) + } + + fn version(&self) -> Option { + self.version.clone() + } +} + +/// Create a new `WasmtimeRuntime` given the code. This function performs translation from Wasm to +/// machine code, which can be computationally heavy. +pub fn create_instance(ext: &mut E, code: &[u8], heap_pages: u64) + -> std::result::Result +{ + let (mut compiled_module, mut context) = create_compiled_unit(code)?; + + // Inspect the module for the min and max memory sizes. + let (min_memory_size, max_memory_size) = { + let module = compiled_module.module_ref(); + let memory_index = match module.exports.get("memory") { + Some(wasmtime_environ::Export::Memory(memory_index)) => *memory_index, + _ => return Err(WasmError::InvalidMemory), + }; + let memory_plan = module.memory_plans.get(memory_index) + .expect("memory_index is retrieved from the module's exports map; qed"); + (memory_plan.memory.minimum, memory_plan.memory.maximum) + }; + + // Check that heap_pages is within the allowed range. + let max_heap_pages = max_memory_size.map(|max| max.saturating_sub(min_memory_size)); + let heap_pages = heap_pages_valid(heap_pages, max_heap_pages) + .ok_or_else(|| WasmError::InvalidHeapPages)?; + + // Call to determine runtime version. + let version_result = call_method( + &mut context, + &mut compiled_module, + ext, + "Core_version", + &[], + heap_pages + ); + let version = version_result + .ok() + .and_then(|v| RuntimeVersion::decode(&mut v.as_slice()).ok()); + Ok(WasmtimeRuntime { + module: compiled_module, + context, + max_heap_pages, + heap_pages, + version, + }) +} + +fn create_compiled_unit(code: &[u8]) + -> std::result::Result<(CompiledModule, Context), WasmError> +{ + let isa = target_isa()?; + let mut context = Context::with_isa(isa, CompilationStrategy::Cranelift); + + // Enable/disable producing of debug info. + context.set_debug_info(false); + + // TODO: Instantiate and link the env module. + + // Compile the wasm module. + let module = context.compile_module(&code) + .map_err(WasmError::WasmtimeSetup)?; + + Ok((module, context)) +} + +/// Call a function inside a precompiled Wasm module. +fn call_method( + context: &mut Context, + module: &mut CompiledModule, + ext: &mut dyn Externalities, + method: &str, + data: &[u8], + heap_pages: u32, +) -> Result> { + // Old exports get clobbered in `InstanceHandle::new` if we don't explicitly remove them first. + // + // The global exports mechanism is temporary in Wasmtime and expected to be removed. + // https://github.com/CraneStation/wasmtime/issues/332 + clear_globals(&mut *context.get_global_exports().borrow_mut()); + + let mut instance = module.instantiate() + .map_err(SetupError::Instantiate) + .map_err(ActionError::Setup) + .map_err(Error::Wasmtime)?; + + unsafe { + // Ideally there would be a way to set the heap pages during instantiation rather than + // growing the memory after the fact. Current this may require an additional mmap and copy. + // However, the wasmtime API doesn't support modifying the size of memory on instantiation + // at this time. + grow_memory(&mut instance, heap_pages)?; + } + + // TODO: Construct arguments properly by allocating heap space with data. + let args = []; + + // Invoke the function in the runtime. + let outcome = externalities::set_and_run_with_externalities(ext, || { + context + .invoke(&mut instance, method, &args[..]) + .map_err(Error::Wasmtime) + })?; + let (output_ptr, output_len) = match outcome { + ActionOutcome::Returned { values } => { + if values.len() != 1 { + return Err(Error::InvalidReturn); + } + if let RuntimeValue::I64(val) = values[0] { + let output_ptr = >::new(val as u32); + let output_len = ((val as u64) >> 32) as usize; + (output_ptr, output_len) + } else { + return Err(Error::InvalidReturn); + } + } + ActionOutcome::Trapped { message } => + return Err(format!("Wasm execution trapped: {}", message).into()), + }; + + // Read the output data from guest memory. + let mut output = vec![0; output_len]; + let memory = unsafe { get_memory_mut(&mut instance)? }; + read_memory_into(memory, output_ptr, &mut output)?; + Ok(output) +} + +/// Build a new TargetIsa for the host machine. +fn target_isa() -> std::result::Result, WasmError> { + let isa_builder = cranelift_native::builder() + .map_err(WasmError::MissingCompilerSupport)?; + let flag_builder = cranelift_codegen::settings::builder(); + Ok(isa_builder.finish(cranelift_codegen::settings::Flags::new(flag_builder))) +} + +fn clear_globals(global_exports: &mut HashMap>) { + global_exports.remove("memory"); + global_exports.remove("__heap_base"); + global_exports.remove("__indirect_function_table"); +} + +unsafe fn grow_memory(instance: &mut InstanceHandle, pages: u32) -> Result<()> { + let memory_index = match instance.lookup_immutable("memory") { + Some(Export::Memory { definition, vmctx: _, memory: _ }) => + instance.memory_index(&*definition), + _ => return Err(Error::InvalidMemoryReference), + }; + instance.memory_grow(memory_index, pages) + .map(|_| ()) + .ok_or_else(|| "requested heap_pages would exceed maximum memory size".into()) +} + +unsafe fn get_memory_mut(instance: &mut InstanceHandle) -> Result<&mut [u8]> { + match instance.lookup("memory") { + Some(Export::Memory { definition, vmctx: _, memory: _ }) => + Ok(std::slice::from_raw_parts_mut( + (*definition).base, + (*definition).current_length, + )), + _ => Err(Error::InvalidMemoryReference), + } +} + +/// Checks whether the heap_pages parameter is within the valid range and converts it to a u32. +/// Returns None if heaps_pages in not in range. +fn heap_pages_valid(heap_pages: u64, max_heap_pages: Option) + -> Option +{ + let heap_pages = u32::try_from(heap_pages).ok()?; + if let Some(max_heap_pages) = max_heap_pages { + if heap_pages > max_heap_pages { + return None; + } + } + Some(heap_pages) +} diff --git a/core/executor/src/wasmtime/util.rs b/core/executor/src/wasmtime/util.rs new file mode 100644 index 0000000000000..ce88d71d9b6d1 --- /dev/null +++ b/core/executor/src/wasmtime/util.rs @@ -0,0 +1,88 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +use crate::error::{Error, Result}; + +use std::ops::Range; +use wasm_interface::Pointer; + + +/// Read data from a slice of memory into a destination buffer. +/// +/// Returns an error if the read would go out of the memory bounds. +pub fn read_memory_into(memory: &[u8], address: Pointer, dest: &mut [u8]) -> Result<()> { + let range = checked_range(address.into(), dest.len(), memory.len()) + .ok_or_else(|| Error::Other("memory read is out of bounds".into()))?; + dest.copy_from_slice(&memory[range]); + Ok(()) +} + +/// Write data to a slice of memory. +/// +/// Returns an error if the write would go out of the memory bounds. +pub fn write_memory_from(memory: &mut [u8], address: Pointer, data: &[u8]) -> Result<()> { + let range = checked_range(address.into(), data.len(), memory.len()) + .ok_or_else(|| Error::Other("memory write is out of bounds".into()))?; + &mut memory[range].copy_from_slice(data); + Ok(()) +} + +/// Construct a range from an offset to a data length after the offset. +/// Returns None if the end of the range would exceed some maximum offset. +pub fn checked_range(offset: usize, len: usize, max: usize) -> Option> { + let end = offset.checked_add(len)?; + if end <= max { + Some(offset..end) + } else { + None + } +} + +#[cfg(test)] +mod tests { + use super::*; + use assert_matches::assert_matches; + + #[test] + fn test_read_memory_into() { + let mut memory = [0; 20]; + let mut dest = [0; 5]; + + &mut memory[15..20].copy_from_slice(b"hello"); + + read_memory_into(&memory[..], Pointer::new(15), &mut dest[..]).unwrap(); + + // Test that out of bounds read fails. + assert_matches!( + read_memory_into(&memory[..], Pointer::new(16), &mut dest[..]), + Err(Error::Other(_)) + ) + } + + #[test] + fn test_write_memory_from() { + let mut memory = [0; 20]; + let data = b"hello"; + + write_memory_from(&mut memory[..], Pointer::new(15), data).unwrap(); + + // Test that out of bounds write fails. + assert_matches!( + write_memory_from(&mut memory[..], Pointer::new(16), data), + Err(Error::Other(_)) + ) + } +} From 447c31ad7f65e48d93071ba1ac94a95ea68f215a Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Mon, 21 Oct 2019 16:04:36 +0200 Subject: [PATCH 07/26] executor: Define and implement basic FunctionExecutor. The SandboxCapabilities::invoke is still left unimplemented. --- core/executor/src/wasmi_execution.rs | 2 +- .../src/wasmtime/function_executor.rs | 300 ++++++++++++++++++ core/executor/src/wasmtime/mod.rs | 1 + core/executor/src/wasmtime/util.rs | 28 +- core/wasm-interface/src/lib.rs | 2 +- 5 files changed, 330 insertions(+), 3 deletions(-) create mode 100644 core/executor/src/wasmtime/function_executor.rs diff --git a/core/executor/src/wasmi_execution.rs b/core/executor/src/wasmi_execution.rs index eae54625c8509..4e5c384bd16f5 100644 --- a/core/executor/src/wasmi_execution.rs +++ b/core/executor/src/wasmi_execution.rs @@ -138,7 +138,7 @@ impl FunctionContext for FunctionExecutor { impl Sandbox for FunctionExecutor { fn memory_get( - &self, + &mut self, memory_id: MemoryId, offset: WordSize, buf_ptr: Pointer, diff --git a/core/executor/src/wasmtime/function_executor.rs b/core/executor/src/wasmtime/function_executor.rs new file mode 100644 index 0000000000000..80e118ef12e34 --- /dev/null +++ b/core/executor/src/wasmtime/function_executor.rs @@ -0,0 +1,300 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +use crate::allocator::FreeingBumpHeapAllocator; +use crate::error::{Error, Result}; +use crate::sandbox::{self, SandboxCapabilities, SupervisorFuncIndex}; +use crate::wasmtime::util::{checked_range, read_memory_into, write_memory_from}; + +use codec::{Decode, Encode}; +use log::trace; +use primitives::sandbox as sandbox_primitives; +use wasmtime_jit::Compiler; +use wasmtime_runtime::{Export, VMCallerCheckedAnyfunc, VMContext}; +use wasm_interface::{ + FunctionContext, MemoryId, Pointer, Result as WResult, Sandbox, WordSize, +}; + +/// Wrapper type for pointer to a Wasm table entry. +/// +/// The wrapper type is used to ensure that the function reference is valid as it must be unsafely +/// dereferenced from within the safe method `::invoke`. +#[derive(Clone, Copy)] +pub struct SupervisorFuncRef(*const VMCallerCheckedAnyfunc); + +/// The state required to construct a FunctionExecutor context. The context only lasts for one host +/// call, whereas the state is maintained for the duration of a Wasm runtime call, which may make +/// many different host calls that must share state. +/// +/// This is stored as part of the host state of the "env" Wasmtime instance. +pub struct FunctionExecutorState { + // The lifetime of the reference is not actually static, but is unsafely cast to static since + // the compiler is owned by the wasmtime-jit `Context` and must be accessed during host calls. + compiler: &'static mut Compiler, + sandbox_store: sandbox::Store, + heap: FreeingBumpHeapAllocator, +} + +impl FunctionExecutorState { + /// Constructs a new `FunctionExecutorState`. + pub fn new(compiler: &'static mut Compiler, heap_base: u32) -> Self { + FunctionExecutorState { + compiler, + sandbox_store: sandbox::Store::new(), + heap: FreeingBumpHeapAllocator::new(heap_base), + } + } + + /// Returns a mutable reference to the heap allocator. + pub fn heap(&mut self) -> &mut FreeingBumpHeapAllocator { + &mut self.heap + } +} + +/// A `FunctionExecutor` implements `FunctionContext` for making host calls from a Wasmtime +/// runtime. The `FunctionExecutor` exists only for the lifetime of the call and borrows state from +/// a longer-living `FunctionExecutorState`. +pub struct FunctionExecutor<'a> { + compiler: &'a mut Compiler, + sandbox_store: &'a mut sandbox::Store, + heap: &'a mut FreeingBumpHeapAllocator, + memory: &'a mut [u8], + table: Option<&'a [VMCallerCheckedAnyfunc]>, +} + +impl<'a> FunctionExecutor<'a> { + /// Construct a new `FunctionExecutor`. + /// + /// The vmctx MUST come from a call to a function in the "env" module. + /// The state MUST be looked up from the host state of the "env" module. + pub unsafe fn new(vmctx: *mut VMContext, state: &'a mut FunctionExecutorState) + -> Result + { + let memory = match (*vmctx).lookup_global_export("memory") { + Some(Export::Memory { definition, vmctx: _, memory: _ }) => + std::slice::from_raw_parts_mut( + (*definition).base, + (*definition).current_length, + ), + _ => return Err(Error::InvalidMemoryReference), + }; + let table = match (*vmctx).lookup_global_export("__indirect_function_table") { + Some(Export::Table { definition, vmctx: _, table: _ }) => + Some(std::slice::from_raw_parts( + (*definition).base as *const VMCallerCheckedAnyfunc, + (*definition).current_elements as usize, + )), + _ => None, + }; + Ok(FunctionExecutor { + compiler: &mut state.compiler, + sandbox_store: &mut state.sandbox_store, + heap: &mut state.heap, + memory, + table, + }) + } +} + +impl<'a> SandboxCapabilities for FunctionExecutor<'a> { + type SupervisorFuncRef = SupervisorFuncRef; + + fn store(&self) -> &sandbox::Store { + &self.sandbox_store + } + + fn store_mut(&mut self) -> &mut sandbox::Store { + &mut self.sandbox_store + } + + fn allocate(&mut self, len: WordSize) -> Result> { + self.heap.allocate(self.memory, len) + } + + fn deallocate(&mut self, ptr: Pointer) -> Result<()> { + self.heap.deallocate(self.memory, ptr) + } + + fn write_memory(&mut self, ptr: Pointer, data: &[u8]) -> Result<()> { + write_memory_from(self.memory, ptr, data) + } + + fn read_memory(&self, ptr: Pointer, len: WordSize) -> Result> { + let mut output = vec![0; len as usize]; + read_memory_into(self.memory, ptr, output.as_mut())?; + Ok(output) + } + + fn invoke( + &mut self, + _dispatch_thunk: &Self::SupervisorFuncRef, + _invoke_args_ptr: Pointer, + _invoke_args_len: WordSize, + _state: u32, + _func_idx: SupervisorFuncIndex, + ) -> Result + { + unimplemented!() + } +} + +impl<'a> FunctionContext for FunctionExecutor<'a> { + fn read_memory_into(&self, address: Pointer, dest: &mut [u8]) -> WResult<()> { + read_memory_into(self.memory, address, dest).map_err(|e| e.to_string()) + } + + fn write_memory(&mut self, address: Pointer, data: &[u8]) -> WResult<()> { + write_memory_from(self.memory, address, data).map_err(|e| e.to_string()) + } + + fn allocate_memory(&mut self, size: WordSize) -> WResult> { + self.heap.allocate(self.memory, size).map_err(|e| e.to_string()) + } + + fn deallocate_memory(&mut self, ptr: Pointer) -> WResult<()> { + self.heap.deallocate(self.memory, ptr).map_err(|e| e.to_string()) + } + + fn sandbox(&mut self) -> &mut dyn Sandbox { + self + } +} + +impl<'a> Sandbox for FunctionExecutor<'a> { + fn memory_get( + &mut self, + memory_id: MemoryId, + offset: WordSize, + buf_ptr: Pointer, + buf_len: WordSize, + ) -> WResult + { + let sandboxed_memory = self.sandbox_store.memory(memory_id) + .map_err(|e| e.to_string())?; + sandboxed_memory.with_direct_access(|memory| { + let len = buf_len as usize; + let src_range = match checked_range(offset as usize, len, memory.len()) { + Some(range) => range, + None => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS), + }; + let dst_range = match checked_range(buf_ptr.into(), len, self.memory.len()) { + Some(range) => range, + None => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS), + }; + &mut self.memory[dst_range].copy_from_slice(&memory[src_range]); + Ok(sandbox_primitives::ERR_OK) + }) + } + + fn memory_set( + &mut self, + memory_id: MemoryId, + offset: WordSize, + val_ptr: Pointer, + val_len: WordSize, + ) -> WResult + { + let sandboxed_memory = self.sandbox_store.memory(memory_id) + .map_err(|e| e.to_string())?; + sandboxed_memory.with_direct_access_mut(|memory| { + let len = val_len as usize; + let src_range = match checked_range(val_ptr.into(), len, self.memory.len()) { + Some(range) => range, + None => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS), + }; + let dst_range = match checked_range(offset as usize, len, memory.len()) { + Some(range) => range, + None => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS), + }; + &mut memory[dst_range].copy_from_slice(&self.memory[src_range]); + Ok(sandbox_primitives::ERR_OK) + }) + } + + fn memory_teardown(&mut self, memory_id: MemoryId) + -> WResult<()> + { + self.sandbox_store.memory_teardown(memory_id).map_err(|e| e.to_string()) + } + + fn memory_new(&mut self, initial: u32, maximum: MemoryId) -> WResult { + self.sandbox_store.new_memory(initial, maximum).map_err(|e| e.to_string()) + } + + fn invoke( + &mut self, + instance_id: u32, + export_name: &str, + args: &[u8], + return_val: Pointer, + return_val_len: u32, + state: u32, + ) -> WResult { + trace!(target: "sr-sandbox", "invoke, instance_idx={}", instance_id); + + // Deserialize arguments and convert them into wasmi types. + let args = Vec::::decode(&mut &args[..]) + .map_err(|_| "Can't decode serialized arguments for the invocation")? + .into_iter() + .map(Into::into) + .collect::>(); + + let instance = self.sandbox_store.instance(instance_id).map_err(|e| e.to_string())?; + let result = instance.invoke(export_name, &args, self, state); + + match result { + Ok(None) => Ok(sandbox_primitives::ERR_OK), + Ok(Some(val)) => { + // Serialize return value and write it back into the memory. + sandbox_primitives::ReturnValue::Value(val.into()).using_encoded(|val| { + if val.len() > return_val_len as usize { + Err("Return value buffer is too small")?; + } + FunctionContext::write_memory(self, return_val, val)?; + Ok(sandbox_primitives::ERR_OK) + }) + } + Err(_) => Ok(sandbox_primitives::ERR_EXECUTION), + } + } + + fn instance_teardown(&mut self, instance_id: u32) -> WResult<()> { + self.sandbox_store.instance_teardown(instance_id).map_err(|e| e.to_string()) + } + + fn instance_new(&mut self, dispatch_thunk_id: u32, wasm: &[u8], raw_env_def: &[u8], state: u32) + -> WResult + { + // Extract a dispatch thunk from instance's table by the specified index. + let dispatch_thunk = { + let table = self.table.as_ref() + .ok_or_else(|| "Runtime doesn't have a table; sandbox is unavailable")?; + let func_ref = table.get(dispatch_thunk_id as usize) + .ok_or_else(|| "dispatch_thunk_idx is out of the table bounds")?; + SupervisorFuncRef(func_ref) + }; + + let instance_idx_or_err_code = + match sandbox::instantiate(self, dispatch_thunk, wasm, raw_env_def, state) { + Ok(instance_idx) => instance_idx, + Err(sandbox::InstantiationError::StartTrapped) => + sandbox_primitives::ERR_EXECUTION, + Err(_) => sandbox_primitives::ERR_MODULE, + }; + + Ok(instance_idx_or_err_code as u32) + } +} diff --git a/core/executor/src/wasmtime/mod.rs b/core/executor/src/wasmtime/mod.rs index 1cef04188dbd3..a490a7f518a02 100644 --- a/core/executor/src/wasmtime/mod.rs +++ b/core/executor/src/wasmtime/mod.rs @@ -16,6 +16,7 @@ ///! Defines a `WasmRuntime` that uses the Wasmtime JIT to execute. +mod function_executor; mod runtime; mod util; diff --git a/core/executor/src/wasmtime/util.rs b/core/executor/src/wasmtime/util.rs index ce88d71d9b6d1..2502c3cc5fb8d 100644 --- a/core/executor/src/wasmtime/util.rs +++ b/core/executor/src/wasmtime/util.rs @@ -16,8 +16,9 @@ use crate::error::{Error, Result}; +use cranelift_codegen::{ir, isa}; use std::ops::Range; -use wasm_interface::Pointer; +use wasm_interface::{Pointer, Signature, ValueType}; /// Read data from a slice of memory into a destination buffer. @@ -51,6 +52,31 @@ pub fn checked_range(offset: usize, len: usize, max: usize) -> Option ir::Signature { + ir::Signature { + params: signature.args.iter() + .map(cranelift_ir_type) + .map(ir::AbiParam::new) + .collect(), + returns: signature.return_value.iter() + .map(cranelift_ir_type) + .map(ir::AbiParam::new) + .collect(), + call_conv: call_conv.clone(), + } +} + +/// Convert a wasm_interface ValueType into a cranelift_codegen Type. +pub fn cranelift_ir_type(value_type: &ValueType) -> ir::types::Type { + match value_type { + ValueType::I32 => ir::types::I32, + ValueType::I64 => ir::types::I64, + ValueType::F32 => ir::types::F32, + ValueType::F64 => ir::types::F64, + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/core/wasm-interface/src/lib.rs b/core/wasm-interface/src/lib.rs index b3cbde556eea0..47f8ad05810e5 100644 --- a/core/wasm-interface/src/lib.rs +++ b/core/wasm-interface/src/lib.rs @@ -212,7 +212,7 @@ pub type MemoryId = u32; pub trait Sandbox { /// Get sandbox memory from the `memory_id` instance at `offset` into the given buffer. fn memory_get( - &self, + &mut self, memory_id: MemoryId, offset: WordSize, buf_ptr: Pointer, From 828fbf0087dcebb30b03ee36fca9b8ba39f705bd Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Mon, 21 Oct 2019 16:21:28 +0200 Subject: [PATCH 08/26] executor: Implement host function trampoline generation. --- core/executor/src/wasmtime/mod.rs | 1 + core/executor/src/wasmtime/trampoline.rs | 297 +++++++++++++++++++++++ 2 files changed, 298 insertions(+) create mode 100644 core/executor/src/wasmtime/trampoline.rs diff --git a/core/executor/src/wasmtime/mod.rs b/core/executor/src/wasmtime/mod.rs index a490a7f518a02..7f442417ab849 100644 --- a/core/executor/src/wasmtime/mod.rs +++ b/core/executor/src/wasmtime/mod.rs @@ -18,6 +18,7 @@ mod function_executor; mod runtime; +mod trampoline; mod util; pub use runtime::create_instance; diff --git a/core/executor/src/wasmtime/trampoline.rs b/core/executor/src/wasmtime/trampoline.rs new file mode 100644 index 0000000000000..27d65afcdff88 --- /dev/null +++ b/core/executor/src/wasmtime/trampoline.rs @@ -0,0 +1,297 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! The trampoline is the dynamically generated entry point to a runtime host call. +//! +//! This code is based on and large parts are copied from wasmtime's +//! wasmtime-api/src/trampoline/func.rs. + +use cranelift_codegen::{Context, binemit, ir, isa}; +use cranelift_codegen::ir::{InstBuilder, StackSlotData, StackSlotKind, TrapCode}; +use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; +use wasmtime_jit::CodeMemory; +use wasmtime_runtime::{VMContext, VMFunctionBody}; +use wasm_interface::{HostFunctions, Function, Value, ValueType}; +use std::{cmp, mem, ptr}; + +use crate::error::{Error, Result}; +use crate::wasmtime::function_executor::{FunctionExecutorState, FunctionExecutor}; + +/// The top-level host state of the "env" module. This state is used by the trampoline function to +/// construct a `FunctionExecutor` which can execute the host call. +pub struct TrampolineState { + externals: &'static [&'static dyn Function], + trap: Option, + /// The executor state stored across host calls during a single Wasm runtime call. + /// During a runtime call, this MUST be `Some`. + pub executor_state: Option, + // The code memory must be kept around on the state to prevent it from being dropped. + #[allow(dead_code)] + code_memory: CodeMemory, +} + +impl TrampolineState { + /// Construct a new `TrampolineState` which owns the given code memory. + pub fn new(code_memory: CodeMemory) -> Self { + TrampolineState { + externals: HF::functions(), + trap: None, + executor_state: None, + code_memory, + } + } + + /// Resets the trap error to None and returns the current value. + pub fn reset_trap(&mut self) -> Option { + mem::replace(&mut self.trap, None) + } +} + +/// This is called by the dynamically generated trampoline taking the function index and reference +/// to the call arguments on the stack as arguments. Returns 0 on success and 1 on an error. +unsafe extern "C" fn stub_fn(vmctx: *mut VMContext, func_index: u32, values_vec: *mut i64) -> u32 { + if let Some(state) = (*vmctx).host_state().downcast_mut::() { + match stub_fn_inner( + vmctx, + state.externals, + func_index, + state.executor_state.as_mut(), + values_vec + ) { + Ok(()) => 0, + Err(err) => { + state.trap = Some(err); + 1 + } + } + } else { + // Well, we can't even set a trap message, so we'll just exit without one. + 1 + } +} + +/// Implements most of the logic in `stub_fn` but returning a `Result` instead of an integer error +/// for the sake of readability. +unsafe fn stub_fn_inner( + vmctx: *mut VMContext, + externals: &[&dyn Function], + func_index: u32, + executor_state: Option<&mut FunctionExecutorState>, + values_vec: *mut i64, +) -> Result<()> +{ + let func = externals.get(func_index as usize) + .ok_or_else(|| format!("call to undefined external function with index {}", func_index))?; + let executor_state = executor_state + .ok_or_else(|| "executor state is None during call to external function")?; + + // Build the external function context. + let mut context = FunctionExecutor::new(vmctx, executor_state)?; + + let signature = func.signature(); + + // Read the arguments from the stack. + let mut args = signature.args.iter() + .enumerate() + .map(|(i, ¶m_type)| read_value_from(values_vec.offset(i as isize), param_type)); + + // Execute and write output back to the stack. + let return_val = func.execute(&mut context, &mut args) + .map_err(|e| Error::FunctionExecution( + format!("error in external function {}: {}", func.name(), e) + ))?; + if let Some(val) = return_val { + write_value_to(values_vec, val); + } + + Ok(()) +} + +/// Create a trampoline for invoking a host function. +/// +/// This code is mostly copied from wasmtime's wasmtime-api/src/trampoline/func.rs. +pub fn make_trampoline( + isa: &dyn isa::TargetIsa, + code_memory: &mut CodeMemory, + fn_builder_ctx: &mut FunctionBuilderContext, + call_id: u32, + signature: &ir::Signature, +) -> *const VMFunctionBody { + // Mostly reverse copy of the similar method from wasmtime's + // wasmtime-jit/src/compiler.rs. + let pointer_type = isa.pointer_type(); + let mut stub_sig = ir::Signature::new(isa.frontend_config().default_call_conv); + + // Add the `vmctx` parameter. + stub_sig.params.push(ir::AbiParam::special( + pointer_type, + ir::ArgumentPurpose::VMContext, + )); + + // Add the `call_id` parameter. + stub_sig.params.push(ir::AbiParam::new(ir::types::I32)); + + // Add the `values_vec` parameter. + stub_sig.params.push(ir::AbiParam::new(pointer_type)); + + // Add error/trap return. + stub_sig.returns.push(ir::AbiParam::new(ir::types::I32)); + + let values_vec_len = 8 * cmp::max(signature.params.len() - 1, signature.returns.len()) as u32; + + let mut context = Context::new(); + context.func = + ir::Function::with_name_signature(ir::ExternalName::user(0, 0), signature.clone()); + + let ss = context.func.create_stack_slot(StackSlotData::new( + StackSlotKind::ExplicitSlot, + values_vec_len, + )); + let value_size = 8; + + { + let mut builder = FunctionBuilder::new(&mut context.func, fn_builder_ctx); + let block0 = builder.create_ebb(); + + builder.append_ebb_params_for_function_params(block0); + builder.switch_to_block(block0); + builder.seal_block(block0); + + let values_vec_ptr_val = builder.ins().stack_addr(pointer_type, ss, 0); + let mflags = ir::MemFlags::trusted(); + for i in 1..signature.params.len() { + if i == 0 { + continue; + } + + let val = builder.func.dfg.ebb_params(block0)[i]; + builder.ins().store( + mflags, + val, + values_vec_ptr_val, + ((i - 1) * value_size) as i32, + ); + } + + let vmctx_ptr_val = builder.func.dfg.ebb_params(block0)[0]; + let call_id_val = builder.ins().iconst(ir::types::I32, call_id as i64); + + let callee_args = vec![vmctx_ptr_val, call_id_val, values_vec_ptr_val]; + + let new_sig = builder.import_signature(stub_sig.clone()); + + let callee_value = builder + .ins() + .iconst(pointer_type, stub_fn as *const VMFunctionBody as i64); + let call = builder + .ins() + .call_indirect(new_sig, callee_value, &callee_args); + + let call_result = builder.func.dfg.inst_results(call)[0]; + builder.ins().trapnz(call_result, TrapCode::User(0)); + + let mflags = ir::MemFlags::trusted(); + let mut results = Vec::new(); + for (i, r) in signature.returns.iter().enumerate() { + let load = builder.ins().load( + r.value_type, + mflags, + values_vec_ptr_val, + (i * value_size) as i32, + ); + results.push(load); + } + builder.ins().return_(&results); + builder.finalize() + } + + let mut code_buf: Vec = Vec::new(); + let mut reloc_sink = RelocSink {}; + let mut trap_sink = binemit::NullTrapSink {}; + let mut stackmap_sink = binemit::NullStackmapSink {}; + context + .compile_and_emit( + isa, + &mut code_buf, + &mut reloc_sink, + &mut trap_sink, + &mut stackmap_sink, + ) + .expect("compile_and_emit"); + + code_memory + .allocate_copy_of_byte_slice(&code_buf) + .expect("allocate_copy_of_byte_slice") + .as_ptr() +} + +/// We don't expect trampoline compilation to produce any relocations, so +/// this `RelocSink` just asserts that it doesn't recieve any. +struct RelocSink {} + +impl binemit::RelocSink for RelocSink { + fn reloc_ebb( + &mut self, + _offset: binemit::CodeOffset, + _reloc: binemit::Reloc, + _ebb_offset: binemit::CodeOffset, + ) { + panic!("trampoline compilation should not produce ebb relocs"); + } + fn reloc_external( + &mut self, + _offset: binemit::CodeOffset, + _reloc: binemit::Reloc, + _name: &ir::ExternalName, + _addend: binemit::Addend, + ) { + panic!("trampoline compilation should not produce external symbol relocs"); + } + fn reloc_constant( + &mut self, + _code_offset: binemit::CodeOffset, + _reloc: binemit::Reloc, + _constant_offset: ir::ConstantOffset, + ) { + panic!("trampoline compilation should not produce constant relocs"); + } + fn reloc_jt( + &mut self, + _offset: binemit::CodeOffset, + _reloc: binemit::Reloc, + _jt: ir::JumpTable, + ) { + panic!("trampoline compilation should not produce jump table relocs"); + } +} + +unsafe fn write_value_to(p: *mut i64, val: Value) { + match val { + Value::I32(i) => ptr::write(p as *mut i32, i), + Value::I64(i) => ptr::write(p as *mut i64, i), + Value::F32(u) => ptr::write(p as *mut u32, u), + Value::F64(u) => ptr::write(p as *mut u64, u), + } +} + +unsafe fn read_value_from(p: *const i64, ty: ValueType) -> Value { + match ty { + ValueType::I32 => Value::I32(ptr::read(p as *const i32)), + ValueType::I64 => Value::I64(ptr::read(p as *const i64)), + ValueType::F32 => Value::F32(ptr::read(p as *const u32)), + ValueType::F64 => Value::F64(ptr::read(p as *const u64)), + } +} From 81535c57788b8fdf65374baeafc8af73d189860b Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Mon, 21 Oct 2019 16:35:04 +0200 Subject: [PATCH 09/26] executor: Instantiate and link runtime module to env module. --- core/executor/src/wasmtime/runtime.rs | 119 ++++++++++++++++++++++++-- 1 file changed, 113 insertions(+), 6 deletions(-) diff --git a/core/executor/src/wasmtime/runtime.rs b/core/executor/src/wasmtime/runtime.rs index db1769d3a03b9..7f255d2891701 100644 --- a/core/executor/src/wasmtime/runtime.rs +++ b/core/executor/src/wasmtime/runtime.rs @@ -17,20 +17,30 @@ //! Defines the compiled Wasm runtime that uses Wasmtime internally. use crate::error::{Error, Result, WasmError}; +use crate::host_interface::SubstrateExternals; use crate::wasm_runtime::WasmRuntime; -use crate::wasmtime::util::read_memory_into; +use crate::wasmtime::function_executor::FunctionExecutorState; +use crate::wasmtime::trampoline::{TrampolineState, make_trampoline}; +use crate::wasmtime::util::{cranelift_ir_signature, read_memory_into}; use crate::{Externalities, RuntimeVersion}; use codec::Decode; use cranelift_codegen::isa::TargetIsa; +use cranelift_entity::{EntityRef, PrimaryMap}; +use cranelift_frontend::FunctionBuilderContext; +use cranelift_wasm::DefinedFuncIndex; +use std::cell::RefCell; use std::collections::HashMap; use std::convert::TryFrom; -use wasm_interface::Pointer; +use std::mem; +use std::rc::Rc; +use wasm_interface::{HostFunctions, Pointer}; +use wasmtime_environ::{Module, translate_signature}; use wasmtime_jit::{ - ActionOutcome, ActionError, CompilationStrategy, CompiledModule, Context, + ActionOutcome, ActionError, CodeMemory, CompilationStrategy, CompiledModule, Compiler, Context, SetupError, RuntimeValue, }; -use wasmtime_runtime::{Export, InstanceHandle}; +use wasmtime_runtime::{Export, Imports, InstanceHandle, VMFunctionBody}; /// A `WasmRuntime` implementation using the Wasmtime JIT to compile the runtime module to native /// and execute the compiled code. @@ -123,7 +133,10 @@ fn create_compiled_unit(code: &[u8]) // Enable/disable producing of debug info. context.set_debug_info(false); - // TODO: Instantiate and link the env module. + // Instantiate and link the env module. + let global_exports = context.get_global_exports(); + let env_module = instantiate_env_module(global_exports)?; + context.name_instance("env".to_owned(), env_module); // Compile the wasm module. let module = context.compile_module(&code) @@ -158,6 +171,18 @@ fn call_method( // However, the wasmtime API doesn't support modifying the size of memory on instantiation // at this time. grow_memory(&mut instance, heap_pages)?; + + // Initialize the function executor state. + let executor_state = FunctionExecutorState::new( + // Unsafely extend the reference lifetime to static. This is necessary because the + // host state must be `Any`. This is OK as we will drop this object either before + // exiting the function in the happy case or in the worst case on the next runtime + // call, which also resets the host state. Thus this reference can never actually + // outlive the Context. + mem::transmute::<_, &'static mut Compiler>(context.compiler_mut()), + get_heap_base(&instance)?, + ); + reset_host_state(context, Some(executor_state))?; } // TODO: Construct arguments properly by allocating heap space with data. @@ -169,6 +194,7 @@ fn call_method( .invoke(&mut instance, method, &args[..]) .map_err(Error::Wasmtime) })?; + let trap_error = reset_host_state(context, None)?; let (output_ptr, output_len) = match outcome { ActionOutcome::Returned { values } => { if values.len() != 1 { @@ -183,7 +209,9 @@ fn call_method( } } ActionOutcome::Trapped { message } => - return Err(format!("Wasm execution trapped: {}", message).into()), + return Err(trap_error.unwrap_or_else(|| + format!("Wasm execution trapped: {}", message).into() + )), }; // Read the output data from guest memory. @@ -193,6 +221,60 @@ fn call_method( Ok(output) } +/// The implementation is based on wasmtime_wasi::instantiate_wasi. +fn instantiate_env_module(global_exports: Rc>>>) + -> std::result::Result +{ + let isa = target_isa()?; + let pointer_type = isa.pointer_type(); + let call_conv = isa.default_call_conv(); + + let mut fn_builder_ctx = FunctionBuilderContext::new(); + let mut module = Module::new(); + let mut finished_functions = >::new(); + let mut code_memory = CodeMemory::new(); + + for function in SubstrateExternals::functions().iter() { + let sig = translate_signature( + cranelift_ir_signature(function.signature(), &call_conv), + pointer_type + ); + let sig_id = module.signatures.push(sig.clone()); + let func_id = module.functions.push(sig_id); + module + .exports + .insert(function.name().to_string(), wasmtime_environ::Export::Function(func_id)); + + let trampoline = make_trampoline( + isa.as_ref(), + &mut code_memory, + &mut fn_builder_ctx, + func_id.index() as u32, + &sig, + ); + finished_functions.push(trampoline); + } + + code_memory.publish(); + + let imports = Imports::none(); + let data_initializers = Vec::new(); + let signatures = PrimaryMap::new(); + let host_state = TrampolineState::new::(code_memory); + + let result = InstanceHandle::new( + Rc::new(module), + global_exports, + finished_functions.into_boxed_slice(), + imports, + &data_initializers, + signatures.into_boxed_slice(), + None, + Box::new(host_state), + ); + result.map_err(|e| WasmError::WasmtimeSetup(SetupError::Instantiate(e))) +} + /// Build a new TargetIsa for the host machine. fn target_isa() -> std::result::Result, WasmError> { let isa_builder = cranelift_native::builder() @@ -218,6 +300,23 @@ unsafe fn grow_memory(instance: &mut InstanceHandle, pages: u32) -> Result<()> { .ok_or_else(|| "requested heap_pages would exceed maximum memory size".into()) } +fn get_host_state(context: &mut Context) -> Result<&mut TrampolineState> { + let env_instance = context.get_instance("env") + .map_err(|err| format!("cannot find \"env\" module: {}", err))?; + env_instance + .host_state() + .downcast_mut::() + .ok_or_else(|| "cannot get \"env\" module host state".into()) +} + +fn reset_host_state(context: &mut Context, executor_state: Option) + -> Result> +{ + let trampoline_state = get_host_state(context)?; + trampoline_state.executor_state = executor_state; + Ok(trampoline_state.reset_trap()) +} + unsafe fn get_memory_mut(instance: &mut InstanceHandle) -> Result<&mut [u8]> { match instance.lookup("memory") { Some(Export::Memory { definition, vmctx: _, memory: _ }) => @@ -229,6 +328,14 @@ unsafe fn get_memory_mut(instance: &mut InstanceHandle) -> Result<&mut [u8]> { } } +unsafe fn get_heap_base(instance: &InstanceHandle) -> Result { + match instance.lookup_immutable("__heap_base") { + Some(Export::Global { definition, vmctx: _, global: _ }) => + Ok(*(*definition).as_u32()), + _ => return Err(Error::HeapBaseNotFoundOrInvalid), + } +} + /// Checks whether the heap_pages parameter is within the valid range and converts it to a u32. /// Returns None if heaps_pages in not in range. fn heap_pages_valid(heap_pages: u64, max_heap_pages: Option) From a3d2b35b9db047ec1d90e0a38413c63c8dcc1fb7 Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Mon, 21 Oct 2019 16:38:47 +0200 Subject: [PATCH 10/26] executor: Provide input data during wasmtime execution. --- core/executor/src/wasmtime/runtime.rs | 31 +++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/core/executor/src/wasmtime/runtime.rs b/core/executor/src/wasmtime/runtime.rs index 7f255d2891701..22a692ff3a31e 100644 --- a/core/executor/src/wasmtime/runtime.rs +++ b/core/executor/src/wasmtime/runtime.rs @@ -21,7 +21,7 @@ use crate::host_interface::SubstrateExternals; use crate::wasm_runtime::WasmRuntime; use crate::wasmtime::function_executor::FunctionExecutorState; use crate::wasmtime::trampoline::{TrampolineState, make_trampoline}; -use crate::wasmtime::util::{cranelift_ir_signature, read_memory_into}; +use crate::wasmtime::util::{cranelift_ir_signature, read_memory_into, write_memory_from}; use crate::{Externalities, RuntimeVersion}; use codec::Decode; @@ -34,7 +34,7 @@ use std::collections::HashMap; use std::convert::TryFrom; use std::mem; use std::rc::Rc; -use wasm_interface::{HostFunctions, Pointer}; +use wasm_interface::{HostFunctions, Pointer, WordSize}; use wasmtime_environ::{Module, translate_signature}; use wasmtime_jit::{ ActionOutcome, ActionError, CodeMemory, CompilationStrategy, CompiledModule, Compiler, Context, @@ -165,7 +165,7 @@ fn call_method( .map_err(ActionError::Setup) .map_err(Error::Wasmtime)?; - unsafe { + let args = unsafe { // Ideally there would be a way to set the heap pages during instantiation rather than // growing the memory after the fact. Current this may require an additional mmap and copy. // However, the wasmtime API doesn't support modifying the size of memory on instantiation @@ -183,10 +183,11 @@ fn call_method( get_heap_base(&instance)?, ); reset_host_state(context, Some(executor_state))?; - } - // TODO: Construct arguments properly by allocating heap space with data. - let args = []; + // Write the input data into guest memory. + let (data_ptr, data_len) = inject_input_data(context, &mut instance, data)?; + [RuntimeValue::I32(u32::from(data_ptr) as i32), RuntimeValue::I32(data_len as i32)] + }; // Invoke the function in the runtime. let outcome = externalities::set_and_run_with_externalities(ext, || { @@ -317,6 +318,24 @@ fn reset_host_state(context: &mut Context, executor_state: Option Result<(Pointer, WordSize)> { + let trampoline_state = get_host_state(context)?; + let executor_state = trampoline_state.executor_state + .as_mut() + .ok_or_else(|| "cannot get \"env\" module executor state")?; + + let memory = get_memory_mut(instance)?; + + let data_len = data.len() as WordSize; + let data_ptr = executor_state.heap().allocate(memory, data_len)?; + write_memory_from(memory, data_ptr, data)?; + Ok((data_ptr, data_len)) +} + unsafe fn get_memory_mut(instance: &mut InstanceHandle) -> Result<&mut [u8]> { match instance.lookup("memory") { Some(Export::Memory { definition, vmctx: _, memory: _ }) => From a7bb75918bf64cb1dc543a22ce5a01e37298a1ec Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Mon, 21 Oct 2019 16:46:29 +0200 Subject: [PATCH 11/26] executor: Implement SandboxCapabilites::invoke for wasmtime executor. --- .../src/wasmtime/function_executor.rs | 108 ++++++++++++++++-- core/wasm-interface/src/lib.rs | 12 ++ 2 files changed, 110 insertions(+), 10 deletions(-) diff --git a/core/executor/src/wasmtime/function_executor.rs b/core/executor/src/wasmtime/function_executor.rs index 80e118ef12e34..7128024c56d4d 100644 --- a/core/executor/src/wasmtime/function_executor.rs +++ b/core/executor/src/wasmtime/function_executor.rs @@ -17,15 +17,22 @@ use crate::allocator::FreeingBumpHeapAllocator; use crate::error::{Error, Result}; use crate::sandbox::{self, SandboxCapabilities, SupervisorFuncIndex}; -use crate::wasmtime::util::{checked_range, read_memory_into, write_memory_from}; +use crate::wasmtime::util::{ + checked_range, cranelift_ir_signature, read_memory_into, write_memory_from, +}; use codec::{Decode, Encode}; +use cranelift_codegen::ir; +use cranelift_codegen::isa::TargetFrontendConfig; use log::trace; use primitives::sandbox as sandbox_primitives; -use wasmtime_jit::Compiler; -use wasmtime_runtime::{Export, VMCallerCheckedAnyfunc, VMContext}; +use std::{cmp, mem, ptr}; +use wasmtime_environ::translate_signature; +use wasmtime_jit::{ActionError, Compiler}; +use wasmtime_runtime::{Export, VMCallerCheckedAnyfunc, VMContext, wasmtime_call_trampoline}; use wasm_interface::{ - FunctionContext, MemoryId, Pointer, Result as WResult, Sandbox, WordSize, + FunctionContext, MemoryId, Pointer, Result as WResult, Sandbox, Signature, Value, ValueType, + WordSize, }; /// Wrapper type for pointer to a Wasm table entry. @@ -140,14 +147,48 @@ impl<'a> SandboxCapabilities for FunctionExecutor<'a> { fn invoke( &mut self, - _dispatch_thunk: &Self::SupervisorFuncRef, - _invoke_args_ptr: Pointer, - _invoke_args_len: WordSize, - _state: u32, - _func_idx: SupervisorFuncIndex, + dispatch_thunk: &Self::SupervisorFuncRef, + invoke_args_ptr: Pointer, + invoke_args_len: WordSize, + state: u32, + func_idx: SupervisorFuncIndex, ) -> Result { - unimplemented!() + let func_ptr = unsafe { (*dispatch_thunk.0).func_ptr }; + let vmctx = unsafe { (*dispatch_thunk.0).vmctx }; + + // The following code is based on the wasmtime_jit::Context::invoke. + let value_size = mem::size_of::(); + let (signature, mut values_vec) = generate_signature_and_args( + &[ + Value::I32(u32::from(invoke_args_ptr) as i32), + Value::I32(invoke_args_len as i32), + Value::I32(state as i32), + Value::I32(usize::from(func_idx) as i32), + ], + Some(ValueType::I64), + self.compiler.frontend_config(), + ); + + // Get the trampoline to call for this function. + let exec_code_buf = self.compiler + .get_published_trampoline(func_ptr, &signature, value_size) + .map_err(ActionError::Setup) + .map_err(Error::Wasmtime)?; + + // Call the trampoline. + if let Err(message) = unsafe { + wasmtime_call_trampoline( + vmctx, + exec_code_buf, + values_vec.as_mut_ptr() as *mut u8, + ) + } { + return Err(Error::FunctionExecution(message)); + } + + // Load the return value out of `values_vec`. + Ok(unsafe { ptr::read(values_vec.as_ptr() as *const i64) }) } } @@ -298,3 +339,50 @@ impl<'a> Sandbox for FunctionExecutor<'a> { Ok(instance_idx_or_err_code as u32) } } + +// The storage for a Wasmtime invocation argument. +#[derive(Debug, Default, Copy, Clone)] +#[repr(C, align(8))] +struct VMInvokeArgument([u8; 8]); + +fn generate_signature_and_args( + args: &[Value], + result_type: Option, + frontend_config: TargetFrontendConfig, +) -> (ir::Signature, Vec) +{ + // This code is based on the wasmtime_jit::Context::invoke. + + let param_types = args.iter() + .map(|arg| arg.value_type()) + .collect::>(); + let signature = translate_signature( + cranelift_ir_signature( + Signature::new(param_types, result_type), + &frontend_config.default_call_conv + ), + frontend_config.pointer_type() + ); + + let mut values_vec = vec![ + VMInvokeArgument::default(); + cmp::max(args.len(), result_type.iter().len()) + ]; + + // Store the argument values into `values_vec`. + for (index, arg) in args.iter().enumerate() { + unsafe { + let ptr = values_vec.as_mut_ptr().add(index); + + match arg { + Value::I32(x) => ptr::write(ptr as *mut i32, *x), + Value::I64(x) => ptr::write(ptr as *mut i64, *x), + Value::F32(x) => ptr::write(ptr as *mut u32, *x), + Value::F64(x) => ptr::write(ptr as *mut u64, *x), + } + } + } + + (signature, values_vec) +} + diff --git a/core/wasm-interface/src/lib.rs b/core/wasm-interface/src/lib.rs index 47f8ad05810e5..9e38a1bfe4d26 100644 --- a/core/wasm-interface/src/lib.rs +++ b/core/wasm-interface/src/lib.rs @@ -49,6 +49,18 @@ pub enum Value { F64(u64), } +impl Value { + /// Returns the type of this value. + pub fn value_type(&self) -> ValueType { + match self { + Value::I32(_) => ValueType::I32, + Value::I64(_) => ValueType::I64, + Value::F32(_) => ValueType::F32, + Value::F64(_) => ValueType::F64, + } + } +} + /// Provides `Sealed` trait to prevent implementing trait `PointerType` outside of this crate. mod private { pub trait Sealed {} From 9076ca029f89558e038fbd9786fe86fd3ca26dd0 Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Mon, 21 Oct 2019 16:57:46 +0200 Subject: [PATCH 12/26] executor: Integrate and test wasmtime execution method. --- core/executor/src/integration_tests/mod.rs | 13 +++++++++++++ core/executor/src/integration_tests/sandbox.rs | 10 ++++++++++ core/executor/src/wasm_runtime.rs | 9 +++++++++ node/executor/Cargo.toml | 3 +++ node/executor/benches/bench.rs | 2 ++ 5 files changed, 37 insertions(+) diff --git a/core/executor/src/integration_tests/mod.rs b/core/executor/src/integration_tests/mod.rs index 4041fe5cf88ba..640795f8f0d5e 100644 --- a/core/executor/src/integration_tests/mod.rs +++ b/core/executor/src/integration_tests/mod.rs @@ -33,6 +33,7 @@ use crate::{WasmExecutionMethod, call_in_wasm}; pub type TestExternalities = CoreTestExternalities; #[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] fn returning_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); @@ -50,6 +51,7 @@ fn returning_should_work(wasm_method: WasmExecutionMethod) { } #[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] fn panicking_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); @@ -87,6 +89,7 @@ fn panicking_should_work(wasm_method: WasmExecutionMethod) { } #[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] fn storage_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); @@ -116,6 +119,7 @@ fn storage_should_work(wasm_method: WasmExecutionMethod) { } #[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] fn clear_prefix_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); { @@ -149,6 +153,7 @@ fn clear_prefix_should_work(wasm_method: WasmExecutionMethod) { } #[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] fn blake2_256_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); @@ -178,6 +183,7 @@ fn blake2_256_should_work(wasm_method: WasmExecutionMethod) { } #[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] fn blake2_128_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); @@ -207,6 +213,7 @@ fn blake2_128_should_work(wasm_method: WasmExecutionMethod) { } #[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] fn twox_256_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); @@ -240,6 +247,7 @@ fn twox_256_should_work(wasm_method: WasmExecutionMethod) { } #[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] fn twox_128_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); @@ -269,6 +277,7 @@ fn twox_128_should_work(wasm_method: WasmExecutionMethod) { } #[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] fn ed25519_verify_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); @@ -310,6 +319,7 @@ fn ed25519_verify_should_work(wasm_method: WasmExecutionMethod) { } #[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] fn sr25519_verify_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); @@ -351,6 +361,7 @@ fn sr25519_verify_should_work(wasm_method: WasmExecutionMethod) { } #[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] fn ordered_trie_root_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); @@ -370,6 +381,7 @@ fn ordered_trie_root_should_work(wasm_method: WasmExecutionMethod) { } #[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] fn offchain_local_storage_should_work(wasm_method: WasmExecutionMethod) { use substrate_client::backend::OffchainStorage; @@ -393,6 +405,7 @@ fn offchain_local_storage_should_work(wasm_method: WasmExecutionMethod) { } #[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] fn offchain_http_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let (offchain, state) = testing::TestOffchainExt::new(); diff --git a/core/executor/src/integration_tests/sandbox.rs b/core/executor/src/integration_tests/sandbox.rs index 621635e6b2dc4..e4a1a0254dbc1 100644 --- a/core/executor/src/integration_tests/sandbox.rs +++ b/core/executor/src/integration_tests/sandbox.rs @@ -23,6 +23,7 @@ use crate::{WasmExecutionMethod, call_in_wasm}; use crate::integration_tests::TestExternalities; #[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] fn sandbox_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); @@ -63,6 +64,7 @@ fn sandbox_should_work(wasm_method: WasmExecutionMethod) { } #[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] fn sandbox_trap(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); @@ -92,6 +94,7 @@ fn sandbox_trap(wasm_method: WasmExecutionMethod) { } #[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] fn sandbox_should_trap_when_heap_exhausted(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); @@ -122,6 +125,7 @@ fn sandbox_should_trap_when_heap_exhausted(wasm_method: WasmExecutionMethod) { } #[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] fn start_called(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); @@ -168,6 +172,7 @@ fn start_called(wasm_method: WasmExecutionMethod) { } #[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] fn invoke_args(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); @@ -210,6 +215,7 @@ fn invoke_args(wasm_method: WasmExecutionMethod) { } #[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] fn return_val(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); @@ -240,6 +246,7 @@ fn return_val(wasm_method: WasmExecutionMethod) { } #[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] fn unlinkable_module(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); @@ -268,6 +275,7 @@ fn unlinkable_module(wasm_method: WasmExecutionMethod) { } #[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] fn corrupted_module(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); @@ -290,6 +298,7 @@ fn corrupted_module(wasm_method: WasmExecutionMethod) { } #[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] fn start_fn_ok(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); @@ -321,6 +330,7 @@ fn start_fn_ok(wasm_method: WasmExecutionMethod) { } #[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] fn start_fn_traps(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); diff --git a/core/executor/src/wasm_runtime.rs b/core/executor/src/wasm_runtime.rs index 8d2291fe04893..56afd8f45ccce 100644 --- a/core/executor/src/wasm_runtime.rs +++ b/core/executor/src/wasm_runtime.rs @@ -21,6 +21,8 @@ use crate::error::{Error, WasmError}; use crate::wasmi_execution; +#[cfg(feature = "wasmtime")] +use crate::wasmtime; use log::{trace, warn}; use codec::Decode; use primitives::{storage::well_known_keys, traits::Externalities}; @@ -51,6 +53,9 @@ pub trait WasmRuntime { pub enum WasmExecutionMethod { /// Uses the Wasmi interpreter. Interpreted, + /// Uses the Wasmtime compiled runtime. + #[cfg(feature = "wasmtime")] + Compiled, } /// Cache for the runtimes. @@ -168,6 +173,10 @@ pub fn create_wasm_runtime_with_code( WasmExecutionMethod::Interpreted => wasmi_execution::create_instance(ext, code, heap_pages) .map(|runtime| -> Box { Box::new(runtime) }), + #[cfg(feature = "wasmtime")] + WasmExecutionMethod::Compiled => + wasmtime::create_instance(ext, code, heap_pages) + .map(|runtime| -> Box { Box::new(runtime) }), } } diff --git a/node/executor/Cargo.toml b/node/executor/Cargo.toml index ebde7f434bffb..93f29910edbe0 100644 --- a/node/executor/Cargo.toml +++ b/node/executor/Cargo.toml @@ -34,6 +34,9 @@ wabt = "0.9.2" criterion = "0.3.0" [features] +wasmtime = [ + "substrate-executor/wasmtime", +] stress-test = [] [[bench]] diff --git a/node/executor/benches/bench.rs b/node/executor/benches/bench.rs index 19c53b2428f4a..e72c28467fa7f 100644 --- a/node/executor/benches/bench.rs +++ b/node/executor/benches/bench.rs @@ -189,6 +189,8 @@ fn bench_execute_block(c: &mut Criterion) { vec![ ExecutionMethod::Native, ExecutionMethod::Wasm(WasmExecutionMethod::Interpreted), + #[cfg(feature = "wasmtime")] + ExecutionMethod::Wasm(WasmExecutionMethod::Compiled), ], ); } From 5a903a80f3bc7b48cee3819599c86b52330602a9 Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Mon, 21 Oct 2019 18:19:34 +0200 Subject: [PATCH 13/26] executor: Improve FunctionExecution error messages. --- core/executor/src/error.rs | 4 ++-- core/executor/src/wasmi_execution.rs | 2 +- core/executor/src/wasmtime/function_executor.rs | 2 +- core/executor/src/wasmtime/trampoline.rs | 4 +--- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/core/executor/src/error.rs b/core/executor/src/error.rs index 52fd68ff8411e..211bc3b00fb01 100644 --- a/core/executor/src/error.rs +++ b/core/executor/src/error.rs @@ -81,8 +81,8 @@ pub enum Error { #[display(fmt="Requested allocation size is too large")] RequestedAllocationTooLarge, /// Execution of a host function failed. - #[display(fmt="Function execution failed with: {}", _0)] - FunctionExecution(String), + #[display(fmt="Host function {} execution failed with: {}", _0, _1)] + FunctionExecution(String, String), } impl std::error::Error for Error { diff --git a/core/executor/src/wasmi_execution.rs b/core/executor/src/wasmi_execution.rs index 4e5c384bd16f5..a1a889d5074d2 100644 --- a/core/executor/src/wasmi_execution.rs +++ b/core/executor/src/wasmi_execution.rs @@ -309,7 +309,7 @@ impl wasmi::Externals for FunctionExecutor { )?; function.execute(self, &mut args) - .map_err(Error::FunctionExecution) + .map_err(|msg| Error::FunctionExecution(function.name().to_string(), msg)) .map_err(wasmi::Trap::from) .map(|v| v.map(Into::into)) } diff --git a/core/executor/src/wasmtime/function_executor.rs b/core/executor/src/wasmtime/function_executor.rs index 7128024c56d4d..8926f1573a78c 100644 --- a/core/executor/src/wasmtime/function_executor.rs +++ b/core/executor/src/wasmtime/function_executor.rs @@ -184,7 +184,7 @@ impl<'a> SandboxCapabilities for FunctionExecutor<'a> { values_vec.as_mut_ptr() as *mut u8, ) } { - return Err(Error::FunctionExecution(message)); + return Err(Error::Other(message)); } // Load the return value out of `values_vec`. diff --git a/core/executor/src/wasmtime/trampoline.rs b/core/executor/src/wasmtime/trampoline.rs index 27d65afcdff88..4433313f917ae 100644 --- a/core/executor/src/wasmtime/trampoline.rs +++ b/core/executor/src/wasmtime/trampoline.rs @@ -110,9 +110,7 @@ unsafe fn stub_fn_inner( // Execute and write output back to the stack. let return_val = func.execute(&mut context, &mut args) - .map_err(|e| Error::FunctionExecution( - format!("error in external function {}: {}", func.name(), e) - ))?; + .map_err(|e| Error::FunctionExecution(func.name().to_string(), e))?; if let Some(val) = return_val { write_value_to(values_vec, val); } From a77e9bd7ae3935b008ec0d2faf6645c4af4bba6f Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Wed, 23 Oct 2019 18:09:38 +0200 Subject: [PATCH 14/26] Scope the unsafe blocks to be smaller. --- core/executor/src/wasmtime/runtime.rs | 89 ++++++++++++++++----------- 1 file changed, 52 insertions(+), 37 deletions(-) diff --git a/core/executor/src/wasmtime/runtime.rs b/core/executor/src/wasmtime/runtime.rs index 22a692ff3a31e..b4bf9036affa9 100644 --- a/core/executor/src/wasmtime/runtime.rs +++ b/core/executor/src/wasmtime/runtime.rs @@ -25,6 +25,7 @@ use crate::wasmtime::util::{cranelift_ir_signature, read_memory_into, write_memo use crate::{Externalities, RuntimeVersion}; use codec::Decode; +use cranelift_codegen::ir; use cranelift_codegen::isa::TargetIsa; use cranelift_entity::{EntityRef, PrimaryMap}; use cranelift_frontend::FunctionBuilderContext; @@ -165,29 +166,27 @@ fn call_method( .map_err(ActionError::Setup) .map_err(Error::Wasmtime)?; - let args = unsafe { - // Ideally there would be a way to set the heap pages during instantiation rather than - // growing the memory after the fact. Current this may require an additional mmap and copy. - // However, the wasmtime API doesn't support modifying the size of memory on instantiation - // at this time. - grow_memory(&mut instance, heap_pages)?; - - // Initialize the function executor state. - let executor_state = FunctionExecutorState::new( - // Unsafely extend the reference lifetime to static. This is necessary because the - // host state must be `Any`. This is OK as we will drop this object either before - // exiting the function in the happy case or in the worst case on the next runtime - // call, which also resets the host state. Thus this reference can never actually - // outlive the Context. - mem::transmute::<_, &'static mut Compiler>(context.compiler_mut()), - get_heap_base(&instance)?, - ); - reset_host_state(context, Some(executor_state))?; + // Ideally there would be a way to set the heap pages during instantiation rather than + // growing the memory after the fact. Current this may require an additional mmap and copy. + // However, the wasmtime API doesn't support modifying the size of memory on instantiation + // at this time. + grow_memory(&mut instance, heap_pages)?; + + // Initialize the function executor state. + let executor_state = FunctionExecutorState::new( + // Unsafely extend the reference lifetime to static. This is necessary because the + // host state must be `Any`. This is OK as we will drop this object either before + // exiting the function in the happy case or in the worst case on the next runtime + // call, which also resets the host state. Thus this reference can never actually + // outlive the Context. + unsafe { mem::transmute::<_, &'static mut Compiler>(context.compiler_mut()) }, + get_heap_base(&instance)?, + ); + reset_host_state(context, Some(executor_state))?; - // Write the input data into guest memory. - let (data_ptr, data_len) = inject_input_data(context, &mut instance, data)?; - [RuntimeValue::I32(u32::from(data_ptr) as i32), RuntimeValue::I32(data_len as i32)] - }; + // Write the input data into guest memory. + let (data_ptr, data_len) = inject_input_data(context, &mut instance, data)?; + let args = [RuntimeValue::I32(u32::from(data_ptr) as i32), RuntimeValue::I32(data_len as i32)]; // Invoke the function in the runtime. let outcome = externalities::set_and_run_with_externalities(ext, || { @@ -217,7 +216,7 @@ fn call_method( // Read the output data from guest memory. let mut output = vec![0; output_len]; - let memory = unsafe { get_memory_mut(&mut instance)? }; + let memory = get_memory_mut(&mut instance)?; read_memory_into(memory, output_ptr, &mut output)?; Ok(output) } @@ -290,11 +289,16 @@ fn clear_globals(global_exports: &mut HashMap>) { global_exports.remove("__indirect_function_table"); } -unsafe fn grow_memory(instance: &mut InstanceHandle, pages: u32) -> Result<()> { - let memory_index = match instance.lookup_immutable("memory") { - Some(Export::Memory { definition, vmctx: _, memory: _ }) => - instance.memory_index(&*definition), - _ => return Err(Error::InvalidMemoryReference), +fn grow_memory(instance: &mut InstanceHandle, pages: u32) -> Result<()> { + // This is safe to wrap in an unsafe block as: + // - The result of the `lookup_immutable` call is not mutated + // - The definition pointer is returned by a lookup on a valid instance + let memory_index = unsafe { + match instance.lookup_immutable("memory") { + Some(Export::Memory { definition, vmctx: _, memory: _ }) => + instance.memory_index(&*definition), + _ => return Err(Error::InvalidMemoryReference), + } }; instance.memory_grow(memory_index, pages) .map(|_| ()) @@ -318,7 +322,7 @@ fn reset_host_state(context: &mut Context, executor_state: Option Result<&mut [u8]> { +fn get_memory_mut(instance: &mut InstanceHandle) -> Result<&mut [u8]> { match instance.lookup("memory") { - Some(Export::Memory { definition, vmctx: _, memory: _ }) => + // This is safe to wrap in an unsafe block as: + // - The definition pointer is returned by a lookup on a valid instance and thus points to + // a valid memory definition + Some(Export::Memory { definition, vmctx: _, memory: _ }) => unsafe { Ok(std::slice::from_raw_parts_mut( (*definition).base, (*definition).current_length, - )), + )) + }, _ => Err(Error::InvalidMemoryReference), } } -unsafe fn get_heap_base(instance: &InstanceHandle) -> Result { - match instance.lookup_immutable("__heap_base") { - Some(Export::Global { definition, vmctx: _, global: _ }) => - Ok(*(*definition).as_u32()), - _ => return Err(Error::HeapBaseNotFoundOrInvalid), +fn get_heap_base(instance: &InstanceHandle) -> Result { + // This is safe to wrap in an unsafe block as: + // - The result of the `lookup_immutable` call is not mutated + // - The definition pointer is returned by a lookup on a valid instance + // - The defined value is checked to be an I32, which can be read safely as a u32 + unsafe { + match instance.lookup_immutable("__heap_base") { + Some(Export::Global { definition, vmctx: _, global }) + if global.ty == ir::types::I32 => + Ok(*(*definition).as_u32()), + _ => return Err(Error::HeapBaseNotFoundOrInvalid), + } } } From c964753371bdb17bdb1a211250a428808f82b46f Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Wed, 23 Oct 2019 18:16:55 +0200 Subject: [PATCH 15/26] Rename TrampolineState to EnvState. --- core/executor/src/wasmtime/runtime.rs | 26 ++++++++++++------------ core/executor/src/wasmtime/trampoline.rs | 14 ++++++------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/core/executor/src/wasmtime/runtime.rs b/core/executor/src/wasmtime/runtime.rs index b4bf9036affa9..c655b3f446df3 100644 --- a/core/executor/src/wasmtime/runtime.rs +++ b/core/executor/src/wasmtime/runtime.rs @@ -20,7 +20,7 @@ use crate::error::{Error, Result, WasmError}; use crate::host_interface::SubstrateExternals; use crate::wasm_runtime::WasmRuntime; use crate::wasmtime::function_executor::FunctionExecutorState; -use crate::wasmtime::trampoline::{TrampolineState, make_trampoline}; +use crate::wasmtime::trampoline::{EnvState, make_trampoline}; use crate::wasmtime::util::{cranelift_ir_signature, read_memory_into, write_memory_from}; use crate::{Externalities, RuntimeVersion}; @@ -182,7 +182,7 @@ fn call_method( unsafe { mem::transmute::<_, &'static mut Compiler>(context.compiler_mut()) }, get_heap_base(&instance)?, ); - reset_host_state(context, Some(executor_state))?; + reset_env_state(context, Some(executor_state))?; // Write the input data into guest memory. let (data_ptr, data_len) = inject_input_data(context, &mut instance, data)?; @@ -194,7 +194,7 @@ fn call_method( .invoke(&mut instance, method, &args[..]) .map_err(Error::Wasmtime) })?; - let trap_error = reset_host_state(context, None)?; + let trap_error = reset_env_state(context, None)?; let (output_ptr, output_len) = match outcome { ActionOutcome::Returned { values } => { if values.len() != 1 { @@ -260,7 +260,7 @@ fn instantiate_env_module(global_exports: Rc(code_memory); + let env_state = EnvState::new::(code_memory); let result = InstanceHandle::new( Rc::new(module), @@ -270,7 +270,7 @@ fn instantiate_env_module(global_exports: Rc Result<()> { .ok_or_else(|| "requested heap_pages would exceed maximum memory size".into()) } -fn get_host_state(context: &mut Context) -> Result<&mut TrampolineState> { +fn get_env_state(context: &mut Context) -> Result<&mut EnvState> { let env_instance = context.get_instance("env") .map_err(|err| format!("cannot find \"env\" module: {}", err))?; env_instance .host_state() - .downcast_mut::() + .downcast_mut::() .ok_or_else(|| "cannot get \"env\" module host state".into()) } -fn reset_host_state(context: &mut Context, executor_state: Option) +fn reset_env_state(context: &mut Context, executor_state: Option) -> Result> { - let trampoline_state = get_host_state(context)?; - trampoline_state.executor_state = executor_state; - Ok(trampoline_state.reset_trap()) + let env_state = get_env_state(context)?; + env_state.executor_state = executor_state; + Ok(env_state.take_trap()) } fn inject_input_data( @@ -327,8 +327,8 @@ fn inject_input_data( instance: &mut InstanceHandle, data: &[u8], ) -> Result<(Pointer, WordSize)> { - let trampoline_state = get_host_state(context)?; - let executor_state = trampoline_state.executor_state + let env_state = get_env_state(context)?; + let executor_state = env_state.executor_state .as_mut() .ok_or_else(|| "cannot get \"env\" module executor state")?; diff --git a/core/executor/src/wasmtime/trampoline.rs b/core/executor/src/wasmtime/trampoline.rs index 4433313f917ae..e503af630ca0c 100644 --- a/core/executor/src/wasmtime/trampoline.rs +++ b/core/executor/src/wasmtime/trampoline.rs @@ -32,7 +32,7 @@ use crate::wasmtime::function_executor::{FunctionExecutorState, FunctionExecutor /// The top-level host state of the "env" module. This state is used by the trampoline function to /// construct a `FunctionExecutor` which can execute the host call. -pub struct TrampolineState { +pub struct EnvState { externals: &'static [&'static dyn Function], trap: Option, /// The executor state stored across host calls during a single Wasm runtime call. @@ -43,10 +43,10 @@ pub struct TrampolineState { code_memory: CodeMemory, } -impl TrampolineState { - /// Construct a new `TrampolineState` which owns the given code memory. +impl EnvState { + /// Construct a new `EnvState` which owns the given code memory. pub fn new(code_memory: CodeMemory) -> Self { - TrampolineState { + EnvState { externals: HF::functions(), trap: None, executor_state: None, @@ -55,15 +55,15 @@ impl TrampolineState { } /// Resets the trap error to None and returns the current value. - pub fn reset_trap(&mut self) -> Option { - mem::replace(&mut self.trap, None) + pub fn take_trap(&mut self) -> Option { + self.trap.take() } } /// This is called by the dynamically generated trampoline taking the function index and reference /// to the call arguments on the stack as arguments. Returns 0 on success and 1 on an error. unsafe extern "C" fn stub_fn(vmctx: *mut VMContext, func_index: u32, values_vec: *mut i64) -> u32 { - if let Some(state) = (*vmctx).host_state().downcast_mut::() { + if let Some(state) = (*vmctx).host_state().downcast_mut::() { match stub_fn_inner( vmctx, state.externals, From 5e0d828df6034c1e9ee8b143eacdfc58d1267278 Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Thu, 24 Oct 2019 22:50:29 +0200 Subject: [PATCH 16/26] Let EnvState own its own compiler instead of unsafe lifetime cast. --- .../src/wasmtime/function_executor.rs | 15 ++++---- core/executor/src/wasmtime/runtime.rs | 34 ++++++++++--------- core/executor/src/wasmtime/trampoline.rs | 24 +++++++------ 3 files changed, 39 insertions(+), 34 deletions(-) diff --git a/core/executor/src/wasmtime/function_executor.rs b/core/executor/src/wasmtime/function_executor.rs index 8926f1573a78c..5dc8f42b280c4 100644 --- a/core/executor/src/wasmtime/function_executor.rs +++ b/core/executor/src/wasmtime/function_executor.rs @@ -48,18 +48,14 @@ pub struct SupervisorFuncRef(*const VMCallerCheckedAnyfunc); /// /// This is stored as part of the host state of the "env" Wasmtime instance. pub struct FunctionExecutorState { - // The lifetime of the reference is not actually static, but is unsafely cast to static since - // the compiler is owned by the wasmtime-jit `Context` and must be accessed during host calls. - compiler: &'static mut Compiler, sandbox_store: sandbox::Store, heap: FreeingBumpHeapAllocator, } impl FunctionExecutorState { /// Constructs a new `FunctionExecutorState`. - pub fn new(compiler: &'static mut Compiler, heap_base: u32) -> Self { + pub fn new(heap_base: u32) -> Self { FunctionExecutorState { - compiler, sandbox_store: sandbox::Store::new(), heap: FreeingBumpHeapAllocator::new(heap_base), } @@ -87,8 +83,11 @@ impl<'a> FunctionExecutor<'a> { /// /// The vmctx MUST come from a call to a function in the "env" module. /// The state MUST be looked up from the host state of the "env" module. - pub unsafe fn new(vmctx: *mut VMContext, state: &'a mut FunctionExecutorState) - -> Result + pub unsafe fn new( + vmctx: *mut VMContext, + compiler: &'a mut Compiler, + state: &'a mut FunctionExecutorState, + ) -> Result { let memory = match (*vmctx).lookup_global_export("memory") { Some(Export::Memory { definition, vmctx: _, memory: _ }) => @@ -107,7 +106,7 @@ impl<'a> FunctionExecutor<'a> { _ => None, }; Ok(FunctionExecutor { - compiler: &mut state.compiler, + compiler, sandbox_store: &mut state.sandbox_store, heap: &mut state.heap, memory, diff --git a/core/executor/src/wasmtime/runtime.rs b/core/executor/src/wasmtime/runtime.rs index c655b3f446df3..6f5aebb18aac4 100644 --- a/core/executor/src/wasmtime/runtime.rs +++ b/core/executor/src/wasmtime/runtime.rs @@ -33,7 +33,6 @@ use cranelift_wasm::DefinedFuncIndex; use std::cell::RefCell; use std::collections::HashMap; use std::convert::TryFrom; -use std::mem; use std::rc::Rc; use wasm_interface::{HostFunctions, Pointer, WordSize}; use wasmtime_environ::{Module, translate_signature}; @@ -128,15 +127,18 @@ pub fn create_instance(ext: &mut E, code: &[u8], heap_pages: u fn create_compiled_unit(code: &[u8]) -> std::result::Result<(CompiledModule, Context), WasmError> { - let isa = target_isa()?; - let mut context = Context::with_isa(isa, CompilationStrategy::Cranelift); + let compilation_strategy = CompilationStrategy::Cranelift; + + let compiler = new_compiler(compilation_strategy)?; + let mut context = Context::new(Box::new(compiler)); // Enable/disable producing of debug info. context.set_debug_info(false); // Instantiate and link the env module. let global_exports = context.get_global_exports(); - let env_module = instantiate_env_module(global_exports)?; + let compiler = new_compiler(compilation_strategy)?; + let env_module = instantiate_env_module(global_exports, compiler)?; context.name_instance("env".to_owned(), env_module); // Compile the wasm module. @@ -173,15 +175,8 @@ fn call_method( grow_memory(&mut instance, heap_pages)?; // Initialize the function executor state. - let executor_state = FunctionExecutorState::new( - // Unsafely extend the reference lifetime to static. This is necessary because the - // host state must be `Any`. This is OK as we will drop this object either before - // exiting the function in the happy case or in the worst case on the next runtime - // call, which also resets the host state. Thus this reference can never actually - // outlive the Context. - unsafe { mem::transmute::<_, &'static mut Compiler>(context.compiler_mut()) }, - get_heap_base(&instance)?, - ); + let heap_base = get_heap_base(&instance)?; + let executor_state = FunctionExecutorState::new(heap_base); reset_env_state(context, Some(executor_state))?; // Write the input data into guest memory. @@ -222,8 +217,10 @@ fn call_method( } /// The implementation is based on wasmtime_wasi::instantiate_wasi. -fn instantiate_env_module(global_exports: Rc>>>) - -> std::result::Result +fn instantiate_env_module( + global_exports: Rc>>>, + compiler: Compiler, +) -> std::result::Result { let isa = target_isa()?; let pointer_type = isa.pointer_type(); @@ -260,7 +257,7 @@ fn instantiate_env_module(global_exports: Rc(code_memory); + let env_state = EnvState::new::(code_memory, compiler); let result = InstanceHandle::new( Rc::new(module), @@ -283,6 +280,11 @@ fn target_isa() -> std::result::Result, WasmError> { Ok(isa_builder.finish(cranelift_codegen::settings::Flags::new(flag_builder))) } +fn new_compiler(strategy: CompilationStrategy) -> std::result::Result { + let isa = target_isa()?; + Ok(Compiler::new(isa, strategy)) +} + fn clear_globals(global_exports: &mut HashMap>) { global_exports.remove("memory"); global_exports.remove("__heap_base"); diff --git a/core/executor/src/wasmtime/trampoline.rs b/core/executor/src/wasmtime/trampoline.rs index e503af630ca0c..ce0696e2e34c9 100644 --- a/core/executor/src/wasmtime/trampoline.rs +++ b/core/executor/src/wasmtime/trampoline.rs @@ -22,10 +22,10 @@ use cranelift_codegen::{Context, binemit, ir, isa}; use cranelift_codegen::ir::{InstBuilder, StackSlotData, StackSlotKind, TrapCode}; use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; -use wasmtime_jit::CodeMemory; +use wasmtime_jit::{CodeMemory, Compiler}; use wasmtime_runtime::{VMContext, VMFunctionBody}; use wasm_interface::{HostFunctions, Function, Value, ValueType}; -use std::{cmp, mem, ptr}; +use std::{cmp, ptr}; use crate::error::{Error, Result}; use crate::wasmtime::function_executor::{FunctionExecutorState, FunctionExecutor}; @@ -34,23 +34,25 @@ use crate::wasmtime::function_executor::{FunctionExecutorState, FunctionExecutor /// construct a `FunctionExecutor` which can execute the host call. pub struct EnvState { externals: &'static [&'static dyn Function], + compiler: Compiler, + // The code memory must be kept around on the state to prevent it from being dropped. + #[allow(dead_code)] + code_memory: CodeMemory, trap: Option, /// The executor state stored across host calls during a single Wasm runtime call. /// During a runtime call, this MUST be `Some`. pub executor_state: Option, - // The code memory must be kept around on the state to prevent it from being dropped. - #[allow(dead_code)] - code_memory: CodeMemory, } impl EnvState { /// Construct a new `EnvState` which owns the given code memory. - pub fn new(code_memory: CodeMemory) -> Self { + pub fn new(code_memory: CodeMemory, compiler: Compiler) -> Self { EnvState { externals: HF::functions(), trap: None, - executor_state: None, + compiler, code_memory, + executor_state: None, } } @@ -67,8 +69,9 @@ unsafe extern "C" fn stub_fn(vmctx: *mut VMContext, func_index: u32, values_vec: match stub_fn_inner( vmctx, state.externals, - func_index, + &mut state.compiler, state.executor_state.as_mut(), + func_index, values_vec ) { Ok(()) => 0, @@ -88,8 +91,9 @@ unsafe extern "C" fn stub_fn(vmctx: *mut VMContext, func_index: u32, values_vec: unsafe fn stub_fn_inner( vmctx: *mut VMContext, externals: &[&dyn Function], - func_index: u32, + compiler: &mut Compiler, executor_state: Option<&mut FunctionExecutorState>, + func_index: u32, values_vec: *mut i64, ) -> Result<()> { @@ -99,7 +103,7 @@ unsafe fn stub_fn_inner( .ok_or_else(|| "executor state is None during call to external function")?; // Build the external function context. - let mut context = FunctionExecutor::new(vmctx, executor_state)?; + let mut context = FunctionExecutor::new(vmctx, compiler, executor_state)?; let signature = func.signature(); From 7623f127e26bc31ab6559cb33f290bb5b234cf99 Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Fri, 25 Oct 2019 11:35:28 +0200 Subject: [PATCH 17/26] Refactor out some common wasmi/wasmtime logic. --- core/executor/src/wasm_utils.rs | 13 +++++++++++++ core/executor/src/wasmi_execution.rs | 8 ++++---- core/executor/src/wasmtime/runtime.rs | 18 ++++++------------ 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/core/executor/src/wasm_utils.rs b/core/executor/src/wasm_utils.rs index b217350ac6fc4..6c1b1ebc50ac1 100644 --- a/core/executor/src/wasm_utils.rs +++ b/core/executor/src/wasm_utils.rs @@ -16,6 +16,8 @@ //! Utilities for defining the wasm host environment. +use wasm_interface::{Pointer, WordSize}; + /// Converts arguments into respective WASM types. #[macro_export] macro_rules! convert_args { @@ -171,3 +173,14 @@ macro_rules! impl_wasm_host_interface { } ); } + +/// Runtime API functions return an i64 which encodes a pointer in the least-significant 32 bits +/// and a length in the most-significant 32 bits. This interprets the returned value as a pointer, +/// length tuple. +pub fn interpret_runtime_api_result(retval: i64) -> (Pointer, WordSize) { + let ptr = >::new(retval as u32); + // The first cast to u64 is necessary so that the right shift does not sign-extend. + let len = ((retval as u64) >> 32) as WordSize; + (ptr, len) +} + diff --git a/core/executor/src/wasmi_execution.rs b/core/executor/src/wasmi_execution.rs index a1a889d5074d2..08d3b7bb12ea0 100644 --- a/core/executor/src/wasmi_execution.rs +++ b/core/executor/src/wasmi_execution.rs @@ -27,6 +27,7 @@ use primitives::{sandbox as sandbox_primitives, traits::Externalities}; use crate::host_interface::SubstrateExternals; use crate::sandbox; use crate::allocator; +use crate::wasm_utils::interpret_runtime_api_result; use crate::wasm_runtime::WasmRuntime; use log::trace; use parity_wasm::elements::{deserialize_buffer, DataSegment, Instruction, Module as RawModule}; @@ -356,10 +357,9 @@ fn call_in_wasm_module( Ok(vec![I32(offset as i32), I32(data.len() as i32)]) }, |res, memory| { - if let Some(I64(r)) = res { - let offset = r as u32; - let length = (r as u64 >> 32) as usize; - memory.get(offset, length).map_err(|_| Error::Runtime).map(Some) + if let Some(I64(retval)) = res { + let (ptr, length) = interpret_runtime_api_result(retval); + memory.get(ptr.into(), length as usize).map_err(|_| Error::Runtime).map(Some) } else { Ok(None) } diff --git a/core/executor/src/wasmtime/runtime.rs b/core/executor/src/wasmtime/runtime.rs index 6f5aebb18aac4..9b69f13c24b55 100644 --- a/core/executor/src/wasmtime/runtime.rs +++ b/core/executor/src/wasmtime/runtime.rs @@ -19,6 +19,7 @@ use crate::error::{Error, Result, WasmError}; use crate::host_interface::SubstrateExternals; use crate::wasm_runtime::WasmRuntime; +use crate::wasm_utils::interpret_runtime_api_result; use crate::wasmtime::function_executor::FunctionExecutorState; use crate::wasmtime::trampoline::{EnvState, make_trampoline}; use crate::wasmtime::util::{cranelift_ir_signature, read_memory_into, write_memory_from}; @@ -191,17 +192,10 @@ fn call_method( })?; let trap_error = reset_env_state(context, None)?; let (output_ptr, output_len) = match outcome { - ActionOutcome::Returned { values } => { - if values.len() != 1 { - return Err(Error::InvalidReturn); - } - if let RuntimeValue::I64(val) = values[0] { - let output_ptr = >::new(val as u32); - let output_len = ((val as u64) >> 32) as usize; - (output_ptr, output_len) - } else { - return Err(Error::InvalidReturn); - } + ActionOutcome::Returned { values } => match values.as_slice() { + [RuntimeValue::I64(retval)] => + interpret_runtime_api_result(*retval), + _ => return Err(Error::InvalidReturn), } ActionOutcome::Trapped { message } => return Err(trap_error.unwrap_or_else(|| @@ -210,7 +204,7 @@ fn call_method( }; // Read the output data from guest memory. - let mut output = vec![0; output_len]; + let mut output = vec![0; output_len as usize]; let memory = get_memory_mut(&mut instance)?; read_memory_into(memory, output_ptr, &mut output)?; Ok(output) From 7467e3545da57105326e092238cf9690ccc0ea2e Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Fri, 25 Oct 2019 11:41:49 +0200 Subject: [PATCH 18/26] Typos and cosmetic changes. --- core/executor/src/wasmtime/runtime.rs | 12 +++++++----- core/executor/src/wasmtime/util.rs | 1 - 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/core/executor/src/wasmtime/runtime.rs b/core/executor/src/wasmtime/runtime.rs index 9b69f13c24b55..7e2ee7fe24470 100644 --- a/core/executor/src/wasmtime/runtime.rs +++ b/core/executor/src/wasmtime/runtime.rs @@ -170,7 +170,7 @@ fn call_method( .map_err(Error::Wasmtime)?; // Ideally there would be a way to set the heap pages during instantiation rather than - // growing the memory after the fact. Current this may require an additional mmap and copy. + // growing the memory after the fact. Currently this may require an additional mmap and copy. // However, the wasmtime API doesn't support modifying the size of memory on instantiation // at this time. grow_memory(&mut instance, heap_pages)?; @@ -178,7 +178,7 @@ fn call_method( // Initialize the function executor state. let heap_base = get_heap_base(&instance)?; let executor_state = FunctionExecutorState::new(heap_base); - reset_env_state(context, Some(executor_state))?; + reset_env_state_and_take_trap(context, Some(executor_state))?; // Write the input data into guest memory. let (data_ptr, data_len) = inject_input_data(context, &mut instance, data)?; @@ -190,7 +190,7 @@ fn call_method( .invoke(&mut instance, method, &args[..]) .map_err(Error::Wasmtime) })?; - let trap_error = reset_env_state(context, None)?; + let trap_error = reset_env_state_and_take_trap(context, None)?; let (output_ptr, output_len) = match outcome { ActionOutcome::Returned { values } => match values.as_slice() { [RuntimeValue::I64(retval)] => @@ -310,8 +310,10 @@ fn get_env_state(context: &mut Context) -> Result<&mut EnvState> { .ok_or_else(|| "cannot get \"env\" module host state".into()) } -fn reset_env_state(context: &mut Context, executor_state: Option) - -> Result> +fn reset_env_state_and_take_trap( + context: &mut Context, + executor_state: Option, +) -> Result> { let env_state = get_env_state(context)?; env_state.executor_state = executor_state; diff --git a/core/executor/src/wasmtime/util.rs b/core/executor/src/wasmtime/util.rs index 2502c3cc5fb8d..874ccc8c85fbe 100644 --- a/core/executor/src/wasmtime/util.rs +++ b/core/executor/src/wasmtime/util.rs @@ -20,7 +20,6 @@ use cranelift_codegen::{ir, isa}; use std::ops::Range; use wasm_interface::{Pointer, Signature, ValueType}; - /// Read data from a slice of memory into a destination buffer. /// /// Returns an error if the read would go out of the memory bounds. From b73465c7443e27d75afa41e2160abb5e66bcdacb Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Fri, 25 Oct 2019 12:02:45 +0200 Subject: [PATCH 19/26] More trampoline comments. --- core/executor/src/wasmtime/trampoline.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/core/executor/src/wasmtime/trampoline.rs b/core/executor/src/wasmtime/trampoline.rs index ce0696e2e34c9..ebb15a4aad59d 100644 --- a/core/executor/src/wasmtime/trampoline.rs +++ b/core/executor/src/wasmtime/trampoline.rs @@ -124,7 +124,16 @@ unsafe fn stub_fn_inner( /// Create a trampoline for invoking a host function. /// -/// This code is mostly copied from wasmtime's wasmtime-api/src/trampoline/func.rs. +/// The trampoline is a dynamically generated entry point to a runtime host call. The function is +/// generated by manually constructing Cranelift IR and using the Cranelift compiler. The +/// trampoline embeds the function index (the `call_id` parameter) as a constant and delegates to +/// a stub function in Rust, which takes the function index and a memory reference to the stack +/// arguments and return value slots. +/// +/// This code is taken entirely from wasmtime's wasmtime-api/src/trampoline/func.rs. It is +/// deliberately not modified to improve readability in this code base in order to simplify diffs +/// with the upstream code and updates. We do, however, insert additional comments beginning with +/// "NOTE:" for clarity when helpful. pub fn make_trampoline( isa: &dyn isa::TargetIsa, code_memory: &mut CodeMemory, @@ -152,6 +161,10 @@ pub fn make_trampoline( // Add error/trap return. stub_sig.returns.push(ir::AbiParam::new(ir::types::I32)); + // NOTE: Each parameter and return value gets a 64-bit (8-byte) wide slot on the stack, as that + // is large enough to fit all Wasm primitive types. The `VMContext` pointer, which is a + // parameter the function signature, is excluded as it is passed directly to the stub function, + // rather than being looked up on the caller stack from the `values_vec` pointer. let values_vec_len = 8 * cmp::max(signature.params.len() - 1, signature.returns.len()) as u32; let mut context = Context::new(); From d5b72c6de98884fd5ab98ba6dee9884be09b449a Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Mon, 28 Oct 2019 18:09:15 +0100 Subject: [PATCH 20/26] Cargo.lock update. --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6ad0cff53bdc6..a5ef8c44e5ddd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6269,9 +6269,9 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", ] From 779d4b475a55ec464a59208996b3579126f29259 Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Fri, 25 Oct 2019 15:09:00 +0200 Subject: [PATCH 21/26] cli: CLI option for running Substrate with compiled Wasm execution. --- Cargo.toml | 5 +++++ core/cli/Cargo.toml | 5 +++++ core/cli/src/params.rs | 21 ++++++++++++++++++++- core/service/Cargo.toml | 5 +++++ node/cli/Cargo.toml | 7 +++++++ 5 files changed, 42 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 2d68511b2e003..131a3c9408fab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -133,3 +133,8 @@ is-it-maintained-open-issues = { repository = "paritytech/substrate" } [profile.release] # Substrate runtime requires unwinding. panic = "unwind" + +[features] +wasmtime = [ + "cli/wasmtime", +] diff --git a/core/cli/Cargo.toml b/core/cli/Cargo.toml index a2723484ad4c1..07e389e763053 100644 --- a/core/cli/Cargo.toml +++ b/core/cli/Cargo.toml @@ -38,3 +38,8 @@ rpassword = "4.0.1" [dev-dependencies] tempdir = "0.3.7" + +[features] +wasmtime = [ + "service/wasmtime", +] diff --git a/core/cli/src/params.rs b/core/cli/src/params.rs index 4480736066400..9b03da699c944 100644 --- a/core/cli/src/params.rs +++ b/core/cli/src/params.rs @@ -50,6 +50,19 @@ arg_enum! { pub enum WasmExecutionMethod { // Uses an interpreter. Interpreted, + // Uses a compiled runtime. + Compiled, + } +} + +impl WasmExecutionMethod { + /// Returns list of variants that are not disabled by feature flags. + fn enabled_variants() -> Vec<&'static str> { + Self::variants() + .iter() + .cloned() + .filter(|&name| cfg!(feature = "wasmtime") || name != "Compiled") + .collect() } } @@ -57,6 +70,12 @@ impl Into for WasmExecutionMethod { fn into(self) -> service::config::WasmExecutionMethod { match self { WasmExecutionMethod::Interpreted => service::config::WasmExecutionMethod::Interpreted, + #[cfg(feature = "wasmtime")] + WasmExecutionMethod::Compiled => service::config::WasmExecutionMethod::Compiled, + #[cfg(not(feature = "wasmtime"))] + WasmExecutionMethod::Compiled => panic!( + "Substrate must be compiled with \"wasmtime\" feature for compiled Wasm execution" + ), } } } @@ -407,7 +426,7 @@ pub struct RunCmd { #[structopt( long = "wasm-execution", value_name = "METHOD", - possible_values = &WasmExecutionMethod::variants(), + possible_values = &WasmExecutionMethod::enabled_variants(), case_insensitive = true, default_value = "Interpreted" )] diff --git a/core/service/Cargo.toml b/core/service/Cargo.toml index 0d6f27936244f..55fe9315fb37e 100644 --- a/core/service/Cargo.toml +++ b/core/service/Cargo.toml @@ -48,3 +48,8 @@ babe-primitives = { package = "substrate-consensus-babe-primitives", path = "../ grandpa = { package = "substrate-finality-grandpa", path = "../../core/finality-grandpa" } grandpa-primitives = { package = "substrate-finality-grandpa-primitives", path = "../../core/finality-grandpa/primitives" } tokio = "0.1" + +[features] +wasmtime = [ + "substrate-executor/wasmtime", +] diff --git a/node/cli/Cargo.toml b/node/cli/Cargo.toml index 7e011d9de8687..fd9f86c588881 100644 --- a/node/cli/Cargo.toml +++ b/node/cli/Cargo.toml @@ -63,3 +63,10 @@ tempfile = "3.1.0" [build-dependencies] cli = { package = "substrate-cli", path = "../../core/cli" } structopt = "0.3.3" + +[features] +wasmtime = [ + "cli/wasmtime", + "node-executor/wasmtime", + "substrate-service/wasmtime", +] From 5ffda8154790b822080961cd7fe4bd04a1c7c5c0 Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Mon, 28 Oct 2019 18:46:54 +0100 Subject: [PATCH 22/26] executor: Switch dependency from fork to official wasmtime repo. --- Cargo.lock | 32 ++++++++++++++++---------------- core/executor/Cargo.toml | 6 +++--- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a5ef8c44e5ddd..8dabddcb73cef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5505,9 +5505,9 @@ dependencies = [ "tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "wabt 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", "wasmi 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmtime-environ 0.2.0 (git+https://github.com/jimpo/wasmtime.git?branch=context-compile)", - "wasmtime-jit 0.2.0 (git+https://github.com/jimpo/wasmtime.git?branch=context-compile)", - "wasmtime-runtime 0.2.0 (git+https://github.com/jimpo/wasmtime.git?branch=context-compile)", + "wasmtime-environ 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)", + "wasmtime-jit 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)", + "wasmtime-runtime 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)", ] [[package]] @@ -6975,7 +6975,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "wasmtime-debug" version = "0.2.0" -source = "git+https://github.com/jimpo/wasmtime.git?branch=context-compile#754dd01be66d58fff6b352f1c212e8986e143f1e" +source = "git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6#71dd73d672deb325664e3c9cd4ee7acebed5fb95" dependencies = [ "cranelift-codegen 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", "cranelift-entity 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -6986,13 +6986,13 @@ dependencies = [ "gimli 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", "target-lexicon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "wasmparser 0.39.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmtime-environ 0.2.0 (git+https://github.com/jimpo/wasmtime.git?branch=context-compile)", + "wasmtime-environ 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)", ] [[package]] name = "wasmtime-environ" version = "0.2.0" -source = "git+https://github.com/jimpo/wasmtime.git?branch=context-compile#754dd01be66d58fff6b352f1c212e8986e143f1e" +source = "git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6#71dd73d672deb325664e3c9cd4ee7acebed5fb95" dependencies = [ "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -7020,7 +7020,7 @@ dependencies = [ [[package]] name = "wasmtime-jit" version = "0.2.0" -source = "git+https://github.com/jimpo/wasmtime.git?branch=context-compile#754dd01be66d58fff6b352f1c212e8986e143f1e" +source = "git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6#71dd73d672deb325664e3c9cd4ee7acebed5fb95" dependencies = [ "cranelift-codegen 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", "cranelift-entity 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -7031,15 +7031,15 @@ dependencies = [ "region 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "target-lexicon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "wasmparser 0.39.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmtime-debug 0.2.0 (git+https://github.com/jimpo/wasmtime.git?branch=context-compile)", - "wasmtime-environ 0.2.0 (git+https://github.com/jimpo/wasmtime.git?branch=context-compile)", - "wasmtime-runtime 0.2.0 (git+https://github.com/jimpo/wasmtime.git?branch=context-compile)", + "wasmtime-debug 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)", + "wasmtime-environ 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)", + "wasmtime-runtime 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)", ] [[package]] name = "wasmtime-runtime" version = "0.2.0" -source = "git+https://github.com/jimpo/wasmtime.git?branch=context-compile#754dd01be66d58fff6b352f1c212e8986e143f1e" +source = "git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6#71dd73d672deb325664e3c9cd4ee7acebed5fb95" dependencies = [ "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", "cranelift-codegen 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -7052,7 +7052,7 @@ dependencies = [ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "region 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmtime-environ 0.2.0 (git+https://github.com/jimpo/wasmtime.git?branch=context-compile)", + "wasmtime-environ 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -7791,10 +7791,10 @@ dependencies = [ "checksum wasmi 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f31d26deb2d9a37e6cfed420edce3ed604eab49735ba89035e13c98f9a528313" "checksum wasmi-validation 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6bc0356e3df56e639fc7f7d8a99741915531e27ed735d911ed83d7e1339c8188" "checksum wasmparser 0.39.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e5083b449454f7de0b15f131eee17de54b5a71dcb9adcf11df2b2f78fad0cd82" -"checksum wasmtime-debug 0.2.0 (git+https://github.com/jimpo/wasmtime.git?branch=context-compile)" = "" -"checksum wasmtime-environ 0.2.0 (git+https://github.com/jimpo/wasmtime.git?branch=context-compile)" = "" -"checksum wasmtime-jit 0.2.0 (git+https://github.com/jimpo/wasmtime.git?branch=context-compile)" = "" -"checksum wasmtime-runtime 0.2.0 (git+https://github.com/jimpo/wasmtime.git?branch=context-compile)" = "" +"checksum wasmtime-debug 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)" = "" +"checksum wasmtime-environ 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)" = "" +"checksum wasmtime-jit 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)" = "" +"checksum wasmtime-runtime 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)" = "" "checksum web-sys 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)" = "c84440699cd02ca23bed6f045ffb1497bc18a3c2628bd13e2093186faaaacf6b" "checksum webpki 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4f7e1cd7900a3a6b65a3e8780c51a3e6b59c0e2c55c6dc69578c288d69f7d082" "checksum webpki-roots 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c10fa4212003ba19a564f25cd8ab572c6791f99a03cc219c13ed35ccab00de0e" diff --git a/core/executor/Cargo.toml b/core/executor/Cargo.toml index ab2fa131e762a..c5604d50e4a0f 100644 --- a/core/executor/Cargo.toml +++ b/core/executor/Cargo.toml @@ -28,9 +28,9 @@ cranelift-entity = { version = "0.46.1", optional = true } cranelift-frontend = { version = "0.46.1", optional = true } cranelift-native = { version = "0.46.1", optional = true } cranelift-wasm = { version = "0.46.1", optional = true } -wasmtime-environ = { version = "0.2", optional = true, git = "https://github.com/jimpo/wasmtime.git", branch = "context-compile" } -wasmtime-jit = { version = "0.2", optional = true, git = "https://github.com/jimpo/wasmtime.git", branch = "context-compile" } -wasmtime-runtime = { version = "0.2", optional = true, git = "https://github.com/jimpo/wasmtime.git", branch = "context-compile" } +wasmtime-environ = { version = "0.2", optional = true, git = "https://github.com/CraneStation/wasmtime.git", rev = "71dd73d6" } +wasmtime-jit = { version = "0.2", optional = true, git = "https://github.com/CraneStation/wasmtime.git", rev = "71dd73d6" } +wasmtime-runtime = { version = "0.2", optional = true, git = "https://github.com/CraneStation/wasmtime.git", rev = "71dd73d6" } [dev-dependencies] assert_matches = "1.3.0" From 70896c020a3b84f25f05f94193e7752d73dbe47e Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Mon, 28 Oct 2019 20:34:40 +0100 Subject: [PATCH 23/26] Quiet down cranelift logs. --- core/cli/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/core/cli/src/lib.rs b/core/cli/src/lib.rs index b49f686ae62f7..bdd0a31d72e84 100644 --- a/core/cli/src/lib.rs +++ b/core/cli/src/lib.rs @@ -869,6 +869,7 @@ fn init_logger(pattern: &str) { // Disable info logging by default for some modules: builder.filter(Some("ws"), log::LevelFilter::Off); builder.filter(Some("hyper"), log::LevelFilter::Warn); + builder.filter(Some("cranelift_wasm"), log::LevelFilter::Warn); // Enable info for others. builder.filter(None, log::LevelFilter::Info); From 4b0cdc8565c95aafa832fb187acbe7f726a665a3 Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Wed, 30 Oct 2019 10:49:22 +0100 Subject: [PATCH 24/26] Explicitly catch panics during host calls. We do this to ensure that panics do not cross language boundaries. --- core/executor/src/wasmtime/trampoline.rs | 47 ++++++++++++++---------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/core/executor/src/wasmtime/trampoline.rs b/core/executor/src/wasmtime/trampoline.rs index ebb15a4aad59d..b6fa34ee3c374 100644 --- a/core/executor/src/wasmtime/trampoline.rs +++ b/core/executor/src/wasmtime/trampoline.rs @@ -25,11 +25,16 @@ use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use wasmtime_jit::{CodeMemory, Compiler}; use wasmtime_runtime::{VMContext, VMFunctionBody}; use wasm_interface::{HostFunctions, Function, Value, ValueType}; -use std::{cmp, ptr}; +use std::{cmp, panic, ptr}; use crate::error::{Error, Result}; use crate::wasmtime::function_executor::{FunctionExecutorState, FunctionExecutor}; +const CALL_SUCCESS: u32 = 0; +const CALL_FAILED_WITH_ERROR: u32 = 1; +const CALL_WITH_BAD_HOST_STATE: u32 = 2; +const CALL_PANICKED: u32 = 3; + /// The top-level host state of the "env" module. This state is used by the trampoline function to /// construct a `FunctionExecutor` which can execute the host call. pub struct EnvState { @@ -63,27 +68,31 @@ impl EnvState { } /// This is called by the dynamically generated trampoline taking the function index and reference -/// to the call arguments on the stack as arguments. Returns 0 on success and 1 on an error. +/// to the call arguments on the stack as arguments. Returns zero on success and a non-zero value +/// on failure. unsafe extern "C" fn stub_fn(vmctx: *mut VMContext, func_index: u32, values_vec: *mut i64) -> u32 { - if let Some(state) = (*vmctx).host_state().downcast_mut::() { - match stub_fn_inner( - vmctx, - state.externals, - &mut state.compiler, - state.executor_state.as_mut(), - func_index, - values_vec - ) { - Ok(()) => 0, - Err(err) => { - state.trap = Some(err); - 1 + let result = panic::catch_unwind(|| { + if let Some(state) = (*vmctx).host_state().downcast_mut::() { + match stub_fn_inner( + vmctx, + state.externals, + &mut state.compiler, + state.executor_state.as_mut(), + func_index, + values_vec + ) { + Ok(()) => CALL_SUCCESS, + Err(err) => { + state.trap = Some(err); + CALL_FAILED_WITH_ERROR + } } + } else { + // Well, we can't even set a trap message, so we'll just exit without one. + CALL_WITH_BAD_HOST_STATE } - } else { - // Well, we can't even set a trap message, so we'll just exit without one. - 1 - } + }); + result.unwrap_or(CALL_PANICKED) } /// Implements most of the logic in `stub_fn` but returning a `Result` instead of an integer error From 28f7c11aecc2d3f8dd02f48aec2fab8f096eeeb4 Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Wed, 30 Oct 2019 11:32:29 +0100 Subject: [PATCH 25/26] Additional checks and clarifications in make_trampoline. --- core/executor/src/error.rs | 2 +- core/executor/src/wasmi_execution.rs | 2 +- core/executor/src/wasmtime/runtime.rs | 2 +- core/executor/src/wasmtime/trampoline.rs | 78 +++++++++++++----------- 4 files changed, 46 insertions(+), 38 deletions(-) diff --git a/core/executor/src/error.rs b/core/executor/src/error.rs index 211bc3b00fb01..83168d598e03c 100644 --- a/core/executor/src/error.rs +++ b/core/executor/src/error.rs @@ -126,7 +126,7 @@ pub enum WasmError { /// The number of heap pages requested is disallowed by the module. InvalidHeapPages, /// Instantiation error. - Instantiation(Error), + Instantiation(String), /// The compiler does not support the host machine as a target. #[cfg(feature = "wasmtime")] MissingCompilerSupport(&'static str), diff --git a/core/executor/src/wasmi_execution.rs b/core/executor/src/wasmi_execution.rs index 08d3b7bb12ea0..410b79915cb71 100644 --- a/core/executor/src/wasmi_execution.rs +++ b/core/executor/src/wasmi_execution.rs @@ -614,7 +614,7 @@ pub fn create_instance(ext: &mut E, code: &[u8], heap_pages: u // Instantiate this module. let instance = instantiate_module(heap_pages as usize, &module) - .map_err(WasmError::Instantiation)?; + .map_err(|e| WasmError::Instantiation(e.to_string()))?; // Take state snapshot before executing anything. let state_snapshot = StateSnapshot::take(&instance, data_segments, heap_pages) diff --git a/core/executor/src/wasmtime/runtime.rs b/core/executor/src/wasmtime/runtime.rs index 7e2ee7fe24470..19c24c5d553bb 100644 --- a/core/executor/src/wasmtime/runtime.rs +++ b/core/executor/src/wasmtime/runtime.rs @@ -242,7 +242,7 @@ fn instantiate_env_module( &mut fn_builder_ctx, func_id.index() as u32, &sig, - ); + )?; finished_functions.push(trampoline); } diff --git a/core/executor/src/wasmtime/trampoline.rs b/core/executor/src/wasmtime/trampoline.rs index b6fa34ee3c374..7abc59faa5ef5 100644 --- a/core/executor/src/wasmtime/trampoline.rs +++ b/core/executor/src/wasmtime/trampoline.rs @@ -27,7 +27,7 @@ use wasmtime_runtime::{VMContext, VMFunctionBody}; use wasm_interface::{HostFunctions, Function, Value, ValueType}; use std::{cmp, panic, ptr}; -use crate::error::{Error, Result}; +use crate::error::{Error, WasmError}; use crate::wasmtime::function_executor::{FunctionExecutorState, FunctionExecutor}; const CALL_SUCCESS: u32 = 0; @@ -35,6 +35,13 @@ const CALL_FAILED_WITH_ERROR: u32 = 1; const CALL_WITH_BAD_HOST_STATE: u32 = 2; const CALL_PANICKED: u32 = 3; +/// A code to trap with that indicates a host call error. +const TRAP_USER_CODE: u16 = 0; + +/// The only Wasm types allowed in host function signatures (I32, I64, F32, F64) are all +/// represented in at most 8 bytes. +const MAX_WASM_TYPE_SIZE: usize = 8; + /// The top-level host state of the "env" module. This state is used by the trampoline function to /// construct a `FunctionExecutor` which can execute the host call. pub struct EnvState { @@ -79,7 +86,7 @@ unsafe extern "C" fn stub_fn(vmctx: *mut VMContext, func_index: u32, values_vec: &mut state.compiler, state.executor_state.as_mut(), func_index, - values_vec + values_vec, ) { Ok(()) => CALL_SUCCESS, Err(err) => { @@ -104,7 +111,7 @@ unsafe fn stub_fn_inner( executor_state: Option<&mut FunctionExecutorState>, func_index: u32, values_vec: *mut i64, -) -> Result<()> +) -> Result<(), Error> { let func = externals.get(func_index as usize) .ok_or_else(|| format!("call to undefined external function with index {}", func_index))?; @@ -135,33 +142,36 @@ unsafe fn stub_fn_inner( /// /// The trampoline is a dynamically generated entry point to a runtime host call. The function is /// generated by manually constructing Cranelift IR and using the Cranelift compiler. The -/// trampoline embeds the function index (the `call_id` parameter) as a constant and delegates to -/// a stub function in Rust, which takes the function index and a memory reference to the stack -/// arguments and return value slots. +/// trampoline embeds the function index as a constant and delegates to a stub function in Rust, +/// which takes the function index and a memory reference to the stack arguments and return value +/// slots. /// -/// This code is taken entirely from wasmtime's wasmtime-api/src/trampoline/func.rs. It is -/// deliberately not modified to improve readability in this code base in order to simplify diffs -/// with the upstream code and updates. We do, however, insert additional comments beginning with -/// "NOTE:" for clarity when helpful. +/// This code is of modified copy of wasmtime's wasmtime-api/src/trampoline/func.rs. pub fn make_trampoline( isa: &dyn isa::TargetIsa, code_memory: &mut CodeMemory, fn_builder_ctx: &mut FunctionBuilderContext, - call_id: u32, + func_index: u32, signature: &ir::Signature, -) -> *const VMFunctionBody { +) -> Result<*const VMFunctionBody, WasmError> { // Mostly reverse copy of the similar method from wasmtime's // wasmtime-jit/src/compiler.rs. let pointer_type = isa.pointer_type(); let mut stub_sig = ir::Signature::new(isa.frontend_config().default_call_conv); + // Ensure that the first parameter of the generated function is the `VMContext` pointer. + assert_eq!( + signature.params[0], + ir::AbiParam::special(pointer_type, ir::ArgumentPurpose::VMContext) + ); + // Add the `vmctx` parameter. stub_sig.params.push(ir::AbiParam::special( pointer_type, ir::ArgumentPurpose::VMContext, )); - // Add the `call_id` parameter. + // Add the `func_index` parameter. stub_sig.params.push(ir::AbiParam::new(ir::types::I32)); // Add the `values_vec` parameter. @@ -170,11 +180,13 @@ pub fn make_trampoline( // Add error/trap return. stub_sig.returns.push(ir::AbiParam::new(ir::types::I32)); - // NOTE: Each parameter and return value gets a 64-bit (8-byte) wide slot on the stack, as that - // is large enough to fit all Wasm primitive types. The `VMContext` pointer, which is a - // parameter the function signature, is excluded as it is passed directly to the stub function, - // rather than being looked up on the caller stack from the `values_vec` pointer. - let values_vec_len = 8 * cmp::max(signature.params.len() - 1, signature.returns.len()) as u32; + // Each parameter and return value gets a 64-bit (8-byte) wide slot on the stack, as that is + // large enough to fit all Wasm primitive types that can be used in host function signatures. + // The `VMContext` pointer, which is a parameter of the function signature, is excluded as it + // is passed directly to the stub function rather than being looked up on the caller stack from + // the `values_vec` pointer. + let values_vec_len = cmp::max(signature.params.len() - 1, signature.returns.len()); + let values_vec_size = (MAX_WASM_TYPE_SIZE * values_vec_len) as u32; let mut context = Context::new(); context.func = @@ -182,9 +194,8 @@ pub fn make_trampoline( let ss = context.func.create_stack_slot(StackSlotData::new( StackSlotKind::ExplicitSlot, - values_vec_len, + values_vec_size, )); - let value_size = 8; { let mut builder = FunctionBuilder::new(&mut context.func, fn_builder_ctx); @@ -197,23 +208,19 @@ pub fn make_trampoline( let values_vec_ptr_val = builder.ins().stack_addr(pointer_type, ss, 0); let mflags = ir::MemFlags::trusted(); for i in 1..signature.params.len() { - if i == 0 { - continue; - } - let val = builder.func.dfg.ebb_params(block0)[i]; builder.ins().store( mflags, val, values_vec_ptr_val, - ((i - 1) * value_size) as i32, + ((i - 1) * MAX_WASM_TYPE_SIZE) as i32, ); } let vmctx_ptr_val = builder.func.dfg.ebb_params(block0)[0]; - let call_id_val = builder.ins().iconst(ir::types::I32, call_id as i64); + let func_index_val = builder.ins().iconst(ir::types::I32, func_index as i64); - let callee_args = vec![vmctx_ptr_val, call_id_val, values_vec_ptr_val]; + let callee_args = vec![vmctx_ptr_val, func_index_val, values_vec_ptr_val]; let new_sig = builder.import_signature(stub_sig.clone()); @@ -225,7 +232,7 @@ pub fn make_trampoline( .call_indirect(new_sig, callee_value, &callee_args); let call_result = builder.func.dfg.inst_results(call)[0]; - builder.ins().trapnz(call_result, TrapCode::User(0)); + builder.ins().trapnz(call_result, TrapCode::User(TRAP_USER_CODE)); let mflags = ir::MemFlags::trusted(); let mut results = Vec::new(); @@ -234,7 +241,7 @@ pub fn make_trampoline( r.value_type, mflags, values_vec_ptr_val, - (i * value_size) as i32, + (i * MAX_WASM_TYPE_SIZE) as i32, ); results.push(load); } @@ -243,7 +250,7 @@ pub fn make_trampoline( } let mut code_buf: Vec = Vec::new(); - let mut reloc_sink = RelocSink {}; + let mut reloc_sink = RelocSink; let mut trap_sink = binemit::NullTrapSink {}; let mut stackmap_sink = binemit::NullStackmapSink {}; context @@ -254,17 +261,18 @@ pub fn make_trampoline( &mut trap_sink, &mut stackmap_sink, ) - .expect("compile_and_emit"); + .map_err(|e| WasmError::Instantiation(format!("failed to compile trampoline: {}", e)))?; - code_memory + let func_ref = code_memory .allocate_copy_of_byte_slice(&code_buf) - .expect("allocate_copy_of_byte_slice") - .as_ptr() + .map_err(|e| WasmError::Instantiation(format!("failed to allocate code memory: {}", e)))?; + + Ok(func_ref.as_ptr()) } /// We don't expect trampoline compilation to produce any relocations, so /// this `RelocSink` just asserts that it doesn't recieve any. -struct RelocSink {} +struct RelocSink; impl binemit::RelocSink for RelocSink { fn reloc_ebb( From b76f733cb08f8fa2a5b205acb1499955fb26c66a Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Thu, 31 Oct 2019 13:43:20 +0100 Subject: [PATCH 26/26] Fixes after merge from master and panic safety for wasmtime instantiation. --- Cargo.toml | 4 ---- core/executor/src/wasmi_execution.rs | 9 ++++---- core/executor/src/wasmtime/runtime.rs | 31 ++++++++++++++++++++------- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3f57855708d46..5e1238866b876 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -109,7 +109,3 @@ members = [ # Substrate runtime requires unwinding. panic = "unwind" -[features] -wasmtime = [ - "cli/wasmtime", -] diff --git a/core/executor/src/wasmi_execution.rs b/core/executor/src/wasmi_execution.rs index d6d1ac0927e2c..b6af30b7ee185 100644 --- a/core/executor/src/wasmi_execution.rs +++ b/core/executor/src/wasmi_execution.rs @@ -627,11 +627,12 @@ pub fn create_instance(ext: &mut E, code: &[u8], heap_pages: u let mut ext = AssertUnwindSafe(ext); let call_instance = AssertUnwindSafe(&instance); - let version = crate::native_executor::safe_call( + let version_result = crate::native_executor::safe_call( move || call_in_wasm_module(&mut **ext, *call_instance, "Core_version", &[]) - .ok() - .and_then(|v| RuntimeVersion::decode(&mut v.as_slice()).ok()) - ).map_err(WasmError::Instantiation)?; + ).map_err(|_| WasmError::Instantiation("panic in call to get runtime version".to_string()))?; + let version = version_result + .ok() + .and_then(|v| RuntimeVersion::decode(&mut v.as_slice()).ok()); Ok(WasmiRuntime { instance, diff --git a/core/executor/src/wasmtime/runtime.rs b/core/executor/src/wasmtime/runtime.rs index 19c24c5d553bb..fa360773fb28c 100644 --- a/core/executor/src/wasmtime/runtime.rs +++ b/core/executor/src/wasmtime/runtime.rs @@ -34,6 +34,7 @@ use cranelift_wasm::DefinedFuncIndex; use std::cell::RefCell; use std::collections::HashMap; use std::convert::TryFrom; +use std::panic::AssertUnwindSafe; use std::rc::Rc; use wasm_interface::{HostFunctions, Pointer, WordSize}; use wasmtime_environ::{Module, translate_signature}; @@ -105,17 +106,31 @@ pub fn create_instance(ext: &mut E, code: &[u8], heap_pages: u .ok_or_else(|| WasmError::InvalidHeapPages)?; // Call to determine runtime version. - let version_result = call_method( - &mut context, - &mut compiled_module, - ext, - "Core_version", - &[], - heap_pages - ); + let version_result = { + // `ext` is already implicitly handled as unwind safe, as we store it in a global variable. + let mut ext = AssertUnwindSafe(ext); + + // The following unwind safety assertions are OK because if the method call panics, the + // context and compiled module will be dropped. + let mut context = AssertUnwindSafe(&mut context); + let mut compiled_module = AssertUnwindSafe(&mut compiled_module); + crate::native_executor::safe_call(move || { + call_method( + &mut **context, + &mut **compiled_module, + &mut **ext, + "Core_version", + &[], + heap_pages + ) + }).map_err(|_| { + WasmError::Instantiation("panic in call to get runtime version".to_string()) + })? + }; let version = version_result .ok() .and_then(|v| RuntimeVersion::decode(&mut v.as_slice()).ok()); + Ok(WasmtimeRuntime { module: compiled_module, context,