cfx_executor/stack/
frame_start.rs

1use super::{
2    super::context::OriginInfo, executable::make_executable, run_executable,
3    FrameLocal, FrameStackAction, RuntimeRes,
4};
5use crate::{machine::Machine, state::State, substate::Substate};
6
7use cfx_statedb::Result as DbResult;
8use cfx_types::{Address, AddressSpaceUtil, AddressWithSpace, Space};
9use cfx_vm_types::{
10    ActionParams, ActionValue, CallType, CreateType, Env, Spec,
11};
12use primitives::{storage::STORAGE_LAYOUT_REGULAR_V0, StorageLayout};
13
14/// A frame has not yet been executed, with all the necessary information to
15/// initiate and carry out the execution of the frame.
16pub struct FreshFrame<'a> {
17    /// The input parameters for the frame.
18    params: ActionParams,
19
20    /// The local data associated with this frame.
21    frame_local: FrameLocal<'a>,
22}
23
24impl<'a> FreshFrame<'a> {
25    pub fn new(
26        params: ActionParams, env: &'a Env, machine: &'a Machine,
27        spec: &'a Spec, depth: usize, parent_static_flag: bool,
28    ) -> Self {
29        let is_create = params.create_type != CreateType::None;
30        let create_address = is_create.then_some(params.code_address);
31        trace!(
32            "Executive::{:?}(params={:?}) self.env={:?}, parent_static={}",
33            if is_create { "create" } else { "call" },
34            params,
35            env,
36            parent_static_flag,
37        );
38
39        let static_flag =
40            parent_static_flag || params.call_type == CallType::StaticCall;
41
42        let substate = Substate::new();
43        let origin = OriginInfo::from(&params);
44
45        let frame_local = FrameLocal::new(
46            params.space,
47            env,
48            machine,
49            spec,
50            depth,
51            origin,
52            substate,
53            create_address,
54            static_flag,
55        );
56        FreshFrame {
57            frame_local,
58            params,
59        }
60    }
61
62    /// Initializes and executes a frame, along with runtime resources shared
63    /// across all frames.
64    pub(super) fn init_and_exec(
65        self, resources: &mut RuntimeRes<'a>,
66    ) -> DbResult<FrameStackAction<'a>> {
67        let FreshFrame {
68            frame_local,
69            params,
70        } = self;
71        let is_create = frame_local.create_address.is_some();
72
73        if is_create {
74            debug!(
75                "CallCreateExecutiveKind::ExecCreate: contract_addr = {:?}",
76                params.address
77            );
78            resources.tracer.record_create(&params);
79        } else {
80            resources.tracer.record_call(&params);
81        }
82
83        // Make checkpoint for this executive, callstack is always maintained
84        // with checkpoint.
85        resources.state.checkpoint();
86
87        let contract_address = frame_local.origin.recipient().clone();
88        resources
89            .callstack
90            .push(contract_address.with_space(frame_local.space), is_create);
91
92        // Pre execution: transfer value and init contract.
93        let spec = &frame_local.spec;
94        if is_create {
95            transfer_exec_balance_and_init_contract(
96                &params,
97                spec,
98                resources.state,
99                Some(STORAGE_LAYOUT_REGULAR_V0),
100            )?
101        } else {
102            transfer_balance(&params, resources.state)?
103        };
104
105        let executable =
106            make_executable(&frame_local, params, resources.tracer);
107        run_executable(executable, frame_local, resources)
108    }
109}
110
111fn transfer_balance(params: &ActionParams, state: &mut State) -> DbResult<()> {
112    let sender = AddressWithSpace {
113        address: params.sender,
114        space: params.space,
115    };
116    let receiver = AddressWithSpace {
117        address: params.address,
118        space: params.space,
119    };
120    if let ActionValue::Transfer(val) = params.value {
121        state.transfer_balance(&sender, &receiver, &val)?;
122    }
123
124    Ok(())
125}
126
127fn transfer_exec_balance_and_init_contract(
128    params: &ActionParams, spec: &Spec, state: &mut State,
129    storage_layout: Option<StorageLayout>,
130) -> DbResult<()> {
131    let sender = AddressWithSpace {
132        address: params.sender,
133        space: params.space,
134    };
135    let receiver = AddressWithSpace {
136        address: params.address,
137        space: params.space,
138    };
139    if let ActionValue::Transfer(val) = params.value {
140        // It is possible to first send money to a pre-calculated
141        // contract address.
142        let prev_balance = state.balance(&receiver)?;
143        state.sub_balance(&sender, &val)?;
144        let admin = if params.space == Space::Native {
145            params.original_sender
146        } else {
147            Address::zero()
148        };
149        state.new_contract_with_admin(
150            &receiver,
151            &admin,
152            val.saturating_add(prev_balance),
153            storage_layout,
154            spec.cip107,
155        )?;
156    } else {
157        // In contract creation, the `params.value` should never be
158        // `Apparent`.
159        unreachable!();
160    }
161
162    Ok(())
163}