referrerpolicy=no-referrer-when-downgrade

polkadot_runtime_parachains/
initializer.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Polkadot.
3
4// Polkadot is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Polkadot is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
16
17//! This module is responsible for maintaining a consistent initialization order for all other
18//! parachains modules. It's also responsible for finalization and session change notifications.
19//!
20//! This module can throw fatal errors if session-change notifications are received after
21//! initialization.
22
23use crate::{
24	configuration::{self, HostConfiguration},
25	disputes::{self, DisputesHandler as _, SlashingHandler as _},
26	dmp, hrmp, inclusion, paras, scheduler, session_info, shared,
27};
28use alloc::vec::Vec;
29use codec::{Decode, Encode};
30use frame_support::{
31	traits::{OneSessionHandler, Randomness},
32	weights::Weight,
33};
34use frame_system::limits::BlockWeights;
35use polkadot_primitives::{BlockNumber, ConsensusLog, SessionIndex, ValidatorId};
36use scale_info::TypeInfo;
37
38#[cfg(test)]
39mod tests;
40
41#[cfg(feature = "runtime-benchmarks")]
42mod benchmarking;
43
44pub use pallet::*;
45
46/// Information about a session change that has just occurred.
47#[derive(Clone)]
48pub struct SessionChangeNotification<BlockNumber> {
49	/// The new validators in the session.
50	pub validators: Vec<ValidatorId>,
51	/// The queued validators for the following session.
52	pub queued: Vec<ValidatorId>,
53	/// The configuration before handling the session change
54	pub prev_config: HostConfiguration<BlockNumber>,
55	/// The configuration after handling the session change.
56	pub new_config: HostConfiguration<BlockNumber>,
57	/// A secure random seed for the session, gathered from BABE.
58	pub random_seed: [u8; 32],
59	/// New session index.
60	pub session_index: SessionIndex,
61}
62
63/// Inform something about a new session.
64pub trait OnNewSession<N> {
65	/// A new session was started.
66	fn on_new_session(notification: &SessionChangeNotification<N>);
67}
68
69impl<N> OnNewSession<N> for () {
70	fn on_new_session(_: &SessionChangeNotification<N>) {}
71}
72
73/// Number of validators (not only parachain) in a session.
74pub type ValidatorSetCount = u32;
75
76impl<BlockNumber: Default + From<u32>> Default for SessionChangeNotification<BlockNumber> {
77	fn default() -> Self {
78		Self {
79			validators: Vec::new(),
80			queued: Vec::new(),
81			prev_config: HostConfiguration::default(),
82			new_config: HostConfiguration::default(),
83			random_seed: Default::default(),
84			session_index: Default::default(),
85		}
86	}
87}
88
89#[derive(Encode, Decode, TypeInfo)]
90pub(crate) struct BufferedSessionChange {
91	pub validators: Vec<ValidatorId>,
92	pub queued: Vec<ValidatorId>,
93	pub session_index: SessionIndex,
94}
95
96pub trait WeightInfo {
97	fn force_approve(d: u32) -> Weight;
98}
99
100impl WeightInfo for () {
101	fn force_approve(_: u32) -> Weight {
102		BlockWeights::default().max_block
103	}
104}
105
106#[frame_support::pallet]
107pub mod pallet {
108	use super::*;
109	use frame_support::pallet_prelude::*;
110	use frame_system::pallet_prelude::*;
111
112	#[pallet::pallet]
113	#[pallet::without_storage_info]
114	pub struct Pallet<T>(_);
115
116	#[pallet::config]
117	pub trait Config:
118		frame_system::Config
119		+ configuration::Config
120		+ shared::Config
121		+ paras::Config
122		+ scheduler::Config
123		+ inclusion::Config
124		+ session_info::Config
125		+ disputes::Config
126		+ dmp::Config
127		+ hrmp::Config
128	{
129		/// A randomness beacon.
130		type Randomness: Randomness<Self::Hash, BlockNumberFor<Self>>;
131		/// An origin which is allowed to force updates to parachains.
132		type ForceOrigin: EnsureOrigin<<Self as frame_system::Config>::RuntimeOrigin>;
133		/// Temporary hack to call `Coretime::on_new_session` on chains that support `Coretime` or
134		/// to disable it on the ones that don't support it. Can be removed and replaced by a simple
135		/// bound to `coretime::Config` once all chains support it.
136		type CoretimeOnNewSession: OnNewSession<BlockNumberFor<Self>>;
137		/// Weight information for extrinsics in this pallet.
138		type WeightInfo: WeightInfo;
139	}
140
141	/// Whether the parachains modules have been initialized within this block.
142	///
143	/// Semantically a `bool`, but this guarantees it should never hit the trie,
144	/// as this is cleared in `on_finalize` and Frame optimizes `None` values to be empty values.
145	///
146	/// As a `bool`, `set(false)` and `remove()` both lead to the next `get()` being false, but one
147	/// of them writes to the trie and one does not. This confusion makes `Option<()>` more suitable
148	/// for the semantics of this variable.
149	#[pallet::storage]
150	pub(super) type HasInitialized<T: Config> = StorageValue<_, ()>;
151
152	/// Buffered session changes.
153	///
154	/// Typically this will be empty or one element long. Apart from that this item never hits
155	/// the storage.
156	///
157	/// However this is a `Vec` regardless to handle various edge cases that may occur at runtime
158	/// upgrade boundaries or if governance intervenes.
159	#[pallet::storage]
160	pub(crate) type BufferedSessionChanges<T: Config> =
161		StorageValue<_, Vec<BufferedSessionChange>, ValueQuery>;
162
163	#[pallet::hooks]
164	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
165		fn on_initialize(now: BlockNumberFor<T>) -> Weight {
166			// The other modules are initialized in this order:
167			// - Configuration
168			// - Paras
169			// - Scheduler
170			// - Inclusion
171			// - `SessionInfo`
172			// - Disputes
173			// - DMP
174			// - UMP
175			// - HRMP
176			let total_weight = configuration::Pallet::<T>::initializer_initialize(now) +
177				shared::Pallet::<T>::initializer_initialize(now) +
178				paras::Pallet::<T>::initializer_initialize(now) +
179				scheduler::Pallet::<T>::initializer_initialize(now) +
180				inclusion::Pallet::<T>::initializer_initialize(now) +
181				session_info::Pallet::<T>::initializer_initialize(now) +
182				T::DisputesHandler::initializer_initialize(now) +
183				T::SlashingHandler::initializer_initialize(now) +
184				dmp::Pallet::<T>::initializer_initialize(now) +
185				hrmp::Pallet::<T>::initializer_initialize(now);
186
187			HasInitialized::<T>::set(Some(()));
188
189			total_weight
190		}
191
192		fn on_finalize(now: BlockNumberFor<T>) {
193			// reverse initialization order.
194			hrmp::Pallet::<T>::initializer_finalize();
195			dmp::Pallet::<T>::initializer_finalize();
196			T::SlashingHandler::initializer_finalize();
197			T::DisputesHandler::initializer_finalize();
198			session_info::Pallet::<T>::initializer_finalize();
199			inclusion::Pallet::<T>::initializer_finalize();
200			scheduler::Pallet::<T>::initializer_finalize();
201			paras::Pallet::<T>::initializer_finalize(now);
202			shared::Pallet::<T>::initializer_finalize();
203			configuration::Pallet::<T>::initializer_finalize();
204
205			// Apply buffered session changes as the last thing. This way the runtime APIs and the
206			// next block will observe the next session.
207			//
208			// Note that we only apply the last session as all others lasted less than a block
209			// (weirdly).
210			if let Some(BufferedSessionChange { session_index, validators, queued }) =
211				BufferedSessionChanges::<T>::take().pop()
212			{
213				Self::apply_new_session(session_index, validators, queued);
214			}
215
216			HasInitialized::<T>::take();
217		}
218	}
219
220	#[pallet::call]
221	impl<T: Config> Pallet<T> {
222		/// Issue a signal to the consensus engine to forcibly act as though all parachain
223		/// blocks in all relay chain blocks up to and including the given number in the current
224		/// chain are valid and should be finalized.
225		#[pallet::call_index(0)]
226		#[pallet::weight((
227			<T as Config>::WeightInfo::force_approve(
228				frame_system::Pallet::<T>::digest().logs.len() as u32,
229			),
230			DispatchClass::Operational,
231		))]
232		pub fn force_approve(origin: OriginFor<T>, up_to: BlockNumber) -> DispatchResult {
233			T::ForceOrigin::ensure_origin(origin)?;
234
235			frame_system::Pallet::<T>::deposit_log(ConsensusLog::ForceApprove(up_to).into());
236			Ok(())
237		}
238	}
239}
240
241impl<T: Config> Pallet<T> {
242	fn apply_new_session(
243		session_index: SessionIndex,
244		all_validators: Vec<ValidatorId>,
245		queued: Vec<ValidatorId>,
246	) {
247		let random_seed = {
248			let mut buf = [0u8; 32];
249			// TODO: audit usage of randomness API
250			// https://github.com/paritytech/polkadot/issues/2601
251			let (random_hash, _) = T::Randomness::random(&b"paras"[..]);
252			let len = core::cmp::min(32, random_hash.as_ref().len());
253			buf[..len].copy_from_slice(&random_hash.as_ref()[..len]);
254			buf
255		};
256
257		let configuration::SessionChangeOutcome { prev_config, new_config } =
258			configuration::Pallet::<T>::initializer_on_new_session(&session_index);
259		let new_config = new_config.unwrap_or_else(|| prev_config.clone());
260
261		let validators = shared::Pallet::<T>::initializer_on_new_session(
262			session_index,
263			random_seed,
264			&new_config,
265			all_validators,
266		);
267
268		let notification = SessionChangeNotification {
269			validators,
270			queued,
271			prev_config,
272			new_config,
273			random_seed,
274			session_index,
275		};
276
277		let outgoing_paras = paras::Pallet::<T>::initializer_on_new_session(&notification);
278		scheduler::Pallet::<T>::initializer_on_new_session(&notification);
279		inclusion::Pallet::<T>::initializer_on_new_session(&notification, &outgoing_paras);
280		session_info::Pallet::<T>::initializer_on_new_session(&notification);
281		T::DisputesHandler::initializer_on_new_session(&notification);
282		T::SlashingHandler::initializer_on_new_session(session_index);
283		dmp::Pallet::<T>::initializer_on_new_session(&notification, &outgoing_paras);
284		hrmp::Pallet::<T>::initializer_on_new_session(&notification, &outgoing_paras);
285		T::CoretimeOnNewSession::on_new_session(&notification);
286	}
287
288	/// Should be called when a new session occurs. Buffers the session notification to be applied
289	/// at the end of the block. If `queued` is `None`, the `validators` are considered queued.
290	fn on_new_session<'a, I: 'a>(
291		_changed: bool,
292		session_index: SessionIndex,
293		validators: I,
294		queued: Option<I>,
295	) where
296		I: Iterator<Item = (&'a T::AccountId, ValidatorId)>,
297	{
298		let validators: Vec<_> = validators.map(|(_, v)| v).collect();
299		let queued: Vec<_> = if let Some(queued) = queued {
300			queued.map(|(_, v)| v).collect()
301		} else {
302			validators.clone()
303		};
304
305		if session_index == 0 {
306			// Genesis session should be immediately enacted.
307			Self::apply_new_session(0, validators, queued);
308		} else {
309			BufferedSessionChanges::<T>::mutate(|v| {
310				v.push(BufferedSessionChange { validators, queued, session_index })
311			});
312		}
313	}
314
315	// Allow to trigger `on_new_session` in tests, this is needed as long as `pallet_session` is not
316	// implemented in mock.
317	#[cfg(any(test, feature = "runtime-benchmarks"))]
318	pub(crate) fn test_trigger_on_new_session<'a, I: 'a>(
319		changed: bool,
320		session_index: SessionIndex,
321		validators: I,
322		queued: Option<I>,
323	) where
324		I: Iterator<Item = (&'a T::AccountId, ValidatorId)>,
325	{
326		Self::on_new_session(changed, session_index, validators, queued)
327	}
328
329	/// Return whether at the end of this block a new session will be initialized.
330	pub(crate) fn upcoming_session_change() -> bool {
331		!BufferedSessionChanges::<T>::get().is_empty()
332	}
333}
334
335impl<T: Config> sp_runtime::BoundToRuntimeAppPublic for Pallet<T> {
336	type Public = ValidatorId;
337}
338
339impl<T: pallet_session::Config + Config> OneSessionHandler<T::AccountId> for Pallet<T> {
340	type Key = ValidatorId;
341
342	fn on_genesis_session<'a, I: 'a>(validators: I)
343	where
344		I: Iterator<Item = (&'a T::AccountId, Self::Key)>,
345	{
346		Pallet::<T>::on_new_session(false, 0, validators, None);
347	}
348
349	fn on_new_session<'a, I: 'a>(changed: bool, validators: I, queued: I)
350	where
351		I: Iterator<Item = (&'a T::AccountId, Self::Key)>,
352	{
353		let session_index = pallet_session::Pallet::<T>::current_index();
354		Pallet::<T>::on_new_session(changed, session_index, validators, Some(queued));
355	}
356
357	fn on_disabled(_i: u32) {}
358}