Skip to main content

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/// An 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 deadlocks
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 contract to its parent 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    /// a 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 = gas_state
534            .externally_used_gas
535            .saturating_add(info.externally_used);
536        // Reduce the amount of gas available to Wasm executor,
537        // so it cannot consume gas that was already consumed externally.
538        gas_left
539            .saturating_sub(info.externally_used)
540            .saturating_sub(info.cost)
541    });
542
543    // This tells wasmer how much more gas it can consume from this point in time.
544    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    // prepared data
571    const INIT_KEY: &[u8] = b"foo";
572    const INIT_VALUE: &[u8] = b"bar";
573    // this account has some coins
574    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; // ~0.5ms
579    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        // we need stubs for all required imports
595        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        // create some mock data
634        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        // empty data on start
649        let (inits, initq) = env.move_out();
650        assert!(inits.is_none());
651        assert!(initq.is_none());
652
653        // store it on the instance
654        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        // now is empty again
664        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        // Consume all the Gas that we allocated
675        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        // Using one more unit of gas triggers a failure
685        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        // Consume all the Gas that we allocated
697        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        // Using one more unit of gas triggers a failure
707        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        // More cost fail but do not change stats
740        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        // More externally used fails and changes stats
750        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        // with_externally_used
763        {
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        // with_cost
777        {
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        // Some gas was consumed externally
797        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        // Consume 20 gas directly in wasmer
803        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        // Using one more unit of gas triggers a failure
812        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        // change
832        env.set_storage_readonly(false);
833        assert!(!env.is_storage_readonly());
834
835        // still false
836        env.set_storage_readonly(false);
837        assert!(!env.is_storage_readonly());
838
839        // change back
840        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        // Clear context's wasmer_instance
862        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}