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 use frame_system::pallet_prelude::BlockNumberFor;
42
43 #[pallet::pallet]
44 pub struct Pallet<T>(_);
45
46 #[pallet::config]
47 pub trait Config: frame_system::Config + pallet_session::Config {
49 type MaxAuthorities: Get<u32>;
51 }
52
53 #[pallet::storage]
54 pub type Keys<T: Config> =
56 StorageValue<_, WeakBoundedVec<AuthorityId, T::MaxAuthorities>, ValueQuery>;
57
58 #[pallet::storage]
59 pub type NextKeys<T: Config> =
61 StorageValue<_, WeakBoundedVec<AuthorityId, T::MaxAuthorities>, ValueQuery>;
62
63 #[derive(frame_support::DefaultNoBound)]
64 #[pallet::genesis_config]
65 pub struct GenesisConfig<T: Config> {
66 pub keys: Vec<AuthorityId>,
67 #[serde(skip)]
68 pub _config: core::marker::PhantomData<T>,
69 }
70
71 #[pallet::genesis_build]
72 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
73 fn build(&self) {
74 Pallet::<T>::initialize_keys(&self.keys)
75 }
76 }
77
78 #[pallet::hooks]
79 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
80 #[cfg(feature = "try-runtime")]
81 fn try_state(_n: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
82 Self::do_try_state()
83 }
84 }
85}
86
87impl<T: Config> Pallet<T> {
88 pub fn authorities() -> Vec<AuthorityId> {
91 let mut keys = Keys::<T>::get().to_vec();
92 let next = NextKeys::<T>::get().to_vec();
93
94 keys.extend(next);
95 keys.sort();
96 keys.dedup();
97
98 keys.to_vec()
99 }
100
101 pub fn current_authorities() -> WeakBoundedVec<AuthorityId, T::MaxAuthorities> {
103 Keys::<T>::get()
104 }
105
106 pub fn next_authorities() -> WeakBoundedVec<AuthorityId, T::MaxAuthorities> {
108 NextKeys::<T>::get()
109 }
110
111 fn initialize_keys(keys: &Vec<AuthorityId>) {
112 if !keys.is_empty() {
113 assert!(Keys::<T>::get().is_empty(), "Keys are already initialized!");
114
115 let bounded_keys =
116 WeakBoundedVec::<AuthorityId, T::MaxAuthorities>::try_from((*keys).clone())
117 .expect("Keys vec too big");
118
119 Keys::<T>::put(&bounded_keys);
120 NextKeys::<T>::put(&bounded_keys);
121 }
122 }
123}
124
125impl<T: Config> sp_runtime::BoundToRuntimeAppPublic for Pallet<T> {
126 type Public = AuthorityId;
127}
128
129impl<T: Config> OneSessionHandler<T::AccountId> for Pallet<T> {
130 type Key = AuthorityId;
131
132 fn on_genesis_session<'a, I: 'a>(authorities: I)
133 where
134 I: Iterator<Item = (&'a T::AccountId, Self::Key)>,
135 {
136 Self::initialize_keys(&authorities.map(|x| x.1).collect::<Vec<_>>());
137 }
138
139 fn on_new_session<'a, I: 'a>(changed: bool, validators: I, queued_validators: I)
140 where
141 I: Iterator<Item = (&'a T::AccountId, Self::Key)>,
142 {
143 if changed {
145 let keys = validators.map(|x| x.1).collect::<Vec<_>>();
146
147 let bounded_keys = WeakBoundedVec::<_, T::MaxAuthorities>::force_from(
148 keys,
149 Some(
150 "Warning: The session has more validators than expected. \
151 A runtime configuration adjustment may be needed.",
152 ),
153 );
154
155 Keys::<T>::put(bounded_keys);
156 }
157
158 let next_keys = queued_validators.map(|x| x.1).collect::<Vec<_>>();
161
162 let next_bounded_keys = WeakBoundedVec::<_, T::MaxAuthorities>::force_from(
163 next_keys,
164 Some(
165 "Warning: The session has more queued validators than expected. \
166 A runtime configuration adjustment may be needed.",
167 ),
168 );
169
170 NextKeys::<T>::put(next_bounded_keys);
171 }
172
173 fn on_disabled(_i: u32) {
174 }
176}
177
178#[cfg(any(feature = "try-runtime", test))]
179impl<T: Config> Pallet<T> {
180 pub fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> {
189 use frame_support::ensure;
190 let keys = Keys::<T>::get();
191 ensure!(keys.len() as u32 <= T::MaxAuthorities::get(), "Keys exceeds MaxAuthorities");
192 let mut sorted_keys = keys.to_vec();
193 sorted_keys.sort();
194 ensure!(sorted_keys.windows(2).all(|w| w[0] != w[1]), "Duplicate keys found in Keys");
195
196 let next_keys = NextKeys::<T>::get();
197 ensure!(
198 next_keys.len() as u32 <= T::MaxAuthorities::get(),
199 "NextKeys exceeds MaxAuthorities"
200 );
201 let mut sorted_next_keys = next_keys.to_vec();
202 sorted_next_keys.sort();
203 ensure!(
204 sorted_next_keys.windows(2).all(|w| w[0] != w[1]),
205 "Duplicate keys found in NextKeys"
206 );
207
208 Ok(())
209 }
210}
211
212#[cfg(test)]
213mod tests {
214 use super::*;
215 use crate as pallet_authority_discovery;
216 use alloc::vec;
217 use frame_support::{derive_impl, parameter_types, traits::ConstU32};
218 use sp_application_crypto::Pair;
219 use sp_authority_discovery::AuthorityPair;
220 use sp_core::crypto::key_types;
221 use sp_io::TestExternalities;
222 use sp_runtime::{
223 testing::UintAuthorityId,
224 traits::{ConvertInto, IdentityLookup, OpaqueKeys},
225 BuildStorage, KeyTypeId, Perbill,
226 };
227
228 type Block = frame_system::mocking::MockBlock<Test>;
229
230 frame_support::construct_runtime!(
231 pub enum Test
232 {
233 System: frame_system,
234 Session: pallet_session,
235 Balances: pallet_balances,
236 AuthorityDiscovery: pallet_authority_discovery,
237 }
238 );
239
240 parameter_types! {
241 pub const DisabledValidatorsThreshold: Perbill = Perbill::from_percent(33);
242 }
243
244 impl Config for Test {
245 type MaxAuthorities = ConstU32<100>;
246 }
247
248 impl pallet_session::Config for Test {
249 type SessionManager = ();
250 type Keys = UintAuthorityId;
251 type ShouldEndSession = pallet_session::PeriodicSessions<Period, Offset>;
252 type SessionHandler = TestSessionHandler;
253 type RuntimeEvent = RuntimeEvent;
254 type ValidatorId = AuthorityId;
255 type ValidatorIdOf = ConvertInto;
256 type NextSessionRotation = pallet_session::PeriodicSessions<Period, Offset>;
257 type DisablingStrategy = ();
258 type WeightInfo = ();
259 type Currency = Balances;
260 type KeyDeposit = ();
261 }
262
263 pub type BlockNumber = u64;
264
265 parameter_types! {
266 pub const Period: BlockNumber = 1;
267 pub const Offset: BlockNumber = 0;
268 }
269
270 #[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
271 impl frame_system::Config for Test {
272 type AccountId = AuthorityId;
273 type Lookup = IdentityLookup<Self::AccountId>;
274 type Block = Block;
275 type AccountData = pallet_balances::AccountData<u64>;
276 }
277
278 #[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)]
279 impl pallet_balances::Config for Test {
280 type AccountStore = System;
281 }
282
283 pub struct TestSessionHandler;
284 impl pallet_session::SessionHandler<AuthorityId> for TestSessionHandler {
285 const KEY_TYPE_IDS: &'static [KeyTypeId] = &[key_types::DUMMY];
286
287 fn on_new_session<Ks: OpaqueKeys>(
288 _changed: bool,
289 _validators: &[(AuthorityId, Ks)],
290 _queued_validators: &[(AuthorityId, Ks)],
291 ) {
292 }
293
294 fn on_disabled(_validator_index: u32) {}
295
296 fn on_genesis_session<Ks: OpaqueKeys>(_validators: &[(AuthorityId, Ks)]) {}
297 }
298
299 #[test]
300 fn authorities_returns_current_and_next_authority_set() {
301 let account_id = AuthorityPair::from_seed_slice(vec![10; 32].as_ref()).unwrap().public();
305
306 let mut first_authorities: Vec<AuthorityId> = vec![0, 1]
307 .into_iter()
308 .map(|i| AuthorityPair::from_seed_slice(vec![i; 32].as_ref()).unwrap().public())
309 .map(AuthorityId::from)
310 .collect();
311
312 let second_authorities: Vec<AuthorityId> = vec![2, 3]
313 .into_iter()
314 .map(|i| AuthorityPair::from_seed_slice(vec![i; 32].as_ref()).unwrap().public())
315 .map(AuthorityId::from)
316 .collect();
317 let second_authorities_and_account_ids = second_authorities
319 .clone()
320 .into_iter()
321 .map(|id| (&account_id, id))
322 .collect::<Vec<(&AuthorityId, AuthorityId)>>();
323
324 let third_authorities: Vec<AuthorityId> = vec![4, 5]
325 .into_iter()
326 .map(|i| AuthorityPair::from_seed_slice(vec![i; 32].as_ref()).unwrap().public())
327 .map(AuthorityId::from)
328 .collect();
329 let third_authorities_and_account_ids = third_authorities
331 .clone()
332 .into_iter()
333 .map(|id| (&account_id, id))
334 .collect::<Vec<(&AuthorityId, AuthorityId)>>();
335
336 let mut fourth_authorities: Vec<AuthorityId> = vec![6, 7]
337 .into_iter()
338 .map(|i| AuthorityPair::from_seed_slice(vec![i; 32].as_ref()).unwrap().public())
339 .map(AuthorityId::from)
340 .collect();
341 let fourth_authorities_and_account_ids = fourth_authorities
343 .clone()
344 .into_iter()
345 .map(|id| (&account_id, id))
346 .collect::<Vec<(&AuthorityId, AuthorityId)>>();
347
348 let mut t = frame_system::GenesisConfig::<Test>::default().build_storage().unwrap();
350
351 pallet_authority_discovery::GenesisConfig::<Test> { keys: vec![], ..Default::default() }
352 .assimilate_storage(&mut t)
353 .unwrap();
354
355 let mut externalities = TestExternalities::new(t);
357
358 externalities.execute_with(|| {
359 use frame_support::traits::OneSessionHandler;
360
361 AuthorityDiscovery::on_genesis_session(
362 first_authorities.iter().map(|id| (id, id.clone())),
363 );
364 first_authorities.sort();
365 let mut authorities_returned = AuthorityDiscovery::authorities();
366 authorities_returned.sort();
367 assert_eq!(first_authorities, authorities_returned);
368
369 AuthorityDiscovery::do_try_state().unwrap();
371
372 AuthorityDiscovery::on_new_session(
374 false,
375 second_authorities_and_account_ids.clone().into_iter(),
376 third_authorities_and_account_ids.clone().into_iter(),
377 );
378 let authorities_returned = AuthorityDiscovery::authorities();
379 let mut first_and_third_authorities = first_authorities
380 .iter()
381 .chain(third_authorities.iter())
382 .cloned()
383 .collect::<Vec<AuthorityId>>();
384 first_and_third_authorities.sort();
385
386 assert_eq!(
387 first_and_third_authorities, authorities_returned,
388 "Expected authority set not to change as `changed` was set to false.",
389 );
390
391 AuthorityDiscovery::do_try_state().unwrap();
393
394 AuthorityDiscovery::on_new_session(
396 true,
397 third_authorities_and_account_ids.into_iter(),
398 fourth_authorities_and_account_ids.clone().into_iter(),
399 );
400
401 let mut third_and_fourth_authorities = third_authorities
402 .iter()
403 .chain(fourth_authorities.iter())
404 .cloned()
405 .collect::<Vec<AuthorityId>>();
406 third_and_fourth_authorities.sort();
407 assert_eq!(
408 third_and_fourth_authorities,
409 AuthorityDiscovery::authorities(),
410 "Expected authority set to contain both the authorities of the new as well as the \
411 next session."
412 );
413
414 AuthorityDiscovery::do_try_state().unwrap();
416
417 AuthorityDiscovery::on_new_session(
419 true,
420 fourth_authorities_and_account_ids.clone().into_iter(),
421 fourth_authorities_and_account_ids.clone().into_iter(),
422 );
423 fourth_authorities.sort();
424 assert_eq!(
425 fourth_authorities,
426 AuthorityDiscovery::authorities(),
427 "Expected authority set to be deduplicated."
428 );
429
430 AuthorityDiscovery::do_try_state().unwrap();
432 });
433 }
434}