1mod 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
91pub 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,
126}
127
128#[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 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#[derive(Debug)]
164struct GetExecutionResultTask {
165 pub epoch_hash: H256,
166 pub sender: Sender<Option<EpochExecutionCommitment>>,
167}
168
169pub struct ConsensusExecutor {
171 thread: Mutex<Option<JoinHandle<()>>>,
173
174 sender: Mutex<Sender<ExecutionTask>>,
176
177 stopped: AtomicBool,
179
180 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 let handle = thread::Builder::new()
218 .name("Consensus Execution Worker".into())
219 .spawn(move || loop {
220 if executor_thread.stopped.load(Relaxed) {
221 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 match receiver.try_recv() {
242 Ok(task) => Some(task),
243 Err(TryRecvError::Empty) => {
244 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 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 break;
276 }
277 })
278 .expect("Cannot fail");
279 *executor.thread.lock() = Some(handle);
280 executor
281 }
282
283 pub fn wait_for_result(
293 &self, epoch_hash: H256,
294 ) -> Result<EpochExecutionCommitment, String> {
295 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 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 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 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, false, );
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 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 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 if anticone_difficulty / U512::from(epoch_difficulty)
461 >= U512::from(self.handler.machine.params().anticone_penalty_ratio)
462 {
463 no_reward = true;
464 }
465 }
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 fn wait_and_compute_state_valid_and_blame_info(
493 &self, me: usize, inner_lock: &RwLock<ConsensusGraphInner>,
494 ) -> Result<(), String> {
495 let waiting_blocks = inner_lock
501 .read()
502 .collect_defer_blocks_missing_execution_commitments(me)?;
503 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 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 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 state_block_hash in waiting_blocks {
542 self.wait_for_result(state_block_hash)?;
543 }
544 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 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 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 self.stopped.store(true, Relaxed);
670
671 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 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 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 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 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 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 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 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 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 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, false, ));
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, false, ));
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 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 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 .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 .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 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 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 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 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 self.safety_guard(pivot_block_header.as_ref());
1028 }
1029
1030 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 let epoch_blocks = self
1051 .data_man
1052 .blocks_by_hash_list(
1053 epoch_block_hashes,
1054 true, )
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 None,
1076 )
1077 .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 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 let state_root = &self
1140 .data_man
1141 .get_epoch_execution_commitment(epoch_hash)
1142 .unwrap()
1143 .state_root_with_aux_info;
1144 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 .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 let maybe_parent_pos_ref = self
1175 .data_man
1176 .block_header_by_hash(&pivot_header.parent_hash()) .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 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 let accounts_for_txpool = commit_result.accounts_for_txpool.clone();
1227 {
1228 debug!("Notify epoch[{}]", epoch_hash);
1229
1230 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 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 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 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 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 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 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 }
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 let mut tx_fee = HashMap::new();
1370
1371 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 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, true, ) {
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 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 .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 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 debug_assert!(
1431 fee.is_zero() || info.0.is_zero() || info.1.len() == 0
1432 );
1433 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 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 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 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 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 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 state.add_total_issued(new_mint - burnt_fee);
1563 } else {
1564 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 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 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 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 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}