mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 07:41:08 +00:00
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:
@@ -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.
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,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(
|
||||
|
||||
@@ -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",
|
||||
¶ms,
|
||||
).unwrap();
|
||||
let raw_result = instance.call_export("test_fp_f32add", ¶ms).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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user