cfx_executor/internal_contract/impls/
cross_space.rs

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