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 doesnt, 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, store) = fe_mut.data_and_store_mut();
490
491        write_region(&env.memory(&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, AllBalanceResponse, BalanceResponse, BankQuery, Empty, QueryRequest,
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 CONTRACT: &[u8] = include_bytes!("../testdata/hackatom.wasm");
553    static CYBERPUNK: &[u8] = include_bytes!("../testdata/cyberpunk.wasm");
554
555    #[test]
556    fn from_code_works() {
557        let backend = mock_backend(&[]);
558        let (instance_options, memory_limit) = mock_instance_options();
559        let _instance =
560            Instance::from_code(CONTRACT, backend, instance_options, memory_limit).unwrap();
561    }
562
563    #[test]
564    fn set_debug_handler_and_unset_debug_handler_work() {
565        const LIMIT: u64 = 70_000_000_000;
566        let mut instance = mock_instance_with_gas_limit(CYBERPUNK, LIMIT);
567
568        // init contract
569        let info = mock_info("creator", &coins(1000, "earth"));
570        call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, br#"{}"#)
571            .unwrap()
572            .unwrap();
573
574        let info = mock_info("caller", &[]);
575        call_execute::<_, _, _, Empty>(&mut instance, &mock_env(), &info, br#"{"debug":{}}"#)
576            .unwrap()
577            .unwrap();
578
579        let start = SystemTime::now();
580        instance.set_debug_handler(move |msg, info| {
581            let gas = info.gas_remaining;
582            let runtime = SystemTime::now().duration_since(start).unwrap().as_micros();
583            eprintln!("{msg} (gas: {gas}, runtime: {runtime}µs)");
584        });
585
586        let info = mock_info("caller", &[]);
587        call_execute::<_, _, _, Empty>(&mut instance, &mock_env(), &info, br#"{"debug":{}}"#)
588            .unwrap()
589            .unwrap();
590
591        eprintln!("Unsetting debug handler. From here nothing is printed anymore.");
592        instance.unset_debug_handler();
593
594        let info = mock_info("caller", &[]);
595        call_execute::<_, _, _, Empty>(&mut instance, &mock_env(), &info, br#"{"debug":{}}"#)
596            .unwrap()
597            .unwrap();
598    }
599
600    #[test]
601    fn required_capabilities_works() {
602        let backend = mock_backend(&[]);
603        let (instance_options, memory_limit) = mock_instance_options();
604        let instance =
605            Instance::from_code(CONTRACT, backend, instance_options, memory_limit).unwrap();
606        assert_eq!(instance.required_capabilities().len(), 0);
607    }
608
609    #[test]
610    fn required_capabilities_works_for_many_exports() {
611        let wasm = wat::parse_str(
612            r#"(module
613            (memory 3)
614            (export "memory" (memory 0))
615
616            (type (func))
617            (func (type 0) nop)
618            (export "requires_water" (func 0))
619            (export "requires_" (func 0))
620            (export "requires_nutrients" (func 0))
621            (export "require_milk" (func 0))
622            (export "REQUIRES_air" (func 0))
623            (export "requires_sun" (func 0))
624            )"#,
625        )
626        .unwrap();
627
628        let backend = mock_backend(&[]);
629        let (instance_options, memory_limit) = mock_instance_options();
630        let instance = Instance::from_code(&wasm, backend, instance_options, memory_limit).unwrap();
631        assert_eq!(instance.required_capabilities().len(), 3);
632        assert!(instance.required_capabilities().contains("nutrients"));
633        assert!(instance.required_capabilities().contains("sun"));
634        assert!(instance.required_capabilities().contains("water"));
635    }
636
637    #[test]
638    fn extra_imports_get_added() {
639        let (instance_options, memory_limit) = mock_instance_options();
640
641        let wasm = wat::parse_str(
642            r#"(module
643            (import "foo" "bar" (func $bar))
644            (memory 3)
645            (export "memory" (memory 0))
646            (func (export "main") (call $bar))
647            )"#,
648        )
649        .unwrap();
650
651        let backend = mock_backend(&[]);
652        let engine = make_compiling_engine(memory_limit);
653        let module = compile(&engine, &wasm).unwrap();
654        let mut store = Store::new(engine);
655
656        let called = Arc::new(AtomicBool::new(false));
657
658        #[derive(Clone)]
659        struct MyEnv {
660            // This can be mutated across threads safely. We initialize it as `false`
661            // and let our imported fn switch it to `true` to confirm it works.
662            called: Arc<AtomicBool>,
663        }
664
665        let fe = FunctionEnv::new(
666            &mut store,
667            MyEnv {
668                called: called.clone(),
669            },
670        );
671
672        let fun =
673            Function::new_typed_with_env(&mut store, &fe, move |fe_mut: FunctionEnvMut<MyEnv>| {
674                fe_mut.data().called.store(true, Ordering::Relaxed);
675            });
676        let mut exports = Exports::new();
677        exports.insert("bar", fun);
678        let mut extra_imports = HashMap::new();
679        extra_imports.insert("foo", exports);
680        let mut instance = Instance::from_module(
681            store,
682            &module,
683            backend,
684            instance_options.gas_limit,
685            Some(extra_imports),
686            None,
687        )
688        .unwrap();
689
690        instance.call_function0("main", &[]).unwrap();
691
692        assert!(called.load(Ordering::Relaxed));
693    }
694
695    #[test]
696    fn call_function0_works() {
697        let mut instance = mock_instance(CONTRACT, &[]);
698
699        instance
700            .call_function0("interface_version_8", &[])
701            .expect("error calling function");
702    }
703
704    #[test]
705    fn call_function1_works() {
706        let mut instance = mock_instance(CONTRACT, &[]);
707
708        // can call function few times
709        let result = instance
710            .call_function1("allocate", &[0u32.into()])
711            .expect("error calling allocate");
712        assert_ne!(result.unwrap_i32(), 0);
713
714        let result = instance
715            .call_function1("allocate", &[1u32.into()])
716            .expect("error calling allocate");
717        assert_ne!(result.unwrap_i32(), 0);
718
719        let result = instance
720            .call_function1("allocate", &[33u32.into()])
721            .expect("error calling allocate");
722        assert_ne!(result.unwrap_i32(), 0);
723    }
724
725    #[test]
726    fn allocate_deallocate_works() {
727        let mut instance = mock_instance_with_options(
728            CONTRACT,
729            MockInstanceOptions {
730                memory_limit: Some(Size::mebi(500)),
731                ..Default::default()
732            },
733        );
734
735        let sizes: Vec<usize> = vec![
736            0,
737            4,
738            40,
739            400,
740            4 * KIB,
741            40 * KIB,
742            400 * KIB,
743            4 * MIB,
744            40 * MIB,
745            400 * MIB,
746        ];
747        for size in sizes.into_iter() {
748            let region_ptr = instance.allocate(size).expect("error allocating");
749            instance.deallocate(region_ptr).expect("error deallocating");
750        }
751    }
752
753    #[test]
754    fn write_and_read_memory_works() {
755        let mut instance = mock_instance(CONTRACT, &[]);
756
757        let sizes: Vec<usize> = vec![
758            0,
759            4,
760            40,
761            400,
762            4 * KIB,
763            40 * KIB,
764            400 * KIB,
765            4 * MIB,
766            // disabled for performance reasons, but pass as well
767            // 40 * MIB,
768            // 400 * MIB,
769        ];
770        for size in sizes.into_iter() {
771            let region_ptr = instance.allocate(size).expect("error allocating");
772            let original = vec![170u8; size];
773            instance
774                .write_memory(region_ptr, &original)
775                .expect("error writing");
776            let data = instance
777                .read_memory(region_ptr, size)
778                .expect("error reading");
779            assert_eq!(data, original);
780            instance.deallocate(region_ptr).expect("error deallocating");
781        }
782    }
783
784    #[test]
785    fn errors_in_imports() {
786        // set up an instance that will experience an error in an import
787        let error_message = "Api failed intentionally";
788        let mut instance = mock_instance_with_failing_api(CONTRACT, &[], error_message);
789        let init_result = call_instantiate::<_, _, _, Empty>(
790            &mut instance,
791            &mock_env(),
792            &mock_info("someone", &[]),
793            b"{\"verifier\": \"some1\", \"beneficiary\": \"some2\"}",
794        );
795
796        match init_result.unwrap_err() {
797            VmError::RuntimeErr { msg, .. } => assert!(msg.contains(error_message)),
798            err => panic!("Unexpected error: {err:?}"),
799        }
800    }
801
802    #[test]
803    fn read_memory_errors_when_when_length_is_too_long() {
804        let length = 6;
805        let max_length = 5;
806        let mut instance = mock_instance(CONTRACT, &[]);
807
808        // Allocate sets length to 0. Write some data to increase length.
809        let region_ptr = instance.allocate(length).expect("error allocating");
810        let data = vec![170u8; length];
811        instance
812            .write_memory(region_ptr, &data)
813            .expect("error writing");
814
815        let result = instance.read_memory(region_ptr, max_length);
816        match result.unwrap_err() {
817            VmError::CommunicationErr {
818                source:
819                    CommunicationError::RegionLengthTooBig {
820                        length, max_length, ..
821                    },
822                ..
823            } => {
824                assert_eq!(length, 6);
825                assert_eq!(max_length, 5);
826            }
827            err => panic!("unexpected error: {err:?}"),
828        };
829
830        instance.deallocate(region_ptr).expect("error deallocating");
831    }
832
833    #[test]
834    fn memory_pages_returns_min_memory_size_by_default() {
835        // min: 0 pages, max: none
836        let wasm = wat::parse_str(
837            r#"(module
838                (memory 0)
839                (export "memory" (memory 0))
840
841                (type (func))
842                (func (type 0) nop)
843                (export "interface_version_8" (func 0))
844                (export "instantiate" (func 0))
845                (export "allocate" (func 0))
846                (export "deallocate" (func 0))
847            )"#,
848        )
849        .unwrap();
850        let mut instance = mock_instance(&wasm, &[]);
851        assert_eq!(instance.memory_pages(), 0);
852
853        // min: 3 pages, max: none
854        let wasm = wat::parse_str(
855            r#"(module
856                (memory 3)
857                (export "memory" (memory 0))
858
859                (type (func))
860                (func (type 0) nop)
861                (export "interface_version_8" (func 0))
862                (export "instantiate" (func 0))
863                (export "allocate" (func 0))
864                (export "deallocate" (func 0))
865            )"#,
866        )
867        .unwrap();
868        let mut instance = mock_instance(&wasm, &[]);
869        assert_eq!(instance.memory_pages(), 3);
870    }
871
872    #[test]
873    fn memory_pages_grows_with_usage() {
874        let mut instance = mock_instance(CONTRACT, &[]);
875
876        assert_eq!(instance.memory_pages(), 17);
877
878        // 100 KiB require two more pages
879        let region_ptr = instance.allocate(100 * 1024).expect("error allocating");
880
881        assert_eq!(instance.memory_pages(), 19);
882
883        // Deallocating does not shrink memory
884        instance.deallocate(region_ptr).expect("error deallocating");
885        assert_eq!(instance.memory_pages(), 19);
886    }
887
888    #[test]
889    fn get_gas_left_works() {
890        let mut instance = mock_instance_with_gas_limit(CONTRACT, 123321);
891        let orig_gas = instance.get_gas_left();
892        assert_eq!(orig_gas, 123321);
893    }
894
895    #[test]
896    fn create_gas_report_works() {
897        const LIMIT: u64 = 700_000_000;
898        let mut instance = mock_instance_with_gas_limit(CONTRACT, LIMIT);
899
900        let report1 = instance.create_gas_report();
901        assert_eq!(report1.used_externally, 0);
902        assert_eq!(report1.used_internally, 0);
903        assert_eq!(report1.limit, LIMIT);
904        assert_eq!(report1.remaining, LIMIT);
905
906        // init contract
907        let info = mock_info(&instance.api().addr_make("creator"), &coins(1000, "earth"));
908        let verifier = instance.api().addr_make("verifies");
909        let beneficiary = instance.api().addr_make("benefits");
910        let msg = format!(r#"{{"verifier": "{verifier}", "beneficiary": "{beneficiary}"}}"#);
911        call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg.as_bytes())
912            .unwrap()
913            .unwrap();
914
915        let report2 = instance.create_gas_report();
916        assert_eq!(report2.used_externally, 251);
917        assert_eq!(report2.used_internally, 23164835);
918        assert_eq!(report2.limit, LIMIT);
919        assert_eq!(
920            report2.remaining,
921            LIMIT - report2.used_externally - report2.used_internally
922        );
923    }
924
925    #[test]
926    fn set_storage_readonly_works() {
927        let mut instance = mock_instance(CONTRACT, &[]);
928
929        assert!(instance.is_storage_readonly());
930
931        instance.set_storage_readonly(false);
932        assert!(!instance.is_storage_readonly());
933
934        instance.set_storage_readonly(false);
935        assert!(!instance.is_storage_readonly());
936
937        instance.set_storage_readonly(true);
938        assert!(instance.is_storage_readonly());
939    }
940
941    #[test]
942    fn with_storage_works() {
943        let mut instance = mock_instance(CONTRACT, &[]);
944
945        // initial check
946        instance
947            .with_storage(|store| {
948                assert!(store.get(b"foo").0.unwrap().is_none());
949                Ok(())
950            })
951            .unwrap();
952
953        // write some data
954        instance
955            .with_storage(|store| {
956                store.set(b"foo", b"bar").0.unwrap();
957                Ok(())
958            })
959            .unwrap();
960
961        // read some data
962        instance
963            .with_storage(|store| {
964                assert_eq!(store.get(b"foo").0.unwrap(), Some(b"bar".to_vec()));
965                Ok(())
966            })
967            .unwrap();
968    }
969
970    #[test]
971    #[should_panic]
972    fn with_storage_safe_for_panic() {
973        // this should fail with the assertion, but not cause a double-free crash (issue #59)
974        let mut instance = mock_instance(CONTRACT, &[]);
975        instance
976            .with_storage::<_, ()>(|_store| panic!("trigger failure"))
977            .unwrap();
978    }
979
980    #[test]
981    #[allow(deprecated)]
982    fn with_querier_works_readonly() {
983        let rich_addr = String::from("foobar");
984        let rich_balance = vec![coin(10000, "gold"), coin(8000, "silver")];
985        let mut instance = mock_instance_with_balances(CONTRACT, &[(&rich_addr, &rich_balance)]);
986
987        // query one
988        instance
989            .with_querier(|querier| {
990                let response = querier
991                    .query::<Empty>(
992                        &QueryRequest::Bank(BankQuery::Balance {
993                            address: rich_addr.clone(),
994                            denom: "silver".to_string(),
995                        }),
996                        DEFAULT_QUERY_GAS_LIMIT,
997                    )
998                    .0
999                    .unwrap()
1000                    .unwrap()
1001                    .unwrap();
1002                let BalanceResponse { amount, .. } = from_json(response).unwrap();
1003                assert_eq!(amount.amount.u128(), 8000);
1004                assert_eq!(amount.denom, "silver");
1005                Ok(())
1006            })
1007            .unwrap();
1008
1009        // query all
1010        instance
1011            .with_querier(|querier| {
1012                let response = querier
1013                    .query::<Empty>(
1014                        &QueryRequest::Bank(BankQuery::AllBalances {
1015                            address: rich_addr.clone(),
1016                        }),
1017                        DEFAULT_QUERY_GAS_LIMIT,
1018                    )
1019                    .0
1020                    .unwrap()
1021                    .unwrap()
1022                    .unwrap();
1023                let AllBalanceResponse { amount, .. } = from_json(response).unwrap();
1024                assert_eq!(amount.len(), 2);
1025                assert_eq!(amount[0].amount.u128(), 10000);
1026                assert_eq!(amount[0].denom, "gold");
1027                assert_eq!(amount[1].amount.u128(), 8000);
1028                assert_eq!(amount[1].denom, "silver");
1029
1030                Ok(())
1031            })
1032            .unwrap();
1033    }
1034
1035    /// This is needed for writing integration tests in which the balance of a contract changes over time.
1036    #[test]
1037    fn with_querier_allows_updating_balances() {
1038        let rich_addr = String::from("foobar");
1039        let rich_balance1 = vec![coin(10000, "gold"), coin(500, "silver")];
1040        let rich_balance2 = vec![coin(10000, "gold"), coin(8000, "silver")];
1041        let mut instance = mock_instance_with_balances(CONTRACT, &[(&rich_addr, &rich_balance1)]);
1042
1043        // Get initial state
1044        instance
1045            .with_querier(|querier| {
1046                let response = querier
1047                    .query::<Empty>(
1048                        &QueryRequest::Bank(BankQuery::Balance {
1049                            address: rich_addr.clone(),
1050                            denom: "silver".to_string(),
1051                        }),
1052                        DEFAULT_QUERY_GAS_LIMIT,
1053                    )
1054                    .0
1055                    .unwrap()
1056                    .unwrap()
1057                    .unwrap();
1058                let BalanceResponse { amount, .. } = from_json(response).unwrap();
1059                assert_eq!(amount.amount.u128(), 500);
1060                Ok(())
1061            })
1062            .unwrap();
1063
1064        // Update balance
1065        instance
1066            .with_querier(|querier| {
1067                querier.update_balance(&rich_addr, rich_balance2);
1068                Ok(())
1069            })
1070            .unwrap();
1071
1072        // Get updated state
1073        instance
1074            .with_querier(|querier| {
1075                let response = querier
1076                    .query::<Empty>(
1077                        &QueryRequest::Bank(BankQuery::Balance {
1078                            address: rich_addr.clone(),
1079                            denom: "silver".to_string(),
1080                        }),
1081                        DEFAULT_QUERY_GAS_LIMIT,
1082                    )
1083                    .0
1084                    .unwrap()
1085                    .unwrap()
1086                    .unwrap();
1087                let BalanceResponse { amount, .. } = from_json(response).unwrap();
1088                assert_eq!(amount.amount.u128(), 8000);
1089                Ok(())
1090            })
1091            .unwrap();
1092    }
1093
1094    #[test]
1095    fn contract_deducts_gas_init() {
1096        let mut instance = mock_instance(CONTRACT, &[]);
1097        let orig_gas = instance.get_gas_left();
1098
1099        // init contract
1100        let info = mock_info(&instance.api().addr_make("creator"), &coins(1000, "earth"));
1101        let verifier = instance.api().addr_make("verifies");
1102        let beneficiary = instance.api().addr_make("benefits");
1103        let msg = format!(r#"{{"verifier": "{verifier}", "beneficiary": "{beneficiary}"}}"#);
1104        call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg.as_bytes())
1105            .unwrap()
1106            .unwrap();
1107
1108        let init_used = orig_gas - instance.get_gas_left();
1109        assert_eq!(init_used, 23165086);
1110    }
1111
1112    #[test]
1113    fn contract_deducts_gas_execute() {
1114        let mut instance = mock_instance(CONTRACT, &[]);
1115
1116        // init contract
1117        let info = mock_info(&instance.api().addr_make("creator"), &coins(1000, "earth"));
1118        let verifier = instance.api().addr_make("verifies");
1119        let beneficiary = instance.api().addr_make("benefits");
1120        let msg = format!(r#"{{"verifier": "{verifier}", "beneficiary": "{beneficiary}"}}"#);
1121        call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg.as_bytes())
1122            .unwrap()
1123            .unwrap();
1124
1125        // run contract - just sanity check - results validate in contract unit tests
1126        let gas_before_execute = instance.get_gas_left();
1127        let info = mock_info(&verifier, &coins(15, "earth"));
1128        let msg = br#"{"release":{}}"#;
1129        call_execute::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg)
1130            .unwrap()
1131            .unwrap();
1132
1133        let execute_used = gas_before_execute - instance.get_gas_left();
1134        assert_eq!(execute_used, 27661681);
1135    }
1136
1137    #[test]
1138    fn contract_enforces_gas_limit() {
1139        let mut instance = mock_instance_with_gas_limit(CONTRACT, 20_000);
1140
1141        // init contract
1142        let info = mock_info(&instance.api().addr_make("creator"), &coins(1000, "earth"));
1143        let verifier = instance.api().addr_make("verifies");
1144        let beneficiary = instance.api().addr_make("benefits");
1145        let msg = format!(r#"{{"verifier": "{verifier}", "beneficiary": "{beneficiary}"}}"#);
1146        let res =
1147            call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg.as_bytes());
1148        assert!(res.is_err());
1149    }
1150
1151    #[test]
1152    fn query_works_with_gas_metering() {
1153        let mut instance = mock_instance(CONTRACT, &[]);
1154
1155        // init contract
1156        let info = mock_info(&instance.api().addr_make("creator"), &coins(1000, "earth"));
1157        let verifier = instance.api().addr_make("verifies");
1158        let beneficiary = instance.api().addr_make("benefits");
1159        let msg = format!(r#"{{"verifier": "{verifier}", "beneficiary": "{beneficiary}"}}"#);
1160        let _res =
1161            call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg.as_bytes())
1162                .unwrap()
1163                .unwrap();
1164
1165        // run contract - just sanity check - results validate in contract unit tests
1166        let gas_before_query = instance.get_gas_left();
1167        // we need to encode the key in base64
1168        let msg = br#"{"verifier":{}}"#;
1169        let res = call_query(&mut instance, &mock_env(), msg).unwrap();
1170        let answer = res.unwrap();
1171        assert_eq!(
1172            answer.as_slice(),
1173            format!("{{\"verifier\":\"{verifier}\"}}").as_bytes()
1174        );
1175
1176        let query_used = gas_before_query - instance.get_gas_left();
1177        assert_eq!(query_used, 16370691);
1178    }
1179}