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