1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// Copyright 2020 Conflux Foundation. All rights reserved.
// Conflux is free software and distributed under GNU General Public License.
// See http://www.gnu.org/licenses/

pub fn log_debug_epoch_computation(
    epoch_arena_index: usize, inner: &mut ConsensusGraphInner,
    executor: &ConsensusExecutor, block_hash: H256, block_height: u64,
    state_root: &StateRootWithAuxInfo,
) -> ComputeEpochDebugRecord {
    // Parent state root.
    let parent_arena_index = inner.arena[epoch_arena_index].parent;
    let parent_epoch_hash = inner.arena[parent_arena_index].hash;
    let parent_state_root = inner
        .data_man
        .get_epoch_execution_commitment(&parent_epoch_hash)
        .unwrap()
        .state_root_with_aux_info
        .clone();

    let reward_index = inner.get_pivot_reward_index(epoch_arena_index);

    let reward_execution_info =
        executor.get_reward_execution_info_from_index(inner, reward_index);
    let task = EpochExecutionTask::new(
        epoch_arena_index,
        inner,
        reward_execution_info,
        false, /* on_local_pivot */
        false, /* force_recompute */
    );
    let mut debug_record = ComputeEpochDebugRecord::default();
    {
        debug_record.block_height = block_height;
        debug_record.block_hash = block_hash;
        debug_record.state_root_after_applying_rewards = state_root.clone();
        debug_record.parent_epoch_hash = parent_epoch_hash;
        debug_record.parent_state_root = parent_state_root;
        debug_record.reward_epoch_hash =
            if let Some((reward_epoch_block, _)) = reward_index.clone() {
                Some(inner.arena[reward_epoch_block].hash)
            } else {
                None
            };
        debug_record.anticone_penalty_cutoff_epoch_hash =
            if let Some((_, anticone_penalty_cutoff_epoch_block)) =
                reward_index.clone()
            {
                Some(inner.arena[anticone_penalty_cutoff_epoch_block].hash)
            } else {
                None
            };

        let epoch_block_hashes =
            inner.get_epoch_block_hashes(epoch_arena_index);
        let blocks = epoch_block_hashes
            .iter()
            .map(|hash| {
                inner
                    .data_man
                    .block_by_hash(hash, false /* update_cache */)
                    .unwrap()
            })
            .collect::<Vec<_>>();

        debug_record.block_hashes = epoch_block_hashes;
        debug_record.block_txs = blocks
            .iter()
            .map(|block| block.transactions.len())
            .collect::<Vec<_>>();
        debug_record.transactions = blocks
            .iter()
            .flat_map(|block| block.transactions.clone())
            .collect::<Vec<_>>();

        debug_record.block_authors = blocks
            .iter()
            .map(|block| *block.block_header.author())
            .collect::<Vec<_>>();
    }
    executor.compute_epoch(task, Some(&mut debug_record), false);

    debug_record
}

pub fn log_invalid_state_root(
    deferred: usize, inner: &mut ConsensusGraphInner,
    executor: &ConsensusExecutor, block_hash: H256, block_height: u64,
    state_root: &StateRootWithAuxInfo,
) -> std::io::Result<()> {
    if let Some(dump_dir) =
        inner.inner_conf.debug_dump_dir_invalid_state_root.clone()
    {
        let invalid_state_root_path =
            dump_dir.clone() + &format!("{}_{:?}", block_height, block_hash);
        let txt_path = invalid_state_root_path.clone() + ".txt";
        if Path::new(&txt_path).exists() {
            return Ok(());
        }

        // TODO: refactor the consensus executor to make it run at background.
        let debug_record = log_debug_epoch_computation(
            deferred,
            inner,
            executor,
            block_hash,
            block_height,
            state_root,
        );
        let deferred_block_hash = inner.arena[deferred].hash;
        let got_state_root = inner
            .data_man
            .get_epoch_execution_commitment(&deferred_block_hash)
            .unwrap()
            .state_root_with_aux_info
            .clone();

        {
            std::fs::create_dir_all(dump_dir)?;

            let mut debug_file = File::create(&txt_path)?;
            debug_file.write_all(format!("{:?}", debug_record).as_bytes())?;
            let json_path = invalid_state_root_path + ".json.txt";
            let mut json_file = File::create(&json_path)?;
            json_file
                .write_all(serde_json::to_string(&debug_record)?.as_bytes())?;
        }

        warn!(
            "State debug recompute: got {:?}, deferred block: {:?}, block hash: {:?}\
            reward epoch bock: {:?}, anticone cutoff block: {:?}, \
            number of blocks in epoch: {:?}, number of transactions in epoch: {:?}, rewards: {:?}",
            got_state_root,
            deferred_block_hash,
            block_hash,
            debug_record.reward_epoch_hash,
            debug_record.anticone_penalty_cutoff_epoch_hash,
            debug_record.block_hashes.len(),
            debug_record.transactions.len(),
            debug_record.merged_rewards_by_author,
        );
    }

    Ok(())
}

use crate::consensus::{
    consensus_inner::consensus_executor::{
        ConsensusExecutor, EpochExecutionTask,
    },
    ConsensusGraphInner,
};
use cfx_internal_common::{
    debug::ComputeEpochDebugRecord, StateRootWithAuxInfo,
};
use cfx_types::H256;
use serde_json;
use std::{fs::File, io::Write, path::Path};