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 = gas_state
534 .externally_used_gas
535 .saturating_add(info.externally_used);
536 gas_left
539 .saturating_sub(info.externally_used)
540 .saturating_sub(info.cost)
541 });
542
543 env.set_gas_left(store, new_limit);
545
546 let Some(gas_total) = info.externally_used.checked_add(info.cost) else {
547 return Err(VmError::gas_depletion());
548 };
549 if gas_total > gas_left {
550 Err(VmError::gas_depletion())
551 } else {
552 Ok(())
553 }
554}
555
556#[cfg(test)]
557mod tests {
558 use super::*;
559 use crate::conversion::ref_to_u32;
560 use crate::size::Size;
561 use crate::testing::{MockApi, MockQuerier, MockStorage};
562 use crate::wasm_backend::compile_module;
563 use cosmwasm_std::{
564 coin, coins, from_json, to_json_vec, BalanceResponse, BankQuery, Empty, QueryRequest,
565 };
566 use wasmer::{imports, Function, Instance as WasmerInstance, Store};
567
568 static HACKATOM: &[u8] = include_bytes!("../testdata/hackatom.wasm");
569
570 const INIT_KEY: &[u8] = b"foo";
572 const INIT_VALUE: &[u8] = b"bar";
573 const INIT_ADDR: &str = "someone";
575 const INIT_AMOUNT: u128 = 500;
576 const INIT_DENOM: &str = "TOKEN";
577
578 const TESTING_GAS_LIMIT: u64 = 500_000_000; const DEFAULT_QUERY_GAS_LIMIT: u64 = 300_000;
580 const TESTING_MEMORY_LIMIT: Option<Size> = Some(Size::mebi(16));
581
582 fn make_instance(
583 gas_limit: u64,
584 ) -> (
585 Environment<MockApi, MockStorage, MockQuerier>,
586 Store,
587 Box<WasmerInstance>,
588 ) {
589 let env = Environment::new(MockApi::default(), gas_limit);
590
591 let (module, engine) = compile_module(HACKATOM, TESTING_MEMORY_LIMIT).unwrap();
592 let mut store = Store::new(engine);
593
594 let import_obj = imports! {
596 "env" => {
597 "db_read" => Function::new_typed(&mut store, |_a: u32| -> u32 { 0 }),
598 "db_write" => Function::new_typed(&mut store, |_a: u32, _b: u32| {}),
599 "db_remove" => Function::new_typed(&mut store, |_a: u32| {}),
600 "db_scan" => Function::new_typed(&mut store, |_a: u32, _b: u32, _c: i32| -> u32 { 0 }),
601 "db_next" => Function::new_typed(&mut store, |_a: u32| -> u32 { 0 }),
602 "db_next_key" => Function::new_typed(&mut store, |_a: u32| -> u32 { 0 }),
603 "db_next_value" => Function::new_typed(&mut store, |_a: u32| -> u32 { 0 }),
604 "query_chain" => Function::new_typed(&mut store, |_a: u32| -> u32 { 0 }),
605 "addr_validate" => Function::new_typed(&mut store, |_a: u32| -> u32 { 0 }),
606 "addr_canonicalize" => Function::new_typed(&mut store, |_a: u32, _b: u32| -> u32 { 0 }),
607 "addr_humanize" => Function::new_typed(&mut store, |_a: u32, _b: u32| -> u32 { 0 }),
608 "bls12_381_aggregate_g1" => Function::new_typed(&mut store, |_a: u32, _b: u32| -> u32 { 0 }),
609 "bls12_381_aggregate_g2" => Function::new_typed(&mut store, |_a: u32, _b: u32| -> u32 { 0 }),
610 "bls12_381_pairing_equality" => Function::new_typed(&mut store, |_a: u32, _b: u32, _c: u32, _d: u32| -> u32 { 0 }),
611 "bls12_381_hash_to_g1" => Function::new_typed(&mut store, |_a: u32, _b: u32, _c: u32, _d: u32| -> u32 { 0 }),
612 "bls12_381_hash_to_g2" => Function::new_typed(&mut store, |_a: u32, _b: u32, _c: u32, _d: u32| -> u32 { 0 }),
613 "secp256k1_verify" => Function::new_typed(&mut store, |_a: u32, _b: u32, _c: u32| -> u32 { 0 }),
614 "secp256k1_recover_pubkey" => Function::new_typed(&mut store, |_a: u32, _b: u32, _c: u32| -> u64 { 0 }),
615 "secp256r1_verify" => Function::new_typed(&mut store, |_a: u32, _b: u32, _c: u32| -> u32 { 0 }),
616 "secp256r1_recover_pubkey" => Function::new_typed(&mut store, |_a: u32, _b: u32, _c: u32| -> u64 { 0 }),
617 "ed25519_verify" => Function::new_typed(&mut store, |_a: u32, _b: u32, _c: u32| -> u32 { 0 }),
618 "ed25519_batch_verify" => Function::new_typed(&mut store, |_a: u32, _b: u32, _c: u32| -> u32 { 0 }),
619 "debug" => Function::new_typed(&mut store, |_a: u32| {}),
620 "abort" => Function::new_typed(&mut store, |_a: u32| {}),
621 },
622 };
623 let instance = Box::from(WasmerInstance::new(&mut store, &module, &import_obj).unwrap());
624
625 let instance_ptr = NonNull::from(instance.as_ref());
626 env.set_wasmer_instance(Some(instance_ptr));
627 env.set_gas_left(&mut store, gas_limit);
628
629 (env, store, instance)
630 }
631
632 fn leave_default_data(env: &Environment<MockApi, MockStorage, MockQuerier>) {
633 let mut storage = MockStorage::new();
635 storage
636 .set(INIT_KEY, INIT_VALUE)
637 .0
638 .expect("error setting value");
639 let querier: MockQuerier<Empty> =
640 MockQuerier::new(&[(INIT_ADDR, &coins(INIT_AMOUNT, INIT_DENOM))]);
641 env.move_in(storage, querier);
642 }
643
644 #[test]
645 fn move_out_works() {
646 let (env, _store, _instance) = make_instance(TESTING_GAS_LIMIT);
647
648 let (inits, initq) = env.move_out();
650 assert!(inits.is_none());
651 assert!(initq.is_none());
652
653 leave_default_data(&env);
655 let (s, q) = env.move_out();
656 assert!(s.is_some());
657 assert!(q.is_some());
658 assert_eq!(
659 s.unwrap().get(INIT_KEY).0.unwrap(),
660 Some(INIT_VALUE.to_vec())
661 );
662
663 let (ends, endq) = env.move_out();
665 assert!(ends.is_none());
666 assert!(endq.is_none());
667 }
668
669 #[test]
670 fn process_gas_info_works_for_cost() {
671 let (env, mut store, _instance) = make_instance(100);
672 assert_eq!(env.get_gas_left(&mut store), 100);
673
674 process_gas_info(&env, &mut store, GasInfo::with_cost(70)).unwrap();
676 assert_eq!(env.get_gas_left(&mut store), 30);
677 process_gas_info(&env, &mut store, GasInfo::with_cost(4)).unwrap();
678 assert_eq!(env.get_gas_left(&mut store), 26);
679 process_gas_info(&env, &mut store, GasInfo::with_cost(6)).unwrap();
680 assert_eq!(env.get_gas_left(&mut store), 20);
681 process_gas_info(&env, &mut store, GasInfo::with_cost(20)).unwrap();
682 assert_eq!(env.get_gas_left(&mut store), 0);
683
684 match process_gas_info(&env, &mut store, GasInfo::with_cost(1)).unwrap_err() {
686 VmError::GasDepletion { .. } => {}
687 err => panic!("unexpected error: {err:?}"),
688 }
689 }
690
691 #[test]
692 fn process_gas_info_works_for_externally_used() {
693 let (env, mut store, _instance) = make_instance(100);
694 assert_eq!(env.get_gas_left(&mut store), 100);
695
696 process_gas_info(&env, &mut store, GasInfo::with_externally_used(70)).unwrap();
698 assert_eq!(env.get_gas_left(&mut store), 30);
699 process_gas_info(&env, &mut store, GasInfo::with_externally_used(4)).unwrap();
700 assert_eq!(env.get_gas_left(&mut store), 26);
701 process_gas_info(&env, &mut store, GasInfo::with_externally_used(6)).unwrap();
702 assert_eq!(env.get_gas_left(&mut store), 20);
703 process_gas_info(&env, &mut store, GasInfo::with_externally_used(20)).unwrap();
704 assert_eq!(env.get_gas_left(&mut store), 0);
705
706 match process_gas_info(&env, &mut store, GasInfo::with_externally_used(1)).unwrap_err() {
708 VmError::GasDepletion { .. } => {}
709 err => panic!("unexpected error: {err:?}"),
710 }
711 }
712
713 #[test]
714 fn process_gas_info_works_for_cost_and_externally_used() {
715 let (env, mut store, _instance) = make_instance(100);
716 assert_eq!(env.get_gas_left(&mut store), 100);
717 let gas_state = env.with_gas_state(|gas_state| gas_state.clone());
718 assert_eq!(gas_state.gas_limit, 100);
719 assert_eq!(gas_state.externally_used_gas, 0);
720
721 process_gas_info(&env, &mut store, GasInfo::new(17, 4)).unwrap();
722 assert_eq!(env.get_gas_left(&mut store), 79);
723 let gas_state = env.with_gas_state(|gas_state| gas_state.clone());
724 assert_eq!(gas_state.gas_limit, 100);
725 assert_eq!(gas_state.externally_used_gas, 4);
726
727 process_gas_info(&env, &mut store, GasInfo::new(9, 0)).unwrap();
728 assert_eq!(env.get_gas_left(&mut store), 70);
729 let gas_state = env.with_gas_state(|gas_state| gas_state.clone());
730 assert_eq!(gas_state.gas_limit, 100);
731 assert_eq!(gas_state.externally_used_gas, 4);
732
733 process_gas_info(&env, &mut store, GasInfo::new(0, 70)).unwrap();
734 assert_eq!(env.get_gas_left(&mut store), 0);
735 let gas_state = env.with_gas_state(|gas_state| gas_state.clone());
736 assert_eq!(gas_state.gas_limit, 100);
737 assert_eq!(gas_state.externally_used_gas, 74);
738
739 match process_gas_info(&env, &mut store, GasInfo::new(1, 0)).unwrap_err() {
741 VmError::GasDepletion { .. } => {}
742 err => panic!("unexpected error: {err:?}"),
743 }
744 assert_eq!(env.get_gas_left(&mut store), 0);
745 let gas_state = env.with_gas_state(|gas_state| gas_state.clone());
746 assert_eq!(gas_state.gas_limit, 100);
747 assert_eq!(gas_state.externally_used_gas, 74);
748
749 match process_gas_info(&env, &mut store, GasInfo::new(0, 1)).unwrap_err() {
751 VmError::GasDepletion { .. } => {}
752 err => panic!("unexpected error: {err:?}"),
753 }
754 assert_eq!(env.get_gas_left(&mut store), 0);
755 let gas_state = env.with_gas_state(|gas_state| gas_state.clone());
756 assert_eq!(gas_state.gas_limit, 100);
757 assert_eq!(gas_state.externally_used_gas, 75);
758 }
759
760 #[test]
761 fn process_gas_info_zeros_gas_left_when_exceeded() {
762 {
764 let (env, mut store, _instance) = make_instance(100);
765 let result = process_gas_info(&env, &mut store, GasInfo::with_externally_used(120));
766 match result.unwrap_err() {
767 VmError::GasDepletion { .. } => {}
768 err => panic!("unexpected error: {err:?}"),
769 }
770 assert_eq!(env.get_gas_left(&mut store), 0);
771 let gas_state = env.with_gas_state(|gas_state| gas_state.clone());
772 assert_eq!(gas_state.gas_limit, 100);
773 assert_eq!(gas_state.externally_used_gas, 120);
774 }
775
776 {
778 let (env, mut store, _instance) = make_instance(100);
779 let result = process_gas_info(&env, &mut store, GasInfo::with_cost(120));
780 match result.unwrap_err() {
781 VmError::GasDepletion { .. } => {}
782 err => panic!("unexpected error: {err:?}"),
783 }
784 assert_eq!(env.get_gas_left(&mut store), 0);
785 let gas_state = env.with_gas_state(|gas_state| gas_state.clone());
786 assert_eq!(gas_state.gas_limit, 100);
787 assert_eq!(gas_state.externally_used_gas, 0);
788 }
789 }
790
791 #[test]
792 fn process_gas_info_works_correctly_with_gas_consumption_in_wasmer() {
793 let (env, mut store, _instance) = make_instance(100);
794 assert_eq!(env.get_gas_left(&mut store), 100);
795
796 process_gas_info(&env, &mut store, GasInfo::with_externally_used(50)).unwrap();
798 assert_eq!(env.get_gas_left(&mut store), 50);
799 process_gas_info(&env, &mut store, GasInfo::with_externally_used(4)).unwrap();
800 assert_eq!(env.get_gas_left(&mut store), 46);
801
802 env.decrease_gas_left(&mut store, 20).unwrap();
804 assert_eq!(env.get_gas_left(&mut store), 26);
805
806 process_gas_info(&env, &mut store, GasInfo::with_externally_used(6)).unwrap();
807 assert_eq!(env.get_gas_left(&mut store), 20);
808 process_gas_info(&env, &mut store, GasInfo::with_externally_used(20)).unwrap();
809 assert_eq!(env.get_gas_left(&mut store), 0);
810
811 match process_gas_info(&env, &mut store, GasInfo::with_externally_used(1)).unwrap_err() {
813 VmError::GasDepletion { .. } => {}
814 err => panic!("unexpected error: {err:?}"),
815 }
816 }
817
818 #[test]
819 fn is_storage_readonly_defaults_to_true() {
820 let (env, _store, _instance) = make_instance(TESTING_GAS_LIMIT);
821 leave_default_data(&env);
822
823 assert!(env.is_storage_readonly());
824 }
825
826 #[test]
827 fn set_storage_readonly_can_change_flag() {
828 let (env, _store, _instance) = make_instance(TESTING_GAS_LIMIT);
829 leave_default_data(&env);
830
831 env.set_storage_readonly(false);
833 assert!(!env.is_storage_readonly());
834
835 env.set_storage_readonly(false);
837 assert!(!env.is_storage_readonly());
838
839 env.set_storage_readonly(true);
841 assert!(env.is_storage_readonly());
842 }
843
844 #[test]
845 fn call_function_works() {
846 let (env, mut store, _instance) = make_instance(TESTING_GAS_LIMIT);
847 leave_default_data(&env);
848
849 let result = env
850 .call_function(&mut store, "allocate", &[10u32.into()])
851 .unwrap();
852 let ptr = ref_to_u32(&result[0]).unwrap();
853 assert!(ptr > 0);
854 }
855
856 #[test]
857 fn call_function_fails_for_missing_instance() {
858 let (env, mut store, _instance) = make_instance(TESTING_GAS_LIMIT);
859 leave_default_data(&env);
860
861 env.set_wasmer_instance(None);
863
864 let res = env.call_function(&mut store, "allocate", &[]);
865 match res.unwrap_err() {
866 VmError::UninitializedContextData { kind, .. } => assert_eq!(kind, "wasmer_instance"),
867 err => panic!("Unexpected error: {err:?}"),
868 }
869 }
870
871 #[test]
872 fn call_function_fails_for_missing_function() {
873 let (env, mut store, _instance) = make_instance(TESTING_GAS_LIMIT);
874 leave_default_data(&env);
875
876 let res = env.call_function(&mut store, "doesnt_exist", &[]);
877 match res.unwrap_err() {
878 VmError::ResolveErr { msg, .. } => {
879 assert_eq!(msg, "Could not get export: Missing export doesnt_exist");
880 }
881 err => panic!("Unexpected error: {err:?}"),
882 }
883 }
884
885 #[test]
886 fn call_function0_works() {
887 let (env, mut store, _instance) = make_instance(TESTING_GAS_LIMIT);
888 leave_default_data(&env);
889
890 env.call_function0(&mut store, "interface_version_8", &[])
891 .unwrap();
892 }
893
894 #[test]
895 fn call_function0_errors_for_wrong_result_count() {
896 let (env, mut store, _instance) = make_instance(TESTING_GAS_LIMIT);
897 leave_default_data(&env);
898
899 let result = env.call_function0(&mut store, "allocate", &[10u32.into()]);
900 match result.unwrap_err() {
901 VmError::ResultMismatch {
902 function_name,
903 expected,
904 actual,
905 ..
906 } => {
907 assert_eq!(function_name, "allocate");
908 assert_eq!(expected, 0);
909 assert_eq!(actual, 1);
910 }
911 err => panic!("unexpected error: {err:?}"),
912 }
913 }
914
915 #[test]
916 fn call_function1_works() {
917 let (env, mut store, _instance) = make_instance(TESTING_GAS_LIMIT);
918 leave_default_data(&env);
919
920 let result = env
921 .call_function1(&mut store, "allocate", &[10u32.into()])
922 .unwrap();
923 let ptr = ref_to_u32(&result).unwrap();
924 assert!(ptr > 0);
925 }
926
927 #[test]
928 fn call_function1_errors_for_wrong_result_count() {
929 let (env, mut store, _instance) = make_instance(TESTING_GAS_LIMIT);
930 leave_default_data(&env);
931
932 let result = env
933 .call_function1(&mut store, "allocate", &[10u32.into()])
934 .unwrap();
935 let ptr = ref_to_u32(&result).unwrap();
936 assert!(ptr > 0);
937
938 let result = env.call_function1(&mut store, "deallocate", &[ptr.into()]);
939 match result.unwrap_err() {
940 VmError::ResultMismatch {
941 function_name,
942 expected,
943 actual,
944 ..
945 } => {
946 assert_eq!(function_name, "deallocate");
947 assert_eq!(expected, 1);
948 assert_eq!(actual, 0);
949 }
950 err => panic!("unexpected error: {err:?}"),
951 }
952 }
953
954 #[test]
955 fn with_storage_from_context_set_get() {
956 let (env, _store, _instance) = make_instance(TESTING_GAS_LIMIT);
957 leave_default_data(&env);
958
959 let val = env
960 .with_storage_from_context::<_, _>(|store| {
961 Ok(store.get(INIT_KEY).0.expect("error getting value"))
962 })
963 .unwrap();
964 assert_eq!(val, Some(INIT_VALUE.to_vec()));
965
966 let set_key: &[u8] = b"more";
967 let set_value: &[u8] = b"data";
968
969 env.with_storage_from_context::<_, _>(|store| {
970 store
971 .set(set_key, set_value)
972 .0
973 .expect("error setting value");
974 Ok(())
975 })
976 .unwrap();
977
978 env.with_storage_from_context::<_, _>(|store| {
979 assert_eq!(store.get(INIT_KEY).0.unwrap(), Some(INIT_VALUE.to_vec()));
980 assert_eq!(store.get(set_key).0.unwrap(), Some(set_value.to_vec()));
981 Ok(())
982 })
983 .unwrap();
984 }
985
986 #[test]
987 #[should_panic(expected = "A panic occurred in the callback.")]
988 fn with_storage_from_context_handles_panics() {
989 let (env, _store, _instance) = make_instance(TESTING_GAS_LIMIT);
990 leave_default_data(&env);
991
992 env.with_storage_from_context::<_, ()>(|_store| {
993 panic!("A panic occurred in the callback.")
994 })
995 .unwrap();
996 }
997
998 #[test]
999 #[allow(deprecated)]
1000 fn with_querier_from_context_works() {
1001 let (env, _store, _instance) = make_instance(TESTING_GAS_LIMIT);
1002 leave_default_data(&env);
1003
1004 let res = env
1005 .with_querier_from_context::<_, _>(|querier| {
1006 let req: QueryRequest<Empty> = QueryRequest::Bank(BankQuery::Balance {
1007 address: INIT_ADDR.to_string(),
1008 denom: INIT_DENOM.to_string(),
1009 });
1010 let (result, _gas_info) =
1011 querier.query_raw(&to_json_vec(&req).unwrap(), DEFAULT_QUERY_GAS_LIMIT);
1012 Ok(result.unwrap())
1013 })
1014 .unwrap()
1015 .unwrap()
1016 .unwrap();
1017 let balance: BalanceResponse = from_json(res).unwrap();
1018
1019 assert_eq!(balance.amount, coin(INIT_AMOUNT, INIT_DENOM));
1020 }
1021
1022 #[test]
1023 #[should_panic(expected = "A panic occurred in the callback.")]
1024 fn with_querier_from_context_handles_panics() {
1025 let (env, _store, _instance) = make_instance(TESTING_GAS_LIMIT);
1026 leave_default_data(&env);
1027
1028 env.with_querier_from_context::<_, ()>(|_querier| {
1029 panic!("A panic occurred in the callback.")
1030 })
1031 .unwrap();
1032 }
1033
1034 #[test]
1035 fn gas_depletion_must_not_be_overpassed() {
1036 let (env, mut store, _instance) = make_instance(100);
1037 let gas_info = GasInfo {
1038 externally_used: u64::MAX / 2 + 1,
1039 cost: u64::MAX / 2 + 1,
1040 };
1041 assert!(matches!(
1042 process_gas_info(&env, &mut store, gas_info).err().unwrap(),
1043 VmError::GasDepletion { .. }
1044 ));
1045 }
1046
1047 #[test]
1048 fn gas_info_add_assign_should_saturate() {
1049 let mut gas_info = GasInfo {
1050 cost: u64::MAX - 1,
1051 externally_used: u64::MAX - 1,
1052 };
1053 let gas_info_delta = GasInfo {
1054 cost: 2,
1055 externally_used: 2,
1056 };
1057 gas_info += gas_info_delta;
1058 assert_eq!(u64::MAX, gas_info.cost);
1059 assert_eq!(u64::MAX, gas_info.externally_used);
1060 }
1061
1062 #[test]
1063 fn externally_used_gas_should_saturate() {
1064 let (env, mut store, _instance) = make_instance(TESTING_GAS_LIMIT);
1065 let gas_info = GasInfo {
1066 cost: 0,
1067 externally_used: u64::MAX / 2 + 1,
1068 };
1069 let _ = process_gas_info(&env, &mut store, gas_info);
1070 let gas_before = env.with_gas_state(|gas_state| gas_state.externally_used_gas);
1071 assert_eq!(u64::MAX / 2 + 1, gas_before);
1072 let _ = process_gas_info(&env, &mut store, gas_info);
1073 let gas_after = env.with_gas_state(|gas_state| gas_state.externally_used_gas);
1074 assert!(gas_after > gas_before);
1075 assert_eq!(u64::MAX, gas_after);
1076 }
1077}