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; #[derive(Copy, Clone, Debug)]
32pub struct GasReport {
33 pub limit: u64,
35 pub remaining: u64,
37 pub used_externally: u64,
39 pub used_internally: u64,
42}
43
44#[derive(Copy, Clone, Debug)]
45pub struct InstanceOptions {
46 pub gas_limit: u64,
48}
49
50pub struct Instance<A: BackendApi, S: Storage, Q: Querier> {
51 _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, S: Storage + 'static, Q: Querier + 'static, {
67 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 env_imports.insert(
100 "db_read",
101 Function::new_typed_with_env(&mut store, &fe, do_db_read),
102 );
103
104 env_imports.insert(
107 "db_write",
108 Function::new_typed_with_env(&mut store, &fe, do_db_write),
109 );
110
111 env_imports.insert(
116 "db_remove",
117 Function::new_typed_with_env(&mut store, &fe, do_db_remove),
118 );
119
120 env_imports.insert(
124 "addr_validate",
125 Function::new_typed_with_env(&mut store, &fe, do_addr_validate),
126 );
127
128 env_imports.insert(
133 "addr_canonicalize",
134 Function::new_typed_with_env(&mut store, &fe, do_addr_canonicalize),
135 );
136
137 env_imports.insert(
142 "addr_humanize",
143 Function::new_typed_with_env(&mut store, &fe, do_addr_humanize),
144 );
145
146 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 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 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 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 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 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 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 env_imports.insert(
219 "ed25519_verify",
220 Function::new_typed_with_env(&mut store, &fe, do_ed25519_verify),
221 );
222
223 env_imports.insert(
229 "ed25519_batch_verify",
230 Function::new_typed_with_env(&mut store, &fe, do_ed25519_batch_verify),
231 );
232
233 env_imports.insert(
238 "debug",
239 Function::new_typed_with_env(&mut store, &fe, do_debug),
240 );
241
242 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 #[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 #[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 #[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 #[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 #[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(&'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 pub fn required_capabilities(&self) -> HashSet<String> {
385 required_capabilities_from_module(self._inner.module())
386 }
387
388 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 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 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 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 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 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 pub(crate) fn deallocate(&mut self, ptr: u32) -> VmResult<()> {
474 self.call_function0("deallocate", &[ptr.into()])?;
475 Ok(())
476 }
477
478 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 pub(crate) fn write_memory(&mut self, region_ptr: u32, data: &[u8]) -> VmResult<()> {
488 let mut fe_mut = self.fe.clone().into_mut(&mut self.store);
489 let (env, mut store) = fe_mut.data_and_store_mut();
490
491 write_region(env, &mut store, region_ptr, data)?;
492 Ok(())
493 }
494
495 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 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
514pub 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, S: Storage + 'static, Q: Querier + 'static,
527{
528 Instance::from_module(store, module, backend, gas_limit, extra_imports, None)
529}
530
531#[cfg(test)]
532mod tests {
533 use std::sync::atomic::{AtomicBool, Ordering};
534 use std::sync::Arc;
535 use std::time::SystemTime;
536
537 use super::*;
538 use crate::calls::{call_execute, call_instantiate, call_query};
539 use crate::testing::{
540 mock_backend, mock_env, mock_info, mock_instance, mock_instance_options,
541 mock_instance_with_balances, mock_instance_with_failing_api, mock_instance_with_gas_limit,
542 mock_instance_with_options, MockInstanceOptions,
543 };
544 use cosmwasm_std::{
545 coin, coins, from_json, BalanceResponse, BankQuery, Empty, QueryRequest, Uint256,
546 };
547 use wasmer::FunctionEnvMut;
548
549 const KIB: usize = 1024;
550 const MIB: usize = 1024 * 1024;
551 const DEFAULT_QUERY_GAS_LIMIT: u64 = 300_000;
552 static HACKATOM: &[u8] = include_bytes!("../testdata/hackatom.wasm");
553 static HACKATOM_1_3: &[u8] = include_bytes!("../testdata/hackatom_1.3.wasm");
554 static CYBERPUNK: &[u8] = include_bytes!("../testdata/cyberpunk.wasm");
555
556 #[test]
557 fn from_code_works() {
558 let backend = mock_backend(&[]);
559 let (instance_options, memory_limit) = mock_instance_options();
560 let _instance =
561 Instance::from_code(HACKATOM, backend, instance_options, memory_limit).unwrap();
562 }
563
564 #[test]
565 fn set_debug_handler_and_unset_debug_handler_work() {
566 const LIMIT: u64 = 70_000_000_000;
567 let mut instance = mock_instance_with_gas_limit(CYBERPUNK, LIMIT);
568
569 let info = mock_info("creator", &coins(1000, "earth"));
571 call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, br#"{}"#)
572 .unwrap()
573 .unwrap();
574
575 let info = mock_info("caller", &[]);
576 call_execute::<_, _, _, Empty>(&mut instance, &mock_env(), &info, br#"{"debug":{}}"#)
577 .unwrap()
578 .unwrap();
579
580 let start = SystemTime::now();
581 instance.set_debug_handler(move |msg, info| {
582 let gas = info.gas_remaining;
583 let runtime = SystemTime::now().duration_since(start).unwrap().as_micros();
584 eprintln!("{msg} (gas: {gas}, runtime: {runtime}µs)");
585 });
586
587 let info = mock_info("caller", &[]);
588 call_execute::<_, _, _, Empty>(&mut instance, &mock_env(), &info, br#"{"debug":{}}"#)
589 .unwrap()
590 .unwrap();
591
592 eprintln!("Unsetting debug handler. From here nothing is printed anymore.");
593 instance.unset_debug_handler();
594
595 let info = mock_info("caller", &[]);
596 call_execute::<_, _, _, Empty>(&mut instance, &mock_env(), &info, br#"{"debug":{}}"#)
597 .unwrap()
598 .unwrap();
599 }
600
601 #[test]
602 fn required_capabilities_works() {
603 let backend = mock_backend(&[]);
604 let (instance_options, memory_limit) = mock_instance_options();
605 let instance =
606 Instance::from_code(HACKATOM_1_3, backend, instance_options, memory_limit).unwrap();
607 assert_eq!(instance.required_capabilities().len(), 0);
608
609 let backend = mock_backend(&[]);
610 let (instance_options, memory_limit) = mock_instance_options();
611 let instance =
612 Instance::from_code(HACKATOM, backend, instance_options, memory_limit).unwrap();
613 assert_eq!(instance.required_capabilities().len(), 7);
614 }
615
616 #[test]
617 fn required_capabilities_works_for_many_exports() {
618 let wasm = wat::parse_str(
619 r#"(module
620 (memory 3)
621 (export "memory" (memory 0))
622
623 (type (func))
624 (func (type 0) nop)
625 (export "requires_water" (func 0))
626 (export "requires_" (func 0))
627 (export "requires_nutrients" (func 0))
628 (export "require_milk" (func 0))
629 (export "REQUIRES_air" (func 0))
630 (export "requires_sun" (func 0))
631 )"#,
632 )
633 .unwrap();
634
635 let backend = mock_backend(&[]);
636 let (instance_options, memory_limit) = mock_instance_options();
637 let instance = Instance::from_code(&wasm, backend, instance_options, memory_limit).unwrap();
638 assert_eq!(instance.required_capabilities().len(), 3);
639 assert!(instance.required_capabilities().contains("nutrients"));
640 assert!(instance.required_capabilities().contains("sun"));
641 assert!(instance.required_capabilities().contains("water"));
642 }
643
644 #[test]
645 fn extra_imports_get_added() {
646 let (instance_options, memory_limit) = mock_instance_options();
647
648 let wasm = wat::parse_str(
649 r#"(module
650 (import "foo" "bar" (func $bar))
651 (memory 3)
652 (export "memory" (memory 0))
653 (func (export "main") (call $bar))
654 )"#,
655 )
656 .unwrap();
657
658 let backend = mock_backend(&[]);
659 let engine = make_compiling_engine(memory_limit);
660 let module = compile(&engine, &wasm).unwrap();
661 let mut store = Store::new(engine);
662
663 let called = Arc::new(AtomicBool::new(false));
664
665 #[derive(Clone)]
666 struct MyEnv {
667 called: Arc<AtomicBool>,
670 }
671
672 let fe = FunctionEnv::new(
673 &mut store,
674 MyEnv {
675 called: called.clone(),
676 },
677 );
678
679 let fun =
680 Function::new_typed_with_env(&mut store, &fe, move |fe_mut: FunctionEnvMut<MyEnv>| {
681 fe_mut.data().called.store(true, Ordering::Relaxed);
682 });
683 let mut exports = Exports::new();
684 exports.insert("bar", fun);
685 let mut extra_imports = HashMap::new();
686 extra_imports.insert("foo", exports);
687 let mut instance = Instance::from_module(
688 store,
689 &module,
690 backend,
691 instance_options.gas_limit,
692 Some(extra_imports),
693 None,
694 )
695 .unwrap();
696
697 instance.call_function0("main", &[]).unwrap();
698
699 assert!(called.load(Ordering::Relaxed));
700 }
701
702 #[test]
703 fn call_function0_works() {
704 let mut instance = mock_instance(HACKATOM, &[]);
705
706 instance
707 .call_function0("interface_version_8", &[])
708 .expect("error calling function");
709 }
710
711 #[test]
712 fn call_function1_works() {
713 let mut instance = mock_instance(HACKATOM, &[]);
714
715 let result = instance
717 .call_function1("allocate", &[0u32.into()])
718 .expect("error calling allocate");
719 assert_ne!(result.unwrap_i32(), 0);
720
721 let result = instance
722 .call_function1("allocate", &[1u32.into()])
723 .expect("error calling allocate");
724 assert_ne!(result.unwrap_i32(), 0);
725
726 let result = instance
727 .call_function1("allocate", &[33u32.into()])
728 .expect("error calling allocate");
729 assert_ne!(result.unwrap_i32(), 0);
730 }
731
732 #[test]
733 fn allocate_deallocate_works() {
734 let mut instance = mock_instance_with_options(
735 HACKATOM,
736 MockInstanceOptions {
737 memory_limit: Some(Size::mebi(500)),
738 ..Default::default()
739 },
740 );
741
742 let sizes: Vec<usize> = vec![
743 0,
744 4,
745 40,
746 400,
747 4 * KIB,
748 40 * KIB,
749 400 * KIB,
750 4 * MIB,
751 40 * MIB,
752 400 * MIB,
753 ];
754 for size in sizes.into_iter() {
755 let region_ptr = instance.allocate(size).expect("error allocating");
756 instance.deallocate(region_ptr).expect("error deallocating");
757 }
758 }
759
760 #[test]
761 fn write_and_read_memory_works() {
762 let mut instance = mock_instance_with_gas_limit(HACKATOM, 6_000_000_000);
763
764 let sizes: Vec<usize> = vec![
765 0,
766 4,
767 40,
768 400,
769 4 * KIB,
770 40 * KIB,
771 400 * KIB,
772 4 * MIB,
773 ];
777 for size in sizes.into_iter() {
778 let region_ptr = instance.allocate(size).expect("error allocating");
779 let original = vec![170u8; size];
780 instance
781 .write_memory(region_ptr, &original)
782 .expect("error writing");
783 let data = instance
784 .read_memory(region_ptr, size)
785 .expect("error reading");
786 assert_eq!(data, original);
787 instance.deallocate(region_ptr).expect("error deallocating");
788 }
789 }
790
791 #[test]
792 fn errors_in_imports() {
793 let error_message = "Api failed intentionally";
795 let mut instance = mock_instance_with_failing_api(HACKATOM, &[], error_message);
796 let init_result = call_instantiate::<_, _, _, Empty>(
797 &mut instance,
798 &mock_env(),
799 &mock_info("someone", &[]),
800 b"{\"verifier\": \"some1\", \"beneficiary\": \"some2\"}",
801 );
802
803 match init_result.unwrap_err() {
804 VmError::RuntimeErr { msg, .. } => assert!(msg.contains(error_message)),
805 err => panic!("Unexpected error: {err:?}"),
806 }
807 }
808
809 #[test]
810 fn read_memory_errors_when_when_length_is_too_long() {
811 let length = 6;
812 let max_length = 5;
813 let mut instance = mock_instance(HACKATOM, &[]);
814
815 let region_ptr = instance.allocate(length).expect("error allocating");
817 let data = vec![170u8; length];
818 instance
819 .write_memory(region_ptr, &data)
820 .expect("error writing");
821
822 let result = instance.read_memory(region_ptr, max_length);
823 match result.unwrap_err() {
824 VmError::CommunicationErr {
825 source:
826 CommunicationError::RegionLengthTooBig {
827 length, max_length, ..
828 },
829 ..
830 } => {
831 assert_eq!(length, 6);
832 assert_eq!(max_length, 5);
833 }
834 err => panic!("unexpected error: {err:?}"),
835 };
836
837 instance.deallocate(region_ptr).expect("error deallocating");
838 }
839
840 #[test]
841 fn memory_pages_returns_min_memory_size_by_default() {
842 let wasm = wat::parse_str(
844 r#"(module
845 (memory 0)
846 (export "memory" (memory 0))
847
848 (type (func))
849 (func (type 0) nop)
850 (export "interface_version_8" (func 0))
851 (export "instantiate" (func 0))
852 (export "allocate" (func 0))
853 (export "deallocate" (func 0))
854 )"#,
855 )
856 .unwrap();
857 let mut instance = mock_instance(&wasm, &[]);
858 assert_eq!(instance.memory_pages(), 0);
859
860 let wasm = wat::parse_str(
862 r#"(module
863 (memory 3)
864 (export "memory" (memory 0))
865
866 (type (func))
867 (func (type 0) nop)
868 (export "interface_version_8" (func 0))
869 (export "instantiate" (func 0))
870 (export "allocate" (func 0))
871 (export "deallocate" (func 0))
872 )"#,
873 )
874 .unwrap();
875 let mut instance = mock_instance(&wasm, &[]);
876 assert_eq!(instance.memory_pages(), 3);
877 }
878
879 #[test]
880 fn memory_pages_grows_with_usage() {
881 let mut instance = mock_instance(HACKATOM, &[]);
882
883 assert_eq!(instance.memory_pages(), 17);
884
885 let region_ptr = instance.allocate(100 * 1024).expect("error allocating");
887
888 assert_eq!(instance.memory_pages(), 19);
889
890 instance.deallocate(region_ptr).expect("error deallocating");
892 assert_eq!(instance.memory_pages(), 19);
893 }
894
895 #[test]
896 fn get_gas_left_works() {
897 let mut instance = mock_instance_with_gas_limit(HACKATOM, 123321);
898 let orig_gas = instance.get_gas_left();
899 assert_eq!(orig_gas, 123321);
900 }
901
902 #[test]
903 fn create_gas_report_works() {
904 const LIMIT: u64 = 700_000_000;
905 let mut instance = mock_instance_with_gas_limit(HACKATOM, LIMIT);
906
907 let report1 = instance.create_gas_report();
908 assert_eq!(report1.used_externally, 0);
909 assert_eq!(report1.used_internally, 0);
910 assert_eq!(report1.limit, LIMIT);
911 assert_eq!(report1.remaining, LIMIT);
912
913 let info = mock_info(&instance.api().addr_make("creator"), &coins(1000, "earth"));
915 let verifier = instance.api().addr_make("verifies");
916 let beneficiary = instance.api().addr_make("benefits");
917 let msg = format!(r#"{{"verifier": "{verifier}", "beneficiary": "{beneficiary}"}}"#);
918 call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg.as_bytes())
919 .unwrap()
920 .unwrap();
921
922 let report2 = instance.create_gas_report();
923 assert_eq!(report2.used_externally, 251);
924 assert_eq!(report2.used_internally, 18034325);
925 assert_eq!(report2.limit, LIMIT);
926 assert_eq!(
927 report2.remaining,
928 LIMIT - report2.used_externally - report2.used_internally
929 );
930 }
931
932 #[test]
933 fn set_storage_readonly_works() {
934 let mut instance = mock_instance(HACKATOM, &[]);
935
936 assert!(instance.is_storage_readonly());
937
938 instance.set_storage_readonly(false);
939 assert!(!instance.is_storage_readonly());
940
941 instance.set_storage_readonly(false);
942 assert!(!instance.is_storage_readonly());
943
944 instance.set_storage_readonly(true);
945 assert!(instance.is_storage_readonly());
946 }
947
948 #[test]
949 fn with_storage_works() {
950 let mut instance = mock_instance(HACKATOM, &[]);
951
952 instance
954 .with_storage(|store| {
955 assert!(store.get(b"foo").0.unwrap().is_none());
956 Ok(())
957 })
958 .unwrap();
959
960 instance
962 .with_storage(|store| {
963 store.set(b"foo", b"bar").0.unwrap();
964 Ok(())
965 })
966 .unwrap();
967
968 instance
970 .with_storage(|store| {
971 assert_eq!(store.get(b"foo").0.unwrap(), Some(b"bar".to_vec()));
972 Ok(())
973 })
974 .unwrap();
975 }
976
977 #[test]
978 #[should_panic]
979 fn with_storage_safe_for_panic() {
980 let mut instance = mock_instance(HACKATOM, &[]);
982 instance
983 .with_storage::<_, ()>(|_store| panic!("trigger failure"))
984 .unwrap();
985 }
986
987 #[test]
988 #[allow(deprecated)]
989 fn with_querier_works_readonly() {
990 let rich_addr = String::from("foobar");
991 let rich_balance = vec![coin(10000, "gold"), coin(8000, "silver")];
992 let mut instance = mock_instance_with_balances(HACKATOM, &[(&rich_addr, &rich_balance)]);
993
994 instance
996 .with_querier(|querier| {
997 let response = querier
998 .query::<Empty>(
999 &QueryRequest::Bank(BankQuery::Balance {
1000 address: rich_addr.clone(),
1001 denom: "silver".to_string(),
1002 }),
1003 DEFAULT_QUERY_GAS_LIMIT,
1004 )
1005 .0
1006 .unwrap()
1007 .unwrap()
1008 .unwrap();
1009 let BalanceResponse { amount, .. } = from_json(response).unwrap();
1010 assert_eq!(amount.amount, Uint256::new(8000));
1011 assert_eq!(amount.denom, "silver");
1012 Ok(())
1013 })
1014 .unwrap();
1015 }
1016
1017 #[test]
1019 fn with_querier_allows_updating_balances() {
1020 let rich_addr = String::from("foobar");
1021 let rich_balance1 = vec![coin(10000, "gold"), coin(500, "silver")];
1022 let rich_balance2 = vec![coin(10000, "gold"), coin(8000, "silver")];
1023 let mut instance = mock_instance_with_balances(HACKATOM, &[(&rich_addr, &rich_balance1)]);
1024
1025 instance
1027 .with_querier(|querier| {
1028 let response = querier
1029 .query::<Empty>(
1030 &QueryRequest::Bank(BankQuery::Balance {
1031 address: rich_addr.clone(),
1032 denom: "silver".to_string(),
1033 }),
1034 DEFAULT_QUERY_GAS_LIMIT,
1035 )
1036 .0
1037 .unwrap()
1038 .unwrap()
1039 .unwrap();
1040 let BalanceResponse { amount, .. } = from_json(response).unwrap();
1041 assert_eq!(amount.amount, Uint256::new(500));
1042 Ok(())
1043 })
1044 .unwrap();
1045
1046 instance
1048 .with_querier(|querier| {
1049 querier.update_balance(&rich_addr, rich_balance2);
1050 Ok(())
1051 })
1052 .unwrap();
1053
1054 instance
1056 .with_querier(|querier| {
1057 let response = querier
1058 .query::<Empty>(
1059 &QueryRequest::Bank(BankQuery::Balance {
1060 address: rich_addr.clone(),
1061 denom: "silver".to_string(),
1062 }),
1063 DEFAULT_QUERY_GAS_LIMIT,
1064 )
1065 .0
1066 .unwrap()
1067 .unwrap()
1068 .unwrap();
1069 let BalanceResponse { amount, .. } = from_json(response).unwrap();
1070 assert_eq!(amount.amount, Uint256::new(8000));
1071 Ok(())
1072 })
1073 .unwrap();
1074 }
1075
1076 #[test]
1077 fn contract_deducts_gas_init() {
1078 let mut instance = mock_instance(HACKATOM, &[]);
1079 let orig_gas = instance.get_gas_left();
1080
1081 let info = mock_info(&instance.api().addr_make("creator"), &coins(1000, "earth"));
1083 let verifier = instance.api().addr_make("verifies");
1084 let beneficiary = instance.api().addr_make("benefits");
1085 let msg = format!(r#"{{"verifier": "{verifier}", "beneficiary": "{beneficiary}"}}"#);
1086 call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg.as_bytes())
1087 .unwrap()
1088 .unwrap();
1089
1090 let init_used = orig_gas - instance.get_gas_left();
1091 assert_eq!(init_used, 18034576);
1092 }
1093
1094 #[test]
1095 fn contract_deducts_gas_execute() {
1096 let mut instance = mock_instance(HACKATOM, &[]);
1097
1098 let info = mock_info(&instance.api().addr_make("creator"), &coins(1000, "earth"));
1100 let verifier = instance.api().addr_make("verifies");
1101 let beneficiary = instance.api().addr_make("benefits");
1102 let msg = format!(r#"{{"verifier": "{verifier}", "beneficiary": "{beneficiary}"}}"#);
1103 call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg.as_bytes())
1104 .unwrap()
1105 .unwrap();
1106
1107 let gas_before_execute = instance.get_gas_left();
1109 let info = mock_info(&verifier, &coins(15, "earth"));
1110 let msg = br#"{"release":{"denom":"earth"}}"#;
1111 call_execute::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg)
1112 .unwrap()
1113 .unwrap();
1114
1115 let execute_used = gas_before_execute - instance.get_gas_left();
1116 assert_eq!(execute_used, 24624251);
1117 }
1118
1119 #[test]
1120 fn contract_enforces_gas_limit() {
1121 let mut instance = mock_instance_with_gas_limit(HACKATOM, 20_000);
1122
1123 let info = mock_info(&instance.api().addr_make("creator"), &coins(1000, "earth"));
1125 let verifier = instance.api().addr_make("verifies");
1126 let beneficiary = instance.api().addr_make("benefits");
1127 let msg = format!(r#"{{"verifier": "{verifier}", "beneficiary": "{beneficiary}"}}"#);
1128 let res =
1129 call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg.as_bytes());
1130 assert!(res.is_err());
1131 }
1132
1133 #[test]
1134 fn query_works_with_gas_metering() {
1135 let mut instance = mock_instance(HACKATOM, &[]);
1136
1137 let info = mock_info(&instance.api().addr_make("creator"), &coins(1000, "earth"));
1139 let verifier = instance.api().addr_make("verifies");
1140 let beneficiary = instance.api().addr_make("benefits");
1141 let msg = format!(r#"{{"verifier": "{verifier}", "beneficiary": "{beneficiary}"}}"#);
1142 let _res =
1143 call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg.as_bytes())
1144 .unwrap()
1145 .unwrap();
1146
1147 let gas_before_query = instance.get_gas_left();
1149 let msg = br#"{"verifier":{}}"#;
1151 let res = call_query(&mut instance, &mock_env(), msg).unwrap();
1152 let answer = res.unwrap();
1153 assert_eq!(
1154 answer.as_slice(),
1155 format!("{{\"verifier\":\"{verifier}\"}}").as_bytes()
1156 );
1157
1158 let query_used = gas_before_query - instance.get_gas_left();
1159 assert_eq!(query_used, 11105221);
1160 }
1161}