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_inherents::{InherentData, InherentIdentifier},
13 sp_runtime::traits::Block as BlockT,
14 sp_session_validator_management::{
15 INHERENT_IDENTIFIER, InherentError, MainChainScripts, SessionValidatorManagementApi,
16 },
17};
18
19#[cfg(feature = "std")]
20pub use sidechain_domain::mainchain_epoch::MainchainEpochConfig;
21
22#[derive(Clone, Debug, Encode, Decode)]
23pub struct AriadneInherentDataProvider {
25 pub data: Option<AuthoritySelectionInputs>,
27}
28
29#[cfg(feature = "std")]
30impl AriadneInherentDataProvider {
31 pub async fn new<Block, AuthorityId, AuthorityKeys, T>(
42 client: &T,
43 sc_epoch_duration_millis: u64,
44 mc_epoch_config: &MainchainEpochConfig,
45 parent_hash: <Block as BlockT>::Hash,
46 timestamp_millis: u64,
47 data_source: &(dyn AuthoritySelectionDataSource + Send + Sync),
48 mc_reference_epoch: McEpochNumber,
49 ) -> Result<Self, InherentProviderCreationError>
50 where
51 AuthorityKeys: Decode + Encode,
52 AuthorityId: Decode + Encode,
53 Block: BlockT,
54 T: ProvideRuntimeApi<Block> + Send + Sync,
55 T::Api: SessionValidatorManagementApi<Block, AuthorityId, AuthorityKeys, ScEpochNumber>,
56 {
57 let for_mc_epoch = mc_epoch_for_next_ariadne_cidp(
58 client,
59 sc_epoch_duration_millis,
60 mc_epoch_config,
61 parent_hash,
62 timestamp_millis,
63 )?;
64
65 let data_epoch = data_source.data_epoch(for_mc_epoch).await?;
66 let scripts = client.runtime_api().get_main_chain_scripts(parent_hash)?;
69 if data_epoch < mc_reference_epoch {
70 Ok(AriadneInherentDataProvider::from_mc_data(data_source, for_mc_epoch, scripts)
71 .await?)
72 } else {
73 Ok(AriadneInherentDataProvider { data: None })
74 }
75 }
76
77 async fn from_mc_data(
78 candidate_data_source: &(dyn AuthoritySelectionDataSource + Send + Sync),
79 for_epoch: McEpochNumber,
80 scripts: MainChainScripts,
81 ) -> Result<Self, InherentProviderCreationError> {
82 use crate::authority_selection_inputs::authority_selection_inputs_from_mc_data;
83
84 Ok(Self {
85 data: Some(
86 authority_selection_inputs_from_mc_data(candidate_data_source, for_epoch, scripts)
87 .await?,
88 ),
89 })
90 }
91}
92
93#[cfg(feature = "std")]
94#[derive(Debug, thiserror::Error)]
95pub enum InherentProviderCreationError {
97 #[error("Couldn't convert timestamp to main chain epoch: {0}")]
99 McEpochDerivationError(#[from] sidechain_domain::mainchain_epoch::EpochDerivationError),
100 #[error("Runtime API call failed: {0}")]
102 ApiError(#[from] sp_api::ApiError),
103 #[error("Failed to create authority selection inputs: {0}")]
105 InputsCreationError(#[from] AuthoritySelectionInputsCreationError),
106 #[error("Data source call failed: {0}")]
108 DataSourceError(#[from] Box<dyn std::error::Error + Send + Sync>),
109}
110
111#[cfg(feature = "std")]
112fn mc_epoch_for_next_ariadne_cidp<Block, AuthorityId, AuthorityKeys, T>(
113 client: &T,
114 sc_epoch_duration_millis: u64,
115 epoch_config: &MainchainEpochConfig,
116 parent_hash: <Block as BlockT>::Hash,
117 timestamp_millis: u64,
118) -> Result<McEpochNumber, InherentProviderCreationError>
119where
120 Block: BlockT,
121 AuthorityKeys: Decode + Encode,
122 AuthorityId: Decode + Encode,
123 T: ProvideRuntimeApi<Block> + Send + Sync,
124 T::Api: SessionValidatorManagementApi<Block, AuthorityId, AuthorityKeys, ScEpochNumber>,
125{
126 use sp_core::offchain::Timestamp;
127
128 let next_unset_epoch = client.runtime_api().get_next_unset_epoch_number(parent_hash)?;
129
130 let for_timestamp = {
131 if next_unset_epoch == ScEpochNumber(1) {
135 let current_pc_epoch = timestamp_millis / sc_epoch_duration_millis;
139 current_pc_epoch * sc_epoch_duration_millis
140 } else {
141 next_unset_epoch.0 * sc_epoch_duration_millis
142 }
143 };
144
145 epoch_config
146 .timestamp_to_mainchain_epoch(Timestamp::from_unix_millis(for_timestamp))
147 .map_err(InherentProviderCreationError::McEpochDerivationError)
148}
149
150#[cfg(feature = "std")]
151#[async_trait::async_trait]
152impl sp_inherents::InherentDataProvider for AriadneInherentDataProvider {
153 async fn provide_inherent_data(
154 &self,
155 inherent_data: &mut InherentData,
156 ) -> Result<(), sp_inherents::Error> {
157 match &self.data {
158 None => Ok(()),
159 Some(data) => inherent_data.put_data(INHERENT_IDENTIFIER, data),
160 }
161 }
162
163 async fn try_handle_error(
164 &self,
165 identifier: &InherentIdentifier,
166 error: &[u8],
167 ) -> Option<Result<(), sp_inherents::Error>> {
168 if *identifier != INHERENT_IDENTIFIER {
170 return None;
171 }
172
173 let mut error = error;
174 Some(Err(sp_inherents::Error::Application(Box::from(
175 <InherentError as parity_scale_codec::Decode>::decode(&mut error).ok()?,
176 ))))
177 }
178}
179
180#[cfg(test)]
181mod tests {
182 use super::*;
183 use crate::ariadne_inherent_data_provider::AriadneInherentDataProvider;
184 use crate::mock::MockAuthoritySelectionDataSource;
185 use crate::runtime_api_mock::*;
186 use sidechain_domain::mainchain_epoch::*;
187 use sp_core::H256;
188 use sp_core::offchain::Timestamp;
189
190 const TIMESTAMP: u64 = 400_000;
191
192 #[tokio::test]
193 async fn return_empty_ariadne_cidp_if_runtime_requests_too_new_epoch() {
194 let next_unset_epoch_number = ScEpochNumber(42);
196 let mc_reference_epoch = McEpochNumber(1);
197 let empty_ariadne_idp = AriadneInherentDataProvider::new(
198 &client(next_unset_epoch_number),
199 sc_epoch_duration_millis(),
200 &epoch_config(),
201 H256::zero(),
202 TIMESTAMP,
204 &MockAuthoritySelectionDataSource::default(),
205 mc_reference_epoch,
206 )
207 .await;
208
209 assert!(empty_ariadne_idp.is_ok());
210 assert!(empty_ariadne_idp.unwrap().data.is_none());
211 }
212
213 #[tokio::test]
214 async fn error_if_num_permissioned_candidates_non_zero_and_no_permissioned_candidate_list() {
215 use crate::ariadne_inherent_data_provider::InherentProviderCreationError::InputsCreationError;
216 use crate::authority_selection_inputs::AuthoritySelectionInputsCreationError::AriadneParametersQuery;
217
218 let next_unset_epoch_number = ScEpochNumber(42);
219 let mc_reference_epoch = McEpochNumber(5);
220 let ariadne_idp = AriadneInherentDataProvider::new(
221 &client(next_unset_epoch_number),
222 sc_epoch_duration_millis(),
223 &epoch_config(),
224 H256::zero(),
225 TIMESTAMP,
227 &MockAuthoritySelectionDataSource::default()
228 .with_permissioned_candidates(vec![None, None, None, None, None])
229 .with_num_permissioned_candidates(3),
230 mc_reference_epoch,
231 )
232 .await;
233
234 assert!(ariadne_idp.is_err());
235 assert!(matches!(
236 ariadne_idp.unwrap_err(),
237 InputsCreationError(AriadneParametersQuery(_, _, _, _))
238 ));
239 }
240
241 #[tokio::test]
242 async fn ok_if_num_permissioned_candidates_zero_and_no_permissioned_candidate_list() {
243 let next_unset_epoch_number = ScEpochNumber(42);
244 let mc_reference_epoch = McEpochNumber(5);
245 let ariadne_idp = AriadneInherentDataProvider::new(
246 &client(next_unset_epoch_number),
247 sc_epoch_duration_millis(),
248 &epoch_config(),
249 H256::zero(),
250 TIMESTAMP,
252 &MockAuthoritySelectionDataSource::default()
253 .with_permissioned_candidates(vec![None, None, None, None, None])
254 .with_num_permissioned_candidates(0),
255 mc_reference_epoch,
256 )
257 .await;
258
259 assert!(ariadne_idp.is_ok());
260 assert!(ariadne_idp.unwrap().data.is_some());
261 }
262
263 fn sc_epoch_duration_millis() -> u64 {
264 60 * 60 * 1000
265 }
266
267 fn client(next_unset_epoch_number: ScEpochNumber) -> TestApi {
268 TestApi { next_unset_epoch_number }
269 }
270
271 fn epoch_config() -> MainchainEpochConfig {
272 MainchainEpochConfig {
273 first_epoch_timestamp_millis: Timestamp::from_unix_millis(0),
274 first_epoch_number: 0,
275 epoch_duration_millis: Duration::from_millis(10 * sc_epoch_duration_millis()),
276 first_slot_number: 0,
277 slot_duration_millis: Duration::from_millis(1000),
278 }
279 }
280}