referrerpolicy=no-referrer-when-downgrade

polkadot_runtime_parachains/
shared.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//! A pallet for any shared state that other pallets may want access to.
18//!
19//! To avoid cyclic dependencies, it is important that this pallet is not
20//! dependent on any of the other pallets.
21
22use alloc::{
23	collections::{btree_map::BTreeMap, btree_set::BTreeSet, vec_deque::VecDeque},
24	vec::Vec,
25};
26use frame_support::{pallet_prelude::*, traits::DisabledValidators};
27use frame_system::pallet_prelude::BlockNumberFor;
28use polkadot_primitives::{
29	transpose_claim_queue, vstaging::RelayParentInfo, CoreIndex, Id as ParaId, Id, SessionIndex,
30	ValidatorId, ValidatorIndex,
31};
32use sp_runtime::traits::AtLeast32BitUnsigned;
33
34use rand::{seq::SliceRandom, SeedableRng};
35use rand_chacha::ChaCha20Rng;
36
37use crate::configuration::HostConfiguration;
38
39pub use pallet::*;
40
41// `SESSION_DELAY` is used to delay any changes to Paras registration or configurations.
42// Wait until the session index is 2 larger then the current index to apply any changes,
43// which guarantees that at least one full session has passed before any changes are applied.
44pub(crate) const SESSION_DELAY: SessionIndex = 2;
45
46#[cfg(test)]
47mod tests;
48
49pub mod migration;
50
51/// Information about a scheduling parent.
52#[derive(Encode, Decode, Default, TypeInfo, Debug)]
53pub struct SchedulingParentInfo<Hash> {
54	// Scheduling parent hash
55	pub scheduling_parent: Hash,
56	// Claim queue snapshot, optimized for accessing the assignments by `ParaId`.
57	// For each para we store the cores assigned per depth.
58	pub claim_queue: BTreeMap<Id, BTreeMap<u8, BTreeSet<CoreIndex>>>,
59}
60
61/// Keeps tracks of information about all viable scheduling parents.
62#[derive(Encode, Decode, Default, TypeInfo)]
63pub struct AllowedSchedulingParentsTracker<Hash, BlockNumber> {
64	// Information about past scheduling parents that are viable to use for backing.
65	//
66	// They are in ascending chronologic order, so the newest scheduling parents are at
67	// the back of the deque.
68	buffer: VecDeque<SchedulingParentInfo<Hash>>,
69
70	// The number of the most recent scheduling-parent, if any.
71	// If the buffer is empty, this value has no meaning and may
72	// be nonsensical.
73	latest_number: BlockNumber,
74}
75
76impl<Hash: PartialEq + Copy, BlockNumber: AtLeast32BitUnsigned + Copy>
77	AllowedSchedulingParentsTracker<Hash, BlockNumber>
78{
79	/// Add a new scheduling-parent to the allowed scheduling parents, along with info about the
80	/// header. Provide a maximum ancestry length for the buffer, which will cause old
81	/// scheduling-parents to be pruned.
82	/// If the scheduling parent hash is already present, do nothing.
83	pub(crate) fn update(
84		&mut self,
85		scheduling_parent: Hash,
86		claim_queue: BTreeMap<CoreIndex, VecDeque<Id>>,
87		number: BlockNumber,
88		max_ancestry_len: u32,
89	) {
90		if self.buffer.iter().any(|info| info.scheduling_parent == scheduling_parent) {
91			// Already present.
92			return;
93		}
94
95		let claim_queue = transpose_claim_queue(claim_queue);
96
97		self.buffer.push_back(SchedulingParentInfo { scheduling_parent, claim_queue });
98
99		self.latest_number = number;
100		while self.buffer.len() > (max_ancestry_len as usize) {
101			let _ = self.buffer.pop_front();
102		}
103
104		// We only allow scheduling parents within the same sessions, the buffer
105		// gets cleared on session changes.
106	}
107
108	/// Attempt to acquire the state root and block number to be used when building
109	/// upon the given scheduling-parent.
110	pub(crate) fn acquire_info(
111		&self,
112		scheduling_parent: Hash,
113	) -> Option<(&SchedulingParentInfo<Hash>, BlockNumber)> {
114		let pos = self
115			.buffer
116			.iter()
117			.position(|info| info.scheduling_parent == scheduling_parent)?;
118		let age = (self.buffer.len() - 1) - pos;
119		let number = self.latest_number - BlockNumber::from(age as u32);
120
121		Some((&self.buffer[pos], number))
122	}
123}
124
125#[frame_support::pallet]
126pub mod pallet {
127	use super::*;
128
129	const STORAGE_VERSION: StorageVersion = StorageVersion::new(2);
130
131	#[pallet::pallet]
132	#[pallet::without_storage_info]
133	#[pallet::storage_version(STORAGE_VERSION)]
134	pub struct Pallet<T>(_);
135
136	#[pallet::config]
137	pub trait Config: frame_system::Config {
138		type DisabledValidators: frame_support::traits::DisabledValidators;
139	}
140
141	/// The current session index.
142	#[pallet::storage]
143	pub type CurrentSessionIndex<T: Config> = StorageValue<_, SessionIndex, ValueQuery>;
144
145	/// All the validators actively participating in parachain consensus.
146	/// Indices are into the broader validator set.
147	#[pallet::storage]
148	pub type ActiveValidatorIndices<T: Config> = StorageValue<_, Vec<ValidatorIndex>, ValueQuery>;
149
150	/// The parachain attestation keys of the validators actively participating in parachain
151	/// consensus. This should be the same length as `ActiveValidatorIndices`.
152	#[pallet::storage]
153	pub type ActiveValidatorKeys<T: Config> = StorageValue<_, Vec<ValidatorId>, ValueQuery>;
154
155	/// All allowed scheduling parents.
156	#[pallet::storage]
157	pub(crate) type AllowedSchedulingParents<T: Config> =
158		StorageValue<_, AllowedSchedulingParentsTracker<T::Hash, BlockNumberFor<T>>, ValueQuery>;
159
160	/// All allowed relay parents, keyed by (session_index, relay_parent_hash).
161	#[pallet::storage]
162	pub(crate) type AllowedRelayParents<T: Config> = StorageDoubleMap<
163		_,
164		Twox64Concat,
165		SessionIndex,
166		Blake2_128Concat,
167		T::Hash,
168		RelayParentInfo<T::Hash, BlockNumberFor<T>>,
169	>;
170
171	/// The oldest session index for which we still have relay parent entries in
172	/// `AllowedRelayParents`. Used to efficiently prune all expired sessions
173	/// when `max_relay_parent_session_age` decreases.
174	#[pallet::storage]
175	pub(crate) type OldestRelayParentSession<T: Config> = StorageValue<_, SessionIndex, ValueQuery>;
176
177	/// The minimum relay parent block number for each session that has entries in
178	/// `AllowedRelayParents`. This is the block number of the first relay parent
179	/// added to each session.
180	#[pallet::storage]
181	pub(crate) type MinimumRelayParentNumber<T: Config> =
182		StorageMap<_, Twox64Concat, SessionIndex, BlockNumberFor<T>>;
183
184	#[pallet::call]
185	impl<T: Config> Pallet<T> {}
186}
187
188impl<T: Config> Pallet<T> {
189	/// Called by the initializer to initialize the configuration pallet.
190	pub(crate) fn initializer_initialize(_now: BlockNumberFor<T>) -> Weight {
191		Weight::zero()
192	}
193
194	/// Called by the initializer to finalize the configuration pallet.
195	pub(crate) fn initializer_finalize() {}
196
197	/// Called by the initializer to note that a new session has started.
198	///
199	/// Returns the list of outgoing paras from the actions queue.
200	pub(crate) fn initializer_on_new_session(
201		session_index: SessionIndex,
202		random_seed: [u8; 32],
203		new_config: &HostConfiguration<BlockNumberFor<T>>,
204		all_validators: Vec<ValidatorId>,
205	) -> Vec<ValidatorId> {
206		// Drop allowed scheduling parents buffer on a session change.
207		//
208		// During the initialization of the next block we always add its parent
209		// to the tracker.
210		//
211		// With asynchronous backing candidates built on top of scheduling
212		// parent `R` are still restricted by the runtime to be backed
213		// by the group assigned at `number(R) + 1`, which is guaranteed
214		// to be in the current session.
215		AllowedSchedulingParents::<T>::mutate(|tracker| tracker.buffer.clear());
216
217		CurrentSessionIndex::<T>::set(session_index);
218		let mut rng: ChaCha20Rng = SeedableRng::from_seed(random_seed);
219
220		let mut shuffled_indices: Vec<_> = (0..all_validators.len())
221			.enumerate()
222			.map(|(i, _)| ValidatorIndex(i as _))
223			.collect();
224
225		shuffled_indices.shuffle(&mut rng);
226
227		if let Some(max) = new_config.max_validators {
228			shuffled_indices.truncate(max as usize);
229		}
230
231		let active_validator_keys =
232			crate::util::take_active_subset(&shuffled_indices, &all_validators);
233
234		ActiveValidatorIndices::<T>::set(shuffled_indices);
235		ActiveValidatorKeys::<T>::set(active_validator_keys.clone());
236
237		active_validator_keys
238	}
239
240	/// Return the session index that should be used for any future scheduled changes.
241	pub fn scheduled_session() -> SessionIndex {
242		CurrentSessionIndex::<T>::get().saturating_add(SESSION_DELAY)
243	}
244
245	/// Fetches disabled validators list from session pallet.
246	/// CAVEAT: this might produce incorrect results on session boundaries
247	pub fn disabled_validators() -> Vec<ValidatorIndex> {
248		let shuffled_indices = ActiveValidatorIndices::<T>::get();
249		// mapping from raw validator index to `ValidatorIndex`
250		// this computation is the same within a session, but should be cheap
251		let reverse_index = shuffled_indices
252			.iter()
253			.enumerate()
254			.map(|(i, v)| (v.0, ValidatorIndex(i as u32)))
255			.collect::<BTreeMap<u32, ValidatorIndex>>();
256
257		// we might have disabled validators who are not parachain validators
258		T::DisabledValidators::disabled_validators()
259			.iter()
260			.filter_map(|v| reverse_index.get(v).cloned())
261			.collect()
262	}
263
264	/// Called at the beginning of each block to update the allowed scheduling and relay parents.
265	///
266	/// Adds the parent block as an allowed scheduling parent and relay parent.
267	/// Prunes relay parents from sessions that are older than `max_relay_parent_session_age`.
268	pub fn new_block(
269		hash: T::Hash,
270		cq: BTreeMap<CoreIndex, VecDeque<ParaId>>,
271		block_number: BlockNumberFor<T>,
272		max_ancestry_len: u32,
273		storage_root: T::Hash,
274		session_index: SessionIndex,
275		max_relay_parent_session_age: u32,
276	) {
277		// Update the allowed scheduling parents.
278		AllowedSchedulingParents::<T>::mutate(|tracker| {
279			tracker.update(hash, cq, block_number, max_ancestry_len);
280		});
281
282		// Insert this block's parent as an allowed relay parent for the current session.
283		AllowedRelayParents::<T>::insert(
284			session_index,
285			hash,
286			RelayParentInfo { number: block_number, state_root: storage_root },
287		);
288
289		// Track the minimum relay parent number for this session.
290		// Only set on the first relay parent of the session (subsequent blocks have
291		// higher numbers).
292		if !MinimumRelayParentNumber::<T>::contains_key(session_index) {
293			MinimumRelayParentNumber::<T>::insert(session_index, block_number);
294		}
295
296		// Prune relay parents from sessions that are now too old.
297		let oldest_allowed_session = session_index.saturating_sub(max_relay_parent_session_age);
298		let oldest_stored = OldestRelayParentSession::<T>::get();
299
300		// Only prune and advance the pointer if the allowed oldest session has moved
301		// forward. If max_relay_parent_session_age was increased at runtime,
302		// oldest_allowed_session may be less than oldest_stored; in that case, entries
303		// for those older sessions were already pruned in prior blocks and we must not
304		// move the pointer backward.
305		if oldest_allowed_session > oldest_stored {
306			for expired in oldest_stored..oldest_allowed_session {
307				let _ = AllowedRelayParents::<T>::clear_prefix(expired, u32::MAX, None);
308				MinimumRelayParentNumber::<T>::remove(expired);
309			}
310			OldestRelayParentSession::<T>::set(oldest_allowed_session);
311		}
312	}
313
314	/// Retrieve relay parent info by session index and relay parent hash.
315	pub fn get_relay_parent_info(
316		session_index: SessionIndex,
317		relay_parent: T::Hash,
318	) -> Option<RelayParentInfo<T::Hash, BlockNumberFor<T>>> {
319		AllowedRelayParents::<T>::get(session_index, relay_parent)
320	}
321
322	/// Get the minimum allowed relay parent block number across all sessions.
323	/// Returns the minimum from the oldest session's entry in `MinimumRelayParentNumber`.
324	pub fn get_minimum_relay_parent_number() -> Option<BlockNumberFor<T>> {
325		let oldest_session = OldestRelayParentSession::<T>::get();
326		MinimumRelayParentNumber::<T>::get(oldest_session)
327	}
328
329	/// Test function for setting the current session index.
330	#[cfg(any(feature = "std", feature = "runtime-benchmarks", test))]
331	pub fn set_session_index(index: SessionIndex) {
332		CurrentSessionIndex::<T>::set(index);
333	}
334
335	#[cfg(any(feature = "std", feature = "runtime-benchmarks", test))]
336	pub fn set_active_validators_ascending(active: Vec<ValidatorId>) {
337		ActiveValidatorIndices::<T>::set(
338			(0..active.len()).map(|i| ValidatorIndex(i as _)).collect(),
339		);
340		ActiveValidatorKeys::<T>::set(active);
341	}
342
343	#[cfg(test)]
344	pub(crate) fn set_active_validators_with_indices(
345		indices: Vec<ValidatorIndex>,
346		keys: Vec<ValidatorId>,
347	) {
348		assert_eq!(indices.len(), keys.len());
349		ActiveValidatorIndices::<T>::set(indices);
350		ActiveValidatorKeys::<T>::set(keys);
351	}
352
353	#[cfg(test)]
354	pub(crate) fn add_allowed_scheduling_parent(
355		scheduling_parent: T::Hash,
356		claim_queue: BTreeMap<CoreIndex, VecDeque<Id>>,
357		number: BlockNumberFor<T>,
358		max_ancestry_len: u32,
359	) {
360		AllowedSchedulingParents::<T>::mutate(|tracker| {
361			tracker.update(scheduling_parent, claim_queue, number, max_ancestry_len + 1)
362		});
363
364		// Also populate the AllowedRelayParents DoubleMap so that tests
365		// which call verify_backed_candidate can look up relay parent info.
366		let session_index = CurrentSessionIndex::<T>::get();
367		AllowedRelayParents::<T>::insert(
368			session_index,
369			scheduling_parent,
370			RelayParentInfo { number, state_root: Default::default() },
371		);
372	}
373}