Skip to main content

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	use frame_system::pallet_prelude::BlockNumberFor;
42
43	#[pallet::pallet]
44	pub struct Pallet<T>(_);
45
46	#[pallet::config]
47	/// The pallet's config trait.
48	pub trait Config: frame_system::Config + pallet_session::Config {
49		/// The maximum number of authorities that can be added.
50		type MaxAuthorities: Get<u32>;
51	}
52
53	#[pallet::storage]
54	/// Keys of the current authority set.
55	pub type Keys<T: Config> =
56		StorageValue<_, WeakBoundedVec<AuthorityId, T::MaxAuthorities>, ValueQuery>;
57
58	#[pallet::storage]
59	/// Keys of the next authority set.
60	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	/// Retrieve authority identifiers of the current and next authority set
89	/// sorted and deduplicated.
90	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	/// Retrieve authority identifiers of the current authority set in the original order.
102	pub fn current_authorities() -> WeakBoundedVec<AuthorityId, T::MaxAuthorities> {
103		Keys::<T>::get()
104	}
105
106	/// Retrieve authority identifiers of the next authority set in the original order.
107	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		// Remember who the authorities are for the new and next session.
144		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		// `changed` represents if queued_validators changed in the previous session not in the
159		// current one.
160		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		// ignore
175	}
176}
177
178#[cfg(any(feature = "try-runtime", test))]
179impl<T: Config> Pallet<T> {
180	/// Ensure the correctness of the state of this pallet.
181	///
182	/// # Invariants
183	///
184	/// * `Keys` must not exceed `MaxAuthorities`.
185	/// * `NextKeys` must not exceed `MaxAuthorities`.
186	/// * `Keys` should not contain duplicates.
187	/// * `NextKeys` should not contain duplicates.
188	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		// The whole authority discovery pallet ignores account ids, but we still need them for
302		// `pallet_session::OneSessionHandler::on_new_session`, thus its safe to use the same value
303		// everywhere.
304		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		// Needed for `pallet_session::OneSessionHandler::on_new_session`.
318		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		// Needed for `pallet_session::OneSessionHandler::on_new_session`.
330		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		// Needed for `pallet_session::OneSessionHandler::on_new_session`.
342		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		// Build genesis.
349		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		// Create externalities.
356		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			// Verify state
370			AuthorityDiscovery::do_try_state().unwrap();
371
372			// When `changed` set to false, the authority set should not be updated.
373			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			// Verify state
392			AuthorityDiscovery::do_try_state().unwrap();
393
394			// When `changed` set to true, the authority set should be updated.
395			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			// Verify state
415			AuthorityDiscovery::do_try_state().unwrap();
416
417			// With overlapping authority sets, `authorities()` should return a deduplicated set.
418			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			// Verify state
431			AuthorityDiscovery::do_try_state().unwrap();
432		});
433	}
434}