cosmwasm_vm/
instance.rs

1use std::cell::RefCell;
2use std::collections::{HashMap, HashSet};
3use std::ptr::NonNull;
4use std::rc::Rc;
5use std::sync::Mutex;
6
7use wasmer::{
8    Exports, Function, FunctionEnv, Imports, Instance as WasmerInstance, Module, Store, Value,
9};
10
11use crate::backend::{Backend, BackendApi, Querier, Storage};
12use crate::capabilities::required_capabilities_from_module;
13use crate::conversion::{ref_to_u32, to_u32};
14use crate::environment::Environment;
15use crate::errors::{CommunicationError, VmError, VmResult};
16use crate::imports::{
17    do_abort, do_addr_canonicalize, do_addr_humanize, do_addr_validate, do_bls12_381_aggregate_g1,
18    do_bls12_381_aggregate_g2, do_bls12_381_hash_to_g1, do_bls12_381_hash_to_g2,
19    do_bls12_381_pairing_equality, do_db_read, do_db_remove, do_db_write, do_debug,
20    do_ed25519_batch_verify, do_ed25519_verify, do_query_chain, do_secp256k1_recover_pubkey,
21    do_secp256k1_verify, do_secp256r1_recover_pubkey, do_secp256r1_verify,
22};
23#[cfg(feature = "iterator")]
24use crate::imports::{do_db_next, do_db_next_key, do_db_next_value, do_db_scan};
25use crate::memory::{read_region, write_region};
26use crate::size::Size;
27use crate::wasm_backend::{compile, make_compiling_engine};
28
29pub use crate::environment::DebugInfo; // Re-exported as public via to be usable for set_debug_handler
30
31#[derive(Copy, Clone, Debug)]
32pub struct GasReport {
33    /// The original limit the instance was created with
34    pub limit: u64,
35    /// The remaining gas that can be spend
36    pub remaining: u64,
37    /// The amount of gas that was spend and metered externally in operations triggered by this instance
38    pub used_externally: u64,
39    /// The amount of gas that was spend and metered internally (i.e. by executing Wasm and calling
40    /// API methods which are not metered externally)
41    pub used_internally: u64,
42}
43
44#[derive(Copy, Clone, Debug)]
45pub struct InstanceOptions {
46    /// Gas limit measured in [CosmWasm gas](https://github.com/CosmWasm/cosmwasm/blob/main/docs/GAS.md).
47    pub gas_limit: u64,
48}
49
50pub struct Instance<A: BackendApi, S: Storage, Q: Querier> {
51    /// We put this instance in a box to maintain a constant memory address for the entire
52    /// lifetime of the instance in the cache. This is needed e.g. when linking the wasmer
53    /// instance to a context. See also https://github.com/CosmWasm/cosmwasm/pull/245.
54    ///
55    /// This instance should only be accessed via the Environment, which provides safe access.
56    _inner: Box<WasmerInstance>,
57    fe: FunctionEnv<Environment<A, S, Q>>,
58    store: Store,
59}
60
61impl<A, S, Q> Instance<A, S, Q>
62where
63    A: BackendApi + 'static, // 'static is needed here to allow copying API instances into closures
64    S: Storage + 'static, // 'static is needed here to allow using this in an Environment that is cloned into closures
65    Q: Querier + 'static, // 'static is needed here to allow using this in an Environment that is cloned into closures
66{
67    /// This is the only Instance constructor that can be called from outside of cosmwasm-vm,
68    /// e.g. in test code that needs a customized variant of cosmwasm_vm::testing::mock_instance*.
69    pub fn from_code(
70        code: &[u8],
71        backend: Backend<A, S, Q>,
72        options: InstanceOptions,
73        memory_limit: Option<Size>,
74    ) -> VmResult<Self> {
75        let engine = make_compiling_engine(memory_limit);
76        let module = compile(&engine, code)?;
77        let store = Store::new(engine);
78        Instance::from_module(store, &module, backend, options.gas_limit, None, None)
79    }
80
81    #[allow(clippy::too_many_arguments)]
82    pub(crate) fn from_module(
83        mut store: Store,
84        module: &Module,
85        backend: Backend<A, S, Q>,
86        gas_limit: u64,
87        extra_imports: Option<HashMap<&str, Exports>>,
88        instantiation_lock: Option<&Mutex<()>>,
89    ) -> VmResult<Self> {
90        let fe = FunctionEnv::new(&mut store, Environment::new(backend.api, gas_limit));
91
92        let mut import_obj = Imports::new();
93        let mut env_imports = Exports::new();
94
95        // Reads the database entry at the given key into the value.
96        // Returns 0 if key does not exist and pointer to result region otherwise.
97        // Ownership of the key pointer is not transferred to the host.
98        // Ownership of the value pointer is transferred to the contract.
99        env_imports.insert(
100            "db_read",
101            Function::new_typed_with_env(&mut store, &fe, do_db_read),
102        );
103
104        // Writes the given value into the database entry at the given key.
105        // Ownership of both input and output pointer is not transferred to the host.
106        env_imports.insert(
107            "db_write",
108            Function::new_typed_with_env(&mut store, &fe, do_db_write),
109        );
110
111        // Removes the value at the given key. Different than writing &[] as future
112        // scans will not find this key.
113        // At the moment it is not possible to differentiate between a key that existed before and one that did not exist (https://github.com/CosmWasm/cosmwasm/issues/290).
114        // Ownership of both key pointer is not transferred to the host.
115        env_imports.insert(
116            "db_remove",
117            Function::new_typed_with_env(&mut store, &fe, do_db_remove),
118        );
119
120        // Reads human address from source_ptr and checks if it is valid.
121        // Returns 0 on if the input is valid. Returns a non-zero memory location to a Region containing an UTF-8 encoded error string for invalid inputs.
122        // Ownership of the input pointer is not transferred to the host.
123        env_imports.insert(
124            "addr_validate",
125            Function::new_typed_with_env(&mut store, &fe, do_addr_validate),
126        );
127
128        // Reads human address from source_ptr and writes canonicalized representation to destination_ptr.
129        // A prepared and sufficiently large memory Region is expected at destination_ptr that points to pre-allocated memory.
130        // Returns 0 on success. Returns a non-zero memory location to a Region containing an UTF-8 encoded error string for invalid inputs.
131        // Ownership of both input and output pointer is not transferred to the host.
132        env_imports.insert(
133            "addr_canonicalize",
134            Function::new_typed_with_env(&mut store, &fe, do_addr_canonicalize),
135        );
136
137        // Reads canonical address from source_ptr and writes humanized representation to destination_ptr.
138        // A prepared and sufficiently large memory Region is expected at destination_ptr that points to pre-allocated memory.
139        // Returns 0 on success. Returns a non-zero memory location to a Region containing an UTF-8 encoded error string for invalid inputs.
140        // Ownership of both input and output pointer is not transferred to the host.
141        env_imports.insert(
142            "addr_humanize",
143            Function::new_typed_with_env(&mut store, &fe, do_addr_humanize),
144        );
145
146        // Reads a list of points on of the subgroup G1 on the BLS12-381 curve and aggregates them down to a single element.
147        // The "out_ptr" parameter has to be a pointer to a region with the sufficient size to fit an element of G1 (48 bytes).
148        // Returns a u32 as a result. 0 signifies success, anything else may be converted into a `CryptoError`.
149        env_imports.insert(
150            "bls12_381_aggregate_g1",
151            Function::new_typed_with_env(&mut store, &fe, do_bls12_381_aggregate_g1),
152        );
153
154        // Reads a list of points on of the subgroup G2 on the BLS12-381 curve and aggregates them down to a single element.
155        // The "out_ptr" parameter has to be a pointer to a region with the sufficient size to fit an element of G2 (96 bytes).
156        // Returns a u32 as a result. 0 signifies success, anything else may be converted into a `CryptoError`.
157        env_imports.insert(
158            "bls12_381_aggregate_g2",
159            Function::new_typed_with_env(&mut store, &fe, do_bls12_381_aggregate_g2),
160        );
161
162        // Four parameters, "ps", "qs", "r", "s", which all represent elements on the BLS12-381 curve (where "ps" and "r" are elements of the G1 subgroup, and "qs" and "s" elements of G2).
163        // The "ps" and "qs" are interpreted as a continuous list of points in the subgroups G1 and G2 respectively.
164        // Returns a single u32 which signifies the validity of the pairing equality.
165        // Returns 0 if the pairing equality exists, 1 if it doesn't, and any other code may be interpreted as a `CryptoError`.
166        env_imports.insert(
167            "bls12_381_pairing_equality",
168            Function::new_typed_with_env(&mut store, &fe, do_bls12_381_pairing_equality),
169        );
170
171        // Three parameters, "hash_function" and "msg" and "dst", are passed down which are both arbitrary octet strings.
172        // The "hash_function" parameter is interpreted as a case of the "HashFunction" enum.
173        // The "out_ptr" parameter has to be a pointer to a region with the sufficient size to fit an element of G1 (48 bytes).
174        // Returns a u32 as a result. 0 signifies success, anything else may be converted into a `CryptoError`.
175        env_imports.insert(
176            "bls12_381_hash_to_g1",
177            Function::new_typed_with_env(&mut store, &fe, do_bls12_381_hash_to_g1),
178        );
179
180        // Three parameters, "hash_function" and "msg" and "dst", are passed down which are both arbitrary octet strings.
181        // The "hash_function" parameter is interpreted as a case of the "HashFunction" enum.
182        // The "out_ptr" parameter has to be a pointer to a region with the sufficient size to fit an element of G2 (96 bytes).
183        // Returns a u32 as a result. 0 signifies success, anything else may be converted into a `CryptoError`.
184        env_imports.insert(
185            "bls12_381_hash_to_g2",
186            Function::new_typed_with_env(&mut store, &fe, do_bls12_381_hash_to_g2),
187        );
188
189        // Verifies message hashes against a signature with a public key, using the secp256k1 ECDSA parametrization.
190        // Returns 0 on verification success, 1 on verification failure, and values greater than 1 in case of error.
191        // Ownership of input pointers is not transferred to the host.
192        env_imports.insert(
193            "secp256k1_verify",
194            Function::new_typed_with_env(&mut store, &fe, do_secp256k1_verify),
195        );
196
197        env_imports.insert(
198            "secp256k1_recover_pubkey",
199            Function::new_typed_with_env(&mut store, &fe, do_secp256k1_recover_pubkey),
200        );
201
202        // Verifies message hashes against a signature with a public key, using the secp256r1 ECDSA parametrization.
203        // Returns 0 on verification success, 1 on verification failure, and values greater than 1 in case of error.
204        // Ownership of input pointers is not transferred to the host.
205        env_imports.insert(
206            "secp256r1_verify",
207            Function::new_typed_with_env(&mut store, &fe, do_secp256r1_verify),
208        );
209
210        env_imports.insert(
211            "secp256r1_recover_pubkey",
212            Function::new_typed_with_env(&mut store, &fe, do_secp256r1_recover_pubkey),
213        );
214
215        // Verifies a message against a signature with a public key, using the ed25519 EdDSA scheme.
216        // Returns 0 on verification success, 1 on verification failure, and values greater than 1 in case of error.
217        // Ownership of input pointers is not transferred to the host.
218        env_imports.insert(
219            "ed25519_verify",
220            Function::new_typed_with_env(&mut store, &fe, do_ed25519_verify),
221        );
222
223        // Verifies a batch of messages against a batch of signatures with a batch of public keys,
224        // using the ed25519 EdDSA scheme.
225        // Returns 0 on verification success (all batches verify correctly), 1 on verification failure, and values
226        // greater than 1 in case of error.
227        // Ownership of input pointers is not transferred to the host.
228        env_imports.insert(
229            "ed25519_batch_verify",
230            Function::new_typed_with_env(&mut store, &fe, do_ed25519_batch_verify),
231        );
232
233        // Allows the contract to emit debug logs that the host can either process or ignore.
234        // This is never written to chain.
235        // Takes a pointer argument of a memory region that must contain an UTF-8 encoded string.
236        // Ownership of both input and output pointer is not transferred to the host.
237        env_imports.insert(
238            "debug",
239            Function::new_typed_with_env(&mut store, &fe, do_debug),
240        );
241
242        // Aborts the contract execution with an error message provided by the contract.
243        // Takes a pointer argument of a memory region that must contain an UTF-8 encoded string.
244        // Ownership of both input and output pointer is not transferred to the host.
245        env_imports.insert(
246            "abort",
247            Function::new_typed_with_env(&mut store, &fe, do_abort),
248        );
249
250        env_imports.insert(
251            "query_chain",
252            Function::new_typed_with_env(&mut store, &fe, do_query_chain),
253        );
254
255        // Creates an iterator that will go from start to end.
256        // If start_ptr == 0, the start is unbounded.
257        // If end_ptr == 0, the end is unbounded.
258        // Order is defined in cosmwasm_std::Order and may be 1 (ascending) or 2 (descending). All other values result in an error.
259        // Ownership of both start and end pointer is not transferred to the host.
260        // Returns an iterator ID.
261        #[cfg(feature = "iterator")]
262        env_imports.insert(
263            "db_scan",
264            Function::new_typed_with_env(&mut store, &fe, do_db_scan),
265        );
266
267        // Get next element of iterator with ID `iterator_id`.
268        // Creates a region containing both key and value and returns its address.
269        // Ownership of the result region is transferred to the contract.
270        // The KV region uses the format value || key || keylen, where keylen is a fixed size big endian u32 value.
271        // An empty key (i.e. KV region ends with \0\0\0\0) means no more element, no matter what the value is.
272        #[cfg(feature = "iterator")]
273        env_imports.insert(
274            "db_next",
275            Function::new_typed_with_env(&mut store, &fe, do_db_next),
276        );
277
278        // Get next key of iterator with ID `iterator_id`.
279        // Returns 0 if there are no more entries and pointer to result region otherwise.
280        // Ownership of the result region is transferred to the contract.
281        #[cfg(feature = "iterator")]
282        env_imports.insert(
283            "db_next_key",
284            Function::new_typed_with_env(&mut store, &fe, do_db_next_key),
285        );
286
287        // Get next value of iterator with ID `iterator_id`.
288        // Returns 0 if there are no more entries and pointer to result region otherwise.
289        // Ownership of the result region is transferred to the contract.
290        #[cfg(feature = "iterator")]
291        env_imports.insert(
292            "db_next_value",
293            Function::new_typed_with_env(&mut store, &fe, do_db_next_value),
294        );
295
296        import_obj.register_namespace("env", env_imports);
297
298        if let Some(extra_imports) = extra_imports {
299            for (namespace, exports_obj) in extra_imports {
300                import_obj.register_namespace(namespace, exports_obj);
301            }
302        }
303
304        let wasmer_instance = Box::from(
305            {
306                let _lock = instantiation_lock.map(|l| l.lock().unwrap());
307                WasmerInstance::new(&mut store, module, &import_obj)
308            }
309            .map_err(|original| {
310                VmError::instantiation_err(format!("Error instantiating module: {original}"))
311            })?,
312        );
313
314        let memory = wasmer_instance
315            .exports
316            .get_memory("memory")
317            .map_err(|original| {
318                VmError::instantiation_err(format!("Could not get memory 'memory': {original}"))
319            })?
320            .clone();
321
322        let instance_ptr = NonNull::from(wasmer_instance.as_ref());
323
324        {
325            let mut fe_mut = fe.clone().into_mut(&mut store);
326            let (env, mut store) = fe_mut.data_and_store_mut();
327
328            env.memory = Some(memory);
329            env.set_wasmer_instance(Some(instance_ptr));
330            env.set_gas_left(&mut store, gas_limit);
331            env.move_in(backend.storage, backend.querier);
332        }
333
334        Ok(Instance {
335            _inner: wasmer_instance,
336            fe,
337            store,
338        })
339    }
340
341    pub fn api(&self) -> &A {
342        &self.fe.as_ref(&self.store).api
343    }
344
345    /// Decomposes this instance into its components.
346    /// External dependencies are returned for reuse, the rest is dropped.
347    #[must_use = "Calling ::recycle() without reusing the returned backend just drops the instance"]
348    pub fn recycle(self) -> Option<Backend<A, S, Q>> {
349        let Instance {
350            _inner, fe, store, ..
351        } = self;
352
353        let env = fe.as_ref(&store);
354        if let (Some(storage), Some(querier)) = env.move_out() {
355            let api = env.api.clone();
356            Some(Backend {
357                api,
358                storage,
359                querier,
360            })
361        } else {
362            None
363        }
364    }
365
366    pub fn set_debug_handler<H>(&mut self, debug_handler: H)
367    where
368        H: for<'a, 'b> FnMut(/* msg */ &'a str, DebugInfo<'b>) + 'static,
369    {
370        self.fe
371            .as_ref(&self.store)
372            .set_debug_handler(Some(Rc::new(RefCell::new(debug_handler))));
373    }
374
375    pub fn unset_debug_handler(&mut self) {
376        self.fe.as_ref(&self.store).set_debug_handler(None);
377    }
378
379    /// Returns the features required by this contract.
380    ///
381    /// This is not needed for production because we can do static analysis
382    /// on the Wasm file before instantiation to obtain this information. It's
383    /// only kept because it can be handy for integration testing.
384    pub fn required_capabilities(&self) -> HashSet<String> {
385        required_capabilities_from_module(self._inner.module())
386    }
387
388    /// Returns the size of the default memory in pages.
389    /// This provides a rough idea of the peak memory consumption. Note that
390    /// Wasm memory always grows in 64 KiB steps (pages) and can never shrink
391    /// (https://github.com/WebAssembly/design/issues/1300#issuecomment-573867836).
392    pub fn memory_pages(&mut self) -> usize {
393        let mut fe_mut = self.fe.clone().into_mut(&mut self.store);
394        let (env, store) = fe_mut.data_and_store_mut();
395
396        env.memory(&store).size().0 as _
397    }
398
399    /// Returns the currently remaining gas.
400    pub fn get_gas_left(&mut self) -> u64 {
401        let mut fe_mut = self.fe.clone().into_mut(&mut self.store);
402        let (env, mut store) = fe_mut.data_and_store_mut();
403
404        env.get_gas_left(&mut store)
405    }
406
407    /// Creates and returns a gas report.
408    /// This is a snapshot and multiple reports can be created during the lifetime of
409    /// an instance.
410    pub fn create_gas_report(&mut self) -> GasReport {
411        let mut fe_mut = self.fe.clone().into_mut(&mut self.store);
412        let (env, mut store) = fe_mut.data_and_store_mut();
413
414        let state = env.with_gas_state(|gas_state| gas_state.clone());
415        let gas_left = env.get_gas_left(&mut store);
416        GasReport {
417            limit: state.gas_limit,
418            remaining: gas_left,
419            used_externally: state.externally_used_gas,
420            // If externally_used_gas exceeds the gas limit, this will return 0.
421            // no matter how much gas was used internally. But then we error with out of gas
422            // anyways, and it does not matter much anymore where gas was spend.
423            used_internally: state
424                .gas_limit
425                .saturating_sub(state.externally_used_gas)
426                .saturating_sub(gas_left),
427        }
428    }
429
430    pub fn is_storage_readonly(&mut self) -> bool {
431        let mut fe_mut = self.fe.clone().into_mut(&mut self.store);
432        let (env, _) = fe_mut.data_and_store_mut();
433
434        env.is_storage_readonly()
435    }
436
437    /// Sets the readonly storage flag on this instance. Since one instance can be used
438    /// for multiple calls in integration tests, this should be set to the desired value
439    /// right before every call.
440    pub fn set_storage_readonly(&mut self, new_value: bool) {
441        let mut fe_mut = self.fe.clone().into_mut(&mut self.store);
442        let (env, _) = fe_mut.data_and_store_mut();
443
444        env.set_storage_readonly(new_value);
445    }
446
447    pub fn with_storage<F: FnOnce(&mut S) -> VmResult<T>, T>(&mut self, func: F) -> VmResult<T> {
448        self.fe
449            .as_ref(&self.store)
450            .with_storage_from_context::<F, T>(func)
451    }
452
453    pub fn with_querier<F: FnOnce(&mut Q) -> VmResult<T>, T>(&mut self, func: F) -> VmResult<T> {
454        self.fe
455            .as_ref(&self.store)
456            .with_querier_from_context::<F, T>(func)
457    }
458
459    /// Requests memory allocation by the instance and returns a pointer
460    /// in the Wasm address space to the created Region object.
461    pub(crate) fn allocate(&mut self, size: usize) -> VmResult<u32> {
462        let ret = self.call_function1("allocate", &[to_u32(size)?.into()])?;
463        let ptr = ref_to_u32(&ret)?;
464        if ptr == 0 {
465            return Err(CommunicationError::zero_address().into());
466        }
467        Ok(ptr)
468    }
469
470    // deallocate frees memory in the instance and that was either previously
471    // allocated by us, or a pointer from a return value after we copy it into rust.
472    // we need to clean up the wasm-side buffers to avoid memory leaks
473    pub(crate) fn deallocate(&mut self, ptr: u32) -> VmResult<()> {
474        self.call_function0("deallocate", &[ptr.into()])?;
475        Ok(())
476    }
477
478    /// Copies all data described by the Region at the given pointer from Wasm to the caller.
479    pub(crate) fn read_memory(&mut self, region_ptr: u32, max_length: usize) -> VmResult<Vec<u8>> {
480        let mut fe_mut = self.fe.clone().into_mut(&mut self.store);
481        let (env, mut store) = fe_mut.data_and_store_mut();
482
483        read_region(env, &mut store, region_ptr, max_length)
484    }
485
486    /// Copies data to the memory region that was created before using allocate.
487    pub(crate) fn write_memory(&mut self, region_ptr: u32, data: &[u8]) -> VmResult<()> {
488        let mut fe_mut = self.fe.clone().into_mut(&mut self.store);
489        let (env, mut store) = fe_mut.data_and_store_mut();
490
491        write_region(env, &mut store, region_ptr, data)?;
492        Ok(())
493    }
494
495    /// Calls a function exported by the instance.
496    /// The function is expected to return no value. Otherwise this calls errors.
497    pub(crate) fn call_function0(&mut self, name: &str, args: &[Value]) -> VmResult<()> {
498        let mut fe_mut = self.fe.clone().into_mut(&mut self.store);
499        let (env, mut store) = fe_mut.data_and_store_mut();
500
501        env.call_function0(&mut store, name, args)
502    }
503
504    /// Calls a function exported by the instance.
505    /// The function is expected to return one value. Otherwise this calls errors.
506    pub(crate) fn call_function1(&mut self, name: &str, args: &[Value]) -> VmResult<Value> {
507        let mut fe_mut = self.fe.clone().into_mut(&mut self.store);
508        let (env, mut store) = fe_mut.data_and_store_mut();
509
510        env.call_function1(&mut store, name, args)
511    }
512}
513
514/// This exists only to be exported through `internals` for use by crates that are
515/// part of Cosmwasm.
516pub fn instance_from_module<A, S, Q>(
517    store: Store,
518    module: &Module,
519    backend: Backend<A, S, Q>,
520    gas_limit: u64,
521    extra_imports: Option<HashMap<&str, Exports>>,
522) -> VmResult<Instance<A, S, Q>>
523where
524    A: BackendApi + 'static, // 'static is needed here to allow copying API instances into closures
525    S: Storage + 'static, // 'static is needed here to allow using this in an Environment that is cloned into closures
526    Q: Querier + 'static,
527{
528    Instance::from_module(store, module, backend, gas_limit, extra_imports, None)
529}
530
531#[cfg(test)]
532mod tests {
533    use std::sync::atomic::{AtomicBool, Ordering};
534    use std::sync::Arc;
535    use std::time::SystemTime;
536
537    use super::*;
538    use crate::calls::{call_execute, call_instantiate, call_query};
539    use crate::testing::{
540        mock_backend, mock_env, mock_info, mock_instance, mock_instance_options,
541        mock_instance_with_balances, mock_instance_with_failing_api, mock_instance_with_gas_limit,
542        mock_instance_with_options, MockInstanceOptions,
543    };
544    use cosmwasm_std::{
545        coin, coins, from_json, BalanceResponse, BankQuery, Empty, QueryRequest, Uint256,
546    };
547    use wasmer::FunctionEnvMut;
548
549    const KIB: usize = 1024;
550    const MIB: usize = 1024 * 1024;
551    const DEFAULT_QUERY_GAS_LIMIT: u64 = 300_000;
552    static HACKATOM: &[u8] = include_bytes!("../testdata/hackatom.wasm");
553    static HACKATOM_1_3: &[u8] = include_bytes!("../testdata/hackatom_1.3.wasm");
554    static CYBERPUNK: &[u8] = include_bytes!("../testdata/cyberpunk.wasm");
555
556    #[test]
557    fn from_code_works() {
558        let backend = mock_backend(&[]);
559        let (instance_options, memory_limit) = mock_instance_options();
560        let _instance =
561            Instance::from_code(HACKATOM, backend, instance_options, memory_limit).unwrap();
562    }
563
564    #[test]
565    fn set_debug_handler_and_unset_debug_handler_work() {
566        const LIMIT: u64 = 70_000_000_000;
567        let mut instance = mock_instance_with_gas_limit(CYBERPUNK, LIMIT);
568
569        // init contract
570        let info = mock_info("creator", &coins(1000, "earth"));
571        call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, br#"{}"#)
572            .unwrap()
573            .unwrap();
574
575        let info = mock_info("caller", &[]);
576        call_execute::<_, _, _, Empty>(&mut instance, &mock_env(), &info, br#"{"debug":{}}"#)
577            .unwrap()
578            .unwrap();
579
580        let start = SystemTime::now();
581        instance.set_debug_handler(move |msg, info| {
582            let gas = info.gas_remaining;
583            let runtime = SystemTime::now().duration_since(start).unwrap().as_micros();
584            eprintln!("{msg} (gas: {gas}, runtime: {runtime}µs)");
585        });
586
587        let info = mock_info("caller", &[]);
588        call_execute::<_, _, _, Empty>(&mut instance, &mock_env(), &info, br#"{"debug":{}}"#)
589            .unwrap()
590            .unwrap();
591
592        eprintln!("Unsetting debug handler. From here nothing is printed anymore.");
593        instance.unset_debug_handler();
594
595        let info = mock_info("caller", &[]);
596        call_execute::<_, _, _, Empty>(&mut instance, &mock_env(), &info, br#"{"debug":{}}"#)
597            .unwrap()
598            .unwrap();
599    }
600
601    #[test]
602    fn required_capabilities_works() {
603        let backend = mock_backend(&[]);
604        let (instance_options, memory_limit) = mock_instance_options();
605        let instance =
606            Instance::from_code(HACKATOM_1_3, backend, instance_options, memory_limit).unwrap();
607        assert_eq!(instance.required_capabilities().len(), 0);
608
609        let backend = mock_backend(&[]);
610        let (instance_options, memory_limit) = mock_instance_options();
611        let instance =
612            Instance::from_code(HACKATOM, backend, instance_options, memory_limit).unwrap();
613        assert_eq!(instance.required_capabilities().len(), 7);
614    }
615
616    #[test]
617    fn required_capabilities_works_for_many_exports() {
618        let wasm = wat::parse_str(
619            r#"(module
620            (memory 3)
621            (export "memory" (memory 0))
622
623            (type (func))
624            (func (type 0) nop)
625            (export "requires_water" (func 0))
626            (export "requires_" (func 0))
627            (export "requires_nutrients" (func 0))
628            (export "require_milk" (func 0))
629            (export "REQUIRES_air" (func 0))
630            (export "requires_sun" (func 0))
631            )"#,
632        )
633        .unwrap();
634
635        let backend = mock_backend(&[]);
636        let (instance_options, memory_limit) = mock_instance_options();
637        let instance = Instance::from_code(&wasm, backend, instance_options, memory_limit).unwrap();
638        assert_eq!(instance.required_capabilities().len(), 3);
639        assert!(instance.required_capabilities().contains("nutrients"));
640        assert!(instance.required_capabilities().contains("sun"));
641        assert!(instance.required_capabilities().contains("water"));
642    }
643
644    #[test]
645    fn extra_imports_get_added() {
646        let (instance_options, memory_limit) = mock_instance_options();
647
648        let wasm = wat::parse_str(
649            r#"(module
650            (import "foo" "bar" (func $bar))
651            (memory 3)
652            (export "memory" (memory 0))
653            (func (export "main") (call $bar))
654            )"#,
655        )
656        .unwrap();
657
658        let backend = mock_backend(&[]);
659        let engine = make_compiling_engine(memory_limit);
660        let module = compile(&engine, &wasm).unwrap();
661        let mut store = Store::new(engine);
662
663        let called = Arc::new(AtomicBool::new(false));
664
665        #[derive(Clone)]
666        struct MyEnv {
667            // This can be mutated across threads safely. We initialize it as `false`
668            // and let our imported fn switch it to `true` to confirm it works.
669            called: Arc<AtomicBool>,
670        }
671
672        let fe = FunctionEnv::new(
673            &mut store,
674            MyEnv {
675                called: called.clone(),
676            },
677        );
678
679        let fun =
680            Function::new_typed_with_env(&mut store, &fe, move |fe_mut: FunctionEnvMut<MyEnv>| {
681                fe_mut.data().called.store(true, Ordering::Relaxed);
682            });
683        let mut exports = Exports::new();
684        exports.insert("bar", fun);
685        let mut extra_imports = HashMap::new();
686        extra_imports.insert("foo", exports);
687        let mut instance = Instance::from_module(
688            store,
689            &module,
690            backend,
691            instance_options.gas_limit,
692            Some(extra_imports),
693            None,
694        )
695        .unwrap();
696
697        instance.call_function0("main", &[]).unwrap();
698
699        assert!(called.load(Ordering::Relaxed));
700    }
701
702    #[test]
703    fn call_function0_works() {
704        let mut instance = mock_instance(HACKATOM, &[]);
705
706        instance
707            .call_function0("interface_version_8", &[])
708            .expect("error calling function");
709    }
710
711    #[test]
712    fn call_function1_works() {
713        let mut instance = mock_instance(HACKATOM, &[]);
714
715        // can call function few times
716        let result = instance
717            .call_function1("allocate", &[0u32.into()])
718            .expect("error calling allocate");
719        assert_ne!(result.unwrap_i32(), 0);
720
721        let result = instance
722            .call_function1("allocate", &[1u32.into()])
723            .expect("error calling allocate");
724        assert_ne!(result.unwrap_i32(), 0);
725
726        let result = instance
727            .call_function1("allocate", &[33u32.into()])
728            .expect("error calling allocate");
729        assert_ne!(result.unwrap_i32(), 0);
730    }
731
732    #[test]
733    fn allocate_deallocate_works() {
734        let mut instance = mock_instance_with_options(
735            HACKATOM,
736            MockInstanceOptions {
737                memory_limit: Some(Size::mebi(500)),
738                ..Default::default()
739            },
740        );
741
742        let sizes: Vec<usize> = vec![
743            0,
744            4,
745            40,
746            400,
747            4 * KIB,
748            40 * KIB,
749            400 * KIB,
750            4 * MIB,
751            40 * MIB,
752            400 * MIB,
753        ];
754        for size in sizes.into_iter() {
755            let region_ptr = instance.allocate(size).expect("error allocating");
756            instance.deallocate(region_ptr).expect("error deallocating");
757        }
758    }
759
760    #[test]
761    fn write_and_read_memory_works() {
762        let mut instance = mock_instance_with_gas_limit(HACKATOM, 6_000_000_000);
763
764        let sizes: Vec<usize> = vec![
765            0,
766            4,
767            40,
768            400,
769            4 * KIB,
770            40 * KIB,
771            400 * KIB,
772            4 * MIB,
773            // disabled for performance reasons, but pass as well (with much more gas)
774            // 40 * MIB,
775            // 400 * MIB,
776        ];
777        for size in sizes.into_iter() {
778            let region_ptr = instance.allocate(size).expect("error allocating");
779            let original = vec![170u8; size];
780            instance
781                .write_memory(region_ptr, &original)
782                .expect("error writing");
783            let data = instance
784                .read_memory(region_ptr, size)
785                .expect("error reading");
786            assert_eq!(data, original);
787            instance.deallocate(region_ptr).expect("error deallocating");
788        }
789    }
790
791    #[test]
792    fn errors_in_imports() {
793        // set up an instance that will experience an error in an import
794        let error_message = "Api failed intentionally";
795        let mut instance = mock_instance_with_failing_api(HACKATOM, &[], error_message);
796        let init_result = call_instantiate::<_, _, _, Empty>(
797            &mut instance,
798            &mock_env(),
799            &mock_info("someone", &[]),
800            b"{\"verifier\": \"some1\", \"beneficiary\": \"some2\"}",
801        );
802
803        match init_result.unwrap_err() {
804            VmError::RuntimeErr { msg, .. } => assert!(msg.contains(error_message)),
805            err => panic!("Unexpected error: {err:?}"),
806        }
807    }
808
809    #[test]
810    fn read_memory_errors_when_when_length_is_too_long() {
811        let length = 6;
812        let max_length = 5;
813        let mut instance = mock_instance(HACKATOM, &[]);
814
815        // Allocate sets length to 0. Write some data to increase length.
816        let region_ptr = instance.allocate(length).expect("error allocating");
817        let data = vec![170u8; length];
818        instance
819            .write_memory(region_ptr, &data)
820            .expect("error writing");
821
822        let result = instance.read_memory(region_ptr, max_length);
823        match result.unwrap_err() {
824            VmError::CommunicationErr {
825                source:
826                    CommunicationError::RegionLengthTooBig {
827                        length, max_length, ..
828                    },
829                ..
830            } => {
831                assert_eq!(length, 6);
832                assert_eq!(max_length, 5);
833            }
834            err => panic!("unexpected error: {err:?}"),
835        };
836
837        instance.deallocate(region_ptr).expect("error deallocating");
838    }
839
840    #[test]
841    fn memory_pages_returns_min_memory_size_by_default() {
842        // min: 0 pages, max: none
843        let wasm = wat::parse_str(
844            r#"(module
845                (memory 0)
846                (export "memory" (memory 0))
847
848                (type (func))
849                (func (type 0) nop)
850                (export "interface_version_8" (func 0))
851                (export "instantiate" (func 0))
852                (export "allocate" (func 0))
853                (export "deallocate" (func 0))
854            )"#,
855        )
856        .unwrap();
857        let mut instance = mock_instance(&wasm, &[]);
858        assert_eq!(instance.memory_pages(), 0);
859
860        // min: 3 pages, max: none
861        let wasm = wat::parse_str(
862            r#"(module
863                (memory 3)
864                (export "memory" (memory 0))
865
866                (type (func))
867                (func (type 0) nop)
868                (export "interface_version_8" (func 0))
869                (export "instantiate" (func 0))
870                (export "allocate" (func 0))
871                (export "deallocate" (func 0))
872            )"#,
873        )
874        .unwrap();
875        let mut instance = mock_instance(&wasm, &[]);
876        assert_eq!(instance.memory_pages(), 3);
877    }
878
879    #[test]
880    fn memory_pages_grows_with_usage() {
881        let mut instance = mock_instance(HACKATOM, &[]);
882
883        assert_eq!(instance.memory_pages(), 17);
884
885        // 100 KiB require two more pages
886        let region_ptr = instance.allocate(100 * 1024).expect("error allocating");
887
888        assert_eq!(instance.memory_pages(), 19);
889
890        // Deallocating does not shrink memory
891        instance.deallocate(region_ptr).expect("error deallocating");
892        assert_eq!(instance.memory_pages(), 19);
893    }
894
895    #[test]
896    fn get_gas_left_works() {
897        let mut instance = mock_instance_with_gas_limit(HACKATOM, 123321);
898        let orig_gas = instance.get_gas_left();
899        assert_eq!(orig_gas, 123321);
900    }
901
902    #[test]
903    fn create_gas_report_works() {
904        const LIMIT: u64 = 700_000_000;
905        let mut instance = mock_instance_with_gas_limit(HACKATOM, LIMIT);
906
907        let report1 = instance.create_gas_report();
908        assert_eq!(report1.used_externally, 0);
909        assert_eq!(report1.used_internally, 0);
910        assert_eq!(report1.limit, LIMIT);
911        assert_eq!(report1.remaining, LIMIT);
912
913        // init contract
914        let info = mock_info(&instance.api().addr_make("creator"), &coins(1000, "earth"));
915        let verifier = instance.api().addr_make("verifies");
916        let beneficiary = instance.api().addr_make("benefits");
917        let msg = format!(r#"{{"verifier": "{verifier}", "beneficiary": "{beneficiary}"}}"#);
918        call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg.as_bytes())
919            .unwrap()
920            .unwrap();
921
922        let report2 = instance.create_gas_report();
923        assert_eq!(report2.used_externally, 251);
924        assert_eq!(report2.used_internally, 18034325);
925        assert_eq!(report2.limit, LIMIT);
926        assert_eq!(
927            report2.remaining,
928            LIMIT - report2.used_externally - report2.used_internally
929        );
930    }
931
932    #[test]
933    fn set_storage_readonly_works() {
934        let mut instance = mock_instance(HACKATOM, &[]);
935
936        assert!(instance.is_storage_readonly());
937
938        instance.set_storage_readonly(false);
939        assert!(!instance.is_storage_readonly());
940
941        instance.set_storage_readonly(false);
942        assert!(!instance.is_storage_readonly());
943
944        instance.set_storage_readonly(true);
945        assert!(instance.is_storage_readonly());
946    }
947
948    #[test]
949    fn with_storage_works() {
950        let mut instance = mock_instance(HACKATOM, &[]);
951
952        // initial check
953        instance
954            .with_storage(|store| {
955                assert!(store.get(b"foo").0.unwrap().is_none());
956                Ok(())
957            })
958            .unwrap();
959
960        // write some data
961        instance
962            .with_storage(|store| {
963                store.set(b"foo", b"bar").0.unwrap();
964                Ok(())
965            })
966            .unwrap();
967
968        // read some data
969        instance
970            .with_storage(|store| {
971                assert_eq!(store.get(b"foo").0.unwrap(), Some(b"bar".to_vec()));
972                Ok(())
973            })
974            .unwrap();
975    }
976
977    #[test]
978    #[should_panic]
979    fn with_storage_safe_for_panic() {
980        // this should fail with the assertion, but not cause a double-free crash (issue #59)
981        let mut instance = mock_instance(HACKATOM, &[]);
982        instance
983            .with_storage::<_, ()>(|_store| panic!("trigger failure"))
984            .unwrap();
985    }
986
987    #[test]
988    #[allow(deprecated)]
989    fn with_querier_works_readonly() {
990        let rich_addr = String::from("foobar");
991        let rich_balance = vec![coin(10000, "gold"), coin(8000, "silver")];
992        let mut instance = mock_instance_with_balances(HACKATOM, &[(&rich_addr, &rich_balance)]);
993
994        // query one
995        instance
996            .with_querier(|querier| {
997                let response = querier
998                    .query::<Empty>(
999                        &QueryRequest::Bank(BankQuery::Balance {
1000                            address: rich_addr.clone(),
1001                            denom: "silver".to_string(),
1002                        }),
1003                        DEFAULT_QUERY_GAS_LIMIT,
1004                    )
1005                    .0
1006                    .unwrap()
1007                    .unwrap()
1008                    .unwrap();
1009                let BalanceResponse { amount, .. } = from_json(response).unwrap();
1010                assert_eq!(amount.amount, Uint256::new(8000));
1011                assert_eq!(amount.denom, "silver");
1012                Ok(())
1013            })
1014            .unwrap();
1015    }
1016
1017    /// This is needed for writing integration tests in which the balance of a contract changes over time.
1018    #[test]
1019    fn with_querier_allows_updating_balances() {
1020        let rich_addr = String::from("foobar");
1021        let rich_balance1 = vec![coin(10000, "gold"), coin(500, "silver")];
1022        let rich_balance2 = vec![coin(10000, "gold"), coin(8000, "silver")];
1023        let mut instance = mock_instance_with_balances(HACKATOM, &[(&rich_addr, &rich_balance1)]);
1024
1025        // Get initial state
1026        instance
1027            .with_querier(|querier| {
1028                let response = querier
1029                    .query::<Empty>(
1030                        &QueryRequest::Bank(BankQuery::Balance {
1031                            address: rich_addr.clone(),
1032                            denom: "silver".to_string(),
1033                        }),
1034                        DEFAULT_QUERY_GAS_LIMIT,
1035                    )
1036                    .0
1037                    .unwrap()
1038                    .unwrap()
1039                    .unwrap();
1040                let BalanceResponse { amount, .. } = from_json(response).unwrap();
1041                assert_eq!(amount.amount, Uint256::new(500));
1042                Ok(())
1043            })
1044            .unwrap();
1045
1046        // Update balance
1047        instance
1048            .with_querier(|querier| {
1049                querier.update_balance(&rich_addr, rich_balance2);
1050                Ok(())
1051            })
1052            .unwrap();
1053
1054        // Get updated state
1055        instance
1056            .with_querier(|querier| {
1057                let response = querier
1058                    .query::<Empty>(
1059                        &QueryRequest::Bank(BankQuery::Balance {
1060                            address: rich_addr.clone(),
1061                            denom: "silver".to_string(),
1062                        }),
1063                        DEFAULT_QUERY_GAS_LIMIT,
1064                    )
1065                    .0
1066                    .unwrap()
1067                    .unwrap()
1068                    .unwrap();
1069                let BalanceResponse { amount, .. } = from_json(response).unwrap();
1070                assert_eq!(amount.amount, Uint256::new(8000));
1071                Ok(())
1072            })
1073            .unwrap();
1074    }
1075
1076    #[test]
1077    fn contract_deducts_gas_init() {
1078        let mut instance = mock_instance(HACKATOM, &[]);
1079        let orig_gas = instance.get_gas_left();
1080
1081        // init contract
1082        let info = mock_info(&instance.api().addr_make("creator"), &coins(1000, "earth"));
1083        let verifier = instance.api().addr_make("verifies");
1084        let beneficiary = instance.api().addr_make("benefits");
1085        let msg = format!(r#"{{"verifier": "{verifier}", "beneficiary": "{beneficiary}"}}"#);
1086        call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg.as_bytes())
1087            .unwrap()
1088            .unwrap();
1089
1090        let init_used = orig_gas - instance.get_gas_left();
1091        assert_eq!(init_used, 18034576);
1092    }
1093
1094    #[test]
1095    fn contract_deducts_gas_execute() {
1096        let mut instance = mock_instance(HACKATOM, &[]);
1097
1098        // init contract
1099        let info = mock_info(&instance.api().addr_make("creator"), &coins(1000, "earth"));
1100        let verifier = instance.api().addr_make("verifies");
1101        let beneficiary = instance.api().addr_make("benefits");
1102        let msg = format!(r#"{{"verifier": "{verifier}", "beneficiary": "{beneficiary}"}}"#);
1103        call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg.as_bytes())
1104            .unwrap()
1105            .unwrap();
1106
1107        // run contract - just sanity check - results validate in contract unit tests
1108        let gas_before_execute = instance.get_gas_left();
1109        let info = mock_info(&verifier, &coins(15, "earth"));
1110        let msg = br#"{"release":{"denom":"earth"}}"#;
1111        call_execute::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg)
1112            .unwrap()
1113            .unwrap();
1114
1115        let execute_used = gas_before_execute - instance.get_gas_left();
1116        assert_eq!(execute_used, 24624251);
1117    }
1118
1119    #[test]
1120    fn contract_enforces_gas_limit() {
1121        let mut instance = mock_instance_with_gas_limit(HACKATOM, 20_000);
1122
1123        // init contract
1124        let info = mock_info(&instance.api().addr_make("creator"), &coins(1000, "earth"));
1125        let verifier = instance.api().addr_make("verifies");
1126        let beneficiary = instance.api().addr_make("benefits");
1127        let msg = format!(r#"{{"verifier": "{verifier}", "beneficiary": "{beneficiary}"}}"#);
1128        let res =
1129            call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg.as_bytes());
1130        assert!(res.is_err());
1131    }
1132
1133    #[test]
1134    fn query_works_with_gas_metering() {
1135        let mut instance = mock_instance(HACKATOM, &[]);
1136
1137        // init contract
1138        let info = mock_info(&instance.api().addr_make("creator"), &coins(1000, "earth"));
1139        let verifier = instance.api().addr_make("verifies");
1140        let beneficiary = instance.api().addr_make("benefits");
1141        let msg = format!(r#"{{"verifier": "{verifier}", "beneficiary": "{beneficiary}"}}"#);
1142        let _res =
1143            call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg.as_bytes())
1144                .unwrap()
1145                .unwrap();
1146
1147        // run contract - just sanity check - results validate in contract unit tests
1148        let gas_before_query = instance.get_gas_left();
1149        // we need to encode the key in base64
1150        let msg = br#"{"verifier":{}}"#;
1151        let res = call_query(&mut instance, &mock_env(), msg).unwrap();
1152        let answer = res.unwrap();
1153        assert_eq!(
1154            answer.as_slice(),
1155            format!("{{\"verifier\":\"{verifier}\"}}").as_bytes()
1156        );
1157
1158        let query_used = gas_before_query - instance.get_gas_left();
1159        assert_eq!(query_used, 11105221);
1160    }
1161}