Coverage Report

Created: 2025-07-01 09:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/__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
}