Skip to content

Commit

Permalink
feat(sp-wasm-interface): Gear sandbox (gear-tech/substrate/pull/35)
Browse files Browse the repository at this point in the history
* move allocator to primitives
* move HostState to sp-wasm-interface
* move StoreData to sp-wasm-interface
* caller method
* move MemoryWrapper to sp-wasm-interface
* add sp-wasm-interface util module
* refactor with_caller_mut method
  • Loading branch information
ukint-vs committed May 6, 2024
1 parent ff66f76 commit 3cbd56d
Show file tree
Hide file tree
Showing 30 changed files with 1,283 additions and 509 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,6 @@ members = [
"substrate/bin/node/testing",
"substrate/bin/utils/chain-spec-builder",
"substrate/bin/utils/subkey",
"substrate/client/allocator",
"substrate/client/api",
"substrate/client/authority-discovery",
"substrate/client/basic-authorship",
Expand Down Expand Up @@ -388,6 +387,7 @@ members = [
"substrate/frame/utility",
"substrate/frame/vesting",
"substrate/frame/whitelist",
"substrate/primitives/allocator",
"substrate/primitives/api",
"substrate/primitives/api/proc-macro",
"substrate/primitives/api/test",
Expand Down
20 changes: 0 additions & 20 deletions substrate/client/allocator/Cargo.toml

This file was deleted.

2 changes: 1 addition & 1 deletion substrate/client/executor/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
thiserror = "1.0.48"
wasm-instrument = "0.3"
sc-allocator = { path = "../../allocator" }
sp-allocator = { path = "../../../primitives/allocator" }
sp-maybe-compressed-blob = { path = "../../../primitives/maybe-compressed-blob" }
sp-wasm-interface = { path = "../../../primitives/wasm-interface" }

Expand Down
2 changes: 1 addition & 1 deletion substrate/client/executor/common/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ pub enum Error {
Other(String),

#[error(transparent)]
Allocator(#[from] sc_allocator::Error),
Allocator(#[from] sp_allocator::Error),

#[error("Host function {0} execution failed with: {1}")]
FunctionExecution(String, String),
Expand Down
8 changes: 0 additions & 8 deletions substrate/client/executor/common/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,6 @@
use crate::error::Result;
use sp_wasm_interface::Pointer;
use std::ops::Range;

/// Construct a range from an offset to a data length after the offset.
/// Returns None if the end of the range would exceed some maximum offset.
pub fn checked_range(offset: usize, len: usize, max: usize) -> Option<Range<usize>> {
let end = offset.checked_add(len)?;
(end <= max).then(|| offset..end)
}

/// Provides safe memory access interface using an external buffer
pub trait MemoryTransfer {
Expand Down
2 changes: 1 addition & 1 deletion substrate/client/executor/common/src/wasm_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
use crate::error::Error;
use sp_wasm_interface::Value;

pub use sc_allocator::AllocationStats;
pub use sp_allocator::AllocationStats;

/// Default heap allocation strategy.
pub const DEFAULT_HEAP_ALLOC_STRATEGY: HeapAllocStrategy =
Expand Down
2 changes: 1 addition & 1 deletion substrate/client/executor/wasmi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ targets = ["x86_64-unknown-linux-gnu"]
log = "0.4.17"
wasmi = { git = "https://github.com/gear-tech/wasmi", branch = "v0.13.2-sign-ext", features = ["virtual_memory"] }
sc-executor-common = { version = "0.10.0-dev", path = "../common" }
sp-allocator = { version = "4.1.0-dev", path = "../../../primitives/allocator" }
sp-allocator = { path = "../../primitives/allocator" }
sp-runtime-interface = { version = "7.0.0", path = "../../../primitives/runtime-interface" }
sp-wasm-interface = { version = "7.0.0", path = "../../../primitives/wasm-interface" }
2 changes: 1 addition & 1 deletion substrate/client/executor/wasmtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ wasmtime = { version = "8.0.1", default-features = false, features = [
"pooling-allocator",
] }
anyhow = "1.0.68"
sc-allocator = { path = "../../allocator" }
sp-allocator = { path = "../../../primitives/allocator" }
sc-executor-common = { path = "../common" }
sp-runtime-interface = { path = "../../../primitives/runtime-interface" }
sp-wasm-interface = { path = "../../../primitives/wasm-interface", features = ["wasmtime"] }
Expand Down
93 changes: 19 additions & 74 deletions substrate/client/executor/wasmtime/src/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,41 +21,10 @@
use wasmtime::Caller;

use sc_allocator::{AllocationStats, FreeingBumpHeapAllocator};
use sp_wasm_interface::{Pointer, WordSize};
pub use sp_wasm_interface::HostState;
use sp_wasm_interface::{util, FunctionContextToken, Pointer, WordSize};

use crate::{instance_wrapper::MemoryWrapper, runtime::StoreData, util};

/// The state required to construct a HostContext context. The context only lasts for one host
/// call, whereas the state is maintained for the duration of a Wasm runtime call, which may make
/// many different host calls that must share state.
pub struct HostState {
/// The allocator instance to keep track of allocated memory.
///
/// This is stored as an `Option` as we need to temporarily set this to `None` when we are
/// allocating/deallocating memory. The problem being that we can only mutable access `caller`
/// once.
allocator: Option<FreeingBumpHeapAllocator>,
panic_message: Option<String>,
}

impl HostState {
/// Constructs a new `HostState`.
pub fn new(allocator: FreeingBumpHeapAllocator) -> Self {
HostState { allocator: Some(allocator), panic_message: None }
}

/// Takes the error message out of the host state, leaving a `None` in its place.
pub fn take_panic_message(&mut self) -> Option<String> {
self.panic_message.take()
}

pub(crate) fn allocation_stats(&self) -> AllocationStats {
self.allocator.as_ref()
.expect("Allocator is always set and only unavailable when doing an allocation/deallocation; qed")
.stats()
}
}
use crate::runtime::StoreData;

/// A `HostContext` implements `FunctionContext` for making host calls from a Wasmtime
/// runtime. The `HostContext` exists only for the lifetime of the call and borrows state from
Expand All @@ -64,15 +33,6 @@ pub(crate) struct HostContext<'a> {
pub(crate) caller: Caller<'a, StoreData>,
}

impl<'a> HostContext<'a> {
fn host_state_mut(&mut self) -> &mut HostState {
self.caller
.data_mut()
.host_state_mut()
.expect("host state is not empty when calling a function in wasm; qed")
}
}

impl<'a> sp_wasm_interface::FunctionContext for HostContext<'a> {
fn read_memory_into(
&self,
Expand All @@ -87,42 +47,27 @@ impl<'a> sp_wasm_interface::FunctionContext for HostContext<'a> {
}

fn allocate_memory(&mut self, size: WordSize) -> sp_wasm_interface::Result<Pointer<u8>> {
let memory = self.caller.data().memory();
let mut allocator = self
.host_state_mut()
.allocator
.take()
.expect("allocator is not empty when calling a function in wasm; qed");

// We can not return on error early, as we need to store back allocator.
let res = allocator
.allocate(&mut MemoryWrapper(&memory, &mut self.caller), size)
.map_err(|e| e.to_string());

self.host_state_mut().allocator = Some(allocator);

res
util::allocate_memory(&mut self.caller, size)
}

fn deallocate_memory(&mut self, ptr: Pointer<u8>) -> sp_wasm_interface::Result<()> {
let memory = self.caller.data().memory();
let mut allocator = self
.host_state_mut()
.allocator
.take()
.expect("allocator is not empty when calling a function in wasm; qed");

// We can not return on error early, as we need to store back allocator.
let res = allocator
.deallocate(&mut MemoryWrapper(&memory, &mut self.caller), ptr)
.map_err(|e| e.to_string());

self.host_state_mut().allocator = Some(allocator);

res
util::deallocate_memory(&mut self.caller, ptr)
}

fn register_panic_error_message(&mut self, message: &str) {
self.host_state_mut().panic_message = Some(message.to_owned());
self.caller
.data_mut()
.host_state_mut()
.expect("host state is not empty when calling a function in wasm; qed")
.panic_message = Some(message.to_owned());
}

fn with_caller_mut_impl(
&mut self,
_: FunctionContextToken,
context: *mut (),
callback: fn(*mut (), &mut Caller<StoreData>),
) {
callback(context, &mut self.caller)
}
}
45 changes: 6 additions & 39 deletions substrate/client/executor/wasmtime/src/instance_wrapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,12 @@ impl EntryPoint {
let data_len = u32::from(data_len);

match self.call_type {
EntryPointType::Direct { ref entrypoint } =>
entrypoint.call(&mut *store, (data_ptr, data_len)),
EntryPointType::Wrapped { func, ref dispatcher } =>
dispatcher.call(&mut *store, (func, data_ptr, data_len)),
EntryPointType::Direct { ref entrypoint } => {
entrypoint.call(&mut *store, (data_ptr, data_len))
},
EntryPointType::Wrapped { func, ref dispatcher } => {
dispatcher.call(&mut *store, (func, data_ptr, data_len))
},
}
.map_err(|trap| {
let host_state = store
Expand Down Expand Up @@ -114,41 +116,6 @@ impl EntryPoint {
}
}

/// Wrapper around [`Memory`] that implements [`sc_allocator::Memory`].
pub(crate) struct MemoryWrapper<'a, C>(pub &'a wasmtime::Memory, pub &'a mut C);

impl<C: AsContextMut> sc_allocator::Memory for MemoryWrapper<'_, C> {
fn with_access_mut<R>(&mut self, run: impl FnOnce(&mut [u8]) -> R) -> R {
run(self.0.data_mut(&mut self.1))
}

fn with_access<R>(&self, run: impl FnOnce(&[u8]) -> R) -> R {
run(self.0.data(&self.1))
}

fn grow(&mut self, additional: u32) -> std::result::Result<(), ()> {
self.0
.grow(&mut self.1, additional as u64)
.map_err(|e| {
log::error!(
target: "wasm-executor",
"Failed to grow memory by {} pages: {}",
additional,
e,
)
})
.map(drop)
}

fn pages(&self) -> u32 {
self.0.size(&self.1) as u32
}

fn max_pages(&self) -> Option<u32> {
self.0.ty(&self.1).maximum().map(|p| p as _)
}
}

/// Wrap the given WebAssembly Instance of a wasm module with Substrate-runtime.
///
/// This struct is a handy wrapper around a wasmtime `Instance` that provides substrate specific
Expand Down
Loading

0 comments on commit 3cbd56d

Please sign in to comment.