1use 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
39pub 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 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 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 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 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 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
166fn 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}