cfxcore/consensus/consensus_graph/
mining_api.rs

1use super::ConsensusGraph;
2
3use crate::consensus::consensus_inner::{ConsensusGraphInner, StateBlameInfo};
4
5use cfx_parameters::consensus::*;
6use cfx_types::{H256, U256};
7use primitives::pos::PosBlockId;
8
9use std::{thread::sleep, time::Duration};
10
11impl ConsensusGraph {
12    /// Determine whether the next mined block should have adaptive weight or
13    /// not
14    pub fn check_mining_adaptive_block(
15        &self, inner: &mut ConsensusGraphInner, parent_hash: &H256,
16        referees: &Vec<H256>, difficulty: &U256,
17        pos_reference: Option<PosBlockId>,
18    ) -> bool {
19        let parent_index =
20            *inner.hash_to_arena_indices.get(parent_hash).expect(
21                "parent_hash is the pivot chain tip,\
22                 so should still exist in ConsensusInner",
23            );
24        let referee_indices: Vec<_> = referees
25            .iter()
26            .map(|h| {
27                *inner
28                    .hash_to_arena_indices
29                    .get(h)
30                    .expect("Checked by the caller")
31            })
32            .collect();
33        inner.check_mining_adaptive_block(
34            parent_index,
35            referee_indices,
36            *difficulty,
37            pos_reference,
38        )
39    }
40
41    /// Wait for the generation and the execution completion of a block in the
42    /// consensus graph. This API is used mainly for testing purpose
43    pub fn wait_for_generation(&self, hash: &H256) {
44        while !self
45            .inner
46            .read_recursive()
47            .hash_to_arena_indices
48            .contains_key(hash)
49        {
50            sleep(Duration::from_millis(1));
51        }
52        let best_state_block =
53            self.inner.read_recursive().best_state_block_hash();
54        match self.executor.wait_for_result(best_state_block) {
55            Ok(_) => (),
56            Err(msg) => warn!("wait_for_generation() gets the following error from the ConsensusExecutor: {}", msg)
57        }
58        // Ensure that `best_info` has been updated when this returns, so if we
59        // are calling RPCs to generate many blocks, they will form a
60        // strict chain. Note that it's okay to call `update_best_info`
61        // multiple times, and we only generate blocks after
62        // `ready_for_mining` is true.
63        self.update_best_info(true);
64        if let Err(e) = self
65            .txpool
66            .notify_new_best_info(self.best_info.read_recursive().clone())
67        {
68            error!("wait for generation: notify_new_best_info err={:?}", e);
69        }
70    }
71
72    /// After considering the latest `pos_reference`, `parent_hash` may become
73    /// an invalid choice, so this function tries to update the parent and
74    /// referee choices with `pos_reference` provided.
75    pub fn choose_correct_parent(
76        &self, parent_hash: &mut H256, referees: &mut Vec<H256>,
77        blame_info: &mut StateBlameInfo, pos_reference: Option<PosBlockId>,
78    ) {
79        let correct_parent_hash = {
80            if let Some(pos_ref) = &pos_reference {
81                loop {
82                    let inner = self.inner.read();
83                    let pivot_decision = inner
84                        .pos_verifier
85                        .get_pivot_decision(pos_ref)
86                        .expect("pos ref committed");
87                    if inner.hash_to_arena_indices.contains_key(&pivot_decision)
88                        || inner.pivot_block_processed(&pivot_decision)
89                    {
90                        // If this pos ref is processed in catching-up, its
91                        // pivot decision may have not been processed
92                        break;
93                    } else {
94                        // Wait without holding consensus inner lock.
95                        drop(inner);
96                        warn!("Wait for PoW to catch up with PoS");
97                        sleep(Duration::from_secs(1));
98                    }
99                }
100            }
101            // recompute `blame_info` needs locking `self.inner`, so we limit
102            // the lock scope here.
103            let mut inner = self.inner.write();
104            referees.retain(|h| inner.hash_to_arena_indices.contains_key(h));
105            let parent_index =
106                *inner.hash_to_arena_indices.get(parent_hash).expect(
107                    "parent_hash is the pivot chain tip,\
108                     so should still exist in ConsensusInner",
109                );
110            let referee_indices: Vec<_> = referees
111                .iter()
112                .map(|h| {
113                    *inner
114                        .hash_to_arena_indices
115                        .get(h)
116                        .expect("Checked by the caller")
117                })
118                .collect();
119            let correct_parent = inner.choose_correct_parent(
120                parent_index,
121                referee_indices,
122                pos_reference,
123            );
124            inner.arena[correct_parent].hash
125        };
126
127        if correct_parent_hash != *parent_hash {
128            debug!(
129                "Change parent from {:?} to {:?}",
130                parent_hash, correct_parent_hash
131            );
132
133            // correct_parent may be among referees, so check and remove it.
134            referees.retain(|i| *i != correct_parent_hash);
135
136            // Old parent is a valid block terminal to refer to.
137            if referees.len() < self.config.referee_bound {
138                referees.push(*parent_hash);
139            }
140
141            // correct_parent may not be on the pivot chain, so recompute
142            // blame_info if needed.
143            *blame_info = self
144                .force_compute_blame_and_deferred_state_for_generation(
145                    &correct_parent_hash,
146                )
147                .expect("blame info computation error");
148            *parent_hash = correct_parent_hash;
149        }
150    }
151
152    /// Force the engine to recompute the deferred state root for a particular
153    /// block given a delay.
154    pub fn force_compute_blame_and_deferred_state_for_generation(
155        &self, parent_block_hash: &H256,
156    ) -> Result<StateBlameInfo, String> {
157        {
158            let inner = &mut *self.inner.write();
159            let hash = inner
160                .get_state_block_with_delay(
161                    parent_block_hash,
162                    DEFERRED_STATE_EPOCH_COUNT as usize - 1,
163                )?
164                .clone();
165            self.executor.compute_state_for_block(&hash, inner)?;
166        }
167        self.executor.get_blame_and_deferred_state_for_generation(
168            parent_block_hash,
169            &self.inner,
170        )
171    }
172
173    pub fn get_blame_and_deferred_state_for_generation(
174        &self, parent_block_hash: &H256,
175    ) -> Result<StateBlameInfo, String> {
176        self.executor.get_blame_and_deferred_state_for_generation(
177            parent_block_hash,
178            &self.inner,
179        )
180    }
181}