referrerpolicy=no-referrer-when-downgrade

pallet_authority_discovery/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! # Authority discovery pallet.
19//!
20//! This pallet is used by the `client/authority-discovery` and by polkadot's parachain logic
21//! to retrieve the current and the next set of authorities.
22
23// Ensure we're `no_std` when compiling for Wasm.
24#![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	/// The pallet's config trait.
47	pub trait Config: frame_system::Config + pallet_session::Config {
48		/// The maximum number of authorities that can be added.
49		type MaxAuthorities: Get<u32>;
50	}
51
52	#[pallet::storage]
53	/// Keys of the current authority set.
54	pub type Keys<T: Config> =
55		StorageValue<_, WeakBoundedVec<AuthorityId, T::MaxAuthorities>, ValueQuery>;
56
57	#[pallet::storage]
58	/// Keys of the next authority set.
59	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	/// Retrieve authority identifiers of the current and next authority set
80	/// sorted and deduplicated.
81	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	/// Retrieve authority identifiers of the current authority set in the original order.
93	pub fn current_authorities() -> WeakBoundedVec<AuthorityId, T::MaxAuthorities> {
94		Keys::<T>::get()
95	}
96
97	/// Retrieve authority identifiers of the next authority set in the original order.
98	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		// Remember who the authorities are for the new and next session.
135		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		// `changed` represents if queued_validators changed in the previous session not in the
150		// current one.
151		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		// ignore
166	}
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		// The whole authority discovery pallet ignores account ids, but we still need them for
259		// `pallet_session::OneSessionHandler::on_new_session`, thus its safe to use the same value
260		// everywhere.
261		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		// Needed for `pallet_session::OneSessionHandler::on_new_session`.
275		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		// Needed for `pallet_session::OneSessionHandler::on_new_session`.
287		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		// Needed for `pallet_session::OneSessionHandler::on_new_session`.
299		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		// Build genesis.
306		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		// Create externalities.
313		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			// When `changed` set to false, the authority set should not be updated.
327			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			// When `changed` set to true, the authority set should be updated.
346			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			// With overlapping authority sets, `authorities()` should return a deduplicated set.
366			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}