cfxcore/consensus/consensus_graph/rpc_api/
execution_provider.rs

1use super::super::ConsensusGraph;
2
3use crate::{
4    block_data_manager::BlockExecutionResultWithEpoch,
5    errors::Result as CoreResult,
6};
7use cfx_execute_helper::estimation::{EstimateExt, EstimateRequest};
8use cfx_executor::executive::ExecutionOutcome;
9use cfx_parameters::rpc::{
10    GAS_PRICE_BLOCK_SAMPLE_SIZE, GAS_PRICE_DEFAULT_VALUE,
11    GAS_PRICE_TRANSACTION_SAMPLE_SIZE,
12};
13use cfx_rpc_eth_types::EvmOverrides;
14use cfx_types::{Space, H256, U256};
15use primitives::{EpochNumber, SignedTransaction};
16
17impl ConsensusGraph {
18    /// Get the average gas price of the last GAS_PRICE_TRANSACTION_SAMPLE_SIZE
19    /// blocks
20    pub fn gas_price(&self, space: Space) -> Option<U256> {
21        let inner = self.inner.read();
22        let mut last_epoch_number = inner.best_epoch_number();
23        let (
24            number_of_tx_to_sample,
25            mut number_of_blocks_to_sample,
26            block_gas_ratio,
27        ) = (
28            GAS_PRICE_TRANSACTION_SAMPLE_SIZE,
29            GAS_PRICE_BLOCK_SAMPLE_SIZE,
30            1,
31        );
32        let mut prices = Vec::new();
33        let mut total_block_gas_limit: u64 = 0;
34        let mut total_tx_gas_limit: u64 = 0;
35
36        loop {
37            if number_of_blocks_to_sample == 0 || last_epoch_number == 0 {
38                break;
39            }
40            if prices.len() == number_of_tx_to_sample {
41                break;
42            }
43            let mut hashes = inner
44                .block_hashes_by_epoch(last_epoch_number.into())
45                .unwrap();
46            hashes.reverse();
47            last_epoch_number -= 1;
48
49            for hash in hashes {
50                let block = self
51                    .data_man
52                    .block_by_hash(&hash, false /* update_cache */)
53                    .unwrap();
54                total_block_gas_limit +=
55                    block.block_header.gas_limit().as_u64() * block_gas_ratio;
56                for tx in block.transactions.iter() {
57                    if space == Space::Native && tx.space() != Space::Native {
58                        // For cfx_gasPrice, we only count Native transactions.
59                        continue;
60                    }
61                    // add the tx.gas() to total_tx_gas_limit even it is packed
62                    // multiple times because these tx all
63                    // will occupy block's gas space
64                    total_tx_gas_limit += tx.transaction.gas().as_u64();
65                    prices.push(tx.gas_price().clone());
66                    if prices.len() == number_of_tx_to_sample {
67                        break;
68                    }
69                }
70                number_of_blocks_to_sample -= 1;
71                if number_of_blocks_to_sample == 0
72                    || prices.len() == number_of_tx_to_sample
73                {
74                    break;
75                }
76            }
77        }
78
79        prices.sort();
80        if prices.is_empty() || total_tx_gas_limit == 0 {
81            Some(U256::from(GAS_PRICE_DEFAULT_VALUE))
82        } else {
83            let average_gas_limit_multiple =
84                total_block_gas_limit / total_tx_gas_limit;
85            if average_gas_limit_multiple > 5 {
86                // used less than 20%
87                Some(U256::from(GAS_PRICE_DEFAULT_VALUE))
88            } else if average_gas_limit_multiple >= 2 {
89                // used less than 50%
90                Some(prices[prices.len() / 8])
91            } else {
92                // used more than 50%
93                Some(prices[prices.len() / 2])
94            }
95        }
96    }
97
98    pub fn get_block_execution_info(
99        &self, block_hash: &H256,
100    ) -> Option<(BlockExecutionResultWithEpoch, Option<H256>)> {
101        let results_with_epoch = self
102            .inner
103            .read_recursive()
104            .block_execution_results_by_hash(block_hash, true)?;
105
106        let pivot_hash = results_with_epoch.0;
107
108        let maybe_state_root = match self.executor.wait_for_result(pivot_hash) {
109            Ok(execution_commitment) => {
110                // We already has transaction address with epoch_hash executed,
111                // so we can always get the state_root with
112                // `wait_for_result`
113                Some(
114                    execution_commitment
115                        .state_root_with_aux_info
116                        .aux_info
117                        .state_root_hash,
118                )
119            }
120            Err(msg) => {
121                warn!("get_transaction_receipt_and_block_info() gets the following error from ConsensusExecutor: {}", msg);
122                None
123            }
124        };
125
126        Some((results_with_epoch, maybe_state_root))
127    }
128
129    pub fn call_virtual(
130        &self, tx: &SignedTransaction, epoch: EpochNumber,
131        request: EstimateRequest, evm_overrides: EvmOverrides,
132    ) -> CoreResult<(ExecutionOutcome, EstimateExt)> {
133        // only allow to call against stated epoch
134        self.validate_stated_epoch(&epoch)?;
135        let (epoch_id, epoch_size) = if let Ok(v) =
136            self.get_block_hashes_by_epoch(epoch)
137        {
138            (v.last().expect("pivot block always exist").clone(), v.len())
139        } else {
140            bail!("cannot get block hashes in the specified epoch, maybe it does not exist?");
141        };
142        self.executor.call_virtual(
143            tx,
144            &epoch_id,
145            epoch_size,
146            request,
147            evm_overrides,
148        )
149    }
150}