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