initializer.rs 9.94 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.

// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.

//! This module is responsible for maintaining a consistent initialization order for all other
//! parachains modules. It's also responsible for finalization and session change notifications.
//!
//! This module can throw fatal errors if session-change notifications are received after initialization.

use sp_std::prelude::*;
use frame_support::weights::Weight;
asynchronous rob's avatar
asynchronous rob committed
24
use primitives::v1::ValidatorId;
25
use frame_support::{
asynchronous rob's avatar
asynchronous rob committed
26
	decl_storage, decl_module, decl_error, traits::Randomness,
27
};
28
use sp_runtime::traits::One;
29
use parity_scale_codec::{Encode, Decode};
30
31
use crate::{
	configuration::{self, HostConfiguration},
32
	paras, scheduler, inclusion, session_info, dmp, ump, hrmp,
33
};
asynchronous rob's avatar
asynchronous rob committed
34
35

/// Information about a session change that has just occurred.
36
#[derive(Clone)]
asynchronous rob's avatar
asynchronous rob committed
37
38
39
40
41
42
43
44
45
46
47
pub struct SessionChangeNotification<BlockNumber> {
	/// The new validators in the session.
	pub validators: Vec<ValidatorId>,
	/// The qeueud validators for the following session.
	pub queued: Vec<ValidatorId>,
	/// The configuration before handling the session change
	pub prev_config: HostConfiguration<BlockNumber>,
	/// The configuration after handling the session change.
	pub new_config: HostConfiguration<BlockNumber>,
	/// A secure random seed for the session, gathered from BABE.
	pub random_seed: [u8; 32],
asynchronous rob's avatar
asynchronous rob committed
48
49
	/// New session index.
	pub session_index: sp_staking::SessionIndex,
asynchronous rob's avatar
asynchronous rob committed
50
}
51

52
53
54
55
56
57
58
59
60
61
62
63
64
impl<BlockNumber: Default + From<u32>> Default for SessionChangeNotification<BlockNumber> {
	fn default() -> Self {
		Self {
			validators: Vec::new(),
			queued: Vec::new(),
			prev_config: HostConfiguration::default(),
			new_config: HostConfiguration::default(),
			random_seed: Default::default(),
			session_index: Default::default(),
		}
	}
}

65
66
67
68
69
70
71
72
#[derive(Encode, Decode)]
struct BufferedSessionChange<N> {
	apply_at: N,
	validators: Vec<ValidatorId>,
	queued: Vec<ValidatorId>,
	session_index: sp_staking::SessionIndex,
}

73
74
75
76
77
78
79
80
81
82
pub trait Config:
	frame_system::Config
	+ configuration::Config
	+ paras::Config
	+ scheduler::Config
	+ inclusion::Config
	+ session_info::Config
	+ dmp::Config
	+ ump::Config
	+ hrmp::Config
asynchronous rob's avatar
asynchronous rob committed
83
{
asynchronous rob's avatar
asynchronous rob committed
84
85
86
	/// A randomness beacon.
	type Randomness: Randomness<Self::Hash>;
}
87
88

decl_storage! {
89
	trait Store for Module<T: Config> as Initializer {
90
91
92
93
94
95
96
97
98
		/// Whether the parachains modules have been initialized within this block.
		///
		/// Semantically a bool, but this guarantees it should never hit the trie,
		/// as this is cleared in `on_finalize` and Frame optimizes `None` values to be empty values.
		///
		/// As a bool, `set(false)` and `remove()` both lead to the next `get()` being false, but one of
		/// them writes to the trie and one does not. This confusion makes `Option<()>` more suitable for
		/// the semantics of this variable.
		HasInitialized: Option<()>;
99
100
101
102
103
104
105
106
		/// Buffered session changes along with the block number at which they should be applied.
		///
		/// Typically this will be empty or one element long, with the single element having a block
		/// number of the next block.
		///
		/// However this is a `Vec` regardless to handle various edge cases that may occur at runtime
		/// upgrade boundaries or if governance intervenes.
		BufferedSessionChanges: Vec<BufferedSessionChange<T::BlockNumber>>;
107
108
109
110
	}
}

decl_error! {
111
	pub enum Error for Module<T: Config> { }
112
113
114
115
}

decl_module! {
	/// The initializer module.
116
	pub struct Module<T: Config> for enum Call where origin: <T as frame_system::Config>::Origin {
117
118
119
		type Error = Error<T>;

		fn on_initialize(now: T::BlockNumber) -> Weight {
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
			// Apply buffered session changes before initializing modules, so they
			// can be initialized with respect to the current validator set.
			<BufferedSessionChanges<T>>::mutate(|v| {
				let drain_up_to = v.iter().take_while(|b| b.apply_at <= now).count();

				// apply only the last session as all others lasted less than a block (weirdly).
				if let Some(buffered) = v.drain(..drain_up_to).last() {
					Self::apply_new_session(
						buffered.session_index,
						buffered.validators,
						buffered.queued,
					);
				}
			});

135
136
137
138
139
			// The other modules are initialized in this order:
			// - Configuration
			// - Paras
			// - Scheduler
			// - Inclusion
140
			// - SessionInfo
141
			// - Validity
142
143
144
			// - DMP
			// - UMP
			// - HRMP
145
			let total_weight = configuration::Module::<T>::initializer_initialize(now) +
asynchronous rob's avatar
asynchronous rob committed
146
				paras::Module::<T>::initializer_initialize(now) +
asynchronous rob's avatar
asynchronous rob committed
147
				scheduler::Module::<T>::initializer_initialize(now) +
148
				inclusion::Module::<T>::initializer_initialize(now) +
149
				session_info::Module::<T>::initializer_initialize(now) +
150
151
152
				dmp::Module::<T>::initializer_initialize(now) +
				ump::Module::<T>::initializer_initialize(now) +
				hrmp::Module::<T>::initializer_initialize(now);
153
154
155
156
157
158
159

			HasInitialized::set(Some(()));

			total_weight
		}

		fn on_finalize() {
asynchronous rob's avatar
asynchronous rob committed
160
161
			// reverse initialization order.

162
163
164
			hrmp::Module::<T>::initializer_finalize();
			ump::Module::<T>::initializer_finalize();
			dmp::Module::<T>::initializer_finalize();
165
			session_info::Module::<T>::initializer_finalize();
asynchronous rob's avatar
asynchronous rob committed
166
			inclusion::Module::<T>::initializer_finalize();
asynchronous rob's avatar
asynchronous rob committed
167
			scheduler::Module::<T>::initializer_finalize();
168
169
170
171
172
173
174
			paras::Module::<T>::initializer_finalize();
			configuration::Module::<T>::initializer_finalize();
			HasInitialized::take();
		}
	}
}

175
impl<T: Config> Module<T> {
176
	fn apply_new_session(
asynchronous rob's avatar
asynchronous rob committed
177
		session_index: sp_staking::SessionIndex,
178
179
180
		validators: Vec<ValidatorId>,
		queued: Vec<ValidatorId>,
	) {
asynchronous rob's avatar
asynchronous rob committed
181
182
183
184
185
186
187
188
189
190
191
192
		let prev_config = <configuration::Module<T>>::config();

		let random_seed = {
			let mut buf = [0u8; 32];
			let random_hash = T::Randomness::random(&b"paras"[..]);
			let len = sp_std::cmp::min(32, random_hash.as_ref().len());
			buf[..len].copy_from_slice(&random_hash.as_ref()[..len]);
			buf
		};

		// We can't pass the new config into the thing that determines the new config,
		// so we don't pass the `SessionChangeNotification` into this module.
193
		configuration::Module::<T>::initializer_on_new_session(&validators, &queued);
asynchronous rob's avatar
asynchronous rob committed
194
195
196
197
198
199
200
201
202

		let new_config = <configuration::Module<T>>::config();

		let notification = SessionChangeNotification {
			validators,
			queued,
			prev_config,
			new_config,
			random_seed,
asynchronous rob's avatar
asynchronous rob committed
203
			session_index,
asynchronous rob's avatar
asynchronous rob committed
204
205
206
207
		};

		paras::Module::<T>::initializer_on_new_session(&notification);
		scheduler::Module::<T>::initializer_on_new_session(&notification);
asynchronous rob's avatar
asynchronous rob committed
208
		inclusion::Module::<T>::initializer_on_new_session(&notification);
209
		session_info::Module::<T>::initializer_on_new_session(&notification);
210
211
212
		dmp::Module::<T>::initializer_on_new_session(&notification);
		ump::Module::<T>::initializer_on_new_session(&notification);
		hrmp::Module::<T>::initializer_on_new_session(&notification);
213
	}
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232

	/// Should be called when a new session occurs. Buffers the session notification to be applied
	/// at the next block. If `queued` is `None`, the `validators` are considered queued.
	fn on_new_session<'a, I: 'a>(
		_changed: bool,
		session_index: sp_staking::SessionIndex,
		validators: I,
		queued: Option<I>,
	)
		where I: Iterator<Item=(&'a T::AccountId, ValidatorId)>
	{
		let validators: Vec<_> = validators.map(|(_, v)| v).collect();
		let queued: Vec<_> = if let Some(queued) = queued {
			queued.map(|(_, v)| v).collect()
		} else {
			validators.clone()
		};

		<BufferedSessionChanges<T>>::mutate(|v| v.push(BufferedSessionChange {
233
			apply_at: <frame_system::Module<T>>::block_number() + One::one(),
234
235
236
237
238
			validators,
			queued,
			session_index,
		}));
	}
239
240
}

241
impl<T: Config> sp_runtime::BoundToRuntimeAppPublic for Module<T> {
242
243
244
	type Public = ValidatorId;
}

245
impl<T: pallet_session::Config + Config> pallet_session::OneSessionHandler<T::AccountId> for Module<T> {
246
247
248
249
250
251
252
253
254
255
256
	type Key = ValidatorId;

	fn on_genesis_session<'a, I: 'a>(_validators: I)
		where I: Iterator<Item=(&'a T::AccountId, Self::Key)>
	{

	}

	fn on_new_session<'a, I: 'a>(changed: bool, validators: I, queued: I)
		where I: Iterator<Item=(&'a T::AccountId, Self::Key)>
	{
257
		let session_index = <pallet_session::Module<T>>::current_index();
asynchronous rob's avatar
asynchronous rob committed
258
		<Module<T>>::on_new_session(changed, session_index, validators, Some(queued));
259
260
261
262
263
264
265
266
	}

	fn on_disabled(_i: usize) { }
}

#[cfg(test)]
mod tests {
	use super::*;
267
	use crate::mock::{new_test_ext, Initializer, Test, System};
268
269
270
271

	use frame_support::traits::{OnFinalize, OnInitialize};

	#[test]
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
	fn session_change_before_initialize_is_still_buffered_after() {
		new_test_ext(Default::default()).execute_with(|| {
			Initializer::on_new_session(
				false,
				1,
				Vec::new().into_iter(),
				Some(Vec::new().into_iter()),
			);

			let now = System::block_number();
			Initializer::on_initialize(now);

			let v = <BufferedSessionChanges<Test>>::get();
			assert_eq!(v.len(), 1);

			let apply_at = now + 1;
			assert_eq!(v[0].apply_at, apply_at);
		});
	}

	#[test]
	fn session_change_applied_on_initialize() {
294
295
		new_test_ext(Default::default()).execute_with(|| {
			Initializer::on_initialize(1);
296
297

			let now = System::block_number();
asynchronous rob's avatar
asynchronous rob committed
298
299
300
301
302
303
			Initializer::on_new_session(
				false,
				1,
				Vec::new().into_iter(),
				Some(Vec::new().into_iter()),
			);
304
305
306
307

			Initializer::on_initialize(now + 1);

			assert!(<BufferedSessionChanges<Test>>::get().is_empty());
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
		});
	}

	#[test]
	fn sets_flag_on_initialize() {
		new_test_ext(Default::default()).execute_with(|| {
			Initializer::on_initialize(1);

			assert!(HasInitialized::get().is_some());
		})
	}

	#[test]
	fn clears_flag_on_finalize() {
		new_test_ext(Default::default()).execute_with(|| {
			Initializer::on_initialize(1);
			Initializer::on_finalize(1);

			assert!(HasInitialized::get().is_none());
		})
	}
}