cosmwasm_vm/
environment.rs

1//! Internal details to be used by instance.rs only
2use 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
16/// Keep this as low as necessary to avoid deepy nested errors like this:
17///
18/// ```plain
19/// RuntimeErr { msg: "Wasmer runtime error: RuntimeError: Error executing Wasm: Wasmer runtime error: RuntimeError: Error executing Wasm: Wasmer runtime error: RuntimeError: Error executing Wasm: Wasmer runtime error: RuntimeError: Error executing Wasm: Wasmer runtime error: RuntimeError: Maximum call depth exceeded." }
20/// ```
21const MAX_CALL_DEPTH: usize = 2;
22
23/// Never can never be instantiated.
24/// Replace this with the [never primitive type](https://doc.rust-lang.org/std/primitive.never.html) when stable.
25#[derive(Debug)]
26pub enum Never {}
27
28/** gas config data */
29
30#[derive(Clone, PartialEq, Eq, Debug)]
31#[non_exhaustive]
32pub struct GasConfig {
33    /// Gas costs of VM (not Backend) provided functionality
34    /// secp256k1 signature verification cost
35    pub secp256k1_verify_cost: u64,
36    /// secp256k1 public key recovery cost
37    pub secp256k1_recover_pubkey_cost: u64,
38    /// secp256r1 signature verification cost
39    pub secp256r1_verify_cost: u64,
40    /// secp256r1 public key recovery cost
41    pub secp256r1_recover_pubkey_cost: u64,
42    /// ed25519 signature verification cost
43    pub ed25519_verify_cost: u64,
44    /// ed25519 batch signature verification cost
45    pub ed25519_batch_verify_cost: LinearGasCost,
46    /// ed25519 batch signature verification cost (single public key)
47    pub ed25519_batch_verify_one_pubkey_cost: LinearGasCost,
48    /// bls12-381 aggregate cost (g1)
49    pub bls12_381_aggregate_g1_cost: LinearGasCost,
50    /// bls12-381 aggregate cost (g2)
51    pub bls12_381_aggregate_g2_cost: LinearGasCost,
52    /// bls12-381 hash to g1 cost
53    pub bls12_381_hash_to_g1_cost: u64,
54    /// bls12-381 hash to g2 cost
55    pub bls12_381_hash_to_g2_cost: u64,
56    /// bls12-381 pairing equality check cost
57    pub bls12_381_pairing_equality_cost: LinearGasCost,
58    /// cost for writing memory regions
59    pub write_region_cost: LinearGasCost,
60    /// cost for reading memory regions <= 8MB
61    pub read_region_small_cost: LinearGasCost,
62    /// cost for reading memory regions > 8MB
63    pub read_region_large_cost: LinearGasCost,
64    /// cost for validating bytes into a String
65    pub string_from_bytes_cost: LinearGasCost,
66    /// cost for calling a host function
67    pub host_call_cost: u64,
68}
69
70impl Default for GasConfig {
71    fn default() -> Self {
72        // Target is 10^12 per second (see GAS.md), i.e. 10^6 gas per ยต second.
73        const GAS_PER_US: u64 = 1_000_000;
74        Self {
75            // ~96 us in crypto benchmarks
76            secp256k1_verify_cost: 96 * GAS_PER_US,
77            // ~194 us in crypto benchmarks
78            secp256k1_recover_pubkey_cost: 194 * GAS_PER_US,
79            // ~279 us in crypto benchmarks
80            secp256r1_verify_cost: 279 * GAS_PER_US,
81            // ~592 us in crypto benchmarks
82            secp256r1_recover_pubkey_cost: 592 * GAS_PER_US,
83            // ~35 us in crypto benchmarks
84            ed25519_verify_cost: 35 * GAS_PER_US,
85            // Calculated based on the benchmark results for `ed25519_batch_verify_{x}`.
86            ed25519_batch_verify_cost: LinearGasCost {
87                base: 24 * GAS_PER_US,
88                per_item: 21 * GAS_PER_US,
89            },
90            // Calculated based on the benchmark results for `ed25519_batch_verify_one_pubkey_{x}`.
91            ed25519_batch_verify_one_pubkey_cost: LinearGasCost {
92                base: 36 * GAS_PER_US,
93                per_item: 10 * GAS_PER_US,
94            },
95            // just assume the production machines have more than 4 cores, so we can half that
96            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/// Linear gas cost model where the cost is linear in the number of items.
143///
144/// To calculate it, you sample the cost for a few different amounts of items and fit a line to it.
145/// Let `b` be that line of best fit. Then `base = b(0)` is the y-intercept and
146/// `per_item = b(1) - b(0)` the slope.
147#[derive(Clone, PartialEq, Eq, Debug)]
148pub struct LinearGasCost {
149    /// This is a flat part of the cost, charged once per batch.
150    base: u64,
151    /// This is the cost per item in the batch.
152    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/** context data **/
167
168#[derive(Clone, PartialEq, Eq, Debug, Default)]
169pub struct GasState {
170    /// Gas limit for the computation, including internally and externally used gas.
171    /// This is set when the Environment is created and never mutated.
172    ///
173    /// Measured in [CosmWasm gas](https://github.com/CosmWasm/cosmwasm/blob/main/docs/GAS.md).
174    pub gas_limit: u64,
175    /// Tracking the gas used in the Cosmos SDK, in CosmWasm gas units.
176    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/// Additional environmental information in a debug call.
189///
190/// The currently unused lifetime parameter 'a allows accessing referenced data in the debug implementation
191/// without cloning it.
192#[derive(Debug)]
193#[non_exhaustive]
194pub struct DebugInfo<'a> {
195    pub gas_remaining: u64,
196    // This field is just to allow us to add the unused lifetime parameter. It can be removed
197    // at any time.
198    #[doc(hidden)]
199    #[debug(skip)]
200    pub(crate) __lifetime: PhantomData<&'a ()>,
201}
202
203// Unfortunately we cannot create an alias for the trait (https://github.com/rust-lang/rust/issues/41517).
204// So we need to copy it in a few places.
205//
206//                            /- BEGIN TRAIT                          END TRAIT \
207//                            |                                                 |
208//                            v                                                 v
209pub type DebugHandlerFn = dyn for<'a, 'b> FnMut(/* msg */ &'a str, DebugInfo<'b>);
210
211/// A environment that provides access to the ContextData.
212/// The environment is cloneable but clones access the same underlying data.
213pub 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            // This clone here requires us to wrap the function in Rc instead of Box
254            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    /// Calls a function with the given name and arguments.
303    /// The number of return values is variable and controlled by the guest.
304    /// Usually we expect 0 or 1 return values. Use [`Self::call_function0`]
305    /// or [`Self::call_function1`] to ensure the number of return values is checked.
306    fn call_function(
307        &self,
308        store: &mut impl AsStoreMut,
309        name: &str,
310        args: &[Value],
311    ) -> VmResult<Box<[Value]>> {
312        // Clone function before calling it to avoid dead locks
313        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() // with_wasmer_instance can only succeed if the callback succeeds
331        });
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    /// Creates a back reference from a contact to its partent instance
387    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    /// Returns true iff the storage is set to readonly mode
394    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    /// Increments the call depth by 1 and returns the new value
405    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    /// Decrements the call depth by 1 and returns the new value
418    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    /// Returns the remaining gas measured in [CosmWasm gas].
430    ///
431    /// [CosmWasm gas]: https://github.com/CosmWasm/cosmwasm/blob/main/docs/GAS.md
432    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    /// Sets the remaining gas measured in [CosmWasm gas].
443    ///
444    /// [CosmWasm gas]: https://github.com/CosmWasm/cosmwasm/blob/main/docs/GAS.md
445    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    /// Decreases gas left by the given amount.
454    /// If the amount exceeds the available gas, the remaining gas is set to 0 and
455    /// an VmError::GasDepletion error is returned.
456    #[allow(unused)] // used in tests
457    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    /// Creates a MemoryView.
474    /// This must be short living and not be used after the memory was grown.
475    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    /// Moves owned instances of storage and querier into the env.
483    /// Should be followed by exactly one call to move_out when the instance is finished.
484    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    /// Returns the original storage and querier as owned instances, and closes any remaining
492    /// iterators. This is meant to be called when recycling the instance.
493    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    /// A non-owning link to the wasmer instance
508    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        // These lines reduce the amount of gas available to wasmer
535        // so it can not consume gas that was consumed externally.
536        gas_left
537            .saturating_sub(info.externally_used)
538            .saturating_sub(info.cost)
539    });
540
541    // This tells wasmer how much more gas it can consume from this point in time.
542    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    // prepared data
566    const INIT_KEY: &[u8] = b"foo";
567    const INIT_VALUE: &[u8] = b"bar";
568    // this account has some coins
569    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; // ~0.5ms
574    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        // we need stubs for all required imports
591        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        // create some mock data
630        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        // empty data on start
645        let (inits, initq) = env.move_out();
646        assert!(inits.is_none());
647        assert!(initq.is_none());
648
649        // store it on the instance
650        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        // now is empty again
660        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        // Consume all the Gas that we allocated
671        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        // Using one more unit of gas triggers a failure
681        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        // Consume all the Gas that we allocated
693        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        // Using one more unit of gas triggers a failure
703        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        // More cost fail but do not change stats
736        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        // More externally used fails and changes stats
746        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        // with_externally_used
759        {
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        // with_cost
773        {
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        // Some gas was consumed externally
793        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        // Consume 20 gas directly in wasmer
799        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        // Using one more unit of gas triggers a failure
808        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        // change
828        env.set_storage_readonly(false);
829        assert!(!env.is_storage_readonly());
830
831        // still false
832        env.set_storage_readonly(false);
833        assert!(!env.is_storage_readonly());
834
835        // change back
836        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        // Clear context's wasmer_instance
858        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}