1use super::ConsensusExecutionHandler;
2use std::{collections::BTreeSet, convert::From, sync::Arc};
3
4use alloy_rpc_types_trace::geth::GethDebugTracingOptions;
5use cfx_parameters::genesis::GENESIS_ACCOUNT_ADDRESS;
6use geth_tracer::{GethTraceWithHash, GethTracer, TxExecContext};
7use pow_types::StakingEvent;
8
9use cfx_statedb::{Error as DbErrorKind, Result as DbResult};
10use cfx_types::{AddressSpaceUtil, Space, SpaceMap, H256, U256};
11use primitives::{
12 receipt::BlockReceipts, AccessListItem, Action, Block, BlockNumber,
13 Receipt, SignedTransaction, TransactionIndex,
14};
15
16use crate::{
17 block_data_manager::BlockDataManager,
18 consensus::consensus_inner::consensus_executor::GOOD_TPS_METER,
19};
20use cfx_execute_helper::{
21 exec_tracer::TransactionExecTraces,
22 observer::Observer,
23 tx_outcome::{make_process_tx_outcome, ProcessTxOutcome},
24};
25use cfx_executor::{
26 executive::{ExecutiveContext, TransactOptions, TransactSettings},
27 internal_contract::{
28 block_hash_slot, epoch_hash_slot, initialize_internal_contract_accounts,
29 },
30 state::{
31 initialize_cip107, initialize_cip137,
32 initialize_or_update_dao_voted_params, State,
33 },
34};
35use cfx_vm_types::Env;
36
37pub enum VirtualCall<'a> {
38 GethTrace(GethTask<'a>),
39}
40
41pub struct GethTask<'a> {
42 pub(super) tx_hash: Option<H256>,
43 pub(super) opts: GethDebugTracingOptions,
44 pub(super) answer: &'a mut Vec<GethTraceWithHash>,
45}
46
47impl ConsensusExecutionHandler {
48 pub(super) fn process_epoch_transactions<'a>(
49 &self, state: &mut State, epoch_blocks: &Vec<Arc<Block>>,
50 start_block_number: u64, on_local_pivot: bool,
51 virtual_call: Option<VirtualCall<'a>>,
52 ) -> DbResult<Vec<Arc<BlockReceipts>>> {
53 self.prefetch_storage_for_execution(state, epoch_blocks);
54
55 let pivot_block = epoch_blocks.last().expect("Epoch not empty");
56
57 let dry_run = virtual_call.is_some();
58
59 self.before_epoch_execution(state, &*pivot_block)?;
60
61 let base_gas_price =
62 pivot_block.block_header.base_price().unwrap_or_default();
63
64 let burnt_gas_price =
65 base_gas_price.map_all(|x| state.burnt_gas_price(x));
66 let context = EpochProcessContext {
67 on_local_pivot,
68 executive_trace: self.config.executive_trace,
69 dry_run,
70 virtual_call,
71 pivot_block,
72 base_gas_price,
73 burnt_gas_price,
74 };
75
76 let mut epoch_recorder = EpochProcessRecorder::new();
77
78 let mut block_context = BlockProcessContext::first_block(
79 &context,
80 epoch_blocks.first().unwrap(),
81 start_block_number,
82 );
83
84 for (idx, block) in epoch_blocks.iter().enumerate() {
85 if idx > 0 {
86 block_context.next_block(block);
87 }
88
89 self.process_block_transactions(
90 &block_context,
91 state,
92 &mut epoch_recorder,
93 )?;
94 }
95
96 if let Some(VirtualCall::GethTrace(task)) = context.virtual_call {
97 std::mem::swap(&mut epoch_recorder.geth_traces, task.answer);
98 }
99
100 if !dry_run
103 && on_local_pivot
104 && self.pos_verifier.pos_option().is_some()
105 {
106 debug!(
107 "put_staking_events: {:?} height={} len={}",
108 pivot_block.hash(),
109 pivot_block.block_header.height(),
110 epoch_recorder.staking_events.len()
111 );
112 self.pos_verifier
113 .consensus_db()
114 .put_staking_events(
115 pivot_block.block_header.height(),
116 pivot_block.hash(),
117 epoch_recorder.staking_events,
118 )
119 .map_err(|e| {
120 cfx_statedb::Error::from(DbErrorKind::PosDatabaseError(
121 format!("{:?}", e),
122 ))
123 })?;
124 }
125
126 if !dry_run && on_local_pivot {
127 self.tx_pool.recycle_transactions(epoch_recorder.repack_tx);
128 }
129
130 debug!("Finish processing tx for epoch");
131 Ok(epoch_recorder.receipts)
132 }
133
134 fn prefetch_storage_for_execution(
135 &self, state: &mut State, epoch_blocks: &Vec<Arc<Block>>,
136 ) {
137 let pool = if let Some(prefetcher) =
141 self.execution_state_prefetcher.as_ref()
142 {
143 prefetcher
144 } else {
145 return;
146 };
147
148 let mut accounts = BTreeSet::new();
149 for block in epoch_blocks.iter() {
150 for transaction in block.transactions.iter() {
151 let space = transaction.space();
152 accounts.insert(transaction.sender.with_space(space));
153 if let Action::Call(ref address) = transaction.action() {
154 accounts.insert(address.with_space(space));
155 }
156 if let Some(access_list) = transaction.access_list() {
157 for AccessListItem { address, .. } in access_list.iter() {
158 accounts.insert(address.with_space(space));
159 }
160 }
161 }
162 }
163 accounts.remove(&GENESIS_ACCOUNT_ADDRESS.with_native_space());
166 let res = state.prefetch_accounts(accounts, pool);
167 if let Err(e) = res {
168 warn!("Fail to prefetch account {:?}", e);
169 }
170 }
171
172 fn make_block_env(&self, block_context: &BlockProcessContext) -> Env {
173 let BlockProcessContext {
174 epoch_context:
175 &EpochProcessContext {
176 pivot_block,
177 base_gas_price,
178 burnt_gas_price,
179 ..
180 },
181 block,
182 block_number,
183 last_hash,
184 } = *block_context;
185
186 let last_block_header = &self.data_man.block_header_by_hash(&last_hash);
187
188 let pos_id = last_block_header
189 .as_ref()
190 .and_then(|header| header.pos_reference().as_ref());
191 let pos_view_number =
192 pos_id.and_then(|id| self.pos_verifier.get_pos_view(id));
193 let pivot_decision_epoch = pos_id
194 .and_then(|id| self.pos_verifier.get_pivot_decision(id))
195 .and_then(|hash| self.data_man.block_header_by_hash(&hash))
196 .map(|header| header.height());
197
198 let epoch_height = pivot_block.block_header.height();
199 let chain_id = self.machine.params().chain_id_map(epoch_height);
200 Env {
201 chain_id,
202 number: block_number,
203 author: block.block_header.author().clone(),
204 timestamp: pivot_block.block_header.timestamp(),
205 difficulty: block.block_header.difficulty().clone(),
206 accumulated_gas_used: U256::zero(),
207 last_hash,
208 gas_limit: U256::from(block.block_header.gas_limit()),
209 epoch_height,
210 pos_view: pos_view_number,
211 finalized_epoch: pivot_decision_epoch,
212 transaction_epoch_bound: self
213 .verification_config
214 .transaction_epoch_bound,
215 base_gas_price,
216 burnt_gas_price,
217 transaction_hash: H256::zero(),
220 ..Default::default()
221 }
222 }
223
224 fn process_block_transactions(
225 &self, block_context: &BlockProcessContext, state: &mut State,
226 epoch_recorder: &mut EpochProcessRecorder,
227 ) -> DbResult<()> {
228 let BlockProcessContext {
229 epoch_context: &EpochProcessContext { on_local_pivot, .. },
230 block,
231 block_number,
232 ..
233 } = *block_context;
234
235 debug!(
236 "process txs in block: hash={:?}, tx count={:?}",
237 block.hash(),
238 block.transactions.len()
239 );
240
241 let secondary_reward =
247 self.before_block_execution(state, block_number, block)?;
248
249 let mut env = self.make_block_env(block_context);
250
251 let mut block_recorder =
252 BlockProcessRecorder::new(epoch_recorder.evm_tx_idx);
253
254 for (idx, transaction) in block.transactions.iter().enumerate() {
255 self.process_transaction(
256 idx,
257 transaction,
258 block_context,
259 state,
260 &mut env,
261 on_local_pivot,
262 &mut block_recorder,
263 )?;
264 }
265
266 block_recorder.finish_block(
267 &self.data_man,
268 epoch_recorder,
269 block_context,
270 secondary_reward,
271 );
272
273 Ok(())
274 }
275
276 fn process_transaction(
277 &self, idx: usize, transaction: &Arc<SignedTransaction>,
278 block_context: &BlockProcessContext, state: &mut State, env: &mut Env,
279 on_local_pivot: bool, recorder: &mut BlockProcessRecorder,
280 ) -> DbResult<()> {
281 let rpc_index = recorder.tx_idx[transaction.space()];
282
283 let block = &block_context.block;
284 let dry_run = block_context.epoch_context.dry_run;
285
286 let machine = self.machine.as_ref();
287
288 let spec = machine.spec(env.number, env.epoch_height);
289
290 let options = TransactOptions {
291 observer: self.make_observer(transaction, block_context),
292 settings: TransactSettings::all_checks(),
293 };
294
295 env.transaction_hash = transaction.hash();
296 let execution_outcome =
297 ExecutiveContext::new(state, env, machine, &spec)
298 .transact(transaction, options)?;
299 state.update_state_post_tx_execution(!spec.cip645.fix_eip1153);
300 execution_outcome.log(transaction, &block_context.block.hash());
301
302 if let Some(burnt_fee) = execution_outcome
303 .try_as_executed()
304 .and_then(|e| e.burnt_fee)
305 {
306 state.burn_by_cip1559(burnt_fee);
307 };
308
309 let r = make_process_tx_outcome(
310 execution_outcome,
311 &mut env.accumulated_gas_used,
312 transaction.hash,
313 &spec,
314 );
315
316 if r.receipt.tx_success() {
317 GOOD_TPS_METER.mark(1);
318 }
319
320 let tx_skipped = r.receipt.tx_skipped();
321 let phantom_txs = r.phantom_txs.clone();
322
323 recorder.receive_tx_outcome(r, transaction, block_context);
324
325 if !on_local_pivot || tx_skipped || dry_run {
326 return Ok(());
328 }
329
330 let hash = transaction.hash();
331
332 self.data_man.insert_transaction_index(
333 &hash,
334 &TransactionIndex {
335 block_hash: block.hash(),
336 real_index: idx,
337 is_phantom: false,
338 rpc_index: Some(rpc_index),
339 },
340 );
341
342 let evm_chain_id = env.chain_id[&Space::Ethereum];
349 let evm_tx_index = &mut recorder.tx_idx[Space::Ethereum];
350
351 for ptx in phantom_txs {
352 self.data_man.insert_transaction_index(
353 &ptx.into_eip155(evm_chain_id).hash(),
354 &TransactionIndex {
355 block_hash: block.hash(),
356 real_index: idx,
357 is_phantom: true,
358 rpc_index: Some(*evm_tx_index),
359 },
360 );
361
362 *evm_tx_index += 1;
363 }
364
365 Ok(())
366 }
367
368 fn make_observer(
369 &self, transaction: &Arc<SignedTransaction>,
370 block_context: &BlockProcessContext,
371 ) -> Observer {
372 use alloy_rpc_types_trace::geth::{
373 GethDebugBuiltInTracerType::*, GethDebugTracerType::BuiltInTracer,
374 };
375
376 let mut observer = if self.config.executive_trace {
377 Observer::with_tracing()
378 } else {
379 Observer::with_no_tracing()
380 };
381
382 if let Some(VirtualCall::GethTrace(ref task)) =
383 block_context.epoch_context.virtual_call
384 {
385 let need_trace =
386 task.tx_hash.map_or(true, |hash| transaction.hash() == hash);
387 let support_tracer = matches!(
388 task.opts.tracer,
389 Some(BuiltInTracer(
390 FourByteTracer | CallTracer | PreStateTracer | NoopTracer
391 )) | None
392 );
393 let tx_gas_limit = transaction.gas_limit().as_u64();
394
395 if need_trace && support_tracer {
396 observer.geth_tracer = Some(GethTracer::new(
397 TxExecContext {
398 tx_gas_limit,
399 block_height: block_context
400 .epoch_context
401 .pivot_block
402 .block_header
403 .height(),
404 block_number: block_context.block_number,
405 },
406 Arc::clone(&self.machine),
407 task.opts.clone(),
408 ))
409 }
410 }
411 observer
412 }
413
414 fn before_epoch_execution(
415 &self, state: &mut State, pivot_block: &Block,
416 ) -> DbResult<()> {
417 let params = self.machine.params();
418
419 let epoch_number = pivot_block.block_header.height();
420 let hash = pivot_block.hash();
421 let parent_hash = pivot_block.block_header.parent_hash();
422
423 if epoch_number >= params.transition_heights.cip133e {
424 state.set_system_storage(
425 epoch_hash_slot(epoch_number).into(),
426 U256::from_big_endian(&hash.0),
427 )?;
428 }
429
430 if epoch_number >= params.transition_heights.eip2935 {
431 state.set_eip2935_storage(epoch_number - 1, *parent_hash)?;
432 }
433 Ok(())
434 }
435
436 pub fn before_block_execution(
437 &self, state: &mut State, block_number: BlockNumber, block: &Block,
438 ) -> DbResult<U256> {
439 let params = self.machine.params();
440 let transition_numbers = ¶ms.transition_numbers;
441
442 let cip94_start = transition_numbers.cip94n;
443 let period = params.params_dao_vote_period;
444 if block_number >= cip94_start
446 && (block_number - cip94_start) % period == 0
447 {
448 let set_pos_staking = block_number > transition_numbers.cip105;
449 initialize_or_update_dao_voted_params(state, set_pos_staking)?;
450 }
451
452 if block_number == transition_numbers.cip107 {
458 initialize_cip107(state)?;
459 }
460
461 if block_number >= transition_numbers.cip133b {
462 state.set_system_storage(
463 block_hash_slot(block_number).into(),
464 U256::from_big_endian(&block.hash().0),
465 )?;
466 }
467
468 if block_number == transition_numbers.cip137 {
469 initialize_cip137(state);
470 }
471
472 if block_number < transition_numbers.cip43a {
473 state.bump_block_number_accumulate_interest();
474 }
475
476 let secondary_reward = state.secondary_reward();
477
478 state.inc_distributable_pos_interest(block_number)?;
479
480 initialize_internal_contract_accounts(
481 state,
482 self.machine
483 .internal_contracts()
484 .initialized_at(block_number),
485 )?;
486
487 state.commit_cache(false);
488
489 Ok(secondary_reward)
490 }
491}
492
493struct EpochProcessContext<'a> {
494 on_local_pivot: bool,
495 executive_trace: bool,
496 virtual_call: Option<VirtualCall<'a>>,
497 dry_run: bool,
498
499 pivot_block: &'a Block,
500
501 base_gas_price: SpaceMap<U256>,
502 burnt_gas_price: SpaceMap<U256>,
503}
504
505struct BlockProcessContext<'a, 'b> {
506 epoch_context: &'b EpochProcessContext<'a>,
507 block: &'b Block,
508 block_number: u64,
509 last_hash: H256,
510}
511
512impl<'a, 'b> BlockProcessContext<'a, 'b> {
513 fn first_block(
514 epoch_context: &'b EpochProcessContext<'a>, block: &'b Block,
515 start_block_number: u64,
516 ) -> Self {
517 let EpochProcessContext { pivot_block, .. } = *epoch_context;
518 let last_hash = *pivot_block.block_header.parent_hash();
519 Self {
520 epoch_context,
521 block,
522 block_number: start_block_number,
523 last_hash,
524 }
525 }
526
527 fn next_block(&mut self, block: &'b Block) {
528 self.last_hash = self.block.hash();
529 self.block_number += 1;
530 self.block = block;
531 }
532}
533
534#[derive(Default)]
535struct EpochProcessRecorder {
536 receipts: Vec<Arc<BlockReceipts>>,
537 staking_events: Vec<StakingEvent>,
538 repack_tx: Vec<Arc<SignedTransaction>>,
539 geth_traces: Vec<GethTraceWithHash>,
540
541 evm_tx_idx: usize,
542}
543
544impl EpochProcessRecorder {
545 fn new() -> Self { Default::default() }
546}
547
548struct BlockProcessRecorder {
549 receipt: Vec<Receipt>,
550 tx_error_msg: Vec<String>,
551 traces: Vec<TransactionExecTraces>,
552 geth_traces: Vec<GethTraceWithHash>,
553 repack_tx: Vec<Arc<SignedTransaction>>,
554 staking_events: Vec<StakingEvent>,
555
556 tx_idx: SpaceMap<usize>,
557}
558
559impl BlockProcessRecorder {
560 fn new(evm_tx_idx: usize) -> BlockProcessRecorder {
561 let mut tx_idx = SpaceMap::default();
562 tx_idx[Space::Ethereum] = evm_tx_idx;
563 Self {
564 receipt: vec![],
565 tx_error_msg: vec![],
566 traces: vec![],
567 geth_traces: vec![],
568 repack_tx: vec![],
569 staking_events: vec![],
570 tx_idx,
571 }
572 }
573
574 fn receive_tx_outcome(
575 &mut self, r: ProcessTxOutcome, tx: &Arc<SignedTransaction>,
576 block_context: &BlockProcessContext,
577 ) {
578 let EpochProcessContext {
579 on_local_pivot,
580 executive_trace,
581 ..
582 } = *block_context.epoch_context;
583
584 if on_local_pivot && r.consider_repacked {
585 self.repack_tx.push(tx.clone())
586 }
587
588 let not_skipped = !r.receipt.tx_skipped();
589
590 if executive_trace {
591 self.traces.push(r.tx_traces.into());
592 }
593
594 self.receipt.push(r.receipt);
595 self.tx_error_msg.push(r.tx_exec_error_msg);
596 self.staking_events.extend(r.tx_staking_events);
597
598 if let Some(trace) = r.geth_trace {
599 self.geth_traces.push(GethTraceWithHash {
600 trace,
601 tx_hash: tx.hash(),
602 space: tx.space(),
603 });
604 }
605
606 match tx.space() {
607 Space::Native => {
608 self.tx_idx[Space::Native] += 1;
609 }
610 Space::Ethereum if not_skipped => {
611 self.tx_idx[Space::Ethereum] += 1;
612 }
613 _ => {}
614 };
615 }
616
617 fn finish_block(
618 self, data_man: &BlockDataManager,
619 epoch_recorder: &mut EpochProcessRecorder,
620 block_context: &BlockProcessContext, secondary_reward: U256,
621 ) {
622 let BlockProcessContext {
623 epoch_context:
624 &EpochProcessContext {
625 on_local_pivot,
626 executive_trace,
627 pivot_block,
628 dry_run,
629 ..
630 },
631 block,
632 block_number,
633 ..
634 } = *block_context;
635
636 let block_receipts = Arc::new(BlockReceipts {
637 receipts: self.receipt,
638 block_number: block_number + 1,
641 secondary_reward,
642 tx_execution_error_messages: self.tx_error_msg,
643 });
644
645 epoch_recorder.receipts.push(block_receipts.clone());
646 epoch_recorder.staking_events.extend(self.staking_events);
647 epoch_recorder.repack_tx.extend(self.repack_tx);
648 epoch_recorder.geth_traces.extend(self.geth_traces);
649
650 epoch_recorder.evm_tx_idx = self.tx_idx[Space::Ethereum];
651
652 if dry_run {
653 return;
654 }
655
656 if executive_trace {
657 data_man.insert_block_traces(
658 block.hash(),
659 self.traces.into(),
660 pivot_block.hash(),
661 on_local_pivot,
662 );
663 }
664
665 data_man.insert_block_execution_result(
666 block.hash(),
667 pivot_block.hash(),
668 block_receipts.clone(),
669 on_local_pivot,
670 );
671 }
672}