* Build every wasm crate in its own project with wasm-builder Building all wasm crates in one workspace was a nice idea, however it just introduced problems: 1. We needed to prune old members, but this didn't worked for old git deps. 2. We locked the whole wasm workspace while building one crate. This could lead to infinitely locking the workspace on a crash. Now we just build every crate in its own project, this means we will build the dependencies multiple times. While building the dependencies multiple times, we still decrease the build time by around 30 seconds for Polkadot and Substrate because of the new parallelism ;) * Remove the requirement on wasm-builder-runner This removes the requirement on wasm-builder-runner by using the new `build_dep` feature of cargo. We use nightly anyway and that enables us to use this feature. This solves the problem of not mixing build/proc-macro deps with normal deps. By doing this we get rid off this complicated project structure and can depend directly on `wasm-builder`. This also removes all the code from wasm-builder-runner and mentions that it is deprecated. * Copy the `Cargo.lock` to the correct folder * Remove wasm-builder-runner * Update docs * Fix deterministic check Modified-by: Bastian Köcher <git@kchr.de> * Try to make the ui test happy * Switch to `SKIP_WASM_BUILD` * Rename `SKIP_WASM_BINARY` to the correct name... * Update utils/wasm-builder/src/builder.rs Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> * Update utils/wasm-builder/src/builder.rs Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com>
Substrate runtime interface
This crate provides types, traits and macros around runtime interfaces. A runtime interface is a fixed interface between a Substrate runtime and a Substrate node. For a native runtime the interface maps to a direct function call of the implementation. For a wasm runtime the interface maps to an external function call. These external functions are exported by the wasm executor and they map to the same implementation as the native calls.
Using a type in a runtime interface
Any type that should be used in a runtime interface as argument or return value needs to
implement [RIType]. The associated type FFIType
is the type that is used in the FFI function to represent the actual type. For example [T] is
represented by an u64. The slice pointer and the length will be mapped to an u64 value.
For more information see this table.
The FFI function definition is used when calling from the wasm runtime into the node.
Traits are used to convert from a type to the corresponding
RIType::FFIType.
Depending on where and how a type should be used in a function signature, a combination of the
following traits need to be implemented:
- Pass as function argument: [
wasm::IntoFFIValue] and [host::FromFFIValue] - As function return value: [
wasm::FromFFIValue] and [host::IntoFFIValue] - Pass as mutable function argument: [
host::IntoPreallocatedFFIValue]
The traits are implemented for most of the common types like [T], Vec<T>, arrays and
primitive types.
For custom types, we provide the PassBy trait and strategies that define
how a type is passed between the wasm runtime and the node. Each strategy also provides a derive
macro to simplify the implementation.
Performance
To not waste any more performance when calling into the node, not all types are SCALE encoded
when being passed as arguments between the wasm runtime and the node. For most types that
are raw bytes like Vec<u8>, [u8] or [u8; N] we pass them directly, without SCALE encoding
them in front of. The implementation of [RIType] each type provides more information on how
the data is passed.
Declaring a runtime interface
Declaring a runtime interface is similar to declaring a trait in Rust:
#[sp_runtime_interface::runtime_interface]
trait RuntimeInterface {
fn some_function(value: &[u8]) -> bool {
value.iter().all(|v| *v > 125)
}
}
For more information on declaring a runtime interface, see
#[runtime_interface].
FFI type and conversion
The following table documents how values of types are passed between the wasm and the host side and how they are converted into the corresponding type.
| Type | FFI type | Conversion |
|---|---|---|
u8 |
u8 |
Identity |
u16 |
u16 |
Identity |
u32 |
u32 |
Identity |
u64 |
u64 |
Identity |
i128 |
u32 |
v.as_ptr() (pointer to a 16 byte array) |
i8 |
i8 |
Identity |
i16 |
i16 |
Identity |
i32 |
i32 |
Identity |
i64 |
i64 |
Identity |
u128 |
u32 |
v.as_ptr() (pointer to a 16 byte array) |
bool |
u8 |
if v { 1 } else { 0 } |
&str |
u64 |
v.len() 32bit << 32 | v.as_ptr() 32bit |
&[u8] |
u64 |
v.len() 32bit << 32 | v.as_ptr() 32bit |
Vec<u8> |
u64 |
v.len() 32bit << 32 | v.as_ptr() 32bit |
Vec<T> where T: Encode |
u64 |
let e = v.encode();e.len() 32bit << 32 | e.as_ptr() 32bit |
&[T] where T: Encode |
u64 |
let e = v.encode();e.len() 32bit << 32 | e.as_ptr() 32bit |
[u8; N] |
u32 |
v.as_ptr() |
*const T |
u32 |
Identity |
Option<T> |
u64 |
let e = v.encode();e.len() 32bit << 32 | e.as_ptr() 32bit |
T where T: PassBy<PassBy=Inner> |
Depends on inner | Depends on inner |
T where T: PassBy<PassBy=Codec> |
u64 |
v.len() 32bit << 32 | v.as_ptr() 32bit |
Identity means that the value is converted directly into the corresponding FFI type.
License: Apache-2.0