pchain_runtime/execution/
contract.rs1use std::sync::{Arc, Mutex};
9
10use pchain_types::cryptography::PublicAddress;
11use pchain_world_state::storage::WorldStateStorage;
12use wasmer::Store;
13
14use crate::{
15 contract::{
16 self, ContractBinaryFunctions, ContractValidateError, MethodCallError, ModuleBuildError,
17 SmartContractContext,
18 },
19 cost::CostChange,
20 read_write_set::ReadWriteSet,
21 transition::TransitionContext,
22 types::CallTx,
23 wasmer::{wasmer_env, wasmer_store},
24 BlockchainParams, Cache,
25};
26
27pub(crate) struct ContractModule {
29 store: Store,
30 module: contract::Module,
31 pub gas_cost: CostChange,
33}
34
35impl ContractModule {
36 pub(crate) fn new(
37 contract_code: &Vec<u8>,
38 memory_limit: Option<usize>,
39 ) -> Result<Self, ModuleBuildError> {
40 let wasmer_store = wasmer_store::instantiate_store(u64::MAX, memory_limit);
41 let module = contract::Module::from_wasm_bytecode(
43 contract::CBI_VERSION,
44 contract_code,
45 &wasmer_store,
46 )?;
47
48 Ok(Self {
49 store: wasmer_store,
50 module,
51 gas_cost: CostChange::default(),
52 })
53 }
54
55 pub(crate) fn build_contract<S>(
56 contract_address: PublicAddress,
57 sc_ctx: &SmartContractContext,
58 rw_set: &ReadWriteSet<S>,
59 ) -> Result<Self, ()>
60 where
61 S: WorldStateStorage + Send + Sync + Clone + 'static,
62 {
63 let (module, store, gas_cost) = {
64 let (result, gas_cost) = rw_set.code_from_sc_cache(contract_address, sc_ctx);
65 match result {
66 Some((module, store)) => (module, store, gas_cost),
67 None => return Err(()),
68 }
69 };
70
71 Ok(Self {
72 store,
73 module,
74 gas_cost,
75 })
76 }
77
78 pub(crate) fn validate(&self) -> Result<(), ContractValidateError> {
79 self.module.validate_contract(&self.store)
80 }
81
82 pub(crate) fn cache(&self, contract_address: PublicAddress, cache: &mut Cache) {
83 self.module.cache_to(contract_address, cache)
84 }
85
86 pub(crate) fn instantiate<S>(
87 self,
88 ctx: Arc<Mutex<TransitionContext<S>>>,
89 call_counter: u32,
90 is_view: bool,
91 tx: CallTx,
92 bd: BlockchainParams,
93 ) -> Result<ContractInstance<S>, ()>
94 where
95 S: WorldStateStorage + Send + Sync + Clone + 'static,
96 {
97 let gas_limit = tx.gas_limit;
98 let environment = wasmer_env::Env::new(ctx, call_counter, is_view, tx, bd);
99
100 let importable = if is_view {
101 contract::create_importable_view::<wasmer_env::Env<S>, ContractBinaryFunctions>(
102 &self.store,
103 &environment,
104 )
105 } else {
106 contract::create_importable::<wasmer_env::Env<S>, ContractBinaryFunctions>(
107 &self.store,
108 &environment,
109 )
110 };
111
112 let instance = self
113 .module
114 .instantiate(&importable, gas_limit)
115 .map_err(|_| ())?;
116
117 Ok(ContractInstance {
118 environment,
119 instance,
120 })
121 }
122}
123
124pub(crate) struct ContractInstance<S>
126where
127 S: WorldStateStorage + Send + Sync + Clone + 'static,
128{
129 environment: wasmer_env::Env<S>,
130 instance: contract::Instance,
131}
132
133impl<S> ContractInstance<S>
134where
135 S: WorldStateStorage + Send + Sync + Clone + 'static,
136{
137 pub(crate) fn call(self) -> (TransitionContext<S>, u64, Option<MethodCallError>) {
138 self.environment
140 .init_wasmer_remaining_points(self.instance.remaining_points());
141
142 let call_result = unsafe { self.instance.call_method() };
144
145 let non_wasmer_gas_amount = self.environment.get_non_wasm_gas_amount();
146
147 self.environment.drop_wasmer_remaining_points();
149
150 let (remaining_gas, call_error) = match call_result {
151 Ok(remaining_gas) => (remaining_gas, None),
152 Err((remaining_gas, call_error)) => (remaining_gas, Some(call_error)),
153 };
154
155 let total_gas = self
156 .environment
157 .call_tx
158 .gas_limit
159 .saturating_sub(remaining_gas)
160 .saturating_sub(non_wasmer_gas_amount); let ctx = self.environment.context.lock().unwrap().clone();
164 (ctx, total_gas, call_error)
165 }
166}