cfxcore/consensus/
debug_recompute.rs

1// Copyright 2020 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
5pub fn log_debug_epoch_computation(
6    epoch_arena_index: usize, inner: &mut ConsensusGraphInner,
7    executor: &ConsensusExecutor, block_hash: H256, block_height: u64,
8    state_root: &StateRootWithAuxInfo,
9) -> ComputeEpochDebugRecord {
10    // Parent state root.
11    let parent_arena_index = inner.arena[epoch_arena_index].parent;
12    let parent_epoch_hash = inner.arena[parent_arena_index].hash;
13    let parent_state_root = inner
14        .data_man
15        .get_epoch_execution_commitment(&parent_epoch_hash)
16        .unwrap()
17        .state_root_with_aux_info
18        .clone();
19
20    let reward_index = inner.get_pivot_reward_index(epoch_arena_index);
21
22    let reward_execution_info =
23        executor.get_reward_execution_info_from_index(inner, reward_index);
24    let task = EpochExecutionTask::new(
25        epoch_arena_index,
26        inner,
27        reward_execution_info,
28        false, /* on_local_pivot */
29        false, /* force_recompute */
30    );
31    let mut debug_record = ComputeEpochDebugRecord::default();
32    {
33        debug_record.block_height = block_height;
34        debug_record.block_hash = block_hash;
35        debug_record.state_root_after_applying_rewards = state_root.clone();
36        debug_record.parent_epoch_hash = parent_epoch_hash;
37        debug_record.parent_state_root = parent_state_root;
38        debug_record.reward_epoch_hash =
39            if let Some((reward_epoch_block, _)) = reward_index.clone() {
40                Some(inner.arena[reward_epoch_block].hash)
41            } else {
42                None
43            };
44        debug_record.anticone_penalty_cutoff_epoch_hash =
45            if let Some((_, anticone_penalty_cutoff_epoch_block)) =
46                reward_index.clone()
47            {
48                Some(inner.arena[anticone_penalty_cutoff_epoch_block].hash)
49            } else {
50                None
51            };
52
53        let epoch_block_hashes =
54            inner.get_epoch_block_hashes(epoch_arena_index);
55        let blocks = epoch_block_hashes
56            .iter()
57            .map(|hash| {
58                inner
59                    .data_man
60                    .block_by_hash(hash, false /* update_cache */)
61                    .unwrap()
62            })
63            .collect::<Vec<_>>();
64
65        debug_record.block_hashes = epoch_block_hashes;
66        debug_record.block_txs = blocks
67            .iter()
68            .map(|block| block.transactions.len())
69            .collect::<Vec<_>>();
70        debug_record.transactions = blocks
71            .iter()
72            .flat_map(|block| block.transactions.clone())
73            .collect::<Vec<_>>();
74
75        debug_record.block_authors = blocks
76            .iter()
77            .map(|block| *block.block_header.author())
78            .collect::<Vec<_>>();
79    }
80    executor.compute_epoch(task, Some(&mut debug_record), false);
81
82    debug_record
83}
84
85pub fn log_invalid_state_root(
86    deferred: usize, inner: &mut ConsensusGraphInner,
87    executor: &ConsensusExecutor, block_hash: H256, block_height: u64,
88    state_root: &StateRootWithAuxInfo,
89) -> std::io::Result<()> {
90    if let Some(dump_dir) =
91        inner.inner_conf.debug_dump_dir_invalid_state_root.clone()
92    {
93        let invalid_state_root_path =
94            dump_dir.clone() + &format!("{}_{:?}", block_height, block_hash);
95        let txt_path = invalid_state_root_path.clone() + ".txt";
96        if Path::new(&txt_path).exists() {
97            return Ok(());
98        }
99
100        // TODO: refactor the consensus executor to make it run at background.
101        let debug_record = log_debug_epoch_computation(
102            deferred,
103            inner,
104            executor,
105            block_hash,
106            block_height,
107            state_root,
108        );
109        let deferred_block_hash = inner.arena[deferred].hash;
110        let got_state_root = inner
111            .data_man
112            .get_epoch_execution_commitment(&deferred_block_hash)
113            .unwrap()
114            .state_root_with_aux_info
115            .clone();
116
117        {
118            std::fs::create_dir_all(dump_dir)?;
119
120            let mut debug_file = File::create(&txt_path)?;
121            debug_file.write_all(format!("{:?}", debug_record).as_bytes())?;
122            let json_path = invalid_state_root_path + ".json.txt";
123            let mut json_file = File::create(&json_path)?;
124            json_file
125                .write_all(serde_json::to_string(&debug_record)?.as_bytes())?;
126        }
127
128        warn!(
129            "State debug recompute: got {:?}, deferred block: {:?}, block hash: {:?}\
130            reward epoch bock: {:?}, anticone cutoff block: {:?}, \
131            number of blocks in epoch: {:?}, number of transactions in epoch: {:?}, rewards: {:?}",
132            got_state_root,
133            deferred_block_hash,
134            block_hash,
135            debug_record.reward_epoch_hash,
136            debug_record.anticone_penalty_cutoff_epoch_hash,
137            debug_record.block_hashes.len(),
138            debug_record.transactions.len(),
139            debug_record.merged_rewards_by_author,
140        );
141    }
142
143    Ok(())
144}
145
146use crate::consensus::{
147    consensus_inner::consensus_executor::{
148        ConsensusExecutor, EpochExecutionTask,
149    },
150    ConsensusGraphInner,
151};
152use cfx_internal_common::{
153    debug::ComputeEpochDebugRecord, StateRootWithAuxInfo,
154};
155use cfx_types::H256;
156use serde_json;
157use std::{fs::File, io::Write, path::Path};