authority_selection_inherents/
ariadne_inherent_data_provider.rs1#[cfg(feature = "std")]
3use crate::authority_selection_inputs::AuthoritySelectionDataSource;
4use crate::authority_selection_inputs::AuthoritySelectionInputs;
5use parity_scale_codec::{Decode, Encode};
6#[cfg(feature = "std")]
7use {
8 crate::authority_selection_inputs::AuthoritySelectionInputsCreationError,
9 sidechain_domain::mainchain_epoch::MainchainEpochDerivation,
10 sidechain_domain::*,
11 sp_api::ProvideRuntimeApi,
12 sp_consensus_slots::Slot,
13 sp_inherents::{InherentData, InherentIdentifier},
14 sp_runtime::traits::Block as BlockT,
15 sp_session_validator_management::CommitteeMember as CommitteeMemberT,
16 sp_session_validator_management::{
17 INHERENT_IDENTIFIER, InherentError, MainChainScripts, SessionValidatorManagementApi,
18 },
19};
20
21#[cfg(feature = "std")]
22pub use {sidechain_domain::mainchain_epoch::MainchainEpochConfig, sidechain_slots::ScSlotConfig};
23
24#[derive(Clone, Debug, Encode, Decode)]
25pub struct AriadneInherentDataProvider {
27 pub data: Option<AuthoritySelectionInputs>,
29}
30
31#[cfg(feature = "std")]
32impl AriadneInherentDataProvider {
33 pub async fn new<Block, CommitteeMember, T>(
44 client: &T,
45 sc_slot_config: &ScSlotConfig,
46 mc_epoch_config: &MainchainEpochConfig,
47 parent_hash: <Block as BlockT>::Hash,
48 slot: Slot,
49 data_source: &(dyn AuthoritySelectionDataSource + Send + Sync),
50 mc_reference_epoch: McEpochNumber,
51 ) -> Result<Self, InherentProviderCreationError>
52 where
53 CommitteeMember: CommitteeMemberT + Decode + Encode,
54 CommitteeMember::AuthorityKeys: Decode + Encode,
55 CommitteeMember::AuthorityId: Decode + Encode,
56 Block: BlockT,
57 T: ProvideRuntimeApi<Block> + Send + Sync,
58 T::Api: SessionValidatorManagementApi<
59 Block,
60 CommitteeMember,
61 AuthoritySelectionInputs,
62 ScEpochNumber,
63 >,
64 {
65 let for_mc_epoch = mc_epoch_for_next_ariadne_cidp(
66 client,
67 sc_slot_config,
68 mc_epoch_config,
69 parent_hash,
70 slot,
71 )?;
72
73 let data_epoch = data_source.data_epoch(for_mc_epoch).await?;
74 let scripts = client.runtime_api().get_main_chain_scripts(parent_hash)?;
77 if data_epoch < mc_reference_epoch {
78 Ok(AriadneInherentDataProvider::from_mc_data(data_source, for_mc_epoch, scripts)
79 .await?)
80 } else {
81 Ok(AriadneInherentDataProvider { data: None })
82 }
83 }
84
85 async fn from_mc_data(
86 candidate_data_source: &(dyn AuthoritySelectionDataSource + Send + Sync),
87 for_epoch: McEpochNumber,
88 scripts: MainChainScripts,
89 ) -> Result<Self, InherentProviderCreationError> {
90 Ok(Self {
91 data: Some(
92 AuthoritySelectionInputs::from_mc_data(candidate_data_source, for_epoch, scripts)
93 .await?,
94 ),
95 })
96 }
97}
98
99#[cfg(feature = "std")]
100#[derive(Debug, thiserror::Error)]
101pub enum InherentProviderCreationError {
103 #[error("Slot represents a timestamp bigger than of u64::MAX")]
105 SlotTooBig,
106 #[error("Couldn't convert timestamp to main chain epoch: {0}")]
108 McEpochDerivationError(#[from] sidechain_domain::mainchain_epoch::EpochDerivationError),
109 #[error("Runtime API call failed: {0}")]
111 ApiError(#[from] sp_api::ApiError),
112 #[error("Failed to create authority selection inputs: {0}")]
114 InputsCreationError(#[from] AuthoritySelectionInputsCreationError),
115 #[error("Data source call failed: {0}")]
117 DataSourceError(#[from] Box<dyn std::error::Error + Send + Sync>),
118}
119
120#[cfg(feature = "std")]
121fn mc_epoch_for_next_ariadne_cidp<Block, CommitteeMember, T>(
122 client: &T,
123 sc_slot_config: &ScSlotConfig,
124 epoch_config: &MainchainEpochConfig,
125 parent_hash: <Block as BlockT>::Hash,
126 slot: Slot,
127) -> Result<McEpochNumber, InherentProviderCreationError>
128where
129 Block: BlockT,
130 CommitteeMember: Decode + Encode + CommitteeMemberT,
131 CommitteeMember::AuthorityKeys: Decode + Encode,
132 CommitteeMember::AuthorityId: Decode + Encode,
133 T: ProvideRuntimeApi<Block> + Send + Sync,
134 T::Api: SessionValidatorManagementApi<
135 Block,
136 CommitteeMember,
137 AuthoritySelectionInputs,
138 ScEpochNumber,
139 >,
140{
141 let next_unset_epoch = client.runtime_api().get_next_unset_epoch_number(parent_hash)?;
142
143 let for_sc_epoch_number = {
144 if next_unset_epoch == ScEpochNumber(1) {
148 sc_slot_config.epoch_number(slot)
149 } else {
150 next_unset_epoch
151 }
152 };
153
154 sc_epoch_to_mc_epoch(for_sc_epoch_number, sc_slot_config, epoch_config)
155}
156
157#[cfg(feature = "std")]
158fn sc_epoch_to_mc_epoch(
159 sc_epoch: ScEpochNumber,
160 sc_slot_config: &ScSlotConfig,
161 epoch_config: &MainchainEpochConfig,
162) -> Result<McEpochNumber, InherentProviderCreationError> {
163 let timestamp = sc_slot_config
164 .epoch_start_time(sc_epoch)
165 .ok_or(InherentProviderCreationError::SlotTooBig)?;
166
167 epoch_config
168 .timestamp_to_mainchain_epoch(timestamp)
169 .map_err(InherentProviderCreationError::McEpochDerivationError)
170}
171
172#[cfg(feature = "std")]
173#[async_trait::async_trait]
174impl sp_inherents::InherentDataProvider for AriadneInherentDataProvider {
175 async fn provide_inherent_data(
176 &self,
177 inherent_data: &mut InherentData,
178 ) -> Result<(), sp_inherents::Error> {
179 match &self.data {
180 None => Ok(()),
181 Some(data) => inherent_data.put_data(INHERENT_IDENTIFIER, data),
182 }
183 }
184
185 async fn try_handle_error(
186 &self,
187 identifier: &InherentIdentifier,
188 error: &[u8],
189 ) -> Option<Result<(), sp_inherents::Error>> {
190 if *identifier != INHERENT_IDENTIFIER {
192 return None;
193 }
194
195 let mut error = error;
196 Some(Err(sp_inherents::Error::Application(Box::from(
197 <InherentError as parity_scale_codec::Decode>::decode(&mut error).ok()?,
198 ))))
199 }
200}
201
202#[cfg(test)]
203mod tests {
204 use super::*;
205 use crate::ariadne_inherent_data_provider::AriadneInherentDataProvider;
206 use crate::mock::MockAuthoritySelectionDataSource;
207 use crate::runtime_api_mock::*;
208 use SlotDuration;
209 use sidechain_domain::mainchain_epoch::*;
210 use sidechain_slots::*;
211 use sp_core::H256;
212 use sp_core::offchain::Timestamp;
213
214 #[tokio::test]
215 async fn return_empty_ariadne_cidp_if_runtime_requests_too_new_epoch() {
216 let next_unset_epoch_number = ScEpochNumber(42);
218 let mc_reference_epoch = McEpochNumber(1);
219 let empty_ariadne_idp = AriadneInherentDataProvider::new(
220 &client(next_unset_epoch_number),
221 &sc_slot_config(),
222 &epoch_config(),
223 H256::zero(),
224 Slot::from(400u64),
226 &MockAuthoritySelectionDataSource::default(),
227 mc_reference_epoch,
228 )
229 .await;
230
231 assert!(empty_ariadne_idp.is_ok());
232 assert!(empty_ariadne_idp.unwrap().data.is_none());
233 }
234
235 #[tokio::test]
236 async fn error_if_num_permissioned_candidates_non_zero_and_no_permissioned_candidate_list() {
237 use crate::ariadne_inherent_data_provider::InherentProviderCreationError::InputsCreationError;
238 use crate::authority_selection_inputs::AuthoritySelectionInputsCreationError::AriadneParametersQuery;
239
240 let next_unset_epoch_number = ScEpochNumber(42);
241 let mc_reference_epoch = McEpochNumber(5);
242 let ariadne_idp = AriadneInherentDataProvider::new(
243 &client(next_unset_epoch_number),
244 &sc_slot_config(),
245 &epoch_config(),
246 H256::zero(),
247 Slot::from(400u64),
249 &MockAuthoritySelectionDataSource::default()
250 .with_permissioned_candidates(vec![None, None, None, None, None])
251 .with_num_permissioned_candidates(3),
252 mc_reference_epoch,
253 )
254 .await;
255
256 assert!(ariadne_idp.is_err());
257 assert!(matches!(
258 ariadne_idp.unwrap_err(),
259 InputsCreationError(AriadneParametersQuery(_, _, _, _))
260 ));
261 }
262
263 #[tokio::test]
264 async fn ok_if_num_permissioned_candidates_zero_and_no_permissioned_candidate_list() {
265 let next_unset_epoch_number = ScEpochNumber(42);
266 let mc_reference_epoch = McEpochNumber(5);
267 let ariadne_idp = AriadneInherentDataProvider::new(
268 &client(next_unset_epoch_number),
269 &sc_slot_config(),
270 &epoch_config(),
271 H256::zero(),
272 Slot::from(400u64),
274 &MockAuthoritySelectionDataSource::default()
275 .with_permissioned_candidates(vec![None, None, None, None, None])
276 .with_num_permissioned_candidates(0),
277 mc_reference_epoch,
278 )
279 .await;
280
281 assert!(ariadne_idp.is_ok());
282 assert!(ariadne_idp.unwrap().data.is_some());
283 }
284
285 fn sc_slot_config() -> ScSlotConfig {
286 ScSlotConfig {
287 slots_per_epoch: SlotsPerEpoch(10),
288 slot_duration: SlotDuration::from_millis(1000),
289 }
290 }
291
292 fn client(next_unset_epoch_number: ScEpochNumber) -> TestApi {
293 TestApi { next_unset_epoch_number }
294 }
295
296 fn epoch_config() -> MainchainEpochConfig {
297 MainchainEpochConfig {
298 first_epoch_timestamp_millis: Timestamp::from_unix_millis(0),
299 first_epoch_number: 0,
300 epoch_duration_millis: Duration::from_millis(
301 u64::from(sc_slot_config().slots_per_epoch.0)
302 * sc_slot_config().slot_duration.as_millis()
303 * 10,
304 ),
305 first_slot_number: 0,
306 slot_duration_millis: Duration::from_millis(1000),
307 }
308 }
309}