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, code_hash) = if context.spec.cip175 {
274        // CIP-175: resolve EIP-7702 delegation as an in-space call does.
275        context.state.code_with_hash_on_call(&address)?
276    } else {
277        (
278            context.state.code(&address)?,
279            context.state.code_hash(&address)?,
280        )
281    };
282
283    let next_params = ActionParams {
284        space: Space::Ethereum,
285        sender: mapped_sender.address,
286        address: address.address,
287        value: ActionValue::Transfer(value),
288        code_address: address.address,
289        original_sender: mapped_origin.address,
290        storage_owner: mapped_sender.address,
291        gas: call_gas,
292        gas_price: params.gas_price,
293        code,
294        code_hash,
295        data: Some(data.clone()),
296        call_type,
297        create_type: CreateType::None,
298        params_type: vm::ParamsType::Separate,
299    };
300
301    let mut wait_return_log = false;
302
303    if call_type == CallType::Call {
304        let nonce = context.state.nonce(&mapped_sender)?;
305        context.state.inc_nonce(&mapped_sender)?;
306        CallEvent::log(
307            &(mapped_sender.address.0, address.address.0),
308            &(value, nonce, data),
309            params,
310            context,
311        )?;
312        wait_return_log = true;
313    }
314
315    Ok((
316        next_params,
317        Box::new(Resume {
318            params: params.clone(),
319            gas_retained: reserved_gas,
320            wait_return_log,
321        }),
322    ))
323}
324
325pub fn create_to_evmcore(
326    init: Vec<u8>, salt: Option<H256>, params: &ActionParams, gas_left: U256,
327    context: &mut InternalRefContext,
328) -> vm::Result<(ActionParams, Box<dyn Resumable>)> {
329    if context.depth >= context.spec.max_depth {
330        internal_bail!("Exceed Depth");
331    }
332
333    let call_gas = gas_left / CROSS_SPACE_GAS_RATIO
334        + if params.value.value() > U256::zero() {
335            U256::from(context.spec.call_stipend)
336        } else {
337            U256::zero()
338        };
339    let reserved_gas = gas_left - gas_left / CROSS_SPACE_GAS_RATIO;
340
341    let mapped_sender = params.sender.evm_map();
342    let mapped_origin = params.original_sender.evm_map();
343
344    let value = params.value.value();
345    context.state.transfer_balance(
346        &params.address.with_native_space(),
347        &mapped_sender,
348        &value,
349    )?;
350    context.state.add_total_evm_tokens(value);
351    context.tracer.trace_internal_transfer(
352        AddressPocket::Balance(params.address.with_native_space()),
353        AddressPocket::Balance(mapped_sender),
354        params.value.value(),
355    );
356
357    let (address_scheme, create_type) = match salt {
358        None => (
359            CreateContractAddressType::FromSenderNonce,
360            CreateType::CREATE,
361        ),
362        Some(salt) => (
363            CreateContractAddressType::FromSenderSaltAndCodeHash(salt),
364            CreateType::CREATE2,
365        ),
366    };
367    let (address_with_space, code_hash) = cal_contract_address_with_space(
368        address_scheme,
369        &mapped_sender,
370        &context.state.nonce(&mapped_sender)?,
371        &init,
372    );
373    let address = address_with_space.address;
374
375    let next_params = ActionParams {
376        space: Space::Ethereum,
377        code_address: address,
378        address,
379        sender: mapped_sender.address,
380        original_sender: mapped_origin.address,
381        storage_owner: Address::zero(),
382        gas: call_gas,
383        gas_price: params.gas_price,
384        value: ActionValue::Transfer(value),
385        code: Some(Arc::new(init.clone())),
386        code_hash,
387        data: None,
388        call_type: CallType::None,
389        create_type,
390        params_type: ParamsType::Embedded,
391    };
392
393    let nonce = context.state.nonce(&mapped_sender)?;
394    context.state.inc_nonce(&mapped_sender)?;
395    CreateEvent::log(
396        &(mapped_sender.address.0, address.0),
397        &(value, nonce, init),
398        params,
399        context,
400    )?;
401
402    Ok((
403        next_params,
404        Box::new(Resume {
405            params: params.clone(),
406            gas_retained: reserved_gas,
407            wait_return_log: true,
408        }),
409    ))
410}
411
412pub fn withdraw_from_evmcore(
413    sender: Address, value: U256, params: &ActionParams,
414    context: &mut InternalRefContext,
415) -> vm::Result<()> {
416    let mapped_address = sender.evm_map();
417    let balance = context.state.balance(&mapped_address)?;
418    if balance < value {
419        internal_bail!(
420            "Not enough balance for withdrawing from mapped address"
421        );
422    }
423    context.state.transfer_balance(
424        &mapped_address,
425        &sender.with_native_space(),
426        &value,
427    )?;
428    context.state.sub_total_evm_tokens(value);
429    context.tracer.trace_internal_transfer(
430        AddressPocket::Balance(mapped_address),
431        AddressPocket::Balance(sender.with_native_space()),
432        value,
433    );
434
435    let nonce = context.state.nonce(&mapped_address)?;
436    context.state.inc_nonce(&mapped_address)?;
437    WithdrawEvent::log(
438        &(mapped_address.address.0, sender),
439        &(value, nonce),
440        params,
441        context,
442    )?;
443
444    Ok(())
445}
446
447pub fn mapped_balance(
448    address: Address, context: &mut InternalRefContext,
449) -> vm::Result<U256> {
450    Ok(context.state.balance(&address.evm_map())?)
451}
452
453pub fn mapped_nonce(
454    address: Address, context: &mut InternalRefContext,
455) -> vm::Result<U256> {
456    Ok(context.state.nonce(&address.evm_map())?)
457}