cfx_executor/internal_contract/impls/
cross_space.rs

1use crate::{
2    executive::gas_required_for,
3    executive_observer::AddressPocket,
4    internal_bail,
5    stack::{
6        Context, Executable, ExecutableOutcome, FrameResult, FrameReturn,
7        Resumable,
8    },
9};
10
11use cfx_parameters::block::CROSS_SPACE_GAS_RATIO;
12use cfx_statedb::Result as DbResult;
13use cfx_types::{
14    address_util::AddressUtil, cal_contract_address_with_space, Address,
15    AddressSpaceUtil, CreateContractAddressType, Space, H256, U256,
16};
17use cfx_vm_interpreter::Finalize;
18use cfx_vm_types::{
19    self as vm, ActionParams, ActionValue, CallType, Context as _, CreateType,
20    GasLeft, ParamsType, Spec,
21};
22use solidity_abi::ABIEncodable;
23use std::{marker::PhantomData, sync::Arc};
24
25use super::super::{
26    components::{InternalRefContext, InternalTrapResult, SolidityEventTrait},
27    contracts::cross_space::{
28        CallEvent, CreateEvent, ReturnEvent, WithdrawEvent,
29    },
30};
31
32pub fn create_gas(context: &InternalRefContext, code: &[u8]) -> DbResult<U256> {
33    let code_length = code.len();
34
35    let transaction_gas = gas_required_for(
36        /* is_create */ true,
37        code,
38        None,
39        0,
40        &context.spec.to_consensus_spec(),
41    ) + context.spec.tx_gas as u64;
42
43    let create_gas = U256::from(context.spec.create_gas);
44
45    let address_mapping_gas = context.spec.sha3_gas * 2;
46
47    let create_log_gas = {
48        let log_data_length =
49            H256::len_bytes() * 4 + (code_length + 31) / 32 * 32;
50        context.spec.log_gas
51            + 3 * context.spec.log_topic_gas
52            + context.spec.log_data_gas * log_data_length
53    };
54
55    let return_log_gas = {
56        let log_data_length = H256::len_bytes();
57        context.spec.log_gas
58            + context.spec.log_topic_gas
59            + context.spec.log_data_gas * log_data_length
60    };
61
62    Ok(create_gas
63        + transaction_gas
64        + address_mapping_gas
65        + create_log_gas
66        + return_log_gas)
67}
68
69pub fn call_gas(
70    receiver: Address, params: &ActionParams, context: &InternalRefContext,
71    data: &[u8],
72) -> DbResult<U256> {
73    let data_length = data.len();
74
75    let transaction_gas = gas_required_for(
76        /* is_create */ false,
77        data,
78        None,
79        0,
80        &context.spec.to_consensus_spec(),
81    ) + context.spec.tx_gas as u64;
82
83    let new_account = !context
84        .state
85        .exists_and_not_null(&receiver.with_evm_space())?;
86    let new_account_gas = if new_account {
87        context.spec.call_new_account_gas * context.spec.evm_gas_ratio
88    } else {
89        0
90    };
91
92    let transfer_gas = if params.value.value() > U256::zero() {
93        context.spec.call_value_transfer_gas
94    } else {
95        0
96    };
97
98    let call_gas =
99        U256::from(context.spec.call_gas) + new_account_gas + transfer_gas;
100
101    let address_mapping_gas = context.spec.sha3_gas * 2;
102
103    let call_log_gas = {
104        let log_data_length =
105            H256::len_bytes() * 4 + (data_length + 31) / 32 * 32;
106        context.spec.log_gas
107            + 3 * context.spec.log_topic_gas
108            + context.spec.log_data_gas * log_data_length
109    };
110
111    let return_log_gas = {
112        let log_data_length = H256::len_bytes();
113        context.spec.log_gas
114            + context.spec.log_topic_gas
115            + context.spec.log_data_gas * log_data_length
116    };
117
118    Ok(call_gas
119        + transaction_gas
120        + address_mapping_gas
121        + call_log_gas
122        + return_log_gas)
123}
124
125pub fn static_call_gas(spec: &Spec) -> U256 {
126    let call_gas = U256::from(spec.call_gas);
127    let address_mapping_gas = spec.sha3_gas * 2;
128
129    call_gas + address_mapping_gas
130}
131
132pub fn withdraw_gas(spec: &Spec) -> U256 {
133    let call_gas = U256::from(spec.call_value_transfer_gas);
134    let transaction_gas = spec.tx_gas;
135    let address_mapping_gas = spec.sha3_gas;
136    let log_gas = spec.log_gas
137        + spec.log_topic_gas * 3
138        + spec.log_data_gas * H256::len_bytes() * 2;
139
140    call_gas + transaction_gas + address_mapping_gas + log_gas
141}
142
143#[derive(Clone)]
144pub struct Resume {
145    pub params: ActionParams,
146    pub gas_retained: U256,
147    pub wait_return_log: bool,
148}
149
150impl Resumable for Resume {
151    fn resume(self: Box<Self>, result: FrameResult) -> Box<dyn Executable> {
152        let frame_return = match result {
153            Ok(r) => r,
154            Err(e) => {
155                return Box::new(PassResult {
156                    params: self.params,
157                    result: Err(e),
158                    apply_state: false,
159                    wait_return_log: self.wait_return_log,
160                });
161            }
162        };
163
164        let gas_left = self.gas_retained + frame_return.gas_left;
165        let apply_state = frame_return.apply_state;
166
167        let data = match frame_return {
168            FrameReturn {
169                apply_state: true,
170                create_address: Some(create_address),
171                ..
172            } => create_address.0.abi_encode().into(),
173            FrameReturn {
174                apply_state: true,
175                return_data,
176                create_address: None,
177                ..
178            } => return_data.to_vec().abi_encode().into(),
179            FrameReturn {
180                apply_state: false,
181                return_data,
182                ..
183            } => return_data,
184        };
185
186        Box::new(PassResult {
187            params: self.params,
188            apply_state,
189            result: Ok(GasLeft::NeedsReturn {
190                gas_left,
191                data,
192                apply_state,
193            }),
194            wait_return_log: self.wait_return_log,
195        })
196    }
197}
198
199pub struct PassResult {
200    params: ActionParams,
201    result: vm::Result<GasLeft>,
202    apply_state: bool,
203    wait_return_log: bool,
204}
205
206impl Executable for PassResult {
207    fn execute(
208        self: Box<Self>, mut context: Context,
209    ) -> DbResult<ExecutableOutcome> {
210        if self.wait_return_log {
211            ReturnEvent::log(
212                &(),
213                &self.apply_state,
214                &self.params,
215                &mut context.internal_ref(),
216            )
217            .expect("Must have no static flag");
218        }
219
220        let result = self
221            .result
222            .and_then(|r| r.charge_return_data_gas(context.spec()))
223            .finalize(context);
224        Ok(ExecutableOutcome::Return(result))
225    }
226}
227
228pub fn process_trap<T>(
229    result: vm::Result<(ActionParams, Box<dyn Resumable>)>,
230    _phantom: PhantomData<T>,
231) -> InternalTrapResult<T> {
232    match result {
233        Ok((p, r)) => InternalTrapResult::Invoke(p, r),
234        Err(err) => InternalTrapResult::Return(Err(err)),
235    }
236}
237
238pub fn call_to_evmcore(
239    receiver: Address, data: Vec<u8>, call_type: CallType,
240    params: &ActionParams, gas_left: U256, context: &mut InternalRefContext,
241) -> vm::Result<(ActionParams, Box<dyn Resumable>)> {
242    if context.depth >= context.spec.max_depth {
243        internal_bail!("Exceed Depth");
244    }
245
246    let value = params.value.value();
247
248    let call_gas = gas_left / CROSS_SPACE_GAS_RATIO
249        + if value > U256::zero() {
250            U256::from(context.spec.call_stipend)
251        } else {
252            U256::zero()
253        };
254    let reserved_gas = gas_left - gas_left / CROSS_SPACE_GAS_RATIO;
255
256    let mapped_sender = params.sender.evm_map();
257    let mapped_origin = params.original_sender.evm_map();
258
259    context.state.transfer_balance(
260        &params.address.with_native_space(),
261        &mapped_sender,
262        &value,
263    )?;
264    context.state.add_total_evm_tokens(value);
265    context.tracer.trace_internal_transfer(
266        AddressPocket::Balance(params.address.with_native_space()),
267        AddressPocket::Balance(mapped_sender),
268        params.value.value(),
269    );
270
271    let address = receiver.with_evm_space();
272
273    let code = context.state.code(&address)?;
274    let code_hash = context.state.code_hash(&address)?;
275
276    let next_params = ActionParams {
277        space: Space::Ethereum,
278        sender: mapped_sender.address,
279        address: address.address,
280        value: ActionValue::Transfer(value),
281        code_address: address.address,
282        original_sender: mapped_origin.address,
283        storage_owner: mapped_sender.address,
284        gas: call_gas,
285        gas_price: params.gas_price,
286        code,
287        code_hash,
288        data: Some(data.clone()),
289        call_type,
290        create_type: CreateType::None,
291        params_type: vm::ParamsType::Separate,
292    };
293
294    let mut wait_return_log = false;
295
296    if call_type == CallType::Call {
297        let nonce = context.state.nonce(&mapped_sender)?;
298        context.state.inc_nonce(&mapped_sender)?;
299        CallEvent::log(
300            &(mapped_sender.address.0, address.address.0),
301            &(value, nonce, data),
302            params,
303            context,
304        )?;
305        wait_return_log = true;
306    }
307
308    Ok((
309        next_params,
310        Box::new(Resume {
311            params: params.clone(),
312            gas_retained: reserved_gas,
313            wait_return_log,
314        }),
315    ))
316}
317
318pub fn create_to_evmcore(
319    init: Vec<u8>, salt: Option<H256>, params: &ActionParams, gas_left: U256,
320    context: &mut InternalRefContext,
321) -> vm::Result<(ActionParams, Box<dyn Resumable>)> {
322    if context.depth >= context.spec.max_depth {
323        internal_bail!("Exceed Depth");
324    }
325
326    let call_gas = gas_left / CROSS_SPACE_GAS_RATIO
327        + if params.value.value() > U256::zero() {
328            U256::from(context.spec.call_stipend)
329        } else {
330            U256::zero()
331        };
332    let reserved_gas = gas_left - gas_left / CROSS_SPACE_GAS_RATIO;
333
334    let mapped_sender = params.sender.evm_map();
335    let mapped_origin = params.original_sender.evm_map();
336
337    let value = params.value.value();
338    context.state.transfer_balance(
339        &params.address.with_native_space(),
340        &mapped_sender,
341        &value,
342    )?;
343    context.state.add_total_evm_tokens(value);
344    context.tracer.trace_internal_transfer(
345        AddressPocket::Balance(params.address.with_native_space()),
346        AddressPocket::Balance(mapped_sender),
347        params.value.value(),
348    );
349
350    let (address_scheme, create_type) = match salt {
351        None => (
352            CreateContractAddressType::FromSenderNonce,
353            CreateType::CREATE,
354        ),
355        Some(salt) => (
356            CreateContractAddressType::FromSenderSaltAndCodeHash(salt),
357            CreateType::CREATE2,
358        ),
359    };
360    let (address_with_space, code_hash) = cal_contract_address_with_space(
361        address_scheme,
362        &mapped_sender,
363        &context.state.nonce(&mapped_sender)?,
364        &init,
365    );
366    let address = address_with_space.address;
367
368    let next_params = ActionParams {
369        space: Space::Ethereum,
370        code_address: address,
371        address,
372        sender: mapped_sender.address,
373        original_sender: mapped_origin.address,
374        storage_owner: Address::zero(),
375        gas: call_gas,
376        gas_price: params.gas_price,
377        value: ActionValue::Transfer(value),
378        code: Some(Arc::new(init.clone())),
379        code_hash,
380        data: None,
381        call_type: CallType::None,
382        create_type,
383        params_type: ParamsType::Embedded,
384    };
385
386    let nonce = context.state.nonce(&mapped_sender)?;
387    context.state.inc_nonce(&mapped_sender)?;
388    CreateEvent::log(
389        &(mapped_sender.address.0, address.0),
390        &(value, nonce, init),
391        params,
392        context,
393    )?;
394
395    Ok((
396        next_params,
397        Box::new(Resume {
398            params: params.clone(),
399            gas_retained: reserved_gas,
400            wait_return_log: true,
401        }),
402    ))
403}
404
405pub fn withdraw_from_evmcore(
406    sender: Address, value: U256, params: &ActionParams,
407    context: &mut InternalRefContext,
408) -> vm::Result<()> {
409    let mapped_address = sender.evm_map();
410    let balance = context.state.balance(&mapped_address)?;
411    if balance < value {
412        internal_bail!(
413            "Not enough balance for withdrawing from mapped address"
414        );
415    }
416    context.state.transfer_balance(
417        &mapped_address,
418        &sender.with_native_space(),
419        &value,
420    )?;
421    context.state.sub_total_evm_tokens(value);
422    context.tracer.trace_internal_transfer(
423        AddressPocket::Balance(mapped_address),
424        AddressPocket::Balance(sender.with_native_space()),
425        value,
426    );
427
428    let nonce = context.state.nonce(&mapped_address)?;
429    context.state.inc_nonce(&mapped_address)?;
430    WithdrawEvent::log(
431        &(mapped_address.address.0, sender),
432        &(value, nonce),
433        params,
434        context,
435    )?;
436
437    Ok(())
438}
439
440pub fn mapped_balance(
441    address: Address, context: &mut InternalRefContext,
442) -> vm::Result<U256> {
443    Ok(context.state.balance(&address.evm_map())?)
444}
445
446pub fn mapped_nonce(
447    address: Address, context: &mut InternalRefContext,
448) -> vm::Result<U256> {
449    Ok(context.state.nonce(&address.evm_map())?)
450}