referrerpolicy=no-referrer-when-downgrade

sc_consensus_aura/
authorities_tracker.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//! Module implementing the logic for verifying and importing AuRa blocks.
20
21use std::{fmt::Debug, sync::Arc};
22
23use codec::Codec;
24use fork_tree::ForkTree;
25use parking_lot::RwLock;
26use sp_api::ProvideRuntimeApi;
27use sp_blockchain::{HeaderBackend, HeaderMetadata};
28use sp_consensus_aura::{AuraApi, ConsensusLog, AURA_ENGINE_ID};
29use sp_core::Pair;
30use sp_runtime::{
31	generic::OpaqueDigestItemId,
32	traits::{Block, Header, NumberFor},
33};
34
35use crate::{fetch_authorities_from_runtime, AuthorityId, CompatibilityMode};
36
37const LOG_TARGET: &str = "aura::authorities_tracker";
38
39/// AURA authorities tracker. Updates authorities based on the AURA authorities change
40/// digest in the block header.
41pub struct AuthoritiesTracker<P: Pair, B: Block, C> {
42	authorities: RwLock<ForkTree<B::Hash, NumberFor<B>, Vec<AuthorityId<P>>>>,
43	client: Arc<C>,
44}
45
46impl<P: Pair, B: Block, C> AuthoritiesTracker<P, B, C> {
47	/// Create a new `AuthoritiesTracker`.
48	pub fn new(client: Arc<C>) -> Self {
49		Self { authorities: RwLock::new(ForkTree::new()), client }
50	}
51}
52
53impl<P, B, C> AuthoritiesTracker<P, B, C>
54where
55	P: Pair,
56	B: Block,
57	C: HeaderBackend<B> + HeaderMetadata<B, Error = sp_blockchain::Error> + ProvideRuntimeApi<B>,
58	P::Public: Codec + Debug,
59	C::Api: AuraApi<B, AuthorityId<P>>,
60{
61	/// Fetch authorities from the tracker, if available. If not available, fetch from the client
62	/// and update the tracker.
63	pub fn fetch_or_update(
64		&self,
65		header: &B::Header,
66		compatibility_mode: &CompatibilityMode<NumberFor<B>>,
67	) -> Result<Vec<AuthorityId<P>>, String> {
68		let hash = header.hash();
69		let number = *header.number();
70		let parent_hash = *header.parent_hash();
71
72		// Fetch authorities from cache, if available.
73		let authorities = {
74			let is_descendent_of =
75				sc_client_api::utils::is_descendent_of(&*self.client, Some((hash, parent_hash)));
76			let authorities_cache = self.authorities.read();
77			authorities_cache
78				.find_node_where(&hash, &number, &is_descendent_of, &|_| true)
79				.map_err(|e| {
80					format!("Could not find authorities for block {hash:?} at number {number}: {e}")
81				})?
82				.map(|node| node.data.clone())
83		};
84
85		match authorities {
86			Some(authorities) => {
87				log::debug!(
88					target: LOG_TARGET,
89					"Authorities for block {:?} at number {} found in cache",
90					hash,
91					number,
92				);
93				Ok(authorities)
94			},
95			None => {
96				// Authorities are missing from the cache. Fetch them from the runtime and cache
97				// them.
98				log::debug!(
99					target: LOG_TARGET,
100					"Authorities for block {:?} at number {} not found in cache, fetching from runtime",
101					hash,
102					number
103				);
104				let authorities = fetch_authorities_from_runtime(
105					&*self.client,
106					parent_hash,
107					number,
108					compatibility_mode,
109				)
110				.map_err(|e| format!("Could not fetch authorities at {:?}: {}", parent_hash, e))?;
111				let is_descendent_of = sc_client_api::utils::is_descendent_of(&*self.client, None);
112				let mut authorities_cache = self.authorities.write();
113				authorities_cache
114					.import(
115						parent_hash,
116						number - 1u32.into(),
117						authorities.clone(),
118						&is_descendent_of,
119					)
120					.map_err(|e| {
121						format!("Could not import authorities for block {parent_hash:?} at number {}: {e}", number - 1u32.into())
122					})?;
123				Ok(authorities)
124			},
125		}
126	}
127
128	/// If there is an authorities change digest in the header, import it into the tracker.
129	pub fn import(&self, post_header: &B::Header) -> Result<(), String> {
130		if let Some(authorities_change) = find_authorities_change_digest::<B, P>(&post_header) {
131			let hash = post_header.hash();
132			let parent_hash = *post_header.parent_hash();
133			let number = *post_header.number();
134			log::debug!(
135				target: LOG_TARGET,
136				"Importing authorities change for block {:?} at number {} found in header digest",
137				hash,
138				number,
139			);
140			self.prune_finalized()?;
141			let is_descendent_of =
142				sc_client_api::utils::is_descendent_of(&*self.client, Some((hash, parent_hash)));
143			let mut authorities_cache = self.authorities.write();
144			authorities_cache
145				.import(hash, number, authorities_change, &is_descendent_of)
146				.map_err(|e| {
147					format!(
148						"Could not import authorities for block {hash:?} at number {number}: {e}"
149					)
150				})?;
151		}
152		Ok(())
153	}
154
155	fn prune_finalized(&self) -> Result<(), String> {
156		let is_descendent_of = sc_client_api::utils::is_descendent_of(&*self.client, None);
157		let info = self.client.info();
158		let mut authorities_cache = self.authorities.write();
159		let _pruned = authorities_cache
160			.prune(&info.finalized_hash, &info.finalized_number, &is_descendent_of, &|_| true)
161			.map_err(|e| e.to_string())?;
162		Ok(())
163	}
164}
165
166/// Extract the AURA authorities change digest from the given header, if it exists.
167fn find_authorities_change_digest<B, P>(header: &B::Header) -> Option<Vec<AuthorityId<P>>>
168where
169	B: Block,
170	P: Pair,
171	P::Public: Codec,
172{
173	for log in header.digest().logs() {
174		log::trace!(target: LOG_TARGET, "Checking log {:?}, looking for authorities change digest.", log);
175		let log = log
176			.try_to::<ConsensusLog<AuthorityId<P>>>(OpaqueDigestItemId::Consensus(&AURA_ENGINE_ID));
177		if let Some(ConsensusLog::AuthoritiesChange(authorities)) = log {
178			return Some(authorities);
179		}
180	}
181	None
182}