referrerpolicy=no-referrer-when-downgrade

sc_consensus/
longest_chain.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19//! Longest chain implementation
20
21use sc_client_api::backend;
22use sp_blockchain::{Backend, HeaderBackend};
23use sp_consensus::{Error as ConsensusError, SelectChain};
24use sp_runtime::traits::{Block as BlockT, Header, NumberFor};
25use std::{marker::PhantomData, sync::Arc};
26
27/// Implement Longest Chain Select implementation
28/// where 'longest' is defined as the highest number of blocks
29pub struct LongestChain<B, Block> {
30	backend: Arc<B>,
31	_phantom: PhantomData<Block>,
32}
33
34impl<B, Block> Clone for LongestChain<B, Block> {
35	fn clone(&self) -> Self {
36		let backend = self.backend.clone();
37		LongestChain { backend, _phantom: Default::default() }
38	}
39}
40
41impl<B, Block> LongestChain<B, Block>
42where
43	B: backend::Backend<Block>,
44	Block: BlockT,
45{
46	/// Instantiate a new LongestChain for Backend B
47	pub fn new(backend: Arc<B>) -> Self {
48		LongestChain { backend, _phantom: Default::default() }
49	}
50
51	fn best_hash(&self) -> sp_blockchain::Result<<Block as BlockT>::Hash> {
52		let info = self.backend.blockchain().info();
53		let import_lock = self.backend.get_import_lock();
54		let best_hash = self
55			.backend
56			.blockchain()
57			.longest_containing(info.best_hash, import_lock)?
58			.unwrap_or(info.best_hash);
59		Ok(best_hash)
60	}
61
62	fn best_header(&self) -> sp_blockchain::Result<<Block as BlockT>::Header> {
63		let best_hash = self.best_hash()?;
64		Ok(self
65			.backend
66			.blockchain()
67			.header(best_hash)?
68			.expect("given block hash was fetched from block in db; qed"))
69	}
70
71	/// Returns the highest descendant of the given block that is a valid
72	/// candidate to be finalized.
73	///
74	/// In this context, being a valid target means being an ancestor of
75	/// the best chain according to the `best_header` method.
76	///
77	/// If `maybe_max_number` is `Some(max_block_number)` the search is
78	/// limited to block `number <= max_block_number`. In other words
79	/// as if there were no blocks greater than `max_block_number`.
80	fn finality_target(
81		&self,
82		base_hash: Block::Hash,
83		maybe_max_number: Option<NumberFor<Block>>,
84	) -> sp_blockchain::Result<Block::Hash> {
85		use sp_blockchain::Error::{Application, MissingHeader};
86		let blockchain = self.backend.blockchain();
87
88		let mut current_head = self.best_header()?;
89		let mut best_hash = current_head.hash();
90
91		let base_header = blockchain
92			.header(base_hash)?
93			.ok_or_else(|| MissingHeader(base_hash.to_string()))?;
94		let base_number = *base_header.number();
95
96		if let Some(max_number) = maybe_max_number {
97			if max_number < base_number {
98				let msg = format!(
99					"Requested a finality target using max number {} below the base number {}",
100					max_number, base_number
101				);
102				return Err(Application(msg.into()))
103			}
104
105			while current_head.number() > &max_number {
106				best_hash = *current_head.parent_hash();
107				current_head = blockchain
108					.header(best_hash)?
109					.ok_or_else(|| MissingHeader(format!("{best_hash:?}")))?;
110			}
111		}
112
113		while current_head.hash() != base_hash {
114			if *current_head.number() < base_number {
115				let msg = format!(
116					"Requested a finality target using a base {:?} not in the best chain {:?}",
117					base_hash, best_hash,
118				);
119				return Err(Application(msg.into()))
120			}
121			let current_hash = *current_head.parent_hash();
122			current_head = blockchain
123				.header(current_hash)?
124				.ok_or_else(|| MissingHeader(format!("{best_hash:?}")))?;
125		}
126
127		Ok(best_hash)
128	}
129
130	fn leaves(&self) -> Result<Vec<<Block as BlockT>::Hash>, sp_blockchain::Error> {
131		self.backend.blockchain().leaves()
132	}
133}
134
135#[async_trait::async_trait]
136impl<B, Block> SelectChain<Block> for LongestChain<B, Block>
137where
138	B: backend::Backend<Block>,
139	Block: BlockT,
140{
141	async fn leaves(&self) -> Result<Vec<<Block as BlockT>::Hash>, ConsensusError> {
142		LongestChain::leaves(self).map_err(|e| ConsensusError::ChainLookup(e.to_string()))
143	}
144
145	async fn best_chain(&self) -> Result<<Block as BlockT>::Header, ConsensusError> {
146		LongestChain::best_header(self).map_err(|e| ConsensusError::ChainLookup(e.to_string()))
147	}
148
149	async fn finality_target(
150		&self,
151		base_hash: Block::Hash,
152		maybe_max_number: Option<NumberFor<Block>>,
153	) -> Result<Block::Hash, ConsensusError> {
154		LongestChain::finality_target(self, base_hash, maybe_max_number)
155			.map_err(|e| ConsensusError::ChainLookup(e.to_string()))
156	}
157}