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