mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-11 20:01:08 +00:00
First step for generating host externals out of the function definition in sr-io (#3567)
* Adds new wrapper traits for wasm executor * Add new crate `substrate-wasm-interface` Thew new crate holds types and traits for the communicating between the wasm runtime and the host. * Rewrite externals with new macro etc * Fix vec initialization * Make executor tests working * Remove unused code + warnings * Introduce `Pointer` and `WordSize` for working with wasm * Fix tests and compilation * Fix compilation * Apply suggestions from code review Co-Authored-By: Sergei Pepyakin <sergei@parity.io> * Review feedback * Remove unused conversions * Make each host function its own struct `HostFunctions` now just returns these function structs. Each function can be executed by using one of the function structs. The inherent host functions are now moved to the "normal" host functions. * Remove byteorder * Add floating point types * Make pointer interface more safe * Add type alias for wasm-interface Result * More review comments
This commit is contained in:
Generated
+8
-1
@@ -4868,7 +4868,6 @@ name = "substrate-executor"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hex-literal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -4887,6 +4886,7 @@ dependencies = [
|
||||
"substrate-serializer 2.0.0",
|
||||
"substrate-state-machine 2.0.0",
|
||||
"substrate-trie 2.0.0",
|
||||
"substrate-wasm-interface 2.0.0",
|
||||
"tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasmi 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -5464,6 +5464,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
name = "substrate-wasm-builder-runner"
|
||||
version = "1.0.3"
|
||||
|
||||
[[package]]
|
||||
name = "substrate-wasm-interface"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"wasmi 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "1.0.0"
|
||||
|
||||
@@ -19,6 +19,7 @@ vergen = "3"
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"core/authority-discovery",
|
||||
"core/application-crypto",
|
||||
"core/cli",
|
||||
"core/client",
|
||||
@@ -64,7 +65,7 @@ members = [
|
||||
"core/utils/fork-tree",
|
||||
"core/utils/wasm-builder",
|
||||
"core/utils/wasm-builder-runner",
|
||||
"core/authority-discovery",
|
||||
"core/wasm-interface",
|
||||
"srml/support",
|
||||
"srml/support/procedural",
|
||||
"srml/support/procedural/tools",
|
||||
|
||||
@@ -14,9 +14,9 @@ serializer = { package = "substrate-serializer", path = "../serializer" }
|
||||
state_machine = { package = "substrate-state-machine", path = "../state-machine" }
|
||||
runtime_version = { package = "sr-version", path = "../sr-version" }
|
||||
panic-handler = { package = "substrate-panic-handler", path = "../panic-handler" }
|
||||
wasm-interface = { package = "substrate-wasm-interface", path = "../wasm-interface" }
|
||||
wasmi = "0.5.0"
|
||||
parity-wasm = "0.31"
|
||||
byteorder = "1.3"
|
||||
lazy_static = "1.3"
|
||||
parking_lot = "0.9.0"
|
||||
log = "0.4"
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use log::trace;
|
||||
use wasmi::MemoryRef;
|
||||
use wasmi::memory_units::Bytes;
|
||||
use wasmi::{MemoryRef, memory_units::Bytes};
|
||||
use wasm_interface::{Pointer, WordSize};
|
||||
|
||||
// The pointers need to be aligned to 8 bytes.
|
||||
const ALIGNMENT: u32 = 8;
|
||||
@@ -79,7 +79,7 @@ impl FreeingBumpHeapAllocator {
|
||||
|
||||
/// Gets requested number of bytes to allocate and returns a pointer.
|
||||
/// The maximum size which can be allocated at once is 16 MiB.
|
||||
pub fn allocate(&mut self, size: u32) -> Result<u32> {
|
||||
pub fn allocate(&mut self, size: WordSize) -> Result<Pointer<u8>> {
|
||||
if size > MAX_POSSIBLE_ALLOCATION {
|
||||
return Err(Error::RequestedAllocationTooLarge);
|
||||
}
|
||||
@@ -95,7 +95,7 @@ impl FreeingBumpHeapAllocator {
|
||||
// Something from the free list
|
||||
let item = self.heads[list_index];
|
||||
let four_bytes = self.get_heap_4bytes(item)?;
|
||||
self.heads[list_index] = FreeingBumpHeapAllocator::le_bytes_to_u32(four_bytes);
|
||||
self.heads[list_index] = Self::le_bytes_to_u32(four_bytes);
|
||||
item + 8
|
||||
} else {
|
||||
// Nothing to be freed. Bump.
|
||||
@@ -109,12 +109,12 @@ impl FreeingBumpHeapAllocator {
|
||||
self.total_size = self.total_size + item_size + 8;
|
||||
trace!(target: "wasm-heap", "Heap size is {} bytes after allocation", self.total_size);
|
||||
|
||||
Ok(self.ptr_offset + ptr)
|
||||
Ok(Pointer::new(self.ptr_offset + ptr))
|
||||
}
|
||||
|
||||
/// Deallocates the space which was allocated for a pointer.
|
||||
pub fn deallocate(&mut self, ptr: u32) -> Result<()> {
|
||||
let ptr = ptr - self.ptr_offset;
|
||||
pub fn deallocate(&mut self, ptr: Pointer<u8>) -> Result<()> {
|
||||
let ptr = u32::from(ptr) - self.ptr_offset;
|
||||
if ptr < 8 {
|
||||
return Err(error("Invalid pointer for deallocation"));
|
||||
}
|
||||
@@ -125,10 +125,10 @@ impl FreeingBumpHeapAllocator {
|
||||
self.heads[list_index] = ptr - 8;
|
||||
|
||||
let mut slice = self.get_heap_4bytes(ptr - 8)?;
|
||||
FreeingBumpHeapAllocator::write_u32_into_le_bytes(tail, &mut slice);
|
||||
Self::write_u32_into_le_bytes(tail, &mut slice);
|
||||
self.set_heap_4bytes(ptr - 8, slice)?;
|
||||
|
||||
let item_size = FreeingBumpHeapAllocator::get_item_size_from_index(list_index);
|
||||
let item_size = Self::get_item_size_from_index(list_index);
|
||||
self.total_size = self.total_size.checked_sub(item_size as u32 + 8)
|
||||
.ok_or_else(|| error("Unable to subtract from total heap size without overflow"))?;
|
||||
trace!(target: "wasm-heap", "Heap size is {} bytes after deallocation", self.total_size);
|
||||
@@ -147,8 +147,7 @@ impl FreeingBumpHeapAllocator {
|
||||
}
|
||||
|
||||
fn write_u32_into_le_bytes(bytes: u32, slice: &mut [u8]) {
|
||||
let bytes: [u8; 4] = unsafe { std::mem::transmute::<u32, [u8; 4]>(bytes.to_le()) };
|
||||
for i in 0..4 { slice[i] = bytes[i]; }
|
||||
slice[..4].copy_from_slice(&bytes.to_le_bytes());
|
||||
}
|
||||
|
||||
fn get_item_size_from_index(index: usize) -> usize {
|
||||
@@ -175,7 +174,6 @@ impl FreeingBumpHeapAllocator {
|
||||
fn set_heap_4bytes(&mut self, ptr: u32, value: [u8; 4]) -> Result<()> {
|
||||
self.heap.set(self.ptr_offset + ptr, &value).map_err(Into::into)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -186,6 +184,11 @@ mod tests {
|
||||
|
||||
const PAGE_SIZE: u32 = 65536;
|
||||
|
||||
/// Makes a pointer out of the given address.
|
||||
fn to_pointer(address: u32) -> Pointer<u8> {
|
||||
Pointer::new(address)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_allocate_properly() {
|
||||
// given
|
||||
@@ -196,7 +199,7 @@ mod tests {
|
||||
let ptr = heap.allocate(1).unwrap();
|
||||
|
||||
// then
|
||||
assert_eq!(ptr, 8);
|
||||
assert_eq!(ptr, to_pointer(8));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -211,7 +214,7 @@ mod tests {
|
||||
// then
|
||||
// the pointer must start at the next multiple of 8 from 13
|
||||
// + the prefix of 8 bytes.
|
||||
assert_eq!(ptr, 24);
|
||||
assert_eq!(ptr, to_pointer(24));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -227,14 +230,14 @@ mod tests {
|
||||
|
||||
// then
|
||||
// a prefix of 8 bytes is prepended to each pointer
|
||||
assert_eq!(ptr1, 8);
|
||||
assert_eq!(ptr1, to_pointer(8));
|
||||
|
||||
// the prefix of 8 bytes + the content of ptr1 padded to the lowest possible
|
||||
// item size of 8 bytes + the prefix of ptr1
|
||||
assert_eq!(ptr2, 24);
|
||||
assert_eq!(ptr2, to_pointer(24));
|
||||
|
||||
// ptr2 + its content of 16 bytes + the prefix of 8 bytes
|
||||
assert_eq!(ptr3, 24 + 16 + 8);
|
||||
assert_eq!(ptr3, to_pointer(24 + 16 + 8));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -244,11 +247,11 @@ mod tests {
|
||||
let mut heap = FreeingBumpHeapAllocator::new(mem, 0);
|
||||
let ptr1 = heap.allocate(1).unwrap();
|
||||
// the prefix of 8 bytes is prepended to the pointer
|
||||
assert_eq!(ptr1, 8);
|
||||
assert_eq!(ptr1, to_pointer(8));
|
||||
|
||||
let ptr2 = heap.allocate(1).unwrap();
|
||||
// the prefix of 8 bytes + the content of ptr 1 is prepended to the pointer
|
||||
assert_eq!(ptr2, 24);
|
||||
assert_eq!(ptr2, to_pointer(24));
|
||||
|
||||
// when
|
||||
heap.deallocate(ptr2).unwrap();
|
||||
@@ -256,7 +259,7 @@ mod tests {
|
||||
// then
|
||||
// then the heads table should contain a pointer to the
|
||||
// prefix of ptr2 in the leftmost entry
|
||||
assert_eq!(heap.heads[0], ptr2 - 8);
|
||||
assert_eq!(heap.heads[0], u32::from(ptr2) - 8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -268,13 +271,13 @@ mod tests {
|
||||
|
||||
let ptr1 = heap.allocate(1).unwrap();
|
||||
// the prefix of 8 bytes is prepended to the pointer
|
||||
assert_eq!(ptr1, padded_offset + 8);
|
||||
assert_eq!(ptr1, to_pointer(padded_offset + 8));
|
||||
|
||||
let ptr2 = heap.allocate(9).unwrap();
|
||||
// the padded_offset + the previously allocated ptr (8 bytes prefix +
|
||||
// 8 bytes content) + the prefix of 8 bytes which is prepended to the
|
||||
// current pointer
|
||||
assert_eq!(ptr2, padded_offset + 16 + 8);
|
||||
assert_eq!(ptr2, to_pointer(padded_offset + 16 + 8));
|
||||
|
||||
// when
|
||||
heap.deallocate(ptr2).unwrap();
|
||||
@@ -282,7 +285,7 @@ mod tests {
|
||||
|
||||
// then
|
||||
// should have re-allocated
|
||||
assert_eq!(ptr3, padded_offset + 16 + 8);
|
||||
assert_eq!(ptr3, to_pointer(padded_offset + 16 + 8));
|
||||
assert_eq!(heap.heads, [0; N]);
|
||||
}
|
||||
|
||||
@@ -302,15 +305,12 @@ mod tests {
|
||||
heap.deallocate(ptr3).unwrap();
|
||||
|
||||
// then
|
||||
let mut expected = [0; N];
|
||||
expected[0] = ptr3 - 8;
|
||||
assert_eq!(heap.heads, expected);
|
||||
assert_eq!(heap.heads[0], u32::from(ptr3) - 8);
|
||||
|
||||
let ptr4 = heap.allocate(8).unwrap();
|
||||
assert_eq!(ptr4, ptr3);
|
||||
|
||||
expected[0] = ptr2 - 8;
|
||||
assert_eq!(heap.heads, expected);
|
||||
assert_eq!(heap.heads[0], u32::from(ptr2) - 8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -338,7 +338,7 @@ mod tests {
|
||||
let mem = MemoryInstance::alloc(Pages(1), Some(Pages(1))).unwrap();
|
||||
let mut heap = FreeingBumpHeapAllocator::new(mem, 0);
|
||||
let ptr1 = heap.allocate((PAGE_SIZE / 2) - 8).unwrap();
|
||||
assert_eq!(ptr1, 8);
|
||||
assert_eq!(ptr1, to_pointer(8));
|
||||
|
||||
// when
|
||||
let ptr2 = heap.allocate(PAGE_SIZE / 2);
|
||||
@@ -365,7 +365,7 @@ mod tests {
|
||||
let ptr = heap.allocate(MAX_POSSIBLE_ALLOCATION).unwrap();
|
||||
|
||||
// then
|
||||
assert_eq!(ptr, 8);
|
||||
assert_eq!(ptr, to_pointer(8));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -409,7 +409,7 @@ mod tests {
|
||||
|
||||
// when
|
||||
let ptr = heap.allocate(42).unwrap();
|
||||
assert_eq!(ptr, 16 + 8);
|
||||
assert_eq!(ptr, to_pointer(16 + 8));
|
||||
heap.deallocate(ptr).unwrap();
|
||||
|
||||
// then
|
||||
|
||||
@@ -66,7 +66,7 @@ pub enum Error {
|
||||
#[display(fmt="The runtime has the `start` function")]
|
||||
RuntimeHasStartFn,
|
||||
/// Some other error occurred
|
||||
Other(&'static str),
|
||||
Other(String),
|
||||
/// Some error occurred in the allocator
|
||||
#[display(fmt="Error in allocator: {}", _0)]
|
||||
Allocator(&'static str),
|
||||
@@ -76,6 +76,9 @@ pub enum Error {
|
||||
/// Someone tried to allocate more memory than the allowed maximum per allocation.
|
||||
#[display(fmt="Requested allocation size is too large")]
|
||||
RequestedAllocationTooLarge,
|
||||
/// Executing the given function failed with the given error.
|
||||
#[display(fmt="Function execution failed with: {}", _0)]
|
||||
FunctionExecution(String),
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {
|
||||
@@ -93,8 +96,8 @@ impl state_machine::Error for Error {}
|
||||
|
||||
impl wasmi::HostError for Error {}
|
||||
|
||||
impl From<&'static str> for Error {
|
||||
fn from(err: &'static str) -> Error {
|
||||
impl From<String> for Error {
|
||||
fn from(err: String) -> Error {
|
||||
Error::Other(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,8 @@ pub use runtime_version::{RuntimeVersion, NativeVersion};
|
||||
pub use codec::Codec;
|
||||
#[doc(hidden)]
|
||||
pub use primitives::Blake2Hasher;
|
||||
#[doc(hidden)]
|
||||
pub use wasm_interface;
|
||||
|
||||
/// Provides runtime information.
|
||||
pub trait RuntimeInfo {
|
||||
|
||||
@@ -26,6 +26,7 @@ use wasmi::{
|
||||
Externals, FuncRef, ImportResolver, MemoryInstance, MemoryRef, Module, ModuleInstance,
|
||||
ModuleRef, RuntimeArgs, RuntimeValue, Trap, TrapKind, memory_units::Pages,
|
||||
};
|
||||
use wasm_interface::{Pointer, WordSize};
|
||||
|
||||
/// Index of a function inside the supervisor.
|
||||
///
|
||||
@@ -150,7 +151,7 @@ pub trait SandboxCapabilities {
|
||||
/// Returns `Err` if allocation not possible or errors during heap management.
|
||||
///
|
||||
/// Returns pointer to the allocated block.
|
||||
fn allocate(&mut self, len: u32) -> Result<u32>;
|
||||
fn allocate(&mut self, len: WordSize) -> Result<Pointer<u8>>;
|
||||
|
||||
/// Deallocate space specified by the pointer that was previously returned by [`allocate`].
|
||||
///
|
||||
@@ -159,21 +160,21 @@ pub trait SandboxCapabilities {
|
||||
/// Returns `Err` if deallocation not possible or because of errors in heap management.
|
||||
///
|
||||
/// [`allocate`]: #tymethod.allocate
|
||||
fn deallocate(&mut self, ptr: u32) -> Result<()>;
|
||||
fn deallocate(&mut self, ptr: Pointer<u8>) -> Result<()>;
|
||||
|
||||
/// Write `data` into the supervisor memory at offset specified by `ptr`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `Err` if `ptr + data.len()` is out of bounds.
|
||||
fn write_memory(&mut self, ptr: u32, data: &[u8]) -> Result<()>;
|
||||
fn write_memory(&mut self, ptr: Pointer<u8>, data: &[u8]) -> Result<()>;
|
||||
|
||||
/// Read `len` bytes from the supervisor memory.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `Err` if `ptr + len` is out of bounds.
|
||||
fn read_memory(&self, ptr: u32, len: u32) -> Result<Vec<u8>>;
|
||||
fn read_memory(&self, ptr: Pointer<u8>, len: WordSize) -> Result<Vec<u8>>;
|
||||
}
|
||||
|
||||
/// Implementation of [`Externals`] that allows execution of guest module with
|
||||
@@ -187,7 +188,7 @@ pub struct GuestExternals<'a, FE: SandboxCapabilities + Externals + 'a> {
|
||||
}
|
||||
|
||||
fn trap(msg: &'static str) -> Trap {
|
||||
TrapKind::Host(Box::new(Error::Other(msg))).into()
|
||||
TrapKind::Host(Box::new(Error::Other(msg.into()))).into()
|
||||
}
|
||||
|
||||
fn deserialize_result(serialized_result: &[u8]) -> std::result::Result<Option<RuntimeValue>, Trap> {
|
||||
@@ -243,7 +244,7 @@ impl<'a, FE: SandboxCapabilities + Externals + 'a> Externals for GuestExternals<
|
||||
let result = ::wasmi::FuncInstance::invoke(
|
||||
&dispatch_thunk,
|
||||
&[
|
||||
RuntimeValue::I32(invoke_args_ptr as i32),
|
||||
RuntimeValue::I32(u32::from(invoke_args_ptr) as i32),
|
||||
RuntimeValue::I32(invoke_args_data.len() as i32),
|
||||
RuntimeValue::I32(state as i32),
|
||||
RuntimeValue::I32(func_idx.0 as i32),
|
||||
@@ -260,7 +261,7 @@ impl<'a, FE: SandboxCapabilities + Externals + 'a> Externals for GuestExternals<
|
||||
let v = v as u64;
|
||||
let ptr = (v as u64 >> 32) as u32;
|
||||
let len = (v & 0xFFFFFFFF) as u32;
|
||||
(ptr, len)
|
||||
(Pointer::new(ptr), len)
|
||||
}
|
||||
Ok(_) => return Err(trap("Supervisor function returned unexpected result!")),
|
||||
Err(_) => return Err(trap("Supervisor function trapped!")),
|
||||
@@ -643,7 +644,10 @@ mod tests {
|
||||
if let Err(err) = res {
|
||||
assert_eq!(
|
||||
format!("{}", err),
|
||||
format!("{}", wasmi::Error::Trap(Error::AllocatorOutOfSpace.into()))
|
||||
format!(
|
||||
"{}",
|
||||
wasmi::Error::Trap(Error::FunctionExecution("AllocatorOutOfSpace".into()).into()),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -282,7 +282,11 @@ impl RuntimesCache {
|
||||
},
|
||||
Entry::Vacant(v) => {
|
||||
trace!(target: "runtimes_cache", "no instance found in cache, creating now.");
|
||||
let result = Self::create_wasm_instance(wasm_executor, ext, heap_pages);
|
||||
let result = Self::create_wasm_instance(
|
||||
wasm_executor,
|
||||
ext,
|
||||
heap_pages,
|
||||
);
|
||||
if let Err(ref err) = result {
|
||||
warn!(target: "runtimes_cache", "cannot create a runtime: {:?}", err);
|
||||
}
|
||||
@@ -305,10 +309,10 @@ impl RuntimesCache {
|
||||
//
|
||||
// A return of this error actually indicates that there is a problem in logic, since
|
||||
// we just loaded and validated the `module` above.
|
||||
let data_segments = extract_data_segments(&code).ok_or(CacheError::CantDeserializeWasm)?;
|
||||
let data_segments = extract_data_segments(&code)?;
|
||||
|
||||
// Instantiate this module.
|
||||
let instance = WasmExecutor::instantiate_module::<E>(heap_pages as usize, &module)
|
||||
let instance = WasmExecutor::instantiate_module(heap_pages as usize, &module)
|
||||
.map_err(CacheError::Instantiation)?;
|
||||
|
||||
// Take state snapshot before executing anything.
|
||||
@@ -335,12 +339,14 @@ impl RuntimesCache {
|
||||
/// Extract the data segments from the given wasm code.
|
||||
///
|
||||
/// Returns `Err` if the given wasm code cannot be deserialized.
|
||||
fn extract_data_segments(wasm_code: &[u8]) -> Option<Vec<DataSegment>> {
|
||||
let raw_module: RawModule = deserialize_buffer(wasm_code).ok()?;
|
||||
fn extract_data_segments(wasm_code: &[u8]) -> Result<Vec<DataSegment>, CacheError> {
|
||||
let raw_module: RawModule = deserialize_buffer(wasm_code)
|
||||
.map_err(|_| CacheError::CantDeserializeWasm)?;
|
||||
|
||||
let segments = raw_module
|
||||
.data_section()
|
||||
.map(|ds| ds.entries())
|
||||
.unwrap_or(&[])
|
||||
.to_vec();
|
||||
Some(segments)
|
||||
Ok(segments)
|
||||
}
|
||||
|
||||
@@ -16,129 +16,94 @@
|
||||
|
||||
//! Utilities for defining the wasm host environment.
|
||||
|
||||
use wasmi::{ValueType, RuntimeValue};
|
||||
use wasmi::nan_preserving_float::{F32, F64};
|
||||
|
||||
pub trait ConvertibleToWasm {
|
||||
const VALUE_TYPE: ValueType;
|
||||
type NativeType; fn to_runtime_value(self) -> RuntimeValue;
|
||||
}
|
||||
|
||||
impl ConvertibleToWasm for i32 {
|
||||
type NativeType = i32;
|
||||
const VALUE_TYPE: ValueType = ValueType::I32;
|
||||
fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self) }
|
||||
}
|
||||
|
||||
impl ConvertibleToWasm for u32 {
|
||||
type NativeType = u32;
|
||||
const VALUE_TYPE: ValueType = ValueType::I32;
|
||||
fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self as i32) }
|
||||
}
|
||||
|
||||
impl ConvertibleToWasm for i64 {
|
||||
type NativeType = i64;
|
||||
const VALUE_TYPE: ValueType = ValueType::I64;
|
||||
fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I64(self) }
|
||||
}
|
||||
|
||||
impl ConvertibleToWasm for u64 {
|
||||
type NativeType = u64;
|
||||
const VALUE_TYPE: ValueType = ValueType::I64;
|
||||
fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I64(self as i64) }
|
||||
}
|
||||
|
||||
impl ConvertibleToWasm for F32 {
|
||||
type NativeType = F32;
|
||||
const VALUE_TYPE: ValueType = ValueType::F32;
|
||||
fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::F32(self) }
|
||||
}
|
||||
|
||||
impl ConvertibleToWasm for F64 {
|
||||
type NativeType = F64;
|
||||
const VALUE_TYPE: ValueType = ValueType::F64;
|
||||
fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::F64(self) }
|
||||
}
|
||||
|
||||
impl ConvertibleToWasm for isize {
|
||||
type NativeType = i32;
|
||||
const VALUE_TYPE: ValueType = ValueType::I32;
|
||||
fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self as i32) }
|
||||
}
|
||||
|
||||
impl ConvertibleToWasm for usize {
|
||||
type NativeType = u32;
|
||||
const VALUE_TYPE: ValueType = ValueType::I32;
|
||||
fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self as u32 as i32) }
|
||||
}
|
||||
|
||||
impl<T> ConvertibleToWasm for *const T {
|
||||
type NativeType = u32;
|
||||
const VALUE_TYPE: ValueType = ValueType::I32;
|
||||
fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self as isize as i32) }
|
||||
}
|
||||
|
||||
impl<T> ConvertibleToWasm for *mut T {
|
||||
type NativeType = u32;
|
||||
const VALUE_TYPE: ValueType = ValueType::I32;
|
||||
fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self as isize as i32) }
|
||||
}
|
||||
|
||||
/// Converts arguments into respective WASM types.
|
||||
#[macro_export]
|
||||
macro_rules! convert_args {
|
||||
() => ([]);
|
||||
( $( $t:ty ),* ) => ( [ $( { use $crate::wasm_utils::ConvertibleToWasm; <$t>::VALUE_TYPE }, )* ] );
|
||||
( $( $t:ty ),* ) => ( [ $( <$t as $crate::wasm_interface::IntoValue>::VALUE_TYPE, )* ] );
|
||||
}
|
||||
|
||||
/// Generates a WASM signature for given list of parameters.
|
||||
#[macro_export]
|
||||
macro_rules! gen_signature {
|
||||
( ( $( $params: ty ),* ) ) => (
|
||||
{
|
||||
$crate::wasmi::Signature::new(&convert_args!($($params),*)[..], None)
|
||||
$crate::wasm_interface::Signature {
|
||||
args: std::borrow::Cow::Borrowed(&convert_args!( $( $params ),* )[..]),
|
||||
return_value: None,
|
||||
}
|
||||
);
|
||||
|
||||
( ( $( $params: ty ),* ) -> $returns: ty ) => (
|
||||
{
|
||||
$crate::wasmi::Signature::new(&convert_args!($($params),*)[..], Some({
|
||||
use $crate::wasm_utils::ConvertibleToWasm; <$returns>::VALUE_TYPE
|
||||
}))
|
||||
( ( $( $params: ty ),* ) -> $returns:ty ) => (
|
||||
$crate::wasm_interface::Signature {
|
||||
args: std::borrow::Cow::Borrowed(&convert_args!( $( $params ),* )[..]),
|
||||
return_value: Some(<$returns as $crate::wasm_interface::IntoValue>::VALUE_TYPE),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
macro_rules! resolve_fn {
|
||||
(@iter $index:expr, $sig_var:ident, $name_var:ident) => ();
|
||||
(@iter $index:expr, $sig_var:ident, $name_var:ident $name:ident ( $( $params:ty ),* ) $( -> $returns:ty )* => $($tail:tt)* ) => (
|
||||
if $name_var == stringify!($name) {
|
||||
let signature = gen_signature!( ( $( $params ),* ) $( -> $returns )* );
|
||||
if $sig_var != &signature {
|
||||
return Err($crate::wasmi::Error::Instantiation(
|
||||
format!("Export {} has different signature {:?}", $name_var, $sig_var),
|
||||
));
|
||||
macro_rules! gen_functions {
|
||||
(@INTERNAL
|
||||
{ $( $generated:tt )* }
|
||||
$context:ident,
|
||||
) => (
|
||||
&[ $( $generated )* ]
|
||||
);
|
||||
(@INTERNAL
|
||||
{ $( $generated:tt )* }
|
||||
$context:ident,
|
||||
$name:ident ( $( $names:ident: $params:ty ),* ) $( -> $returns:ty )? { $( $body:tt )* }
|
||||
$( $tail:tt )*
|
||||
) => (
|
||||
gen_functions! {
|
||||
@INTERNAL
|
||||
{
|
||||
$( $generated )*
|
||||
{
|
||||
struct $name;
|
||||
|
||||
#[allow(unused)]
|
||||
impl $crate::wasm_interface::Function for $name {
|
||||
fn name(&self) -> &str {
|
||||
stringify!($name)
|
||||
}
|
||||
fn signature(&self) -> $crate::wasm_interface::Signature {
|
||||
gen_signature!( ( $( $params ),* ) $( -> $returns )? )
|
||||
}
|
||||
fn execute(
|
||||
&self,
|
||||
context: &mut dyn $crate::wasm_interface::FunctionContext,
|
||||
args: &mut dyn Iterator<Item=$crate::wasm_interface::Value>,
|
||||
) -> ::std::result::Result<Option<$crate::wasm_interface::Value>, String> {
|
||||
let mut $context = context;
|
||||
marshall! {
|
||||
args,
|
||||
( $( $names : $params ),* ) $( -> $returns )? => { $( $body )* }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&$name as &dyn $crate::wasm_interface::Function
|
||||
},
|
||||
}
|
||||
return Ok($crate::wasmi::FuncInstance::alloc_host(signature, $index));
|
||||
$context,
|
||||
$( $tail )*
|
||||
}
|
||||
resolve_fn!(@iter $index + 1, $sig_var, $name_var $($tail)*)
|
||||
);
|
||||
|
||||
($sig_var:ident, $name_var:ident, $($tail:tt)* ) => (
|
||||
resolve_fn!(@iter 0, $sig_var, $name_var $($tail)*);
|
||||
( $context:ident, $( $tail:tt )* ) => (
|
||||
gen_functions!(@INTERNAL {} $context, $($tail)*);
|
||||
);
|
||||
}
|
||||
|
||||
/// Converts the list of arguments coming from WASM into their native types.
|
||||
#[macro_export]
|
||||
macro_rules! unmarshall_args {
|
||||
( $body:tt, $objectname:ident, $args_iter:ident, $( $names:ident : $params:ty ),*) => ({
|
||||
( $body:tt, $args_iter:ident, $( $names:ident : $params:ty ),*) => ({
|
||||
$(
|
||||
let $names : <$params as $crate::wasm_utils::ConvertibleToWasm>::NativeType =
|
||||
let $names : $params =
|
||||
$args_iter.next()
|
||||
.and_then(|rt_val| rt_val.try_into())
|
||||
.and_then(|val| <$params as $crate::wasm_interface::TryFromValue>::try_from_value(val))
|
||||
.expect(
|
||||
"`$args_iter` comes from an argument of Externals::invoke_index;
|
||||
"`$args_iter` comes from an argument of Externals::execute_function;
|
||||
args to an external call always matches the signature of the external;
|
||||
external signatures are built with count and types and in order defined by `$params`;
|
||||
here, we iterating on `$params`;
|
||||
@@ -160,7 +125,7 @@ macro_rules! unmarshall_args {
|
||||
#[inline(always)]
|
||||
pub fn constrain_closure<R, F>(f: F) -> F
|
||||
where
|
||||
F: FnOnce() -> Result<R, crate::error::Error>
|
||||
F: FnOnce() -> Result<R, String>
|
||||
{
|
||||
f
|
||||
}
|
||||
@@ -168,103 +133,40 @@ where
|
||||
/// Pass the list of parameters by converting them to respective WASM types.
|
||||
#[macro_export]
|
||||
macro_rules! marshall {
|
||||
( $args_iter:ident, $objectname:ident, ( $( $names:ident : $params:ty ),* ) -> $returns:ty => $body:tt ) => ({
|
||||
let body = $crate::wasm_utils::constrain_closure::<
|
||||
<$returns as $crate::wasm_utils::ConvertibleToWasm>::NativeType, _
|
||||
>(|| {
|
||||
unmarshall_args!($body, $objectname, $args_iter, $( $names : $params ),*)
|
||||
( $args_iter:ident, ( $( $names:ident : $params:ty ),* ) -> $returns:ty => $body:tt ) => ({
|
||||
let body = $crate::wasm_utils::constrain_closure::<$returns, _>(|| {
|
||||
unmarshall_args!($body, $args_iter, $( $names : $params ),*)
|
||||
});
|
||||
let r = body().map_err(wasmi::Trap::from)?;
|
||||
return Ok(Some({ use $crate::wasm_utils::ConvertibleToWasm; r.to_runtime_value() }))
|
||||
let r = body()?;
|
||||
return Ok(Some($crate::wasm_interface::IntoValue::into_value(r)))
|
||||
});
|
||||
( $args_iter:ident, $objectname:ident, ( $( $names:ident : $params:ty ),* ) => $body:tt ) => ({
|
||||
( $args_iter:ident, ( $( $names:ident : $params:ty ),* ) => $body:tt ) => ({
|
||||
let body = $crate::wasm_utils::constrain_closure::<(), _>(|| {
|
||||
unmarshall_args!($body, $objectname, $args_iter, $( $names : $params ),*)
|
||||
unmarshall_args!($body, $args_iter, $( $names : $params ),*)
|
||||
});
|
||||
body().map_err(wasmi::Trap::from)?;
|
||||
body()?;
|
||||
return Ok(None)
|
||||
})
|
||||
}
|
||||
|
||||
macro_rules! dispatch_fn {
|
||||
( @iter $index:expr, $index_ident:ident, $objectname:ident, $args_iter:ident) => {
|
||||
// `$index` comes from an argument of Externals::invoke_index;
|
||||
// externals are always invoked with index given by resolve_fn! at resolve time;
|
||||
// For each next function resolve_fn! gives new index, starting from 0;
|
||||
// Both dispatch_fn! and resolve_fn! are called with the same list of functions;
|
||||
// qed;
|
||||
panic!("fn with index {} is undefined", $index);
|
||||
};
|
||||
|
||||
(@iter
|
||||
$index:expr,
|
||||
$index_ident:ident,
|
||||
$objectname:ident,
|
||||
$args_iter:ident,
|
||||
$name:ident ( $( $names:ident : $params:ty ),* ) $( -> $returns:ty )* => $body:tt $($tail:tt)*
|
||||
) => (
|
||||
if $index_ident == $index {
|
||||
{ marshall!($args_iter, $objectname, ( $( $names : $params ),* ) $( -> $returns )* => $body) }
|
||||
}
|
||||
dispatch_fn!( @iter $index + 1, $index_ident, $objectname, $args_iter $($tail)*)
|
||||
);
|
||||
|
||||
( $index_ident:ident, $objectname:ident, $args_iter:ident, $($tail:tt)* ) => (
|
||||
dispatch_fn!( @iter 0, $index_ident, $objectname, $args_iter, $($tail)*);
|
||||
);
|
||||
}
|
||||
|
||||
/// Implements `wasmi::Externals` trait and `Resolver` for given struct.
|
||||
/// Implements the wasm host interface for the given type.
|
||||
#[macro_export]
|
||||
macro_rules! impl_function_executor {
|
||||
macro_rules! impl_wasm_host_interface {
|
||||
(
|
||||
$objectname:ident : $structname:ty,
|
||||
$(
|
||||
$name:ident
|
||||
( $( $names:ident : $params:ty ),* $(,)? )
|
||||
$( -> $returns:ty )? => { $( $body:tt )* },
|
||||
)*
|
||||
=> $( $pre:tt )+
|
||||
) => (
|
||||
impl $( $pre ) + $structname {
|
||||
#[allow(unused)]
|
||||
fn resolver() -> &'static dyn $crate::wasmi::ModuleImportResolver {
|
||||
struct Resolver;
|
||||
impl $crate::wasmi::ModuleImportResolver for Resolver {
|
||||
fn resolve_func(
|
||||
&self,
|
||||
name: &str,
|
||||
signature: &$crate::wasmi::Signature
|
||||
) -> std::result::Result<$crate::wasmi::FuncRef, $crate::wasmi::Error> {
|
||||
resolve_fn!(
|
||||
signature,
|
||||
name,
|
||||
$( $name( $( $params ),* ) $( -> $returns )? => )*
|
||||
);
|
||||
|
||||
Err($crate::wasmi::Error::Instantiation(
|
||||
format!("Export {} not found", name),
|
||||
))
|
||||
}
|
||||
}
|
||||
&Resolver
|
||||
}
|
||||
impl $interface_name:ident where $context:ident {
|
||||
$(
|
||||
$name:ident($( $names:ident : $params:ty ),* $(,)? ) $( -> $returns:ty )?
|
||||
{ $( $body:tt )* }
|
||||
)*
|
||||
}
|
||||
|
||||
impl $( $pre ) + $crate::wasmi::Externals for $structname {
|
||||
fn invoke_index(
|
||||
&mut self,
|
||||
index: usize,
|
||||
args: $crate::wasmi::RuntimeArgs,
|
||||
) -> std::result::Result<Option<$crate::wasmi::RuntimeValue>, $crate::wasmi::Trap> {
|
||||
let $objectname = self;
|
||||
let mut args = args.as_ref().iter();
|
||||
dispatch_fn! {
|
||||
index,
|
||||
$objectname,
|
||||
args,
|
||||
$( $name( $( $names : $params ),* ) $( -> $returns )? => { $( $body )* } ),*
|
||||
};
|
||||
) => (
|
||||
impl $crate::wasm_interface::HostFunctions for $interface_name {
|
||||
#[allow(non_camel_case_types)]
|
||||
fn functions() -> &'static [&'static dyn $crate::wasm_interface::Function] {
|
||||
gen_functions!(
|
||||
$context,
|
||||
$( $name( $( $names: $params ),* ) $( -> $returns )? { $( $body )* } )*
|
||||
)
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -279,7 +279,7 @@ export_api! {
|
||||
/// Submit transaction to the pool.
|
||||
///
|
||||
/// The transaction will end up in the pool.
|
||||
fn submit_transaction<T: codec::Encode>(data: &T) -> Result<(), ()>;
|
||||
fn submit_transaction(data: Vec<u8>) -> Result<(), ()>;
|
||||
|
||||
/// Returns information about the local node's network state.
|
||||
fn network_state() -> Result<OpaqueNetworkState, ()>;
|
||||
|
||||
@@ -334,9 +334,9 @@ impl OffchainApi for () {
|
||||
}, "is_validator can be called only in the offchain worker context")
|
||||
}
|
||||
|
||||
fn submit_transaction<T: codec::Encode>(data: &T) -> Result<(), ()> {
|
||||
fn submit_transaction(data: Vec<u8>) -> Result<(), ()> {
|
||||
with_offchain(|ext| {
|
||||
ext.submit_transaction(codec::Encode::encode(data))
|
||||
ext.submit_transaction(data)
|
||||
}, "submit_transaction can be called only in the offchain worker context")
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ pub fn panic(info: &PanicInfo) -> ! {
|
||||
|
||||
#[cfg(not(feature = "no_oom"))]
|
||||
#[alloc_error_handler]
|
||||
pub extern fn oom(_: ::core::alloc::Layout) -> ! {
|
||||
pub extern fn oom(_: core::alloc::Layout) -> ! {
|
||||
static OOM_MSG: &str = "Runtime memory exhausted. Aborting";
|
||||
|
||||
unsafe {
|
||||
@@ -980,10 +980,9 @@ impl OffchainApi for () {
|
||||
unsafe { ext_is_validator.get()() == 1 }
|
||||
}
|
||||
|
||||
fn submit_transaction<T: codec::Encode>(data: &T) -> Result<(), ()> {
|
||||
let encoded_data = codec::Encode::encode(data);
|
||||
fn submit_transaction(data: Vec<u8>) -> Result<(), ()> {
|
||||
let ret = unsafe {
|
||||
ext_submit_transaction.get()(encoded_data.as_ptr(), encoded_data.len() as u32)
|
||||
ext_submit_transaction.get()(data.as_ptr(), data.len() as u32)
|
||||
};
|
||||
|
||||
if ret == 0 {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
pub extern crate alloc;
|
||||
|
||||
extern "C" {
|
||||
fn ext_malloc(size: usize) -> *mut u8;
|
||||
fn ext_malloc(size: u32) -> *mut u8;
|
||||
fn ext_free(ptr: *mut u8);
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ mod __impl {
|
||||
|
||||
unsafe impl GlobalAlloc for WasmAllocator {
|
||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||
super::ext_malloc(layout.size()) as *mut u8
|
||||
super::ext_malloc(layout.size() as u32) as *mut u8
|
||||
}
|
||||
|
||||
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
|
||||
|
||||
@@ -629,7 +629,7 @@ cfg_if! {
|
||||
impl offchain_primitives::OffchainWorkerApi<Block> for Runtime {
|
||||
fn offchain_worker(block: u64) {
|
||||
let ex = Extrinsic::IncludeData(block.encode());
|
||||
runtime_io::submit_transaction(&ex).unwrap();
|
||||
runtime_io::submit_transaction(ex.encode()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -844,7 +844,7 @@ cfg_if! {
|
||||
impl offchain_primitives::OffchainWorkerApi<Block> for Runtime {
|
||||
fn offchain_worker(block: u64) {
|
||||
let ex = Extrinsic::IncludeData(block.encode());
|
||||
runtime_io::submit_transaction(&ex).unwrap()
|
||||
runtime_io::submit_transaction(ex.encode()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "substrate-wasm-interface"
|
||||
version = "2.0.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
wasmi = "0.5.0"
|
||||
@@ -0,0 +1,324 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Types and traits for interfacing between the host and the wasm runtime.
|
||||
|
||||
use std::{borrow::Cow, marker::PhantomData, mem, iter::Iterator, result};
|
||||
|
||||
mod wasmi_impl;
|
||||
|
||||
/// Result type used by traits in this crate.
|
||||
pub type Result<T> = result::Result<T, String>;
|
||||
|
||||
/// Value types supported by Substrate on the boundary between host/Wasm.
|
||||
#[derive(Copy, Clone, PartialEq, Debug, Eq)]
|
||||
pub enum ValueType {
|
||||
/// An `i32` value type.
|
||||
I32,
|
||||
/// An `i64` value type.
|
||||
I64,
|
||||
/// An `f32` value type.
|
||||
F32,
|
||||
/// An `f64` value type.
|
||||
F64,
|
||||
}
|
||||
|
||||
/// Values supported by Substrate on the boundary between host/Wasm.
|
||||
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||
pub enum Value {
|
||||
/// An `i32` value.
|
||||
I32(i32),
|
||||
/// An `i64` value.
|
||||
I64(i64),
|
||||
/// An nan-preserving `f32` value.
|
||||
F32(u32),
|
||||
/// An nan-preserving `f64` value.
|
||||
F64(u64),
|
||||
}
|
||||
|
||||
/// Provides `Sealed` trait to prevent implementing trait `PointerType` outside of this crate.
|
||||
mod private {
|
||||
pub trait Sealed {}
|
||||
|
||||
impl Sealed for u8 {}
|
||||
impl Sealed for u16 {}
|
||||
impl Sealed for u32 {}
|
||||
impl Sealed for u64 {}
|
||||
}
|
||||
|
||||
/// Something that can be wrapped in a wasm `Pointer`.
|
||||
///
|
||||
/// This trait is sealed.
|
||||
pub trait PointerType: Sized {
|
||||
/// The size of the type in wasm.
|
||||
const SIZE: u32 = mem::size_of::<Self>() as u32;
|
||||
}
|
||||
|
||||
impl PointerType for u8 {}
|
||||
impl PointerType for u16 {}
|
||||
impl PointerType for u32 {}
|
||||
impl PointerType for u64 {}
|
||||
|
||||
/// Type to represent a pointer in wasm at the host.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub struct Pointer<T: PointerType> {
|
||||
ptr: u32,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: PointerType> Pointer<T> {
|
||||
/// Create a new instance of `Self`.
|
||||
pub fn new(ptr: u32) -> Self {
|
||||
Self {
|
||||
ptr,
|
||||
_marker: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate the offset from this pointer.
|
||||
///
|
||||
/// `offset` is in units of `T`. So, `3` means `3 * mem::size_of::<T>()` as offset to the pointer.
|
||||
///
|
||||
/// Returns an `Option` to respect that the pointer could probably overflow.
|
||||
pub fn offset(self, offset: u32) -> Option<Self> {
|
||||
offset.checked_mul(T::SIZE).and_then(|o| self.ptr.checked_add(o)).map(|ptr| {
|
||||
Self {
|
||||
ptr,
|
||||
_marker: Default::default(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a null pointer.
|
||||
pub fn null() -> Self {
|
||||
Self::new(0)
|
||||
}
|
||||
|
||||
/// Cast this pointer of type `T` to a pointer of type `R`.
|
||||
pub fn cast<R: PointerType>(self) -> Pointer<R> {
|
||||
Pointer::new(self.ptr)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PointerType> From<Pointer<T>> for u32 {
|
||||
fn from(ptr: Pointer<T>) -> Self {
|
||||
ptr.ptr
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PointerType> From<Pointer<T>> for usize {
|
||||
fn from(ptr: Pointer<T>) -> Self {
|
||||
ptr.ptr as _
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PointerType> IntoValue for Pointer<T> {
|
||||
const VALUE_TYPE: ValueType = ValueType::I32;
|
||||
fn into_value(self) -> Value { Value::I32(self.ptr as _) }
|
||||
}
|
||||
|
||||
impl<T: PointerType> TryFromValue for Pointer<T> {
|
||||
fn try_from_value(val: Value) -> Option<Self> {
|
||||
match val {
|
||||
Value::I32(val) => Some(Self::new(val as _)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The word size used in wasm. Normally known as `usize` in Rust.
|
||||
pub type WordSize = u32;
|
||||
|
||||
/// The Signature of a function
|
||||
#[derive(Eq, PartialEq, Debug, Clone)]
|
||||
pub struct Signature {
|
||||
/// The arguments of a function.
|
||||
pub args: Cow<'static, [ValueType]>,
|
||||
/// The optional return value of a function.
|
||||
pub return_value: Option<ValueType>,
|
||||
}
|
||||
|
||||
impl Signature {
|
||||
/// Create a new instance of `Signature`.
|
||||
pub fn new<T: Into<Cow<'static, [ValueType]>>>(args: T, return_value: Option<ValueType>) -> Self {
|
||||
Self {
|
||||
args: args.into(),
|
||||
return_value,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new instance of `Signature` with the given `args` and without any return value.
|
||||
pub fn new_with_args<T: Into<Cow<'static, [ValueType]>>>(args: T) -> Self {
|
||||
Self {
|
||||
args: args.into(),
|
||||
return_value: None,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Something that provides a function implementation on the host for a wasm function.
|
||||
pub trait Function {
|
||||
/// Returns the name of this function.
|
||||
fn name(&self) -> &str;
|
||||
/// Returns the signature of this function.
|
||||
fn signature(&self) -> Signature;
|
||||
/// Execute this function with the given arguments.
|
||||
fn execute(
|
||||
&self,
|
||||
context: &mut dyn FunctionContext,
|
||||
args: &mut dyn Iterator<Item=Value>,
|
||||
) -> Result<Option<Value>>;
|
||||
}
|
||||
|
||||
/// Context used by `Function` to interact with the allocator and the memory of the wasm instance.
|
||||
pub trait FunctionContext {
|
||||
/// Read memory from `address` into a vector.
|
||||
fn read_memory(&self, address: Pointer<u8>, size: WordSize) -> Result<Vec<u8>> {
|
||||
let mut vec = Vec::with_capacity(size as usize);
|
||||
vec.resize(size as usize, 0);
|
||||
self.read_memory_into(address, &mut vec)?;
|
||||
Ok(vec)
|
||||
}
|
||||
/// Read memory into the given `dest` buffer from `address`.
|
||||
fn read_memory_into(&self, address: Pointer<u8>, dest: &mut [u8]) -> Result<()>;
|
||||
/// Write the given data at `address` into the memory.
|
||||
fn write_memory(&mut self, address: Pointer<u8>, data: &[u8]) -> Result<()>;
|
||||
/// Allocate a memory instance of `size` bytes.
|
||||
fn allocate_memory(&mut self, size: WordSize) -> Result<Pointer<u8>>;
|
||||
/// Deallocate a given memory instance.
|
||||
fn deallocate_memory(&mut self, ptr: Pointer<u8>) -> Result<()>;
|
||||
/// Provides access to the sandbox.
|
||||
fn sandbox(&mut self) -> &mut dyn Sandbox;
|
||||
}
|
||||
|
||||
/// Sandbox memory identifier.
|
||||
pub type MemoryId = u32;
|
||||
|
||||
/// Something that provides access to the sandbox.
|
||||
pub trait Sandbox {
|
||||
/// Get sandbox memory from the `memory_id` instance at `offset` into the given buffer.
|
||||
fn memory_get(
|
||||
&self,
|
||||
memory_id: MemoryId,
|
||||
offset: WordSize,
|
||||
buf_ptr: Pointer<u8>,
|
||||
buf_len: WordSize,
|
||||
) -> Result<u32>;
|
||||
/// Set sandbox memory from the given value.
|
||||
fn memory_set(
|
||||
&mut self,
|
||||
memory_id: MemoryId,
|
||||
offset: WordSize,
|
||||
val_ptr: Pointer<u8>,
|
||||
val_len: WordSize,
|
||||
) -> Result<u32>;
|
||||
/// Delete a memory instance.
|
||||
fn memory_teardown(&mut self, memory_id: MemoryId) -> Result<()>;
|
||||
/// Create a new memory instance with the given `initial` size and the `maximum` size.
|
||||
/// The size is given in wasm pages.
|
||||
fn memory_new(&mut self, initial: u32, maximum: u32) -> Result<MemoryId>;
|
||||
/// Invoke an exported function by a name.
|
||||
fn invoke(
|
||||
&mut self,
|
||||
instance_id: u32,
|
||||
export_name: &str,
|
||||
args: &[u8],
|
||||
return_val: Pointer<u8>,
|
||||
return_val_len: WordSize,
|
||||
state: u32,
|
||||
) -> Result<u32>;
|
||||
/// Delete a sandbox instance.
|
||||
fn instance_teardown(&mut self, instance_id: u32) -> Result<()>;
|
||||
/// Create a new sandbox instance.
|
||||
fn instance_new(
|
||||
&mut self,
|
||||
dispatch_thunk_id: u32,
|
||||
wasm: &[u8],
|
||||
raw_env_def: &[u8],
|
||||
state: u32,
|
||||
) -> Result<u32>;
|
||||
}
|
||||
|
||||
/// Something that provides implementations for host functions.
|
||||
pub trait HostFunctions {
|
||||
/// Returns all host functions.
|
||||
fn functions() -> &'static [&'static dyn Function];
|
||||
}
|
||||
|
||||
/// Something that can be converted into a wasm compatible `Value`.
|
||||
pub trait IntoValue {
|
||||
/// The type of the value in wasm.
|
||||
const VALUE_TYPE: ValueType;
|
||||
|
||||
/// Convert `self` into a wasm `Value`.
|
||||
fn into_value(self) -> Value;
|
||||
}
|
||||
|
||||
/// Something that can may be created from a wasm `Value`.
|
||||
pub trait TryFromValue: Sized {
|
||||
/// Try to convert the given `Value` into `Self`.
|
||||
fn try_from_value(val: Value) -> Option<Self>;
|
||||
}
|
||||
|
||||
macro_rules! impl_into_and_from_value {
|
||||
(
|
||||
$(
|
||||
$type:ty, $( < $gen:ident >, )? $value_variant:ident,
|
||||
)*
|
||||
) => {
|
||||
$(
|
||||
impl $( <$gen> )? IntoValue for $type {
|
||||
const VALUE_TYPE: ValueType = ValueType::$value_variant;
|
||||
fn into_value(self) -> Value { Value::$value_variant(self as _) }
|
||||
}
|
||||
|
||||
impl $( <$gen> )? TryFromValue for $type {
|
||||
fn try_from_value(val: Value) -> Option<Self> {
|
||||
match val {
|
||||
Value::$value_variant(val) => Some(val as _),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
impl_into_and_from_value! {
|
||||
u32, I32,
|
||||
i32, I32,
|
||||
u64, I64,
|
||||
i64, I64,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn pointer_offset_works() {
|
||||
let ptr = Pointer::<u32>::null();
|
||||
|
||||
assert_eq!(ptr.offset(10).unwrap(), Pointer::new(40));
|
||||
assert_eq!(ptr.offset(32).unwrap(), Pointer::new(128));
|
||||
|
||||
let ptr = Pointer::<u64>::null();
|
||||
|
||||
assert_eq!(ptr.offset(10).unwrap(), Pointer::new(80));
|
||||
assert_eq!(ptr.offset(32).unwrap(), Pointer::new(256));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Implementation of conversions between Substrate and wasmi types.
|
||||
|
||||
use crate::{Value, ValueType, Signature};
|
||||
|
||||
impl From<Value> for wasmi::RuntimeValue {
|
||||
fn from(value: Value) -> Self {
|
||||
match value {
|
||||
Value::I32(val) => Self::I32(val),
|
||||
Value::I64(val) => Self::I64(val),
|
||||
Value::F32(val) => Self::F32(val.into()),
|
||||
Value::F64(val) => Self::F64(val.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<wasmi::RuntimeValue> for Value {
|
||||
fn from(value: wasmi::RuntimeValue) -> Self {
|
||||
match value {
|
||||
wasmi::RuntimeValue::I32(val) => Self::I32(val),
|
||||
wasmi::RuntimeValue::I64(val) => Self::I64(val),
|
||||
wasmi::RuntimeValue::F32(val) => Self::F32(val.into()),
|
||||
wasmi::RuntimeValue::F64(val) => Self::F64(val.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ValueType> for wasmi::ValueType {
|
||||
fn from(value: ValueType) -> Self {
|
||||
match value {
|
||||
ValueType::I32 => Self::I32,
|
||||
ValueType::I64 => Self::I64,
|
||||
ValueType::F32 => Self::F32,
|
||||
ValueType::F64 => Self::F64,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<wasmi::ValueType> for ValueType {
|
||||
fn from(value: wasmi::ValueType) -> Self {
|
||||
match value {
|
||||
wasmi::ValueType::I32 => Self::I32,
|
||||
wasmi::ValueType::I64 => Self::I64,
|
||||
wasmi::ValueType::F32 => Self::F32,
|
||||
wasmi::ValueType::F64 => Self::F64,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Signature> for wasmi::Signature {
|
||||
fn from(sig: Signature) -> Self {
|
||||
let args = sig.args.iter().map(|a| (*a).into()).collect::<Vec<_>>();
|
||||
wasmi::Signature::new(args, sig.return_value.map(Into::into))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&wasmi::Signature> for Signature {
|
||||
fn from(sig: &wasmi::Signature) -> Self {
|
||||
Signature::new(
|
||||
sig.params().into_iter().copied().map(Into::into).collect::<Vec<_>>(),
|
||||
sig.return_type().map(Into::into),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ pub use substrate_executor::NativeExecutor;
|
||||
native_executor_instance!(
|
||||
pub Executor,
|
||||
node_template_runtime::api::dispatch,
|
||||
node_template_runtime::native_version
|
||||
node_template_runtime::native_version,
|
||||
);
|
||||
|
||||
construct_simple_protocol! {
|
||||
|
||||
@@ -82,7 +82,7 @@ pub trait SubmitSignedTransaction<T: crate::Trait, Call> {
|
||||
::create_transaction::<Self::Signer>(call, id, expected)
|
||||
.ok_or(())?;
|
||||
let xt = Self::Extrinsic::new(call, Some(signature_data)).ok_or(())?;
|
||||
runtime_io::submit_transaction(&xt)
|
||||
runtime_io::submit_transaction(xt.encode())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ pub trait SubmitUnsignedTransaction<T: crate::Trait, Call> {
|
||||
/// and `Err` if transaction was rejected from the pool.
|
||||
fn submit_unsigned(call: impl Into<Call>) -> Result<(), ()> {
|
||||
let xt = Self::Extrinsic::new(call.into(), None).ok_or(())?;
|
||||
runtime_io::submit_transaction(&xt)
|
||||
runtime_io::submit_transaction(xt.encode())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user