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, store) = fe_mut.data_and_store_mut();
490
491 write_region(&env.memory(&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, 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 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 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 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 ];
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 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 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 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 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 let region_ptr = instance.allocate(100 * 1024).expect("error allocating");
880
881 assert_eq!(instance.memory_pages(), 19);
882
883 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 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 instance
947 .with_storage(|store| {
948 assert!(store.get(b"foo").0.unwrap().is_none());
949 Ok(())
950 })
951 .unwrap();
952
953 instance
955 .with_storage(|store| {
956 store.set(b"foo", b"bar").0.unwrap();
957 Ok(())
958 })
959 .unwrap();
960
961 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 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 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 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 #[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 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 instance
1066 .with_querier(|querier| {
1067 querier.update_balance(&rich_addr, rich_balance2);
1068 Ok(())
1069 })
1070 .unwrap();
1071
1072 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 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 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 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 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 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 let gas_before_query = instance.get_gas_left();
1167 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}