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		finalized_block_parent_hash: Block::Hash,
257	) -> std::result::Result<DisplacedLeavesAfterFinalization<Block>, Error> {
258		// There are no forks at genesis.
259		if finalized_block_number.is_zero() {
260			return Ok(DisplacedLeavesAfterFinalization::default());
261		}
262
263		let leaves = self.leaves()?;
264
265		let now = std::time::Instant::now();
266		debug!(
267			target: crate::LOG_TARGET,
268			?leaves,
269			?finalized_block_hash,
270			?finalized_block_number,
271			"Checking for displaced leaves after finalization."
272		);
273
274		// Store hashes of finalized blocks for quick checking later, the last block is the
275		// finalized one
276		let mut finalized_chain = VecDeque::new();
277		finalized_chain.push_front(MinimalBlockMetadata {
278			number: finalized_block_number,
279			hash: finalized_block_hash,
280			parent: finalized_block_parent_hash,
281		});
282
283		// Local cache is a performance optimization in case of finalized block deep below the
284		// tip of the chain with a lot of leaves above finalized block
285		let mut local_cache = HashMap::<Block::Hash, MinimalBlockMetadata<Block>>::new();
286
287		let mut result = DisplacedLeavesAfterFinalization {
288			displaced_leaves: Vec::with_capacity(leaves.len()),
289			displaced_blocks: Vec::with_capacity(leaves.len()),
290		};
291
292		let mut displaced_blocks_candidates = Vec::new();
293
294		let genesis_hash = self.info().genesis_hash;
295
296		for leaf_hash in leaves {
297			let mut current_header_metadata =
298				MinimalBlockMetadata::from(&self.header_metadata(leaf_hash).map_err(|err| {
299					debug!(
300						target: crate::LOG_TARGET,
301						?leaf_hash,
302						?err,
303						elapsed = ?now.elapsed(),
304						"Failed to fetch leaf header.",
305					);
306					err
307				})?);
308			let leaf_number = current_header_metadata.number;
309
310			// The genesis block is part of the canonical chain.
311			if leaf_hash == genesis_hash {
312				result.displaced_leaves.push((leaf_number, leaf_hash));
313				debug!(
314					target: crate::LOG_TARGET,
315					?leaf_hash,
316					elapsed = ?now.elapsed(),
317					"Added genesis leaf to displaced leaves."
318				);
319				continue;
320			}
321
322			debug!(
323				target: crate::LOG_TARGET,
324				?leaf_number,
325				?leaf_hash,
326				elapsed = ?now.elapsed(),
327				"Handle displaced leaf.",
328			);
329
330			// Collect all block hashes until the height of the finalized block
331			displaced_blocks_candidates.clear();
332			while current_header_metadata.number > finalized_block_number {
333				displaced_blocks_candidates.push(current_header_metadata.hash);
334
335				let parent_hash = current_header_metadata.parent;
336				match local_cache.get(&parent_hash) {
337					Some(metadata_header) => {
338						current_header_metadata = *metadata_header;
339					},
340					None => {
341						current_header_metadata = MinimalBlockMetadata::from(
342							&self.header_metadata(parent_hash).map_err(|err| {
343								debug!(
344									target: crate::LOG_TARGET,
345									?err,
346									?parent_hash,
347									?leaf_hash,
348									elapsed = ?now.elapsed(),
349									"Failed to fetch parent header during leaf tracking.",
350								);
351
352								err
353							})?,
354						);
355						// Cache locally in case more branches above finalized block reference
356						// the same block hash
357						local_cache.insert(parent_hash, current_header_metadata);
358					},
359				}
360			}
361
362			// If points back to the finalized header then nothing left to do, this leaf will be
363			// checked again later
364			if current_header_metadata.hash == finalized_block_hash {
365				debug!(
366					target: crate::LOG_TARGET,
367					?leaf_hash,
368					elapsed = ?now.elapsed(),
369					"Leaf points to the finalized header, skipping for now.",
370				);
371
372				continue;
373			}
374
375			// We reuse `displaced_blocks_candidates` to store the current metadata.
376			// This block is not displaced if there is a gap in the ancestry. We
377			// check for this gap later.
378			displaced_blocks_candidates.push(current_header_metadata.hash);
379
380			debug!(
381				target: crate::LOG_TARGET,
382				current_hash = ?current_header_metadata.hash,
383				current_num = ?current_header_metadata.number,
384				?finalized_block_number,
385				elapsed = ?now.elapsed(),
386				"Looking for path from finalized block number to current leaf number"
387			);
388
389			// Collect the rest of the displaced blocks of leaf branch
390			for distance_from_finalized in 1_u32.. {
391				// Find block at `distance_from_finalized` from finalized block
392				let (finalized_chain_block_number, finalized_chain_block_hash) =
393					match finalized_chain.iter().rev().nth(distance_from_finalized as usize) {
394						Some(header) => (header.number, header.hash),
395						None => {
396							let to_fetch = finalized_chain.front().expect("Not empty; qed");
397							let metadata = match self.header_metadata(to_fetch.parent) {
398								Ok(metadata) => metadata,
399								Err(Error::UnknownBlock(_)) => {
400									debug!(
401										target: crate::LOG_TARGET,
402										distance_from_finalized,
403										hash = ?to_fetch.parent,
404										number = ?to_fetch.number,
405										elapsed = ?now.elapsed(),
406										"Tried to fetch unknown block, block ancestry has gaps."
407									);
408									break;
409								},
410								Err(err) => {
411									debug!(
412										target: crate::LOG_TARGET,
413										hash = ?to_fetch.parent,
414										number = ?to_fetch.number,
415										?err,
416										elapsed = ?now.elapsed(),
417										"Failed to fetch header for parent hash.",
418									);
419									return Err(err);
420								},
421							};
422							let metadata = MinimalBlockMetadata::from(&metadata);
423							let result = (metadata.number, metadata.hash);
424							finalized_chain.push_front(metadata);
425							result
426						},
427					};
428
429				if current_header_metadata.hash == finalized_chain_block_hash {
430					// Found the block on the finalized chain, nothing left to do
431					result.displaced_leaves.push((leaf_number, leaf_hash));
432
433					debug!(
434						target: crate::LOG_TARGET,
435						?leaf_hash,
436						elapsed = ?now.elapsed(),
437						"Leaf is ancestor of finalized block."
438					);
439					break;
440				}
441
442				if current_header_metadata.number <= finalized_chain_block_number {
443					// Skip more blocks until we get all blocks on finalized chain until the height
444					// of the parent block
445					continue;
446				}
447
448				let parent_hash = current_header_metadata.parent;
449				if finalized_chain_block_hash == parent_hash {
450					// Reached finalized chain, nothing left to do
451					result.displaced_blocks.extend(displaced_blocks_candidates.drain(..));
452					result.displaced_leaves.push((leaf_number, leaf_hash));
453
454					debug!(
455						target: crate::LOG_TARGET,
456						?leaf_hash,
457						elapsed = ?now.elapsed(),
458						"Found displaced leaf."
459					);
460					break;
461				}
462
463				// Store displaced block and look deeper for block on finalized chain
464				debug!(
465					target: crate::LOG_TARGET,
466					?parent_hash,
467					elapsed = ?now.elapsed(),
468					"Found displaced block. Looking further.",
469				);
470				displaced_blocks_candidates.push(parent_hash);
471				current_header_metadata = MinimalBlockMetadata::from(
472					&self.header_metadata(parent_hash).map_err(|err| {
473						debug!(
474							target: crate::LOG_TARGET,
475							?err,
476							?parent_hash,
477							elapsed = ?now.elapsed(),
478							"Failed to fetch header for parent during displaced block collection",
479						);
480						err
481					})?,
482				);
483			}
484		}
485
486		// There could be duplicates shared by multiple branches, clean them up
487		result.displaced_blocks.sort_unstable();
488		result.displaced_blocks.dedup();
489
490		debug!(
491			target: crate::LOG_TARGET,
492			%finalized_block_hash,
493			?finalized_block_number,
494			?result,
495			elapsed = ?now.elapsed(),
496			"Finished checking for displaced leaves after finalization.",
497		);
498
499		return Ok(result);
500	}
501}
502
503/// Result of  [`Backend::displaced_leaves_after_finalizing`].
504#[derive(Clone, Debug)]
505pub struct DisplacedLeavesAfterFinalization<Block: BlockT> {
506	/// A list of hashes and block numbers of displaced leaves.
507	pub displaced_leaves: Vec<(NumberFor<Block>, Block::Hash)>,
508
509	/// A list of hashes displaced blocks from all displaced leaves.
510	pub displaced_blocks: Vec<Block::Hash>,
511}
512
513impl<Block: BlockT> Default for DisplacedLeavesAfterFinalization<Block> {
514	fn default() -> Self {
515		Self { displaced_leaves: Vec::new(), displaced_blocks: Vec::new() }
516	}
517}
518
519impl<Block: BlockT> DisplacedLeavesAfterFinalization<Block> {
520	/// Returns a collection of hashes for the displaced leaves.
521	pub fn hashes(&self) -> impl Iterator<Item = Block::Hash> + '_ {
522		self.displaced_leaves.iter().map(|(_, hash)| *hash)
523	}
524}
525
526/// Represents the type of block gaps that may result from either warp sync or fast sync.
527#[derive(Debug, Clone, Copy, Eq, PartialEq, Encode, Decode)]
528pub enum BlockGapType {
529	/// Both the header and body are missing, as a result of warp sync.
530	MissingHeaderAndBody,
531	/// The block body is missing, as a result of fast sync.
532	MissingBody,
533}
534
535/// Represents the block gap resulted by warp sync or fast sync.
536///
537/// A block gap is a range of blocks where either the bodies, or both headers and bodies are
538/// missing.
539#[derive(Debug, Clone, Copy, Eq, PartialEq, Encode, Decode)]
540pub struct BlockGap<N> {
541	/// The starting block number of the gap (inclusive).
542	pub start: N,
543	/// The ending block number of the gap (inclusive).
544	pub end: N,
545	/// The type of gap.
546	pub gap_type: BlockGapType,
547}
548
549/// Blockchain info
550#[derive(Debug, Eq, PartialEq, Clone)]
551pub struct Info<Block: BlockT> {
552	/// Best block hash.
553	pub best_hash: Block::Hash,
554	/// Best block number.
555	pub best_number: <<Block as BlockT>::Header as HeaderT>::Number,
556	/// Genesis block hash.
557	pub genesis_hash: Block::Hash,
558	/// The head of the finalized chain.
559	pub finalized_hash: Block::Hash,
560	/// Last finalized block number.
561	pub finalized_number: <<Block as BlockT>::Header as HeaderT>::Number,
562	/// Last finalized state.
563	pub finalized_state: Option<(Block::Hash, <<Block as BlockT>::Header as HeaderT>::Number)>,
564	/// Number of concurrent leave forks.
565	pub number_leaves: usize,
566	/// Missing blocks after warp sync or fast sync.
567	pub block_gap: Option<BlockGap<NumberFor<Block>>>,
568}
569
570/// Block status.
571#[derive(Debug, Clone, Copy, PartialEq, Eq)]
572pub enum BlockStatus {
573	/// Already in the blockchain.
574	InChain,
575	/// Not in the queue or the blockchain.
576	Unknown,
577}