linera_execution/wasm/
wasmtime.rs1use std::sync::LazyLock;
7
8use linera_base::data_types::{Bytecode, StreamUpdate};
9use linera_witty::{wasmtime::EntrypointInstance, ExportTo};
10use tokio::sync::Mutex;
11use tracing::instrument;
12use wasmtime::{Config, Engine, Linker, Module, Store};
13
14use super::{
15 add_metering,
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 CONTRACT_ENGINE: LazyLock<Engine> = LazyLock::new(|| {
27 let mut config = Config::default();
28 config.cranelift_nan_canonicalization(true);
29
30 Engine::new(&config).expect("Failed to create Wasmtime `Engine` for contracts")
31});
32
33static SERVICE_ENGINE: LazyLock<Engine> = LazyLock::new(Engine::default);
35
36static CONTRACT_CACHE: LazyLock<Mutex<ModuleCache<Module>>> = LazyLock::new(Mutex::default);
38
39static SERVICE_CACHE: LazyLock<Mutex<ModuleCache<Module>>> = LazyLock::new(Mutex::default);
41
42pub(crate) struct WasmtimeContractInstance<Runtime>
47where
48 Runtime: ContractRuntime + 'static,
49{
50 instance: EntrypointInstance<RuntimeApiData<Runtime>>,
52}
53
54pub struct WasmtimeServiceInstance<Runtime> {
56 instance: EntrypointInstance<RuntimeApiData<Runtime>>,
58}
59
60impl WasmContractModule {
61 pub async fn from_wasmtime(contract_bytecode: Bytecode) -> Result<Self, WasmExecutionError> {
63 let mut contract_cache = CONTRACT_CACHE.lock().await;
64 let module = contract_cache
65 .get_or_insert_with(contract_bytecode, "contract", |bytecode| {
66 let metered_bytecode = add_metering(&bytecode)?;
67 Module::new(&CONTRACT_ENGINE, metered_bytecode)
68 })
69 .map_err(WasmExecutionError::LoadContractModule)?;
70 Ok(WasmContractModule::Wasmtime { module })
71 }
72}
73
74impl<Runtime> WasmtimeContractInstance<Runtime>
75where
76 Runtime: ContractRuntime + 'static,
77{
78 pub fn prepare(contract_module: &Module, runtime: Runtime) -> Result<Self, WasmExecutionError> {
80 let mut linker = Linker::new(&CONTRACT_ENGINE);
81
82 BaseRuntimeApi::export_to(&mut linker)?;
83 ContractRuntimeApi::export_to(&mut linker)?;
84
85 let user_data = RuntimeApiData::new(runtime);
86 let mut store = Store::new(&CONTRACT_ENGINE, user_data);
87 let instance = linker
88 .instantiate(&mut store, contract_module)
89 .map_err(WasmExecutionError::LoadContractModule)?;
90
91 Ok(Self {
92 instance: EntrypointInstance::new(instance, store),
93 })
94 }
95}
96
97impl WasmServiceModule {
98 pub async fn from_wasmtime(service_bytecode: Bytecode) -> Result<Self, WasmExecutionError> {
100 let mut service_cache = SERVICE_CACHE.lock().await;
101 let module = service_cache
102 .get_or_insert_with(service_bytecode, "service", |bytecode| {
103 Module::new(&SERVICE_ENGINE, bytecode)
104 })
105 .map_err(WasmExecutionError::LoadServiceModule)?;
106 Ok(WasmServiceModule::Wasmtime { module })
107 }
108}
109
110impl<Runtime> WasmtimeServiceInstance<Runtime>
111where
112 Runtime: ServiceRuntime + 'static,
113{
114 pub fn prepare(service_module: &Module, runtime: Runtime) -> Result<Self, WasmExecutionError> {
116 let mut linker = Linker::new(&SERVICE_ENGINE);
117
118 BaseRuntimeApi::export_to(&mut linker)?;
119 ServiceRuntimeApi::export_to(&mut linker)?;
120
121 let user_data = RuntimeApiData::new(runtime);
122 let mut store = Store::new(&SERVICE_ENGINE, user_data);
123 let instance = linker
124 .instantiate(&mut store, service_module)
125 .map_err(WasmExecutionError::LoadServiceModule)?;
126
127 Ok(Self {
128 instance: EntrypointInstance::new(instance, store),
129 })
130 }
131}
132
133impl<Runtime> crate::UserContract for WasmtimeContractInstance<Runtime>
134where
135 Runtime: ContractRuntime + 'static,
136{
137 #[instrument(skip_all)]
138 fn instantiate(&mut self, argument: Vec<u8>) -> Result<(), ExecutionError> {
139 ContractEntrypoints::new(&mut self.instance)
140 .instantiate(argument)
141 .map_err(WasmExecutionError::from)?;
142 Ok(())
143 }
144
145 #[instrument(skip_all)]
146 fn execute_operation(&mut self, operation: Vec<u8>) -> Result<Vec<u8>, ExecutionError> {
147 let result = ContractEntrypoints::new(&mut self.instance)
148 .execute_operation(operation)
149 .map_err(WasmExecutionError::from)?;
150 Ok(result)
151 }
152
153 #[instrument(skip_all)]
154 fn execute_message(&mut self, message: Vec<u8>) -> Result<(), ExecutionError> {
155 ContractEntrypoints::new(&mut self.instance)
156 .execute_message(message)
157 .map_err(WasmExecutionError::from)?;
158 Ok(())
159 }
160
161 #[instrument(skip_all)]
162 fn process_streams(&mut self, updates: Vec<StreamUpdate>) -> Result<(), ExecutionError> {
163 ContractEntrypoints::new(&mut self.instance)
164 .process_streams(updates)
165 .map_err(WasmExecutionError::from)?;
166 Ok(())
167 }
168
169 #[instrument(skip_all)]
170 fn finalize(&mut self) -> Result<(), ExecutionError> {
171 ContractEntrypoints::new(&mut self.instance)
172 .finalize()
173 .map_err(WasmExecutionError::from)?;
174 Ok(())
175 }
176}
177
178impl<Runtime> crate::UserService for WasmtimeServiceInstance<Runtime>
179where
180 Runtime: ServiceRuntime + 'static,
181{
182 fn handle_query(&mut self, argument: Vec<u8>) -> Result<Vec<u8>, ExecutionError> {
183 Ok(ServiceEntrypoints::new(&mut self.instance)
184 .handle_query(argument)
185 .map_err(WasmExecutionError::from)?)
186 }
187}