linera_execution/wasm/
wasmer.rs1use std::{marker::Unpin, sync::LazyLock};
7
8use linera_base::data_types::{Bytecode, StreamUpdate};
9use linera_witty::{
10 wasmer::{EntrypointInstance, InstanceBuilder},
11 ExportTo,
12};
13use tokio::sync::Mutex;
14use tracing::instrument;
15
16use super::{
17 module_cache::ModuleCache,
18 runtime_api::{BaseRuntimeApi, ContractRuntimeApi, RuntimeApiData, ServiceRuntimeApi},
19 ContractEntrypoints, ServiceEntrypoints, WasmExecutionError,
20};
21use crate::{
22 wasm::{WasmContractModule, WasmServiceModule},
23 ContractRuntime, ExecutionError, ServiceRuntime,
24};
25
26static SERVICE_ENGINE: LazyLock<wasmer::Engine> = LazyLock::new(|| {
28 #[cfg(web)]
29 {
30 wasmer::Engine::default()
31 }
32
33 #[cfg(not(web))]
34 {
35 wasmer::sys::EngineBuilder::new(wasmer::Cranelift::new()).into()
36 }
37});
38
39static CONTRACT_CACHE: LazyLock<Mutex<ModuleCache<CachedContractModule>>> =
41 LazyLock::new(Mutex::default);
42
43static SERVICE_CACHE: LazyLock<Mutex<ModuleCache<wasmer::Module>>> = LazyLock::new(Mutex::default);
45
46pub(crate) struct WasmerContractInstance<Runtime> {
48 instance: EntrypointInstance<RuntimeApiData<Runtime>>,
50}
51
52pub struct WasmerServiceInstance<Runtime> {
54 instance: EntrypointInstance<RuntimeApiData<Runtime>>,
56}
57
58impl WasmContractModule {
59 pub async fn from_wasmer(contract_bytecode: Bytecode) -> Result<Self, WasmExecutionError> {
61 let mut contract_cache = CONTRACT_CACHE.lock().await;
62 let (engine, module) = contract_cache
63 .get_or_insert_with(contract_bytecode, CachedContractModule::new)
64 .map_err(WasmExecutionError::LoadContractModule)?
65 .create_execution_instance()
66 .map_err(WasmExecutionError::LoadContractModule)?;
67 Ok(WasmContractModule::Wasmer { engine, module })
68 }
69}
70
71impl<Runtime> WasmerContractInstance<Runtime>
72where
73 Runtime: ContractRuntime + Clone + Unpin + 'static,
74{
75 pub fn prepare(
77 contract_engine: wasmer::Engine,
78 contract_module: &wasmer::Module,
79 runtime: Runtime,
80 ) -> Result<Self, WasmExecutionError> {
81 let system_api_data = RuntimeApiData::new(runtime);
82 let mut instance_builder = InstanceBuilder::new(contract_engine, system_api_data);
83
84 BaseRuntimeApi::export_to(&mut instance_builder)?;
85 ContractRuntimeApi::export_to(&mut instance_builder)?;
86
87 let instance = instance_builder.instantiate(contract_module)?;
88
89 Ok(Self { instance })
90 }
91}
92
93impl WasmServiceModule {
94 pub async fn from_wasmer(service_bytecode: Bytecode) -> Result<Self, WasmExecutionError> {
96 let mut service_cache = SERVICE_CACHE.lock().await;
97 let module = service_cache
98 .get_or_insert_with(service_bytecode, |bytecode| {
99 wasmer::Module::new(&*SERVICE_ENGINE, bytecode).map_err(anyhow::Error::from)
100 })
101 .map_err(WasmExecutionError::LoadServiceModule)?;
102 Ok(WasmServiceModule::Wasmer { module })
103 }
104}
105
106impl<Runtime> WasmerServiceInstance<Runtime>
107where
108 Runtime: ServiceRuntime + Clone + Unpin + 'static,
109{
110 pub fn prepare(
112 service_module: &wasmer::Module,
113 runtime: Runtime,
114 ) -> Result<Self, WasmExecutionError> {
115 let system_api_data = RuntimeApiData::new(runtime);
116 let mut instance_builder = InstanceBuilder::new(SERVICE_ENGINE.clone(), system_api_data);
117
118 BaseRuntimeApi::export_to(&mut instance_builder)?;
119 ServiceRuntimeApi::export_to(&mut instance_builder)?;
120
121 let instance = instance_builder.instantiate(service_module)?;
122
123 Ok(Self { instance })
124 }
125}
126
127impl<Runtime> crate::UserContract for WasmerContractInstance<Runtime>
128where
129 Runtime: ContractRuntime + Unpin + 'static,
130{
131 #[instrument(skip_all)]
132 fn instantiate(&mut self, argument: Vec<u8>) -> Result<(), ExecutionError> {
133 ContractEntrypoints::new(&mut self.instance)
134 .instantiate(argument)
135 .map_err(WasmExecutionError::from)?;
136 Ok(())
137 }
138
139 #[instrument(skip_all)]
140 fn execute_operation(&mut self, operation: Vec<u8>) -> Result<Vec<u8>, ExecutionError> {
141 Ok(ContractEntrypoints::new(&mut self.instance)
142 .execute_operation(operation)
143 .map_err(WasmExecutionError::from)?)
144 }
145
146 #[instrument(skip_all)]
147 fn execute_message(&mut self, message: Vec<u8>) -> Result<(), ExecutionError> {
148 ContractEntrypoints::new(&mut self.instance)
149 .execute_message(message)
150 .map_err(WasmExecutionError::from)?;
151 Ok(())
152 }
153
154 #[instrument(skip_all)]
155 fn process_streams(&mut self, updates: Vec<StreamUpdate>) -> Result<(), ExecutionError> {
156 ContractEntrypoints::new(&mut self.instance)
157 .process_streams(updates)
158 .map_err(WasmExecutionError::from)?;
159 Ok(())
160 }
161
162 #[instrument(skip_all)]
163 fn finalize(&mut self) -> Result<(), ExecutionError> {
164 ContractEntrypoints::new(&mut self.instance)
165 .finalize()
166 .map_err(WasmExecutionError::from)?;
167 Ok(())
168 }
169}
170
171impl<Runtime: 'static> crate::UserService for WasmerServiceInstance<Runtime> {
172 fn handle_query(&mut self, argument: Vec<u8>) -> Result<Vec<u8>, ExecutionError> {
173 Ok(ServiceEntrypoints::new(&mut self.instance)
174 .handle_query(argument)
175 .map_err(WasmExecutionError::from)?)
176 }
177}
178
179impl From<ExecutionError> for wasmer::RuntimeError {
180 fn from(error: ExecutionError) -> Self {
181 wasmer::RuntimeError::user(Box::new(error))
182 }
183}
184
185impl From<wasmer::RuntimeError> for ExecutionError {
186 fn from(error: wasmer::RuntimeError) -> Self {
187 error
188 .downcast::<ExecutionError>()
189 .unwrap_or_else(|unknown_error| {
190 ExecutionError::WasmError(WasmExecutionError::ExecuteModuleInWasmer(unknown_error))
191 })
192 }
193}
194
195#[derive(Clone)]
198pub struct CachedContractModule(wasmer::Module);
199
200impl CachedContractModule {
201 pub fn new(contract_bytecode: Bytecode) -> Result<Self, anyhow::Error> {
203 let module = wasmer::Module::new(&Self::create_compilation_engine(), contract_bytecode)?;
204 Ok(CachedContractModule(module))
205 }
206
207 fn create_compilation_engine() -> wasmer::Engine {
209 #[cfg(not(web))]
210 {
211 let mut compiler_config = wasmer_compiler_singlepass::Singlepass::default();
212 compiler_config.canonicalize_nans(true);
213
214 wasmer::sys::EngineBuilder::new(compiler_config).into()
215 }
216
217 #[cfg(web)]
218 wasmer::Engine::default()
219 }
220
221 pub fn create_execution_instance(
223 &self,
224 ) -> Result<(wasmer::Engine, wasmer::Module), anyhow::Error> {
225 #[cfg(web)]
226 {
227 Ok((wasmer::Engine::default(), self.0.clone()))
228 }
229
230 #[cfg(not(web))]
231 {
232 let engine = wasmer::Engine::default();
233 let store = wasmer::Store::new(engine.clone());
234 let bytes = self.0.serialize()?;
235 let module = unsafe { wasmer::Module::deserialize(&store, bytes) }?;
236 Ok((engine, module))
237 }
238 }
239}