calimero_runtime/
lib.rs

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        // todo! apply a prepare step
53        // todo! - parse the wasm blob, validate and apply transformations
54        // todo!   - validations:
55        // todo!     - there is no memory import
56        // todo!     - there is no _start function
57        // todo!   - transformations:
58        // todo!     - remove memory export
59        // todo!     - remove memory section
60        // todo! cache the compiled module in storage for later
61
62        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            // todo! test memory returns MethodNotFound
121            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}