sp_blockchain/
backend.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Substrate blockchain trait
19
20use codec::{Decode, Encode};
21use parking_lot::RwLock;
22use sp_runtime::{
23	generic::BlockId,
24	traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero},
25	Justifications,
26};
27use std::collections::{btree_set::BTreeSet, HashMap, VecDeque};
28use tracing::{debug, warn};
29
30use crate::{
31	error::{Error, Result},
32	header_metadata::HeaderMetadata,
33	tree_route, CachedHeaderMetadata,
34};
35
36/// Blockchain database header backend. Does not perform any validation.
37pub trait HeaderBackend<Block: BlockT>: Send + Sync {
38	/// Get block header. Returns `None` if block is not found.
39	fn header(&self, hash: Block::Hash) -> Result<Option<Block::Header>>;
40	/// Get blockchain info.
41	fn info(&self) -> Info<Block>;
42	/// Get block status.
43	fn status(&self, hash: Block::Hash) -> Result<BlockStatus>;
44	/// Get block number by hash. Returns `None` if the header is not in the chain.
45	fn number(
46		&self,
47		hash: Block::Hash,
48	) -> Result<Option<<<Block as BlockT>::Header as HeaderT>::Number>>;
49	/// Get block hash by number. Returns `None` if the header is not in the chain.
50	fn hash(&self, number: NumberFor<Block>) -> Result<Option<Block::Hash>>;
51
52	/// Convert an arbitrary block ID into a block hash.
53	fn block_hash_from_id(&self, id: &BlockId<Block>) -> Result<Option<Block::Hash>> {
54		match *id {
55			BlockId::Hash(h) => Ok(Some(h)),
56			BlockId::Number(n) => self.hash(n),
57		}
58	}
59
60	/// Convert an arbitrary block ID into a block hash.
61	fn block_number_from_id(&self, id: &BlockId<Block>) -> Result<Option<NumberFor<Block>>> {
62		match *id {
63			BlockId::Hash(h) => self.number(h),
64			BlockId::Number(n) => Ok(Some(n)),
65		}
66	}
67
68	/// Get block header. Returns `UnknownBlock` error if block is not found.
69	fn expect_header(&self, hash: Block::Hash) -> Result<Block::Header> {
70		self.header(hash)?
71			.ok_or_else(|| Error::UnknownBlock(format!("Expect header: {}", hash)))
72	}
73
74	/// Convert an arbitrary block ID into a block number. Returns `UnknownBlock` error if block is
75	/// not found.
76	fn expect_block_number_from_id(&self, id: &BlockId<Block>) -> Result<NumberFor<Block>> {
77		self.block_number_from_id(id).and_then(|n| {
78			n.ok_or_else(|| Error::UnknownBlock(format!("Expect block number from id: {}", id)))
79		})
80	}
81
82	/// Convert an arbitrary block ID into a block hash. Returns `UnknownBlock` error if block is
83	/// not found.
84	fn expect_block_hash_from_id(&self, id: &BlockId<Block>) -> Result<Block::Hash> {
85		self.block_hash_from_id(id).and_then(|h| {
86			h.ok_or_else(|| Error::UnknownBlock(format!("Expect block hash from id: {}", id)))
87		})
88	}
89}
90
91/// Handles stale forks.
92pub trait ForkBackend<Block: BlockT>:
93	HeaderMetadata<Block> + HeaderBackend<Block> + Send + Sync
94{
95	/// Returns block hashes for provided fork heads. It skips the fork if when blocks are missing
96	/// (e.g. warp-sync) and internal `tree_route` function fails.
97	///
98	/// Example:
99	///  G --- A1 --- A2 --- A3 --- A4           ( < fork1 )
100	///                       \-----C4 --- C5    ( < fork2 )
101	/// We finalize A3 and call expand_fork(C5). Result = (C5,C4).
102	fn expand_forks(
103		&self,
104		fork_heads: &[Block::Hash],
105	) -> std::result::Result<BTreeSet<Block::Hash>, Error> {
106		let mut expanded_forks = BTreeSet::new();
107		for fork_head in fork_heads {
108			match tree_route(self, *fork_head, self.info().finalized_hash) {
109				Ok(tree_route) => {
110					for block in tree_route.retracted() {
111						expanded_forks.insert(block.hash);
112					}
113					continue;
114				},
115				Err(_) => {
116					// There are cases when blocks are missing (e.g. warp-sync).
117				},
118			}
119		}
120
121		Ok(expanded_forks)
122	}
123}
124
125impl<Block, T> ForkBackend<Block> for T
126where
127	Block: BlockT,
128	T: HeaderMetadata<Block> + HeaderBackend<Block> + Send + Sync,
129{
130}
131
132struct MinimalBlockMetadata<Block: BlockT> {
133	number: NumberFor<Block>,
134	hash: Block::Hash,
135	parent: Block::Hash,
136}
137
138impl<Block> Clone for MinimalBlockMetadata<Block>
139where
140	Block: BlockT,
141{
142	fn clone(&self) -> Self {
143		Self { number: self.number, hash: self.hash, parent: self.parent }
144	}
145}
146
147impl<Block> Copy for MinimalBlockMetadata<Block> where Block: BlockT {}
148
149impl<Block> From<&CachedHeaderMetadata<Block>> for MinimalBlockMetadata<Block>
150where
151	Block: BlockT,
152{
153	fn from(value: &CachedHeaderMetadata<Block>) -> Self {
154		Self { number: value.number, hash: value.hash, parent: value.parent }
155	}
156}
157
158/// Blockchain database backend. Does not perform any validation.
159pub trait Backend<Block: BlockT>:
160	HeaderBackend<Block> + HeaderMetadata<Block, Error = Error>
161{
162	/// Get block body. Returns `None` if block is not found.
163	fn body(&self, hash: Block::Hash) -> Result<Option<Vec<<Block as BlockT>::Extrinsic>>>;
164	/// Get block justifications. Returns `None` if no justification exists.
165	fn justifications(&self, hash: Block::Hash) -> Result<Option<Justifications>>;
166	/// Get last finalized block hash.
167	fn last_finalized(&self) -> Result<Block::Hash>;
168
169	/// Returns hashes of all blocks that are leaves of the block tree.
170	/// in other words, that have no children, are chain heads.
171	/// Results must be ordered best (longest, highest) chain first.
172	fn leaves(&self) -> Result<Vec<Block::Hash>>;
173
174	/// Return hashes of all blocks that are children of the block with `parent_hash`.
175	fn children(&self, parent_hash: Block::Hash) -> Result<Vec<Block::Hash>>;
176
177	/// Get the most recent block hash of the longest chain that contains
178	/// a block with the given `base_hash`.
179	///
180	/// The search space is always limited to blocks which are in the finalized
181	/// chain or descendants of it.
182	///
183	/// Returns `Ok(None)` if `base_hash` is not found in search space.
184	// TODO: document time complexity of this, see [#1444](https://github.com/paritytech/substrate/issues/1444)
185	fn longest_containing(
186		&self,
187		base_hash: Block::Hash,
188		import_lock: &RwLock<()>,
189	) -> Result<Option<Block::Hash>> {
190		let Some(base_header) = self.header(base_hash)? else { return Ok(None) };
191
192		let leaves = {
193			// ensure no blocks are imported during this code block.
194			// an import could trigger a reorg which could change the canonical chain.
195			// we depend on the canonical chain staying the same during this code block.
196			let _import_guard = import_lock.read();
197			let info = self.info();
198			if info.finalized_number > *base_header.number() {
199				// `base_header` is on a dead fork.
200				return Ok(None);
201			}
202			self.leaves()?
203		};
204
205		// for each chain. longest chain first. shortest last
206		for leaf_hash in leaves {
207			let mut current_hash = leaf_hash;
208			// go backwards through the chain (via parent links)
209			loop {
210				if current_hash == base_hash {
211					return Ok(Some(leaf_hash));
212				}
213
214				let current_header = self
215					.header(current_hash)?
216					.ok_or_else(|| Error::MissingHeader(current_hash.to_string()))?;
217
218				// stop search in this chain once we go below the target's block number
219				if current_header.number() < base_header.number() {
220					break;
221				}
222
223				current_hash = *current_header.parent_hash();
224			}
225		}
226
227		// header may be on a dead fork -- the only leaves that are considered are
228		// those which can still be finalized.
229		//
230		// FIXME #1558 only issue this warning when not on a dead fork
231		warn!(
232			target: crate::LOG_TARGET,
233			"Block {:?} exists in chain but not found when following all leaves backwards",
234			base_hash,
235		);
236
237		Ok(None)
238	}
239
240	/// Get single indexed transaction by content hash. Note that this will only fetch transactions
241	/// that are indexed by the runtime with `storage_index_transaction`.
242	fn indexed_transaction(&self, hash: Block::Hash) -> Result<Option<Vec<u8>>>;
243
244	/// Check if indexed transaction exists.
245	fn has_indexed_transaction(&self, hash: Block::Hash) -> Result<bool> {
246		Ok(self.indexed_transaction(hash)?.is_some())
247	}
248
249	fn block_indexed_body(&self, hash: Block::Hash) -> Result<Option<Vec<Vec<u8>>>>;
250
251	/// Returns all leaves that will be displaced after the block finalization.
252	fn displaced_leaves_after_finalizing(
253		&self,
254		finalized_block_hash: Block::Hash,
255		finalized_block_number: NumberFor<Block>,
256	) -> std::result::Result<DisplacedLeavesAfterFinalization<Block>, Error> {
257		let leaves = self.leaves()?;
258
259		let now = std::time::Instant::now();
260		debug!(
261			target: crate::LOG_TARGET,
262			?leaves,
263			?finalized_block_hash,
264			?finalized_block_number,
265			"Checking for displaced leaves after finalization."
266		);
267
268		// If we have only one leaf there are no forks, and we can return early.
269		if finalized_block_number == Zero::zero() || leaves.len() == 1 {
270			return Ok(DisplacedLeavesAfterFinalization::default());
271		}
272
273		// Store hashes of finalized blocks for quick checking later, the last block is the
274		// finalized one
275		let mut finalized_chain = VecDeque::new();
276		let current_finalized = match self.header_metadata(finalized_block_hash) {
277			Ok(metadata) => metadata,
278			Err(Error::UnknownBlock(_)) => {
279				debug!(
280					target: crate::LOG_TARGET,
281					hash = ?finalized_block_hash,
282					elapsed = ?now.elapsed(),
283					"Tried to fetch unknown block, block ancestry has gaps.",
284				);
285				return Ok(DisplacedLeavesAfterFinalization::default());
286			},
287			Err(e) => {
288				debug!(
289					target: crate::LOG_TARGET,
290					hash = ?finalized_block_hash,
291					err = ?e,
292					elapsed = ?now.elapsed(),
293					"Failed to fetch block.",
294				);
295				return Err(e);
296			},
297		};
298		finalized_chain.push_front(MinimalBlockMetadata::from(&current_finalized));
299
300		// Local cache is a performance optimization in case of finalized block deep below the
301		// tip of the chain with a lot of leaves above finalized block
302		let mut local_cache = HashMap::<Block::Hash, MinimalBlockMetadata<Block>>::new();
303
304		let mut result = DisplacedLeavesAfterFinalization {
305			displaced_leaves: Vec::with_capacity(leaves.len()),
306			displaced_blocks: Vec::with_capacity(leaves.len()),
307		};
308
309		let mut displaced_blocks_candidates = Vec::new();
310
311		let genesis_hash = self.info().genesis_hash;
312
313		for leaf_hash in leaves {
314			let mut current_header_metadata =
315				MinimalBlockMetadata::from(&self.header_metadata(leaf_hash).map_err(|err| {
316					debug!(
317						target: crate::LOG_TARGET,
318						?leaf_hash,
319						?err,
320						elapsed = ?now.elapsed(),
321						"Failed to fetch leaf header.",
322					);
323					err
324				})?);
325			let leaf_number = current_header_metadata.number;
326
327			// The genesis block is part of the canonical chain.
328			if leaf_hash == genesis_hash {
329				result.displaced_leaves.push((leaf_number, leaf_hash));
330				debug!(
331					target: crate::LOG_TARGET,
332					?leaf_hash,
333					elapsed = ?now.elapsed(),
334					"Added genesis leaf to displaced leaves."
335				);
336				continue;
337			}
338
339			debug!(
340				target: crate::LOG_TARGET,
341				?leaf_number,
342				?leaf_hash,
343				elapsed = ?now.elapsed(),
344				"Handle displaced leaf.",
345			);
346
347			// Collect all block hashes until the height of the finalized block
348			displaced_blocks_candidates.clear();
349			while current_header_metadata.number > finalized_block_number {
350				displaced_blocks_candidates.push(current_header_metadata.hash);
351
352				let parent_hash = current_header_metadata.parent;
353				match local_cache.get(&parent_hash) {
354					Some(metadata_header) => {
355						current_header_metadata = *metadata_header;
356					},
357					None => {
358						current_header_metadata = MinimalBlockMetadata::from(
359							&self.header_metadata(parent_hash).map_err(|err| {
360								debug!(
361									target: crate::LOG_TARGET,
362									?err,
363									?parent_hash,
364									?leaf_hash,
365									elapsed = ?now.elapsed(),
366									"Failed to fetch parent header during leaf tracking.",
367								);
368
369								err
370							})?,
371						);
372						// Cache locally in case more branches above finalized block reference
373						// the same block hash
374						local_cache.insert(parent_hash, current_header_metadata);
375					},
376				}
377			}
378
379			// If points back to the finalized header then nothing left to do, this leaf will be
380			// checked again later
381			if current_header_metadata.hash == finalized_block_hash {
382				debug!(
383					target: crate::LOG_TARGET,
384					?leaf_hash,
385					elapsed = ?now.elapsed(),
386					"Leaf points to the finalized header, skipping for now.",
387				);
388
389				continue;
390			}
391
392			// We reuse `displaced_blocks_candidates` to store the current metadata.
393			// This block is not displaced if there is a gap in the ancestry. We
394			// check for this gap later.
395			displaced_blocks_candidates.push(current_header_metadata.hash);
396
397			debug!(
398				target: crate::LOG_TARGET,
399				current_hash = ?current_header_metadata.hash,
400				current_num = ?current_header_metadata.number,
401				?finalized_block_number,
402				elapsed = ?now.elapsed(),
403				"Looking for path from finalized block number to current leaf number"
404			);
405
406			// Collect the rest of the displaced blocks of leaf branch
407			for distance_from_finalized in 1_u32.. {
408				// Find block at `distance_from_finalized` from finalized block
409				let (finalized_chain_block_number, finalized_chain_block_hash) =
410					match finalized_chain.iter().rev().nth(distance_from_finalized as usize) {
411						Some(header) => (header.number, header.hash),
412						None => {
413							let to_fetch = finalized_chain.front().expect("Not empty; qed");
414							let metadata = match self.header_metadata(to_fetch.parent) {
415								Ok(metadata) => metadata,
416								Err(Error::UnknownBlock(_)) => {
417									debug!(
418										target: crate::LOG_TARGET,
419										distance_from_finalized,
420										hash = ?to_fetch.parent,
421										number = ?to_fetch.number,
422										elapsed = ?now.elapsed(),
423										"Tried to fetch unknown block, block ancestry has gaps."
424									);
425									break;
426								},
427								Err(err) => {
428									debug!(
429										target: crate::LOG_TARGET,
430										hash = ?to_fetch.parent,
431										number = ?to_fetch.number,
432										?err,
433										elapsed = ?now.elapsed(),
434										"Failed to fetch header for parent hash.",
435									);
436									return Err(err);
437								},
438							};
439							let metadata = MinimalBlockMetadata::from(&metadata);
440							let result = (metadata.number, metadata.hash);
441							finalized_chain.push_front(metadata);
442							result
443						},
444					};
445
446				if current_header_metadata.hash == finalized_chain_block_hash {
447					// Found the block on the finalized chain, nothing left to do
448					result.displaced_leaves.push((leaf_number, leaf_hash));
449
450					debug!(
451						target: crate::LOG_TARGET,
452						?leaf_hash,
453						elapsed = ?now.elapsed(),
454						"Leaf is ancestor of finalized block."
455					);
456					break;
457				}
458
459				if current_header_metadata.number <= finalized_chain_block_number {
460					// Skip more blocks until we get all blocks on finalized chain until the height
461					// of the parent block
462					continue;
463				}
464
465				let parent_hash = current_header_metadata.parent;
466				if finalized_chain_block_hash == parent_hash {
467					// Reached finalized chain, nothing left to do
468					result.displaced_blocks.extend(displaced_blocks_candidates.drain(..));
469					result.displaced_leaves.push((leaf_number, leaf_hash));
470
471					debug!(
472						target: crate::LOG_TARGET,
473						?leaf_hash,
474						elapsed = ?now.elapsed(),
475						"Found displaced leaf."
476					);
477					break;
478				}
479
480				// Store displaced block and look deeper for block on finalized chain
481				debug!(
482					target: crate::LOG_TARGET,
483					?parent_hash,
484					elapsed = ?now.elapsed(),
485					"Found displaced block. Looking further.",
486				);
487				displaced_blocks_candidates.push(parent_hash);
488				current_header_metadata = MinimalBlockMetadata::from(
489					&self.header_metadata(parent_hash).map_err(|err| {
490						debug!(
491							target: crate::LOG_TARGET,
492							?err,
493							?parent_hash,
494							elapsed = ?now.elapsed(),
495							"Failed to fetch header for parent during displaced block collection",
496						);
497						err
498					})?,
499				);
500			}
501		}
502
503		// There could be duplicates shared by multiple branches, clean them up
504		result.displaced_blocks.sort_unstable();
505		result.displaced_blocks.dedup();
506
507		debug!(
508			target: crate::LOG_TARGET,
509			%finalized_block_hash,
510			?finalized_block_number,
511			?result,
512			elapsed = ?now.elapsed(),
513			"Finished checking for displaced leaves after finalization.",
514		);
515
516		return Ok(result);
517	}
518}
519
520/// Result of  [`Backend::displaced_leaves_after_finalizing`].
521#[derive(Clone, Debug)]
522pub struct DisplacedLeavesAfterFinalization<Block: BlockT> {
523	/// A list of hashes and block numbers of displaced leaves.
524	pub displaced_leaves: Vec<(NumberFor<Block>, Block::Hash)>,
525
526	/// A list of hashes displaced blocks from all displaced leaves.
527	pub displaced_blocks: Vec<Block::Hash>,
528}
529
530impl<Block: BlockT> Default for DisplacedLeavesAfterFinalization<Block> {
531	fn default() -> Self {
532		Self { displaced_leaves: Vec::new(), displaced_blocks: Vec::new() }
533	}
534}
535
536impl<Block: BlockT> DisplacedLeavesAfterFinalization<Block> {
537	/// Returns a collection of hashes for the displaced leaves.
538	pub fn hashes(&self) -> impl Iterator<Item = Block::Hash> + '_ {
539		self.displaced_leaves.iter().map(|(_, hash)| *hash)
540	}
541}
542
543/// Represents the type of block gaps that may result from either warp sync or fast sync.
544#[derive(Debug, Clone, Copy, Eq, PartialEq, Encode, Decode)]
545pub enum BlockGapType {
546	/// Both the header and body are missing, as a result of warp sync.
547	MissingHeaderAndBody,
548	/// The block body is missing, as a result of fast sync.
549	MissingBody,
550}
551
552/// Represents the block gap resulted by warp sync or fast sync.
553///
554/// A block gap is a range of blocks where either the bodies, or both headers and bodies are
555/// missing.
556#[derive(Debug, Clone, Copy, Eq, PartialEq, Encode, Decode)]
557pub struct BlockGap<N> {
558	/// The starting block number of the gap (inclusive).
559	pub start: N,
560	/// The ending block number of the gap (inclusive).
561	pub end: N,
562	/// The type of gap.
563	pub gap_type: BlockGapType,
564}
565
566/// Blockchain info
567#[derive(Debug, Eq, PartialEq, Clone)]
568pub struct Info<Block: BlockT> {
569	/// Best block hash.
570	pub best_hash: Block::Hash,
571	/// Best block number.
572	pub best_number: <<Block as BlockT>::Header as HeaderT>::Number,
573	/// Genesis block hash.
574	pub genesis_hash: Block::Hash,
575	/// The head of the finalized chain.
576	pub finalized_hash: Block::Hash,
577	/// Last finalized block number.
578	pub finalized_number: <<Block as BlockT>::Header as HeaderT>::Number,
579	/// Last finalized state.
580	pub finalized_state: Option<(Block::Hash, <<Block as BlockT>::Header as HeaderT>::Number)>,
581	/// Number of concurrent leave forks.
582	pub number_leaves: usize,
583	/// Missing blocks after warp sync or fast sync.
584	pub block_gap: Option<BlockGap<NumberFor<Block>>>,
585}
586
587/// Block status.
588#[derive(Debug, Clone, Copy, PartialEq, Eq)]
589pub enum BlockStatus {
590	/// Already in the blockchain.
591	InChain,
592	/// Not in the queue or the blockchain.
593	Unknown,
594}