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