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 pub limit: u64,
32 pub remaining: u64,
34 pub used_externally: u64,
36 pub used_internally: u64,
39}
40
41#[derive(Copy, Clone, Debug)]
42pub struct InstanceOptions {
43 pub gas_limit: u64,
45}
46
47pub struct Instance<A: BackendApi, S: Storage, Q: Querier> {
48 _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, S: Storage + 'static, Q: Querier + 'static, {
64 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 env_imports.insert(
96 "db_read",
97 Function::new_typed_with_env(&mut store, &fe, do_db_read),
98 );
99
100 env_imports.insert(
103 "db_write",
104 Function::new_typed_with_env(&mut store, &fe, do_db_write),
105 );
106
107 env_imports.insert(
112 "db_remove",
113 Function::new_typed_with_env(&mut store, &fe, do_db_remove),
114 );
115
116 env_imports.insert(
120 "addr_validate",
121 Function::new_typed_with_env(&mut store, &fe, do_addr_validate),
122 );
123
124 env_imports.insert(
129 "addr_canonicalize",
130 Function::new_typed_with_env(&mut store, &fe, do_addr_canonicalize),
131 );
132
133 env_imports.insert(
138 "addr_humanize",
139 Function::new_typed_with_env(&mut store, &fe, do_addr_humanize),
140 );
141
142 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 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 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 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 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 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 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 env_imports.insert(
215 "ed25519_verify",
216 Function::new_typed_with_env(&mut store, &fe, do_ed25519_verify),
217 );
218
219 env_imports.insert(
225 "ed25519_batch_verify",
226 Function::new_typed_with_env(&mut store, &fe, do_ed25519_batch_verify),
227 );
228
229 env_imports.insert(
234 "debug",
235 Function::new_typed_with_env(&mut store, &fe, do_debug),
236 );
237
238 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 #[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 #[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 #[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 #[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 #[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(&'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 pub fn required_capabilities(&self) -> HashSet<String> {
381 required_capabilities_from_module(self._inner.module())
382 }
383
384 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 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 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 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 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 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 pub(crate) fn deallocate(&mut self, ptr: u32) -> VmResult<()> {
470 self.call_function0("deallocate", &[ptr.into()])?;
471 Ok(())
472 }
473
474 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 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 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 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
510pub 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, S: Storage + 'static, 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 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 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 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 ];
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 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 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 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 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 let region_ptr = instance.allocate(100 * 1024).expect("error allocating");
882
883 assert_eq!(instance.memory_pages(), 19);
884
885 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 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 instance
949 .with_storage(|store| {
950 assert!(store.get(b"foo").0.unwrap().is_none());
951 Ok(())
952 })
953 .unwrap();
954
955 instance
957 .with_storage(|store| {
958 store.set(b"foo", b"bar").0.unwrap();
959 Ok(())
960 })
961 .unwrap();
962
963 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 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 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 #[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 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 instance
1043 .with_querier(|querier| {
1044 querier.update_balance(&rich_addr, rich_balance2);
1045 Ok(())
1046 })
1047 .unwrap();
1048
1049 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 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 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 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 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 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 let gas_before_query = instance.get_gas_left();
1144 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}