1use std::borrow::BorrowMut;
3use std::cell::RefCell;
4use std::marker::PhantomData;
5use std::ptr::NonNull;
6use std::rc::Rc;
7use std::sync::{Arc, RwLock};
8
9use derive_more::Debug;
10use wasmer::{AsStoreMut, Instance as WasmerInstance, Memory, MemoryView, Value};
11use wasmer_middlewares::metering::{get_remaining_points, set_remaining_points, MeteringPoints};
12
13use crate::backend::{BackendApi, GasInfo, Querier, Storage};
14use crate::errors::{VmError, VmResult};
15
16const MAX_CALL_DEPTH: usize = 2;
22
23#[derive(Debug)]
26pub enum Never {}
27
28#[derive(Clone, PartialEq, Eq, Debug)]
31#[non_exhaustive]
32pub struct GasConfig {
33 pub secp256k1_verify_cost: u64,
36 pub secp256k1_recover_pubkey_cost: u64,
38 pub secp256r1_verify_cost: u64,
40 pub secp256r1_recover_pubkey_cost: u64,
42 pub ed25519_verify_cost: u64,
44 pub ed25519_batch_verify_cost: LinearGasCost,
46 pub ed25519_batch_verify_one_pubkey_cost: LinearGasCost,
48 pub bls12_381_aggregate_g1_cost: LinearGasCost,
50 pub bls12_381_aggregate_g2_cost: LinearGasCost,
52 pub bls12_381_hash_to_g1_cost: u64,
54 pub bls12_381_hash_to_g2_cost: u64,
56 pub bls12_381_pairing_equality_cost: LinearGasCost,
58 pub write_region_cost: LinearGasCost,
60 pub read_region_small_cost: LinearGasCost,
62 pub read_region_large_cost: LinearGasCost,
64 pub string_from_bytes_cost: LinearGasCost,
66 pub host_call_cost: u64,
68}
69
70impl Default for GasConfig {
71 fn default() -> Self {
72 const GAS_PER_US: u64 = 1_000_000;
74 Self {
75 secp256k1_verify_cost: 96 * GAS_PER_US,
77 secp256k1_recover_pubkey_cost: 194 * GAS_PER_US,
79 secp256r1_verify_cost: 279 * GAS_PER_US,
81 secp256r1_recover_pubkey_cost: 592 * GAS_PER_US,
83 ed25519_verify_cost: 35 * GAS_PER_US,
85 ed25519_batch_verify_cost: LinearGasCost {
87 base: 24 * GAS_PER_US,
88 per_item: 21 * GAS_PER_US,
89 },
90 ed25519_batch_verify_one_pubkey_cost: LinearGasCost {
92 base: 36 * GAS_PER_US,
93 per_item: 10 * GAS_PER_US,
94 },
95 bls12_381_aggregate_g1_cost: LinearGasCost {
97 base: 136 * GAS_PER_US / 2,
98 per_item: 24 * GAS_PER_US / 2,
99 },
100 bls12_381_aggregate_g2_cost: LinearGasCost {
101 base: 207 * GAS_PER_US / 2,
102 per_item: 49 * GAS_PER_US / 2,
103 },
104 bls12_381_hash_to_g1_cost: 563 * GAS_PER_US,
105 bls12_381_hash_to_g2_cost: 871 * GAS_PER_US,
106 bls12_381_pairing_equality_cost: LinearGasCost {
107 base: 2112 * GAS_PER_US,
108 per_item: 163 * GAS_PER_US,
109 },
110 write_region_cost: LinearGasCost {
111 base: 230000,
112 per_item: 570,
113 },
114 read_region_small_cost: LinearGasCost {
115 base: 200000,
116 per_item: 115,
117 },
118 read_region_large_cost: LinearGasCost {
119 base: 0,
120 per_item: 520,
121 },
122 string_from_bytes_cost: LinearGasCost {
123 base: 28700,
124 per_item: 1400,
125 },
126 host_call_cost: 18000,
127 }
128 }
129}
130
131impl GasConfig {
132 pub fn read_region_cost(&self, bytes: usize) -> VmResult<u64> {
133 const THRESHOLD: usize = 8 * 1000 * 1000;
134 if bytes <= THRESHOLD {
135 self.read_region_small_cost.total_cost(bytes as u64)
136 } else {
137 self.read_region_large_cost.total_cost(bytes as u64)
138 }
139 }
140}
141
142#[derive(Clone, PartialEq, Eq, Debug)]
148pub struct LinearGasCost {
149 base: u64,
151 per_item: u64,
153}
154
155impl LinearGasCost {
156 pub fn total_cost(&self, items: u64) -> VmResult<u64> {
157 self.total_cost_opt(items)
158 .ok_or_else(VmError::gas_depletion)
159 }
160
161 fn total_cost_opt(&self, items: u64) -> Option<u64> {
162 self.base.checked_add(self.per_item.checked_mul(items)?)
163 }
164}
165
166#[derive(Clone, PartialEq, Eq, Debug, Default)]
169pub struct GasState {
170 pub gas_limit: u64,
175 pub externally_used_gas: u64,
177}
178
179impl GasState {
180 fn with_limit(gas_limit: u64) -> Self {
181 Self {
182 gas_limit,
183 externally_used_gas: 0,
184 }
185 }
186}
187
188#[derive(Debug)]
193#[non_exhaustive]
194pub struct DebugInfo<'a> {
195 pub gas_remaining: u64,
196 #[doc(hidden)]
199 #[debug(skip)]
200 pub(crate) __lifetime: PhantomData<&'a ()>,
201}
202
203pub type DebugHandlerFn = dyn for<'a, 'b> FnMut(&'a str, DebugInfo<'b>);
210
211pub struct Environment<A, S, Q> {
214 pub memory: Option<Memory>,
215 pub api: A,
216 pub gas_config: GasConfig,
217 data: Arc<RwLock<ContextData<S, Q>>>,
218}
219
220unsafe impl<A: BackendApi, S: Storage, Q: Querier> Send for Environment<A, S, Q> {}
221
222unsafe impl<A: BackendApi, S: Storage, Q: Querier> Sync for Environment<A, S, Q> {}
223
224impl<A: BackendApi, S: Storage, Q: Querier> Clone for Environment<A, S, Q> {
225 fn clone(&self) -> Self {
226 Environment {
227 memory: None,
228 api: self.api.clone(),
229 gas_config: self.gas_config.clone(),
230 data: self.data.clone(),
231 }
232 }
233}
234
235impl<A: BackendApi, S: Storage, Q: Querier> Environment<A, S, Q> {
236 pub fn new(api: A, gas_limit: u64) -> Self {
237 Environment {
238 memory: None,
239 api,
240 gas_config: GasConfig::default(),
241 data: Arc::new(RwLock::new(ContextData::new(gas_limit))),
242 }
243 }
244
245 pub fn set_debug_handler(&self, debug_handler: Option<Rc<RefCell<DebugHandlerFn>>>) {
246 self.with_context_data_mut(|context_data| {
247 context_data.debug_handler = debug_handler;
248 })
249 }
250
251 pub fn debug_handler(&self) -> Option<Rc<RefCell<DebugHandlerFn>>> {
252 self.with_context_data(|context_data| {
253 context_data.debug_handler.clone()
255 })
256 }
257
258 fn with_context_data_mut<C, R>(&self, callback: C) -> R
259 where
260 C: FnOnce(&mut ContextData<S, Q>) -> R,
261 {
262 let mut guard = self.data.as_ref().write().unwrap();
263 let context_data = guard.borrow_mut();
264 callback(context_data)
265 }
266
267 fn with_context_data<C, R>(&self, callback: C) -> R
268 where
269 C: FnOnce(&ContextData<S, Q>) -> R,
270 {
271 let guard = self.data.as_ref().read().unwrap();
272 callback(&guard)
273 }
274
275 pub fn with_gas_state<C, R>(&self, callback: C) -> R
276 where
277 C: FnOnce(&GasState) -> R,
278 {
279 self.with_context_data(|context_data| callback(&context_data.gas_state))
280 }
281
282 pub fn with_gas_state_mut<C, R>(&self, callback: C) -> R
283 where
284 C: FnOnce(&mut GasState) -> R,
285 {
286 self.with_context_data_mut(|context_data| callback(&mut context_data.gas_state))
287 }
288
289 pub fn with_wasmer_instance<C, R>(&self, callback: C) -> VmResult<R>
290 where
291 C: FnOnce(&WasmerInstance) -> VmResult<R>,
292 {
293 self.with_context_data(|context_data| match context_data.wasmer_instance {
294 Some(instance_ptr) => {
295 let instance_ref = unsafe { instance_ptr.as_ref() };
296 callback(instance_ref)
297 }
298 None => Err(VmError::uninitialized_context_data("wasmer_instance")),
299 })
300 }
301
302 fn call_function(
307 &self,
308 store: &mut impl AsStoreMut,
309 name: &str,
310 args: &[Value],
311 ) -> VmResult<Box<[Value]>> {
312 let func = self.with_wasmer_instance(|instance| {
314 let func = instance.exports.get_function(name)?;
315 Ok(func.clone())
316 })?;
317 let function_arity = func.param_arity(store);
318 if args.len() != function_arity {
319 return Err(VmError::function_arity_mismatch(function_arity));
320 };
321 self.increment_call_depth()?;
322 let res = func.call(store, args).map_err(|runtime_err| -> VmError {
323 self.with_wasmer_instance::<_, Never>(|instance| {
324 let err: VmError = match get_remaining_points(store, instance) {
325 MeteringPoints::Remaining(_) => VmError::from(runtime_err),
326 MeteringPoints::Exhausted => VmError::gas_depletion(),
327 };
328 Err(err)
329 })
330 .unwrap_err() });
332 self.decrement_call_depth();
333 res
334 }
335
336 pub fn call_function0(
337 &self,
338 store: &mut impl AsStoreMut,
339 name: &str,
340 args: &[Value],
341 ) -> VmResult<()> {
342 let result = self.call_function(store, name, args)?;
343 let expected = 0;
344 let actual = result.len();
345 if actual != expected {
346 return Err(VmError::result_mismatch(name, expected, actual));
347 }
348 Ok(())
349 }
350
351 pub fn call_function1(
352 &self,
353 store: &mut impl AsStoreMut,
354 name: &str,
355 args: &[Value],
356 ) -> VmResult<Value> {
357 let result = self.call_function(store, name, args)?;
358 let expected = 1;
359 let actual = result.len();
360 if actual != expected {
361 return Err(VmError::result_mismatch(name, expected, actual));
362 }
363 Ok(result[0].clone())
364 }
365
366 pub fn with_storage_from_context<C, T>(&self, callback: C) -> VmResult<T>
367 where
368 C: FnOnce(&mut S) -> VmResult<T>,
369 {
370 self.with_context_data_mut(|context_data| match context_data.storage.as_mut() {
371 Some(data) => callback(data),
372 None => Err(VmError::uninitialized_context_data("storage")),
373 })
374 }
375
376 pub fn with_querier_from_context<C, T>(&self, callback: C) -> VmResult<T>
377 where
378 C: FnOnce(&mut Q) -> VmResult<T>,
379 {
380 self.with_context_data_mut(|context_data| match context_data.querier.as_mut() {
381 Some(querier) => callback(querier),
382 None => Err(VmError::uninitialized_context_data("querier")),
383 })
384 }
385
386 pub fn set_wasmer_instance(&self, wasmer_instance: Option<NonNull<WasmerInstance>>) {
388 self.with_context_data_mut(|context_data| {
389 context_data.wasmer_instance = wasmer_instance;
390 });
391 }
392
393 pub fn is_storage_readonly(&self) -> bool {
395 self.with_context_data(|context_data| context_data.storage_readonly)
396 }
397
398 pub fn set_storage_readonly(&self, new_value: bool) {
399 self.with_context_data_mut(|context_data| {
400 context_data.storage_readonly = new_value;
401 })
402 }
403
404 pub fn increment_call_depth(&self) -> VmResult<usize> {
406 let new = self.with_context_data_mut(|context_data| {
407 let new = context_data.call_depth + 1;
408 context_data.call_depth = new;
409 new
410 });
411 if new > MAX_CALL_DEPTH {
412 return Err(VmError::max_call_depth_exceeded());
413 }
414 Ok(new)
415 }
416
417 pub fn decrement_call_depth(&self) -> usize {
419 self.with_context_data_mut(|context_data| {
420 let new = context_data
421 .call_depth
422 .checked_sub(1)
423 .expect("Call depth < 0. This is a bug.");
424 context_data.call_depth = new;
425 new
426 })
427 }
428
429 pub fn get_gas_left(&self, store: &mut impl AsStoreMut) -> u64 {
433 self.with_wasmer_instance(|instance| {
434 Ok(match get_remaining_points(store, instance) {
435 MeteringPoints::Remaining(count) => count,
436 MeteringPoints::Exhausted => 0,
437 })
438 })
439 .expect("Wasmer instance is not set. This is a bug in the lifecycle.")
440 }
441
442 pub fn set_gas_left(&self, store: &mut impl AsStoreMut, new_value: u64) {
446 self.with_wasmer_instance(|instance| {
447 set_remaining_points(store, instance, new_value);
448 Ok(())
449 })
450 .expect("Wasmer instance is not set. This is a bug in the lifecycle.")
451 }
452
453 #[allow(unused)] pub fn decrease_gas_left(&self, store: &mut impl AsStoreMut, amount: u64) -> VmResult<()> {
458 self.with_wasmer_instance(|instance| {
459 let remaining = match get_remaining_points(store, instance) {
460 MeteringPoints::Remaining(count) => count,
461 MeteringPoints::Exhausted => 0,
462 };
463 if amount > remaining {
464 set_remaining_points(store, instance, 0);
465 Err(VmError::gas_depletion())
466 } else {
467 set_remaining_points(store, instance, remaining - amount);
468 Ok(())
469 }
470 })
471 }
472
473 pub fn memory<'a>(&self, store: &'a impl AsStoreMut) -> MemoryView<'a> {
476 self.memory
477 .as_ref()
478 .expect("Memory is not set. This is a bug in the lifecycle.")
479 .view(store)
480 }
481
482 pub fn move_in(&self, storage: S, querier: Q) {
485 self.with_context_data_mut(|context_data| {
486 context_data.storage = Some(storage);
487 context_data.querier = Some(querier);
488 });
489 }
490
491 pub fn move_out(&self) -> (Option<S>, Option<Q>) {
494 self.with_context_data_mut(|context_data| {
495 (context_data.storage.take(), context_data.querier.take())
496 })
497 }
498}
499
500pub struct ContextData<S, Q> {
501 gas_state: GasState,
502 storage: Option<S>,
503 storage_readonly: bool,
504 call_depth: usize,
505 querier: Option<Q>,
506 debug_handler: Option<Rc<RefCell<DebugHandlerFn>>>,
507 wasmer_instance: Option<NonNull<WasmerInstance>>,
509}
510
511impl<S: Storage, Q: Querier> ContextData<S, Q> {
512 pub fn new(gas_limit: u64) -> Self {
513 ContextData::<S, Q> {
514 gas_state: GasState::with_limit(gas_limit),
515 storage: None,
516 storage_readonly: true,
517 call_depth: 0,
518 querier: None,
519 debug_handler: None,
520 wasmer_instance: None,
521 }
522 }
523}
524
525pub fn process_gas_info<A: BackendApi, S: Storage, Q: Querier>(
526 env: &Environment<A, S, Q>,
527 store: &mut impl AsStoreMut,
528 info: GasInfo,
529) -> VmResult<()> {
530 let gas_left = env.get_gas_left(store);
531
532 let new_limit = env.with_gas_state_mut(|gas_state| {
533 gas_state.externally_used_gas += info.externally_used;
534 gas_left
537 .saturating_sub(info.externally_used)
538 .saturating_sub(info.cost)
539 });
540
541 env.set_gas_left(store, new_limit);
543
544 if info.externally_used + info.cost > gas_left {
545 Err(VmError::gas_depletion())
546 } else {
547 Ok(())
548 }
549}
550
551#[cfg(test)]
552mod tests {
553 use super::*;
554 use crate::conversion::ref_to_u32;
555 use crate::size::Size;
556 use crate::testing::{MockApi, MockQuerier, MockStorage};
557 use crate::wasm_backend::{compile, make_compiling_engine};
558 use cosmwasm_std::{
559 coin, coins, from_json, to_json_vec, BalanceResponse, BankQuery, Empty, QueryRequest,
560 };
561 use wasmer::{imports, Function, Instance as WasmerInstance, Store};
562
563 static HACKATOM: &[u8] = include_bytes!("../testdata/hackatom.wasm");
564
565 const INIT_KEY: &[u8] = b"foo";
567 const INIT_VALUE: &[u8] = b"bar";
568 const INIT_ADDR: &str = "someone";
570 const INIT_AMOUNT: u128 = 500;
571 const INIT_DENOM: &str = "TOKEN";
572
573 const TESTING_GAS_LIMIT: u64 = 500_000_000; const DEFAULT_QUERY_GAS_LIMIT: u64 = 300_000;
575 const TESTING_MEMORY_LIMIT: Option<Size> = Some(Size::mebi(16));
576
577 fn make_instance(
578 gas_limit: u64,
579 ) -> (
580 Environment<MockApi, MockStorage, MockQuerier>,
581 Store,
582 Box<WasmerInstance>,
583 ) {
584 let env = Environment::new(MockApi::default(), gas_limit);
585
586 let engine = make_compiling_engine(TESTING_MEMORY_LIMIT);
587 let module = compile(&engine, HACKATOM).unwrap();
588 let mut store = Store::new(engine);
589
590 let import_obj = imports! {
592 "env" => {
593 "db_read" => Function::new_typed(&mut store, |_a: u32| -> u32 { 0 }),
594 "db_write" => Function::new_typed(&mut store, |_a: u32, _b: u32| {}),
595 "db_remove" => Function::new_typed(&mut store, |_a: u32| {}),
596 "db_scan" => Function::new_typed(&mut store, |_a: u32, _b: u32, _c: i32| -> u32 { 0 }),
597 "db_next" => Function::new_typed(&mut store, |_a: u32| -> u32 { 0 }),
598 "db_next_key" => Function::new_typed(&mut store, |_a: u32| -> u32 { 0 }),
599 "db_next_value" => Function::new_typed(&mut store, |_a: u32| -> u32 { 0 }),
600 "query_chain" => Function::new_typed(&mut store, |_a: u32| -> u32 { 0 }),
601 "addr_validate" => Function::new_typed(&mut store, |_a: u32| -> u32 { 0 }),
602 "addr_canonicalize" => Function::new_typed(&mut store, |_a: u32, _b: u32| -> u32 { 0 }),
603 "addr_humanize" => Function::new_typed(&mut store, |_a: u32, _b: u32| -> u32 { 0 }),
604 "bls12_381_aggregate_g1" => Function::new_typed(&mut store, |_a: u32, _b: u32| -> u32 { 0 }),
605 "bls12_381_aggregate_g2" => Function::new_typed(&mut store, |_a: u32, _b: u32| -> u32 { 0 }),
606 "bls12_381_pairing_equality" => Function::new_typed(&mut store, |_a: u32, _b: u32, _c: u32, _d: u32| -> u32 { 0 }),
607 "bls12_381_hash_to_g1" => Function::new_typed(&mut store, |_a: u32, _b: u32, _c: u32, _d: u32| -> u32 { 0 }),
608 "bls12_381_hash_to_g2" => Function::new_typed(&mut store, |_a: u32, _b: u32, _c: u32, _d: u32| -> u32 { 0 }),
609 "secp256k1_verify" => Function::new_typed(&mut store, |_a: u32, _b: u32, _c: u32| -> u32 { 0 }),
610 "secp256k1_recover_pubkey" => Function::new_typed(&mut store, |_a: u32, _b: u32, _c: u32| -> u64 { 0 }),
611 "secp256r1_verify" => Function::new_typed(&mut store, |_a: u32, _b: u32, _c: u32| -> u32 { 0 }),
612 "secp256r1_recover_pubkey" => Function::new_typed(&mut store, |_a: u32, _b: u32, _c: u32| -> u64 { 0 }),
613 "ed25519_verify" => Function::new_typed(&mut store, |_a: u32, _b: u32, _c: u32| -> u32 { 0 }),
614 "ed25519_batch_verify" => Function::new_typed(&mut store, |_a: u32, _b: u32, _c: u32| -> u32 { 0 }),
615 "debug" => Function::new_typed(&mut store, |_a: u32| {}),
616 "abort" => Function::new_typed(&mut store, |_a: u32| {}),
617 },
618 };
619 let instance = Box::from(WasmerInstance::new(&mut store, &module, &import_obj).unwrap());
620
621 let instance_ptr = NonNull::from(instance.as_ref());
622 env.set_wasmer_instance(Some(instance_ptr));
623 env.set_gas_left(&mut store, gas_limit);
624
625 (env, store, instance)
626 }
627
628 fn leave_default_data(env: &Environment<MockApi, MockStorage, MockQuerier>) {
629 let mut storage = MockStorage::new();
631 storage
632 .set(INIT_KEY, INIT_VALUE)
633 .0
634 .expect("error setting value");
635 let querier: MockQuerier<Empty> =
636 MockQuerier::new(&[(INIT_ADDR, &coins(INIT_AMOUNT, INIT_DENOM))]);
637 env.move_in(storage, querier);
638 }
639
640 #[test]
641 fn move_out_works() {
642 let (env, _store, _instance) = make_instance(TESTING_GAS_LIMIT);
643
644 let (inits, initq) = env.move_out();
646 assert!(inits.is_none());
647 assert!(initq.is_none());
648
649 leave_default_data(&env);
651 let (s, q) = env.move_out();
652 assert!(s.is_some());
653 assert!(q.is_some());
654 assert_eq!(
655 s.unwrap().get(INIT_KEY).0.unwrap(),
656 Some(INIT_VALUE.to_vec())
657 );
658
659 let (ends, endq) = env.move_out();
661 assert!(ends.is_none());
662 assert!(endq.is_none());
663 }
664
665 #[test]
666 fn process_gas_info_works_for_cost() {
667 let (env, mut store, _instance) = make_instance(100);
668 assert_eq!(env.get_gas_left(&mut store), 100);
669
670 process_gas_info(&env, &mut store, GasInfo::with_cost(70)).unwrap();
672 assert_eq!(env.get_gas_left(&mut store), 30);
673 process_gas_info(&env, &mut store, GasInfo::with_cost(4)).unwrap();
674 assert_eq!(env.get_gas_left(&mut store), 26);
675 process_gas_info(&env, &mut store, GasInfo::with_cost(6)).unwrap();
676 assert_eq!(env.get_gas_left(&mut store), 20);
677 process_gas_info(&env, &mut store, GasInfo::with_cost(20)).unwrap();
678 assert_eq!(env.get_gas_left(&mut store), 0);
679
680 match process_gas_info(&env, &mut store, GasInfo::with_cost(1)).unwrap_err() {
682 VmError::GasDepletion { .. } => {}
683 err => panic!("unexpected error: {err:?}"),
684 }
685 }
686
687 #[test]
688 fn process_gas_info_works_for_externally_used() {
689 let (env, mut store, _instance) = make_instance(100);
690 assert_eq!(env.get_gas_left(&mut store), 100);
691
692 process_gas_info(&env, &mut store, GasInfo::with_externally_used(70)).unwrap();
694 assert_eq!(env.get_gas_left(&mut store), 30);
695 process_gas_info(&env, &mut store, GasInfo::with_externally_used(4)).unwrap();
696 assert_eq!(env.get_gas_left(&mut store), 26);
697 process_gas_info(&env, &mut store, GasInfo::with_externally_used(6)).unwrap();
698 assert_eq!(env.get_gas_left(&mut store), 20);
699 process_gas_info(&env, &mut store, GasInfo::with_externally_used(20)).unwrap();
700 assert_eq!(env.get_gas_left(&mut store), 0);
701
702 match process_gas_info(&env, &mut store, GasInfo::with_externally_used(1)).unwrap_err() {
704 VmError::GasDepletion { .. } => {}
705 err => panic!("unexpected error: {err:?}"),
706 }
707 }
708
709 #[test]
710 fn process_gas_info_works_for_cost_and_externally_used() {
711 let (env, mut store, _instance) = make_instance(100);
712 assert_eq!(env.get_gas_left(&mut store), 100);
713 let gas_state = env.with_gas_state(|gas_state| gas_state.clone());
714 assert_eq!(gas_state.gas_limit, 100);
715 assert_eq!(gas_state.externally_used_gas, 0);
716
717 process_gas_info(&env, &mut store, GasInfo::new(17, 4)).unwrap();
718 assert_eq!(env.get_gas_left(&mut store), 79);
719 let gas_state = env.with_gas_state(|gas_state| gas_state.clone());
720 assert_eq!(gas_state.gas_limit, 100);
721 assert_eq!(gas_state.externally_used_gas, 4);
722
723 process_gas_info(&env, &mut store, GasInfo::new(9, 0)).unwrap();
724 assert_eq!(env.get_gas_left(&mut store), 70);
725 let gas_state = env.with_gas_state(|gas_state| gas_state.clone());
726 assert_eq!(gas_state.gas_limit, 100);
727 assert_eq!(gas_state.externally_used_gas, 4);
728
729 process_gas_info(&env, &mut store, GasInfo::new(0, 70)).unwrap();
730 assert_eq!(env.get_gas_left(&mut store), 0);
731 let gas_state = env.with_gas_state(|gas_state| gas_state.clone());
732 assert_eq!(gas_state.gas_limit, 100);
733 assert_eq!(gas_state.externally_used_gas, 74);
734
735 match process_gas_info(&env, &mut store, GasInfo::new(1, 0)).unwrap_err() {
737 VmError::GasDepletion { .. } => {}
738 err => panic!("unexpected error: {err:?}"),
739 }
740 assert_eq!(env.get_gas_left(&mut store), 0);
741 let gas_state = env.with_gas_state(|gas_state| gas_state.clone());
742 assert_eq!(gas_state.gas_limit, 100);
743 assert_eq!(gas_state.externally_used_gas, 74);
744
745 match process_gas_info(&env, &mut store, GasInfo::new(0, 1)).unwrap_err() {
747 VmError::GasDepletion { .. } => {}
748 err => panic!("unexpected error: {err:?}"),
749 }
750 assert_eq!(env.get_gas_left(&mut store), 0);
751 let gas_state = env.with_gas_state(|gas_state| gas_state.clone());
752 assert_eq!(gas_state.gas_limit, 100);
753 assert_eq!(gas_state.externally_used_gas, 75);
754 }
755
756 #[test]
757 fn process_gas_info_zeros_gas_left_when_exceeded() {
758 {
760 let (env, mut store, _instance) = make_instance(100);
761 let result = process_gas_info(&env, &mut store, GasInfo::with_externally_used(120));
762 match result.unwrap_err() {
763 VmError::GasDepletion { .. } => {}
764 err => panic!("unexpected error: {err:?}"),
765 }
766 assert_eq!(env.get_gas_left(&mut store), 0);
767 let gas_state = env.with_gas_state(|gas_state| gas_state.clone());
768 assert_eq!(gas_state.gas_limit, 100);
769 assert_eq!(gas_state.externally_used_gas, 120);
770 }
771
772 {
774 let (env, mut store, _instance) = make_instance(100);
775 let result = process_gas_info(&env, &mut store, GasInfo::with_cost(120));
776 match result.unwrap_err() {
777 VmError::GasDepletion { .. } => {}
778 err => panic!("unexpected error: {err:?}"),
779 }
780 assert_eq!(env.get_gas_left(&mut store), 0);
781 let gas_state = env.with_gas_state(|gas_state| gas_state.clone());
782 assert_eq!(gas_state.gas_limit, 100);
783 assert_eq!(gas_state.externally_used_gas, 0);
784 }
785 }
786
787 #[test]
788 fn process_gas_info_works_correctly_with_gas_consumption_in_wasmer() {
789 let (env, mut store, _instance) = make_instance(100);
790 assert_eq!(env.get_gas_left(&mut store), 100);
791
792 process_gas_info(&env, &mut store, GasInfo::with_externally_used(50)).unwrap();
794 assert_eq!(env.get_gas_left(&mut store), 50);
795 process_gas_info(&env, &mut store, GasInfo::with_externally_used(4)).unwrap();
796 assert_eq!(env.get_gas_left(&mut store), 46);
797
798 env.decrease_gas_left(&mut store, 20).unwrap();
800 assert_eq!(env.get_gas_left(&mut store), 26);
801
802 process_gas_info(&env, &mut store, GasInfo::with_externally_used(6)).unwrap();
803 assert_eq!(env.get_gas_left(&mut store), 20);
804 process_gas_info(&env, &mut store, GasInfo::with_externally_used(20)).unwrap();
805 assert_eq!(env.get_gas_left(&mut store), 0);
806
807 match process_gas_info(&env, &mut store, GasInfo::with_externally_used(1)).unwrap_err() {
809 VmError::GasDepletion { .. } => {}
810 err => panic!("unexpected error: {err:?}"),
811 }
812 }
813
814 #[test]
815 fn is_storage_readonly_defaults_to_true() {
816 let (env, _store, _instance) = make_instance(TESTING_GAS_LIMIT);
817 leave_default_data(&env);
818
819 assert!(env.is_storage_readonly());
820 }
821
822 #[test]
823 fn set_storage_readonly_can_change_flag() {
824 let (env, _store, _instance) = make_instance(TESTING_GAS_LIMIT);
825 leave_default_data(&env);
826
827 env.set_storage_readonly(false);
829 assert!(!env.is_storage_readonly());
830
831 env.set_storage_readonly(false);
833 assert!(!env.is_storage_readonly());
834
835 env.set_storage_readonly(true);
837 assert!(env.is_storage_readonly());
838 }
839
840 #[test]
841 fn call_function_works() {
842 let (env, mut store, _instance) = make_instance(TESTING_GAS_LIMIT);
843 leave_default_data(&env);
844
845 let result = env
846 .call_function(&mut store, "allocate", &[10u32.into()])
847 .unwrap();
848 let ptr = ref_to_u32(&result[0]).unwrap();
849 assert!(ptr > 0);
850 }
851
852 #[test]
853 fn call_function_fails_for_missing_instance() {
854 let (env, mut store, _instance) = make_instance(TESTING_GAS_LIMIT);
855 leave_default_data(&env);
856
857 env.set_wasmer_instance(None);
859
860 let res = env.call_function(&mut store, "allocate", &[]);
861 match res.unwrap_err() {
862 VmError::UninitializedContextData { kind, .. } => assert_eq!(kind, "wasmer_instance"),
863 err => panic!("Unexpected error: {err:?}"),
864 }
865 }
866
867 #[test]
868 fn call_function_fails_for_missing_function() {
869 let (env, mut store, _instance) = make_instance(TESTING_GAS_LIMIT);
870 leave_default_data(&env);
871
872 let res = env.call_function(&mut store, "doesnt_exist", &[]);
873 match res.unwrap_err() {
874 VmError::ResolveErr { msg, .. } => {
875 assert_eq!(msg, "Could not get export: Missing export doesnt_exist");
876 }
877 err => panic!("Unexpected error: {err:?}"),
878 }
879 }
880
881 #[test]
882 fn call_function0_works() {
883 let (env, mut store, _instance) = make_instance(TESTING_GAS_LIMIT);
884 leave_default_data(&env);
885
886 env.call_function0(&mut store, "interface_version_8", &[])
887 .unwrap();
888 }
889
890 #[test]
891 fn call_function0_errors_for_wrong_result_count() {
892 let (env, mut store, _instance) = make_instance(TESTING_GAS_LIMIT);
893 leave_default_data(&env);
894
895 let result = env.call_function0(&mut store, "allocate", &[10u32.into()]);
896 match result.unwrap_err() {
897 VmError::ResultMismatch {
898 function_name,
899 expected,
900 actual,
901 ..
902 } => {
903 assert_eq!(function_name, "allocate");
904 assert_eq!(expected, 0);
905 assert_eq!(actual, 1);
906 }
907 err => panic!("unexpected error: {err:?}"),
908 }
909 }
910
911 #[test]
912 fn call_function1_works() {
913 let (env, mut store, _instance) = make_instance(TESTING_GAS_LIMIT);
914 leave_default_data(&env);
915
916 let result = env
917 .call_function1(&mut store, "allocate", &[10u32.into()])
918 .unwrap();
919 let ptr = ref_to_u32(&result).unwrap();
920 assert!(ptr > 0);
921 }
922
923 #[test]
924 fn call_function1_errors_for_wrong_result_count() {
925 let (env, mut store, _instance) = make_instance(TESTING_GAS_LIMIT);
926 leave_default_data(&env);
927
928 let result = env
929 .call_function1(&mut store, "allocate", &[10u32.into()])
930 .unwrap();
931 let ptr = ref_to_u32(&result).unwrap();
932 assert!(ptr > 0);
933
934 let result = env.call_function1(&mut store, "deallocate", &[ptr.into()]);
935 match result.unwrap_err() {
936 VmError::ResultMismatch {
937 function_name,
938 expected,
939 actual,
940 ..
941 } => {
942 assert_eq!(function_name, "deallocate");
943 assert_eq!(expected, 1);
944 assert_eq!(actual, 0);
945 }
946 err => panic!("unexpected error: {err:?}"),
947 }
948 }
949
950 #[test]
951 fn with_storage_from_context_set_get() {
952 let (env, _store, _instance) = make_instance(TESTING_GAS_LIMIT);
953 leave_default_data(&env);
954
955 let val = env
956 .with_storage_from_context::<_, _>(|store| {
957 Ok(store.get(INIT_KEY).0.expect("error getting value"))
958 })
959 .unwrap();
960 assert_eq!(val, Some(INIT_VALUE.to_vec()));
961
962 let set_key: &[u8] = b"more";
963 let set_value: &[u8] = b"data";
964
965 env.with_storage_from_context::<_, _>(|store| {
966 store
967 .set(set_key, set_value)
968 .0
969 .expect("error setting value");
970 Ok(())
971 })
972 .unwrap();
973
974 env.with_storage_from_context::<_, _>(|store| {
975 assert_eq!(store.get(INIT_KEY).0.unwrap(), Some(INIT_VALUE.to_vec()));
976 assert_eq!(store.get(set_key).0.unwrap(), Some(set_value.to_vec()));
977 Ok(())
978 })
979 .unwrap();
980 }
981
982 #[test]
983 #[should_panic(expected = "A panic occurred in the callback.")]
984 fn with_storage_from_context_handles_panics() {
985 let (env, _store, _instance) = make_instance(TESTING_GAS_LIMIT);
986 leave_default_data(&env);
987
988 env.with_storage_from_context::<_, ()>(|_store| {
989 panic!("A panic occurred in the callback.")
990 })
991 .unwrap();
992 }
993
994 #[test]
995 #[allow(deprecated)]
996 fn with_querier_from_context_works() {
997 let (env, _store, _instance) = make_instance(TESTING_GAS_LIMIT);
998 leave_default_data(&env);
999
1000 let res = env
1001 .with_querier_from_context::<_, _>(|querier| {
1002 let req: QueryRequest<Empty> = QueryRequest::Bank(BankQuery::Balance {
1003 address: INIT_ADDR.to_string(),
1004 denom: INIT_DENOM.to_string(),
1005 });
1006 let (result, _gas_info) =
1007 querier.query_raw(&to_json_vec(&req).unwrap(), DEFAULT_QUERY_GAS_LIMIT);
1008 Ok(result.unwrap())
1009 })
1010 .unwrap()
1011 .unwrap()
1012 .unwrap();
1013 let balance: BalanceResponse = from_json(res).unwrap();
1014
1015 assert_eq!(balance.amount, coin(INIT_AMOUNT, INIT_DENOM));
1016 }
1017
1018 #[test]
1019 #[should_panic(expected = "A panic occurred in the callback.")]
1020 fn with_querier_from_context_handles_panics() {
1021 let (env, _store, _instance) = make_instance(TESTING_GAS_LIMIT);
1022 leave_default_data(&env);
1023
1024 env.with_querier_from_context::<_, ()>(|_querier| {
1025 panic!("A panic occurred in the callback.")
1026 })
1027 .unwrap();
1028 }
1029}