sp_session_validator_management_query/
lib.rs1#![deny(missing_docs)]
3pub mod commands;
4pub mod get_registrations;
5pub mod types;
6
7use async_trait::async_trait;
8use authority_selection_inherents::authority_selection_inputs::{
9 AuthoritySelectionDataSource, AuthoritySelectionInputs, RawPermissionedCandidateData,
10};
11use authority_selection_inherents::filter_invalid_candidates::CandidateValidationApi;
12use derive_new::new;
13use parity_scale_codec::{Decode, Encode};
14use sidechain_block_search::{FindSidechainBlock, SidechainInfo, predicates::AnyBlockInEpoch};
15use sidechain_domain::{McEpochNumber, ScEpochNumber, StakePoolPublicKey};
16use sp_api::{ApiExt, ProvideRuntimeApi};
17use sp_blockchain::{HeaderBackend, Info};
18use sp_core::bytes::to_hex;
19use sp_runtime::traits::NumberFor;
20use sp_runtime::traits::{Block as BlockT, Zero};
21use sp_session_validator_management::{
22 CommitteeMember as CommitteeMemberT, SessionValidatorManagementApi,
23};
24#[allow(deprecated)]
25use sp_sidechain::{GetGenesisUtxo, GetSidechainStatus};
26use std::sync::Arc;
27use types::*;
28
29#[cfg(test)]
30mod tests;
31
32pub type QueryResult<T> = Result<T, String>;
34
35#[async_trait]
36pub trait SessionValidatorManagementQueryApi {
38 fn get_epoch_committee(&self, epoch_number: u64) -> QueryResult<GetCommitteeResponse>;
40
41 async fn get_registrations(
46 &self,
47 mc_epoch_number: McEpochNumber,
48 stake_pool_public_key: StakePoolPublicKey,
49 ) -> QueryResult<Vec<CandidateRegistrationEntry>>;
50
51 async fn get_ariadne_parameters(
53 &self,
54 epoch_number: McEpochNumber,
55 ) -> QueryResult<AriadneParameters>;
56}
57
58#[derive(new)]
59pub struct SessionValidatorManagementQuery<C, Block, CommitteeMember: Decode> {
61 client: Arc<C>,
62 candidate_data_source: Arc<dyn AuthoritySelectionDataSource + Send + Sync>,
63 _marker: std::marker::PhantomData<(Block, CommitteeMember)>,
64}
65
66impl<C, Block, CommitteeMember> SessionValidatorManagementQuery<C, Block, CommitteeMember>
67where
68 Block: BlockT,
69 C: ProvideRuntimeApi<Block>,
70 C::Api: sp_api::Core<Block> + ApiExt<Block>,
71 CommitteeMember: CommitteeMemberT + Encode + Decode,
72 CommitteeMember::AuthorityId: Encode + Decode + AsRef<[u8]>,
73 CommitteeMember::AuthorityKeys: Encode + Decode,
74 AuthoritySelectionInputs: Encode + Decode,
75 C::Api: SessionValidatorManagementApi<
76 Block,
77 CommitteeMember,
78 AuthoritySelectionInputs,
79 ScEpochNumber,
80 >,
81{
82 fn validator_management_api_version(&self, block: Block::Hash) -> QueryResult<u32> {
83 let version = (self.client.runtime_api())
84 .api_version::<dyn SessionValidatorManagementApi<
85 Block,
86 CommitteeMember,
87 AuthoritySelectionInputs,
88 ScEpochNumber,
89 >>(block)
90 .map_err(err_debug)?
91 .unwrap_or(1);
92 Ok(version)
93 }
94
95 fn get_current_committee_versioned(
96 &self,
97 block: Block::Hash,
98 ) -> QueryResult<GetCommitteeResponse> {
99 let api = self.client.runtime_api();
100
101 if self.validator_management_api_version(block)? < 2 {
102 #[allow(deprecated)]
103 let (epoch, authority_ids) =
104 api.get_current_committee_before_version_2(block).map_err(err_debug)?;
105 Ok(GetCommitteeResponse::new_legacy(epoch, authority_ids))
106 } else {
107 let (epoch, authority_data) = api.get_current_committee(block).map_err(err_debug)?;
108 Ok(GetCommitteeResponse::new(epoch, authority_data))
109 }
110 }
111
112 fn get_next_committee_versioned(
113 &self,
114 block: Block::Hash,
115 ) -> QueryResult<Option<GetCommitteeResponse>> {
116 let api = self.client.runtime_api();
117
118 if self.validator_management_api_version(block)? < 2 {
119 #[allow(deprecated)]
120 Ok(api.get_next_committee_before_version_2(block).map_err(err_debug)?.map(
121 |(epoch, authority_ids)| GetCommitteeResponse::new_legacy(epoch, authority_ids),
122 ))
123 } else {
124 Ok(api
125 .get_next_committee(block)
126 .map_err(err_debug)?
127 .map(|(epoch, authority_data)| GetCommitteeResponse::new(epoch, authority_data)))
128 }
129 }
130}
131
132#[async_trait]
133#[allow(deprecated)]
134impl<C, Block, CommitteeMember> SessionValidatorManagementQueryApi
135 for SessionValidatorManagementQuery<C, Block, CommitteeMember>
136where
137 Block: BlockT,
138 NumberFor<Block>: From<u32> + Into<u32>,
139 CommitteeMember: CommitteeMemberT + Decode + Encode + Send + Sync + 'static,
140 CommitteeMember::AuthorityKeys: Decode + Encode,
141 CommitteeMember::AuthorityId: AsRef<[u8]> + Decode + Encode + Send + Sync + 'static,
142 C: Send + Sync + 'static,
143 C: ProvideRuntimeApi<Block>,
144 C: HeaderBackend<Block>,
145 C::Api: sp_api::Core<Block>,
146 C::Api: GetSidechainStatus<Block>,
147 C::Api: SessionValidatorManagementApi<
148 Block,
149 CommitteeMember,
150 AuthoritySelectionInputs,
151 ScEpochNumber,
152 >,
153 C::Api: GetGenesisUtxo<Block>,
154 C::Api: CandidateValidationApi<Block>,
155{
156 fn get_epoch_committee(&self, epoch_number: u64) -> QueryResult<GetCommitteeResponse> {
157 let epoch_number = ScEpochNumber(epoch_number);
158 let Info { genesis_hash, best_number: latest_block, best_hash, .. } = self.client.info();
159
160 if epoch_number.is_zero() {
161 let genesis_committee = self.get_current_committee_versioned(genesis_hash)?;
162 return Ok(GetCommitteeResponse { sidechain_epoch: 0, ..genesis_committee });
163 }
164
165 let first_epoch = {
166 let second_block = (self.client)
167 .hash(1.into())
168 .map_err(|err| {
169 format!("Node is not in archive mode, not able to fetch first block: {err:?}")
170 })?
171 .ok_or("Only the Genesis Block exists at the moment!")?;
172 (self.client.runtime_api())
173 .get_sidechain_status(second_block)
174 .map_err(err_debug)?
175 .epoch
176 };
177
178 if epoch_number < first_epoch {
179 return Err(format!("Epoch {} is earlier than the Initial Epoch!", epoch_number));
180 }
181
182 let epoch_of_latest_block =
183 self.client.get_epoch_of_block(latest_block).map_err(err_debug)?;
184
185 if epoch_number > epoch_of_latest_block.next() {
186 return Err(format!("Committee is unknown for epoch {epoch_number}"));
187 }
188
189 if epoch_number == epoch_of_latest_block.next() {
190 self.get_next_committee_versioned(best_hash)?
191 .ok_or(format!("Committee is unknown for the next epoch: {epoch_number}"))
192 } else {
193 let block_hash = self
194 .client
195 .find_block(AnyBlockInEpoch { epoch: epoch_number })
196 .map_err(err_debug)?;
197 self.get_current_committee_versioned(block_hash)
198 }
199 }
200
201 async fn get_registrations(
202 &self,
203 mc_epoch_number: McEpochNumber,
204 mc_public_key: StakePoolPublicKey,
205 ) -> QueryResult<Vec<CandidateRegistrationEntry>> {
206 let api = self.client.runtime_api();
207 let best_block = self.client.info().best_hash;
208 let scripts = api.get_main_chain_scripts(best_block).map_err(err_debug)?;
209 let mut registrations_map = self
210 .candidates_registrations_for_epoch(
211 mc_epoch_number,
212 scripts.committee_candidate_address,
213 )
214 .await?;
215 Ok(registrations_map.remove(&to_hex(&mc_public_key.0, false)).unwrap_or(vec![]))
216 }
217
218 async fn get_ariadne_parameters(
219 &self,
220 epoch_number: McEpochNumber,
221 ) -> QueryResult<AriadneParameters> {
222 let api = self.client.runtime_api();
223 let best_block = self.client.info().best_hash;
224 let scripts = api.get_main_chain_scripts(best_block).map_err(err_debug)?;
225 let ariadne_parameters_response = self
226 .candidate_data_source
227 .get_ariadne_parameters(
228 epoch_number,
229 scripts.d_parameter_policy_id,
230 scripts.permissioned_candidates_policy_id,
231 )
232 .await
233 .map_err(err_debug)?;
234
235 let candidate_registrations = self
236 .candidates_registrations_for_epoch(epoch_number, scripts.committee_candidate_address)
237 .await?;
238 let validate_permissioned_candidate = |candidate: &RawPermissionedCandidateData| {
239 api.validate_permissioned_candidate_data(
240 best_block,
241 sidechain_domain::PermissionedCandidateData {
242 sidechain_public_key: candidate.sidechain_public_key.clone(),
243 aura_public_key: candidate.aura_public_key.clone(),
244 grandpa_public_key: candidate.grandpa_public_key.clone(),
245 },
246 )
247 };
248
249 let permissioned_candidates = match ariadne_parameters_response.permissioned_candidates {
250 None => None,
251 Some(permissioned_candidates) => Some(
252 permissioned_candidates
253 .into_iter()
254 .map(|candidate| {
255 let validation_result =
256 validate_permissioned_candidate(&candidate).map_err(err_debug)?;
257 Ok::<PermissionedCandidateData, String>(PermissionedCandidateData::new(
258 candidate,
259 validation_result,
260 ))
261 })
262 .collect::<Result<Vec<_>, _>>()?,
263 ),
264 };
265
266 Ok(AriadneParameters {
267 d_parameter: ariadne_parameters_response.d_parameter.into(),
268 permissioned_candidates,
269 candidate_registrations,
270 })
271 }
272}
273
274fn err_debug<T: std::fmt::Debug>(err: T) -> String {
275 format!("{err:?}")
276}