/__w/smoldot/smoldot/repo/lib/src/chain/blocks_tree/verify.rs
Line | Count | Source |
1 | | // Smoldot |
2 | | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. |
3 | | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 |
4 | | |
5 | | // This program is free software: you can redistribute it and/or modify |
6 | | // it under the terms of the GNU General Public License as published by |
7 | | // the Free Software Foundation, either version 3 of the License, or |
8 | | // (at your option) any later version. |
9 | | |
10 | | // This program is distributed in the hope that it will be useful, |
11 | | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | | // GNU General Public License for more details. |
14 | | |
15 | | // You should have received a copy of the GNU General Public License |
16 | | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
17 | | |
18 | | //! Extension module containing the API and implementation of everything related to verifying |
19 | | //! blocks. |
20 | | |
21 | | use crate::{chain::chain_information, header, verify}; |
22 | | |
23 | | use super::{ |
24 | | Arc, BestScore, Block, BlockConsensus, BlockFinality, Duration, Finality, FinalizedConsensus, |
25 | | NonFinalizedTree, Vec, fmt, |
26 | | }; |
27 | | |
28 | | impl<T> NonFinalizedTree<T> { |
29 | | /// Verifies the given block header. |
30 | | /// |
31 | | /// The verification is performed in the context of the chain. In particular, the |
32 | | /// verification will fail if the parent block isn't already in the chain. |
33 | | /// |
34 | | /// If the verification succeeds, an [`VerifiedHeader`] object might be returned which can be |
35 | | /// used to then insert the block in the chain using |
36 | | /// [`NonFinalizedTree::insert_verified_header`]. |
37 | | /// |
38 | | /// Must be passed the current UNIX time in order to verify that the block doesn't pretend to |
39 | | /// come from the future. |
40 | 4 | pub fn verify_header( |
41 | 4 | &self, |
42 | 4 | scale_encoded_header: Vec<u8>, |
43 | 4 | now_from_unix_epoch: Duration, |
44 | 4 | ) -> Result<HeaderVerifySuccess, HeaderVerifyError> { |
45 | 4 | let decoded_header = match header::decode(&scale_encoded_header, self.block_number_bytes) { |
46 | 4 | Ok(h) => h, |
47 | 0 | Err(err) => return Err(HeaderVerifyError::InvalidHeader(err)), |
48 | | }; |
49 | | |
50 | 4 | let hash = header::hash_from_scale_encoded_header(&scale_encoded_header); |
51 | | |
52 | | // Check for duplicates. |
53 | 4 | if self.blocks_by_hash.contains_key(&hash) { |
54 | 0 | return Ok(HeaderVerifySuccess::Duplicate); |
55 | 4 | } |
56 | | |
57 | | // Try to find the parent block in the tree of known blocks. |
58 | | // `Some` with an index of the parent within the tree of unfinalized blocks. |
59 | | // `None` means that the parent is the finalized block. |
60 | 4 | let parent_tree_index = { |
61 | 4 | if *decoded_header.parent_hash == self.finalized_block_hash { |
62 | 2 | None |
63 | | } else { |
64 | 2 | match self.blocks_by_hash.get(decoded_header.parent_hash) { |
65 | 2 | Some(parent) => Some(*parent), |
66 | | None => { |
67 | 0 | let parent_hash = *decoded_header.parent_hash; |
68 | 0 | return Err(HeaderVerifyError::BadParent { parent_hash }); |
69 | | } |
70 | | } |
71 | | } |
72 | | }; |
73 | | |
74 | | // Some consensus-specific information must be fetched from the tree of ancestry. The |
75 | | // information is found either in the parent block, or in the finalized block. |
76 | 4 | let (parent_consensus, parent_best_score, parent_finality) = |
77 | 4 | if let Some(parent_tree_index2 ) = parent_tree_index { |
78 | 2 | let parent = self.blocks.get(parent_tree_index).unwrap(); |
79 | 2 | ( |
80 | 2 | Some(parent.consensus.clone()), |
81 | 2 | parent.best_score, |
82 | 2 | parent.finality.clone(), |
83 | 2 | ) |
84 | | } else { |
85 | 2 | let consensus = match &self.finalized_consensus { |
86 | 0 | FinalizedConsensus::Unknown => None, |
87 | | FinalizedConsensus::Aura { |
88 | 0 | authorities_list, .. |
89 | 0 | } => Some(BlockConsensus::Aura { |
90 | 0 | authorities_list: authorities_list.clone(), |
91 | 0 | }), |
92 | | FinalizedConsensus::Babe { |
93 | 2 | block_epoch_information, |
94 | 2 | next_epoch_transition, |
95 | | .. |
96 | 2 | } => Some(BlockConsensus::Babe { |
97 | 2 | current_epoch: block_epoch_information.clone(), |
98 | 2 | next_epoch: next_epoch_transition.clone(), |
99 | 2 | }), |
100 | | }; |
101 | | |
102 | 2 | let finality = match self.finality { |
103 | 0 | Finality::Outsourced => BlockFinality::Outsourced, |
104 | | Finality::Grandpa { |
105 | 2 | after_finalized_block_authorities_set_id, |
106 | 2 | ref finalized_scheduled_change, |
107 | 2 | ref finalized_triggered_authorities, |
108 | | } => { |
109 | 2 | debug_assert!( |
110 | 2 | finalized_scheduled_change |
111 | 2 | .as_ref() |
112 | 2 | .map(|(n, _)| *n0 >= decoded_header.number0 ) Unexecuted instantiation: _RNCNvMNtNtNtCsjlkOsLH0Zfj_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeuE13verify_headers5_0Ba_ Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionNtNtCsfFWJyR6nd6r_17smoldot_full_node17consensus_service17NonFinalizedBlockEE13verify_headers5_0CscoAnRPySggw_6author Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreepE13verify_headers5_0Ba_ Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionuEE13verify_headers5_0Cs7snhGEhbuap_18smoldot_light_wasm Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionNtNtCsfFWJyR6nd6r_17smoldot_full_node17consensus_service17NonFinalizedBlockEE13verify_headers5_0CsjyNE3yDMkgA_14json_rpc_basic Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionNtNtCsfFWJyR6nd6r_17smoldot_full_node17consensus_service17NonFinalizedBlockEE13verify_headers5_0Cs4VrkfB1pvQ3_25json_rpc_general_requests |
113 | 2 | .unwrap_or(true) |
114 | | ); |
115 | 2 | BlockFinality::Grandpa { |
116 | 2 | prev_auth_change_trigger_number: None, |
117 | 2 | triggers_change: false, |
118 | 2 | scheduled_change: finalized_scheduled_change.clone(), |
119 | 2 | after_block_authorities_set_id: |
120 | 2 | after_finalized_block_authorities_set_id, |
121 | 2 | triggered_authorities: finalized_triggered_authorities.clone(), |
122 | 2 | } |
123 | | } |
124 | | }; |
125 | | |
126 | 2 | (consensus, self.finalized_best_score, finality) |
127 | | }; |
128 | | |
129 | 4 | let parent_block_header = if let Some(parent_tree_index2 ) = parent_tree_index { |
130 | 2 | &self |
131 | 2 | .blocks |
132 | 2 | .get(parent_tree_index) |
133 | 2 | .unwrap_or_else(|| unreachable!()) |
134 | | .header |
135 | | } else { |
136 | 2 | &self.finalized_block_header |
137 | | }; |
138 | | |
139 | 4 | let header_verify_result = { |
140 | 4 | let consensus_config = match (&self.finalized_consensus, &parent_consensus) { |
141 | | ( |
142 | 0 | FinalizedConsensus::Aura { slot_duration, .. }, |
143 | 0 | Some(BlockConsensus::Aura { authorities_list }), |
144 | 0 | ) => verify::header_only::ConfigConsensus::Aura { |
145 | 0 | current_authorities: header::AuraAuthoritiesIter::from_slice(authorities_list), |
146 | 0 | now_from_unix_epoch, |
147 | 0 | slot_duration: *slot_duration, |
148 | 0 | }, |
149 | | ( |
150 | | FinalizedConsensus::Babe { |
151 | 4 | slots_per_epoch, .. |
152 | | }, |
153 | | Some(BlockConsensus::Babe { |
154 | 4 | current_epoch, |
155 | 4 | next_epoch, |
156 | | }), |
157 | | ) => verify::header_only::ConfigConsensus::Babe { |
158 | 4 | parent_block_epoch: current_epoch.as_ref().map(|v| (&**v)2 .into2 ()), _RNCNvMNtNtNtCsjlkOsLH0Zfj_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeuE13verify_headers_0Ba_ Line | Count | Source | 158 | 2 | parent_block_epoch: current_epoch.as_ref().map(|v| (&**v).into()), |
Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionNtNtCsfFWJyR6nd6r_17smoldot_full_node17consensus_service17NonFinalizedBlockEE13verify_headers_0CscoAnRPySggw_6author Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreepE13verify_headers_0Ba_ Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionuEE13verify_headers_0Cs7snhGEhbuap_18smoldot_light_wasm Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionNtNtCsfFWJyR6nd6r_17smoldot_full_node17consensus_service17NonFinalizedBlockEE13verify_headers_0CsjyNE3yDMkgA_14json_rpc_basic Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionNtNtCsfFWJyR6nd6r_17smoldot_full_node17consensus_service17NonFinalizedBlockEE13verify_headers_0Cs4VrkfB1pvQ3_25json_rpc_general_requests |
159 | 4 | parent_block_next_epoch: (&**next_epoch).into(), |
160 | 4 | slots_per_epoch: *slots_per_epoch, |
161 | 4 | now_from_unix_epoch, |
162 | | }, |
163 | | (FinalizedConsensus::Unknown, None) => { |
164 | 0 | return Err(HeaderVerifyError::UnknownConsensusEngine); |
165 | | } |
166 | | _ => { |
167 | 0 | return Err(HeaderVerifyError::ConsensusMismatch); |
168 | | } |
169 | | }; |
170 | | |
171 | 4 | match verify::header_only::verify(verify::header_only::Config { |
172 | 4 | consensus: consensus_config, |
173 | 4 | finality: match &parent_finality { |
174 | 0 | BlockFinality::Outsourced => verify::header_only::ConfigFinality::Outsourced, |
175 | 4 | BlockFinality::Grandpa { .. } => verify::header_only::ConfigFinality::Grandpa, |
176 | | }, |
177 | 4 | allow_unknown_consensus_engines: self.allow_unknown_consensus_engines, |
178 | 4 | block_header: decoded_header.clone(), |
179 | 4 | block_number_bytes: self.block_number_bytes, |
180 | | parent_block_header: { |
181 | | // All headers inserted in `self` are necessarily valid, and thus this |
182 | | // `unwrap()` can't panic. |
183 | 4 | header::decode(parent_block_header, self.block_number_bytes) |
184 | 4 | .unwrap_or_else(|_| unreachable!()) |
185 | | }, |
186 | | }) { |
187 | 4 | Ok(s) => s, |
188 | 0 | Err(err) => { |
189 | | // The code in this module is meant to ensure that the chain is in an |
190 | | // appropriate state, therefore `is_invalid_chain_configuration` being `true` |
191 | | // would indicate a bug in the code somewhere. |
192 | | // We use a `debug_assert` rather than `assert` in order to avoid crashing, |
193 | | // as treating the header as invalid is an appropriate way to handle a bug |
194 | | // here. |
195 | 0 | debug_assert!(!err.is_invalid_chain_configuration()); |
196 | 0 | return Err(HeaderVerifyError::VerificationFailed(err)); |
197 | | } |
198 | | } |
199 | | }; |
200 | | |
201 | | // Updated consensus information for the block being verified. |
202 | 4 | let (best_score_num_primary_slots, best_score_num_secondary_slots, consensus_update) = |
203 | | match ( |
204 | 4 | header_verify_result, |
205 | 4 | &parent_consensus, |
206 | 4 | self.finalized_consensus.clone(), |
207 | 4 | parent_tree_index.map(|idx| self.blocks2 .get2 (idx).unwrap().consensus.clone2 ()), _RNCNvMNtNtNtCsjlkOsLH0Zfj_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeuE13verify_headers1_0Ba_ Line | Count | Source | 207 | 2 | parent_tree_index.map(|idx| self.blocks.get(idx).unwrap().consensus.clone()), |
Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionNtNtCsfFWJyR6nd6r_17smoldot_full_node17consensus_service17NonFinalizedBlockEE13verify_headers1_0CscoAnRPySggw_6author Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreepE13verify_headers1_0Ba_ Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionuEE13verify_headers1_0Cs7snhGEhbuap_18smoldot_light_wasm Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionNtNtCsfFWJyR6nd6r_17smoldot_full_node17consensus_service17NonFinalizedBlockEE13verify_headers1_0CsjyNE3yDMkgA_14json_rpc_basic Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionNtNtCsfFWJyR6nd6r_17smoldot_full_node17consensus_service17NonFinalizedBlockEE13verify_headers1_0Cs4VrkfB1pvQ3_25json_rpc_general_requests |
208 | | ) { |
209 | | // No Aura epoch transition. Just a regular block. |
210 | | ( |
211 | | verify::header_only::Success::Aura { |
212 | | authorities_change: None, |
213 | | }, |
214 | | Some(BlockConsensus::Aura { |
215 | 0 | authorities_list: parent_authorities, |
216 | | }), |
217 | | FinalizedConsensus::Aura { .. }, |
218 | | _, |
219 | 0 | ) => ( |
220 | 0 | parent_best_score.num_primary_slots + 1, |
221 | 0 | parent_best_score.num_secondary_slots, |
222 | 0 | BlockConsensus::Aura { |
223 | 0 | authorities_list: parent_authorities.clone(), |
224 | 0 | }, |
225 | 0 | ), |
226 | | |
227 | | // Aura epoch transition. |
228 | | ( |
229 | | verify::header_only::Success::Aura { |
230 | 0 | authorities_change: Some(new_authorities_list), |
231 | | }, |
232 | | Some(BlockConsensus::Aura { .. }), |
233 | | FinalizedConsensus::Aura { .. }, |
234 | | _, |
235 | 0 | ) => ( |
236 | 0 | parent_best_score.num_primary_slots + 1, |
237 | 0 | parent_best_score.num_secondary_slots, |
238 | 0 | BlockConsensus::Aura { |
239 | 0 | authorities_list: Arc::new(new_authorities_list), |
240 | 0 | }, |
241 | 0 | ), |
242 | | |
243 | | // No Babe epoch transition. Just a regular block. |
244 | | ( |
245 | | verify::header_only::Success::Babe { |
246 | | epoch_transition_target: None, |
247 | 2 | is_primary_slot, |
248 | | .. |
249 | | }, |
250 | | Some(BlockConsensus::Babe { .. }), |
251 | | FinalizedConsensus::Babe { .. }, |
252 | | Some(BlockConsensus::Babe { |
253 | 2 | current_epoch, |
254 | 2 | next_epoch, |
255 | | }), |
256 | | ) |
257 | | | ( |
258 | | verify::header_only::Success::Babe { |
259 | | epoch_transition_target: None, |
260 | 0 | is_primary_slot, |
261 | | .. |
262 | | }, |
263 | | Some(BlockConsensus::Babe { .. }), |
264 | | FinalizedConsensus::Babe { |
265 | 0 | block_epoch_information: current_epoch, |
266 | 0 | next_epoch_transition: next_epoch, |
267 | | .. |
268 | | }, |
269 | | None, |
270 | | ) => ( |
271 | 2 | parent_best_score.num_primary_slots + if is_primary_slot { 11 } else { 01 }, |
272 | 2 | parent_best_score.num_secondary_slots + if is_primary_slot { 01 } else { 11 }, |
273 | 2 | BlockConsensus::Babe { |
274 | 2 | current_epoch, |
275 | 2 | next_epoch, |
276 | 2 | }, |
277 | | ), |
278 | | |
279 | | // Babe epoch transition. |
280 | | ( |
281 | | verify::header_only::Success::Babe { |
282 | 0 | epoch_transition_target: Some(epoch_transition_target), |
283 | 0 | is_primary_slot, |
284 | | .. |
285 | | }, |
286 | | Some(BlockConsensus::Babe { .. }), |
287 | | FinalizedConsensus::Babe { .. }, |
288 | | Some(BlockConsensus::Babe { |
289 | 0 | next_epoch: next_epoch_transition, |
290 | | .. |
291 | | }), |
292 | | ) |
293 | | | ( |
294 | | verify::header_only::Success::Babe { |
295 | 0 | epoch_transition_target: Some(epoch_transition_target), |
296 | 0 | is_primary_slot, |
297 | | .. |
298 | | }, |
299 | | Some(BlockConsensus::Babe { .. }), |
300 | | FinalizedConsensus::Babe { |
301 | 0 | next_epoch_transition, |
302 | | .. |
303 | | }, |
304 | | None, |
305 | 2 | ) if next_epoch_transition.start_slot_number.is_some()0 => ( |
306 | 0 | parent_best_score.num_primary_slots + if is_primary_slot { 1 } else { 0 }, |
307 | 0 | parent_best_score.num_secondary_slots + if is_primary_slot { 0 } else { 1 }, |
308 | 0 | BlockConsensus::Babe { |
309 | 0 | current_epoch: Some(next_epoch_transition), |
310 | 0 | next_epoch: Arc::new(epoch_transition_target), |
311 | 0 | }, |
312 | | ), |
313 | | |
314 | | // Babe epoch transition to first epoch. |
315 | | // Should only ever happen when the verified block is block 1. |
316 | | ( |
317 | | verify::header_only::Success::Babe { |
318 | 0 | epoch_transition_target: Some(epoch_transition_target), |
319 | 0 | slot_number, |
320 | 0 | is_primary_slot, |
321 | | .. |
322 | | }, |
323 | | Some(BlockConsensus::Babe { .. }), |
324 | | FinalizedConsensus::Babe { .. }, |
325 | 0 | Some(BlockConsensus::Babe { next_epoch, .. }), |
326 | | ) |
327 | | | ( |
328 | | verify::header_only::Success::Babe { |
329 | 2 | epoch_transition_target: Some(epoch_transition_target), |
330 | 2 | slot_number, |
331 | 2 | is_primary_slot, |
332 | | .. |
333 | | }, |
334 | | Some(BlockConsensus::Babe { .. }), |
335 | | FinalizedConsensus::Babe { |
336 | 2 | next_epoch_transition: next_epoch, |
337 | | .. |
338 | | }, |
339 | | None, |
340 | | ) => { |
341 | 2 | debug_assert_eq!(decoded_header.number, 1); |
342 | | ( |
343 | 2 | parent_best_score.num_primary_slots + if is_primary_slot { 11 } else { 01 }, |
344 | 2 | parent_best_score.num_secondary_slots + if is_primary_slot { 01 } else { 11 }, |
345 | 2 | BlockConsensus::Babe { |
346 | 2 | current_epoch: Some(Arc::new( |
347 | 2 | chain_information::BabeEpochInformation { |
348 | 2 | start_slot_number: Some(slot_number), |
349 | 2 | allowed_slots: next_epoch.allowed_slots, |
350 | 2 | epoch_index: next_epoch.epoch_index, |
351 | 2 | authorities: next_epoch.authorities.clone(), |
352 | 2 | c: next_epoch.c, |
353 | 2 | randomness: next_epoch.randomness, |
354 | 2 | }, |
355 | 2 | )), |
356 | 2 | next_epoch: Arc::new(epoch_transition_target), |
357 | 2 | }, |
358 | | ) |
359 | | } |
360 | | |
361 | | // Any mismatch between consensus algorithms should have been detected by the |
362 | | // block verification. |
363 | 0 | _ => unreachable!(), |
364 | | }; |
365 | | |
366 | | // Updated finality information for the block being verified. |
367 | 4 | let finality_update = match &parent_finality { |
368 | 0 | BlockFinality::Outsourced => BlockFinality::Outsourced, |
369 | | BlockFinality::Grandpa { |
370 | 4 | prev_auth_change_trigger_number: parent_prev_auth_change_trigger_number, |
371 | 4 | after_block_authorities_set_id: parent_after_block_authorities_set_id, |
372 | 4 | scheduled_change: parent_scheduled_change, |
373 | 4 | triggered_authorities: parent_triggered_authorities, |
374 | 4 | triggers_change: parent_triggers_change, |
375 | | .. |
376 | | } => { |
377 | 4 | let mut triggered_authorities = parent_triggered_authorities.clone(); |
378 | 4 | let mut triggers_change = false; |
379 | 4 | let mut scheduled_change = parent_scheduled_change.clone(); |
380 | | |
381 | | // Check whether the verified block schedules a change of authorities. |
382 | 10 | for grandpa_digest_item0 in decoded_header.digest4 .logs4 ().filter_map4 (|d| match d { |
383 | 0 | header::DigestItemRef::GrandpaConsensus(gp) => Some(gp), |
384 | 10 | _ => None, |
385 | 10 | }) { _RNCNvMNtNtNtCsjlkOsLH0Zfj_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeuE13verify_headers2_0Ba_ Line | Count | Source | 382 | 10 | for grandpa_digest_item in decoded_header.digest.logs().filter_map(|d| match d { | 383 | 0 | header::DigestItemRef::GrandpaConsensus(gp) => Some(gp), | 384 | 10 | _ => None, | 385 | 10 | }) { |
Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionNtNtCsfFWJyR6nd6r_17smoldot_full_node17consensus_service17NonFinalizedBlockEE13verify_headers2_0CscoAnRPySggw_6author Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreepE13verify_headers2_0Ba_ Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionuEE13verify_headers2_0Cs7snhGEhbuap_18smoldot_light_wasm Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionNtNtCsfFWJyR6nd6r_17smoldot_full_node17consensus_service17NonFinalizedBlockEE13verify_headers2_0CsjyNE3yDMkgA_14json_rpc_basic Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionNtNtCsfFWJyR6nd6r_17smoldot_full_node17consensus_service17NonFinalizedBlockEE13verify_headers2_0Cs4VrkfB1pvQ3_25json_rpc_general_requests |
386 | | // TODO: implement items other than ScheduledChange |
387 | | // TODO: when it comes to forced change, they take precedence over scheduled changes but only sheduled changes within the same block |
388 | 0 | if let header::GrandpaConsensusLogRef::ScheduledChange(change) = |
389 | 0 | grandpa_digest_item |
390 | | { |
391 | 0 | let trigger_block_height = |
392 | 0 | decoded_header.number.checked_add(change.delay).unwrap(); |
393 | | |
394 | | // It is forbidden to schedule a change while a change is already |
395 | | // scheduled, otherwise the block is invalid. This is verified during |
396 | | // the block verification. |
397 | 0 | match scheduled_change { |
398 | 0 | Some(_) => { |
399 | 0 | // Ignore any new change if a change is already in progress. |
400 | 0 | // Matches the behaviour here: <https://github.com/paritytech/substrate/blob/a357c29ebabb075235977edd5e3901c66575f995/client/finality-grandpa/src/authorities.rs#L479> |
401 | 0 | } |
402 | | None => { |
403 | 0 | scheduled_change = Some(( |
404 | 0 | trigger_block_height, |
405 | 0 | change.next_authorities.map(|a| a.into()).collect(), Unexecuted instantiation: _RNCNvMNtNtNtCsjlkOsLH0Zfj_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeuE13verify_headers3_0Ba_ Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionNtNtCsfFWJyR6nd6r_17smoldot_full_node17consensus_service17NonFinalizedBlockEE13verify_headers3_0CscoAnRPySggw_6author Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreepE13verify_headers3_0Ba_ Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionuEE13verify_headers3_0Cs7snhGEhbuap_18smoldot_light_wasm Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionNtNtCsfFWJyR6nd6r_17smoldot_full_node17consensus_service17NonFinalizedBlockEE13verify_headers3_0CsjyNE3yDMkgA_14json_rpc_basic Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionNtNtCsfFWJyR6nd6r_17smoldot_full_node17consensus_service17NonFinalizedBlockEE13verify_headers3_0Cs4VrkfB1pvQ3_25json_rpc_general_requests |
406 | | )); |
407 | | } |
408 | | } |
409 | 0 | } |
410 | | } |
411 | | |
412 | | // If the newly-verified block is one where Grandpa scheduled change are |
413 | | // triggered, we need update the field values. |
414 | | // Note that this is checked after we have potentially fetched `scheduled_change` |
415 | | // from the block. |
416 | 4 | if let Some((trigger_height0 , new_list0 )) = &scheduled_change { |
417 | 0 | if *trigger_height == decoded_header.number { |
418 | 0 | triggers_change = true; |
419 | 0 | triggered_authorities = new_list.clone(); |
420 | 0 | scheduled_change = None; |
421 | 0 | } |
422 | 4 | } |
423 | | |
424 | | // Some sanity checks. |
425 | 4 | debug_assert!( |
426 | 4 | scheduled_change |
427 | 4 | .as_ref() |
428 | 4 | .map(|(n, _)| *n0 > decoded_header.number0 ) Unexecuted instantiation: _RNCNvMNtNtNtCsjlkOsLH0Zfj_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeuE13verify_headers6_0Ba_ Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionNtNtCsfFWJyR6nd6r_17smoldot_full_node17consensus_service17NonFinalizedBlockEE13verify_headers6_0CscoAnRPySggw_6author Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreepE13verify_headers6_0Ba_ Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionuEE13verify_headers6_0Cs7snhGEhbuap_18smoldot_light_wasm Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionNtNtCsfFWJyR6nd6r_17smoldot_full_node17consensus_service17NonFinalizedBlockEE13verify_headers6_0CsjyNE3yDMkgA_14json_rpc_basic Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionNtNtCsfFWJyR6nd6r_17smoldot_full_node17consensus_service17NonFinalizedBlockEE13verify_headers6_0Cs4VrkfB1pvQ3_25json_rpc_general_requests |
429 | 4 | .unwrap_or(true) |
430 | | ); |
431 | 4 | debug_assert!( |
432 | 4 | parent_prev_auth_change_trigger_number |
433 | 4 | .as_ref() |
434 | 4 | .map(|n| *n0 < decoded_header.number0 ) Unexecuted instantiation: _RNCNvMNtNtNtCsjlkOsLH0Zfj_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeuE13verify_headers7_0Ba_ Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionNtNtCsfFWJyR6nd6r_17smoldot_full_node17consensus_service17NonFinalizedBlockEE13verify_headers7_0CscoAnRPySggw_6author Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreepE13verify_headers7_0Ba_ Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionuEE13verify_headers7_0Cs7snhGEhbuap_18smoldot_light_wasm Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionNtNtCsfFWJyR6nd6r_17smoldot_full_node17consensus_service17NonFinalizedBlockEE13verify_headers7_0CsjyNE3yDMkgA_14json_rpc_basic Unexecuted instantiation: _RNCNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB6_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionNtNtCsfFWJyR6nd6r_17smoldot_full_node17consensus_service17NonFinalizedBlockEE13verify_headers7_0Cs4VrkfB1pvQ3_25json_rpc_general_requests |
435 | 4 | .unwrap_or(true) |
436 | | ); |
437 | | |
438 | | BlockFinality::Grandpa { |
439 | 4 | prev_auth_change_trigger_number: if *parent_triggers_change { |
440 | 0 | Some(decoded_header.number - 1) |
441 | | } else { |
442 | 4 | *parent_prev_auth_change_trigger_number |
443 | | }, |
444 | 4 | triggered_authorities, |
445 | 4 | scheduled_change, |
446 | 4 | triggers_change, |
447 | 4 | after_block_authorities_set_id: if triggers_change { |
448 | 0 | *parent_after_block_authorities_set_id + 1 |
449 | | } else { |
450 | 4 | *parent_after_block_authorities_set_id |
451 | | }, |
452 | | } |
453 | | } |
454 | | }; |
455 | | |
456 | | // Determine whether this block would be the new best. |
457 | 4 | let is_new_best = { |
458 | 4 | let current_best_score = self |
459 | 4 | .blocks_by_best_score |
460 | 4 | .last_key_value() |
461 | 4 | .map(|(s, _)| s) |
462 | 4 | .unwrap_or(&self.finalized_best_score); |
463 | | |
464 | 4 | let new_block_best_score = BestScore { |
465 | 4 | num_primary_slots: best_score_num_primary_slots, |
466 | 4 | num_secondary_slots: best_score_num_secondary_slots, |
467 | 4 | insertion_counter: self.blocks_insertion_counter, |
468 | 4 | }; |
469 | | |
470 | 4 | debug_assert_ne!(new_block_best_score, *current_best_score); |
471 | 4 | new_block_best_score > *current_best_score |
472 | | }; |
473 | | |
474 | 4 | Ok(HeaderVerifySuccess::Verified { |
475 | 4 | verified_header: VerifiedHeader { |
476 | 4 | number: decoded_header.number, |
477 | 4 | scale_encoded_header, |
478 | 4 | consensus_update, |
479 | 4 | finality_update, |
480 | 4 | best_score_num_primary_slots, |
481 | 4 | best_score_num_secondary_slots, |
482 | 4 | hash, |
483 | 4 | }, |
484 | 4 | is_new_best, |
485 | 4 | }) |
486 | 4 | } _RNvMNtNtNtCsjlkOsLH0Zfj_7smoldot5chain11blocks_tree6verifyINtB4_16NonFinalizedTreeuE13verify_headerB8_ Line | Count | Source | 40 | 4 | pub fn verify_header( | 41 | 4 | &self, | 42 | 4 | scale_encoded_header: Vec<u8>, | 43 | 4 | now_from_unix_epoch: Duration, | 44 | 4 | ) -> Result<HeaderVerifySuccess, HeaderVerifyError> { | 45 | 4 | let decoded_header = match header::decode(&scale_encoded_header, self.block_number_bytes) { | 46 | 4 | Ok(h) => h, | 47 | 0 | Err(err) => return Err(HeaderVerifyError::InvalidHeader(err)), | 48 | | }; | 49 | | | 50 | 4 | let hash = header::hash_from_scale_encoded_header(&scale_encoded_header); | 51 | | | 52 | | // Check for duplicates. | 53 | 4 | if self.blocks_by_hash.contains_key(&hash) { | 54 | 0 | return Ok(HeaderVerifySuccess::Duplicate); | 55 | 4 | } | 56 | | | 57 | | // Try to find the parent block in the tree of known blocks. | 58 | | // `Some` with an index of the parent within the tree of unfinalized blocks. | 59 | | // `None` means that the parent is the finalized block. | 60 | 4 | let parent_tree_index = { | 61 | 4 | if *decoded_header.parent_hash == self.finalized_block_hash { | 62 | 2 | None | 63 | | } else { | 64 | 2 | match self.blocks_by_hash.get(decoded_header.parent_hash) { | 65 | 2 | Some(parent) => Some(*parent), | 66 | | None => { | 67 | 0 | let parent_hash = *decoded_header.parent_hash; | 68 | 0 | return Err(HeaderVerifyError::BadParent { parent_hash }); | 69 | | } | 70 | | } | 71 | | } | 72 | | }; | 73 | | | 74 | | // Some consensus-specific information must be fetched from the tree of ancestry. The | 75 | | // information is found either in the parent block, or in the finalized block. | 76 | 4 | let (parent_consensus, parent_best_score, parent_finality) = | 77 | 4 | if let Some(parent_tree_index2 ) = parent_tree_index { | 78 | 2 | let parent = self.blocks.get(parent_tree_index).unwrap(); | 79 | 2 | ( | 80 | 2 | Some(parent.consensus.clone()), | 81 | 2 | parent.best_score, | 82 | 2 | parent.finality.clone(), | 83 | 2 | ) | 84 | | } else { | 85 | 2 | let consensus = match &self.finalized_consensus { | 86 | 0 | FinalizedConsensus::Unknown => None, | 87 | | FinalizedConsensus::Aura { | 88 | 0 | authorities_list, .. | 89 | 0 | } => Some(BlockConsensus::Aura { | 90 | 0 | authorities_list: authorities_list.clone(), | 91 | 0 | }), | 92 | | FinalizedConsensus::Babe { | 93 | 2 | block_epoch_information, | 94 | 2 | next_epoch_transition, | 95 | | .. | 96 | 2 | } => Some(BlockConsensus::Babe { | 97 | 2 | current_epoch: block_epoch_information.clone(), | 98 | 2 | next_epoch: next_epoch_transition.clone(), | 99 | 2 | }), | 100 | | }; | 101 | | | 102 | 2 | let finality = match self.finality { | 103 | 0 | Finality::Outsourced => BlockFinality::Outsourced, | 104 | | Finality::Grandpa { | 105 | 2 | after_finalized_block_authorities_set_id, | 106 | 2 | ref finalized_scheduled_change, | 107 | 2 | ref finalized_triggered_authorities, | 108 | | } => { | 109 | 2 | debug_assert!( | 110 | 2 | finalized_scheduled_change | 111 | 2 | .as_ref() | 112 | 2 | .map(|(n, _)| *n >= decoded_header.number) | 113 | 2 | .unwrap_or(true) | 114 | | ); | 115 | 2 | BlockFinality::Grandpa { | 116 | 2 | prev_auth_change_trigger_number: None, | 117 | 2 | triggers_change: false, | 118 | 2 | scheduled_change: finalized_scheduled_change.clone(), | 119 | 2 | after_block_authorities_set_id: | 120 | 2 | after_finalized_block_authorities_set_id, | 121 | 2 | triggered_authorities: finalized_triggered_authorities.clone(), | 122 | 2 | } | 123 | | } | 124 | | }; | 125 | | | 126 | 2 | (consensus, self.finalized_best_score, finality) | 127 | | }; | 128 | | | 129 | 4 | let parent_block_header = if let Some(parent_tree_index2 ) = parent_tree_index { | 130 | 2 | &self | 131 | 2 | .blocks | 132 | 2 | .get(parent_tree_index) | 133 | 2 | .unwrap_or_else(|| unreachable!()) | 134 | | .header | 135 | | } else { | 136 | 2 | &self.finalized_block_header | 137 | | }; | 138 | | | 139 | 4 | let header_verify_result = { | 140 | 4 | let consensus_config = match (&self.finalized_consensus, &parent_consensus) { | 141 | | ( | 142 | 0 | FinalizedConsensus::Aura { slot_duration, .. }, | 143 | 0 | Some(BlockConsensus::Aura { authorities_list }), | 144 | 0 | ) => verify::header_only::ConfigConsensus::Aura { | 145 | 0 | current_authorities: header::AuraAuthoritiesIter::from_slice(authorities_list), | 146 | 0 | now_from_unix_epoch, | 147 | 0 | slot_duration: *slot_duration, | 148 | 0 | }, | 149 | | ( | 150 | | FinalizedConsensus::Babe { | 151 | 4 | slots_per_epoch, .. | 152 | | }, | 153 | | Some(BlockConsensus::Babe { | 154 | 4 | current_epoch, | 155 | 4 | next_epoch, | 156 | | }), | 157 | | ) => verify::header_only::ConfigConsensus::Babe { | 158 | 4 | parent_block_epoch: current_epoch.as_ref().map(|v| (&**v).into()), | 159 | 4 | parent_block_next_epoch: (&**next_epoch).into(), | 160 | 4 | slots_per_epoch: *slots_per_epoch, | 161 | 4 | now_from_unix_epoch, | 162 | | }, | 163 | | (FinalizedConsensus::Unknown, None) => { | 164 | 0 | return Err(HeaderVerifyError::UnknownConsensusEngine); | 165 | | } | 166 | | _ => { | 167 | 0 | return Err(HeaderVerifyError::ConsensusMismatch); | 168 | | } | 169 | | }; | 170 | | | 171 | 4 | match verify::header_only::verify(verify::header_only::Config { | 172 | 4 | consensus: consensus_config, | 173 | 4 | finality: match &parent_finality { | 174 | 0 | BlockFinality::Outsourced => verify::header_only::ConfigFinality::Outsourced, | 175 | 4 | BlockFinality::Grandpa { .. } => verify::header_only::ConfigFinality::Grandpa, | 176 | | }, | 177 | 4 | allow_unknown_consensus_engines: self.allow_unknown_consensus_engines, | 178 | 4 | block_header: decoded_header.clone(), | 179 | 4 | block_number_bytes: self.block_number_bytes, | 180 | | parent_block_header: { | 181 | | // All headers inserted in `self` are necessarily valid, and thus this | 182 | | // `unwrap()` can't panic. | 183 | 4 | header::decode(parent_block_header, self.block_number_bytes) | 184 | 4 | .unwrap_or_else(|_| unreachable!()) | 185 | | }, | 186 | | }) { | 187 | 4 | Ok(s) => s, | 188 | 0 | Err(err) => { | 189 | | // The code in this module is meant to ensure that the chain is in an | 190 | | // appropriate state, therefore `is_invalid_chain_configuration` being `true` | 191 | | // would indicate a bug in the code somewhere. | 192 | | // We use a `debug_assert` rather than `assert` in order to avoid crashing, | 193 | | // as treating the header as invalid is an appropriate way to handle a bug | 194 | | // here. | 195 | 0 | debug_assert!(!err.is_invalid_chain_configuration()); | 196 | 0 | return Err(HeaderVerifyError::VerificationFailed(err)); | 197 | | } | 198 | | } | 199 | | }; | 200 | | | 201 | | // Updated consensus information for the block being verified. | 202 | 4 | let (best_score_num_primary_slots, best_score_num_secondary_slots, consensus_update) = | 203 | | match ( | 204 | 4 | header_verify_result, | 205 | 4 | &parent_consensus, | 206 | 4 | self.finalized_consensus.clone(), | 207 | 4 | parent_tree_index.map(|idx| self.blocks.get(idx).unwrap().consensus.clone()), | 208 | | ) { | 209 | | // No Aura epoch transition. Just a regular block. | 210 | | ( | 211 | | verify::header_only::Success::Aura { | 212 | | authorities_change: None, | 213 | | }, | 214 | | Some(BlockConsensus::Aura { | 215 | 0 | authorities_list: parent_authorities, | 216 | | }), | 217 | | FinalizedConsensus::Aura { .. }, | 218 | | _, | 219 | 0 | ) => ( | 220 | 0 | parent_best_score.num_primary_slots + 1, | 221 | 0 | parent_best_score.num_secondary_slots, | 222 | 0 | BlockConsensus::Aura { | 223 | 0 | authorities_list: parent_authorities.clone(), | 224 | 0 | }, | 225 | 0 | ), | 226 | | | 227 | | // Aura epoch transition. | 228 | | ( | 229 | | verify::header_only::Success::Aura { | 230 | 0 | authorities_change: Some(new_authorities_list), | 231 | | }, | 232 | | Some(BlockConsensus::Aura { .. }), | 233 | | FinalizedConsensus::Aura { .. }, | 234 | | _, | 235 | 0 | ) => ( | 236 | 0 | parent_best_score.num_primary_slots + 1, | 237 | 0 | parent_best_score.num_secondary_slots, | 238 | 0 | BlockConsensus::Aura { | 239 | 0 | authorities_list: Arc::new(new_authorities_list), | 240 | 0 | }, | 241 | 0 | ), | 242 | | | 243 | | // No Babe epoch transition. Just a regular block. | 244 | | ( | 245 | | verify::header_only::Success::Babe { | 246 | | epoch_transition_target: None, | 247 | 2 | is_primary_slot, | 248 | | .. | 249 | | }, | 250 | | Some(BlockConsensus::Babe { .. }), | 251 | | FinalizedConsensus::Babe { .. }, | 252 | | Some(BlockConsensus::Babe { | 253 | 2 | current_epoch, | 254 | 2 | next_epoch, | 255 | | }), | 256 | | ) | 257 | | | ( | 258 | | verify::header_only::Success::Babe { | 259 | | epoch_transition_target: None, | 260 | 0 | is_primary_slot, | 261 | | .. | 262 | | }, | 263 | | Some(BlockConsensus::Babe { .. }), | 264 | | FinalizedConsensus::Babe { | 265 | 0 | block_epoch_information: current_epoch, | 266 | 0 | next_epoch_transition: next_epoch, | 267 | | .. | 268 | | }, | 269 | | None, | 270 | | ) => ( | 271 | 2 | parent_best_score.num_primary_slots + if is_primary_slot { 11 } else { 01 }, | 272 | 2 | parent_best_score.num_secondary_slots + if is_primary_slot { 01 } else { 11 }, | 273 | 2 | BlockConsensus::Babe { | 274 | 2 | current_epoch, | 275 | 2 | next_epoch, | 276 | 2 | }, | 277 | | ), | 278 | | | 279 | | // Babe epoch transition. | 280 | | ( | 281 | | verify::header_only::Success::Babe { | 282 | 0 | epoch_transition_target: Some(epoch_transition_target), | 283 | 0 | is_primary_slot, | 284 | | .. | 285 | | }, | 286 | | Some(BlockConsensus::Babe { .. }), | 287 | | FinalizedConsensus::Babe { .. }, | 288 | | Some(BlockConsensus::Babe { | 289 | 0 | next_epoch: next_epoch_transition, | 290 | | .. | 291 | | }), | 292 | | ) | 293 | | | ( | 294 | | verify::header_only::Success::Babe { | 295 | 0 | epoch_transition_target: Some(epoch_transition_target), | 296 | 0 | is_primary_slot, | 297 | | .. | 298 | | }, | 299 | | Some(BlockConsensus::Babe { .. }), | 300 | | FinalizedConsensus::Babe { | 301 | 0 | next_epoch_transition, | 302 | | .. | 303 | | }, | 304 | | None, | 305 | 2 | ) if next_epoch_transition.start_slot_number.is_some()0 => ( | 306 | 0 | parent_best_score.num_primary_slots + if is_primary_slot { 1 } else { 0 }, | 307 | 0 | parent_best_score.num_secondary_slots + if is_primary_slot { 0 } else { 1 }, | 308 | 0 | BlockConsensus::Babe { | 309 | 0 | current_epoch: Some(next_epoch_transition), | 310 | 0 | next_epoch: Arc::new(epoch_transition_target), | 311 | 0 | }, | 312 | | ), | 313 | | | 314 | | // Babe epoch transition to first epoch. | 315 | | // Should only ever happen when the verified block is block 1. | 316 | | ( | 317 | | verify::header_only::Success::Babe { | 318 | 0 | epoch_transition_target: Some(epoch_transition_target), | 319 | 0 | slot_number, | 320 | 0 | is_primary_slot, | 321 | | .. | 322 | | }, | 323 | | Some(BlockConsensus::Babe { .. }), | 324 | | FinalizedConsensus::Babe { .. }, | 325 | 0 | Some(BlockConsensus::Babe { next_epoch, .. }), | 326 | | ) | 327 | | | ( | 328 | | verify::header_only::Success::Babe { | 329 | 2 | epoch_transition_target: Some(epoch_transition_target), | 330 | 2 | slot_number, | 331 | 2 | is_primary_slot, | 332 | | .. | 333 | | }, | 334 | | Some(BlockConsensus::Babe { .. }), | 335 | | FinalizedConsensus::Babe { | 336 | 2 | next_epoch_transition: next_epoch, | 337 | | .. | 338 | | }, | 339 | | None, | 340 | | ) => { | 341 | 2 | debug_assert_eq!(decoded_header.number, 1); | 342 | | ( | 343 | 2 | parent_best_score.num_primary_slots + if is_primary_slot { 11 } else { 01 }, | 344 | 2 | parent_best_score.num_secondary_slots + if is_primary_slot { 01 } else { 11 }, | 345 | 2 | BlockConsensus::Babe { | 346 | 2 | current_epoch: Some(Arc::new( | 347 | 2 | chain_information::BabeEpochInformation { | 348 | 2 | start_slot_number: Some(slot_number), | 349 | 2 | allowed_slots: next_epoch.allowed_slots, | 350 | 2 | epoch_index: next_epoch.epoch_index, | 351 | 2 | authorities: next_epoch.authorities.clone(), | 352 | 2 | c: next_epoch.c, | 353 | 2 | randomness: next_epoch.randomness, | 354 | 2 | }, | 355 | 2 | )), | 356 | 2 | next_epoch: Arc::new(epoch_transition_target), | 357 | 2 | }, | 358 | | ) | 359 | | } | 360 | | | 361 | | // Any mismatch between consensus algorithms should have been detected by the | 362 | | // block verification. | 363 | 0 | _ => unreachable!(), | 364 | | }; | 365 | | | 366 | | // Updated finality information for the block being verified. | 367 | 4 | let finality_update = match &parent_finality { | 368 | 0 | BlockFinality::Outsourced => BlockFinality::Outsourced, | 369 | | BlockFinality::Grandpa { | 370 | 4 | prev_auth_change_trigger_number: parent_prev_auth_change_trigger_number, | 371 | 4 | after_block_authorities_set_id: parent_after_block_authorities_set_id, | 372 | 4 | scheduled_change: parent_scheduled_change, | 373 | 4 | triggered_authorities: parent_triggered_authorities, | 374 | 4 | triggers_change: parent_triggers_change, | 375 | | .. | 376 | | } => { | 377 | 4 | let mut triggered_authorities = parent_triggered_authorities.clone(); | 378 | 4 | let mut triggers_change = false; | 379 | 4 | let mut scheduled_change = parent_scheduled_change.clone(); | 380 | | | 381 | | // Check whether the verified block schedules a change of authorities. | 382 | 4 | for grandpa_digest_item0 in decoded_header.digest.logs().filter_map(|d| match d { | 383 | | header::DigestItemRef::GrandpaConsensus(gp) => Some(gp), | 384 | | _ => None, | 385 | | }) { | 386 | | // TODO: implement items other than ScheduledChange | 387 | | // TODO: when it comes to forced change, they take precedence over scheduled changes but only sheduled changes within the same block | 388 | 0 | if let header::GrandpaConsensusLogRef::ScheduledChange(change) = | 389 | 0 | grandpa_digest_item | 390 | | { | 391 | 0 | let trigger_block_height = | 392 | 0 | decoded_header.number.checked_add(change.delay).unwrap(); | 393 | | | 394 | | // It is forbidden to schedule a change while a change is already | 395 | | // scheduled, otherwise the block is invalid. This is verified during | 396 | | // the block verification. | 397 | 0 | match scheduled_change { | 398 | 0 | Some(_) => { | 399 | 0 | // Ignore any new change if a change is already in progress. | 400 | 0 | // Matches the behaviour here: <https://github.com/paritytech/substrate/blob/a357c29ebabb075235977edd5e3901c66575f995/client/finality-grandpa/src/authorities.rs#L479> | 401 | 0 | } | 402 | | None => { | 403 | 0 | scheduled_change = Some(( | 404 | 0 | trigger_block_height, | 405 | 0 | change.next_authorities.map(|a| a.into()).collect(), | 406 | | )); | 407 | | } | 408 | | } | 409 | 0 | } | 410 | | } | 411 | | | 412 | | // If the newly-verified block is one where Grandpa scheduled change are | 413 | | // triggered, we need update the field values. | 414 | | // Note that this is checked after we have potentially fetched `scheduled_change` | 415 | | // from the block. | 416 | 4 | if let Some((trigger_height0 , new_list0 )) = &scheduled_change { | 417 | 0 | if *trigger_height == decoded_header.number { | 418 | 0 | triggers_change = true; | 419 | 0 | triggered_authorities = new_list.clone(); | 420 | 0 | scheduled_change = None; | 421 | 0 | } | 422 | 4 | } | 423 | | | 424 | | // Some sanity checks. | 425 | 4 | debug_assert!( | 426 | 4 | scheduled_change | 427 | 4 | .as_ref() | 428 | 4 | .map(|(n, _)| *n > decoded_header.number) | 429 | 4 | .unwrap_or(true) | 430 | | ); | 431 | 4 | debug_assert!( | 432 | 4 | parent_prev_auth_change_trigger_number | 433 | 4 | .as_ref() | 434 | 4 | .map(|n| *n < decoded_header.number) | 435 | 4 | .unwrap_or(true) | 436 | | ); | 437 | | | 438 | | BlockFinality::Grandpa { | 439 | 4 | prev_auth_change_trigger_number: if *parent_triggers_change { | 440 | 0 | Some(decoded_header.number - 1) | 441 | | } else { | 442 | 4 | *parent_prev_auth_change_trigger_number | 443 | | }, | 444 | 4 | triggered_authorities, | 445 | 4 | scheduled_change, | 446 | 4 | triggers_change, | 447 | 4 | after_block_authorities_set_id: if triggers_change { | 448 | 0 | *parent_after_block_authorities_set_id + 1 | 449 | | } else { | 450 | 4 | *parent_after_block_authorities_set_id | 451 | | }, | 452 | | } | 453 | | } | 454 | | }; | 455 | | | 456 | | // Determine whether this block would be the new best. | 457 | 4 | let is_new_best = { | 458 | 4 | let current_best_score = self | 459 | 4 | .blocks_by_best_score | 460 | 4 | .last_key_value() | 461 | 4 | .map(|(s, _)| s) | 462 | 4 | .unwrap_or(&self.finalized_best_score); | 463 | | | 464 | 4 | let new_block_best_score = BestScore { | 465 | 4 | num_primary_slots: best_score_num_primary_slots, | 466 | 4 | num_secondary_slots: best_score_num_secondary_slots, | 467 | 4 | insertion_counter: self.blocks_insertion_counter, | 468 | 4 | }; | 469 | | | 470 | 4 | debug_assert_ne!(new_block_best_score, *current_best_score); | 471 | 4 | new_block_best_score > *current_best_score | 472 | | }; | 473 | | | 474 | 4 | Ok(HeaderVerifySuccess::Verified { | 475 | 4 | verified_header: VerifiedHeader { | 476 | 4 | number: decoded_header.number, | 477 | 4 | scale_encoded_header, | 478 | 4 | consensus_update, | 479 | 4 | finality_update, | 480 | 4 | best_score_num_primary_slots, | 481 | 4 | best_score_num_secondary_slots, | 482 | 4 | hash, | 483 | 4 | }, | 484 | 4 | is_new_best, | 485 | 4 | }) | 486 | 4 | } |
Unexecuted instantiation: _RNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB4_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionNtNtCsfFWJyR6nd6r_17smoldot_full_node17consensus_service17NonFinalizedBlockEE13verify_headerCscoAnRPySggw_6author Unexecuted instantiation: _RNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB4_16NonFinalizedTreepE13verify_headerB8_ Unexecuted instantiation: _RNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB4_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionuEE13verify_headerCs7snhGEhbuap_18smoldot_light_wasm Unexecuted instantiation: _RNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB4_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionNtNtCsfFWJyR6nd6r_17smoldot_full_node17consensus_service17NonFinalizedBlockEE13verify_headerCsjyNE3yDMkgA_14json_rpc_basic Unexecuted instantiation: _RNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB4_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionNtNtCsfFWJyR6nd6r_17smoldot_full_node17consensus_service17NonFinalizedBlockEE13verify_headerCs4VrkfB1pvQ3_25json_rpc_general_requests |
487 | | |
488 | | /// Insert a header that has already been verified to be valid. |
489 | | /// |
490 | | /// # Panic |
491 | | /// |
492 | | /// Panics if the parent of the block isn't in the tree. The presence of the parent is verified |
493 | | /// when the block is verified, so this can only happen if you remove the parent after having |
494 | | /// verified the block but before calling this function. |
495 | | /// |
496 | 4 | pub fn insert_verified_header(&mut self, verified_header: VerifiedHeader, user_data: T) { |
497 | | // Try to find the parent block in the tree of known blocks. |
498 | | // `Some` with an index of the parent within the tree of unfinalized blocks. |
499 | | // `None` means that the parent is the finalized block. |
500 | 4 | let parent_tree_index = { |
501 | 4 | let decoded_header = header::decode( |
502 | 4 | &verified_header.scale_encoded_header, |
503 | 4 | self.block_number_bytes, |
504 | | ) |
505 | 4 | .unwrap(); |
506 | | |
507 | 4 | if *decoded_header.parent_hash == self.finalized_block_hash { |
508 | 2 | None |
509 | | } else { |
510 | 2 | Some(*self.blocks_by_hash.get(decoded_header.parent_hash).unwrap()) |
511 | | } |
512 | | }; |
513 | | |
514 | 4 | let best_score = BestScore { |
515 | 4 | num_primary_slots: verified_header.best_score_num_primary_slots, |
516 | 4 | num_secondary_slots: verified_header.best_score_num_secondary_slots, |
517 | 4 | insertion_counter: self.blocks_insertion_counter, |
518 | 4 | }; |
519 | | |
520 | 4 | let prev_auth_change_trigger_number_if_trigger = if let BlockFinality::Grandpa { |
521 | 0 | prev_auth_change_trigger_number, |
522 | | triggers_change: true, |
523 | | .. |
524 | 4 | } = verified_header.finality_update |
525 | | { |
526 | 0 | Some(prev_auth_change_trigger_number) |
527 | | } else { |
528 | 4 | None |
529 | | }; |
530 | | |
531 | 4 | let new_node_index = self.blocks.insert( |
532 | 4 | parent_tree_index, |
533 | 4 | Block { |
534 | 4 | header: verified_header.scale_encoded_header, |
535 | 4 | hash: verified_header.hash, |
536 | 4 | number: verified_header.number, |
537 | 4 | consensus: verified_header.consensus_update, |
538 | 4 | finality: verified_header.finality_update, |
539 | 4 | best_score, |
540 | 4 | user_data, |
541 | 4 | }, |
542 | | ); |
543 | | |
544 | 4 | let _prev_value = self |
545 | 4 | .blocks_by_hash |
546 | 4 | .insert(verified_header.hash, new_node_index); |
547 | | // A bug here would be serious enough that it is worth being an `assert!` |
548 | 4 | assert!(_prev_value.is_none()); |
549 | | |
550 | 4 | self.blocks_by_best_score.insert(best_score, new_node_index); |
551 | | |
552 | 4 | if let Some(prev_auth_change_trigger_number0 ) = prev_auth_change_trigger_number_if_trigger { |
553 | 0 | self.blocks_trigger_gp_change |
554 | 0 | .insert((prev_auth_change_trigger_number, new_node_index)); |
555 | 4 | } |
556 | | |
557 | | // An overflow here would break the logic of the module. It is better to panic than to |
558 | | // continue running. |
559 | 4 | self.blocks_insertion_counter = self.blocks_insertion_counter.checked_add(1).unwrap(); |
560 | 4 | } _RNvMNtNtNtCsjlkOsLH0Zfj_7smoldot5chain11blocks_tree6verifyINtB4_16NonFinalizedTreeuE22insert_verified_headerB8_ Line | Count | Source | 496 | 4 | pub fn insert_verified_header(&mut self, verified_header: VerifiedHeader, user_data: T) { | 497 | | // Try to find the parent block in the tree of known blocks. | 498 | | // `Some` with an index of the parent within the tree of unfinalized blocks. | 499 | | // `None` means that the parent is the finalized block. | 500 | 4 | let parent_tree_index = { | 501 | 4 | let decoded_header = header::decode( | 502 | 4 | &verified_header.scale_encoded_header, | 503 | 4 | self.block_number_bytes, | 504 | | ) | 505 | 4 | .unwrap(); | 506 | | | 507 | 4 | if *decoded_header.parent_hash == self.finalized_block_hash { | 508 | 2 | None | 509 | | } else { | 510 | 2 | Some(*self.blocks_by_hash.get(decoded_header.parent_hash).unwrap()) | 511 | | } | 512 | | }; | 513 | | | 514 | 4 | let best_score = BestScore { | 515 | 4 | num_primary_slots: verified_header.best_score_num_primary_slots, | 516 | 4 | num_secondary_slots: verified_header.best_score_num_secondary_slots, | 517 | 4 | insertion_counter: self.blocks_insertion_counter, | 518 | 4 | }; | 519 | | | 520 | 4 | let prev_auth_change_trigger_number_if_trigger = if let BlockFinality::Grandpa { | 521 | 0 | prev_auth_change_trigger_number, | 522 | | triggers_change: true, | 523 | | .. | 524 | 4 | } = verified_header.finality_update | 525 | | { | 526 | 0 | Some(prev_auth_change_trigger_number) | 527 | | } else { | 528 | 4 | None | 529 | | }; | 530 | | | 531 | 4 | let new_node_index = self.blocks.insert( | 532 | 4 | parent_tree_index, | 533 | 4 | Block { | 534 | 4 | header: verified_header.scale_encoded_header, | 535 | 4 | hash: verified_header.hash, | 536 | 4 | number: verified_header.number, | 537 | 4 | consensus: verified_header.consensus_update, | 538 | 4 | finality: verified_header.finality_update, | 539 | 4 | best_score, | 540 | 4 | user_data, | 541 | 4 | }, | 542 | | ); | 543 | | | 544 | 4 | let _prev_value = self | 545 | 4 | .blocks_by_hash | 546 | 4 | .insert(verified_header.hash, new_node_index); | 547 | | // A bug here would be serious enough that it is worth being an `assert!` | 548 | 4 | assert!(_prev_value.is_none()); | 549 | | | 550 | 4 | self.blocks_by_best_score.insert(best_score, new_node_index); | 551 | | | 552 | 4 | if let Some(prev_auth_change_trigger_number0 ) = prev_auth_change_trigger_number_if_trigger { | 553 | 0 | self.blocks_trigger_gp_change | 554 | 0 | .insert((prev_auth_change_trigger_number, new_node_index)); | 555 | 4 | } | 556 | | | 557 | | // An overflow here would break the logic of the module. It is better to panic than to | 558 | | // continue running. | 559 | 4 | self.blocks_insertion_counter = self.blocks_insertion_counter.checked_add(1).unwrap(); | 560 | 4 | } |
Unexecuted instantiation: _RNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB4_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionNtNtCsfFWJyR6nd6r_17smoldot_full_node17consensus_service17NonFinalizedBlockEE22insert_verified_headerCscoAnRPySggw_6author Unexecuted instantiation: _RNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB4_16NonFinalizedTreepE22insert_verified_headerB8_ Unexecuted instantiation: _RNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB4_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionuEE22insert_verified_headerCs7snhGEhbuap_18smoldot_light_wasm Unexecuted instantiation: _RNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB4_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionNtNtCsfFWJyR6nd6r_17smoldot_full_node17consensus_service17NonFinalizedBlockEE22insert_verified_headerCsjyNE3yDMkgA_14json_rpc_basic Unexecuted instantiation: _RNvMNtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyINtB4_16NonFinalizedTreeINtNtCs1p5UDGgVI4d_4core6option6OptionNtNtCsfFWJyR6nd6r_17smoldot_full_node17consensus_service17NonFinalizedBlockEE22insert_verified_headerCs4VrkfB1pvQ3_25json_rpc_general_requests |
561 | | } |
562 | | |
563 | | /// Successfully-verified block header that can be inserted into the chain. |
564 | | pub struct VerifiedHeader { |
565 | | scale_encoded_header: Vec<u8>, |
566 | | consensus_update: BlockConsensus, |
567 | | finality_update: BlockFinality, |
568 | | best_score_num_primary_slots: u64, |
569 | | best_score_num_secondary_slots: u64, |
570 | | hash: [u8; 32], |
571 | | number: u64, |
572 | | } |
573 | | |
574 | | impl VerifiedHeader { |
575 | | /// Returns the block header. |
576 | 0 | pub fn scale_encoded_header(&self) -> &[u8] { |
577 | 0 | &self.scale_encoded_header |
578 | 0 | } Unexecuted instantiation: _RNvMs_NtNtNtCsjlkOsLH0Zfj_7smoldot5chain11blocks_tree6verifyNtB4_14VerifiedHeader20scale_encoded_header Unexecuted instantiation: _RNvMs_NtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyNtB4_14VerifiedHeader20scale_encoded_header |
579 | | |
580 | | /// Returns the block header. |
581 | 0 | pub fn into_scale_encoded_header(self) -> Vec<u8> { |
582 | 0 | self.scale_encoded_header |
583 | 0 | } Unexecuted instantiation: _RNvMs_NtNtNtCsjlkOsLH0Zfj_7smoldot5chain11blocks_tree6verifyNtB4_14VerifiedHeader25into_scale_encoded_header Unexecuted instantiation: _RNvMs_NtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyNtB4_14VerifiedHeader25into_scale_encoded_header |
584 | | } |
585 | | |
586 | | impl fmt::Debug for VerifiedHeader { |
587 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
588 | 0 | f.debug_tuple("VerifiedHeader") |
589 | 0 | .field(&hex::encode(&self.scale_encoded_header)) |
590 | 0 | .finish() |
591 | 0 | } Unexecuted instantiation: _RNvXs0_NtNtNtCsjlkOsLH0Zfj_7smoldot5chain11blocks_tree6verifyNtB5_14VerifiedHeaderNtNtCs1p5UDGgVI4d_4core3fmt5Debug3fmt Unexecuted instantiation: _RNvXs0_NtNtNtCsc1ywvx6YAnK_7smoldot5chain11blocks_tree6verifyNtB5_14VerifiedHeaderNtNtCs1p5UDGgVI4d_4core3fmt5Debug3fmt |
592 | | } |
593 | | |
594 | | /// See [`NonFinalizedTree::verify_header`]. |
595 | | #[derive(Debug)] |
596 | | pub enum HeaderVerifySuccess { |
597 | | /// Block is already known. |
598 | | Duplicate, |
599 | | /// Block wasn't known and has been successfully verified. |
600 | | Verified { |
601 | | /// Header that has been verified. Can be passed to |
602 | | /// [`NonFinalizedTree::insert_verified_header`]. |
603 | | verified_header: VerifiedHeader, |
604 | | /// True if the verified block will become the new "best" block after being inserted. |
605 | | is_new_best: bool, |
606 | | }, |
607 | | } |
608 | | |
609 | | /// Error that can happen when verifying a block header. |
610 | | // TODO: some of these errors are redundant with verify::header_only::Error |
611 | | #[derive(Debug, derive_more::Display, derive_more::Error)] |
612 | | pub enum HeaderVerifyError { |
613 | | /// Error while decoding the header. |
614 | | #[display("Error while decoding the header: {_0}")] |
615 | | InvalidHeader(header::Error), |
616 | | /// Block can't be verified as it uses an unknown consensus engine. |
617 | | UnknownConsensusEngine, |
618 | | /// Block uses a different consensus than the rest of the chain. |
619 | | ConsensusMismatch, |
620 | | /// The parent of the block isn't known. |
621 | | #[display("The parent of the block isn't known.")] |
622 | | BadParent { |
623 | | /// Hash of the parent block in question. |
624 | | parent_hash: [u8; 32], |
625 | | }, |
626 | | /// The block verification has failed. The block is invalid and should be thrown away. |
627 | | #[display("{_0}")] |
628 | | VerificationFailed(verify::header_only::Error), |
629 | | } |