Skip to main content

cosmwasm_vm/
instance.rs

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