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

            
23
use crate::{
24
	configuration::{self, HostConfiguration},
25
	disputes::{self, DisputesHandler as _, SlashingHandler as _},
26
	dmp, hrmp, inclusion, paras, scheduler, session_info, shared,
27
};
28
use alloc::vec::Vec;
29
use codec::{Decode, Encode};
30
use frame_support::{
31
	traits::{OneSessionHandler, Randomness},
32
	weights::Weight,
33
};
34
use frame_system::limits::BlockWeights;
35
use polkadot_primitives::{BlockNumber, ConsensusLog, SessionIndex, ValidatorId};
36
use scale_info::TypeInfo;
37

            
38
#[cfg(test)]
39
mod tests;
40

            
41
#[cfg(feature = "runtime-benchmarks")]
42
mod benchmarking;
43

            
44
pub use pallet::*;
45

            
46
/// Information about a session change that has just occurred.
47
#[derive(Clone)]
48
pub 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.
64
pub trait OnNewSession<N> {
65
	/// A new session was started.
66
	fn on_new_session(notification: &SessionChangeNotification<N>);
67
}
68

            
69
impl<N> OnNewSession<N> for () {
70
135234
	fn on_new_session(_: &SessionChangeNotification<N>) {}
71
}
72

            
73
/// Number of validators (not only parachain) in a session.
74
pub type ValidatorSetCount = u32;
75

            
76
impl<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)]
90
struct BufferedSessionChange {
91
	validators: Vec<ValidatorId>,
92
	queued: Vec<ValidatorId>,
93
	session_index: SessionIndex,
94
}
95

            
96
pub trait WeightInfo {
97
	fn force_approve(d: u32) -> Weight;
98
}
99

            
100
impl WeightInfo for () {
101
	fn force_approve(_: u32) -> Weight {
102
		BlockWeights::default().max_block
103
	}
104
}
105

            
106
407
#[frame_support::pallet]
107
pub mod pallet {
108
	use super::*;
109
	use frame_support::pallet_prelude::*;
110
	use frame_system::pallet_prelude::*;
111

            
112
1698
	#[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
779052
	#[pallet::storage]
150
	pub(super) type HasInitialized<T: Config> = StorageValue<_, ()>;
151

            
152
	/// Buffered session changes along with the block number at which they should be applied.
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
930450
	#[pallet::storage]
160
	pub(super) type BufferedSessionChanges<T: Config> =
161
		StorageValue<_, Vec<BufferedSessionChange>, ValueQuery>;
162

            
163
554367
	#[pallet::hooks]
164
	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
165
194763
		fn on_initialize(now: BlockNumberFor<T>) -> Weight {
166
194763
			// The other modules are initialized in this order:
167
194763
			// - Configuration
168
194763
			// - Paras
169
194763
			// - Scheduler
170
194763
			// - Inclusion
171
194763
			// - `SessionInfo`
172
194763
			// - Disputes
173
194763
			// - DMP
174
194763
			// - UMP
175
194763
			// - HRMP
176
194763
			let total_weight = configuration::Pallet::<T>::initializer_initialize(now) +
177
194763
				shared::Pallet::<T>::initializer_initialize(now) +
178
194763
				paras::Pallet::<T>::initializer_initialize(now) +
179
194763
				scheduler::Pallet::<T>::initializer_initialize(now) +
180
194763
				inclusion::Pallet::<T>::initializer_initialize(now) +
181
194763
				session_info::Pallet::<T>::initializer_initialize(now) +
182
194763
				T::DisputesHandler::initializer_initialize(now) +
183
194763
				T::SlashingHandler::initializer_initialize(now) +
184
194763
				dmp::Pallet::<T>::initializer_initialize(now) +
185
194763
				hrmp::Pallet::<T>::initializer_initialize(now);
186
194763

            
187
194763
			HasInitialized::<T>::set(Some(()));
188
194763

            
189
194763
			total_weight
190
194763
		}
191

            
192
194763
		fn on_finalize(now: BlockNumberFor<T>) {
193
194763
			// reverse initialization order.
194
194763
			hrmp::Pallet::<T>::initializer_finalize();
195
194763
			dmp::Pallet::<T>::initializer_finalize();
196
194763
			T::SlashingHandler::initializer_finalize();
197
194763
			T::DisputesHandler::initializer_finalize();
198
194763
			session_info::Pallet::<T>::initializer_finalize();
199
194763
			inclusion::Pallet::<T>::initializer_finalize();
200
194763
			scheduler::Pallet::<T>::initializer_finalize();
201
194763
			paras::Pallet::<T>::initializer_finalize(now);
202
194763
			shared::Pallet::<T>::initializer_finalize();
203
194763
			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
135231
			if let Some(BufferedSessionChange { session_index, validators, queued }) =
211
194763
				BufferedSessionChanges::<T>::take().pop()
212
135231
			{
213
135231
				Self::apply_new_session(session_index, validators, queued);
214
135231
			}
215

            
216
194763
			HasInitialized::<T>::take();
217
194763
		}
218
	}
219

            
220
2643
	#[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
402
		pub fn force_approve(origin: OriginFor<T>, up_to: BlockNumber) -> DispatchResult {
233
402
			T::ForceOrigin::ensure_origin(origin)?;
234

            
235
			frame_system::Pallet::<T>::deposit_log(ConsensusLog::ForceApprove(up_to).into());
236
			Ok(())
237
		}
238
	}
239
}
240

            
241
impl<T: Config> Pallet<T> {
242
135234
	fn apply_new_session(
243
135234
		session_index: SessionIndex,
244
135234
		all_validators: Vec<ValidatorId>,
245
135234
		queued: Vec<ValidatorId>,
246
135234
	) {
247
135234
		let random_seed = {
248
135234
			let mut buf = [0u8; 32];
249
135234
			// TODO: audit usage of randomness API
250
135234
			// https://github.com/paritytech/polkadot/issues/2601
251
135234
			let (random_hash, _) = T::Randomness::random(&b"paras"[..]);
252
135234
			let len = core::cmp::min(32, random_hash.as_ref().len());
253
135234
			buf[..len].copy_from_slice(&random_hash.as_ref()[..len]);
254
135234
			buf
255
135234
		};
256
135234

            
257
135234
		// inform about upcoming new session
258
135234
		scheduler::Pallet::<T>::pre_new_session();
259
135234

            
260
135234
		let configuration::SessionChangeOutcome { prev_config, new_config } =
261
135234
			configuration::Pallet::<T>::initializer_on_new_session(&session_index);
262
135234
		let new_config = new_config.unwrap_or_else(|| prev_config.clone());
263
135234

            
264
135234
		let validators = shared::Pallet::<T>::initializer_on_new_session(
265
135234
			session_index,
266
135234
			random_seed,
267
135234
			&new_config,
268
135234
			all_validators,
269
135234
		);
270
135234

            
271
135234
		let notification = SessionChangeNotification {
272
135234
			validators,
273
135234
			queued,
274
135234
			prev_config,
275
135234
			new_config,
276
135234
			random_seed,
277
135234
			session_index,
278
135234
		};
279
135234

            
280
135234
		let outgoing_paras = paras::Pallet::<T>::initializer_on_new_session(&notification);
281
135234
		scheduler::Pallet::<T>::initializer_on_new_session(&notification);
282
135234
		inclusion::Pallet::<T>::initializer_on_new_session(&notification, &outgoing_paras);
283
135234
		session_info::Pallet::<T>::initializer_on_new_session(&notification);
284
135234
		T::DisputesHandler::initializer_on_new_session(&notification);
285
135234
		T::SlashingHandler::initializer_on_new_session(session_index);
286
135234
		dmp::Pallet::<T>::initializer_on_new_session(&notification, &outgoing_paras);
287
135234
		hrmp::Pallet::<T>::initializer_on_new_session(&notification, &outgoing_paras);
288
135234
		T::CoretimeOnNewSession::on_new_session(&notification);
289
135234
	}
290

            
291
	/// Should be called when a new session occurs. Buffers the session notification to be applied
292
	/// at the end of the block. If `queued` is `None`, the `validators` are considered queued.
293
135234
	fn on_new_session<'a, I: 'a>(
294
135234
		_changed: bool,
295
135234
		session_index: SessionIndex,
296
135234
		validators: I,
297
135234
		queued: Option<I>,
298
135234
	) where
299
135234
		I: Iterator<Item = (&'a T::AccountId, ValidatorId)>,
300
135234
	{
301
135234
		let validators: Vec<_> = validators.map(|(_, v)| v).collect();
302
135234
		let queued: Vec<_> = if let Some(queued) = queued {
303
135231
			queued.map(|(_, v)| v).collect()
304
		} else {
305
3
			validators.clone()
306
		};
307

            
308
135234
		if session_index == 0 {
309
3
			// Genesis session should be immediately enacted.
310
3
			Self::apply_new_session(0, validators, queued);
311
135232
		} else {
312
180308
			BufferedSessionChanges::<T>::mutate(|v| {
313
135231
				v.push(BufferedSessionChange { validators, queued, session_index })
314
180308
			});
315
135231
		}
316
135234
	}
317

            
318
	// Allow to trigger `on_new_session` in tests, this is needed as long as `pallet_session` is not
319
	// implemented in mock.
320
	#[cfg(any(test, feature = "runtime-benchmarks"))]
321
	pub(crate) fn test_trigger_on_new_session<'a, I: 'a>(
322
		changed: bool,
323
		session_index: SessionIndex,
324
		validators: I,
325
		queued: Option<I>,
326
	) where
327
		I: Iterator<Item = (&'a T::AccountId, ValidatorId)>,
328
	{
329
		Self::on_new_session(changed, session_index, validators, queued)
330
	}
331
}
332

            
333
impl<T: Config> sp_runtime::BoundToRuntimeAppPublic for Pallet<T> {
334
	type Public = ValidatorId;
335
}
336

            
337
impl<T: pallet_session::Config + Config> OneSessionHandler<T::AccountId> for Pallet<T> {
338
	type Key = ValidatorId;
339

            
340
3
	fn on_genesis_session<'a, I: 'a>(validators: I)
341
3
	where
342
3
		I: Iterator<Item = (&'a T::AccountId, Self::Key)>,
343
3
	{
344
3
		Pallet::<T>::on_new_session(false, 0, validators, None);
345
3
	}
346

            
347
135231
	fn on_new_session<'a, I: 'a>(changed: bool, validators: I, queued: I)
348
135231
	where
349
135231
		I: Iterator<Item = (&'a T::AccountId, Self::Key)>,
350
135231
	{
351
135231
		let session_index = pallet_session::Pallet::<T>::current_index();
352
135231
		Pallet::<T>::on_new_session(changed, session_index, validators, Some(queued));
353
135231
	}
354

            
355
	fn on_disabled(_i: u32) {}
356
}