cfx_executor/internal_contract/components/
function.rs1use cfx_statedb::Result as DbResult;
6use cfx_types::U256;
7use cfx_vm_types::{self as vm, ActionParams, CallType, GasLeft};
8use solidity_abi::{ABIDecodable, ABIEncodable};
9
10use super::{InternalRefContext, InternalTrapResult, IsActive};
11use InternalTrapResult::*;
12
13pub trait SolidityFunctionTrait: Send + Sync + IsActive {
15 fn execute(
16 &self, input: &[u8], params: &ActionParams,
17 context: &mut InternalRefContext,
18 ) -> InternalTrapResult<GasLeft>;
19
20 fn name(&self) -> &'static str;
22
23 fn function_sig(&self) -> [u8; 4];
25}
26
27pub trait SolidityFunctionConfigTrait:
28 InterfaceTrait + PreExecCheckTrait + UpfrontPaymentTrait
29{
30}
31
32impl<T> SolidityFunctionConfigTrait for T where T: InterfaceTrait + PreExecCheckTrait + UpfrontPaymentTrait
33{}
34
35impl<T: SolidityFunctionConfigTrait + ExecutionTrait + IsActive>
49 SolidityFunctionTrait for T
50{
51 fn execute(
52 &self, input: &[u8], params: &ActionParams,
53 context: &mut InternalRefContext,
54 ) -> InternalTrapResult<GasLeft> {
55 let (solidity_params, cost) =
56 match preprocessing(self, input, params, context) {
57 Ok(res) => res,
58 Err(err) => {
59 return Return(Err(err));
60 }
61 };
62
63 let gas_left = params.gas - cost;
64
65 ExecutionTrait::execute_inner(
66 self,
67 solidity_params,
68 params,
69 gas_left,
70 context,
71 )
72 .map_return(|output| {
73 GasLeft::NeedsReturn {
74 gas_left,
75 data: output.abi_encode().into(),
76 apply_state: true,
77 }
78 .charge_return_data_gas(context.spec)
79 })
80 }
81
82 fn name(&self) -> &'static str { return Self::NAME_AND_PARAMS; }
83
84 fn function_sig(&self) -> [u8; 4] { return Self::FUNC_SIG; }
85}
86
87fn preprocessing<T: SolidityFunctionConfigTrait>(
88 sol_fn: &T, input: &[u8], params: &ActionParams,
89 context: &InternalRefContext,
90) -> vm::Result<(T::Input, U256)> {
91 sol_fn.pre_execution_check(params, context)?;
92 let solidity_params = <T::Input as ABIDecodable>::abi_decode(&input)?;
93 let cost = sol_fn.upfront_gas_payment(&solidity_params, params, context)?;
94 if cost > params.gas {
95 return Err(vm::Error::OutOfGas);
96 }
97 Ok((solidity_params, cost))
98}
99
100pub trait InterfaceTrait {
101 type Input: ABIDecodable;
102 type Output: ABIEncodable;
103 const NAME_AND_PARAMS: &'static str;
104 const FUNC_SIG: [u8; 4];
105}
106
107pub trait PreExecCheckTrait: Send + Sync {
108 fn pre_execution_check(
109 &self, params: &ActionParams, context: &InternalRefContext,
110 ) -> vm::Result<()>;
111}
112
113pub trait ExecutionTrait: Send + Sync + InterfaceTrait {
114 fn execute_inner(
115 &self, input: Self::Input, params: &ActionParams, gas_left: U256,
116 context: &mut InternalRefContext,
117 ) -> InternalTrapResult<<Self as InterfaceTrait>::Output>;
118}
119
120pub trait SimpleExecutionTrait: Send + Sync + InterfaceTrait {
122 fn execute_inner(
123 &self, input: Self::Input, params: &ActionParams,
124 context: &mut InternalRefContext,
125 ) -> vm::Result<<Self as InterfaceTrait>::Output>;
126}
127
128impl<T> ExecutionTrait for T
129where T: SimpleExecutionTrait
130{
131 fn execute_inner(
132 &self, input: Self::Input, params: &ActionParams, _gas_left: U256,
133 context: &mut InternalRefContext,
134 ) -> InternalTrapResult<<Self as InterfaceTrait>::Output> {
135 Return(SimpleExecutionTrait::execute_inner(
136 self, input, params, context,
137 ))
138 }
139}
140
141pub trait UpfrontPaymentTrait: Send + Sync + InterfaceTrait {
142 fn upfront_gas_payment(
143 &self, input: &Self::Input, params: &ActionParams,
144 context: &InternalRefContext,
145 ) -> DbResult<U256>;
146}
147
148pub trait PreExecCheckConfTrait: Send + Sync {
149 const PAYABLE: bool;
151 const HAS_WRITE_OP: bool;
153}
154
155impl<T: PreExecCheckConfTrait> PreExecCheckTrait for T {
156 fn pre_execution_check(
157 &self, params: &ActionParams, context: &InternalRefContext,
158 ) -> vm::Result<()> {
159 if !Self::PAYABLE && !params.value.value().is_zero() {
160 return Err(vm::Error::InternalContract(
161 "should not transfer balance to non-payable function".into(),
162 ));
163 }
164
165 let spec = context.spec;
166 let mut static_context = context.callstack.in_reentrancy(spec)
168 || params.call_type == CallType::StaticCall;
169 static_context |= spec.cip132 && context.static_flag;
171
172 if Self::HAS_WRITE_OP && static_context {
173 return Err(vm::Error::MutableCallInStaticContext);
174 }
175
176 Ok(())
177 }
178}
179
180#[macro_export]
181macro_rules! make_solidity_function {
200 ( $(#[$attr:meta])* $visibility:vis struct $name:ident ($input:ty, $interface:expr ); ) => {
201 $crate::make_solidity_function! {
202 $(#[$attr])* $visibility struct $name ($input, $interface, () );
203 }
204 };
205 ( $(#[$attr:meta])* $visibility:vis struct $name:ident ($input:ty, $interface:expr, $output:ty ); ) => {
206 $(#[$attr])*
207 #[derive(Copy, Clone)]
208 $visibility struct $name {
209 }
210
211 impl $name {
212 pub fn instance() -> Self {
213 Self {}
214 }
215 }
216
217 impl InterfaceTrait for $name {
218 type Input = $input;
219 type Output = $output;
220 const NAME_AND_PARAMS: &'static str = $interface;
221 const FUNC_SIG: [u8; 4] = {
222 let x = keccak!($interface);
223 [x[0],x[1],x[2],x[3]]
224 };
225 }
226 };
227}
228
229#[macro_export]
230macro_rules! impl_function_type {
231 ( $name:ident, "non_payable_write" $(, gas: $gas:expr)? ) => {
232 $crate::impl_function_type!(@inner, $name, false, true $(, $gas)?);
233 };
234 ( $name:ident, "payable_write" $(, gas: $gas:expr)? ) => {
235 $crate::impl_function_type!(@inner, $name, true, true $(, $gas)?);
236 };
237 ( $name:ident, "query" $(, gas: $gas:expr)? ) => {
238 $crate::impl_function_type!(@inner, $name, false, false $(, $gas)?);
239 };
240 ( @inner, $name:ident, $payable:expr, $has_write_op:expr $(, $gas:expr)? ) => {
241 impl PreExecCheckConfTrait for $name {
242 const PAYABLE: bool = $payable;
243 const HAS_WRITE_OP: bool = $has_write_op;
244 }
245 $(
246 impl UpfrontPaymentTrait for $name {
247 fn upfront_gas_payment(
248 &self, _input: &Self::Input, _params: &ActionParams, context: &InternalRefContext,
249 ) -> DbResult<U256> {
250 Ok(U256::from($gas(context.spec)))
251 }
252 }
253 )?
254 };
255 ( $name:ident, "query_with_default_gas" ) => {
256 impl PreExecCheckConfTrait for $name {
257 const PAYABLE: bool = false ;
258 const HAS_WRITE_OP: bool = false;
259 }
260
261 impl UpfrontPaymentTrait for $name {
262 fn upfront_gas_payment(
263 &self, _input: &Self::Input, _params: &ActionParams, context: &InternalRefContext,
264 ) -> DbResult<U256> {
265 Ok(U256::from(context.spec.balance_gas))
266 }
267 }
268 };
269}