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