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