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";