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