Skip to content

Commit

Permalink
Wasm on the Web (#1990)
Browse files Browse the repository at this point in the history
* `linera-execution::wasm::wasmer`: set up dependencies for the Web

* `linera-{execution,witty}`: allow non-`Send` types in `UserData`

* `.github/workflows`: enable testing the `wasmer` feature on the Web

* `linera-execution`: don't use `wasmer/js-default` on native targets

* `linera-execution`: make the `wasmtime` backend non-`Send` safe

* `linera-witty`: turn on required `wasmer` features
  • Loading branch information
Twey authored Jun 5, 2024
1 parent 7a6da04 commit 11f9838
Show file tree
Hide file tree
Showing 17 changed files with 92 additions and 81 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ jobs:
--locked \
--target wasm32-unknown-unknown \
--no-default-features \
--features web
--features web,wasmer
- name: Compile the workspace with the default features (build)
run: |
cargo build --locked
Expand Down
22 changes: 8 additions & 14 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ wasm-bindgen = "0.2.92"
wasm-bindgen-test = "0.3.42"
wasm-encoder = "0.24.1"
wasm-instrument = "0.4.0"
wasmer = { version = "4.3.0-alpha.1", features = ["singlepass"] }
wasmer = { version = "4.3.0-alpha.1", default-features = false }
wasmer-compiler-singlepass = "4.3.0-alpha.1"
wasmparser = "0.101.1"
wasmtime = "1.0"
Expand Down Expand Up @@ -217,3 +217,8 @@ opt-level = 3
version = "0.4.1"
git = "https://github.com/Twey/rust-indexed-db"
branch = "no-uuid-wasm-bindgen"

[patch.crates-io.wasmer]
version = "4.3.0-alpha.1"
git = "https://github.com/Twey/wasmer"
branch = "non-send-environments"
21 changes: 7 additions & 14 deletions examples/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,8 @@ strip = 'debuginfo'

[profile.bench]
debug = true

[patch.crates-io.wasmer]
version = "4.3.0-alpha.1"
git = "https://github.com/Twey/wasmer"
branch = "non-send-environments"
8 changes: 7 additions & 1 deletion linera-execution/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,16 @@ thiserror.workspace = true
tracing = { workspace = true, features = ["log"] }
wasm-encoder = { workspace = true, optional = true }
wasm-instrument = { workspace = true, optional = true, features = ["sign_ext"] }
wasmer = { workspace = true, optional = true }
wasmparser = { workspace = true, optional = true }
wasmtime = { workspace = true, optional = true }

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
tokio = { workspace = true, features = ["rt-multi-thread"] }
wasmer = { workspace = true, optional = true, features = ["sys-default", "singlepass"] }

[target.'cfg(target_arch = "wasm32")'.dependencies]
tokio = { workspace = true, features = ["rt"] }
wasmer = { workspace = true, optional = true, features = ["js-default"] }

[dev-dependencies]
anyhow.workspace = true
Expand All @@ -87,3 +88,8 @@ cfg_aliases.workspace = true

[package.metadata.cargo-machete]
ignored = ["serde_bytes"]

[patch.crates-io.wasmer]
version = "4.3.0-alpha.1"
git = "https://github.com/Twey/wasmer"
branch = "non-send-environments"
4 changes: 3 additions & 1 deletion linera-execution/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@

fn main() {
cfg_aliases::cfg_aliases! {
web: { all(target_arch = "wasm32", feature = "web") },

with_fs: { all(not(target_arch = "wasm32"), feature = "fs") },
with_metrics: { all(not(target_arch = "wasm32"), feature = "metrics") },
with_testing: { any(test, feature = "test") },
with_tokio_multi_thread: { not(target_arch = "wasm32") },
with_wasmer: { all(not(target_arch = "wasm32"), feature = "wasmer") },
with_wasmer: { feature = "wasmer" },
with_wasmtime: { all(not(target_arch = "wasm32"), feature = "wasmtime") },

// If you change this, don't forget to update `WasmRuntime` and
Expand Down
4 changes: 2 additions & 2 deletions linera-execution/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,10 @@ pub type UserContractCode = Arc<dyn UserContractModule + Send + Sync + 'static>;
pub type UserServiceCode = Arc<dyn UserServiceModule + Send + Sync + 'static>;

/// An implementation of [`UserContract`].
pub type UserContractInstance = Box<dyn UserContract + Send + Sync + 'static>;
pub type UserContractInstance = Box<dyn UserContract + 'static>;

/// An implementation of [`UserService`].
pub type UserServiceInstance = Box<dyn UserService + Send + Sync + 'static>;
pub type UserServiceInstance = Box<dyn UserService + 'static>;

/// A factory trait to obtain a [`UserContract`] from a [`UserContractModule`]
pub trait UserContractModule {
Expand Down
4 changes: 2 additions & 2 deletions linera-execution/src/test_utils/mock_application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ impl UserContractModule for MockApplication {
fn instantiate(
&self,
runtime: ContractSyncRuntime,
) -> Result<Box<dyn UserContract + Send + Sync + 'static>, ExecutionError> {
) -> Result<Box<dyn UserContract + 'static>, ExecutionError> {
Ok(Box::new(self.create_mock_instance(runtime)))
}
}
Expand All @@ -204,7 +204,7 @@ impl UserServiceModule for MockApplication {
fn instantiate(
&self,
runtime: ServiceSyncRuntime,
) -> Result<Box<dyn UserService + Send + Sync + 'static>, ExecutionError> {
) -> Result<Box<dyn UserService + 'static>, ExecutionError> {
Ok(Box::new(self.create_mock_instance(runtime)))
}
}
Expand Down
6 changes: 3 additions & 3 deletions linera-execution/src/wasm/system_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ pub struct ContractSystemApi<Caller>(PhantomData<Caller>);
impl<Caller, Runtime> ContractSystemApi<Caller>
where
Caller: Instance<UserData = SystemApiData<Runtime>>,
Runtime: ContractRuntime + Send + 'static,
Runtime: ContractRuntime + 'static,
{
/// Returns the ID of the current chain.
fn get_chain_id(caller: &mut Caller) -> Result<ChainId, RuntimeError> {
Expand Down Expand Up @@ -360,7 +360,7 @@ pub struct ServiceSystemApi<Caller>(PhantomData<Caller>);
impl<Caller, Runtime> ServiceSystemApi<Caller>
where
Caller: Instance<UserData = SystemApiData<Runtime>>,
Runtime: ServiceRuntime + Send + 'static,
Runtime: ServiceRuntime + 'static,
{
/// Returns the ID of the current chain.
fn get_chain_id(caller: &mut Caller) -> Result<ChainId, RuntimeError> {
Expand Down Expand Up @@ -514,7 +514,7 @@ pub struct ViewSystemApi<Caller>(PhantomData<Caller>);
impl<Caller, Runtime> ViewSystemApi<Caller>
where
Caller: Instance<UserData = SystemApiData<Runtime>>,
Runtime: BaseRuntime + WriteBatch + Send + 'static,
Runtime: BaseRuntime + WriteBatch + 'static,
{
/// Creates a new promise to check if the `key` is in storage.
fn contains_key_new(caller: &mut Caller, key: Vec<u8>) -> Result<u32, RuntimeError> {
Expand Down
40 changes: 24 additions & 16 deletions linera-execution/src/wasm/wasmer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use linera_witty::{
};
use tokio::sync::Mutex;
use wasm_instrument::{gas_metering, parity_wasm};
use wasmer::{sys::EngineBuilder, Cranelift, Engine, Module, Singlepass, Store};
use wasmer::{Engine, Module, Store};

use super::{
module_cache::ModuleCache,
Expand All @@ -28,8 +28,15 @@ use crate::{

/// An [`Engine`] instance configured to run application services.
static SERVICE_ENGINE: Lazy<Engine> = Lazy::new(|| {
let compiler_config = Cranelift::new();
EngineBuilder::new(compiler_config).into()
#[cfg(web)]
{
wasmer::Engine::default()
}

#[cfg(not(web))]
{
wasmer::sys::EngineBuilder::new(wasmer::Cranelift::new()).into()
}
});

/// A cache of compiled contract modules, with their respective [`Engine`] instances.
Expand Down Expand Up @@ -65,7 +72,7 @@ impl WasmContractModule {

impl<Runtime> WasmerContractInstance<Runtime>
where
Runtime: ContractRuntime + WriteBatch + Clone + Send + Sync + Unpin + 'static,
Runtime: ContractRuntime + WriteBatch + Clone + Unpin + 'static,
{
/// Prepares a runtime instance to call into the Wasm contract.
pub fn prepare(
Expand Down Expand Up @@ -100,7 +107,7 @@ impl WasmServiceModule {

impl<Runtime> WasmerServiceInstance<Runtime>
where
Runtime: ServiceRuntime + WriteBatch + Clone + Send + Sync + Unpin + 'static,
Runtime: ServiceRuntime + WriteBatch + Clone + Unpin + 'static,
{
/// Prepares a runtime instance to call into the Wasm service.
pub fn prepare(service_module: &Module, runtime: Runtime) -> Result<Self, WasmExecutionError> {
Expand All @@ -118,7 +125,7 @@ where

impl<Runtime> crate::UserContract for WasmerContractInstance<Runtime>
where
Runtime: ContractRuntime + Send + Unpin + 'static,
Runtime: ContractRuntime + Unpin + 'static,
{
fn instantiate(
&mut self,
Expand Down Expand Up @@ -160,10 +167,7 @@ where
}
}

impl<Runtime> crate::UserService for WasmerServiceInstance<Runtime>
where
Runtime: Send + 'static,
{
impl<Runtime: 'static> crate::UserService for WasmerServiceInstance<Runtime> {
fn handle_query(
&mut self,
_context: QueryContext,
Expand Down Expand Up @@ -253,17 +257,21 @@ impl CachedContractModule {

/// Creates a new [`Engine`] to compile a contract bytecode.
fn create_compilation_engine() -> Engine {
let mut compiler_config = Singlepass::default();
compiler_config.canonicalize_nans(true);
#[cfg(not(web))]
{
let mut compiler_config = wasmer::Singlepass::default();
compiler_config.canonicalize_nans(true);

wasmer::sys::EngineBuilder::new(compiler_config).into()
}

EngineBuilder::new(compiler_config).into()
#[cfg(web)]
wasmer::Engine::default()
}

/// Creates a [`Module`] from a compiled contract using a headless [`Engine`].
pub fn create_execution_instance(&self) -> Result<(Engine, Module), anyhow::Error> {
use wasmer::NativeEngineExt;

let engine = Engine::headless();
let engine = Engine::default();
let store = Store::new(engine.clone());
let module = unsafe { Module::deserialize(&store, &*self.compiled_bytecode) }?;
Ok((engine, module))
Expand Down
Loading

0 comments on commit 11f9838

Please sign in to comment.