cfxcore/consensus/consensus_inner/consensus_executor/
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/
4
5mod epoch_execution;
6
7use core::convert::TryFrom;
8use std::{
9    collections::{BTreeMap, BTreeSet, HashMap},
10    convert::From,
11    fmt::{Debug, Formatter},
12    sync::{
13        atomic::{AtomicBool, Ordering::Relaxed},
14        mpsc::{channel, RecvError, Sender, TryRecvError},
15        Arc,
16    },
17    thread::{self, JoinHandle},
18};
19
20use crate::hash::KECCAK_EMPTY_LIST_RLP;
21use parking_lot::{Mutex, RwLock};
22use rayon::{ThreadPool, ThreadPoolBuilder};
23use rustc_hex::ToHex;
24
25use cfx_internal_common::{
26    debug::*, EpochExecutionCommitment, StateRootWithAuxInfo,
27};
28use cfx_parameters::consensus::*;
29use cfx_statedb::{Result as DbResult, StateDb};
30use cfx_storage::{
31    defaults::DEFAULT_EXECUTION_PREFETCH_THREADS, StateIndex,
32    StorageManagerTrait,
33};
34use cfx_types::{
35    AddressSpaceUtil, AllChainID, BigEndianHash, Space, H160, H256,
36    KECCAK_EMPTY_BLOOM, U256, U512,
37};
38use metrics::{register_meter_with_group, Meter, MeterTimer};
39use primitives::{
40    compute_block_number, receipt::BlockReceipts, Block, BlockHeader,
41    BlockHeaderBuilder, SignedTransaction, MERKLE_NULL_NODE,
42};
43
44use crate::{
45    block_data_manager::{BlockDataManager, BlockRewardResult, PosRewardInfo},
46    consensus::{
47        consensus_inner::{
48            consensus_new_block_handler::ConsensusNewBlockHandler,
49            StateBlameInfo,
50        },
51        pos_handler::PosVerifier,
52        ConsensusGraphInner,
53    },
54    errors::{invalid_params_check, Result as CoreResult},
55    verification::{
56        compute_receipts_root, VerificationConfig, VerifyTxLocalMode,
57        VerifyTxMode,
58    },
59    SharedTransactionPool,
60};
61use cfx_execute_helper::estimation::{
62    EstimateExt, EstimateRequest, EstimationContext,
63};
64use cfx_executor::{
65    executive::{ExecutionOutcome, ExecutiveContext},
66    machine::Machine,
67    state::{
68        distribute_pos_interest, update_pos_status, State, StateCommitResult,
69    },
70};
71use cfx_vm_types::{Env, Spec};
72use geth_tracer::GethTraceWithHash;
73
74use alloy_rpc_types_trace::geth::GethDebugTracingOptions;
75use cfx_rpc_eth_types::EvmOverrides;
76
77use self::epoch_execution::{GethTask, VirtualCall};
78
79lazy_static! {
80    static ref CONSENSIS_EXECUTION_TIMER: Arc<dyn Meter> =
81        register_meter_with_group("timer", "consensus::handle_epoch_execution");
82    static ref CONSENSIS_COMPUTE_STATE_FOR_BLOCK_TIMER: Arc<dyn Meter> =
83        register_meter_with_group(
84            "timer",
85            "consensus::compute_state_for_block"
86        );
87    static ref GOOD_TPS_METER: Arc<dyn Meter> =
88        register_meter_with_group("system_metrics", "good_tps");
89}
90
91/// The RewardExecutionInfo struct includes most information to compute rewards
92/// for old epochs
93pub struct RewardExecutionInfo {
94    pub past_block_count: u64,
95    pub epoch_blocks: Vec<Arc<Block>>,
96    pub epoch_block_no_reward: Vec<bool>,
97    pub epoch_block_anticone_difficulties: Vec<U512>,
98}
99
100impl Debug for RewardExecutionInfo {
101    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
102        write!(
103            f,
104            "RewardExecutionInfo{{ past_block_count: {} \
105             epoch_blocks: {:?} \
106             epoch_block_no_reward: {:?} \
107             epoch_block_anticone_difficulties: {:?}}}",
108            self.past_block_count,
109            self.epoch_blocks
110                .iter()
111                .map(|b| b.hash())
112                .collect::<Vec<H256>>(),
113            self.epoch_block_no_reward,
114            self.epoch_block_anticone_difficulties
115        )
116    }
117}
118
119#[derive(Debug)]
120enum ExecutionTask {
121    ExecuteEpoch(EpochExecutionTask),
122    GetResult(GetExecutionResultTask),
123
124    /// Stop task is used to stop the execution thread
125    Stop,
126}
127
128/// The EpochExecutionTask struct includes all the information needed to execute
129/// an epoch
130#[derive(Debug)]
131pub struct EpochExecutionTask {
132    epoch_hash: H256,
133    epoch_block_hashes: Vec<H256>,
134    start_block_number: u64,
135    reward_info: Option<RewardExecutionInfo>,
136    // TODO:
137    //  on_local_pivot should be computed at the beginning of the
138    //  epoch execution, not to be set from task.
139    on_local_pivot: bool,
140    force_recompute: bool,
141}
142
143impl EpochExecutionTask {
144    pub fn new(
145        epoch_arena_index: usize, inner: &ConsensusGraphInner,
146        reward_execution_info: Option<RewardExecutionInfo>,
147        on_local_pivot: bool, force_recompute: bool,
148    ) -> Self {
149        Self {
150            epoch_hash: inner.arena[epoch_arena_index].hash,
151            epoch_block_hashes: inner.get_epoch_block_hashes(epoch_arena_index),
152            start_block_number: inner
153                .get_epoch_start_block_number(epoch_arena_index),
154            reward_info: reward_execution_info,
155            on_local_pivot,
156            force_recompute,
157        }
158    }
159}
160
161/// `sender` is used to return the computed `(state_root, receipts_root,
162/// logs_bloom_hash)` to the thread who sends this task.
163#[derive(Debug)]
164struct GetExecutionResultTask {
165    pub epoch_hash: H256,
166    pub sender: Sender<Option<EpochExecutionCommitment>>,
167}
168
169/// ConsensusExecutor processes transaction execution tasks.
170pub struct ConsensusExecutor {
171    /// The thread responsible for execution transactions
172    thread: Mutex<Option<JoinHandle<()>>>,
173
174    /// The sender to send tasks to be executed by `self.thread`
175    sender: Mutex<Sender<ExecutionTask>>,
176
177    /// The state indicating whether the thread should be stopped
178    stopped: AtomicBool,
179
180    /// The handler to provide functions to handle `ExecutionTask` and execute
181    /// transactions It is used both asynchronously by `self.thread` and
182    /// synchronously by the executor itself
183    pub handler: Arc<ConsensusExecutionHandler>,
184
185    consensus_graph_bench_mode: bool,
186}
187
188impl ConsensusExecutor {
189    pub fn start(
190        tx_pool: SharedTransactionPool, data_man: Arc<BlockDataManager>,
191        consensus_inner: Arc<RwLock<ConsensusGraphInner>>,
192        config: ConsensusExecutionConfiguration,
193        verification_config: VerificationConfig, bench_mode: bool,
194        pos_verifier: Arc<PosVerifier>,
195    ) -> Arc<Self> {
196        let machine = tx_pool.machine();
197        let handler = Arc::new(ConsensusExecutionHandler::new(
198            tx_pool,
199            data_man.clone(),
200            config,
201            verification_config,
202            machine,
203            pos_verifier,
204        ));
205        let (sender, receiver) = channel();
206
207        let executor_raw = ConsensusExecutor {
208            thread: Mutex::new(None),
209            sender: Mutex::new(sender),
210            stopped: AtomicBool::new(false),
211            handler: handler.clone(),
212            consensus_graph_bench_mode: bench_mode,
213        };
214        let executor = Arc::new(executor_raw);
215        let executor_thread = executor.clone();
216        // It receives blocks hashes from on_new_block and execute them
217        let handle = thread::Builder::new()
218            .name("Consensus Execution Worker".into())
219            .spawn(move || loop {
220                if executor_thread.stopped.load(Relaxed) {
221                    // The thread should be stopped. The rest tasks in the queue
222                    // will be discarded.
223                    break;
224                }
225
226                let get_optimistic_task = || {
227                    let mut inner = consensus_inner.try_write()?;
228
229                    let task = executor_thread
230                        .get_optimistic_execution_task(&mut *inner)?;
231
232                    debug!("Get optimistic_execution_task {:?}", task);
233                    Some(ExecutionTask::ExecuteEpoch(task))
234                };
235
236                let maybe_task = {
237                    // Here we use `try_write` because some thread
238                    // may wait for execution results while holding the
239                    // Consensus Inner lock, if we wait on
240                    // inner lock here we may get deadlock.
241                    match receiver.try_recv() {
242                        Ok(task) => Some(task),
243                        Err(TryRecvError::Empty) => {
244                            // The channel is empty, so we try to optimistically
245                            // get later epochs to execute.
246                            get_optimistic_task()
247                        }
248                        Err(TryRecvError::Disconnected) => {
249                            info!("Channel disconnected, stop thread");
250                            break;
251                        }
252                    }
253                };
254                let task = match maybe_task {
255                    Some(task) => task,
256                    None => {
257                        //  Even optimistic tasks are all finished, so we block
258                        // and wait for  new execution
259                        // tasks.  New optimistic tasks
260                        // will only exist if pivot_chain changes,
261                        //  and new tasks will be sent to `receiver` in this
262                        // case, so this waiting will
263                        // not prevent new optimistic tasks from being executed.
264                        match receiver.recv() {
265                            Ok(task) => task,
266                            Err(RecvError) => {
267                                info!("Channel receive error, stop thread");
268                                break;
269                            }
270                        }
271                    }
272                };
273                if !handler.handle_execution_work(task) {
274                    // `task` is `Stop`, so just stop.
275                    break;
276                }
277            })
278            .expect("Cannot fail");
279        *executor.thread.lock() = Some(handle);
280        executor
281    }
282
283    // TODO: The comments and method name are not precise,
284    // TODO: given the single-threaded design.
285    /// Wait until all tasks currently in the queue to be executed and return
286    /// `(state_root, receipts_root, logs_bloom_hash)` of the given
287    /// `epoch_hash`.
288    ///
289    /// It is the caller's responsibility to ensure that `epoch_hash` is indeed
290    /// computed when all the tasks before are finished.
291    // TODO Release Consensus inner lock if possible when the function is called
292    pub fn wait_for_result(
293        &self, epoch_hash: H256,
294    ) -> Result<EpochExecutionCommitment, String> {
295        // In consensus_graph_bench_mode execution is skipped.
296        if self.consensus_graph_bench_mode {
297            Ok(EpochExecutionCommitment {
298                state_root_with_aux_info: StateRootWithAuxInfo::genesis(
299                    &MERKLE_NULL_NODE,
300                ),
301                receipts_root: KECCAK_EMPTY_LIST_RLP,
302                logs_bloom_hash: KECCAK_EMPTY_BLOOM,
303            })
304        } else {
305            if self.handler.data_man.epoch_executed(&epoch_hash) {
306                // The epoch already executed, so we do not need wait for the
307                // queue to be empty
308                return self
309                    .handler
310                    .get_execution_result(&epoch_hash).ok_or("Cannot get expected execution results from the data base. Probably the database is corrupted!".to_string());
311            }
312            let (sender, receiver) = channel();
313            debug!("Wait for execution result of epoch {:?}", epoch_hash);
314            self.sender
315                .lock()
316                .send(ExecutionTask::GetResult(GetExecutionResultTask {
317                    epoch_hash,
318                    sender,
319                }))
320                .expect("Cannot fail");
321            receiver.recv().map_err(|e| e.to_string())?.ok_or(
322                "Waiting for an execution result that is not enqueued!"
323                    .to_string(),
324            )
325        }
326    }
327
328    fn get_optimistic_execution_task(
329        &self, inner: &mut ConsensusGraphInner,
330    ) -> Option<EpochExecutionTask> {
331        if !inner.inner_conf.enable_optimistic_execution {
332            return None;
333        }
334
335        let epoch_arena_index = {
336            let mut state_availability_boundary =
337                inner.data_man.state_availability_boundary.write();
338            let opt_height =
339                state_availability_boundary.optimistic_executed_height?;
340            if opt_height != state_availability_boundary.upper_bound + 1 {
341                // The `opt_height` parent's state has not been executed.
342                // This may happen when the pivot chain switches between
343                // the checks of the execution queue and the opt task.
344                return None;
345            }
346            let next_opt_height = opt_height + 1;
347            if next_opt_height
348                >= inner.pivot_index_to_height(inner.pivot_chain.len())
349            {
350                state_availability_boundary.optimistic_executed_height = None;
351            } else {
352                state_availability_boundary.optimistic_executed_height =
353                    Some(next_opt_height);
354            }
355            inner.get_pivot_block_arena_index(opt_height)
356        };
357
358        // `on_local_pivot` is set to `true` because when we later skip its
359        // execution on pivot chain, we will not notify tx pool, so we
360        // will also notify in advance.
361        let reward_execution_info =
362            self.get_reward_execution_info(inner, epoch_arena_index);
363        let execution_task = EpochExecutionTask::new(
364            epoch_arena_index,
365            inner,
366            reward_execution_info,
367            true,  /* on_local_pivot */
368            false, /* force_compute */
369        );
370        Some(execution_task)
371    }
372
373    pub fn get_reward_execution_info_from_index(
374        &self, inner: &mut ConsensusGraphInner,
375        reward_index: Option<(usize, usize)>,
376    ) -> Option<RewardExecutionInfo> {
377        reward_index.map(
378            |(pivot_arena_index, anticone_penalty_cutoff_epoch_arena_index)| {
379                // We have to wait here because blame information will determine the reward of each block.
380                // In order to compute the correct blame information locally, we have to wait for the execution to return.
381                let height = inner.arena[pivot_arena_index].height;
382                if !self.consensus_graph_bench_mode
383                {
384                    debug!(
385                        "wait_and_compute_state_valid_locked, idx = {}, \
386                         height = {}, era_genesis_height = {} era_stable_height = {}",
387                        pivot_arena_index, height, inner.cur_era_genesis_height, inner.cur_era_stable_height
388                    );
389                    self.wait_and_compute_state_valid_and_blame_info_locked(
390                        pivot_arena_index,
391                        inner,
392                    )
393                        .unwrap();
394                }
395
396                let epoch_blocks =
397                    inner.get_executable_epoch_blocks(pivot_arena_index);
398
399                let mut epoch_block_no_reward =
400                    Vec::with_capacity(epoch_blocks.len());
401                let mut epoch_block_anticone_difficulties =
402                    Vec::with_capacity(epoch_blocks.len());
403
404                let epoch_difficulty =
405                    inner.arena[pivot_arena_index].difficulty;
406                let anticone_cutoff_epoch_anticone_set_ref_opt = inner
407                    .anticone_cache
408                    .get(anticone_penalty_cutoff_epoch_arena_index);
409                let anticone_cutoff_epoch_anticone_set;
410                if let Some(r) = anticone_cutoff_epoch_anticone_set_ref_opt {
411                    anticone_cutoff_epoch_anticone_set = r.clone();
412                } else {
413                    anticone_cutoff_epoch_anticone_set = ConsensusNewBlockHandler::compute_anticone_hashset_bruteforce(inner, anticone_penalty_cutoff_epoch_arena_index);
414                }
415                let ordered_epoch_blocks = inner.get_ordered_executable_epoch_blocks(pivot_arena_index).clone();
416                for index in ordered_epoch_blocks.iter() {
417                    let block_consensus_node = &inner.arena[*index];
418
419                    let mut no_reward =
420                        block_consensus_node.data.partial_invalid;
421                    if !self.consensus_graph_bench_mode && !no_reward {
422                        if *index == pivot_arena_index {
423                            no_reward = !inner.arena[pivot_arena_index]
424                                .data
425                                .state_valid.expect("computed in wait_and_compute_state_valid_locked");
426                        } else {
427                            no_reward = !inner
428                                .compute_vote_valid_for_pivot_block(
429                                    *index,
430                                    pivot_arena_index,
431                                );
432                        }
433                    }
434                    // If a block is partial_invalid, it won't have reward and
435                    // anticone_difficulty will not be used, so it's okay to set
436                    // it to 0.
437                    let mut anticone_difficulty: U512 = 0.into();
438                    if !no_reward {
439                        let block_consensus_node_anticone_opt =
440                            inner.anticone_cache.get(*index);
441                        let block_consensus_node_anticone = if let Some(r) = block_consensus_node_anticone_opt {
442                            r.clone()
443                        } else {
444                            ConsensusNewBlockHandler::compute_anticone_hashset_bruteforce(inner, *index)
445                        };
446
447                        for idx in block_consensus_node_anticone {
448                            if inner.is_same_era(idx, pivot_arena_index) && !anticone_cutoff_epoch_anticone_set.contains(&idx) {
449                                anticone_difficulty +=
450                                    U512::from(U256::from(inner.block_weight(
451                                        idx
452                                    )));
453                            }
454                        }
455
456                        // TODO: check the clear definition of anticone penalty,
457                        // normally and around the time of difficulty
458                        // adjustment.
459                        // LINT.IfChange(ANTICONE_PENALTY_1)
460                        if anticone_difficulty / U512::from(epoch_difficulty)
461                            >= U512::from(self.handler.machine.params().anticone_penalty_ratio)
462                        {
463                            no_reward = true;
464                        }
465                        // LINT.ThenChange(consensus/consensus_executor.
466                        // rs#ANTICONE_PENALTY_2)
467                    }
468                    epoch_block_no_reward.push(no_reward);
469                    epoch_block_anticone_difficulties.push(anticone_difficulty);
470                }
471                RewardExecutionInfo {
472                    past_block_count: inner.arena[pivot_arena_index].past_num_blocks,
473                    epoch_blocks,
474                    epoch_block_no_reward,
475                    epoch_block_anticone_difficulties,
476                }
477            },
478        )
479    }
480
481    pub fn get_reward_execution_info(
482        &self, inner: &mut ConsensusGraphInner, epoch_arena_index: usize,
483    ) -> Option<RewardExecutionInfo> {
484        self.get_reward_execution_info_from_index(
485            inner,
486            inner.get_pivot_reward_index(epoch_arena_index),
487        )
488    }
489
490    /// Wait for the deferred state to be executed and compute `state_valid` and
491    /// `blame_info` for `me`.
492    fn wait_and_compute_state_valid_and_blame_info(
493        &self, me: usize, inner_lock: &RwLock<ConsensusGraphInner>,
494    ) -> Result<(), String> {
495        // TODO:
496        //  can we only wait for the deferred block?
497        //  waiting for its parent seems redundant.
498        // We go up from deferred state block of `me`
499        // and find all states whose commitments are missing
500        let waiting_blocks = inner_lock
501            .read()
502            .collect_defer_blocks_missing_execution_commitments(me)?;
503        // Now we wait without holding the inner lock
504        // Note that we must use hash instead of index because once we release
505        // the lock, there might be a checkpoint coming in to break
506        // index
507        for state_block_hash in waiting_blocks {
508            let commitment = self.wait_for_result(state_block_hash)?;
509            self.handler.data_man.insert_epoch_execution_commitment(
510                state_block_hash,
511                commitment.state_root_with_aux_info,
512                commitment.receipts_root,
513                commitment.logs_bloom_hash,
514            );
515        }
516        // Now we need to wait for the execution information of all missing
517        // blocks to come back
518        // TODO: can we merge the state valid computation into the consensus
519        // executor?
520        inner_lock
521            .write()
522            .compute_state_valid_and_blame_info(me, self)?;
523        Ok(())
524    }
525
526    fn wait_and_compute_state_valid_and_blame_info_locked(
527        &self, me: usize, inner: &mut ConsensusGraphInner,
528    ) -> Result<(), String> {
529        // TODO:
530        //  can we only wait for the deferred block?
531        //  waiting for its parent seems redundant.
532        // We go up from deferred state block of `me`
533        // and find all states whose commitments are missing
534        let waiting_blocks =
535            inner.collect_defer_blocks_missing_execution_commitments(me)?;
536        trace!(
537            "wait_and_compute_state_valid_locked: waiting_blocks={:?}",
538            waiting_blocks
539        );
540        // for this rare case, we should make wait_for_result to pop up errors!
541        for state_block_hash in waiting_blocks {
542            self.wait_for_result(state_block_hash)?;
543        }
544        // Now we need to wait for the execution information of all missing
545        // blocks to come back
546        // TODO: can we merge the state valid computation into the consensus
547        // executor?
548        inner.compute_state_valid_and_blame_info(me, self)?;
549        Ok(())
550    }
551
552    pub fn get_blame_and_deferred_state_for_generation(
553        &self, parent_block_hash: &H256,
554        inner_lock: &RwLock<ConsensusGraphInner>,
555    ) -> Result<StateBlameInfo, String> {
556        let (parent_arena_index, last_state_block) = {
557            let inner = inner_lock.read();
558            let parent_opt = inner.hash_to_arena_indices.get(parent_block_hash);
559            if parent_opt.is_none() {
560                return Err(format!(
561                    "Too old parent for generation, parent_hash={:?}",
562                    parent_block_hash
563                ));
564            }
565            (
566                *parent_opt.unwrap(),
567                inner
568                    .get_state_block_with_delay(
569                        parent_block_hash,
570                        DEFERRED_STATE_EPOCH_COUNT as usize - 1,
571                    )?
572                    .clone(),
573            )
574        };
575        let last_result = self.wait_for_result(last_state_block)?;
576        self.wait_and_compute_state_valid_and_blame_info(
577            parent_arena_index,
578            inner_lock,
579        )?;
580        {
581            let inner = &mut *inner_lock.write();
582            if inner.arena[parent_arena_index].hash == *parent_block_hash {
583                Ok(inner.compute_blame_and_state_with_execution_result(
584                    parent_arena_index,
585                    last_result
586                        .state_root_with_aux_info
587                        .aux_info
588                        .state_root_hash,
589                    last_result.receipts_root,
590                    last_result.logs_bloom_hash,
591                )?)
592            } else {
593                Err("Too old parent/subtree to prepare for generation"
594                    .to_owned())
595            }
596        }
597    }
598
599    /// Enqueue the epoch to be executed by the background execution thread
600    /// The parameters are needed for the thread to execute this epoch without
601    /// holding inner lock.
602    pub fn enqueue_epoch(&self, task: EpochExecutionTask) -> bool {
603        if !self.consensus_graph_bench_mode {
604            self.sender
605                .lock()
606                .send(ExecutionTask::ExecuteEpoch(task))
607                .is_ok()
608        } else {
609            true
610        }
611    }
612
613    /// Execute the epoch synchronously
614    pub fn compute_epoch(
615        &self, task: EpochExecutionTask,
616        debug_record: Option<&mut ComputeEpochDebugRecord>,
617        recover_mpt_during_construct_pivot_state: bool,
618    ) {
619        if self.consensus_graph_bench_mode {
620            return;
621        }
622        self.handler.handle_epoch_execution(
623            task,
624            debug_record,
625            recover_mpt_during_construct_pivot_state,
626        )
627    }
628
629    pub fn epoch_executed_and_recovered(
630        &self, epoch_hash: &H256, epoch_block_hashes: &Vec<H256>,
631        on_local_pivot: bool,
632        reward_execution_info: &Option<RewardExecutionInfo>, epoch_height: u64,
633    ) -> bool {
634        self.handler.epoch_executed_and_recovered(
635            epoch_hash,
636            epoch_block_hashes,
637            on_local_pivot,
638            reward_execution_info,
639            epoch_height,
640        )
641    }
642
643    pub fn call_virtual(
644        &self, tx: &SignedTransaction, epoch_id: &H256, epoch_size: usize,
645        request: EstimateRequest, evm_overrides: EvmOverrides,
646    ) -> CoreResult<(ExecutionOutcome, EstimateExt)> {
647        self.handler.call_virtual(
648            tx,
649            epoch_id,
650            epoch_size,
651            request,
652            evm_overrides,
653        )
654    }
655
656    pub fn collect_blocks_geth_trace(
657        &self, epoch_id: H256, epoch_num: u64, blocks: &Vec<Arc<Block>>,
658        opts: GethDebugTracingOptions, tx_hash: Option<H256>,
659    ) -> CoreResult<Vec<GethTraceWithHash>> {
660        self.handler.collect_blocks_geth_trace(
661            epoch_id, epoch_num, blocks, opts, tx_hash,
662        )
663    }
664
665    pub fn stop(&self) {
666        // `stopped` is used to allow the execution thread to stopped even the
667        // queue is not empty and `ExecutionTask::Stop` has not been
668        // processed.
669        self.stopped.store(true, Relaxed);
670
671        // We still need this task because otherwise if the execution queue is
672        // empty the execution thread will block on `recv` forever and
673        // unable to check `stopped`
674        self.sender
675            .lock()
676            .send(ExecutionTask::Stop)
677            .expect("execution receiver exists");
678        if let Some(thread) = self.thread.lock().take() {
679            thread.join().ok();
680        }
681    }
682
683    /// Binary search to find the starting point so we can execute to the end of
684    /// the chain.
685    /// Return the first index that is not executed,
686    /// or return `chain.len()` if they are all executed (impossible for now).
687    ///
688    /// NOTE: If a state for an block exists, all the blocks on its pivot chain
689    /// must have been executed and state committed. The receipts for these
690    /// past blocks may not exist because the receipts on forks will be
691    /// garbage-collected, but when we need them, we will recompute these
692    /// missing receipts in `process_rewards_and_fees`. This 'recompute' is safe
693    /// because the parent state exists. Thus, it's okay that here we do not
694    /// check existence of the receipts that will be needed for reward
695    /// computation during epoch execution.
696    fn find_start_chain_index(
697        inner: &ConsensusGraphInner, chain: &Vec<usize>,
698    ) -> usize {
699        let mut base = 0;
700        let mut size = chain.len();
701        while size > 1 {
702            let half = size / 2;
703            let mid = base + half;
704            let epoch_hash = inner.arena[chain[mid]].hash;
705            base = if inner.data_man.epoch_executed(&epoch_hash) {
706                mid
707            } else {
708                base
709            };
710            size -= half;
711        }
712        let epoch_hash = inner.arena[chain[base]].hash;
713        if inner.data_man.epoch_executed(&epoch_hash) {
714            base + 1
715        } else {
716            base
717        }
718    }
719
720    // TODO:
721    //  this method contains bugs but it's not a big problem since
722    //  it's test-rpc only.
723    /// This is a blocking call to force the execution engine to compute the
724    /// state of a block immediately
725    pub fn compute_state_for_block(
726        &self, block_hash: &H256, inner: &mut ConsensusGraphInner,
727    ) -> Result<(), String> {
728        let _timer = MeterTimer::time_func(
729            CONSENSIS_COMPUTE_STATE_FOR_BLOCK_TIMER.as_ref(),
730        );
731        // If we already computed the state of the block before, we should not
732        // do it again
733        debug!("compute_state_for_block {:?}", block_hash);
734        {
735            let maybe_state_index =
736                self.handler.data_man.get_state_readonly_index(&block_hash);
737            // The state is computed and is retrievable from storage.
738            if let Some(maybe_cached_state_result) =
739                maybe_state_index.map(|state_readonly_index| {
740                    self.handler.data_man.storage_manager.get_state_no_commit(
741                        state_readonly_index,
742                        /* try_open = */ false,
743                        None,
744                    )
745                })
746            {
747                if let Ok(Some(_)) = maybe_cached_state_result {
748                    return Ok(());
749                } else {
750                    return Err("Internal storage error".to_owned());
751                }
752            }
753        }
754        let me_opt = inner.hash_to_arena_indices.get(block_hash);
755        if me_opt == None {
756            return Err("Block hash not found!".to_owned());
757        }
758        // FIXME: isolate this part as a method.
759        let me: usize = *me_opt.unwrap();
760        let block_height = inner.arena[me].height;
761        let mut fork_height = block_height;
762        let mut chain: Vec<usize> = Vec::new();
763        let mut idx = me;
764        // FIXME: this is wrong, however.
765        while fork_height > 0
766            && (fork_height >= inner.get_pivot_height()
767                || inner.get_pivot_block_arena_index(fork_height) != idx)
768        {
769            chain.push(idx);
770            fork_height -= 1;
771            idx = inner.arena[idx].parent;
772        }
773        // FIXME: this is wrong, however.
774        // Because we have genesis at height 0, this should always be true
775        debug_assert!(inner.get_pivot_block_arena_index(fork_height) == idx);
776        debug!("Forked at index {} height {}", idx, fork_height);
777        chain.push(idx);
778        chain.reverse();
779        let start_chain_index =
780            ConsensusExecutor::find_start_chain_index(inner, &chain);
781        debug!("Start execution from index {}", start_chain_index);
782
783        // We need the state of the fork point to start executing the fork
784        if start_chain_index == 0 {
785            let mut last_state_height =
786                if inner.get_pivot_height() > DEFERRED_STATE_EPOCH_COUNT {
787                    inner.get_pivot_height() - DEFERRED_STATE_EPOCH_COUNT
788                } else {
789                    0
790                };
791
792            last_state_height += 1;
793            while last_state_height < fork_height {
794                let epoch_arena_index =
795                    inner.get_pivot_block_arena_index(last_state_height);
796                let reward_execution_info =
797                    self.get_reward_execution_info(inner, epoch_arena_index);
798                self.enqueue_epoch(EpochExecutionTask::new(
799                    epoch_arena_index,
800                    inner,
801                    reward_execution_info,
802                    false, /* on_local_pivot */
803                    false, /* force_recompute */
804                ));
805                last_state_height += 1;
806            }
807        }
808
809        for fork_chain_index in start_chain_index..chain.len() {
810            let epoch_arena_index = chain[fork_chain_index];
811            let reward_index = inner.get_pivot_reward_index(epoch_arena_index);
812
813            let reward_execution_info =
814                self.get_reward_execution_info_from_index(inner, reward_index);
815            self.enqueue_epoch(EpochExecutionTask::new(
816                epoch_arena_index,
817                inner,
818                reward_execution_info,
819                false, /* on_local_pivot */
820                false, /* force_recompute */
821            ));
822        }
823
824        let epoch_execution_result = self.wait_for_result(*block_hash)?;
825        debug!(
826            "Epoch {:?} has state_root={:?} receipts_root={:?} logs_bloom_hash={:?}",
827            inner.arena[me].hash, epoch_execution_result.state_root_with_aux_info,
828            epoch_execution_result.receipts_root, epoch_execution_result.logs_bloom_hash
829        );
830
831        Ok(())
832    }
833}
834
835pub struct ConsensusExecutionHandler {
836    tx_pool: SharedTransactionPool,
837    data_man: Arc<BlockDataManager>,
838    config: ConsensusExecutionConfiguration,
839    verification_config: VerificationConfig,
840    machine: Arc<Machine>,
841    pos_verifier: Arc<PosVerifier>,
842    execution_state_prefetcher: Option<ThreadPool>,
843}
844
845impl ConsensusExecutionHandler {
846    pub fn new(
847        tx_pool: SharedTransactionPool, data_man: Arc<BlockDataManager>,
848        config: ConsensusExecutionConfiguration,
849        verification_config: VerificationConfig, machine: Arc<Machine>,
850        pos_verifier: Arc<PosVerifier>,
851    ) -> Self {
852        ConsensusExecutionHandler {
853            tx_pool,
854            data_man,
855            config,
856            verification_config,
857            machine,
858            pos_verifier,
859            execution_state_prefetcher: if DEFAULT_EXECUTION_PREFETCH_THREADS
860                > 0
861            {
862                Some(
863                    ThreadPoolBuilder::new()
864                        .num_threads(DEFAULT_EXECUTION_PREFETCH_THREADS)
865                        .build()
866                        .unwrap(),
867                )
868            } else {
869                None
870            },
871        }
872    }
873
874    /// Always return `true` for now
875    fn handle_execution_work(&self, task: ExecutionTask) -> bool {
876        debug!("Receive execution task: {:?}", task);
877        match task {
878            ExecutionTask::ExecuteEpoch(task) => {
879                self.handle_epoch_execution(task, None, false)
880            }
881            ExecutionTask::GetResult(task) => self.handle_get_result_task(task),
882            ExecutionTask::Stop => return false,
883        }
884        true
885    }
886
887    fn handle_epoch_execution(
888        &self, task: EpochExecutionTask,
889        debug_record: Option<&mut ComputeEpochDebugRecord>,
890        recover_mpt_during_construct_pivot_state: bool,
891    ) {
892        let _timer = MeterTimer::time_func(CONSENSIS_EXECUTION_TIMER.as_ref());
893        self.compute_epoch(
894            &task.epoch_hash,
895            &task.epoch_block_hashes,
896            task.start_block_number,
897            &task.reward_info,
898            task.on_local_pivot,
899            debug_record,
900            task.force_recompute,
901            recover_mpt_during_construct_pivot_state,
902        );
903    }
904
905    fn handle_get_result_task(&self, task: GetExecutionResultTask) {
906        task.sender
907            .send(self.get_execution_result(&task.epoch_hash))
908            .expect("Consensus Worker fails");
909    }
910
911    /// Get `EpochExecutionCommitment` for an executed epoch.
912    ///
913    /// Return `None` if the commitment does not exist in memory or db.
914    /// For archive node, this should only happen when `epoch_hash` is not
915    /// executed.
916    fn get_execution_result(
917        &self, epoch_hash: &H256,
918    ) -> Option<EpochExecutionCommitment> {
919        self.data_man
920            .get_epoch_execution_commitment_with_db(epoch_hash)
921    }
922
923    fn new_state(
924        &self, pivot_block: &Block,
925        recover_mpt_during_construct_pivot_state: bool,
926    ) -> DbResult<State> {
927        let state_root_with_aux_info = &self
928            .data_man
929            .get_epoch_execution_commitment(
930                pivot_block.block_header.parent_hash(),
931            )
932            // Unwrapping is safe because the state exists.
933            .unwrap()
934            .state_root_with_aux_info;
935
936        let state_index = StateIndex::new_for_next_epoch(
937            pivot_block.block_header.parent_hash(),
938            &state_root_with_aux_info,
939            pivot_block.block_header.height() - 1,
940            self.data_man.get_snapshot_epoch_count(),
941        );
942
943        let storage = self
944            .data_man
945            .storage_manager
946            .get_state_for_next_epoch(
947                state_index,
948                recover_mpt_during_construct_pivot_state,
949            )
950            .expect("No db error")
951            // Unwrapping is safe because the state exists.
952            .expect("State exists");
953
954        let state_db = StateDb::new(storage);
955        State::new(state_db)
956    }
957
958    pub fn epoch_executed_and_recovered(
959        &self, epoch_hash: &H256, epoch_block_hashes: &Vec<H256>,
960        on_local_pivot: bool,
961        reward_execution_info: &Option<RewardExecutionInfo>, epoch_height: u64,
962    ) -> bool {
963        // note: the lock on chain_id is never held so this should be OK.
964        let evm_chain_id = self
965            .machine
966            .params()
967            .chain_id
968            .read()
969            .get_chain_id(epoch_height)
970            .in_evm_space();
971
972        self.data_man.epoch_executed_and_recovered(
973            &epoch_hash,
974            &epoch_block_hashes,
975            on_local_pivot,
976            self.config.executive_trace,
977            reward_execution_info,
978            self.pos_verifier.as_ref(),
979            evm_chain_id,
980        )
981    }
982
983    /// Compute the epoch `epoch_hash`, and skip it if already computed.
984    /// After the function is called, it's assured that the state, the receipt
985    /// root, and the receipts of blocks executed by this epoch exist.
986    ///
987    /// TODO Not sure if this difference is important.
988    /// One different between skipped execution in pivot chain is that the
989    /// transactions packed in the skipped epoch will be checked if they can
990    /// be recycled.
991    pub fn compute_epoch(
992        &self,
993        epoch_hash: &H256,
994        epoch_block_hashes: &Vec<H256>,
995        start_block_number: u64,
996        reward_execution_info: &Option<RewardExecutionInfo>,
997        // TODO: this arg should be removed.
998        on_local_pivot: bool,
999        mut debug_record: Option<&mut ComputeEpochDebugRecord>,
1000        force_recompute: bool,
1001        recover_mpt_during_construct_pivot_state: bool,
1002    ) {
1003        // FIXME: Question: where to calculate if we should make a snapshot?
1004        // FIXME: Currently we make the snapshotting decision when committing
1005        // FIXME: a new state.
1006
1007        // persist block number index
1008        // note: we need to persist before execution because in some cases,
1009        // execution is skipped. when `compute_epoch` is called, it is
1010        // guaranteed that `epoch_hash` is on the current pivot chain.
1011        for (index, hash) in epoch_block_hashes.iter().enumerate() {
1012            let block_number =
1013                compute_block_number(start_block_number, index as u64);
1014            self.data_man
1015                .insert_hash_by_block_number(block_number, hash);
1016        }
1017        let end_block_number =
1018            start_block_number + epoch_block_hashes.len() as u64 - 1;
1019
1020        let pivot_block_header = self
1021            .data_man
1022            .block_header_by_hash(epoch_hash)
1023            .expect("must exists");
1024
1025        if on_local_pivot {
1026            // A temporary safety guard before high nonce
1027            self.safety_guard(pivot_block_header.as_ref());
1028        }
1029
1030        // Check if epoch is computed
1031        if !force_recompute
1032            && debug_record.is_none()
1033            && self.epoch_executed_and_recovered(
1034                &epoch_hash,
1035                &epoch_block_hashes,
1036                on_local_pivot,
1037                reward_execution_info,
1038                pivot_block_header.height(),
1039            )
1040        {
1041            self.update_on_skipped_execution(
1042                epoch_hash,
1043                &pivot_block_header,
1044                on_local_pivot,
1045            );
1046            return;
1047        }
1048
1049        // Get blocks in this epoch after skip checking
1050        let epoch_blocks = self
1051            .data_man
1052            .blocks_by_hash_list(
1053                epoch_block_hashes,
1054                true, /* update_cache */
1055            )
1056            .expect("blocks exist");
1057        let pivot_block = epoch_blocks.last().expect("Not empty");
1058
1059        debug!(
1060            "Process tx epoch_id={}, block_count={}",
1061            epoch_hash,
1062            epoch_blocks.len(),
1063        );
1064
1065        let mut state = self
1066            .new_state(pivot_block, recover_mpt_during_construct_pivot_state)
1067            .expect("Cannot init state");
1068
1069        let epoch_receipts = self
1070            .process_epoch_transactions(
1071                &mut state,
1072                &epoch_blocks,
1073                start_block_number,
1074                on_local_pivot,
1075                /* virtual_call */ None,
1076            )
1077            // TODO: maybe propagate the error all the way up so that the
1078            // program may restart by itself.
1079            .expect("Can not handle db error in consensus, crashing.");
1080
1081        if let Some(reward_execution_info) = reward_execution_info {
1082            let spec = self
1083                .machine
1084                .spec(end_block_number, pivot_block.block_header.height());
1085            // Calculate the block reward for blocks inside the epoch
1086            // All transaction fees are shared among blocks inside one epoch
1087            self.process_rewards_and_fees(
1088                &mut state,
1089                &reward_execution_info,
1090                epoch_hash,
1091                on_local_pivot,
1092                debug_record.as_deref_mut(),
1093                spec,
1094            );
1095        }
1096
1097        self.process_pos_interest(
1098            &mut state,
1099            &pivot_block.block_header,
1100            end_block_number,
1101        )
1102        .expect("db error");
1103
1104        let commit_result = state
1105            .commit(*epoch_hash, debug_record.as_deref_mut())
1106            .expect(&concat!(file!(), ":", line!(), ":", column!()));
1107
1108        if on_local_pivot {
1109            self.notify_txpool(&commit_result, epoch_hash);
1110        };
1111
1112        self.data_man.insert_epoch_execution_commitment(
1113            pivot_block.hash(),
1114            commit_result.state_root.clone(),
1115            compute_receipts_root(&epoch_receipts),
1116            BlockHeaderBuilder::compute_block_logs_bloom_hash(&epoch_receipts),
1117        );
1118
1119        let epoch_execution_commitment = self
1120            .data_man
1121            .get_epoch_execution_commitment(&epoch_hash)
1122            .unwrap();
1123        debug!(
1124            "compute_epoch: on_local_pivot={}, epoch={:?} state_root={:?} receipt_root={:?}, logs_bloom_hash={:?}",
1125            on_local_pivot, epoch_hash, commit_result.state_root, epoch_execution_commitment.receipts_root, epoch_execution_commitment.logs_bloom_hash,
1126        );
1127        self.data_man
1128            .state_availability_boundary
1129            .write()
1130            .adjust_upper_bound(&pivot_block.block_header);
1131    }
1132
1133    fn update_on_skipped_execution(
1134        &self, epoch_hash: &H256, pivot_block_header: &BlockHeader,
1135        on_local_pivot: bool,
1136    ) {
1137        if on_local_pivot {
1138            // Unwrap is safe here because it's guaranteed by outer if.
1139            let state_root = &self
1140                .data_man
1141                .get_epoch_execution_commitment(epoch_hash)
1142                .unwrap()
1143                .state_root_with_aux_info;
1144            // When the state have expired, don't inform TransactionPool.
1145            // TransactionPool doesn't require a precise best_executed_state
1146            // when pivot chain oscillates.
1147            if self
1148                .data_man
1149                .state_availability_boundary
1150                .read()
1151                .check_availability(pivot_block_header.height(), epoch_hash)
1152            {
1153                self.tx_pool
1154                    .set_best_executed_state_by_epoch(
1155                        StateIndex::new_for_readonly(epoch_hash, &state_root),
1156                    )
1157                    // FIXME: propogate error.
1158                    .expect(&concat!(file!(), ":", line!(), ":", column!()));
1159            }
1160        }
1161        self.data_man
1162            .state_availability_boundary
1163            .write()
1164            .adjust_upper_bound(pivot_block_header);
1165        debug!("Skip execution in prefix {:?}", epoch_hash);
1166    }
1167
1168    fn process_pos_interest(
1169        &self, state: &mut State, pivot_header: &BlockHeader,
1170        current_block_number: u64,
1171    ) -> DbResult<()> {
1172        // TODO(peilun): Specify if we unlock before or after executing the
1173        // transactions.
1174        let maybe_parent_pos_ref = self
1175            .data_man
1176            .block_header_by_hash(&pivot_header.parent_hash()) // `None` only for genesis.
1177            .and_then(|parent| parent.pos_reference().clone());
1178        if self
1179            .pos_verifier
1180            .is_enabled_at_height(pivot_header.height())
1181            && maybe_parent_pos_ref.is_some()
1182            && *pivot_header.pos_reference() != maybe_parent_pos_ref
1183        {
1184            let current_pos_ref = pivot_header
1185                .pos_reference()
1186                .as_ref()
1187                .expect("checked before sync graph insertion");
1188            let parent_pos_ref = &maybe_parent_pos_ref.expect("checked");
1189            // The pos_reference is continuous, so after seeing a new
1190            // pos_reference, we only need to process the new
1191            // unlock_txs in it.
1192            for (unlock_node_id, votes) in self
1193                .pos_verifier
1194                .get_unlock_nodes(current_pos_ref, parent_pos_ref)
1195            {
1196                debug!("unlock node: {:?} {}", unlock_node_id, votes);
1197                update_pos_status(state, unlock_node_id, votes)?;
1198            }
1199            if let Some((pos_epoch, reward_event)) = self
1200                .pos_verifier
1201                .get_reward_distribution_event(current_pos_ref, parent_pos_ref)
1202                .as_ref()
1203                .and_then(|x| x.first())
1204            {
1205                debug!("distribute_pos_interest: {:?}", reward_event);
1206                let account_rewards: Vec<(H160, H256, U256)> =
1207                    distribute_pos_interest(
1208                        state,
1209                        reward_event.rewards(),
1210                        current_block_number,
1211                    )?;
1212                self.data_man.insert_pos_reward(
1213                    *pos_epoch,
1214                    &PosRewardInfo::new(account_rewards, pivot_header.hash()),
1215                )
1216            }
1217        }
1218        Ok(())
1219    }
1220
1221    fn notify_txpool(
1222        &self, commit_result: &StateCommitResult, epoch_hash: &H256,
1223    ) {
1224        // FIXME: We may want to propagate the error up.
1225
1226        let accounts_for_txpool = commit_result.accounts_for_txpool.clone();
1227        {
1228            debug!("Notify epoch[{}]", epoch_hash);
1229
1230            // TODO: use channel to deliver the message.
1231            let txpool_clone = self.tx_pool.clone();
1232            std::thread::Builder::new()
1233                .name("txpool_update_state".into())
1234                .spawn(move || {
1235                    txpool_clone.notify_modified_accounts(accounts_for_txpool);
1236                })
1237                .expect("can not notify tx pool to start state");
1238        }
1239
1240        self.tx_pool
1241            .set_best_executed_state_by_epoch(StateIndex::new_for_readonly(
1242                epoch_hash,
1243                &commit_result.state_root,
1244            ))
1245            .expect(&concat!(file!(), ":", line!(), ":", column!()));
1246    }
1247
1248    fn compute_block_base_reward(
1249        &self, past_block_count: u64, pivot_height: u64,
1250    ) -> U512 {
1251        self.machine
1252            .params()
1253            .base_reward_in_ucfx(past_block_count, pivot_height)
1254    }
1255
1256    /// `epoch_block_states` includes if a block is partial invalid and its
1257    /// anticone difficulty
1258    fn process_rewards_and_fees(
1259        &self, state: &mut State, reward_info: &RewardExecutionInfo,
1260        epoch_later: &H256, on_local_pivot: bool,
1261        mut debug_record: Option<&mut ComputeEpochDebugRecord>, spec: Spec,
1262    ) {
1263        /// (Fee, SetOfPackingBlockHash)
1264        struct TxExecutionInfo(U256, BTreeSet<H256>);
1265
1266        let epoch_blocks = &reward_info.epoch_blocks;
1267        let pivot_block = epoch_blocks.last().expect("Not empty");
1268        let reward_epoch_hash = pivot_block.hash();
1269        debug!("Process rewards and fees for {:?}", reward_epoch_hash);
1270        let epoch_difficulty = pivot_block.block_header.difficulty();
1271
1272        let epoch_size = epoch_blocks.len();
1273        let mut epoch_block_total_rewards = Vec::with_capacity(epoch_size);
1274        // This is the total primary tokens issued in this epoch.
1275        let mut total_base_reward: U256 = 0.into();
1276
1277        let base_reward_per_block = if spec.cip94 {
1278            U512::from(state.pow_base_reward())
1279        } else {
1280            self.compute_block_base_reward(
1281                reward_info.past_block_count,
1282                pivot_block.block_header.height(),
1283            )
1284        };
1285        debug!("base_reward: {}", base_reward_per_block);
1286
1287        // Base reward and anticone penalties.
1288        for (enum_idx, block) in epoch_blocks.iter().enumerate() {
1289            let no_reward = reward_info.epoch_block_no_reward[enum_idx];
1290
1291            if no_reward {
1292                epoch_block_total_rewards.push(U256::from(0));
1293                if debug_record.is_some() {
1294                    let debug_out = debug_record.as_mut().unwrap();
1295                    debug_out.no_reward_blocks.push(block.hash());
1296                }
1297            } else {
1298                let pow_quality =
1299                    VerificationConfig::get_or_compute_header_pow_quality(
1300                        &self.data_man.pow,
1301                        &block.block_header,
1302                    );
1303                let mut reward = if pow_quality >= *epoch_difficulty {
1304                    base_reward_per_block
1305                } else {
1306                    debug!(
1307                        "Block {} pow_quality {} is less than epoch_difficulty {}!",
1308                        block.hash(), pow_quality, epoch_difficulty
1309                    );
1310                    0.into()
1311                };
1312
1313                if let Some(debug_out) = &mut debug_record {
1314                    debug_out.block_rewards.push(BlockHashAuthorValue(
1315                        block.hash(),
1316                        block.block_header.author().clone(),
1317                        U256::try_from(reward).unwrap(),
1318                    ));
1319                }
1320
1321                if reward > 0.into() {
1322                    let anticone_difficulty =
1323                        reward_info.epoch_block_anticone_difficulties[enum_idx];
1324                    // LINT.IfChange(ANTICONE_PENALTY_2)
1325                    let anticone_penalty = reward * anticone_difficulty
1326                        / U512::from(epoch_difficulty)
1327                        * anticone_difficulty
1328                        / U512::from(epoch_difficulty)
1329                        / U512::from(
1330                            self.machine.params().anticone_penalty_ratio,
1331                        )
1332                        / U512::from(
1333                            self.machine.params().anticone_penalty_ratio,
1334                        );
1335                    // Lint.ThenChange(consensus/mod.rs#ANTICONE_PENALTY_1)
1336
1337                    debug_assert!(reward > anticone_penalty);
1338                    reward -= anticone_penalty;
1339
1340                    if debug_record.is_some() {
1341                        let debug_out = debug_record.as_mut().unwrap();
1342                        debug_out.anticone_penalties.push(
1343                            BlockHashAuthorValue(
1344                                block.hash(),
1345                                block.block_header.author().clone(),
1346                                U256::try_from(anticone_penalty).unwrap(),
1347                            ),
1348                        );
1349                        //
1350                        // debug_out.anticone_set_size.push(BlockHashValue(
1351                        //                            block.hash(),
1352                        //
1353                        // reward_info.epoch_block_anticone_set_sizes
1354                        //                                [enum_idx],
1355                        //                        ));
1356                    }
1357                }
1358
1359                debug_assert!(reward <= U512::from(U256::max_value()));
1360                let reward = U256::try_from(reward).unwrap();
1361                epoch_block_total_rewards.push(reward);
1362                if !reward.is_zero() {
1363                    total_base_reward += reward;
1364                }
1365            }
1366        }
1367
1368        // Tx fee for each block in this epoch
1369        let mut tx_fee = HashMap::new();
1370
1371        // Compute tx_fee of each block based on gas_used and gas_price of every
1372        // tx
1373        let mut epoch_receipts = None;
1374        let mut secondary_reward = U256::zero();
1375        for (enum_idx, block) in epoch_blocks.iter().enumerate() {
1376            let block_hash = block.hash();
1377            // TODO: better redesign to avoid recomputation.
1378            // FIXME: check state availability boundary here. Actually, it seems
1379            // FIXME: we should never recompute states here.
1380            let block_receipts = match self
1381                .data_man
1382                .block_execution_result_by_hash_with_epoch(
1383                    &block_hash,
1384                    &reward_epoch_hash,
1385                    false, /* update_pivot_assumption */
1386                    true,  /* update_cache */
1387                ) {
1388                Some(block_exec_result) => block_exec_result.block_receipts,
1389                None => {
1390                    let ctx = self
1391                        .data_man
1392                        .get_epoch_execution_context(&reward_epoch_hash)
1393                        .expect("epoch_execution_context should exists here");
1394
1395                    // We need to return receipts instead of getting it through
1396                    // function get_receipts, because it's
1397                    // possible that the computed receipts is deleted by garbage
1398                    // collection before we try get it
1399                    if epoch_receipts.is_none() {
1400                        epoch_receipts = Some(self.recompute_states(
1401                            &reward_epoch_hash,
1402                            &epoch_blocks,
1403                            ctx.start_block_number,
1404                        )
1405                            // TODO: maybe propagate the error all the way up so that the
1406                            // program may restart by itself.
1407                            .expect("Can not handle db error in consensus, crashing."));
1408                    }
1409                    epoch_receipts.as_ref().unwrap()[enum_idx].clone()
1410                }
1411            };
1412
1413            secondary_reward += block_receipts.secondary_reward;
1414            debug_assert!(
1415                block_receipts.receipts.len() == block.transactions.len()
1416            );
1417            // TODO: fill base_price.
1418            for (tx, receipt) in block
1419                .transactions
1420                .iter()
1421                .zip(block_receipts.receipts.iter())
1422            {
1423                let fee =
1424                    receipt.gas_fee - receipt.burnt_gas_fee.unwrap_or_default();
1425
1426                let info = tx_fee
1427                    .entry(tx.hash())
1428                    .or_insert(TxExecutionInfo(fee, BTreeSet::default()));
1429                // The same transaction is executed only once.
1430                debug_assert!(
1431                    fee.is_zero() || info.0.is_zero() || info.1.len() == 0
1432                );
1433                // `false` means the block is fully valid
1434                // Partial invalid blocks will not share the tx fee
1435                if reward_info.epoch_block_no_reward[enum_idx] == false {
1436                    info.1.insert(block_hash);
1437                }
1438                if !fee.is_zero() && info.0.is_zero() {
1439                    info.0 = fee;
1440                }
1441            }
1442        }
1443
1444        let mut block_tx_fees = HashMap::new();
1445        // Note that some transaction fees may get lost due to solely packed by
1446        // a partially invalid block.
1447        let mut burnt_fee = U256::from(0);
1448        for TxExecutionInfo(fee, block_set) in tx_fee.values() {
1449            if block_set.is_empty() {
1450                burnt_fee += *fee;
1451                // tx_fee for the transactions executed in a partial invalid
1452                // blocks and not packed in other blocks will be lost
1453                continue;
1454            }
1455            let block_count = U256::from(block_set.len());
1456            let quotient: U256 = *fee / block_count;
1457            let mut remainder: U256 = *fee - (block_count * quotient);
1458            for block_hash in block_set {
1459                let reward =
1460                    block_tx_fees.entry(*block_hash).or_insert(U256::zero());
1461                *reward += quotient;
1462                if !remainder.is_zero() {
1463                    *reward += 1.into();
1464                    remainder -= 1.into();
1465                }
1466            }
1467            debug_assert!(remainder.is_zero());
1468        }
1469
1470        let mut merged_rewards = BTreeMap::new();
1471        // Here is the exact secondary reward allocated in total
1472        let mut allocated_secondary_reward = U256::from(0);
1473
1474        for (enum_idx, block) in epoch_blocks.iter().enumerate() {
1475            let base_reward = epoch_block_total_rewards[enum_idx];
1476
1477            let block_hash = block.hash();
1478            // Add tx fee to reward.
1479            let tx_fee = if let Some(fee) = block_tx_fees.get(&block_hash) {
1480                if let Some(debug_out) = &mut debug_record {
1481                    debug_out.tx_fees.push(BlockHashAuthorValue(
1482                        block_hash,
1483                        block.block_header.author().clone(),
1484                        *fee,
1485                    ));
1486                }
1487                *fee
1488            } else {
1489                U256::from(0)
1490            };
1491
1492            // Distribute the secondary reward according to primary reward.
1493            let total_reward = if base_reward > U256::from(0) {
1494                let block_secondary_reward =
1495                    base_reward * secondary_reward / total_base_reward;
1496                if let Some(debug_out) = &mut debug_record {
1497                    debug_out.secondary_rewards.push(BlockHashAuthorValue(
1498                        block_hash,
1499                        block.block_header.author().clone(),
1500                        block_secondary_reward,
1501                    ));
1502                }
1503                allocated_secondary_reward += block_secondary_reward;
1504                base_reward + tx_fee + block_secondary_reward
1505            } else {
1506                base_reward + tx_fee
1507            };
1508
1509            *merged_rewards
1510                .entry(*block.block_header.author())
1511                .or_insert(U256::from(0)) += total_reward;
1512
1513            if let Some(debug_out) = &mut debug_record {
1514                debug_out.block_final_rewards.push(BlockHashAuthorValue(
1515                    block_hash,
1516                    block.block_header.author().clone(),
1517                    total_reward,
1518                ));
1519            }
1520            if on_local_pivot {
1521                self.data_man.insert_block_reward_result(
1522                    block_hash,
1523                    epoch_later,
1524                    BlockRewardResult {
1525                        total_reward,
1526                        tx_fee,
1527                        base_reward,
1528                    },
1529                    true,
1530                );
1531                self.data_man
1532                    .receipts_retain_epoch(&block_hash, &reward_epoch_hash);
1533            }
1534        }
1535
1536        debug!("Give rewards merged_reward={:?}", merged_rewards);
1537
1538        for (address, reward) in merged_rewards {
1539            if spec.is_valid_address(&address) {
1540                state
1541                    .add_balance(&address.with_native_space(), &reward)
1542                    .unwrap();
1543            }
1544
1545            if let Some(debug_out) = &mut debug_record {
1546                debug_out
1547                    .merged_rewards_by_author
1548                    .push(AuthorValue(address, reward));
1549                debug_out.state_ops.push(StateOp::IncentiveLevelOp {
1550                    op_name: "add_balance".to_string(),
1551                    key: address.0.to_hex::<String>().as_bytes().to_vec(),
1552                    maybe_value: Some({
1553                        let h: H256 = BigEndianHash::from_uint(&reward);
1554                        h.0.to_hex::<String>().as_bytes().into()
1555                    }),
1556                });
1557            }
1558        }
1559        let new_mint = total_base_reward + allocated_secondary_reward;
1560        if new_mint >= burnt_fee {
1561            // The very likely case
1562            state.add_total_issued(new_mint - burnt_fee);
1563        } else {
1564            // The very unlikely case
1565            state.sub_total_issued(burnt_fee - new_mint);
1566        }
1567    }
1568
1569    fn recompute_states(
1570        &self, pivot_hash: &H256, epoch_blocks: &Vec<Arc<Block>>,
1571        start_block_number: u64,
1572    ) -> DbResult<Vec<Arc<BlockReceipts>>> {
1573        debug!(
1574            "Recompute receipts epoch_id={}, block_count={}",
1575            pivot_hash,
1576            epoch_blocks.len(),
1577        );
1578        let pivot_block = epoch_blocks.last().expect("Not empty");
1579        let mut state = self.new_state(&pivot_block, false)?;
1580        self.process_epoch_transactions(
1581            &mut state,
1582            &epoch_blocks,
1583            start_block_number,
1584            false,
1585            /* virtual_call */ None,
1586        )
1587    }
1588
1589    pub fn call_virtual(
1590        &self, tx: &SignedTransaction, epoch_id: &H256, epoch_size: usize,
1591        request: EstimateRequest, evm_overrides: EvmOverrides,
1592    ) -> CoreResult<(ExecutionOutcome, EstimateExt)> {
1593        let best_block_header = self.data_man.block_header_by_hash(epoch_id);
1594        if best_block_header.is_none() {
1595            bail!("invalid epoch id");
1596        }
1597        let best_block_header = best_block_header.unwrap();
1598        let block_height = best_block_header.height() + 1;
1599
1600        let pos_id = best_block_header.pos_reference().as_ref();
1601        let pos_view_number =
1602            pos_id.and_then(|id| self.pos_verifier.get_pos_view(id));
1603        let pivot_decision_epoch = pos_id
1604            .and_then(|id| self.pos_verifier.get_pivot_decision(id))
1605            .and_then(|hash| self.data_man.block_header_by_hash(&hash))
1606            .map(|header| header.height());
1607
1608        let start_block_number = match self.data_man.get_epoch_execution_context(epoch_id) {
1609            Some(v) => v.start_block_number + epoch_size as u64,
1610            None => bail!("cannot obtain the execution context. Database is potentially corrupted!"),
1611        };
1612        let spec = self.machine.spec(start_block_number, block_height);
1613        let transitions = &self.machine.params().transition_heights;
1614
1615        invalid_params_check(
1616            "tx",
1617            self.verification_config.verify_transaction_common(
1618                tx,
1619                AllChainID::fake_for_virtual(tx.chain_id().unwrap_or(1)),
1620                block_height,
1621                transitions,
1622                VerifyTxMode::Local(VerifyTxLocalMode::Full, &spec),
1623            ),
1624        )?;
1625
1626        let state_space = match tx.space() {
1627            Space::Native => None,
1628            Space::Ethereum => Some(Space::Ethereum),
1629        };
1630        let statedb = self.get_statedb_by_epoch_id_and_space(
1631            epoch_id,
1632            best_block_header.height(),
1633            state_space,
1634        )?;
1635        let mut state = if evm_overrides.has_state() {
1636            State::new_with_override(
1637                statedb,
1638                &evm_overrides.state.as_ref().unwrap(),
1639                tx.space(),
1640            )?
1641        } else {
1642            State::new(statedb)?
1643        };
1644
1645        let time_stamp = best_block_header.timestamp();
1646        let miner = best_block_header.author().clone();
1647
1648        let base_gas_price = best_block_header.base_price().unwrap_or_default();
1649        let burnt_gas_price =
1650            base_gas_price.map_all(|x| state.burnt_gas_price(x));
1651
1652        let mut env = Env {
1653            chain_id: self.machine.params().chain_id_map(block_height),
1654            number: start_block_number,
1655            author: miner,
1656            timestamp: time_stamp,
1657            difficulty: Default::default(),
1658            accumulated_gas_used: U256::zero(),
1659            last_hash: epoch_id.clone(),
1660            gas_limit: tx.gas().clone(),
1661            epoch_height: block_height,
1662            pos_view: pos_view_number,
1663            finalized_epoch: pivot_decision_epoch,
1664            transaction_epoch_bound: self
1665                .verification_config
1666                .transaction_epoch_bound,
1667            base_gas_price,
1668            burnt_gas_price,
1669            transaction_hash: tx.hash(),
1670            ..Default::default()
1671        };
1672        if evm_overrides.has_block() {
1673            ExecutiveContext::apply_env_overrides(
1674                &mut env,
1675                evm_overrides.block.unwrap(),
1676            );
1677        }
1678        let spec = self.machine.spec(env.number, env.epoch_height);
1679        let mut ex = EstimationContext::new(
1680            &mut state,
1681            &env,
1682            self.machine.as_ref(),
1683            &spec,
1684        );
1685
1686        let r = ex.transact_virtual(tx.clone(), request);
1687        trace!("Execution result {:?}", r);
1688        Ok(r?)
1689    }
1690
1691    /// Execute transactions in the blocks to collect traces.
1692    pub fn collect_blocks_geth_trace(
1693        &self, epoch_id: H256, epoch_num: u64, blocks: &Vec<Arc<Block>>,
1694        opts: GethDebugTracingOptions, tx_hash: Option<H256>,
1695    ) -> CoreResult<Vec<GethTraceWithHash>> {
1696        let state_space = None;
1697        let mut state = self.get_state_by_epoch_id_and_space(
1698            &epoch_id,
1699            epoch_num,
1700            state_space,
1701        )?;
1702
1703        let start_block_number = self
1704            .data_man
1705            .get_epoch_execution_context(&epoch_id)
1706            .map(|v| v.start_block_number)
1707            .expect("should exist");
1708
1709        let mut answer = vec![];
1710        let virtual_call = VirtualCall::GethTrace(GethTask {
1711            tx_hash,
1712            opts,
1713            answer: &mut answer,
1714        });
1715        self.process_epoch_transactions(
1716            &mut state,
1717            blocks,
1718            start_block_number,
1719            false,
1720            Some(virtual_call),
1721        )?;
1722
1723        Ok(answer)
1724    }
1725
1726    fn get_state_by_epoch_id_and_space(
1727        &self, epoch_id: &H256, epoch_height: u64, state_space: Option<Space>,
1728    ) -> DbResult<State> {
1729        let state_db = self.get_statedb_by_epoch_id_and_space(
1730            epoch_id,
1731            epoch_height,
1732            state_space,
1733        )?;
1734        let state = State::new(state_db)?;
1735
1736        Ok(state)
1737    }
1738
1739    fn get_statedb_by_epoch_id_and_space(
1740        &self, epoch_id: &H256, epoch_height: u64, state_space: Option<Space>,
1741    ) -> DbResult<StateDb> {
1742        // Keep the lock until we get the desired State, otherwise the State may
1743        // expire.
1744        let state_availability_boundary =
1745            self.data_man.state_availability_boundary.read();
1746
1747        if !state_availability_boundary.check_read_availability(
1748            epoch_height,
1749            epoch_id,
1750            state_space,
1751        ) {
1752            bail!("state is not ready");
1753        }
1754
1755        let state_index = self
1756            .data_man
1757            .get_state_readonly_index(epoch_id)
1758            .expect("state index should exist");
1759
1760        let state_db = StateDb::new(
1761            self.data_man
1762                .storage_manager
1763                .get_state_no_commit(
1764                    state_index,
1765                    /* try_open = */ true,
1766                    state_space,
1767                )?
1768                .ok_or("state deleted")?,
1769        );
1770
1771        drop(state_availability_boundary);
1772
1773        Ok(state_db)
1774    }
1775}
1776
1777pub struct ConsensusExecutionConfiguration {
1778    pub executive_trace: bool,
1779}