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