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