cfx_executor/executive/
mod.rs

1// Copyright 2019 Conflux Foundation. All rights reserved.
2// Conflux is free software and distributed under GNU General Public License.
3// See http://www.gnu.org/licenses/
4pub mod executed;
5pub mod execution_outcome;
6mod fresh_executive;
7mod pre_checked_executive;
8#[cfg(test)]
9mod tests;
10pub mod transact_options;
11
12use cfx_rpc_eth_types::BlockOverrides;
13use cfx_statedb::Result as DbResult;
14use cfx_types::{
15    address_util::AddressUtil, AddressSpaceUtil, AddressWithSpace, Space,
16    SpaceMap, H256, U256,
17};
18use cfx_vm_types::{ConsensusGasSpec, CreateContractAddress, Env, Spec};
19use primitives::{AccessList, SignedTransaction};
20
21use fresh_executive::FreshExecutive;
22use pre_checked_executive::PreCheckedExecutive;
23
24pub use executed::Executed;
25pub use execution_outcome::{
26    ExecutionError, ExecutionOutcome, ToRepackError, TxDropError,
27};
28pub use transact_options::{
29    ChargeCollateral, TransactOptions, TransactSettings,
30};
31
32use crate::{
33    executive_observer::ExecutiveObserver, machine::Machine, state::State,
34};
35
36/// Transaction executor.
37pub struct ExecutiveContext<'a> {
38    state: &'a mut State,
39    env: &'a Env,
40    machine: &'a Machine,
41    spec: &'a Spec,
42}
43
44impl<'a> ExecutiveContext<'a> {
45    pub fn new(
46        state: &'a mut State, env: &'a Env, machine: &'a Machine,
47        spec: &'a Spec,
48    ) -> Self {
49        ExecutiveContext {
50            state,
51            env,
52            machine,
53            spec,
54        }
55    }
56
57    #[inline(never)]
58    pub fn transact<O: ExecutiveObserver>(
59        self, tx: &SignedTransaction, options: TransactOptions<O>,
60    ) -> DbResult<ExecutionOutcome> {
61        // Transaction should execute on an empty cache
62        assert!(self.state.cache.get_mut().is_empty());
63
64        let fresh_exec = FreshExecutive::new(self, tx, options);
65
66        Ok(match fresh_exec.check_all()? {
67            Ok(executive) => executive.execute_transaction()?,
68            Err(execution_outcome) => execution_outcome,
69        })
70    }
71
72    pub fn apply_env_overrides(
73        env: &mut Env, block_override: Box<BlockOverrides>,
74    ) {
75        if let Some(number) = block_override.number {
76            env.number = number.as_u64();
77        }
78        if let Some(difficulty) = block_override.difficulty {
79            env.difficulty = difficulty;
80        }
81        if let Some(timestamp) = block_override.time {
82            env.timestamp = timestamp;
83        }
84        if let Some(gas_limit) = block_override.gas_limit {
85            env.gas_limit = U256::from(gas_limit);
86        }
87        if let Some(author) = block_override.coinbase {
88            env.author = author;
89        }
90        if let Some(_random) = block_override.random {
91            // conflux doesn't have random(prevRandao)
92        }
93        if let Some(base_fee) = block_override.base_fee {
94            env.base_gas_price = SpaceMap::new(base_fee, base_fee); // use same base_fee for both spaces
95        }
96
97        if let Some(_block_hash) = &block_override.block_hash {
98            // TODO impl
99        }
100    }
101}
102
103#[inline]
104pub fn gas_required_for(
105    is_create: bool, data: &[u8], access_list: Option<&AccessList>,
106    authorization_len: usize, spec: &ConsensusGasSpec,
107) -> u64 {
108    let init_gas = (if is_create {
109        spec.tx_create_gas
110    } else {
111        spec.tx_gas
112    }) as u64;
113
114    let byte_gas = |b: &u8| {
115        (match *b {
116            0 => spec.tx_data_zero_gas,
117            _ => spec.tx_data_non_zero_gas,
118        }) as u64
119    };
120    let data_gas: u64 = data.iter().map(byte_gas).sum();
121    let data_word = (data.len() as u64 + 31) / 32;
122
123    // CIP-645i: EIP-3860: Limit and meter initcode
124    let initcode_gas = if spec.cip645.eip3860 && is_create {
125        data_word * spec.init_code_word_gas as u64
126    } else {
127        0
128    };
129
130    let access_gas: u64 = if let Some(acc) = access_list {
131        let address_gas =
132            acc.len() as u64 * spec.access_list_address_gas as u64;
133
134        let storage_key_num =
135            acc.iter().map(|e| e.storage_keys.len() as u64).sum::<u64>();
136        let storage_key_gas =
137            storage_key_num * spec.access_list_storage_key_gas as u64;
138
139        address_gas + storage_key_gas
140    } else {
141        0
142    };
143
144    let authorization_gas = spec.per_empty_account_cost as u64
145        * spec.evm_gas_ratio as u64
146        * authorization_len as u64;
147
148    init_gas + data_gas + access_gas + authorization_gas + initcode_gas
149}
150
151pub fn eip7623_required_gas(data: &[u8], spec: &ConsensusGasSpec) -> u64 {
152    if !spec.eip7623 {
153        return 0;
154    }
155
156    let byte_floor_gas = |b: &u8| {
157        (match *b {
158            0 => spec.tx_data_floor_zero_gas,
159            _ => spec.tx_data_floor_non_zero_gas,
160        }) as u64
161    };
162
163    spec.tx_gas as u64 + data.iter().map(byte_floor_gas).sum::<u64>()
164}
165
166pub fn contract_address(
167    address_scheme: CreateContractAddress, block_number: u64,
168    sender: &AddressWithSpace, nonce: &U256, code: &[u8],
169) -> (AddressWithSpace, H256) {
170    let (mut address, code_hash) = cfx_vm_types::contract_address(
171        address_scheme,
172        block_number,
173        &sender.address,
174        nonce,
175        code,
176    );
177    if sender.space == Space::Native {
178        address.set_contract_type_bits();
179    }
180    (address.with_space(sender.space), code_hash)
181}
182
183#[cfg(test)]
184pub mod test_util {
185    use crate::{
186        executive_observer::TracerTrait, stack::accrue_substate,
187        substate::Substate,
188    };
189    use cfx_statedb::Result as DbResult;
190    use cfx_vm_interpreter::FinalizationResult;
191    use cfx_vm_types::{self as vm, ActionParams};
192
193    use super::{pre_checked_executive::exec_vm, ExecutiveContext};
194
195    impl<'a> ExecutiveContext<'a> {
196        pub fn call_for_test(
197            &mut self, params: ActionParams, substate: &mut Substate,
198            tracer: &mut dyn TracerTrait,
199        ) -> DbResult<vm::Result<FinalizationResult>> {
200            let mut frame_result = exec_vm(self, params, tracer)?;
201            accrue_substate(substate, &mut frame_result);
202
203            Ok(frame_result.map(Into::into))
204        }
205    }
206}