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