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