Skip to main content

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