pallet_authority_discovery/
lib.rs1#![cfg_attr(not(feature = "std"), no_std)]
25
26extern crate alloc;
27
28use alloc::vec::Vec;
29use frame_support::{
30 traits::{Get, OneSessionHandler},
31 WeakBoundedVec,
32};
33use sp_authority_discovery::AuthorityId;
34
35pub use pallet::*;
36
37#[frame_support::pallet]
38pub mod pallet {
39 use super::*;
40 use frame_support::pallet_prelude::*;
41
42 #[pallet::pallet]
43 pub struct Pallet<T>(_);
44
45 #[pallet::config]
46 pub trait Config: frame_system::Config + pallet_session::Config {
48 type MaxAuthorities: Get<u32>;
50 }
51
52 #[pallet::storage]
53 pub type Keys<T: Config> =
55 StorageValue<_, WeakBoundedVec<AuthorityId, T::MaxAuthorities>, ValueQuery>;
56
57 #[pallet::storage]
58 pub type NextKeys<T: Config> =
60 StorageValue<_, WeakBoundedVec<AuthorityId, T::MaxAuthorities>, ValueQuery>;
61
62 #[derive(frame_support::DefaultNoBound)]
63 #[pallet::genesis_config]
64 pub struct GenesisConfig<T: Config> {
65 pub keys: Vec<AuthorityId>,
66 #[serde(skip)]
67 pub _config: core::marker::PhantomData<T>,
68 }
69
70 #[pallet::genesis_build]
71 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
72 fn build(&self) {
73 Pallet::<T>::initialize_keys(&self.keys)
74 }
75 }
76}
77
78impl<T: Config> Pallet<T> {
79 pub fn authorities() -> Vec<AuthorityId> {
82 let mut keys = Keys::<T>::get().to_vec();
83 let next = NextKeys::<T>::get().to_vec();
84
85 keys.extend(next);
86 keys.sort();
87 keys.dedup();
88
89 keys.to_vec()
90 }
91
92 pub fn current_authorities() -> WeakBoundedVec<AuthorityId, T::MaxAuthorities> {
94 Keys::<T>::get()
95 }
96
97 pub fn next_authorities() -> WeakBoundedVec<AuthorityId, T::MaxAuthorities> {
99 NextKeys::<T>::get()
100 }
101
102 fn initialize_keys(keys: &Vec<AuthorityId>) {
103 if !keys.is_empty() {
104 assert!(Keys::<T>::get().is_empty(), "Keys are already initialized!");
105
106 let bounded_keys =
107 WeakBoundedVec::<AuthorityId, T::MaxAuthorities>::try_from((*keys).clone())
108 .expect("Keys vec too big");
109
110 Keys::<T>::put(&bounded_keys);
111 NextKeys::<T>::put(&bounded_keys);
112 }
113 }
114}
115
116impl<T: Config> sp_runtime::BoundToRuntimeAppPublic for Pallet<T> {
117 type Public = AuthorityId;
118}
119
120impl<T: Config> OneSessionHandler<T::AccountId> for Pallet<T> {
121 type Key = AuthorityId;
122
123 fn on_genesis_session<'a, I: 'a>(authorities: I)
124 where
125 I: Iterator<Item = (&'a T::AccountId, Self::Key)>,
126 {
127 Self::initialize_keys(&authorities.map(|x| x.1).collect::<Vec<_>>());
128 }
129
130 fn on_new_session<'a, I: 'a>(changed: bool, validators: I, queued_validators: I)
131 where
132 I: Iterator<Item = (&'a T::AccountId, Self::Key)>,
133 {
134 if changed {
136 let keys = validators.map(|x| x.1).collect::<Vec<_>>();
137
138 let bounded_keys = WeakBoundedVec::<_, T::MaxAuthorities>::force_from(
139 keys,
140 Some(
141 "Warning: The session has more validators than expected. \
142 A runtime configuration adjustment may be needed.",
143 ),
144 );
145
146 Keys::<T>::put(bounded_keys);
147 }
148
149 let next_keys = queued_validators.map(|x| x.1).collect::<Vec<_>>();
152
153 let next_bounded_keys = WeakBoundedVec::<_, T::MaxAuthorities>::force_from(
154 next_keys,
155 Some(
156 "Warning: The session has more queued validators than expected. \
157 A runtime configuration adjustment may be needed.",
158 ),
159 );
160
161 NextKeys::<T>::put(next_bounded_keys);
162 }
163
164 fn on_disabled(_i: u32) {
165 }
167}
168
169#[cfg(test)]
170mod tests {
171 use super::*;
172 use crate as pallet_authority_discovery;
173 use alloc::vec;
174 use frame_support::{derive_impl, parameter_types, traits::ConstU32};
175 use sp_application_crypto::Pair;
176 use sp_authority_discovery::AuthorityPair;
177 use sp_core::crypto::key_types;
178 use sp_io::TestExternalities;
179 use sp_runtime::{
180 testing::UintAuthorityId,
181 traits::{ConvertInto, IdentityLookup, OpaqueKeys},
182 BuildStorage, KeyTypeId, Perbill,
183 };
184
185 type Block = frame_system::mocking::MockBlock<Test>;
186
187 frame_support::construct_runtime!(
188 pub enum Test
189 {
190 System: frame_system,
191 Session: pallet_session,
192 Balances: pallet_balances,
193 AuthorityDiscovery: pallet_authority_discovery,
194 }
195 );
196
197 parameter_types! {
198 pub const DisabledValidatorsThreshold: Perbill = Perbill::from_percent(33);
199 }
200
201 impl Config for Test {
202 type MaxAuthorities = ConstU32<100>;
203 }
204
205 impl pallet_session::Config for Test {
206 type SessionManager = ();
207 type Keys = UintAuthorityId;
208 type ShouldEndSession = pallet_session::PeriodicSessions<Period, Offset>;
209 type SessionHandler = TestSessionHandler;
210 type RuntimeEvent = RuntimeEvent;
211 type ValidatorId = AuthorityId;
212 type ValidatorIdOf = ConvertInto;
213 type NextSessionRotation = pallet_session::PeriodicSessions<Period, Offset>;
214 type DisablingStrategy = ();
215 type WeightInfo = ();
216 type Currency = Balances;
217 type KeyDeposit = ();
218 }
219
220 pub type BlockNumber = u64;
221
222 parameter_types! {
223 pub const Period: BlockNumber = 1;
224 pub const Offset: BlockNumber = 0;
225 }
226
227 #[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
228 impl frame_system::Config for Test {
229 type AccountId = AuthorityId;
230 type Lookup = IdentityLookup<Self::AccountId>;
231 type Block = Block;
232 type AccountData = pallet_balances::AccountData<u64>;
233 }
234
235 #[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)]
236 impl pallet_balances::Config for Test {
237 type AccountStore = System;
238 }
239
240 pub struct TestSessionHandler;
241 impl pallet_session::SessionHandler<AuthorityId> for TestSessionHandler {
242 const KEY_TYPE_IDS: &'static [KeyTypeId] = &[key_types::DUMMY];
243
244 fn on_new_session<Ks: OpaqueKeys>(
245 _changed: bool,
246 _validators: &[(AuthorityId, Ks)],
247 _queued_validators: &[(AuthorityId, Ks)],
248 ) {
249 }
250
251 fn on_disabled(_validator_index: u32) {}
252
253 fn on_genesis_session<Ks: OpaqueKeys>(_validators: &[(AuthorityId, Ks)]) {}
254 }
255
256 #[test]
257 fn authorities_returns_current_and_next_authority_set() {
258 let account_id = AuthorityPair::from_seed_slice(vec![10; 32].as_ref()).unwrap().public();
262
263 let mut first_authorities: Vec<AuthorityId> = vec![0, 1]
264 .into_iter()
265 .map(|i| AuthorityPair::from_seed_slice(vec![i; 32].as_ref()).unwrap().public())
266 .map(AuthorityId::from)
267 .collect();
268
269 let second_authorities: Vec<AuthorityId> = vec![2, 3]
270 .into_iter()
271 .map(|i| AuthorityPair::from_seed_slice(vec![i; 32].as_ref()).unwrap().public())
272 .map(AuthorityId::from)
273 .collect();
274 let second_authorities_and_account_ids = second_authorities
276 .clone()
277 .into_iter()
278 .map(|id| (&account_id, id))
279 .collect::<Vec<(&AuthorityId, AuthorityId)>>();
280
281 let third_authorities: Vec<AuthorityId> = vec![4, 5]
282 .into_iter()
283 .map(|i| AuthorityPair::from_seed_slice(vec![i; 32].as_ref()).unwrap().public())
284 .map(AuthorityId::from)
285 .collect();
286 let third_authorities_and_account_ids = third_authorities
288 .clone()
289 .into_iter()
290 .map(|id| (&account_id, id))
291 .collect::<Vec<(&AuthorityId, AuthorityId)>>();
292
293 let mut fourth_authorities: Vec<AuthorityId> = vec![6, 7]
294 .into_iter()
295 .map(|i| AuthorityPair::from_seed_slice(vec![i; 32].as_ref()).unwrap().public())
296 .map(AuthorityId::from)
297 .collect();
298 let fourth_authorities_and_account_ids = fourth_authorities
300 .clone()
301 .into_iter()
302 .map(|id| (&account_id, id))
303 .collect::<Vec<(&AuthorityId, AuthorityId)>>();
304
305 let mut t = frame_system::GenesisConfig::<Test>::default().build_storage().unwrap();
307
308 pallet_authority_discovery::GenesisConfig::<Test> { keys: vec![], ..Default::default() }
309 .assimilate_storage(&mut t)
310 .unwrap();
311
312 let mut externalities = TestExternalities::new(t);
314
315 externalities.execute_with(|| {
316 use frame_support::traits::OneSessionHandler;
317
318 AuthorityDiscovery::on_genesis_session(
319 first_authorities.iter().map(|id| (id, id.clone())),
320 );
321 first_authorities.sort();
322 let mut authorities_returned = AuthorityDiscovery::authorities();
323 authorities_returned.sort();
324 assert_eq!(first_authorities, authorities_returned);
325
326 AuthorityDiscovery::on_new_session(
328 false,
329 second_authorities_and_account_ids.clone().into_iter(),
330 third_authorities_and_account_ids.clone().into_iter(),
331 );
332 let authorities_returned = AuthorityDiscovery::authorities();
333 let mut first_and_third_authorities = first_authorities
334 .iter()
335 .chain(third_authorities.iter())
336 .cloned()
337 .collect::<Vec<AuthorityId>>();
338 first_and_third_authorities.sort();
339
340 assert_eq!(
341 first_and_third_authorities, authorities_returned,
342 "Expected authority set not to change as `changed` was set to false.",
343 );
344
345 AuthorityDiscovery::on_new_session(
347 true,
348 third_authorities_and_account_ids.into_iter(),
349 fourth_authorities_and_account_ids.clone().into_iter(),
350 );
351
352 let mut third_and_fourth_authorities = third_authorities
353 .iter()
354 .chain(fourth_authorities.iter())
355 .cloned()
356 .collect::<Vec<AuthorityId>>();
357 third_and_fourth_authorities.sort();
358 assert_eq!(
359 third_and_fourth_authorities,
360 AuthorityDiscovery::authorities(),
361 "Expected authority set to contain both the authorities of the new as well as the \
362 next session."
363 );
364
365 AuthorityDiscovery::on_new_session(
367 true,
368 fourth_authorities_and_account_ids.clone().into_iter(),
369 fourth_authorities_and_account_ids.clone().into_iter(),
370 );
371 fourth_authorities.sort();
372 assert_eq!(
373 fourth_authorities,
374 AuthorityDiscovery::authorities(),
375 "Expected authority set to be deduplicated."
376 );
377 });
378 }
379}