I am currently working on a Substrate-based solo chain that has migrated from polkadot-v0.9.34 to polkadot-v0.9.35 and is now on the stable branch 2409. In v0.9.35, the Sandbox
host functions were removed as part of this PR, which has impacted our node synchronization in full mode.
During the initial attempt to synchronize nodes from scratch in full mode, we encountered an error indicating that the Sandbox
host functions were not found. As an initial approach, we tried a stub implementation by which this allowed the nodes to begin synchronization, the process ultimately failed when the nodes encountered a block that utilized these missing functions. Consequently, we decided to proceed with implementing the Sandbox trait ourselves to address this issue comprehensively.
Error: Service(Client(VersionInvalid("Other error happened while constructing the runtime:
runtime requires function imports which are not present on the host:
'env:ext_sandbox_memory_set_version_1', 'env:ext_sandbox_invoke_version_1',
'env:ext_sandbox_instantiate_version_1', 'env:ext_sandbox_memory_teardown_version_1',
'env:ext_sandbox_memory_new_version_1', 'env:ext_sandbox_memory_get_version_1',
'env:ext_sandbox_instance_teardown_version_1'")))
What We Have Done So Far
To address this issue, I have started re-implementing the removed Sandbox
host functions based on the deprecations mentioned in the PR. During this implementation, I first define all the host functions in a file called runtime_interface
. After this I call these host functions in our node/client/lib.rs
like this:
pub type HostFunctions =
(sp_io::SubstrateHostFunctions, frame_benchmarking::benchmarking::HostFunctions,node_runtime_interfaces::sandbox::HostFunctions);
This ensured that the node recognized the availability of the Sandbox host functions.
For the implementation of these functions, I pulled the necessary code from the referenced PR. Additionally, I chose wasmi to execute WebAssembly, as the Sandbox functionality originally worked with wasmi before its removal. This decision was made to expedite the synchronization process for the node.
Below are some of the implemented files:
Sandbox Trait Definition
runtime_interface.rs :
#[runtime_interface(wasm_only)]
pub trait Sandbox {
fn memory_get(
&mut self,
memory_idx: u32,
offset: u32,
buf_ptr: Pointer<u8>,
buf_len: u32,
) -> u32 {
// Implementation
return 0;
}
fn memory_set(
&mut self,
memory_idx: u32,
offset: u32,
val_ptr: Pointer<u8>,
val_len: u32,
) -> u32 {
// Implementation
return 0;
}
fn instantiate(
&mut self,
dispatch_thunk: u32,
wasm_code: &[u8],
env_def: &[u8],
state_ptr: Pointer<u8>,
) -> u32 {
return 0;
}
...
}
Function Implementation
The Sandbox
host functions are implemented in another file (sandbox_interface
: It implements the several traits for FunctionExecutor
), and an example of the memory_get
function is as follows:
sandbox_interface.rs
impl Sandbox for FunctionExecutor {
fn memory_get(
&mut self,
memory_id: MemoryId,
offset: WordSize,
buf_ptr: Pointer<u8>,
buf_len: WordSize,
) -> WResult<u32> {
let sandboxed_memory =
self.sandbox_store.borrow().memory(memory_id).map_err(|e| e.to_string())?;
let len = buf_len as usize;
let buffer = match sandboxed_memory.read(Pointer::new(offset as u32), len) {
Err(_) => return Ok(sandbox_env::ERR_OUT_OF_BOUNDS),
Ok(buffer) => buffer,
};
if self.memory.set(buf_ptr.into(), &buffer).is_err() {
return Ok(sandbox_env::ERR_OUT_OF_BOUNDS);
}
Ok(sandbox_env::ERR_OK)
}
...
}
The Problem
The #[runtime_interface(wasm_only)]
macro is critical to our implementation, as it provides functionalities such as:
- Trait Implementation: Implements the trait defined for the host functions.
- Host Function Registration: The macro registers the trait methods as host functions that can be called from the outside world.
- FFI (Foreign Function Interface) Wrappers: It generates FFI wrappers for the trait methods, allowing them to be called from the host environment.
We use the macro as follows:
#[runtime_interface(wasm_only)]
pub trait Sandbox {
fn memory_get(
&mut self,
memory_idx: u32,
offset: u32,
buf_ptr: Pointer<u8>,
buf_len: u32,
) -> u32 {
self.sandbox() // Implementation
.memory_get(memory_idx, offset, buf_ptr, buf_len)
.expect("Failed to get memory with sandbox");
}
}
However, when the self.sandbox()
method is called, it attempts to locate the sandbox functionality in the wasm-interface
module via the sp-runtime-interface
module. Since the sandbox functionality was removed in this PR, the implementation fails.
Attempted Solutions
We put some efforts to extract the logic of #[runtime_interface(wasm_only)] that is relevant to our use case to bypass this issue but the macro is essential for host function integration and cannot be omitted without sacrificing critical functionality like Trait Implementation
, Host Function Registration
, FFI Wrappers
.
Question
Is it possible to access the Sandbox
host function implementations within the current Substrate architecture by modifying the #[runtime_interface(wasm_only)]
macro? Alternatively, is there another approach to resolve the issue of accessing the host function implementations effectively?
Thanks