1use codec::{Decode, Encode};
10use parking_lot::RwLock;
11use std::collections::{btree_set::BTreeSet, HashMap, VecDeque};
12use subsoil::runtime::{
13 generic::BlockId,
14 traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero},
15 Justifications,
16};
17use tracing::{debug, warn};
18
19use super::{
20 error::{Error, Result},
21 header_metadata::HeaderMetadata,
22 tree_route, CachedHeaderMetadata,
23};
24
25pub trait HeaderBackend<Block: BlockT>: Send + Sync {
27 fn header(&self, hash: Block::Hash) -> Result<Option<Block::Header>>;
29 fn info(&self) -> Info<Block>;
31 fn status(&self, hash: Block::Hash) -> Result<BlockStatus>;
33 fn number(
35 &self,
36 hash: Block::Hash,
37 ) -> Result<Option<<<Block as BlockT>::Header as HeaderT>::Number>>;
38 fn hash(&self, number: NumberFor<Block>) -> Result<Option<Block::Hash>>;
40
41 fn block_hash_from_id(&self, id: &BlockId<Block>) -> Result<Option<Block::Hash>> {
43 match *id {
44 BlockId::Hash(h) => Ok(Some(h)),
45 BlockId::Number(n) => self.hash(n),
46 }
47 }
48
49 fn block_number_from_id(&self, id: &BlockId<Block>) -> Result<Option<NumberFor<Block>>> {
51 match *id {
52 BlockId::Hash(h) => self.number(h),
53 BlockId::Number(n) => Ok(Some(n)),
54 }
55 }
56
57 fn expect_header(&self, hash: Block::Hash) -> Result<Block::Header> {
59 self.header(hash)?
60 .ok_or_else(|| Error::UnknownBlock(format!("Expect header: {}", hash)))
61 }
62
63 fn expect_block_number_from_id(&self, id: &BlockId<Block>) -> Result<NumberFor<Block>> {
66 self.block_number_from_id(id).and_then(|n| {
67 n.ok_or_else(|| Error::UnknownBlock(format!("Expect block number from id: {}", id)))
68 })
69 }
70
71 fn expect_block_hash_from_id(&self, id: &BlockId<Block>) -> Result<Block::Hash> {
74 self.block_hash_from_id(id).and_then(|h| {
75 h.ok_or_else(|| Error::UnknownBlock(format!("Expect block hash from id: {}", id)))
76 })
77 }
78}
79
80pub trait ForkBackend<Block: BlockT>:
82 HeaderMetadata<Block> + HeaderBackend<Block> + Send + Sync
83{
84 fn expand_forks(
92 &self,
93 fork_heads: &[Block::Hash],
94 ) -> std::result::Result<BTreeSet<Block::Hash>, Error> {
95 let mut expanded_forks = BTreeSet::new();
96 for fork_head in fork_heads {
97 match tree_route(self, *fork_head, self.info().finalized_hash) {
98 Ok(tree_route) => {
99 for block in tree_route.retracted() {
100 expanded_forks.insert(block.hash);
101 }
102 continue;
103 },
104 Err(_) => {
105 },
107 }
108 }
109
110 Ok(expanded_forks)
111 }
112}
113
114impl<Block, T> ForkBackend<Block> for T
115where
116 Block: BlockT,
117 T: HeaderMetadata<Block> + HeaderBackend<Block> + Send + Sync,
118{
119}
120
121struct MinimalBlockMetadata<Block: BlockT> {
122 number: NumberFor<Block>,
123 hash: Block::Hash,
124 parent: Block::Hash,
125}
126
127impl<Block> Clone for MinimalBlockMetadata<Block>
128where
129 Block: BlockT,
130{
131 fn clone(&self) -> Self {
132 Self { number: self.number, hash: self.hash, parent: self.parent }
133 }
134}
135
136impl<Block> Copy for MinimalBlockMetadata<Block> where Block: BlockT {}
137
138impl<Block> From<&CachedHeaderMetadata<Block>> for MinimalBlockMetadata<Block>
139where
140 Block: BlockT,
141{
142 fn from(value: &CachedHeaderMetadata<Block>) -> Self {
143 Self { number: value.number, hash: value.hash, parent: value.parent }
144 }
145}
146
147pub trait Backend<Block: BlockT>:
149 HeaderBackend<Block> + HeaderMetadata<Block, Error = Error>
150{
151 fn body(&self, hash: Block::Hash) -> Result<Option<Vec<<Block as BlockT>::Extrinsic>>>;
153 fn justifications(&self, hash: Block::Hash) -> Result<Option<Justifications>>;
155 fn last_finalized(&self) -> Result<Block::Hash>;
157
158 fn leaves(&self) -> Result<Vec<Block::Hash>>;
162
163 fn children(&self, parent_hash: Block::Hash) -> Result<Vec<Block::Hash>>;
165
166 fn longest_containing(
175 &self,
176 base_hash: Block::Hash,
177 import_lock: &RwLock<()>,
178 ) -> Result<Option<Block::Hash>> {
179 let Some(base_header) = self.header(base_hash)? else { return Ok(None) };
180
181 let leaves = {
182 let _import_guard = import_lock.read();
186 let info = self.info();
187 if info.finalized_number > *base_header.number() {
188 return Ok(None);
190 }
191 self.leaves()?
192 };
193
194 for leaf_hash in leaves {
196 let mut current_hash = leaf_hash;
197 loop {
199 if current_hash == base_hash {
200 return Ok(Some(leaf_hash));
201 }
202
203 let current_header = self
204 .header(current_hash)?
205 .ok_or_else(|| Error::MissingHeader(current_hash.to_string()))?;
206
207 if current_header.number() < base_header.number() {
209 break;
210 }
211
212 current_hash = *current_header.parent_hash();
213 }
214 }
215
216 warn!(
221 target: super::LOG_TARGET,
222 "Block {:?} exists in chain but not found when following all leaves backwards",
223 base_hash,
224 );
225
226 Ok(None)
227 }
228
229 fn indexed_transaction(&self, hash: Block::Hash) -> Result<Option<Vec<u8>>>;
232
233 fn has_indexed_transaction(&self, hash: Block::Hash) -> Result<bool> {
235 Ok(self.indexed_transaction(hash)?.is_some())
236 }
237
238 fn block_indexed_body(&self, hash: Block::Hash) -> Result<Option<Vec<Vec<u8>>>>;
239
240 fn displaced_leaves_after_finalizing(
242 &self,
243 finalized_block_hash: Block::Hash,
244 finalized_block_number: NumberFor<Block>,
245 finalized_block_parent_hash: Block::Hash,
246 ) -> std::result::Result<DisplacedLeavesAfterFinalization<Block>, Error> {
247 if finalized_block_number.is_zero() {
249 return Ok(DisplacedLeavesAfterFinalization::default());
250 }
251
252 let leaves = self.leaves()?;
253
254 let now = std::time::Instant::now();
255 debug!(
256 target: super::LOG_TARGET,
257 ?leaves,
258 ?finalized_block_hash,
259 ?finalized_block_number,
260 "Checking for displaced leaves after finalization."
261 );
262
263 let mut finalized_chain = VecDeque::new();
266 finalized_chain.push_front(MinimalBlockMetadata {
267 number: finalized_block_number,
268 hash: finalized_block_hash,
269 parent: finalized_block_parent_hash,
270 });
271
272 let mut local_cache = HashMap::<Block::Hash, MinimalBlockMetadata<Block>>::new();
275
276 let mut result = DisplacedLeavesAfterFinalization {
277 displaced_leaves: Vec::with_capacity(leaves.len()),
278 displaced_blocks: Vec::with_capacity(leaves.len()),
279 };
280
281 let mut displaced_blocks_candidates = Vec::new();
282
283 let genesis_hash = self.info().genesis_hash;
284
285 for leaf_hash in leaves {
286 let mut current_header_metadata =
287 MinimalBlockMetadata::from(&self.header_metadata(leaf_hash).map_err(|err| {
288 debug!(
289 target: super::LOG_TARGET,
290 ?leaf_hash,
291 ?err,
292 elapsed = ?now.elapsed(),
293 "Failed to fetch leaf header.",
294 );
295 err
296 })?);
297 let leaf_number = current_header_metadata.number;
298
299 if leaf_hash == genesis_hash {
301 result.displaced_leaves.push((leaf_number, leaf_hash));
302 debug!(
303 target: super::LOG_TARGET,
304 ?leaf_hash,
305 elapsed = ?now.elapsed(),
306 "Added genesis leaf to displaced leaves."
307 );
308 continue;
309 }
310
311 debug!(
312 target: super::LOG_TARGET,
313 ?leaf_number,
314 ?leaf_hash,
315 elapsed = ?now.elapsed(),
316 "Handle displaced leaf.",
317 );
318
319 displaced_blocks_candidates.clear();
321 while current_header_metadata.number > finalized_block_number {
322 displaced_blocks_candidates.push(current_header_metadata.hash);
323
324 let parent_hash = current_header_metadata.parent;
325 match local_cache.get(&parent_hash) {
326 Some(metadata_header) => {
327 current_header_metadata = *metadata_header;
328 },
329 None => {
330 current_header_metadata = MinimalBlockMetadata::from(
331 &self.header_metadata(parent_hash).map_err(|err| {
332 debug!(
333 target: super::LOG_TARGET,
334 ?err,
335 ?parent_hash,
336 ?leaf_hash,
337 elapsed = ?now.elapsed(),
338 "Failed to fetch parent header during leaf tracking.",
339 );
340
341 err
342 })?,
343 );
344 local_cache.insert(parent_hash, current_header_metadata);
347 },
348 }
349 }
350
351 if current_header_metadata.hash == finalized_block_hash {
354 debug!(
355 target: super::LOG_TARGET,
356 ?leaf_hash,
357 elapsed = ?now.elapsed(),
358 "Leaf points to the finalized header, skipping for now.",
359 );
360
361 continue;
362 }
363
364 displaced_blocks_candidates.push(current_header_metadata.hash);
368
369 debug!(
370 target: super::LOG_TARGET,
371 current_hash = ?current_header_metadata.hash,
372 current_num = ?current_header_metadata.number,
373 ?finalized_block_number,
374 elapsed = ?now.elapsed(),
375 "Looking for path from finalized block number to current leaf number"
376 );
377
378 for distance_from_finalized in 1_u32.. {
380 let (finalized_chain_block_number, finalized_chain_block_hash) =
382 match finalized_chain.iter().rev().nth(distance_from_finalized as usize) {
383 Some(header) => (header.number, header.hash),
384 None => {
385 let to_fetch = finalized_chain.front().expect("Not empty; qed");
386 let metadata = match self.header_metadata(to_fetch.parent) {
387 Ok(metadata) => metadata,
388 Err(Error::UnknownBlock(_)) => {
389 debug!(
390 target: super::LOG_TARGET,
391 distance_from_finalized,
392 hash = ?to_fetch.parent,
393 number = ?to_fetch.number,
394 elapsed = ?now.elapsed(),
395 "Tried to fetch unknown block, block ancestry has gaps."
396 );
397 break;
398 },
399 Err(err) => {
400 debug!(
401 target: super::LOG_TARGET,
402 hash = ?to_fetch.parent,
403 number = ?to_fetch.number,
404 ?err,
405 elapsed = ?now.elapsed(),
406 "Failed to fetch header for parent hash.",
407 );
408 return Err(err);
409 },
410 };
411 let metadata = MinimalBlockMetadata::from(&metadata);
412 let result = (metadata.number, metadata.hash);
413 finalized_chain.push_front(metadata);
414 result
415 },
416 };
417
418 if current_header_metadata.hash == finalized_chain_block_hash {
419 result.displaced_leaves.push((leaf_number, leaf_hash));
421
422 debug!(
423 target: super::LOG_TARGET,
424 ?leaf_hash,
425 elapsed = ?now.elapsed(),
426 "Leaf is ancestor of finalized block."
427 );
428 break;
429 }
430
431 if current_header_metadata.number <= finalized_chain_block_number {
432 continue;
435 }
436
437 let parent_hash = current_header_metadata.parent;
438 if finalized_chain_block_hash == parent_hash {
439 result.displaced_blocks.extend(displaced_blocks_candidates.drain(..));
441 result.displaced_leaves.push((leaf_number, leaf_hash));
442
443 debug!(
444 target: super::LOG_TARGET,
445 ?leaf_hash,
446 elapsed = ?now.elapsed(),
447 "Found displaced leaf."
448 );
449 break;
450 }
451
452 debug!(
454 target: super::LOG_TARGET,
455 ?parent_hash,
456 elapsed = ?now.elapsed(),
457 "Found displaced block. Looking further.",
458 );
459 displaced_blocks_candidates.push(parent_hash);
460 current_header_metadata = MinimalBlockMetadata::from(
461 &self.header_metadata(parent_hash).map_err(|err| {
462 debug!(
463 target: super::LOG_TARGET,
464 ?err,
465 ?parent_hash,
466 elapsed = ?now.elapsed(),
467 "Failed to fetch header for parent during displaced block collection",
468 );
469 err
470 })?,
471 );
472 }
473 }
474
475 result.displaced_blocks.sort_unstable();
477 result.displaced_blocks.dedup();
478
479 debug!(
480 target: super::LOG_TARGET,
481 %finalized_block_hash,
482 ?finalized_block_number,
483 ?result,
484 elapsed = ?now.elapsed(),
485 "Finished checking for displaced leaves after finalization.",
486 );
487
488 return Ok(result);
489 }
490}
491
492#[derive(Clone, Debug)]
494pub struct DisplacedLeavesAfterFinalization<Block: BlockT> {
495 pub displaced_leaves: Vec<(NumberFor<Block>, Block::Hash)>,
497
498 pub displaced_blocks: Vec<Block::Hash>,
500}
501
502impl<Block: BlockT> Default for DisplacedLeavesAfterFinalization<Block> {
503 fn default() -> Self {
504 Self { displaced_leaves: Vec::new(), displaced_blocks: Vec::new() }
505 }
506}
507
508impl<Block: BlockT> DisplacedLeavesAfterFinalization<Block> {
509 pub fn hashes(&self) -> impl Iterator<Item = Block::Hash> + '_ {
511 self.displaced_leaves.iter().map(|(_, hash)| *hash)
512 }
513}
514
515#[derive(Debug, Clone, Copy, Eq, PartialEq, Encode, Decode)]
517pub enum BlockGapType {
518 MissingHeaderAndBody,
520 MissingBody,
522}
523
524#[derive(Debug, Clone, Copy, Eq, PartialEq, Encode, Decode)]
529pub struct BlockGap<N> {
530 pub start: N,
532 pub end: N,
534 pub gap_type: BlockGapType,
536}
537
538#[derive(Debug, Eq, PartialEq, Clone)]
540pub struct Info<Block: BlockT> {
541 pub best_hash: Block::Hash,
543 pub best_number: <<Block as BlockT>::Header as HeaderT>::Number,
545 pub genesis_hash: Block::Hash,
547 pub finalized_hash: Block::Hash,
549 pub finalized_number: <<Block as BlockT>::Header as HeaderT>::Number,
551 pub finalized_state: Option<(Block::Hash, <<Block as BlockT>::Header as HeaderT>::Number)>,
553 pub number_leaves: usize,
555 pub block_gap: Option<BlockGap<NumberFor<Block>>>,
557}
558
559#[derive(Debug, Clone, Copy, PartialEq, Eq)]
561pub enum BlockStatus {
562 InChain,
564 Unknown,
566}