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	vstaging::transpose_claim_queue, CoreIndex, Id, SessionIndex, ValidatorId, ValidatorIndex,
30};
31use sp_runtime::traits::AtLeast32BitUnsigned;
32
33use rand::{seq::SliceRandom, SeedableRng};
34use rand_chacha::ChaCha20Rng;
35
36use crate::configuration::HostConfiguration;
37
38pub use pallet::*;
39
40// `SESSION_DELAY` is used to delay any changes to Paras registration or configurations.
41// Wait until the session index is 2 larger then the current index to apply any changes,
42// which guarantees that at least one full session has passed before any changes are applied.
43pub(crate) const SESSION_DELAY: SessionIndex = 2;
44
45#[cfg(test)]
46mod tests;
47
48pub mod migration;
49
50/// Information about a relay parent.
51#[derive(Encode, Decode, Default, TypeInfo, Debug)]
52pub struct RelayParentInfo<Hash> {
53	// Relay parent hash
54	pub relay_parent: Hash,
55	// The state root at this block
56	pub state_root: Hash,
57	// Claim queue snapshot, optimized for accessing the assignments by `ParaId`.
58	// For each para we store the cores assigned per depth.
59	pub claim_queue: BTreeMap<Id, BTreeMap<u8, BTreeSet<CoreIndex>>>,
60}
61
62/// Keeps tracks of information about all viable relay parents.
63#[derive(Encode, Decode, Default, TypeInfo)]
64pub struct AllowedRelayParentsTracker<Hash, BlockNumber> {
65	// Information about past relay parents that are viable to build upon.
66	//
67	// They are in ascending chronologic order, so the newest relay parents are at
68	// the back of the deque.
69	buffer: VecDeque<RelayParentInfo<Hash>>,
70
71	// The number of the most recent relay-parent, if any.
72	// If the buffer is empty, this value has no meaning and may
73	// be nonsensical.
74	latest_number: BlockNumber,
75}
76
77impl<Hash: PartialEq + Copy, BlockNumber: AtLeast32BitUnsigned + Copy>
78	AllowedRelayParentsTracker<Hash, BlockNumber>
79{
80	/// Add a new relay-parent to the allowed relay parents, along with info about the header.
81	/// Provide a maximum ancestry length for the buffer, which will cause old relay-parents to be
82	/// pruned.
83	/// If the relay parent hash is already present, do nothing.
84	pub(crate) fn update(
85		&mut self,
86		relay_parent: Hash,
87		state_root: Hash,
88		claim_queue: BTreeMap<CoreIndex, VecDeque<Id>>,
89		number: BlockNumber,
90		max_ancestry_len: u32,
91	) {
92		if self.buffer.iter().any(|info| info.relay_parent == relay_parent) {
93			// Already present.
94			return
95		}
96
97		let claim_queue = transpose_claim_queue(claim_queue);
98
99		// + 1 for the most recent block, which is always allowed.
100		let buffer_size_limit = max_ancestry_len as usize + 1;
101
102		self.buffer.push_back(RelayParentInfo { relay_parent, state_root, claim_queue });
103
104		self.latest_number = number;
105		while self.buffer.len() > buffer_size_limit {
106			let _ = self.buffer.pop_front();
107		}
108
109		// We only allow relay parents within the same sessions, the buffer
110		// gets cleared on session changes.
111	}
112
113	/// Attempt to acquire the state root and block number to be used when building
114	/// upon the given relay-parent.
115	///
116	/// This only succeeds if the relay-parent is one of the allowed relay-parents.
117	/// If a previous relay-parent number is passed, then this only passes if the new relay-parent
118	/// is more recent than the previous.
119	pub(crate) fn acquire_info(
120		&self,
121		relay_parent: Hash,
122		prev: Option<BlockNumber>,
123	) -> Option<(&RelayParentInfo<Hash>, BlockNumber)> {
124		let pos = self.buffer.iter().position(|info| info.relay_parent == relay_parent)?;
125		let age = (self.buffer.len() - 1) - pos;
126		let number = self.latest_number - BlockNumber::from(age as u32);
127
128		if let Some(prev) = prev {
129			if prev > number {
130				return None
131			}
132		}
133
134		Some((&self.buffer[pos], number))
135	}
136
137	/// Returns block number of the earliest block the buffer would contain if
138	/// `now` is pushed into it.
139	pub(crate) fn hypothetical_earliest_block_number(
140		&self,
141		now: BlockNumber,
142		max_ancestry_len: u32,
143	) -> BlockNumber {
144		let allowed_ancestry_len = max_ancestry_len.min(self.buffer.len() as u32);
145
146		now - allowed_ancestry_len.into()
147	}
148}
149
150#[frame_support::pallet]
151pub mod pallet {
152	use super::*;
153
154	const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
155
156	#[pallet::pallet]
157	#[pallet::without_storage_info]
158	#[pallet::storage_version(STORAGE_VERSION)]
159	pub struct Pallet<T>(_);
160
161	#[pallet::config]
162	pub trait Config: frame_system::Config {
163		type DisabledValidators: frame_support::traits::DisabledValidators;
164	}
165
166	/// The current session index.
167	#[pallet::storage]
168	pub type CurrentSessionIndex<T: Config> = StorageValue<_, SessionIndex, ValueQuery>;
169
170	/// All the validators actively participating in parachain consensus.
171	/// Indices are into the broader validator set.
172	#[pallet::storage]
173	pub type ActiveValidatorIndices<T: Config> = StorageValue<_, Vec<ValidatorIndex>, ValueQuery>;
174
175	/// The parachain attestation keys of the validators actively participating in parachain
176	/// consensus. This should be the same length as `ActiveValidatorIndices`.
177	#[pallet::storage]
178	pub type ActiveValidatorKeys<T: Config> = StorageValue<_, Vec<ValidatorId>, ValueQuery>;
179
180	/// All allowed relay-parents.
181	#[pallet::storage]
182	pub(crate) type AllowedRelayParents<T: Config> =
183		StorageValue<_, AllowedRelayParentsTracker<T::Hash, BlockNumberFor<T>>, ValueQuery>;
184
185	#[pallet::call]
186	impl<T: Config> Pallet<T> {}
187}
188
189impl<T: Config> Pallet<T> {
190	/// Called by the initializer to initialize the configuration pallet.
191	pub(crate) fn initializer_initialize(_now: BlockNumberFor<T>) -> Weight {
192		Weight::zero()
193	}
194
195	/// Called by the initializer to finalize the configuration pallet.
196	pub(crate) fn initializer_finalize() {}
197
198	/// Called by the initializer to note that a new session has started.
199	///
200	/// Returns the list of outgoing paras from the actions queue.
201	pub(crate) fn initializer_on_new_session(
202		session_index: SessionIndex,
203		random_seed: [u8; 32],
204		new_config: &HostConfiguration<BlockNumberFor<T>>,
205		all_validators: Vec<ValidatorId>,
206	) -> Vec<ValidatorId> {
207		// Drop allowed relay parents buffer on a session change.
208		//
209		// During the initialization of the next block we always add its parent
210		// to the tracker.
211		//
212		// With asynchronous backing candidates built on top of relay
213		// parent `R` are still restricted by the runtime to be backed
214		// by the group assigned at `number(R) + 1`, which is guaranteed
215		// to be in the current session.
216		AllowedRelayParents::<T>::mutate(|tracker| tracker.buffer.clear());
217
218		CurrentSessionIndex::<T>::set(session_index);
219		let mut rng: ChaCha20Rng = SeedableRng::from_seed(random_seed);
220
221		let mut shuffled_indices: Vec<_> = (0..all_validators.len())
222			.enumerate()
223			.map(|(i, _)| ValidatorIndex(i as _))
224			.collect();
225
226		shuffled_indices.shuffle(&mut rng);
227
228		if let Some(max) = new_config.max_validators {
229			shuffled_indices.truncate(max as usize);
230		}
231
232		let active_validator_keys =
233			crate::util::take_active_subset(&shuffled_indices, &all_validators);
234
235		ActiveValidatorIndices::<T>::set(shuffled_indices);
236		ActiveValidatorKeys::<T>::set(active_validator_keys.clone());
237
238		active_validator_keys
239	}
240
241	/// Return the session index that should be used for any future scheduled changes.
242	pub fn scheduled_session() -> SessionIndex {
243		CurrentSessionIndex::<T>::get().saturating_add(SESSION_DELAY)
244	}
245
246	/// Fetches disabled validators list from session pallet.
247	/// CAVEAT: this might produce incorrect results on session boundaries
248	pub fn disabled_validators() -> Vec<ValidatorIndex> {
249		let shuffled_indices = ActiveValidatorIndices::<T>::get();
250		// mapping from raw validator index to `ValidatorIndex`
251		// this computation is the same within a session, but should be cheap
252		let reverse_index = shuffled_indices
253			.iter()
254			.enumerate()
255			.map(|(i, v)| (v.0, ValidatorIndex(i as u32)))
256			.collect::<BTreeMap<u32, ValidatorIndex>>();
257
258		// we might have disabled validators who are not parachain validators
259		T::DisabledValidators::disabled_validators()
260			.iter()
261			.filter_map(|v| reverse_index.get(v).cloned())
262			.collect()
263	}
264
265	/// Test function for setting the current session index.
266	#[cfg(any(feature = "std", feature = "runtime-benchmarks", test))]
267	pub fn set_session_index(index: SessionIndex) {
268		CurrentSessionIndex::<T>::set(index);
269	}
270
271	#[cfg(any(feature = "std", feature = "runtime-benchmarks", test))]
272	pub fn set_active_validators_ascending(active: Vec<ValidatorId>) {
273		ActiveValidatorIndices::<T>::set(
274			(0..active.len()).map(|i| ValidatorIndex(i as _)).collect(),
275		);
276		ActiveValidatorKeys::<T>::set(active);
277	}
278
279	#[cfg(test)]
280	pub(crate) fn set_active_validators_with_indices(
281		indices: Vec<ValidatorIndex>,
282		keys: Vec<ValidatorId>,
283	) {
284		assert_eq!(indices.len(), keys.len());
285		ActiveValidatorIndices::<T>::set(indices);
286		ActiveValidatorKeys::<T>::set(keys);
287	}
288
289	#[cfg(test)]
290	pub(crate) fn add_allowed_relay_parent(
291		relay_parent: T::Hash,
292		state_root: T::Hash,
293		claim_queue: BTreeMap<CoreIndex, VecDeque<Id>>,
294		number: BlockNumberFor<T>,
295		max_ancestry_len: u32,
296	) {
297		AllowedRelayParents::<T>::mutate(|tracker| {
298			tracker.update(relay_parent, state_root, claim_queue, number, max_ancestry_len)
299		})
300	}
301}