cfxcore/consensus/consensus_graph/
onchain_blocks_provider.rs

1use super::ConsensusGraph;
2
3use crate::errors::{invalid_params, Result as CoreResult};
4use cfxcore_errors::ProviderBlockError;
5
6use cfx_parameters::consensus::*;
7
8use cfx_types::H256;
9
10use primitives::{compute_block_number, BlockHashOrEpochNumber, EpochNumber};
11use std::cmp::min;
12
13impl ConsensusGraph {
14    /// Returns the total number of blocks processed in consensus graph.
15    ///
16    /// This function should only be used in tests.
17    /// If the process crashes and recovered, the blocks in the anticone of the
18    /// current checkpoint may not be counted since they will not be
19    /// inserted into consensus in the recover process.
20    pub fn block_count(&self) -> u64 {
21        self.inner.read_recursive().total_processed_block_count()
22    }
23
24    /// Convert EpochNumber to height based on the current ConsensusGraph
25    pub fn get_height_from_epoch_number(
26        &self, epoch_number: EpochNumber,
27    ) -> Result<u64, ProviderBlockError> {
28        Ok(match epoch_number {
29            EpochNumber::Earliest => 0,
30            EpochNumber::LatestCheckpoint => {
31                self.latest_checkpoint_epoch_number()
32            }
33            EpochNumber::LatestConfirmed => {
34                self.latest_confirmed_epoch_number()
35            }
36            EpochNumber::LatestMined => self.best_epoch_number(),
37            EpochNumber::LatestFinalized => {
38                self.latest_finalized_epoch_number()
39            }
40            EpochNumber::LatestState => self.best_executed_state_epoch_number(),
41            EpochNumber::Number(num) => {
42                let epoch_num = num;
43                if epoch_num > self.inner.read_recursive().best_epoch_number() {
44                    return Err(ProviderBlockError::EpochNumberTooLarge);
45                }
46                epoch_num
47            }
48        })
49    }
50
51    pub fn get_block_epoch_number(&self, hash: &H256) -> Option<u64> {
52        // try to get from memory
53        if let Some(e) =
54            self.inner.read_recursive().get_block_epoch_number(hash)
55        {
56            return Some(e);
57        }
58
59        // try to get from db
60        self.data_man.block_epoch_number(hash)
61    }
62
63    pub fn get_block_hashes_by_epoch(
64        &self, epoch_number: EpochNumber,
65    ) -> Result<Vec<H256>, ProviderBlockError> {
66        self.get_height_from_epoch_number(epoch_number)
67            .and_then(|height| {
68                self.inner.read_recursive().block_hashes_by_epoch(height)
69            })
70    }
71
72    pub fn get_block_hashes_by_epoch_or_block_hash(
73        &self, block_hash_or_epoch: BlockHashOrEpochNumber,
74    ) -> Result<Vec<H256>, ProviderBlockError> {
75        let hashes = match block_hash_or_epoch {
76            BlockHashOrEpochNumber::EpochNumber(e) => {
77                self.get_block_hashes_by_epoch(e)?
78            }
79            BlockHashOrEpochNumber::BlockHashWithOption {
80                hash: h,
81                require_pivot,
82            } => {
83                // verify the block header exists
84                let _ = self
85                    .data_manager()
86                    .block_header_by_hash(&h)
87                    .ok_or("block not found")?;
88
89                let e =
90                    self.get_block_epoch_number(&h).ok_or("block not found")?;
91
92                let hashes = self.get_block_hashes_by_epoch(e.into())?;
93
94                // if the provided hash is not the pivot hash,
95                // and require_pivot is true or None(default to true)
96                // abort
97                let pivot_hash = *hashes.last().ok_or("inconsistent state")?;
98
99                if require_pivot.unwrap_or(true) && (h != pivot_hash) {
100                    bail!(ProviderBlockError::Common(
101                        "require_pivot check failed".into()
102                    ));
103                }
104
105                hashes
106            }
107        };
108        Ok(hashes)
109    }
110
111    /// Get the pivot block hash of the specified epoch number
112    pub fn get_hash_from_epoch_number(
113        &self, epoch_number: EpochNumber,
114    ) -> Result<H256, ProviderBlockError> {
115        self.get_height_from_epoch_number(epoch_number)
116            .and_then(|height| {
117                self.inner
118                    .read_recursive()
119                    .get_pivot_hash_from_epoch_number(height)
120            })
121    }
122
123    pub fn get_skipped_block_hashes_by_epoch(
124        &self, epoch_number: EpochNumber,
125    ) -> Result<Vec<H256>, ProviderBlockError> {
126        self.get_height_from_epoch_number(epoch_number)
127            .and_then(|height| {
128                self.inner
129                    .read_recursive()
130                    .skipped_block_hashes_by_epoch(height)
131            })
132    }
133
134    pub fn get_block_epoch_number_with_pivot_check(
135        &self, hash: &H256, require_pivot: bool,
136    ) -> CoreResult<u64> {
137        let inner = &*self.inner.read();
138        // TODO: block not found error
139        let epoch_number =
140            inner.get_block_epoch_number(&hash).ok_or(invalid_params(
141                "epoch parameter",
142                format!("block's epoch number is not found: {:?}", hash),
143            ))?;
144
145        if require_pivot {
146            if let Err(..) =
147                inner.check_block_pivot_assumption(&hash, epoch_number)
148            {
149                bail!(invalid_params(
150                    "epoch parameter",
151                    format!(
152                        "should receive a pivot block hash, receives: {:?}",
153                        hash
154                    ),
155                ))
156            }
157        }
158        Ok(epoch_number)
159    }
160
161    pub fn get_block_number(
162        &self, block_hash: &H256,
163    ) -> Result<Option<u64>, String> {
164        let inner = self.inner.read_recursive();
165
166        let epoch_number = match inner
167            .get_block_epoch_number(block_hash)
168            .or_else(|| self.data_man.block_epoch_number(&block_hash))
169        {
170            None => return Ok(None),
171            Some(epoch_number) => epoch_number,
172        };
173
174        let blocks = match self
175            .get_block_hashes_by_epoch(EpochNumber::Number(epoch_number))
176            .ok()
177            .or_else(|| {
178                self.data_man
179                    .executed_epoch_set_hashes_from_db(epoch_number)
180            }) {
181            None => return Ok(None),
182            Some(hashes) => hashes,
183        };
184
185        let epoch_hash = blocks.last().expect("Epoch not empty");
186
187        let start_block_number =
188            match self.data_man.get_epoch_execution_context(&epoch_hash) {
189                None => return Ok(None),
190                Some(ctx) => ctx.start_block_number,
191            };
192
193        let index_of_block = match blocks.iter().position(|x| x == block_hash) {
194            None => return Ok(None),
195            Some(index) => index as u64,
196        };
197
198        return Ok(Some(compute_block_number(
199            start_block_number,
200            index_of_block,
201        )));
202    }
203
204    pub fn validate_stated_epoch(
205        &self, epoch_number: &EpochNumber,
206    ) -> Result<(), String> {
207        match epoch_number {
208            EpochNumber::LatestMined => {
209                return Err("Latest mined epoch is not executed".into());
210            }
211            EpochNumber::Number(num) => {
212                let latest_state_epoch =
213                    self.best_executed_state_epoch_number();
214                if *num > latest_state_epoch {
215                    return Err(format!("Specified epoch {} is not executed, the latest state epoch is {}", num, latest_state_epoch));
216                }
217            }
218            _ => {}
219        }
220
221        Ok(())
222    }
223
224    /// Returns the latest epoch whose state can be exposed safely, which means
225    /// its state is available and it's not only visible to optimistic
226    /// execution.
227    pub fn best_executed_state_epoch_number(&self) -> u64 {
228        let state_upper_bound =
229            self.data_man.state_availability_boundary.read().upper_bound;
230        // Here we can also get `best_state_epoch` from `inner`, but that
231        // would acquire the inner read lock.
232        let best_epoch_number = self.best_info.read().best_epoch_number;
233        let deferred_state_height =
234            if best_epoch_number < DEFERRED_STATE_EPOCH_COUNT {
235                0
236            } else {
237                best_epoch_number - DEFERRED_STATE_EPOCH_COUNT + 1
238            };
239        // state upper bound can be lower than deferred_state_height because
240        // the execution is async. It can also be higher
241        // because of optimistic execution. Here we guarantee
242        // to return an available state without exposing optimistically
243        // executed states.
244        min(state_upper_bound, deferred_state_height)
245    }
246}