pchain_runtime/contract/
instance.rs

1/*
2    Copyright © 2023, ParallelChain Lab
3    Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0
4*/
5
6//! Defines a struct to use [wasmer::Instance] to perform method call according to ParallelChain Smart Contract Defintions.
7
8use anyhow::Result;
9
10/// Instance represents a WebAssembly module that has been 'instantiated' into a stateful quasi-process and can have its methods
11/// called.
12pub(crate) struct Instance(pub(crate) wasmer::Instance);
13
14impl Instance {
15    /// call_method executes the named method of the Instance
16    ///
17    /// If the call completes successfully, it returns the remaining gas after the execution. If the call terminated early,
18    /// it returns a two-tuple comprising the remaining gas after the execution, and a MethodCallError describing the
19    /// cause of the early termination.
20    ///
21    /// # Panics
22    /// call_method assumes that the Instance does export the name method, and panics otherwise.
23    pub(crate) unsafe fn call_method(&self) -> Result<u64, (u64, MethodCallError)> {
24        // remaining_gas before method call
25        let remaining_gas = match wasmer_middlewares::metering::get_remaining_points(&self.0) {
26            wasmer_middlewares::metering::MeteringPoints::Exhausted => 0,
27            wasmer_middlewares::metering::MeteringPoints::Remaining(gas_left_after_execution) => {
28                gas_left_after_execution
29            }
30        };
31
32        let method = match self
33            .0
34            .exports
35            .get_native_function::<(), ()>(CONTRACT_METHOD)
36        {
37            Ok(m) => m,
38            Err(e) => return Err((remaining_gas, MethodCallError::NoExportedMethod(e))), // Invariant violated: A contract that does not export method_name was deployed.
39        };
40
41        // method call
42        let execution_result = method.call();
43
44        // remaining_gas after method call
45        let remaining_gas = match wasmer_middlewares::metering::get_remaining_points(&self.0) {
46            wasmer_middlewares::metering::MeteringPoints::Exhausted => 0,
47            wasmer_middlewares::metering::MeteringPoints::Remaining(gas_left_after_execution) => {
48                gas_left_after_execution
49            }
50        };
51
52        match execution_result{
53            Ok(_) => Ok(remaining_gas),
54            Err(_) if remaining_gas == 0 => Err((remaining_gas, MethodCallError::GasExhaustion)),
55            Err(e) /* remaining_gas > 0 */ => Err((remaining_gas, MethodCallError::Runtime(e)))
56        }
57    }
58
59    /// return a global variable which can read and modify the metering remaining points of wasm execution of this Instance
60    pub(crate) fn remaining_points(&self) -> wasmer::Global {
61        self.0
62            .exports
63            .get_global("wasmer_metering_remaining_points")
64            .unwrap()
65            .clone()
66    }
67}
68
69/// MethodCallError enumerates through the possible reasons why a call into a contract Instance's exported methods might
70/// terminate early.
71#[derive(Debug)]
72pub enum MethodCallError {
73    Runtime(wasmer::RuntimeError),
74    GasExhaustion,
75    NoExportedMethod(wasmer::ExportError),
76}
77
78/// ContractValidateError enumerates through the possible reasons why the contract is not runnable
79#[derive(Debug)]
80pub enum ContractValidateError {
81    MethodNotFound,
82    InstantiateError,
83}
84
85/// CONTRACT_METHOD is reserved by the ParallelChain Mainnet protocol to name callable function
86/// exports from smart contract Modules.  
87pub const CONTRACT_METHOD: &str = "entrypoint";