1use calimero_node_primitives::client::NodeClient;
2use calimero_primitives::context::ContextId;
3use calimero_primitives::identity::PublicKey;
4use tracing::{debug, error, info};
5use wasmer::{CompileError, DeserializeError, Instance, NativeEngineExt, SerializeError, Store};
6
7mod constants;
8mod constraint;
9pub mod errors;
10pub mod logic;
11mod memory;
12pub mod store;
13
14pub use constraint::Constraint;
15use errors::{FunctionCallError, VMRuntimeError};
16use logic::{Outcome, VMContext, VMLimits, VMLogic, VMLogicError};
17use memory::WasmerTunables;
18use store::Storage;
19
20pub type RuntimeResult<T, E = VMRuntimeError> = Result<T, E>;
21
22#[derive(Clone, Debug)]
23pub struct Engine {
24 limits: VMLimits,
25 engine: wasmer::Engine,
26}
27
28impl Default for Engine {
29 fn default() -> Self {
30 let limits = VMLimits::default();
31
32 let engine = wasmer::Engine::default();
33
34 Self::new(engine, limits)
35 }
36}
37
38impl Engine {
39 pub fn new(mut engine: wasmer::Engine, limits: VMLimits) -> Self {
40 engine.set_tunables(WasmerTunables::new(&limits));
41
42 Self { limits, engine }
43 }
44
45 pub fn headless() -> Self {
46 let limits = VMLimits::default();
47
48 let engine = wasmer::Engine::headless();
49
50 Self::new(engine, limits)
51 }
52
53 pub fn compile(&self, bytes: &[u8]) -> Result<Module, CompileError> {
54 let module = wasmer::Module::new(&self.engine, bytes)?;
65
66 Ok(Module {
67 limits: self.limits.clone(),
68 engine: self.engine.clone(),
69 module,
70 })
71 }
72
73 pub unsafe fn from_precompiled(&self, bytes: &[u8]) -> Result<Module, DeserializeError> {
74 let module = wasmer::Module::deserialize(&self.engine, bytes)?;
75
76 Ok(Module {
77 limits: self.limits.clone(),
78 engine: self.engine.clone(),
79 module,
80 })
81 }
82}
83
84#[derive(Debug)]
85pub struct Module {
86 limits: VMLimits,
87 engine: wasmer::Engine,
88 module: wasmer::Module,
89}
90
91impl Module {
92 pub fn to_bytes(&self) -> Result<Box<[u8]>, SerializeError> {
93 let bytes = self.module.serialize()?;
94
95 Ok(Vec::into_boxed_slice(bytes.into()))
96 }
97
98 pub fn run(
99 &self,
100 context: ContextId,
101 executor: PublicKey,
102 method: &str,
103 input: &[u8],
104 storage: &mut dyn Storage,
105 node_client: Option<NodeClient>,
106 ) -> RuntimeResult<Outcome> {
107 let context_id = context;
108 info!(%context_id, method, "Running WASM method");
109 debug!(%context_id, method, input_len = input.len(), "WASM execution input");
110
111 let context = VMContext::new(input.into(), *context_id, *executor);
112
113 let mut logic = VMLogic::new(storage, context, &self.limits, node_client);
114
115 let mut store = Store::new(self.engine.clone());
116
117 let imports = logic.imports(&mut store);
118
119 let instance = match Instance::new(&mut store, &self.module, &imports) {
120 Ok(instance) => instance,
121 Err(err) => {
122 error!(%context_id, method, error=?err, "Failed to instantiate WASM module");
123 return Ok(logic.finish(Some(err.into())));
124 }
125 };
126
127 let _ = match instance.exports.get_memory("memory") {
128 Ok(memory) => logic.with_memory(memory.clone()),
129 Err(err) => {
131 error!(%context_id, method, error=?err, "Failed to get WASM memory");
132 return Ok(logic.finish(Some(err.into())));
133 }
134 };
135
136 let function = match instance.exports.get_function(method) {
137 Ok(function) => function,
138 Err(err) => {
139 error!(%context_id, method, error=?err, "Method not found in WASM module");
140 return Ok(logic.finish(Some(err.into())));
141 }
142 };
143
144 let signature = function.ty(&store);
145
146 if !(signature.params().is_empty() && signature.results().is_empty()) {
147 error!(%context_id, method, "Invalid method signature");
148 return Ok(logic.finish(Some(FunctionCallError::MethodResolutionError(
149 errors::MethodResolutionError::InvalidSignature {
150 name: method.to_owned(),
151 },
152 ))));
153 }
154
155 if let Err(err) = function.call(&mut store, &[]) {
156 error!(%context_id, method, error=?err, "WASM method execution failed");
157 return match err.downcast::<VMLogicError>() {
158 Ok(err) => Ok(logic.finish(Some(err.try_into()?))),
159 Err(err) => Ok(logic.finish(Some(err.into()))),
160 };
161 }
162
163 let outcome = logic.finish(None);
164 info!(%context_id, method, "WASM method execution completed");
165 debug!(
166 %context_id,
167 method,
168 has_return = outcome.returns.is_ok(),
169 logs_count = outcome.logs.len(),
170 events_count = outcome.events.len(),
171 "WASM execution outcome"
172 );
173
174 Ok(outcome)
175 }
176}
177
178#[cfg(test)]
179mod integration_tests_package_usage {
180 use {eyre as _, owo_colors as _, rand as _};
181}