Run cargo fmt on the whole code base (#9394)

* Run cargo fmt on the whole code base

* Second run

* Add CI check

* Fix compilation

* More unnecessary braces

* Handle weights

* Use --all

* Use correct attributes...

* Fix UI tests

* AHHHHHHHHH

* 🤦

* Docs

* Fix compilation

* 🤷

* Please stop

* 🤦 x 2

* More

* make rustfmt.toml consistent with polkadot

Co-authored-by: André Silva <andrerfosilva@gmail.com>
This commit is contained in:
Bastian Köcher
2021-07-21 16:32:32 +02:00
committed by GitHub
parent d451c38c1c
commit 7b56ab15b4
1010 changed files with 53339 additions and 51208 deletions
+1 -1
View File
@@ -22,6 +22,6 @@
#![deny(unused_crate_dependencies)]
pub mod error;
pub mod runtime_blob;
pub mod sandbox;
pub mod wasm_runtime;
pub mod runtime_blob;
@@ -16,10 +16,10 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::error::{self, Error};
use super::RuntimeBlob;
use std::mem;
use crate::error::{self, Error};
use pwasm_utils::parity_wasm::elements::Instruction;
use std::mem;
/// This is a snapshot of data segments specialzied for a particular instantiation.
///
@@ -49,7 +49,7 @@ impl DataSegmentsSnapshot {
// [op, End]
if init_expr.len() != 2 {
return Err(Error::InitializerHasTooManyExpressions);
return Err(Error::InitializerHasTooManyExpressions)
}
let offset = match &init_expr[0] {
Instruction::I32Const(v) => *v as u32,
@@ -60,8 +60,8 @@ impl DataSegmentsSnapshot {
// At the moment of writing the Substrate Runtime Interface does not provide
// any globals. There is nothing that prevents us from supporting this
// if/when we gain those.
return Err(Error::ImportedGlobalsUnsupported);
}
return Err(Error::ImportedGlobalsUnsupported)
},
insn => return Err(Error::InvalidInitializerExpression(format!("{:?}", insn))),
};
@@ -50,17 +50,14 @@ pub trait InstanceGlobals {
/// a runtime blob that was instrumented by
/// [`RuntimeBlob::expose_mutable_globals`](super::RuntimeBlob::expose_mutable_globals`).
///
/// If the code wasn't instrumented then it would be empty and snapshot would do nothing.
pub struct ExposedMutableGlobalsSet(Vec<String>);
impl ExposedMutableGlobalsSet {
/// Collect the set from the given runtime blob. See the struct documentation for details.
pub fn collect(runtime_blob: &RuntimeBlob) -> Self {
let global_names = runtime_blob
.exported_internal_global_names()
.map(ToOwned::to_owned)
.collect();
let global_names =
runtime_blob.exported_internal_global_names().map(ToOwned::to_owned).collect();
Self(global_names)
}
}
@@ -53,5 +53,5 @@ mod globals_snapshot;
mod runtime_blob;
pub use data_segments_snapshot::DataSegmentsSnapshot;
pub use globals_snapshot::{GlobalsSnapshot, ExposedMutableGlobalsSet, InstanceGlobals};
pub use globals_snapshot::{ExposedMutableGlobalsSet, GlobalsSnapshot, InstanceGlobals};
pub use runtime_blob::RuntimeBlob;
@@ -16,13 +16,11 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use pwasm_utils::{
parity_wasm::elements::{
DataSegment, Module, deserialize_buffer, serialize, Internal,
},
export_mutable_globals,
};
use crate::error::WasmError;
use pwasm_utils::{
export_mutable_globals,
parity_wasm::elements::{deserialize_buffer, serialize, DataSegment, Internal, Module},
};
/// A bunch of information collected from a WebAssembly module.
#[derive(Clone)]
@@ -53,11 +51,7 @@ impl RuntimeBlob {
/// Extract the data segments from the given wasm code.
pub(super) fn data_segments(&self) -> Vec<DataSegment> {
self.raw_module
.data_section()
.map(|ds| ds.entries())
.unwrap_or(&[])
.to_vec()
self.raw_module.data_section().map(|ds| ds.entries()).unwrap_or(&[]).to_vec()
}
/// The number of globals defined in locally in this module.
@@ -70,10 +64,7 @@ impl RuntimeBlob {
/// The number of imports of globals.
pub fn imported_globals_count(&self) -> u32 {
self.raw_module
.import_section()
.map(|is| is.globals() as u32)
.unwrap_or(0)
self.raw_module.import_section().map(|is| is.globals() as u32).unwrap_or(0)
}
/// Perform an instrumentation that makes sure that the mutable globals are exported.
@@ -95,35 +86,29 @@ impl RuntimeBlob {
|e| WasmError::Other(format!("cannot inject the stack limiter: {:?}", e)),
)?;
Ok(Self {
raw_module: injected_module,
})
Ok(Self { raw_module: injected_module })
}
/// Perform an instrumentation that makes sure that a specific function `entry_point` is exported
pub fn entry_point_exists(&self, entry_point: &str) -> bool {
self.raw_module.export_section().map(|e| {
e.entries()
.iter()
.any(|e| matches!(e.internal(), Internal::Function(_)) && e.field() == entry_point)
}).unwrap_or_default()
self.raw_module
.export_section()
.map(|e| {
e.entries().iter().any(|e| {
matches!(e.internal(), Internal::Function(_)) && e.field() == entry_point
})
})
.unwrap_or_default()
}
/// Returns an iterator of all globals which were exported by [`expose_mutable_globals`].
pub(super) fn exported_internal_global_names<'module>(
&'module self,
) -> impl Iterator<Item = &'module str> {
let exports = self
.raw_module
.export_section()
.map(|es| es.entries())
.unwrap_or(&[]);
let exports = self.raw_module.export_section().map(|es| es.entries()).unwrap_or(&[]);
exports.iter().filter_map(|export| match export.internal() {
Internal::Global(_)
if export.field().starts_with("exported_internal_global") =>
{
Some(export.field())
}
Internal::Global(_) if export.field().starts_with("exported_internal_global") =>
Some(export.field()),
_ => None,
})
}
@@ -135,12 +120,11 @@ impl RuntimeBlob {
.custom_sections()
.find(|cs| cs.name() == section_name)
.map(|cs| cs.payload())
}
}
/// Consumes this runtime blob and serializes it.
pub fn serialize(self) -> Vec<u8> {
serialize(self.raw_module)
.expect("serializing into a vec should succeed; qed")
serialize(self.raw_module).expect("serializing into a vec should succeed; qed")
}
/// Destructure this structure into the underlying parity-wasm Module.
+58 -93
View File
@@ -21,15 +21,15 @@
//! Sandboxing is baked by wasmi at the moment. In future, however, we would like to add/switch to
//! a compiled execution engine.
use crate::error::{Result, Error};
use std::{collections::HashMap, rc::Rc};
use crate::error::{Error, Result};
use codec::{Decode, Encode};
use sp_core::sandbox as sandbox_primitives;
use wasmi::{
Externals, ImportResolver, MemoryInstance, MemoryRef, Module, ModuleInstance,
ModuleRef, RuntimeArgs, RuntimeValue, Trap, TrapKind, memory_units::Pages,
};
use sp_wasm_interface::{FunctionContext, Pointer, WordSize};
use std::{collections::HashMap, rc::Rc};
use wasmi::{
memory_units::Pages, Externals, ImportResolver, MemoryInstance, MemoryRef, Module,
ModuleInstance, ModuleRef, RuntimeArgs, RuntimeValue, Trap, TrapKind,
};
/// Index of a function inside the supervisor.
///
@@ -83,15 +83,9 @@ impl ImportResolver for Imports {
field_name: &str,
signature: &::wasmi::Signature,
) -> std::result::Result<wasmi::FuncRef, wasmi::Error> {
let key = (
module_name.as_bytes().to_owned(),
field_name.as_bytes().to_owned(),
);
let key = (module_name.as_bytes().to_owned(), field_name.as_bytes().to_owned());
let idx = *self.func_map.get(&key).ok_or_else(|| {
wasmi::Error::Instantiation(format!(
"Export {}:{} not found",
module_name, field_name
))
wasmi::Error::Instantiation(format!("Export {}:{} not found", module_name, field_name))
})?;
Ok(wasmi::FuncInstance::alloc_host(signature.clone(), idx.0))
}
@@ -102,11 +96,9 @@ impl ImportResolver for Imports {
field_name: &str,
_memory_type: &::wasmi::MemoryDescriptor,
) -> std::result::Result<MemoryRef, wasmi::Error> {
let key = (
module_name.as_bytes().to_vec(),
field_name.as_bytes().to_vec(),
);
let mem = self.memories_map
let key = (module_name.as_bytes().to_vec(), field_name.as_bytes().to_vec());
let mem = self
.memories_map
.get(&key)
.ok_or_else(|| {
wasmi::Error::Instantiation(format!(
@@ -124,10 +116,7 @@ impl ImportResolver for Imports {
field_name: &str,
_global_type: &::wasmi::GlobalDescriptor,
) -> std::result::Result<wasmi::GlobalRef, wasmi::Error> {
Err(wasmi::Error::Instantiation(format!(
"Export {}:{} not found",
module_name, field_name
)))
Err(wasmi::Error::Instantiation(format!("Export {}:{} not found", module_name, field_name)))
}
fn resolve_table(
@@ -136,10 +125,7 @@ impl ImportResolver for Imports {
field_name: &str,
_table_type: &::wasmi::TableDescriptor,
) -> std::result::Result<wasmi::TableRef, wasmi::Error> {
Err(wasmi::Error::Instantiation(format!(
"Export {}:{} not found",
module_name, field_name
)))
Err(wasmi::Error::Instantiation(format!("Export {}:{} not found", module_name, field_name)))
}
}
@@ -187,7 +173,9 @@ fn trap(msg: &'static str) -> Trap {
TrapKind::Host(Box::new(Error::Other(msg.into()))).into()
}
fn deserialize_result(mut serialized_result: &[u8]) -> std::result::Result<Option<RuntimeValue>, Trap> {
fn deserialize_result(
mut serialized_result: &[u8],
) -> std::result::Result<Option<RuntimeValue>, Trap> {
use self::sandbox_primitives::HostError;
use sp_wasm_interface::ReturnValue;
let result_val = std::result::Result::<ReturnValue, HostError>::decode(&mut serialized_result)
@@ -222,7 +210,8 @@ impl<'a, FE: SandboxCapabilities + 'a> Externals for GuestExternals<'a, FE> {
);
// Serialize arguments into a byte vector.
let invoke_args_data: Vec<u8> = args.as_ref()
let invoke_args_data: Vec<u8> = args
.as_ref()
.iter()
.cloned()
.map(sp_wasm_interface::Value::from)
@@ -240,10 +229,7 @@ impl<'a, FE: SandboxCapabilities + 'a> Externals for GuestExternals<'a, FE> {
.map_err(|_| trap("Can't allocate memory in supervisor for the arguments"))?;
let deallocate = |this: &mut GuestExternals<FE>, ptr, fail_msg| {
this
.supervisor_externals
.deallocate_memory(ptr)
.map_err(|_| trap(fail_msg))
this.supervisor_externals.deallocate_memory(ptr).map_err(|_| trap(fail_msg))
};
if self
@@ -251,8 +237,12 @@ impl<'a, FE: SandboxCapabilities + 'a> Externals for GuestExternals<'a, FE> {
.write_memory(invoke_args_ptr, &invoke_args_data)
.is_err()
{
deallocate(self, invoke_args_ptr, "Failed dealloction after failed write of invoke arguments")?;
return Err(trap("Can't write invoke args into memory"));
deallocate(
self,
invoke_args_ptr,
"Failed dealloction after failed write of invoke arguments",
)?;
return Err(trap("Can't write invoke args into memory"))
}
let result = self.supervisor_externals.invoke(
@@ -263,7 +253,11 @@ impl<'a, FE: SandboxCapabilities + 'a> Externals for GuestExternals<'a, FE> {
func_idx,
);
deallocate(self, invoke_args_ptr, "Can't deallocate memory for dispatch thunk's invoke arguments")?;
deallocate(
self,
invoke_args_ptr,
"Can't deallocate memory for dispatch thunk's invoke arguments",
)?;
let result = result?;
// dispatch_thunk returns pointer to serialized arguments.
@@ -276,13 +270,18 @@ impl<'a, FE: SandboxCapabilities + 'a> Externals for GuestExternals<'a, FE> {
(Pointer::new(ptr), len)
};
let serialized_result_val = self.supervisor_externals
let serialized_result_val = self
.supervisor_externals
.read_memory(serialized_result_val_ptr, serialized_result_val_len)
.map_err(|_| trap("Can't read the serialized result from dispatch thunk"));
deallocate(self, serialized_result_val_ptr, "Can't deallocate memory for dispatch thunk's result")
.and_then(|_| serialized_result_val)
.and_then(|serialized_result_val| deserialize_result(&serialized_result_val))
deallocate(
self,
serialized_result_val_ptr,
"Can't deallocate memory for dispatch thunk's result",
)
.and_then(|_| serialized_result_val)
.and_then(|serialized_result_val| deserialize_result(&serialized_result_val))
}
}
@@ -296,11 +295,7 @@ where
FE: SandboxCapabilities,
F: FnOnce(&mut GuestExternals<FE>) -> R,
{
let mut guest_externals = GuestExternals {
supervisor_externals,
sandbox_instance,
state,
};
let mut guest_externals = GuestExternals { supervisor_externals, sandbox_instance, state };
f(&mut guest_externals)
}
@@ -332,32 +327,23 @@ impl<FR> SandboxInstance<FR> {
///
/// The `state` parameter can be used to provide custom data for
/// these syscall implementations.
pub fn invoke<FE: SandboxCapabilities<SupervisorFuncRef=FR>>(
pub fn invoke<FE: SandboxCapabilities<SupervisorFuncRef = FR>>(
&self,
export_name: &str,
args: &[RuntimeValue],
supervisor_externals: &mut FE,
state: u32,
) -> std::result::Result<Option<wasmi::RuntimeValue>, wasmi::Error> {
with_guest_externals(
supervisor_externals,
self,
state,
|guest_externals| {
self.instance
.invoke_export(export_name, args, guest_externals)
},
)
with_guest_externals(supervisor_externals, self, state, |guest_externals| {
self.instance.invoke_export(export_name, args, guest_externals)
})
}
/// Get the value from a global with the given `name`.
///
/// Returns `Some(_)` if the global could be found.
pub fn get_global_val(&self, name: &str) -> Option<sp_wasm_interface::Value> {
let global = self.instance
.export_by_name(name)?
.as_global()?
.get();
let global = self.instance.export_by_name(name)?.as_global()?.get();
Some(global.into())
}
@@ -398,7 +384,7 @@ fn decode_environment_definition(
let externals_idx =
guest_to_supervisor_mapping.define(SupervisorFuncIndex(func_idx as usize));
func_map.insert((module, field), externals_idx);
}
},
sandbox_primitives::ExternEntity::Memory(memory_idx) => {
let memory_ref = memories
.get(memory_idx as usize)
@@ -406,17 +392,11 @@ fn decode_environment_definition(
.ok_or_else(|| InstantiationError::EnvironmentDefinitionCorrupted)?
.ok_or_else(|| InstantiationError::EnvironmentDefinitionCorrupted)?;
memories_map.insert((module, field), memory_ref);
}
},
}
}
Ok((
Imports {
func_map,
memories_map,
},
guest_to_supervisor_mapping,
))
Ok((Imports { func_map, memories_map }, guest_to_supervisor_mapping))
}
/// An environment in which the guest module is instantiated.
@@ -435,10 +415,7 @@ impl GuestEnvironment {
) -> std::result::Result<Self, InstantiationError> {
let (imports, guest_to_supervisor_mapping) =
decode_environment_definition(raw_env_def, &store.memories)?;
Ok(Self {
imports,
guest_to_supervisor_mapping,
})
Ok(Self { imports, guest_to_supervisor_mapping })
}
}
@@ -493,16 +470,11 @@ pub fn instantiate<'a, FE: SandboxCapabilities>(
guest_to_supervisor_mapping: host_env.guest_to_supervisor_mapping,
});
with_guest_externals(
supervisor_externals,
&sandbox_instance,
state,
|guest_externals| {
instance
.run_start(guest_externals)
.map_err(|_| InstantiationError::StartTrapped)
},
)?;
with_guest_externals(supervisor_externals, &sandbox_instance, state, |guest_externals| {
instance
.run_start(guest_externals)
.map_err(|_| InstantiationError::StartTrapped)
})?;
Ok(UnregisteredInstance { sandbox_instance })
}
@@ -519,10 +491,7 @@ pub struct Store<FR> {
impl<FR> Store<FR> {
/// Create a new empty sandbox store.
pub fn new() -> Self {
Store {
instances: Vec::new(),
memories: Vec::new(),
}
Store { instances: Vec::new(), memories: Vec::new() }
}
/// Create a new memory instance and return it's index.
@@ -537,11 +506,7 @@ impl<FR> Store<FR> {
specified_limit => Some(Pages(specified_limit as usize)),
};
let mem =
MemoryInstance::alloc(
Pages(initial as usize),
maximum,
)?;
let mem = MemoryInstance::alloc(Pages(initial as usize), maximum)?;
let mem_idx = self.memories.len();
self.memories.push(Some(mem));
@@ -589,7 +554,7 @@ impl<FR> Store<FR> {
Some(memory) => {
*memory = None;
Ok(())
}
},
}
}
@@ -606,7 +571,7 @@ impl<FR> Store<FR> {
Some(instance) => {
*instance = None;
Ok(())
}
},
}
}
+296 -289
View File
@@ -7,22 +7,28 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
/// Wasm binary unwrapped. If built with `SKIP_WASM_BUILD`, the function panics.
#[cfg(feature = "std")]
pub fn wasm_binary_unwrap() -> &'static [u8] {
WASM_BINARY.expect("Development wasm binary is not available. Testing is only \
supported with the flag disabled.")
WASM_BINARY.expect(
"Development wasm binary is not available. Testing is only \
supported with the flag disabled.",
)
}
#[cfg(not(feature = "std"))]
use sp_std::{vec::Vec, vec};
use sp_std::{vec, vec::Vec};
#[cfg(not(feature = "std"))]
use sp_core::{ed25519, sr25519};
#[cfg(not(feature = "std"))]
use sp_io::{
storage, hashing::{blake2_128, blake2_256, sha2_256, twox_128, twox_256},
crypto::{ed25519_verify, sr25519_verify}, wasm_tracing,
crypto::{ed25519_verify, sr25519_verify},
hashing::{blake2_128, blake2_256, sha2_256, twox_128, twox_256},
storage, wasm_tracing,
};
#[cfg(not(feature = "std"))]
use sp_runtime::{print, traits::{BlakeTwo256, Hash}};
#[cfg(not(feature = "std"))]
use sp_core::{ed25519, sr25519};
use sp_runtime::{
print,
traits::{BlakeTwo256, Hash},
};
#[cfg(not(feature = "std"))]
use sp_sandbox::Value;
@@ -48,347 +54,347 @@ static mut MUTABLE_STATIC: u64 = 32;
static mut MUTABLE_STATIC_BSS: u64 = 0;
sp_core::wasm_export_functions! {
fn test_calling_missing_external() {
unsafe { missing_external() }
}
fn test_calling_missing_external() {
unsafe { missing_external() }
}
fn test_calling_yet_another_missing_external() {
unsafe { yet_another_missing_external() }
}
fn test_calling_yet_another_missing_external() {
unsafe { yet_another_missing_external() }
}
fn test_data_in(input: Vec<u8>) -> Vec<u8> {
print("set_storage");
storage::set(b"input", &input);
fn test_data_in(input: Vec<u8>) -> Vec<u8> {
print("set_storage");
storage::set(b"input", &input);
print("storage");
let foo = storage::get(b"foo").unwrap();
print("storage");
let foo = storage::get(b"foo").unwrap();
print("set_storage");
storage::set(b"baz", &foo);
print("set_storage");
storage::set(b"baz", &foo);
print("finished!");
b"all ok!".to_vec()
}
print("finished!");
b"all ok!".to_vec()
}
fn test_clear_prefix(input: Vec<u8>) -> Vec<u8> {
storage::clear_prefix(&input, None);
b"all ok!".to_vec()
}
fn test_clear_prefix(input: Vec<u8>) -> Vec<u8> {
storage::clear_prefix(&input, None);
b"all ok!".to_vec()
}
fn test_empty_return() {}
fn test_empty_return() {}
fn test_dirty_plenty_memory(heap_base: u32, heap_pages: u32) {
// This piece of code will dirty multiple pages of memory. The number of pages is given by
// the `heap_pages`. It's unit is a wasm page (64KiB). The first page to be cleared
// is a wasm page that that follows the one that holds the `heap_base` address.
//
// This function dirties the **host** pages. I.e. we dirty 4KiB at a time and it will take
// 16 writes to process a single wasm page.
fn test_dirty_plenty_memory(heap_base: u32, heap_pages: u32) {
// This piece of code will dirty multiple pages of memory. The number of pages is given by
// the `heap_pages`. It's unit is a wasm page (64KiB). The first page to be cleared
// is a wasm page that that follows the one that holds the `heap_base` address.
//
// This function dirties the **host** pages. I.e. we dirty 4KiB at a time and it will take
// 16 writes to process a single wasm page.
let mut heap_ptr = heap_base as usize;
let mut heap_ptr = heap_base as usize;
// Find the next wasm page boundary.
let heap_ptr = round_up_to(heap_ptr, 65536);
// Find the next wasm page boundary.
let heap_ptr = round_up_to(heap_ptr, 65536);
// Make it an actual pointer
let heap_ptr = heap_ptr as *mut u8;
// Make it an actual pointer
let heap_ptr = heap_ptr as *mut u8;
// Traverse the host pages and make each one dirty
let host_pages = heap_pages as usize * 16;
for i in 0..host_pages {
unsafe {
// technically this is an UB, but there is no way Rust can find this out.
heap_ptr.add(i * 4096).write(0);
}
}
// Traverse the host pages and make each one dirty
let host_pages = heap_pages as usize * 16;
for i in 0..host_pages {
unsafe {
// technically this is an UB, but there is no way Rust can find this out.
heap_ptr.add(i * 4096).write(0);
}
}
fn round_up_to(n: usize, divisor: usize) -> usize {
(n + divisor - 1) / divisor
}
}
fn round_up_to(n: usize, divisor: usize) -> usize {
(n + divisor - 1) / divisor
}
}
fn test_exhaust_heap() -> Vec<u8> { Vec::with_capacity(16777216) }
fn test_exhaust_heap() -> Vec<u8> { Vec::with_capacity(16777216) }
fn test_fp_f32add(a: [u8; 4], b: [u8; 4]) -> [u8; 4] {
let a = f32::from_le_bytes(a);
let b = f32::from_le_bytes(b);
f32::to_le_bytes(a + b)
}
fn test_fp_f32add(a: [u8; 4], b: [u8; 4]) -> [u8; 4] {
let a = f32::from_le_bytes(a);
let b = f32::from_le_bytes(b);
f32::to_le_bytes(a + b)
}
fn test_panic() { panic!("test panic") }
fn test_panic() { panic!("test panic") }
fn test_conditional_panic(input: Vec<u8>) -> Vec<u8> {
if input.len() > 0 {
panic!("test panic")
}
fn test_conditional_panic(input: Vec<u8>) -> Vec<u8> {
if input.len() > 0 {
panic!("test panic")
}
input
}
input
}
fn test_blake2_256(input: Vec<u8>) -> Vec<u8> {
blake2_256(&input).to_vec()
}
fn test_blake2_256(input: Vec<u8>) -> Vec<u8> {
blake2_256(&input).to_vec()
}
fn test_blake2_128(input: Vec<u8>) -> Vec<u8> {
blake2_128(&input).to_vec()
}
fn test_blake2_128(input: Vec<u8>) -> Vec<u8> {
blake2_128(&input).to_vec()
}
fn test_sha2_256(input: Vec<u8>) -> Vec<u8> {
sha2_256(&input).to_vec()
}
fn test_sha2_256(input: Vec<u8>) -> Vec<u8> {
sha2_256(&input).to_vec()
}
fn test_twox_256(input: Vec<u8>) -> Vec<u8> {
twox_256(&input).to_vec()
}
fn test_twox_256(input: Vec<u8>) -> Vec<u8> {
twox_256(&input).to_vec()
}
fn test_twox_128(input: Vec<u8>) -> Vec<u8> {
twox_128(&input).to_vec()
}
fn test_twox_128(input: Vec<u8>) -> Vec<u8> {
twox_128(&input).to_vec()
}
fn test_ed25519_verify(input: Vec<u8>) -> bool {
let mut pubkey = [0; 32];
let mut sig = [0; 64];
fn test_ed25519_verify(input: Vec<u8>) -> bool {
let mut pubkey = [0; 32];
let mut sig = [0; 64];
pubkey.copy_from_slice(&input[0..32]);
sig.copy_from_slice(&input[32..96]);
pubkey.copy_from_slice(&input[0..32]);
sig.copy_from_slice(&input[32..96]);
let msg = b"all ok!";
ed25519_verify(&ed25519::Signature(sig), &msg[..], &ed25519::Public(pubkey))
}
let msg = b"all ok!";
ed25519_verify(&ed25519::Signature(sig), &msg[..], &ed25519::Public(pubkey))
}
fn test_sr25519_verify(input: Vec<u8>) -> bool {
let mut pubkey = [0; 32];
let mut sig = [0; 64];
fn test_sr25519_verify(input: Vec<u8>) -> bool {
let mut pubkey = [0; 32];
let mut sig = [0; 64];
pubkey.copy_from_slice(&input[0..32]);
sig.copy_from_slice(&input[32..96]);
pubkey.copy_from_slice(&input[0..32]);
sig.copy_from_slice(&input[32..96]);
let msg = b"all ok!";
sr25519_verify(&sr25519::Signature(sig), &msg[..], &sr25519::Public(pubkey))
}
let msg = b"all ok!";
sr25519_verify(&sr25519::Signature(sig), &msg[..], &sr25519::Public(pubkey))
}
fn test_ordered_trie_root() -> Vec<u8> {
BlakeTwo256::ordered_trie_root(
vec![
b"zero"[..].into(),
b"one"[..].into(),
b"two"[..].into(),
],
).as_ref().to_vec()
}
fn test_ordered_trie_root() -> Vec<u8> {
BlakeTwo256::ordered_trie_root(
vec![
b"zero"[..].into(),
b"one"[..].into(),
b"two"[..].into(),
],
).as_ref().to_vec()
}
fn test_sandbox(code: Vec<u8>) -> bool {
execute_sandboxed(&code, &[]).is_ok()
}
fn test_sandbox(code: Vec<u8>) -> bool {
execute_sandboxed(&code, &[]).is_ok()
}
fn test_sandbox_args(code: Vec<u8>) -> bool {
execute_sandboxed(
&code,
&[
Value::I32(0x12345678),
Value::I64(0x1234567887654321),
],
).is_ok()
}
fn test_sandbox_args(code: Vec<u8>) -> bool {
execute_sandboxed(
&code,
&[
Value::I32(0x12345678),
Value::I64(0x1234567887654321),
],
).is_ok()
}
fn test_sandbox_return_val(code: Vec<u8>) -> bool {
let ok = match execute_sandboxed(
&code,
&[
Value::I32(0x1336),
]
) {
Ok(sp_sandbox::ReturnValue::Value(Value::I32(0x1337))) => true,
_ => false,
};
fn test_sandbox_return_val(code: Vec<u8>) -> bool {
let ok = match execute_sandboxed(
&code,
&[
Value::I32(0x1336),
]
) {
Ok(sp_sandbox::ReturnValue::Value(Value::I32(0x1337))) => true,
_ => false,
};
ok
}
ok
}
fn test_sandbox_instantiate(code: Vec<u8>) -> u8 {
let env_builder = sp_sandbox::EnvironmentDefinitionBuilder::new();
let code = match sp_sandbox::Instance::new(&code, &env_builder, &mut ()) {
Ok(_) => 0,
Err(sp_sandbox::Error::Module) => 1,
Err(sp_sandbox::Error::Execution) => 2,
Err(sp_sandbox::Error::OutOfBounds) => 3,
};
fn test_sandbox_instantiate(code: Vec<u8>) -> u8 {
let env_builder = sp_sandbox::EnvironmentDefinitionBuilder::new();
let code = match sp_sandbox::Instance::new(&code, &env_builder, &mut ()) {
Ok(_) => 0,
Err(sp_sandbox::Error::Module) => 1,
Err(sp_sandbox::Error::Execution) => 2,
Err(sp_sandbox::Error::OutOfBounds) => 3,
};
code
}
code
}
fn test_sandbox_get_global_val(code: Vec<u8>) -> i64 {
let env_builder = sp_sandbox::EnvironmentDefinitionBuilder::new();
let instance = if let Ok(i) = sp_sandbox::Instance::new(&code, &env_builder, &mut ()) {
i
} else {
return 20;
};
fn test_sandbox_get_global_val(code: Vec<u8>) -> i64 {
let env_builder = sp_sandbox::EnvironmentDefinitionBuilder::new();
let instance = if let Ok(i) = sp_sandbox::Instance::new(&code, &env_builder, &mut ()) {
i
} else {
return 20;
};
match instance.get_global_val("test_global") {
Some(sp_sandbox::Value::I64(val)) => val,
None => 30,
val => 40,
}
}
match instance.get_global_val("test_global") {
Some(sp_sandbox::Value::I64(val)) => val,
None => 30,
val => 40,
}
}
fn test_offchain_index_set() {
sp_io::offchain_index::set(b"k", b"v");
}
fn test_offchain_index_set() {
sp_io::offchain_index::set(b"k", b"v");
}
fn test_offchain_local_storage() -> bool {
let kind = sp_core::offchain::StorageKind::PERSISTENT;
assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), None);
sp_io::offchain::local_storage_set(kind, b"test", b"asd");
assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), Some(b"asd".to_vec()));
fn test_offchain_local_storage() -> bool {
let kind = sp_core::offchain::StorageKind::PERSISTENT;
assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), None);
sp_io::offchain::local_storage_set(kind, b"test", b"asd");
assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), Some(b"asd".to_vec()));
let res = sp_io::offchain::local_storage_compare_and_set(
kind,
b"test",
Some(b"asd".to_vec()),
b"",
);
assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), Some(b"".to_vec()));
res
}
let res = sp_io::offchain::local_storage_compare_and_set(
kind,
b"test",
Some(b"asd".to_vec()),
b"",
);
assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), Some(b"".to_vec()));
res
}
fn test_offchain_local_storage_with_none() {
let kind = sp_core::offchain::StorageKind::PERSISTENT;
assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), None);
fn test_offchain_local_storage_with_none() {
let kind = sp_core::offchain::StorageKind::PERSISTENT;
assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), None);
let res = sp_io::offchain::local_storage_compare_and_set(kind, b"test", None, b"value");
assert_eq!(res, true);
assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), Some(b"value".to_vec()));
}
let res = sp_io::offchain::local_storage_compare_and_set(kind, b"test", None, b"value");
assert_eq!(res, true);
assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), Some(b"value".to_vec()));
}
fn test_offchain_http() -> bool {
use sp_core::offchain::HttpRequestStatus;
let run = || -> Option<()> {
let id = sp_io::offchain::http_request_start(
"POST",
"http://localhost:12345",
&[],
).ok()?;
sp_io::offchain::http_request_add_header(id, "X-Auth", "test").ok()?;
sp_io::offchain::http_request_write_body(id, &[1, 2, 3, 4], None).ok()?;
sp_io::offchain::http_request_write_body(id, &[], None).ok()?;
let status = sp_io::offchain::http_response_wait(&[id], None);
assert!(status == vec![HttpRequestStatus::Finished(200)], "Expected Finished(200) status.");
let headers = sp_io::offchain::http_response_headers(id);
assert_eq!(headers, vec![(b"X-Auth".to_vec(), b"hello".to_vec())]);
let mut buffer = vec![0; 64];
let read = sp_io::offchain::http_response_read_body(id, &mut buffer, None).ok()?;
assert_eq!(read, 3);
assert_eq!(&buffer[0..read as usize], &[1, 2, 3]);
let read = sp_io::offchain::http_response_read_body(id, &mut buffer, None).ok()?;
assert_eq!(read, 0);
fn test_offchain_http() -> bool {
use sp_core::offchain::HttpRequestStatus;
let run = || -> Option<()> {
let id = sp_io::offchain::http_request_start(
"POST",
"http://localhost:12345",
&[],
).ok()?;
sp_io::offchain::http_request_add_header(id, "X-Auth", "test").ok()?;
sp_io::offchain::http_request_write_body(id, &[1, 2, 3, 4], None).ok()?;
sp_io::offchain::http_request_write_body(id, &[], None).ok()?;
let status = sp_io::offchain::http_response_wait(&[id], None);
assert!(status == vec![HttpRequestStatus::Finished(200)], "Expected Finished(200) status.");
let headers = sp_io::offchain::http_response_headers(id);
assert_eq!(headers, vec![(b"X-Auth".to_vec(), b"hello".to_vec())]);
let mut buffer = vec![0; 64];
let read = sp_io::offchain::http_response_read_body(id, &mut buffer, None).ok()?;
assert_eq!(read, 3);
assert_eq!(&buffer[0..read as usize], &[1, 2, 3]);
let read = sp_io::offchain::http_response_read_body(id, &mut buffer, None).ok()?;
assert_eq!(read, 0);
Some(())
};
Some(())
};
run().is_some()
}
run().is_some()
}
fn test_enter_span() -> u64 {
wasm_tracing::enter_span(Default::default())
}
fn test_enter_span() -> u64 {
wasm_tracing::enter_span(Default::default())
}
fn test_exit_span(span_id: u64) {
wasm_tracing::exit(span_id)
}
fn test_exit_span(span_id: u64) {
wasm_tracing::exit(span_id)
}
fn test_nested_spans() {
sp_io::init_tracing();
let span_id = wasm_tracing::enter_span(Default::default());
{
sp_io::init_tracing();
let span_id = wasm_tracing::enter_span(Default::default());
wasm_tracing::exit(span_id);
}
wasm_tracing::exit(span_id);
}
fn test_nested_spans() {
sp_io::init_tracing();
let span_id = wasm_tracing::enter_span(Default::default());
{
sp_io::init_tracing();
let span_id = wasm_tracing::enter_span(Default::default());
wasm_tracing::exit(span_id);
}
wasm_tracing::exit(span_id);
}
fn returns_mutable_static() -> u64 {
unsafe {
MUTABLE_STATIC += 1;
MUTABLE_STATIC
}
}
fn returns_mutable_static() -> u64 {
unsafe {
MUTABLE_STATIC += 1;
MUTABLE_STATIC
}
}
fn returns_mutable_static_bss() -> u64 {
unsafe {
MUTABLE_STATIC_BSS += 1;
MUTABLE_STATIC_BSS
}
}
fn returns_mutable_static_bss() -> u64 {
unsafe {
MUTABLE_STATIC_BSS += 1;
MUTABLE_STATIC_BSS
}
}
fn allocates_huge_stack_array(trap: bool) -> Vec<u8> {
// Allocate a stack frame that is approx. 75% of the stack (assuming it is 1MB).
// This will just decrease (stacks in wasm32-u-u grow downwards) the stack
// pointer. This won't trap on the current compilers.
let mut data = [0u8; 1024 * 768];
fn allocates_huge_stack_array(trap: bool) -> Vec<u8> {
// Allocate a stack frame that is approx. 75% of the stack (assuming it is 1MB).
// This will just decrease (stacks in wasm32-u-u grow downwards) the stack
// pointer. This won't trap on the current compilers.
let mut data = [0u8; 1024 * 768];
// Then make sure we actually write something to it.
//
// If:
// 1. the stack area is placed at the beginning of the linear memory space, and
// 2. the stack pointer points to out-of-bounds area, and
// 3. a write is performed around the current stack pointer.
//
// then a trap should happen.
//
for (i, v) in data.iter_mut().enumerate() {
*v = i as u8; // deliberate truncation
}
// Then make sure we actually write something to it.
//
// If:
// 1. the stack area is placed at the beginning of the linear memory space, and
// 2. the stack pointer points to out-of-bounds area, and
// 3. a write is performed around the current stack pointer.
//
// then a trap should happen.
//
for (i, v) in data.iter_mut().enumerate() {
*v = i as u8; // deliberate truncation
}
if trap {
// There is a small chance of this to be pulled up in theory. In practice
// the probability of that is rather low.
panic!()
}
if trap {
// There is a small chance of this to be pulled up in theory. In practice
// the probability of that is rather low.
panic!()
}
data.to_vec()
}
data.to_vec()
}
// Check that the heap at `heap_base + offset` don't contains the test message.
// After the check succeeds the test message is written into the heap.
//
// It is expected that the given pointer is not allocated.
fn check_and_set_in_heap(heap_base: u32, offset: u32) {
let test_message = b"Hello invalid heap memory";
let ptr = unsafe { (heap_base + offset) as *mut u8 };
// Check that the heap at `heap_base + offset` don't contains the test message.
// After the check succeeds the test message is written into the heap.
//
// It is expected that the given pointer is not allocated.
fn check_and_set_in_heap(heap_base: u32, offset: u32) {
let test_message = b"Hello invalid heap memory";
let ptr = unsafe { (heap_base + offset) as *mut u8 };
let message_slice = unsafe { sp_std::slice::from_raw_parts_mut(ptr, test_message.len()) };
let message_slice = unsafe { sp_std::slice::from_raw_parts_mut(ptr, test_message.len()) };
assert_ne!(test_message, message_slice);
message_slice.copy_from_slice(test_message);
}
assert_ne!(test_message, message_slice);
message_slice.copy_from_slice(test_message);
}
fn test_spawn() {
let data = vec![1u8, 2u8];
let data_new = sp_tasks::spawn(tasks::incrementer, data).join();
fn test_spawn() {
let data = vec![1u8, 2u8];
let data_new = sp_tasks::spawn(tasks::incrementer, data).join();
assert_eq!(data_new, vec![2u8, 3u8]);
}
assert_eq!(data_new, vec![2u8, 3u8]);
}
fn test_nested_spawn() {
let data = vec![7u8, 13u8];
let data_new = sp_tasks::spawn(tasks::parallel_incrementer, data).join();
fn test_nested_spawn() {
let data = vec![7u8, 13u8];
let data_new = sp_tasks::spawn(tasks::parallel_incrementer, data).join();
assert_eq!(data_new, vec![10u8, 16u8]);
}
assert_eq!(data_new, vec![10u8, 16u8]);
}
fn test_panic_in_spawned() {
sp_tasks::spawn(tasks::panicker, vec![]).join();
}
}
fn test_panic_in_spawned() {
sp_tasks::spawn(tasks::panicker, vec![]).join();
}
}
#[cfg(not(feature = "std"))]
mod tasks {
#[cfg(not(feature = "std"))]
mod tasks {
use sp_std::prelude::*;
pub fn incrementer(data: Vec<u8>) -> Vec<u8> {
data.into_iter().map(|v| v + 1).collect()
data.into_iter().map(|v| v + 1).collect()
}
pub fn panicker(_: Vec<u8>) -> Vec<u8> {
@@ -396,11 +402,11 @@ sp_core::wasm_export_functions! {
}
pub fn parallel_incrementer(data: Vec<u8>) -> Vec<u8> {
let first = data.into_iter().map(|v| v + 2).collect::<Vec<_>>();
let second = sp_tasks::spawn(incrementer, first).join();
second
let first = data.into_iter().map(|v| v + 2).collect::<Vec<_>>();
let second = sp_tasks::spawn(incrementer, first).join();
second
}
}
}
#[cfg(not(feature = "std"))]
fn execute_sandboxed(
@@ -416,7 +422,7 @@ fn execute_sandboxed(
args: &[Value],
) -> Result<sp_sandbox::ReturnValue, sp_sandbox::HostError> {
if args.len() != 1 {
return Err(sp_sandbox::HostError);
return Err(sp_sandbox::HostError)
}
let condition = args[0].as_i32().ok_or_else(|| sp_sandbox::HostError)?;
if condition != 0 {
@@ -430,7 +436,7 @@ fn execute_sandboxed(
args: &[Value],
) -> Result<sp_sandbox::ReturnValue, sp_sandbox::HostError> {
if args.len() != 1 {
return Err(sp_sandbox::HostError);
return Err(sp_sandbox::HostError)
}
let inc_by = args[0].as_i32().ok_or_else(|| sp_sandbox::HostError)?;
e.counter += inc_by as u32;
@@ -445,7 +451,8 @@ fn execute_sandboxed(
env_builder.add_host_func("env", "inc_counter", env_inc_counter);
let memory = match sp_sandbox::Memory::new(1, Some(16)) {
Ok(m) => m,
Err(_) => unreachable!("
Err(_) => unreachable!(
"
Memory::new() can return Err only if parameters are borked; \
We passing params here explicitly and they're correct; \
Memory::new() can't return a Error qed"
@@ -23,8 +23,8 @@
// borthersome.
#![cfg(feature = "wasmtime")]
use crate::WasmExecutionMethod;
use super::mk_test_runtime;
use crate::WasmExecutionMethod;
use codec::Encode as _;
mod smaps;
@@ -54,17 +54,11 @@ fn memory_consumption_compiled() {
}
instance
.call_export(
"test_dirty_plenty_memory",
&(heap_base as u32, 1u32).encode(),
)
.call_export("test_dirty_plenty_memory", &(heap_base as u32, 1u32).encode())
.unwrap();
let probe_1 = probe_rss(&*instance);
instance
.call_export(
"test_dirty_plenty_memory",
&(heap_base as u32, 1024u32).encode(),
)
.call_export("test_dirty_plenty_memory", &(heap_base as u32, 1024u32).encode())
.unwrap();
let probe_2 = probe_rss(&*instance);
@@ -19,8 +19,7 @@
//! A tool for extracting information about the memory consumption of the current process from
//! the procfs.
use std::ops::Range;
use std::collections::BTreeMap;
use std::{collections::BTreeMap, ops::Range};
/// An interface to the /proc/self/smaps
///
@@ -69,7 +68,8 @@ impl Smaps {
}
fn get_map(&self, addr: usize) -> &BTreeMap<String, usize> {
&self.0
&self
.0
.iter()
.find(|(range, _)| addr >= range.start && addr < range.end)
.unwrap()
@@ -20,20 +20,22 @@
mod linux;
mod sandbox;
use std::sync::Arc;
use codec::{Encode, Decode};
use codec::{Decode, Encode};
use hex_literal::hex;
use sp_core::{
blake2_128, blake2_256, ed25519, sr25519, map, Pair,
offchain::{OffchainWorkerExt, OffchainDbExt, testing},
traits::Externalities,
};
use sc_executor_common::{runtime_blob::RuntimeBlob, wasm_runtime::WasmModule};
use sc_runtime_test::wasm_binary_unwrap;
use sp_state_machine::TestExternalities as CoreTestExternalities;
use sp_trie::{TrieConfiguration, trie_types::Layout};
use sp_wasm_interface::HostFunctions as _;
use sp_core::{
blake2_128, blake2_256, ed25519, map,
offchain::{testing, OffchainDbExt, OffchainWorkerExt},
sr25519,
traits::Externalities,
Pair,
};
use sp_runtime::traits::BlakeTwo256;
use sc_executor_common::{wasm_runtime::WasmModule, runtime_blob::RuntimeBlob};
use sp_state_machine::TestExternalities as CoreTestExternalities;
use sp_trie::{trie_types::Layout, TrieConfiguration};
use sp_wasm_interface::HostFunctions as _;
use std::sync::Arc;
use tracing_subscriber::layer::SubscriberExt;
use crate::WasmExecutionMethod;
@@ -96,12 +98,7 @@ fn returning_should_work(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let output = call_in_wasm(
"test_empty_return",
&[],
wasm_method,
&mut ext,
).unwrap();
let output = call_in_wasm("test_empty_return", &[], wasm_method, &mut ext).unwrap();
assert_eq!(output, vec![0u8; 0]);
}
@@ -164,28 +161,13 @@ fn panicking_should_work(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let output = call_in_wasm(
"test_panic",
&[],
wasm_method,
&mut ext,
);
let output = call_in_wasm("test_panic", &[], wasm_method, &mut ext);
assert!(output.is_err());
let output = call_in_wasm(
"test_conditional_panic",
&[0],
wasm_method,
&mut ext,
);
let output = call_in_wasm("test_conditional_panic", &[0], wasm_method, &mut ext);
assert_eq!(Decode::decode(&mut &output.unwrap()[..]), Ok(Vec::<u8>::new()));
let output = call_in_wasm(
"test_conditional_panic",
&vec![2].encode(),
wasm_method,
&mut ext,
);
let output = call_in_wasm("test_conditional_panic", &vec![2].encode(), wasm_method, &mut ext);
assert!(output.is_err());
}
@@ -197,12 +179,9 @@ fn storage_should_work(wasm_method: WasmExecutionMethod) {
let mut ext = ext.ext();
ext.set_storage(b"foo".to_vec(), b"bar".to_vec());
let output = call_in_wasm(
"test_data_in",
&b"Hello world".to_vec().encode(),
wasm_method,
&mut ext,
).unwrap();
let output =
call_in_wasm("test_data_in", &b"Hello world".to_vec().encode(), wasm_method, &mut ext)
.unwrap();
assert_eq!(output, b"all ok!".to_vec().encode());
}
@@ -230,12 +209,9 @@ fn clear_prefix_should_work(wasm_method: WasmExecutionMethod) {
ext.set_storage(b"bbb".to_vec(), b"5".to_vec());
// 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,
).unwrap();
let output =
call_in_wasm("test_clear_prefix", &b"ab".to_vec().encode(), wasm_method, &mut ext)
.unwrap();
assert_eq!(output, b"all ok!".to_vec().encode());
}
@@ -256,21 +232,12 @@ fn blake2_256_should_work(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
assert_eq!(
call_in_wasm(
"test_blake2_256",
&[0],
wasm_method,
&mut ext,
).unwrap(),
call_in_wasm("test_blake2_256", &[0], wasm_method, &mut ext,).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,
).unwrap(),
call_in_wasm("test_blake2_256", &b"Hello world!".to_vec().encode(), wasm_method, &mut ext,)
.unwrap(),
blake2_256(&b"Hello world!"[..]).to_vec().encode(),
);
}
@@ -280,21 +247,12 @@ fn blake2_128_should_work(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
assert_eq!(
call_in_wasm(
"test_blake2_128",
&[0],
wasm_method,
&mut ext,
).unwrap(),
call_in_wasm("test_blake2_128", &[0], wasm_method, &mut ext,).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,
).unwrap(),
call_in_wasm("test_blake2_128", &b"Hello world!".to_vec().encode(), wasm_method, &mut ext,)
.unwrap(),
blake2_128(&b"Hello world!"[..]).to_vec().encode(),
);
}
@@ -304,25 +262,14 @@ fn sha2_256_should_work(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
assert_eq!(
call_in_wasm(
"test_sha2_256",
&[0],
wasm_method,
&mut ext,
)
.unwrap(),
call_in_wasm("test_sha2_256", &[0], wasm_method, &mut ext,).unwrap(),
hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
.to_vec()
.encode(),
);
assert_eq!(
call_in_wasm(
"test_sha2_256",
&b"Hello world!".to_vec().encode(),
wasm_method,
&mut ext,
)
.unwrap(),
call_in_wasm("test_sha2_256", &b"Hello world!".to_vec().encode(), wasm_method, &mut ext,)
.unwrap(),
hex!("c0535e4be2b79ffd93291305436bf889314e4a3faec05ecffcbb7df31ad9e51a")
.to_vec()
.encode(),
@@ -334,26 +281,17 @@ fn twox_256_should_work(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
assert_eq!(
call_in_wasm(
"test_twox_256",
&[0],
wasm_method,
&mut ext,
).unwrap(),
hex!(
"99e9d85137db46ef4bbea33613baafd56f963c64b1f3685a4eb4abd67ff6203a"
).to_vec().encode(),
call_in_wasm("test_twox_256", &[0], wasm_method, &mut ext,).unwrap(),
hex!("99e9d85137db46ef4bbea33613baafd56f963c64b1f3685a4eb4abd67ff6203a")
.to_vec()
.encode(),
);
assert_eq!(
call_in_wasm(
"test_twox_256",
&b"Hello world!".to_vec().encode(),
wasm_method,
&mut ext,
).unwrap(),
hex!(
"b27dfd7f223f177f2a13647b533599af0c07f68bda23d96d059da2b451a35a74"
).to_vec().encode(),
call_in_wasm("test_twox_256", &b"Hello world!".to_vec().encode(), wasm_method, &mut ext,)
.unwrap(),
hex!("b27dfd7f223f177f2a13647b533599af0c07f68bda23d96d059da2b451a35a74")
.to_vec()
.encode(),
);
}
@@ -362,21 +300,12 @@ fn twox_128_should_work(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
assert_eq!(
call_in_wasm(
"test_twox_128",
&[0],
wasm_method,
&mut ext,
).unwrap(),
call_in_wasm("test_twox_128", &[0], wasm_method, &mut ext,).unwrap(),
hex!("99e9d85137db46ef4bbea33613baafd5").to_vec().encode(),
);
assert_eq!(
call_in_wasm(
"test_twox_128",
&b"Hello world!".to_vec().encode(),
wasm_method,
&mut ext,
).unwrap(),
call_in_wasm("test_twox_128", &b"Hello world!".to_vec().encode(), wasm_method, &mut ext,)
.unwrap(),
hex!("b27dfd7f223f177f2a13647b533599af").to_vec().encode(),
);
}
@@ -392,12 +321,7 @@ fn ed25519_verify_should_work(wasm_method: WasmExecutionMethod) {
calldata.extend_from_slice(sig.as_ref());
assert_eq!(
call_in_wasm(
"test_ed25519_verify",
&calldata.encode(),
wasm_method,
&mut ext,
).unwrap(),
call_in_wasm("test_ed25519_verify", &calldata.encode(), wasm_method, &mut ext,).unwrap(),
true.encode(),
);
@@ -407,12 +331,7 @@ fn ed25519_verify_should_work(wasm_method: WasmExecutionMethod) {
calldata.extend_from_slice(other_sig.as_ref());
assert_eq!(
call_in_wasm(
"test_ed25519_verify",
&calldata.encode(),
wasm_method,
&mut ext,
).unwrap(),
call_in_wasm("test_ed25519_verify", &calldata.encode(), wasm_method, &mut ext,).unwrap(),
false.encode(),
);
}
@@ -428,12 +347,7 @@ fn sr25519_verify_should_work(wasm_method: WasmExecutionMethod) {
calldata.extend_from_slice(sig.as_ref());
assert_eq!(
call_in_wasm(
"test_sr25519_verify",
&calldata.encode(),
wasm_method,
&mut ext,
).unwrap(),
call_in_wasm("test_sr25519_verify", &calldata.encode(), wasm_method, &mut ext,).unwrap(),
true.encode(),
);
@@ -443,12 +357,7 @@ fn sr25519_verify_should_work(wasm_method: WasmExecutionMethod) {
calldata.extend_from_slice(other_sig.as_ref());
assert_eq!(
call_in_wasm(
"test_sr25519_verify",
&calldata.encode(),
wasm_method,
&mut ext,
).unwrap(),
call_in_wasm("test_sr25519_verify", &calldata.encode(), wasm_method, &mut ext,).unwrap(),
false.encode(),
);
}
@@ -458,12 +367,7 @@ fn ordered_trie_root_should_work(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let trie_input = vec![b"zero".to_vec(), b"one".to_vec(), b"two".to_vec()];
assert_eq!(
call_in_wasm(
"test_ordered_trie_root",
&[0],
wasm_method,
&mut ext.ext(),
).unwrap(),
call_in_wasm("test_ordered_trie_root", &[0], wasm_method, &mut ext.ext(),).unwrap(),
Layout::<BlakeTwo256>::ordered_trie_root(trie_input.iter()).as_bytes().encode(),
);
}
@@ -473,17 +377,14 @@ fn offchain_index(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let (offchain, _state) = testing::TestOffchainExt::new();
ext.register_extension(OffchainWorkerExt::new(offchain));
call_in_wasm(
"test_offchain_index_set",
&[0],
wasm_method,
&mut ext.ext(),
).unwrap();
call_in_wasm("test_offchain_index_set", &[0], wasm_method, &mut ext.ext()).unwrap();
use sp_core::offchain::OffchainOverlayedChange;
let data = ext.overlayed_changes().clone().offchain_drain_committed().find(|(k, _v)| {
k == &(sp_core::offchain::STORAGE_PREFIX.to_vec(), b"k".to_vec())
});
let data = ext
.overlayed_changes()
.clone()
.offchain_drain_committed()
.find(|(k, _v)| k == &(sp_core::offchain::STORAGE_PREFIX.to_vec(), b"k".to_vec()));
assert_eq!(data.map(|data| data.1), Some(OffchainOverlayedChange::SetValue(b"v".to_vec())));
}
@@ -494,12 +395,7 @@ fn offchain_local_storage_should_work(wasm_method: WasmExecutionMethod) {
ext.register_extension(OffchainDbExt::new(offchain.clone()));
ext.register_extension(OffchainWorkerExt::new(offchain));
assert_eq!(
call_in_wasm(
"test_offchain_local_storage",
&[0],
wasm_method,
&mut ext.ext(),
).unwrap(),
call_in_wasm("test_offchain_local_storage", &[0], wasm_method, &mut ext.ext(),).unwrap(),
true.encode(),
);
assert_eq!(state.read().persistent_storage.get(b"test"), Some(vec![]));
@@ -511,24 +407,18 @@ fn offchain_http_should_work(wasm_method: WasmExecutionMethod) {
let (offchain, state) = testing::TestOffchainExt::new();
ext.register_extension(OffchainWorkerExt::new(offchain));
state.write().expect_request(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()
},
);
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()
});
assert_eq!(
call_in_wasm(
"test_offchain_http",
&[0],
wasm_method,
&mut ext.ext(),
).unwrap(),
call_in_wasm("test_offchain_http", &[0], wasm_method, &mut ext.ext(),).unwrap(),
true.encode(),
);
}
@@ -539,7 +429,7 @@ fn should_trap_when_heap_exhausted(wasm_method: WasmExecutionMethod) {
let executor = crate::WasmExecutor::new(
wasm_method,
Some(17), // `17` is the initial number of pages compiled into the binary.
Some(17), // `17` is the initial number of pages compiled into the binary.
HostFunctions::host_functions(),
8,
None,
@@ -593,17 +483,13 @@ fn returns_mutable_static_bss(wasm_method: WasmExecutionMethod) {
let runtime = mk_test_runtime(wasm_method, 1024);
let instance = runtime.new_instance().unwrap();
let res = instance
.call_export("returns_mutable_static_bss", &[0])
.unwrap();
let res = instance.call_export("returns_mutable_static_bss", &[0]).unwrap();
assert_eq!(1, u64::decode(&mut &res[..]).unwrap());
// We expect that every invocation will need to return the initial
// value plus one. If the value increases more than that then it is
// a sign that the wasm runtime preserves the memory content.
let res = instance
.call_export("returns_mutable_static_bss", &[0])
.unwrap();
let res = instance.call_export("returns_mutable_static_bss", &[0]).unwrap();
assert_eq!(1, u64::decode(&mut &res[..]).unwrap());
}
@@ -638,7 +524,8 @@ fn heap_is_reset_between_calls(wasm_method: WasmExecutionMethod) {
let runtime = mk_test_runtime(wasm_method, 1024);
let instance = runtime.new_instance().unwrap();
let heap_base = instance.get_global_const("__heap_base")
let heap_base = instance
.get_global_const("__heap_base")
.expect("`__heap_base` is valid")
.expect("`__heap_base` exists")
.as_i32()
@@ -689,8 +576,8 @@ fn parallel_execution(wasm_method: WasmExecutionMethod) {
test_wasm_execution!(wasm_tracing_should_work);
fn wasm_tracing_should_work(wasm_method: WasmExecutionMethod) {
use std::sync::Mutex;
use sc_tracing::{SpanDatum, TraceEvent};
use std::sync::Mutex;
struct TestTraceHandler(Arc<Mutex<Vec<SpanDatum>>>);
@@ -706,36 +593,23 @@ fn wasm_tracing_should_work(wasm_method: WasmExecutionMethod) {
let handler = TestTraceHandler(traces.clone());
// Create subscriber with wasm_tracing disabled
let test_subscriber = tracing_subscriber::fmt().finish().with(
sc_tracing::ProfilingLayer::new_with_handler(
Box::new(handler), "default"
)
);
let test_subscriber = tracing_subscriber::fmt()
.finish()
.with(sc_tracing::ProfilingLayer::new_with_handler(Box::new(handler), "default"));
let _guard = tracing::subscriber::set_default(test_subscriber);
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let span_id = call_in_wasm(
"test_enter_span",
Default::default(),
wasm_method,
&mut ext,
).unwrap();
let span_id =
call_in_wasm("test_enter_span", Default::default(), wasm_method, &mut ext).unwrap();
let span_id = u64::decode(&mut &span_id[..]).unwrap();
assert!(
span_id > 0
);
assert!(span_id > 0);
call_in_wasm(
"test_exit_span",
&span_id.encode(),
wasm_method,
&mut ext,
).unwrap();
call_in_wasm("test_exit_span", &span_id.encode(), wasm_method, &mut ext).unwrap();
// Check there is only the single trace
let len = traces.lock().unwrap().len();
@@ -747,12 +621,7 @@ fn wasm_tracing_should_work(wasm_method: WasmExecutionMethod) {
assert_eq!(span_datum.name, "");
assert_eq!(values.bool_values.get("wasm").unwrap(), &true);
call_in_wasm(
"test_nested_spans",
Default::default(),
wasm_method,
&mut ext,
).unwrap();
call_in_wasm("test_nested_spans", Default::default(), wasm_method, &mut ext).unwrap();
let len = traces.lock().unwrap().len();
assert_eq!(len, 2);
}
@@ -762,12 +631,7 @@ fn spawning_runtime_instance_should_work(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
call_in_wasm(
"test_spawn",
&[],
wasm_method,
&mut ext,
).unwrap();
call_in_wasm("test_spawn", &[], wasm_method, &mut ext).unwrap();
}
test_wasm_execution!(spawning_runtime_instance_nested_should_work);
@@ -775,12 +639,7 @@ fn spawning_runtime_instance_nested_should_work(wasm_method: WasmExecutionMethod
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
call_in_wasm(
"test_nested_spawn",
&[],
wasm_method,
&mut ext,
).unwrap();
call_in_wasm("test_nested_spawn", &[], wasm_method, &mut ext).unwrap();
}
test_wasm_execution!(panic_in_spawned_instance_panics_on_joining_its_result);
@@ -788,12 +647,8 @@ fn panic_in_spawned_instance_panics_on_joining_its_result(wasm_method: WasmExecu
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let error_result = call_in_wasm(
"test_panic_in_spawned",
&[],
wasm_method,
&mut ext,
).unwrap_err();
let error_result =
call_in_wasm("test_panic_in_spawned", &[], wasm_method, &mut ext).unwrap_err();
assert!(format!("{}", error_result).contains("Spawned task"));
}
@@ -16,9 +16,8 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use super::{TestExternalities, call_in_wasm};
use crate::WasmExecutionMethod;
use crate::test_wasm_execution;
use super::{call_in_wasm, TestExternalities};
use crate::{test_wasm_execution, WasmExecutionMethod};
use codec::Encode;
@@ -27,7 +26,8 @@ fn sandbox_should_work(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let code = wat::parse_str(r#"
let code = wat::parse_str(
r#"
(module
(import "env" "assert" (func $assert (param i32)))
(import "env" "inc_counter" (func $inc_counter (param i32) (result i32)))
@@ -46,17 +46,12 @@ fn sandbox_should_work(wasm_method: WasmExecutionMethod) {
call $assert
)
)
"#).unwrap().encode();
"#,
)
.unwrap()
.encode();
assert_eq!(
call_in_wasm(
"test_sandbox",
&code,
wasm_method,
&mut ext,
).unwrap(),
true.encode(),
);
assert_eq!(call_in_wasm("test_sandbox", &code, wasm_method, &mut ext,).unwrap(), true.encode(),);
}
test_wasm_execution!(sandbox_trap);
@@ -64,7 +59,8 @@ fn sandbox_trap(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let code = wat::parse_str(r#"
let code = wat::parse_str(
r#"
(module
(import "env" "assert" (func $assert (param i32)))
(func (export "call")
@@ -72,17 +68,11 @@ fn sandbox_trap(wasm_method: WasmExecutionMethod) {
call $assert
)
)
"#).unwrap();
"#,
)
.unwrap();
assert_eq!(
call_in_wasm(
"test_sandbox",
&code,
wasm_method,
&mut ext,
).unwrap(),
vec![0],
);
assert_eq!(call_in_wasm("test_sandbox", &code, wasm_method, &mut ext,).unwrap(), vec![0],);
}
test_wasm_execution!(start_called);
@@ -90,7 +80,8 @@ fn start_called(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let code = wat::parse_str(r#"
let code = wat::parse_str(
r#"
(module
(import "env" "assert" (func $assert (param i32)))
(import "env" "inc_counter" (func $inc_counter (param i32) (result i32)))
@@ -115,17 +106,12 @@ fn start_called(wasm_method: WasmExecutionMethod) {
call $assert
)
)
"#).unwrap().encode();
"#,
)
.unwrap()
.encode();
assert_eq!(
call_in_wasm(
"test_sandbox",
&code,
wasm_method,
&mut ext,
).unwrap(),
true.encode(),
);
assert_eq!(call_in_wasm("test_sandbox", &code, wasm_method, &mut ext,).unwrap(), true.encode(),);
}
test_wasm_execution!(invoke_args);
@@ -133,7 +119,8 @@ fn invoke_args(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let code = wat::parse_str(r#"
let code = wat::parse_str(
r#"
(module
(import "env" "assert" (func $assert (param i32)))
@@ -154,15 +141,13 @@ fn invoke_args(wasm_method: WasmExecutionMethod) {
)
)
)
"#).unwrap().encode();
"#,
)
.unwrap()
.encode();
assert_eq!(
call_in_wasm(
"test_sandbox_args",
&code,
wasm_method,
&mut ext,
).unwrap(),
call_in_wasm("test_sandbox_args", &code, wasm_method, &mut ext,).unwrap(),
true.encode(),
);
}
@@ -172,7 +157,8 @@ fn return_val(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let code = wat::parse_str(r#"
let code = wat::parse_str(
r#"
(module
(func (export "call") (param $x i32) (result i32)
(i32.add
@@ -181,15 +167,13 @@ fn return_val(wasm_method: WasmExecutionMethod) {
)
)
)
"#).unwrap().encode();
"#,
)
.unwrap()
.encode();
assert_eq!(
call_in_wasm(
"test_sandbox_return_val",
&code,
wasm_method,
&mut ext,
).unwrap(),
call_in_wasm("test_sandbox_return_val", &code, wasm_method, &mut ext,).unwrap(),
true.encode(),
);
}
@@ -199,22 +183,21 @@ fn unlinkable_module(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let code = wat::parse_str(r#"
let code = wat::parse_str(
r#"
(module
(import "env" "non-existent" (func))
(func (export "call")
)
)
"#).unwrap().encode();
"#,
)
.unwrap()
.encode();
assert_eq!(
call_in_wasm(
"test_sandbox_instantiate",
&code,
wasm_method,
&mut ext,
).unwrap(),
call_in_wasm("test_sandbox_instantiate", &code, wasm_method, &mut ext,).unwrap(),
1u8.encode(),
);
}
@@ -228,12 +211,7 @@ fn corrupted_module(wasm_method: WasmExecutionMethod) {
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,
).unwrap(),
call_in_wasm("test_sandbox_instantiate", &code, wasm_method, &mut ext,).unwrap(),
1u8.encode(),
);
}
@@ -243,7 +221,8 @@ fn start_fn_ok(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let code = wat::parse_str(r#"
let code = wat::parse_str(
r#"
(module
(func (export "call")
)
@@ -253,15 +232,13 @@ fn start_fn_ok(wasm_method: WasmExecutionMethod) {
(start $start)
)
"#).unwrap().encode();
"#,
)
.unwrap()
.encode();
assert_eq!(
call_in_wasm(
"test_sandbox_instantiate",
&code,
wasm_method,
&mut ext,
).unwrap(),
call_in_wasm("test_sandbox_instantiate", &code, wasm_method, &mut ext,).unwrap(),
0u8.encode(),
);
}
@@ -271,7 +248,8 @@ fn start_fn_traps(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let code = wat::parse_str(r#"
let code = wat::parse_str(
r#"
(module
(func (export "call")
)
@@ -282,15 +260,13 @@ fn start_fn_traps(wasm_method: WasmExecutionMethod) {
(start $start)
)
"#).unwrap().encode();
"#,
)
.unwrap()
.encode();
assert_eq!(
call_in_wasm(
"test_sandbox_instantiate",
&code,
wasm_method,
&mut ext,
).unwrap(),
call_in_wasm("test_sandbox_instantiate", &code, wasm_method, &mut ext,).unwrap(),
2u8.encode(),
);
}
@@ -300,19 +276,18 @@ fn get_global_val_works(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let code = wat::parse_str(r#"
let code = wat::parse_str(
r#"
(module
(global (export "test_global") i64 (i64.const 500))
)
"#).unwrap().encode();
"#,
)
.unwrap()
.encode();
assert_eq!(
call_in_wasm(
"test_sandbox_get_global_val",
&code,
wasm_method,
&mut ext,
).unwrap(),
call_in_wasm("test_sandbox_get_global_val", &code, wasm_method, &mut ext,).unwrap(),
500i64.encode(),
);
}
+10 -11
View File
@@ -29,26 +29,25 @@
//! wasm engine used, instance cache.
#![warn(missing_docs)]
#![recursion_limit="128"]
#![recursion_limit = "128"]
#[macro_use]
mod native_executor;
mod wasm_runtime;
#[cfg(test)]
mod integration_tests;
mod wasm_runtime;
pub use wasmi;
pub use native_executor::{
with_externalities_safe, NativeExecutor, WasmExecutor, NativeExecutionDispatch,
};
pub use sp_version::{RuntimeVersion, NativeVersion};
pub use codec::Codec;
pub use native_executor::{
with_externalities_safe, NativeExecutionDispatch, NativeExecutor, WasmExecutor,
};
#[doc(hidden)]
pub use sp_core::traits::{Externalities};
pub use sp_core::traits::Externalities;
pub use sp_version::{NativeVersion, RuntimeVersion};
#[doc(hidden)]
pub use sp_wasm_interface;
pub use wasm_runtime::WasmExecutionMethod;
pub use wasm_runtime::read_embedded_version;
pub use wasm_runtime::{read_embedded_version, WasmExecutionMethod};
pub use wasmi;
pub use sc_executor_common::{error, sandbox};
@@ -68,10 +67,10 @@ pub trait RuntimeInfo {
#[cfg(test)]
mod tests {
use super::*;
use sc_executor_common::runtime_blob::RuntimeBlob;
use sc_runtime_test::wasm_binary_unwrap;
use sp_io::TestExternalities;
use sp_wasm_interface::HostFunctions;
use sc_executor_common::runtime_blob::RuntimeBlob;
#[test]
fn call_in_interpreted_wasm_works() {
+106 -132
View File
@@ -17,32 +17,36 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::{
RuntimeInfo, error::{Error, Result},
error::{Error, Result},
wasm_runtime::{RuntimeCache, WasmExecutionMethod},
RuntimeInfo,
};
use std::{
collections::HashMap,
panic::{UnwindSafe, AssertUnwindSafe},
result,
sync::{Arc, atomic::{AtomicU64, Ordering}, mpsc},
panic::{AssertUnwindSafe, UnwindSafe},
path::PathBuf,
result,
sync::{
atomic::{AtomicU64, Ordering},
mpsc, Arc,
},
};
use sp_version::{NativeVersion, RuntimeVersion};
use codec::{Decode, Encode};
use sp_core::{
NativeOrEncoded,
traits::{CodeExecutor, Externalities, RuntimeCode, RuntimeSpawnExt, RuntimeSpawn},
};
use log::trace;
use sp_wasm_interface::{HostFunctions, Function};
use sc_executor_common::{
wasm_runtime::{WasmInstance, WasmModule, InvokeMethod},
runtime_blob::RuntimeBlob,
wasm_runtime::{InvokeMethod, WasmInstance, WasmModule},
};
use sp_core::{
traits::{CodeExecutor, Externalities, RuntimeCode, RuntimeSpawn, RuntimeSpawnExt},
NativeOrEncoded,
};
use sp_externalities::ExternalitiesExt as _;
use sp_tasks::new_async_externalities;
use sp_version::{NativeVersion, RuntimeVersion};
use sp_wasm_interface::{Function, HostFunctions};
/// Default num of pages for the heap
const DEFAULT_HEAP_PAGES: u64 = 2048;
@@ -51,25 +55,23 @@ const DEFAULT_HEAP_PAGES: u64 = 2048;
///
/// If the inner closure panics, it will be caught and return an error.
pub fn with_externalities_safe<F, U>(ext: &mut dyn Externalities, f: F) -> Result<U>
where F: UnwindSafe + FnOnce() -> U
where
F: UnwindSafe + FnOnce() -> U,
{
sp_externalities::set_and_run_with_externalities(
ext,
move || {
// Substrate uses custom panic hook that terminates process on panic. Disable
// termination for the native call.
let _guard = sp_panic_handler::AbortGuard::force_unwind();
std::panic::catch_unwind(f).map_err(|e| {
if let Some(err) = e.downcast_ref::<String>() {
Error::RuntimePanicked(err.clone())
} else if let Some(err) = e.downcast_ref::<&'static str>() {
Error::RuntimePanicked(err.to_string())
} else {
Error::RuntimePanicked("Unknown panic".into())
}
})
},
)
sp_externalities::set_and_run_with_externalities(ext, move || {
// Substrate uses custom panic hook that terminates process on panic. Disable
// termination for the native call.
let _guard = sp_panic_handler::AbortGuard::force_unwind();
std::panic::catch_unwind(f).map_err(|e| {
if let Some(err) = e.downcast_ref::<String>() {
Error::RuntimePanicked(err.clone())
} else if let Some(err) = e.downcast_ref::<&'static str>() {
Error::RuntimePanicked(err.to_string())
} else {
Error::RuntimePanicked("Unknown panic".into())
}
})
})
}
/// Delegate for dispatching a CodeExecutor call.
@@ -163,7 +165,8 @@ impl WasmExecutor {
allow_missing_host_functions: bool,
f: F,
) -> Result<R>
where F: FnOnce(
where
F: FnOnce(
AssertUnwindSafe<&Arc<dyn WasmModule>>,
AssertUnwindSafe<&dyn WasmInstance>,
Option<&RuntimeVersion>,
@@ -182,7 +185,7 @@ impl WasmExecutor {
let instance = AssertUnwindSafe(instance);
let ext = AssertUnwindSafe(ext);
f(module, instance, version, ext)
}
},
)? {
Ok(r) => r,
Err(e) => Err(e),
@@ -245,7 +248,7 @@ impl sp_core::traits::ReadRuntimeVersion for WasmExecutor {
.map_err(|e| format!("Failed to read the static section: {:?}", e))
.map(|v| v.map(|v| v.encode()))?
{
return Ok(version);
return Ok(version)
}
// If the blob didn't have embedded runtime version section, we fallback to the legacy
@@ -296,13 +299,13 @@ impl<D: NativeExecutionDispatch> NativeExecutor<D> {
.into_iter()
// filter out any host function overrides provided.
.filter(|host_fn| {
extended.iter()
extended
.iter()
.find(|ext_host_fn| host_fn.name() == ext_host_fn.name())
.is_none()
})
.collect::<Vec<_>>();
// Add the custom host functions provided by the user.
host_functions.extend(extended);
let wasm_executor = WasmExecutor::new(
@@ -331,13 +334,10 @@ impl<D: NativeExecutionDispatch> RuntimeInfo for NativeExecutor<D> {
ext: &mut dyn Externalities,
runtime_code: &RuntimeCode,
) -> Result<RuntimeVersion> {
self.wasm.with_instance(
runtime_code,
ext,
false,
|_module, _instance, version, _ext|
Ok(version.cloned().ok_or_else(|| Error::ApiError("Unknown version".into()))),
)
self.wasm
.with_instance(runtime_code, ext, false, |_module, _instance, version, _ext| {
Ok(version.cloned().ok_or_else(|| Error::ApiError("Unknown version".into())))
})
}
}
@@ -358,70 +358,67 @@ impl RuntimeSpawn for RuntimeInstanceSpawn {
let module = self.module.clone();
let scheduler = self.scheduler.clone();
self.scheduler.spawn("executor-extra-runtime-instance", Box::pin(async move {
let module = AssertUnwindSafe(module);
self.scheduler.spawn(
"executor-extra-runtime-instance",
Box::pin(async move {
let module = AssertUnwindSafe(module);
let async_ext = match new_async_externalities(scheduler.clone()) {
Ok(val) => val,
Err(e) => {
log::error!(
target: "executor",
"Failed to setup externalities for async context: {}",
e,
);
let async_ext = match new_async_externalities(scheduler.clone()) {
Ok(val) => val,
Err(e) => {
log::error!(
target: "executor",
"Failed to setup externalities for async context: {}",
e,
);
// This will drop sender and receiver end will panic
return;
}
};
// This will drop sender and receiver end will panic
return
},
};
let mut async_ext = match async_ext.with_runtime_spawn(
Box::new(RuntimeInstanceSpawn::new(module.clone(), scheduler))
) {
Ok(val) => val,
Err(e) => {
log::error!(
target: "executor",
"Failed to setup runtime extension for async externalities: {}",
e,
);
let mut async_ext = match async_ext.with_runtime_spawn(Box::new(
RuntimeInstanceSpawn::new(module.clone(), scheduler),
)) {
Ok(val) => val,
Err(e) => {
log::error!(
target: "executor",
"Failed to setup runtime extension for async externalities: {}",
e,
);
// This will drop sender and receiver end will panic
return;
}
};
let result = with_externalities_safe(
&mut async_ext,
move || {
// This will drop sender and receiver end will panic
return
},
};
let result = with_externalities_safe(&mut async_ext, move || {
// FIXME: Should be refactored to shared "instance factory".
// Instantiating wasm here every time is suboptimal at the moment, shared
// pool of instances should be used.
//
// https://github.com/paritytech/substrate/issues/7354
let instance = module.new_instance()
.expect("Failed to create new instance from module");
let instance =
module.new_instance().expect("Failed to create new instance from module");
instance.call(
InvokeMethod::TableWithWrapper { dispatcher_ref, func },
&data[..],
).expect("Failed to invoke instance.")
instance
.call(InvokeMethod::TableWithWrapper { dispatcher_ref, func }, &data[..])
.expect("Failed to invoke instance.")
});
match result {
Ok(output) => {
let _ = sender.send(output);
},
Err(error) => {
// If execution is panicked, the `join` in the original runtime code will panic as well,
// since the sender is dropped without sending anything.
log::error!("Call error in spawned task: {:?}", error);
},
}
);
match result {
Ok(output) => {
let _ = sender.send(output);
},
Err(error) => {
// If execution is panicked, the `join` in the original runtime code will panic as well,
// since the sender is dropped without sending anything.
log::error!("Call error in spawned task: {:?}", error);
},
}
}));
}),
);
new_handle
}
@@ -438,12 +435,7 @@ impl RuntimeInstanceSpawn {
module: Arc<dyn WasmModule>,
scheduler: Box<dyn sp_core::traits::SpawnNamed>,
) -> Self {
Self {
module,
scheduler,
counter: 0.into(),
tasks: HashMap::new().into(),
}
Self { module, scheduler, counter: 0.into(), tasks: HashMap::new().into() }
}
fn with_externalities_and_module(
@@ -495,17 +487,13 @@ impl<D: NativeExecutionDispatch + 'static> CodeExecutor for NativeExecutor<D> {
ext,
false,
|module, instance, onchain_version, mut ext| {
let onchain_version = onchain_version.ok_or_else(
|| Error::ApiError("Unknown version".into())
)?;
let onchain_version =
onchain_version.ok_or_else(|| Error::ApiError("Unknown version".into()))?;
let can_call_with = onchain_version.can_call_with(&self.native_version.runtime_version);
let can_call_with =
onchain_version.can_call_with(&self.native_version.runtime_version);
match (
use_native,
can_call_with,
native_call,
) {
match (use_native, can_call_with, native_call) {
(_, false, _) | (false, _, _) => {
if !can_call_with {
trace!(
@@ -516,13 +504,10 @@ impl<D: NativeExecutionDispatch + 'static> CodeExecutor for NativeExecutor<D> {
);
}
with_externalities_safe(
&mut **ext,
move || {
preregister_builtin_ext(module.clone());
instance.call_export(method, data).map(NativeOrEncoded::Encoded)
}
)
with_externalities_safe(&mut **ext, move || {
preregister_builtin_ext(module.clone());
instance.call_export(method, data).map(NativeOrEncoded::Encoded)
})
},
(true, true, Some(call)) => {
trace!(
@@ -535,13 +520,10 @@ impl<D: NativeExecutionDispatch + 'static> CodeExecutor for NativeExecutor<D> {
used_native = true;
let res = with_externalities_safe(&mut **ext, move || (call)())
.and_then(|r| r
.map(NativeOrEncoded::Native)
.map_err(Error::ApiError)
);
.and_then(|r| r.map(NativeOrEncoded::Native).map_err(Error::ApiError));
Ok(res)
}
},
_ => {
trace!(
target: "executor",
@@ -552,9 +534,9 @@ impl<D: NativeExecutionDispatch + 'static> CodeExecutor for NativeExecutor<D> {
used_native = true;
Ok(D::dispatch(&mut **ext, method, data).map(NativeOrEncoded::Encoded))
}
},
}
}
},
);
(result, used_native)
}
@@ -617,7 +599,6 @@ impl<D: NativeExecutionDispatch> sp_core::traits::ReadRuntimeVersion for NativeE
///
/// When you have multiple interfaces, you can give the host functions as a tuple e.g.:
/// `(my_interface::HostFunctions, my_interface2::HostFunctions)`
///
#[macro_export]
macro_rules! native_executor_instance {
( $pub:vis $name:ident, $dispatcher:path, $version:path $(,)?) => {
@@ -675,16 +656,9 @@ mod tests {
#[test]
fn native_executor_registers_custom_interface() {
let executor = NativeExecutor::<MyExecutor>::new(
WasmExecutionMethod::Interpreted,
None,
8,
);
let executor = NativeExecutor::<MyExecutor>::new(WasmExecutionMethod::Interpreted, None, 8);
my_interface::HostFunctions::host_functions().iter().for_each(|function| {
assert_eq!(
executor.wasm.host_functions.iter().filter(|f| f == &function).count(),
2,
);
assert_eq!(executor.wasm.host_functions.iter().filter(|f| f == &function).count(), 2,);
});
my_interface::say_hello_world("hey");
+82 -99
View File
@@ -21,17 +21,19 @@
//! The primary means of accessing the runtimes is through a cache which saves the reusable
//! components of the runtime that are expensive to initialize.
use std::sync::Arc;
use crate::error::{Error, WasmError};
use parking_lot::Mutex;
use codec::Decode;
use sp_core::traits::{Externalities, RuntimeCode, FetchRuntimeCode};
use sp_version::RuntimeVersion;
use std::panic::AssertUnwindSafe;
use std::path::{Path, PathBuf};
use parking_lot::Mutex;
use sc_executor_common::{
wasm_runtime::{WasmModule, WasmInstance},
runtime_blob::RuntimeBlob,
wasm_runtime::{WasmInstance, WasmModule},
};
use sp_core::traits::{Externalities, FetchRuntimeCode, RuntimeCode};
use sp_version::RuntimeVersion;
use std::{
panic::AssertUnwindSafe,
path::{Path, PathBuf},
sync::Arc,
};
use sp_wasm_interface::Function;
@@ -70,27 +72,26 @@ struct VersionedRuntime {
impl VersionedRuntime {
/// Run the given closure `f` with an instance of this runtime.
fn with_instance<'c, R, F>(
&self,
ext: &mut dyn Externalities,
f: F,
) -> Result<R, Error>
where F: FnOnce(
fn with_instance<'c, R, F>(&self, ext: &mut dyn Externalities, f: F) -> Result<R, Error>
where
F: FnOnce(
&Arc<dyn WasmModule>,
&dyn WasmInstance,
Option<&RuntimeVersion>,
&mut dyn Externalities)
-> Result<R, Error>,
&mut dyn Externalities,
) -> Result<R, Error>,
{
// Find a free instance
let instance = self.instances
let instance = self
.instances
.iter()
.enumerate()
.find_map(|(index, i)| i.try_lock().map(|i| (index, i)));
match instance {
Some((index, mut locked)) => {
let (instance, new_inst) = locked.take()
let (instance, new_inst) = locked
.take()
.map(|r| Ok((r, false)))
.unwrap_or_else(|| self.module.new_instance().map(|i| (i, true)))?;
@@ -131,7 +132,7 @@ impl VersionedRuntime {
let instance = self.module.new_instance()?;
f(&self.module, &*instance, self.version.as_ref(), ext)
}
},
}
}
}
@@ -168,11 +169,7 @@ impl RuntimeCache {
/// `cache_path` allows to specify an optional directory where the executor can store files
/// for caching.
pub fn new(max_runtime_instances: usize, cache_path: Option<PathBuf>) -> RuntimeCache {
RuntimeCache {
runtimes: Default::default(),
max_runtime_instances,
cache_path,
}
RuntimeCache { runtimes: Default::default(), max_runtime_instances, cache_path }
}
/// Prepares a WASM module instance and executes given function for it.
@@ -213,29 +210,31 @@ impl RuntimeCache {
allow_missing_func_imports: bool,
f: F,
) -> Result<Result<R, Error>, Error>
where F: FnOnce(
where
F: FnOnce(
&Arc<dyn WasmModule>,
&dyn WasmInstance,
Option<&RuntimeVersion>,
&mut dyn Externalities)
-> Result<R, Error>,
&mut dyn Externalities,
) -> Result<R, Error>,
{
let code_hash = &runtime_code.hash;
let heap_pages = runtime_code.heap_pages.unwrap_or(default_heap_pages);
let mut runtimes = self.runtimes.lock(); // this must be released prior to calling f
let pos = runtimes.iter().position(|r| r.as_ref().map_or(
false,
|r| r.wasm_method == wasm_method &&
r.code_hash == *code_hash &&
r.heap_pages == heap_pages
));
let pos = runtimes.iter().position(|r| {
r.as_ref().map_or(false, |r| {
r.wasm_method == wasm_method &&
r.code_hash == *code_hash &&
r.heap_pages == heap_pages
})
});
let runtime = match pos {
Some(n) => runtimes[n]
.clone()
.expect("`position` only returns `Some` for entries that are `Some`"),
None => {
None => {
let code = runtime_code.fetch_runtime_code().ok_or(WasmError::CodeNotFound)?;
#[cfg(not(target_os = "unknown"))]
@@ -262,30 +261,29 @@ impl RuntimeCache {
result.version,
time.elapsed().as_millis(),
);
}
},
Err(ref err) => {
log::warn!(target: "wasm-runtime", "Cannot create a runtime: {:?}", err);
}
},
}
Arc::new(result?)
}
},
};
// Rearrange runtimes by last recently used.
match pos {
Some(0) => {},
Some(n) => {
for i in (1 .. n + 1).rev() {
Some(n) =>
for i in (1..n + 1).rev() {
runtimes.swap(i, i - 1);
}
}
},
None => {
runtimes[MAX_RUNTIMES-1] = Some(runtime.clone());
for i in (1 .. MAX_RUNTIMES).rev() {
runtimes[MAX_RUNTIMES - 1] = Some(runtime.clone());
for i in (1..MAX_RUNTIMES).rev() {
runtimes.swap(i, i - 1);
}
}
},
}
drop(runtimes);
@@ -317,49 +315,48 @@ pub fn create_wasm_runtime_with_code(
allow_missing_func_imports,
)
.map(|runtime| -> Arc<dyn WasmModule> { Arc::new(runtime) })
}
#[cfg(feature = "wasmtime")]
WasmExecutionMethod::Compiled => {
sc_executor_wasmtime::create_runtime(
blob,
sc_executor_wasmtime::Config {
heap_pages: heap_pages as u32,
allow_missing_func_imports,
cache_path: cache_path.map(ToOwned::to_owned),
semantics: sc_executor_wasmtime::Semantics {
fast_instance_reuse: true,
deterministic_stack_limit: None,
canonicalize_nans: false,
},
},
host_functions,
).map(|runtime| -> Arc<dyn WasmModule> { Arc::new(runtime) })
},
#[cfg(feature = "wasmtime")]
WasmExecutionMethod::Compiled => sc_executor_wasmtime::create_runtime(
blob,
sc_executor_wasmtime::Config {
heap_pages: heap_pages as u32,
allow_missing_func_imports,
cache_path: cache_path.map(ToOwned::to_owned),
semantics: sc_executor_wasmtime::Semantics {
fast_instance_reuse: true,
deterministic_stack_limit: None,
canonicalize_nans: false,
},
},
host_functions,
)
.map(|runtime| -> Arc<dyn WasmModule> { Arc::new(runtime) }),
}
}
fn decode_version(mut version: &[u8]) -> Result<RuntimeVersion, WasmError> {
let v: RuntimeVersion = sp_api::OldRuntimeVersion::decode(&mut &version[..])
.map_err(|_|
WasmError::Instantiation(
"failed to decode \"Core_version\" result using old runtime version".into(),
)
)?.into();
.map_err(|_| {
WasmError::Instantiation(
"failed to decode \"Core_version\" result using old runtime version".into(),
)
})?
.into();
let core_api_id = sp_core::hashing::blake2_64(b"Core");
if v.has_api_with(&core_api_id, |v| v >= 3) {
sp_api::RuntimeVersion::decode(&mut version)
.map_err(|_|
WasmError::Instantiation("failed to decode \"Core_version\" result".into())
)
sp_api::RuntimeVersion::decode(&mut version).map_err(|_| {
WasmError::Instantiation("failed to decode \"Core_version\" result".into())
})
} else {
Ok(v)
}
}
fn decode_runtime_apis(apis: &[u8]) -> Result<Vec<([u8; 8], u32)>, WasmError> {
use std::convert::TryFrom;
use sp_api::RUNTIME_API_INFO_SIZE;
use std::convert::TryFrom;
apis.chunks(RUNTIME_API_INFO_SIZE)
.map(|chunk| {
@@ -367,9 +364,7 @@ fn decode_runtime_apis(apis: &[u8]) -> Result<Vec<([u8; 8], u32)>, WasmError> {
// completely divide by `RUNTIME_API_INFO_SIZE`.
<[u8; RUNTIME_API_INFO_SIZE]>::try_from(chunk)
.map(sp_api::deserialize_runtime_api_info)
.map_err(|_| {
WasmError::Other("a clipped runtime api info declaration".to_owned())
})
.map_err(|_| WasmError::Other("a clipped runtime api info declaration".to_owned()))
})
.collect::<Result<Vec<_>, WasmError>>()
}
@@ -379,9 +374,7 @@ fn decode_runtime_apis(apis: &[u8]) -> Result<Vec<([u8; 8], u32)>, WasmError> {
///
/// If there are no such sections, it returns `None`. If there is an error during decoding those
/// sections, `Err` will be returned.
pub fn read_embedded_version(
blob: &RuntimeBlob,
) -> Result<Option<RuntimeVersion>, WasmError> {
pub fn read_embedded_version(blob: &RuntimeBlob) -> Result<Option<RuntimeVersion>, WasmError> {
if let Some(mut version_section) = blob.custom_section_contents("runtime_version") {
// We do not use `decode_version` here because the runtime_version section is not supposed
// to ever contain a legacy version. Apart from that `decode_version` relies on presence
@@ -389,9 +382,7 @@ pub fn read_embedded_version(
// the structure found in the `runtime_version` always contain an empty `apis` field. Therefore
// the version read will be mistakenly treated as an legacy one.
let mut decoded_version = sp_api::RuntimeVersion::decode(&mut version_section)
.map_err(|_|
WasmError::Instantiation("failed to decode version section".into())
)?;
.map_err(|_| WasmError::Instantiation("failed to decode version section".into()))?;
// Don't stop on this and check if there is a special section that encodes all runtime APIs.
if let Some(apis_section) = blob.custom_section_contents("runtime_apis") {
@@ -443,10 +434,10 @@ fn create_versioned_wasm_runtime(
// The following unwind safety assertion is OK because if the method call panics, the
// runtime will be dropped.
let runtime = AssertUnwindSafe(runtime.as_ref());
crate::native_executor::with_externalities_safe(
&mut **ext,
move || runtime.new_instance()?.call("Core_version".into(), &[])
).map_err(|_| WasmError::Instantiation("panic in call to get runtime version".into()))?
crate::native_executor::with_externalities_safe(&mut **ext, move || {
runtime.new_instance()?.call("Core_version".into(), &[])
})
.map_err(|_| WasmError::Instantiation("panic in call to get runtime version".into()))?
};
if let Ok(version_buf) = version_result {
@@ -457,23 +448,16 @@ fn create_versioned_wasm_runtime(
let mut instances = Vec::with_capacity(max_instances);
instances.resize_with(max_instances, || Mutex::new(None));
Ok(VersionedRuntime {
code_hash,
module: runtime,
version,
heap_pages,
wasm_method,
instances,
})
Ok(VersionedRuntime { code_hash, module: runtime, version, heap_pages, wasm_method, instances })
}
#[cfg(test)]
mod tests {
use super::*;
use sp_wasm_interface::HostFunctions;
use sp_api::{Core, RuntimeApiInfo};
use substrate_test_runtime::Block;
use codec::Encode;
use sp_api::{Core, RuntimeApiInfo};
use sp_wasm_interface::HostFunctions;
use substrate_test_runtime::Block;
#[test]
fn host_functions_are_equal() {
@@ -533,7 +517,8 @@ mod tests {
let wasm = sp_maybe_compressed_blob::decompress(
substrate_test_runtime::wasm_binary_unwrap(),
sp_maybe_compressed_blob::CODE_BLOB_BOMB_LIMIT,
).expect("Decompressing works");
)
.expect("Decompressing works");
let runtime_version = RuntimeVersion {
spec_name: "test_replace".into(),
@@ -545,10 +530,8 @@ mod tests {
transaction_version: 100,
};
let embedded = sp_version::embed::embed_runtime_version(
&wasm,
runtime_version.clone(),
).expect("Embedding works");
let embedded = sp_version::embed::embed_runtime_version(&wasm, runtime_version.clone())
.expect("Embedding works");
let blob = RuntimeBlob::new(&embedded).expect("Embedded blob is valid");
let read_version = read_embedded_version(&blob)
+89 -94
View File
@@ -18,25 +18,26 @@
//! This crate provides an implementation of `WasmModule` that is baked by wasmi.
use std::{str, cell::RefCell, sync::Arc};
use wasmi::{
Module, ModuleInstance, MemoryInstance, MemoryRef, TableRef, ImportsBuilder, ModuleRef,
FuncInstance, memory_units::Pages,
RuntimeValue::{I32, I64, self},
};
use codec::{Encode, Decode};
use sp_core::sandbox as sandbox_primitives;
use log::{error, trace, debug};
use sp_wasm_interface::{
FunctionContext, Pointer, WordSize, Sandbox, MemoryId, Result as WResult, Function,
};
use sp_runtime_interface::unpack_ptr_and_len;
use sc_executor_common::wasm_runtime::{WasmModule, WasmInstance, InvokeMethod};
use codec::{Decode, Encode};
use log::{debug, error, trace};
use sc_executor_common::{
error::{Error, WasmError},
runtime_blob::{DataSegmentsSnapshot, RuntimeBlob},
sandbox,
wasm_runtime::{InvokeMethod, WasmInstance, WasmModule},
};
use sp_core::sandbox as sandbox_primitives;
use sp_runtime_interface::unpack_ptr_and_len;
use sp_wasm_interface::{
Function, FunctionContext, MemoryId, Pointer, Result as WResult, Sandbox, WordSize,
};
use std::{cell::RefCell, str, sync::Arc};
use wasmi::{
memory_units::Pages,
FuncInstance, ImportsBuilder, MemoryInstance, MemoryRef, Module, ModuleInstance, ModuleRef,
RuntimeValue::{self, I32, I64},
TableRef,
};
use sc_executor_common::runtime_blob::{RuntimeBlob, DataSegmentsSnapshot};
struct FunctionExecutor<'a> {
sandbox_store: sandbox::Store<wasmi::FuncRef>,
@@ -109,16 +110,14 @@ impl<'a> FunctionContext for FunctionExecutor<'a> {
fn allocate_memory(&mut self, size: WordSize) -> WResult<Pointer<u8>> {
let heap = &mut self.heap;
self.memory.with_direct_access_mut(|mem| {
heap.allocate(mem, size).map_err(|e| e.to_string())
})
self.memory
.with_direct_access_mut(|mem| heap.allocate(mem, size).map_err(|e| e.to_string()))
}
fn deallocate_memory(&mut self, ptr: Pointer<u8>) -> WResult<()> {
let heap = &mut self.heap;
self.memory.with_direct_access_mut(|mem| {
heap.deallocate(mem, ptr).map_err(|e| e.to_string())
})
self.memory
.with_direct_access_mut(|mem| heap.deallocate(mem, ptr).map_err(|e| e.to_string()))
}
fn sandbox(&mut self) -> &mut dyn Sandbox {
@@ -173,11 +172,7 @@ impl<'a> Sandbox for FunctionExecutor<'a> {
self.sandbox_store.memory_teardown(memory_id).map_err(|e| e.to_string())
}
fn memory_new(
&mut self,
initial: u32,
maximum: u32,
) -> WResult<MemoryId> {
fn memory_new(&mut self, initial: u32, maximum: u32) -> WResult<MemoryId> {
self.sandbox_store.new_memory(initial, maximum).map_err(|e| e.to_string())
}
@@ -213,7 +208,7 @@ impl<'a> Sandbox for FunctionExecutor<'a> {
self.write_memory(return_val, val).map_err(|_| "Return value buffer is OOB")?;
Ok(sandbox_primitives::ERR_OK)
})
}
},
Err(_) => Ok(sandbox_primitives::ERR_EXECUTION),
}
}
@@ -231,9 +226,12 @@ impl<'a> Sandbox for FunctionExecutor<'a> {
) -> WResult<u32> {
// Extract a dispatch thunk from instance's table by the specified index.
let dispatch_thunk = {
let table = self.table.as_ref()
let table = self
.table
.as_ref()
.ok_or_else(|| "Runtime doesn't have a table; sandbox is unavailable")?;
table.get(dispatch_thunk_id)
table
.get(dispatch_thunk_id)
.map_err(|_| "dispatch_thunk_idx is out of the table bounds")?
.ok_or_else(|| "dispatch_thunk_idx points on an empty table entry")?
};
@@ -248,8 +246,7 @@ impl<'a> Sandbox for FunctionExecutor<'a> {
.map(|i| i.register(&mut self.sandbox_store))
{
Ok(instance_idx) => instance_idx,
Err(sandbox::InstantiationError::StartTrapped) =>
sandbox_primitives::ERR_EXECUTION,
Err(sandbox::InstantiationError::StartTrapped) => sandbox_primitives::ERR_EXECUTION,
Err(_) => sandbox_primitives::ERR_MODULE,
};
@@ -288,7 +285,7 @@ struct Resolver<'a> {
impl<'a> Resolver<'a> {
fn new(
host_functions: &'a[&'static dyn Function],
host_functions: &'a [&'static dyn Function],
allow_missing_func_imports: bool,
heap_pages: usize,
) -> Resolver<'a> {
@@ -303,25 +300,23 @@ impl<'a> Resolver<'a> {
}
impl<'a> wasmi::ModuleImportResolver for Resolver<'a> {
fn resolve_func(&self, name: &str, signature: &wasmi::Signature)
-> std::result::Result<wasmi::FuncRef, wasmi::Error>
{
fn resolve_func(
&self,
name: &str,
signature: &wasmi::Signature,
) -> std::result::Result<wasmi::FuncRef, wasmi::Error> {
let signature = sp_wasm_interface::Signature::from(signature);
for (function_index, function) in self.host_functions.iter().enumerate() {
if name == function.name() {
if signature == function.signature() {
return Ok(
wasmi::FuncInstance::alloc_host(signature.into(), function_index),
)
return Ok(wasmi::FuncInstance::alloc_host(signature.into(), function_index))
} else {
return Err(wasmi::Error::Instantiation(
format!(
"Invalid signature for function `{}` expected `{:?}`, got `{:?}`",
function.name(),
signature,
function.signature(),
),
))
return Err(wasmi::Error::Instantiation(format!(
"Invalid signature for function `{}` expected `{:?}`, got `{:?}`",
function.name(),
signature,
function.signature(),
)))
}
}
}
@@ -333,9 +328,7 @@ impl<'a> wasmi::ModuleImportResolver for Resolver<'a> {
Ok(wasmi::FuncInstance::alloc_host(signature.into(), id))
} else {
Err(wasmi::Error::Instantiation(
format!("Export {} not found", name),
))
Err(wasmi::Error::Instantiation(format!("Export {} not found", name)))
}
}
@@ -346,15 +339,14 @@ impl<'a> wasmi::ModuleImportResolver for Resolver<'a> {
) -> Result<MemoryRef, wasmi::Error> {
if field_name == "memory" {
match &mut *self.import_memory.borrow_mut() {
Some(_) => Err(wasmi::Error::Instantiation(
"Memory can not be imported twice!".into(),
)),
Some(_) =>
Err(wasmi::Error::Instantiation("Memory can not be imported twice!".into())),
memory_ref @ None => {
if memory_type
.maximum()
.map(|m| m.saturating_sub(memory_type.initial()))
.map(|m| self.heap_pages > m as usize)
.unwrap_or(false)
.maximum()
.map(|m| m.saturating_sub(memory_type.initial()))
.map(|m| self.heap_pages > m as usize)
.unwrap_or(false)
{
Err(wasmi::Error::Instantiation(format!(
"Heap pages ({}) is greater than imported memory maximum ({}).",
@@ -372,35 +364,40 @@ impl<'a> wasmi::ModuleImportResolver for Resolver<'a> {
*memory_ref = Some(memory.clone());
Ok(memory)
}
}
},
}
} else {
Err(wasmi::Error::Instantiation(
format!("Unknown memory reference with name: {}", field_name),
))
Err(wasmi::Error::Instantiation(format!(
"Unknown memory reference with name: {}",
field_name
)))
}
}
}
impl<'a> wasmi::Externals for FunctionExecutor<'a> {
fn invoke_index(&mut self, index: usize, args: wasmi::RuntimeArgs)
-> Result<Option<wasmi::RuntimeValue>, wasmi::Trap>
{
fn invoke_index(
&mut self,
index: usize,
args: wasmi::RuntimeArgs,
) -> Result<Option<wasmi::RuntimeValue>, wasmi::Trap> {
let mut args = args.as_ref().iter().copied().map(Into::into);
if let Some(function) = self.host_functions.get(index) {
function.execute(self, &mut args)
function
.execute(self, &mut args)
.map_err(|msg| Error::FunctionExecution(function.name().to_string(), msg))
.map_err(wasmi::Trap::from)
.map(|v| v.map(Into::into))
} else if self.allow_missing_func_imports
&& index >= self.host_functions.len()
&& index < self.host_functions.len() + self.missing_functions.len()
} else if self.allow_missing_func_imports &&
index >= self.host_functions.len() &&
index < self.host_functions.len() + self.missing_functions.len()
{
Err(Error::from(format!(
"Function `{}` is only a stub. Calling a stub is not allowed.",
self.missing_functions[index - self.host_functions.len()],
)).into())
))
.into())
} else {
Err(Error::from(format!("Could not find host function with index: {}", index)).into())
}
@@ -462,25 +459,26 @@ fn call_in_wasm_module(
function_executor.write_memory(offset, data)?;
let result = match method {
InvokeMethod::Export(method) => {
module_instance.invoke_export(
method,
&[I32(u32::from(offset) as i32), I32(data.len() as i32)],
&mut function_executor,
)
},
InvokeMethod::Export(method) => module_instance.invoke_export(
method,
&[I32(u32::from(offset) as i32), I32(data.len() as i32)],
&mut function_executor,
),
InvokeMethod::Table(func_ref) => {
let func = table.ok_or(Error::NoTable)?
let func = table
.ok_or(Error::NoTable)?
.get(func_ref)?
.ok_or(Error::NoTableEntryWithIndex(func_ref))?;
FuncInstance::invoke(
&func,
&[I32(u32::from(offset) as i32), I32(data.len() as i32)],
&mut function_executor,
).map_err(Into::into)
)
.map_err(Into::into)
},
InvokeMethod::TableWithWrapper { dispatcher_ref, func } => {
let dispatcher = table.ok_or(Error::NoTable)?
let dispatcher = table
.ok_or(Error::NoTable)?
.get(dispatcher_ref)?
.ok_or(Error::NoTableEntryWithIndex(dispatcher_ref))?;
@@ -488,7 +486,8 @@ fn call_in_wasm_module(
&dispatcher,
&[I32(func as _), I32(u32::from(offset) as i32), I32(data.len() as i32)],
&mut function_executor,
).map_err(Into::into)
)
.map_err(Into::into)
},
};
@@ -518,15 +517,12 @@ fn instantiate_module(
) -> Result<(ModuleRef, Vec<String>, MemoryRef), Error> {
let resolver = Resolver::new(host_functions, allow_missing_func_imports, heap_pages);
// start module instantiation. Don't run 'start' function yet.
let intermediate_instance = ModuleInstance::new(
module,
&ImportsBuilder::new().with_resolver("env", &resolver),
)?;
let intermediate_instance =
ModuleInstance::new(module, &ImportsBuilder::new().with_resolver("env", &resolver))?;
// Verify that the module has the heap base global variable.
let _ = get_heap_base(intermediate_instance.not_started_instance())?;
// Get the memory reference. Runtimes should import memory, but to be backwards
// compatible we also support exported memory.
let memory = match resolver.import_memory.into_inner() {
@@ -541,7 +537,7 @@ fn instantiate_module(
memory.grow(Pages(heap_pages)).map_err(|_| Error::Runtime)?;
memory
}
},
};
if intermediate_instance.has_start() {
@@ -592,9 +588,7 @@ impl GlobalValsSnapshot {
// the instance should be the same as used for preserving and
// we iterate the same way it as we do it for preserving values that means that the
// types should be the same and all the values are mutable. So no error is expected/
global_ref
.set(*global_val)
.map_err(|_| WasmError::ApplySnapshotFailed)?;
global_ref.set(*global_val).map_err(|_| WasmError::ApplySnapshotFailed)?;
}
Ok(())
}
@@ -624,7 +618,8 @@ impl WasmModule for WasmiRuntime {
&self.module,
&self.host_functions,
self.allow_missing_func_imports,
).map_err(|e| WasmError::Instantiation(e.to_string()))?;
)
.map_err(|e| WasmError::Instantiation(e.to_string()))?;
Ok(Box::new(WasmiInstance {
instance,
@@ -646,11 +641,11 @@ pub fn create_runtime(
host_functions: Vec<&'static dyn Function>,
allow_missing_func_imports: bool,
) -> Result<WasmiRuntime, WasmError> {
let data_segments_snapshot = DataSegmentsSnapshot::take(&blob)
.map_err(|e| WasmError::Other(e.to_string()))?;
let data_segments_snapshot =
DataSegmentsSnapshot::take(&blob).map_err(|e| WasmError::Other(e.to_string()))?;
let module = Module::from_parity_wasm_module(blob.into_inner())
.map_err(|_| WasmError::InvalidModule)?;
let module =
Module::from_parity_wasm_module(blob.into_inner()).map_err(|_| WasmError::InvalidModule)?;
let global_vals_snapshot = {
let (instance, _, _) = instantiate_module(
@@ -734,7 +729,7 @@ impl WasmInstance for WasmiInstance {
.as_global()
.ok_or_else(|| format!("`{}` is not a global", name))?
.get()
.into()
.into(),
)),
None => Ok(None),
}
+19 -31
View File
@@ -19,16 +19,17 @@
//! This module defines `HostState` and `HostContext` structs which provide logic and state
//! required for execution of host.
use crate::instance_wrapper::InstanceWrapper;
use crate::util;
use std::{cell::RefCell, rc::Rc};
use crate::{instance_wrapper::InstanceWrapper, util};
use codec::{Decode, Encode};
use log::trace;
use codec::{Encode, Decode};
use sc_allocator::FreeingBumpHeapAllocator;
use sc_executor_common::error::Result;
use sc_executor_common::sandbox::{self, SandboxCapabilities, SupervisorFuncIndex};
use sc_executor_common::{
error::Result,
sandbox::{self, SandboxCapabilities, SupervisorFuncIndex},
};
use sp_core::sandbox as sandbox_primitives;
use sp_wasm_interface::{FunctionContext, MemoryId, Pointer, Sandbox, WordSize};
use std::{cell::RefCell, rc::Rc};
use wasmtime::{Func, Val};
/// Wrapper type for pointer to a Wasm table entry.
@@ -108,7 +109,7 @@ impl<'a> SandboxCapabilities for HostContext<'a> {
"Supervisor function returned {} results, expected 1",
ret_vals.len()
)
.into());
.into())
} else {
&ret_vals[0]
};
@@ -116,9 +117,9 @@ impl<'a> SandboxCapabilities for HostContext<'a> {
if let Some(ret_val) = ret_val.i64() {
Ok(ret_val)
} else {
return Err("Supervisor function returned unexpected result!".into());
return Err("Supervisor function returned unexpected result!".into())
}
}
},
Err(err) => Err(err.to_string().into()),
}
}
@@ -130,15 +131,11 @@ impl<'a> sp_wasm_interface::FunctionContext for HostContext<'a> {
address: Pointer<u8>,
dest: &mut [u8],
) -> sp_wasm_interface::Result<()> {
self.instance
.read_memory_into(address, dest)
.map_err(|e| e.to_string())
self.instance.read_memory_into(address, dest).map_err(|e| e.to_string())
}
fn write_memory(&mut self, address: Pointer<u8>, data: &[u8]) -> sp_wasm_interface::Result<()> {
self.instance
.write_memory_from(address, data)
.map_err(|e| e.to_string())
self.instance.write_memory_from(address, data).map_err(|e| e.to_string())
}
fn allocate_memory(&mut self, size: WordSize) -> sp_wasm_interface::Result<Pointer<u8>> {
@@ -166,11 +163,8 @@ impl<'a> Sandbox for HostContext<'a> {
buf_ptr: Pointer<u8>,
buf_len: WordSize,
) -> sp_wasm_interface::Result<u32> {
let sandboxed_memory = self
.sandbox_store
.borrow()
.memory(memory_id)
.map_err(|e| e.to_string())?;
let sandboxed_memory =
self.sandbox_store.borrow().memory(memory_id).map_err(|e| e.to_string())?;
sandboxed_memory.with_direct_access(|sandboxed_memory| {
let len = buf_len as usize;
let src_range = match util::checked_range(offset as usize, len, sandboxed_memory.len())
@@ -200,11 +194,8 @@ impl<'a> Sandbox for HostContext<'a> {
val_ptr: Pointer<u8>,
val_len: WordSize,
) -> sp_wasm_interface::Result<u32> {
let sandboxed_memory = self
.sandbox_store
.borrow()
.memory(memory_id)
.map_err(|e| e.to_string())?;
let sandboxed_memory =
self.sandbox_store.borrow().memory(memory_id).map_err(|e| e.to_string())?;
sandboxed_memory.with_direct_access_mut(|sandboxed_memory| {
let len = val_len as usize;
let supervisor_mem_size = self.instance.memory_size() as usize;
@@ -259,11 +250,8 @@ impl<'a> Sandbox for HostContext<'a> {
.map(Into::into)
.collect::<Vec<_>>();
let instance = self
.sandbox_store
.borrow()
.instance(instance_id)
.map_err(|e| e.to_string())?;
let instance =
self.sandbox_store.borrow().instance(instance_id).map_err(|e| e.to_string())?;
let result = instance.invoke(export_name, &args, self, state);
match result {
@@ -278,7 +266,7 @@ impl<'a> Sandbox for HostContext<'a> {
.map_err(|_| "can't write return value")?;
Ok(sandbox_primitives::ERR_OK)
})
}
},
Err(_) => Ok(sandbox_primitives::ERR_EXECUTION),
}
}
@@ -21,8 +21,8 @@ use sc_executor_common::error::WasmError;
use sp_wasm_interface::{Function, ValueType};
use std::any::Any;
use wasmtime::{
Extern, ExternType, Func, FuncType, ImportType, Limits, Memory, MemoryType, Module,
Trap, Val, Store,
Extern, ExternType, Func, FuncType, ImportType, Limits, Memory, MemoryType, Module, Store,
Trap, Val,
};
pub struct Imports {
@@ -51,36 +51,29 @@ pub fn resolve_imports(
"host doesn't provide any imports from non-env module: {}:{}",
import_ty.module(),
name,
)));
)))
}
let resolved = match name {
"memory" => {
memory_import_index = Some(externs.len());
resolve_memory_import(store, &import_ty, heap_pages)?
}
_ => resolve_func_import(
store,
&import_ty,
host_functions,
allow_missing_func_imports,
)?,
},
_ =>
resolve_func_import(store, &import_ty, host_functions, allow_missing_func_imports)?,
};
externs.push(resolved);
}
Ok(Imports {
memory_import_index,
externs,
})
Ok(Imports { memory_import_index, externs })
}
/// When the module linking proposal is supported the import's name can be `None`.
/// Because we are not using this proposal we could safely unwrap the name.
/// However, we opt for an error in order to avoid panics at all costs.
fn import_name<'a, 'b: 'a>(import: &'a ImportType<'b>) -> Result<&'a str, WasmError> {
let name = import.name().ok_or_else(||
let name = import.name().ok_or_else(|| {
WasmError::Other("The module linking proposal is not supported.".to_owned())
)?;
})?;
Ok(name)
}
@@ -91,21 +84,17 @@ fn resolve_memory_import(
) -> Result<Extern, WasmError> {
let requested_memory_ty = match import_ty.ty() {
ExternType::Memory(memory_ty) => memory_ty,
_ => {
_ =>
return Err(WasmError::Other(format!(
"this import must be of memory type: {}:{}",
import_ty.module(),
import_name(&import_ty)?,
)))
}
))),
};
// Increment the min (a.k.a initial) number of pages by `heap_pages` and check if it exceeds the
// maximum specified by the import.
let initial = requested_memory_ty
.limits()
.min()
.saturating_add(heap_pages);
let initial = requested_memory_ty.limits().min().saturating_add(heap_pages);
if let Some(max) = requested_memory_ty.limits().max() {
if initial > max {
return Err(WasmError::Other(format!(
@@ -113,7 +102,7 @@ fn resolve_memory_import(
by the runtime wasm module {}",
initial,
max,
)));
)))
}
}
@@ -137,37 +126,31 @@ fn resolve_func_import(
let func_ty = match import_ty.ty() {
ExternType::Func(func_ty) => func_ty,
_ => {
_ =>
return Err(WasmError::Other(format!(
"host doesn't provide any non function imports besides 'memory': {}:{}",
import_ty.module(),
name,
)));
}
))),
};
let host_func = match host_functions
.iter()
.find(|host_func| host_func.name() == name)
{
let host_func = match host_functions.iter().find(|host_func| host_func.name() == name) {
Some(host_func) => host_func,
None if allow_missing_func_imports => {
return Ok(MissingHostFuncHandler::new(import_ty)?.into_extern(store, &func_ty));
}
None => {
None if allow_missing_func_imports =>
return Ok(MissingHostFuncHandler::new(import_ty)?.into_extern(store, &func_ty)),
None =>
return Err(WasmError::Other(format!(
"host doesn't provide such function: {}:{}",
import_ty.module(),
name,
)));
}
))),
};
if &func_ty != &wasmtime_func_sig(*host_func) {
return Err(WasmError::Other(format!(
"signature mismatch for: {}:{}",
import_ty.module(),
name,
)));
)))
}
Ok(HostFuncHandler::new(*host_func).into_extern(store))
@@ -218,7 +201,7 @@ fn call_static(
);
wasmtime_results[0] = util::into_wasmtime_val(ret_val);
Ok(())
}
},
Ok(None) => {
debug_assert!(
wasmtime_results.len() == 0,
@@ -226,26 +209,22 @@ fn call_static(
correspond to the number of results returned by the host function",
);
Ok(())
}
},
Err(msg) => Err(Trap::new(msg)),
}
}
impl HostFuncHandler {
fn new(host_func: &'static dyn Function) -> Self {
Self {
host_func,
}
Self { host_func }
}
fn into_extern(self, store: &Store) -> Extern {
let host_func = self.host_func;
let func_ty = wasmtime_func_sig(self.host_func);
let func = Func::new(store, func_ty,
move |_, params, result| {
call_static(host_func, params, result)
}
);
let func = Func::new(store, func_ty, move |_, params, result| {
call_static(host_func, params, result)
});
Extern::Func(func)
}
}
@@ -266,28 +245,17 @@ impl MissingHostFuncHandler {
fn into_extern(self, store: &Store, func_ty: &FuncType) -> Extern {
let Self { module, name } = self;
let func = Func::new(store, func_ty.clone(),
move |_, _, _| Err(Trap::new(format!(
"call to a missing function {}:{}",
module, name
)))
);
let func = Func::new(store, func_ty.clone(), move |_, _, _| {
Err(Trap::new(format!("call to a missing function {}:{}", module, name)))
});
Extern::Func(func)
}
}
fn wasmtime_func_sig(func: &dyn Function) -> wasmtime::FuncType {
let signature = func.signature();
let params = signature
.args
.iter()
.cloned()
.map(into_wasmtime_val_type);
let results = signature
.return_value
.iter()
.cloned()
.map(into_wasmtime_val_type);
let params = signature.args.iter().cloned().map(into_wasmtime_val_type);
let results = signature.return_value.iter().cloned().map(into_wasmtime_val_type);
wasmtime::FuncType::new(params, results)
}
@@ -19,26 +19,23 @@
//! Defines data and logic needed for interaction with an WebAssembly instance of a substrate
//! runtime module.
use crate::util;
use crate::imports::Imports;
use crate::{imports::Imports, util};
use std::{slice, marker};
use sc_executor_common::{
error::{Error, Result},
runtime_blob,
wasm_runtime::InvokeMethod,
};
use sp_wasm_interface::{Pointer, WordSize, Value};
use wasmtime::{Instance, Module, Memory, Table, Val, Func, Extern, Global, Store};
use sp_wasm_interface::{Pointer, Value, WordSize};
use std::{marker, slice};
use wasmtime::{Extern, Func, Global, Instance, Memory, Module, Store, Table, Val};
/// Invoked entrypoint format.
pub enum EntryPointType {
/// Direct call.
///
/// Call is made by providing only payload reference and length.
Direct {
entrypoint: wasmtime::TypedFunc<(u32, u32), u64>,
},
Direct { entrypoint: wasmtime::TypedFunc<(u32, u32), u64> },
/// Indirect call.
///
/// Call is made by providing payload reference and length, and extra argument
@@ -66,17 +63,10 @@ impl EntryPoint {
}
match self.call_type {
EntryPointType::Direct { ref entrypoint } => {
entrypoint.call((data_ptr, data_len)).map_err(handle_trap)
}
EntryPointType::Wrapped {
func,
ref dispatcher,
} => {
dispatcher
.call((func, data_ptr, data_len))
.map_err(handle_trap)
}
EntryPointType::Direct { ref entrypoint } =>
entrypoint.call((data_ptr, data_len)).map_err(handle_trap),
EntryPointType::Wrapped { func, ref dispatcher } =>
dispatcher.call((func, data_ptr, data_len)).map_err(handle_trap),
}
}
@@ -85,9 +75,7 @@ impl EntryPoint {
.typed::<(u32, u32), u64>()
.map_err(|_| "Invalid signature for direct entry point")?
.clone();
Ok(Self {
call_type: EntryPointType::Direct { entrypoint },
})
Ok(Self { call_type: EntryPointType::Direct { entrypoint } })
}
pub fn wrapped(
@@ -98,9 +86,7 @@ impl EntryPoint {
.typed::<(u32, u32, u32), u64>()
.map_err(|_| "Invalid signature for wrapped entry point")?
.clone();
Ok(Self {
call_type: EntryPointType::Wrapped { func, dispatcher },
})
Ok(Self { call_type: EntryPointType::Wrapped { func, dispatcher } })
}
}
@@ -127,7 +113,6 @@ fn extern_memory(extern_: &Extern) -> Option<&Memory> {
}
}
fn extern_global(extern_: &Extern) -> Option<&Global> {
match extern_ {
Extern::Global(glob) => Some(glob),
@@ -156,15 +141,13 @@ impl InstanceWrapper {
.map_err(|e| Error::from(format!("cannot instantiate: {}", e)))?;
let memory = match imports.memory_import_index {
Some(memory_idx) => {
extern_memory(&imports.externs[memory_idx])
.expect("only memory can be at the `memory_idx`; qed")
.clone()
}
Some(memory_idx) => extern_memory(&imports.externs[memory_idx])
.expect("only memory can be at the `memory_idx`; qed")
.clone(),
None => {
let memory = get_linear_memory(&instance)?;
if !memory.grow(heap_pages).is_ok() {
return Err("failed top increase the linear memory size".into());
return Err("failed top increase the linear memory size".into())
}
memory
},
@@ -186,42 +169,38 @@ impl InstanceWrapper {
Ok(match method {
InvokeMethod::Export(method) => {
// Resolve the requested method and verify that it has a proper signature.
let export = self
.instance
.get_export(method)
.ok_or_else(|| Error::from(format!("Exported method {} is not found", method)))?;
let export = self.instance.get_export(method).ok_or_else(|| {
Error::from(format!("Exported method {} is not found", method))
})?;
let func = extern_func(&export)
.ok_or_else(|| Error::from(format!("Export {} is not a function", method)))?
.clone();
EntryPoint::direct(func)
.map_err(|_|
Error::from(format!(
"Exported function '{}' has invalid signature.",
method,
))
)?
EntryPoint::direct(func).map_err(|_| {
Error::from(format!("Exported function '{}' has invalid signature.", method,))
})?
},
InvokeMethod::Table(func_ref) => {
let table = self.instance.get_table("__indirect_function_table").ok_or(Error::NoTable)?;
let val = table.get(func_ref)
.ok_or(Error::NoTableEntryWithIndex(func_ref))?;
let table =
self.instance.get_table("__indirect_function_table").ok_or(Error::NoTable)?;
let val = table.get(func_ref).ok_or(Error::NoTableEntryWithIndex(func_ref))?;
let func = val
.funcref()
.ok_or(Error::TableElementIsNotAFunction(func_ref))?
.ok_or(Error::FunctionRefIsNull(func_ref))?
.clone();
EntryPoint::direct(func)
.map_err(|_|
Error::from(format!(
"Function @{} in exported table has invalid signature for direct call.",
func_ref,
))
)?
},
EntryPoint::direct(func).map_err(|_| {
Error::from(format!(
"Function @{} in exported table has invalid signature for direct call.",
func_ref,
))
})?
},
InvokeMethod::TableWithWrapper { dispatcher_ref, func } => {
let table = self.instance.get_table("__indirect_function_table").ok_or(Error::NoTable)?;
let val = table.get(dispatcher_ref)
let table =
self.instance.get_table("__indirect_function_table").ok_or(Error::NoTable)?;
let val = table
.get(dispatcher_ref)
.ok_or(Error::NoTableEntryWithIndex(dispatcher_ref))?;
let dispatcher = val
.funcref()
@@ -229,13 +208,12 @@ impl InstanceWrapper {
.ok_or(Error::FunctionRefIsNull(dispatcher_ref))?
.clone();
EntryPoint::wrapped(dispatcher, func)
.map_err(|_|
Error::from(format!(
"Function @{} in exported table has invalid signature for wrapped call.",
dispatcher_ref,
))
)?
EntryPoint::wrapped(dispatcher, func).map_err(|_| {
Error::from(format!(
"Function @{} in exported table has invalid signature for wrapped call.",
dispatcher_ref,
))
})?
},
})
}
@@ -426,7 +404,7 @@ impl InstanceWrapper {
/// relied upon. Thus this function acts as a hint.
pub fn decommit(&self) {
if self.memory.data_size() == 0 {
return;
return
}
cfg_if::cfg_if! {
@@ -16,7 +16,7 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
///! Defines a `WasmRuntime` that uses the Wasmtime JIT to execute.
/// ! Defines a `WasmRuntime` that uses the Wasmtime JIT to execute.
mod host;
mod imports;
mod instance_wrapper;
@@ -28,6 +28,6 @@ mod util;
mod tests;
pub use runtime::{
create_runtime, create_runtime_from_artifact, prepare_runtime_artifact, Config, Semantics,
DeterministicStackLimit,
create_runtime, create_runtime_from_artifact, prepare_runtime_artifact, Config,
DeterministicStackLimit, Semantics,
};
@@ -18,22 +18,26 @@
//! Defines the compiled Wasm runtime that uses Wasmtime internally.
use crate::host::HostState;
use crate::imports::{Imports, resolve_imports};
use crate::instance_wrapper::{InstanceWrapper, EntryPoint};
use crate::state_holder;
use crate::{
host::HostState,
imports::{resolve_imports, Imports},
instance_wrapper::{EntryPoint, InstanceWrapper},
state_holder,
};
use std::{path::PathBuf, rc::Rc};
use std::sync::Arc;
use std::path::Path;
use sc_allocator::FreeingBumpHeapAllocator;
use sc_executor_common::{
error::{Result, WasmError},
runtime_blob::{DataSegmentsSnapshot, ExposedMutableGlobalsSet, GlobalsSnapshot, RuntimeBlob},
wasm_runtime::{WasmModule, WasmInstance, InvokeMethod},
wasm_runtime::{InvokeMethod, WasmInstance, WasmModule},
};
use sc_allocator::FreeingBumpHeapAllocator;
use sp_runtime_interface::unpack_ptr_and_len;
use sp_wasm_interface::{Function, Pointer, WordSize, Value};
use sp_wasm_interface::{Function, Pointer, Value, WordSize};
use std::{
path::{Path, PathBuf},
rc::Rc,
sync::Arc,
};
use wasmtime::{Engine, Store};
enum Strategy {
@@ -102,7 +106,8 @@ impl WasmModule for WasmtimeRuntime {
// the mutable globals were collected. Here, it is easy to see that there is only a single
// runtime blob and thus it's the same that was used for both creating the instance and
// collecting the mutable globals.
let globals_snapshot = GlobalsSnapshot::take(&snapshot_data.mutable_globals, &instance_wrapper);
let globals_snapshot =
GlobalsSnapshot::take(&snapshot_data.mutable_globals, &instance_wrapper);
Strategy::FastInstanceReuse {
instance_wrapper: Rc::new(instance_wrapper),
@@ -150,14 +155,15 @@ impl WasmInstance for WasmtimeInstance {
globals_snapshot.apply(&**instance_wrapper);
let allocator = FreeingBumpHeapAllocator::new(*heap_base);
let result = perform_call(data, Rc::clone(&instance_wrapper), entrypoint, allocator);
let result =
perform_call(data, Rc::clone(&instance_wrapper), entrypoint, allocator);
// Signal to the OS that we are done with the linear memory and that it can be
// reclaimed.
instance_wrapper.decommit();
result
}
},
Strategy::RecreateInstance(instance_creator) => {
let instance_wrapper = instance_creator.instantiate()?;
let heap_base = instance_wrapper.extract_heap_base()?;
@@ -165,18 +171,16 @@ impl WasmInstance for WasmtimeInstance {
let allocator = FreeingBumpHeapAllocator::new(heap_base);
perform_call(data, Rc::new(instance_wrapper), entrypoint, allocator)
}
},
}
}
fn get_global_const(&self, name: &str) -> Result<Option<Value>> {
match &self.strategy {
Strategy::FastInstanceReuse {
instance_wrapper, ..
} => instance_wrapper.get_global_val(name),
Strategy::RecreateInstance(instance_creator) => {
instance_creator.instantiate()?.get_global_val(name)
}
Strategy::FastInstanceReuse { instance_wrapper, .. } =>
instance_wrapper.get_global_val(name),
Strategy::RecreateInstance(instance_creator) =>
instance_creator.instantiate()?.get_global_val(name),
}
}
@@ -186,10 +190,9 @@ impl WasmInstance for WasmtimeInstance {
// We do not keep the wasm instance around, therefore there is no linear memory
// associated with it.
None
}
Strategy::FastInstanceReuse {
instance_wrapper, ..
} => Some(instance_wrapper.base_ptr()),
},
Strategy::FastInstanceReuse { instance_wrapper, .. } =>
Some(instance_wrapper.base_ptr()),
}
}
}
@@ -237,9 +240,8 @@ fn common_config(semantics: &Semantics) -> std::result::Result<wasmtime::Config,
config.cranelift_opt_level(wasmtime::OptLevel::SpeedAndSize);
config.cranelift_nan_canonicalization(semantics.canonicalize_nans);
if let Some(DeterministicStackLimit {
native_stack_max, ..
}) = semantics.deterministic_stack_limit
if let Some(DeterministicStackLimit { native_stack_max, .. }) =
semantics.deterministic_stack_limit
{
config
.max_wasm_stack(native_stack_max as usize)
@@ -411,11 +413,7 @@ pub unsafe fn create_runtime_from_artifact(
config: Config,
host_functions: Vec<&'static dyn Function>,
) -> std::result::Result<WasmtimeRuntime, WasmError> {
do_create_runtime(
CodeSupplyMode::Artifact { compiled_artifact },
config,
host_functions,
)
do_create_runtime(CodeSupplyMode::Artifact { compiled_artifact }, config, host_functions)
}
/// # Safety
@@ -456,16 +454,13 @@ unsafe fn do_create_runtime(
let module = wasmtime::Module::new(&engine, &blob.serialize())
.map_err(|e| WasmError::Other(format!("cannot create module: {}", e)))?;
(module, Some(InstanceSnapshotData {
data_segments_snapshot,
mutable_globals,
}))
(module, Some(InstanceSnapshotData { data_segments_snapshot, mutable_globals }))
} else {
let module = wasmtime::Module::new(&engine, &blob.serialize())
.map_err(|e| WasmError::Other(format!("cannot create module: {}", e)))?;
(module, None)
}
}
},
CodeSupplyMode::Artifact { compiled_artifact } => {
// SAFETY: The unsafity of `deserialize` is covered by this function. The
// responsibilities to maintain the invariants are passed to the caller.
@@ -473,16 +468,10 @@ unsafe fn do_create_runtime(
.map_err(|e| WasmError::Other(format!("cannot deserialize module: {}", e)))?;
(module, None)
}
},
};
Ok(WasmtimeRuntime {
module: Arc::new(module),
snapshot_data,
config,
host_functions,
engine,
})
Ok(WasmtimeRuntime { module: Arc::new(module), snapshot_data, config, host_functions, engine })
}
fn instrument(
+14 -25
View File
@@ -16,12 +16,9 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use sc_executor_common::{
runtime_blob::RuntimeBlob,
wasm_runtime::WasmModule,
};
use codec::{Decode as _, Encode as _};
use sc_executor_common::{runtime_blob::RuntimeBlob, wasm_runtime::WasmModule};
use sc_runtime_test::wasm_binary_unwrap;
use codec::{Encode as _, Decode as _};
use std::sync::Arc;
type HostFunctions = sp_io::SubstrateHostFunctions;
@@ -68,7 +65,7 @@ impl RuntimeBuilder {
Some(wat) => {
wasm = wat::parse_str(wat).unwrap();
&wasm
}
},
};
RuntimeBlob::uncompress_if_needed(&wasm)
@@ -83,21 +80,20 @@ impl RuntimeBuilder {
cache_path: None,
semantics: crate::Semantics {
fast_instance_reuse: self.fast_instance_reuse,
deterministic_stack_limit:
match self.deterministic_stack {
true => Some(crate::DeterministicStackLimit {
logical_max: 65536,
native_stack_max: 256 * 1024 * 1024,
}),
false => None,
},
deterministic_stack_limit: match self.deterministic_stack {
true => Some(crate::DeterministicStackLimit {
logical_max: 65536,
native_stack_max: 256 * 1024 * 1024,
}),
false => None,
},
canonicalize_nans: self.canonicalize_nans,
},
},
{
use sp_wasm_interface::HostFunctions as _;
HostFunctions::host_functions()
}
},
)
.expect("cannot create runtime");
@@ -113,9 +109,7 @@ fn test_nan_canonicalization() {
builder.build()
};
let instance = runtime
.new_instance()
.expect("failed to instantiate a runtime");
let instance = runtime.new_instance().expect("failed to instantiate a runtime");
/// A NaN with canonical payload bits.
const CANONICAL_NAN_BITS: u32 = 0x7fc00000;
@@ -142,10 +136,7 @@ fn test_nan_canonicalization() {
let params = (u32::to_le_bytes(ARBITRARY_NAN_BITS), u32::to_le_bytes(1)).encode();
let res = {
let raw_result = instance.call_export(
"test_fp_f32add",
&params,
).unwrap();
let raw_result = instance.call_export("test_fp_f32add", &params).unwrap();
u32::from_le_bytes(<[u8; 4]>::decode(&mut &raw_result[..]).unwrap())
};
assert_eq!(res, CANONICAL_NAN_BITS);
@@ -161,9 +152,7 @@ fn test_stack_depth_reaching() {
builder.deterministic_stack(true);
builder.build()
};
let instance = runtime
.new_instance()
.expect("failed to instantiate a runtime");
let instance = runtime.new_instance().expect("failed to instantiate a runtime");
let err = instance.call_export("test-many-locals", &[]).unwrap_err();