Skip to main content

topsoil_core/traits/
metadata.rs

1// This file is part of Soil.
2
3// Copyright (C) Soil contributors.
4// Copyright (C) Parity Technologies (UK) Ltd.
5// SPDX-License-Identifier: Apache-2.0 OR GPL-3.0-or-later WITH Classpath-exception-2.0
6
7//! Traits for managing information attached to pallets and their constituents.
8
9use alloc::{vec, vec::Vec};
10use codec::{Decode, Encode};
11use core::ops::Add;
12use impl_trait_for_tuples::impl_for_tuples;
13use Debug;
14
15/// Provides information about the pallet itself and its setup in the runtime.
16///
17/// An implementor should be able to provide information about each pallet that
18/// is configured in `construct_runtime!`.
19pub trait PalletInfo {
20	/// Convert the given pallet `P` into its index as configured in the runtime.
21	fn index<P: 'static>() -> Option<usize>;
22	/// Convert the given pallet `P` into its name as configured in the runtime.
23	fn name<P: 'static>() -> Option<&'static str>;
24	/// The two128 hash of name.
25	fn name_hash<P: 'static>() -> Option<[u8; 16]>;
26	/// Convert the given pallet `P` into its Rust module name as used in `construct_runtime!`.
27	fn module_name<P: 'static>() -> Option<&'static str>;
28	/// Convert the given pallet `P` into its containing crate version.
29	fn crate_version<P: 'static>() -> Option<CrateVersion>;
30}
31
32/// Information regarding an instance of a pallet.
33#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
34pub struct PalletInfoData {
35	/// Index of the pallet as configured in the runtime.
36	pub index: usize,
37	/// Name of the pallet as configured in the runtime.
38	pub name: &'static str,
39	/// Name of the Rust module containing the pallet.
40	pub module_name: &'static str,
41	/// Version of the crate containing the pallet.
42	pub crate_version: CrateVersion,
43}
44
45/// Provides information about the pallet itself and its setup in the runtime.
46///
47/// Declare some information and access the information provided by [`PalletInfo`] for a specific
48/// pallet.
49pub trait PalletInfoAccess {
50	/// Index of the pallet as configured in the runtime.
51	fn index() -> usize;
52	/// Name of the pallet as configured in the runtime.
53	fn name() -> &'static str;
54	/// Two128 hash of name.
55	fn name_hash() -> [u8; 16];
56	/// Name of the Rust module containing the pallet.
57	fn module_name() -> &'static str;
58	/// Version of the crate containing the pallet.
59	fn crate_version() -> CrateVersion;
60}
61
62/// Provide information about a bunch of pallets.
63pub trait PalletsInfoAccess {
64	/// The number of pallets' information that this type represents.
65	///
66	/// You probably don't want this function but `infos()` instead.
67	fn count() -> usize {
68		// for backwards compatibility with XCM-3, Mark as deprecated.
69		Self::infos().len()
70	}
71
72	/// All of the pallets' information that this type represents.
73	fn infos() -> Vec<PalletInfoData>;
74}
75
76#[cfg_attr(all(not(feature = "tuples-96"), not(feature = "tuples-128")), impl_for_tuples(64))]
77#[cfg_attr(all(feature = "tuples-96", not(feature = "tuples-128")), impl_for_tuples(96))]
78#[cfg_attr(feature = "tuples-128", impl_for_tuples(128))]
79impl PalletsInfoAccess for Tuple {
80	fn infos() -> Vec<PalletInfoData> {
81		let mut res = vec![];
82		for_tuples!( #( res.extend(Tuple::infos()); )* );
83		res
84	}
85}
86
87/// The function and pallet name of the Call.
88#[derive(Clone, Eq, PartialEq, Default, Debug)]
89pub struct CallMetadata {
90	/// Name of the function.
91	pub function_name: &'static str,
92	/// Name of the pallet to which the function belongs.
93	pub pallet_name: &'static str,
94}
95
96/// Gets the function name of the Call.
97pub trait GetCallName {
98	/// Return all function names in the same order as [`GetCallIndex`].
99	fn get_call_names() -> &'static [&'static str];
100	/// Return the function name of the Call.
101	fn get_call_name(&self) -> &'static str;
102}
103
104/// Gets the function index of the Call.
105pub trait GetCallIndex {
106	/// Return all call indices in the same order as [`GetCallName`].
107	fn get_call_indices() -> &'static [u8];
108	/// Return the index of this Call.
109	fn get_call_index(&self) -> u8;
110}
111
112/// Gets the metadata for the Call - function name and pallet name.
113pub trait GetCallMetadata {
114	/// Return all module names.
115	fn get_module_names() -> &'static [&'static str];
116	/// Return all function names for the given `module`.
117	fn get_call_names(module: &str) -> &'static [&'static str];
118	/// Return a [`CallMetadata`], containing function and pallet name of the Call.
119	fn get_call_metadata(&self) -> CallMetadata;
120}
121
122/// The version of a crate.
123#[derive(Debug, Eq, PartialEq, Encode, Decode, Clone, Copy, Default)]
124pub struct CrateVersion {
125	/// The major version of the crate.
126	pub major: u16,
127	/// The minor version of the crate.
128	pub minor: u8,
129	/// The patch version of the crate.
130	pub patch: u8,
131}
132
133impl CrateVersion {
134	pub const fn new(major: u16, minor: u8, patch: u8) -> Self {
135		Self { major, minor, patch }
136	}
137}
138
139impl Ord for CrateVersion {
140	fn cmp(&self, other: &Self) -> core::cmp::Ordering {
141		self.major
142			.cmp(&other.major)
143			.then_with(|| self.minor.cmp(&other.minor).then_with(|| self.patch.cmp(&other.patch)))
144	}
145}
146
147impl PartialOrd for CrateVersion {
148	fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
149		Some(<Self as Ord>::cmp(self, other))
150	}
151}
152
153/// The storage key postfix that is used to store the [`StorageVersion`] per pallet.
154///
155/// The full storage key is built by using:
156/// Twox128([`PalletInfo::name`]) ++ Twox128([`STORAGE_VERSION_STORAGE_KEY_POSTFIX`])
157pub const STORAGE_VERSION_STORAGE_KEY_POSTFIX: &[u8] = b":__STORAGE_VERSION__:";
158
159/// The storage version of a pallet.
160///
161/// Each storage version of a pallet is stored in the state under a fixed key. See
162/// [`STORAGE_VERSION_STORAGE_KEY_POSTFIX`] for how this key is built.
163#[derive(Debug, Eq, PartialEq, Encode, Decode, Ord, Clone, Copy, PartialOrd, Default)]
164pub struct StorageVersion(u16);
165
166impl StorageVersion {
167	/// Creates a new instance of `Self`.
168	pub const fn new(version: u16) -> Self {
169		Self(version)
170	}
171
172	/// Returns the storage key for a storage version.
173	///
174	/// See [`STORAGE_VERSION_STORAGE_KEY_POSTFIX`] on how this key is built.
175	pub fn storage_key<P: PalletInfoAccess>() -> [u8; 32] {
176		let pallet_name = P::name();
177		crate::storage::storage_prefix(pallet_name.as_bytes(), STORAGE_VERSION_STORAGE_KEY_POSTFIX)
178	}
179
180	/// Put this storage version for the given pallet into the storage.
181	///
182	/// It will use the storage key that is associated with the given `Pallet`.
183	///
184	/// # Panics
185	///
186	/// This function will panic iff `Pallet` can not be found by `PalletInfo`.
187	/// In a runtime that is put together using
188	/// [`construct_runtime!`](crate::construct_runtime) this should never happen.
189	///
190	/// It will also panic if this function isn't executed in an externalities
191	/// provided environment.
192	pub fn put<P: PalletInfoAccess>(&self) {
193		let key = Self::storage_key::<P>();
194
195		crate::storage::unhashed::put(&key, self);
196	}
197
198	/// Get the storage version of the given pallet from the storage.
199	///
200	/// It will use the storage key that is associated with the given `Pallet`.
201	///
202	/// # Panics
203	///
204	/// This function will panic iff `Pallet` can not be found by `PalletInfo`.
205	/// In a runtime that is put together using
206	/// [`construct_runtime!`](crate::construct_runtime) this should never happen.
207	///
208	/// It will also panic if this function isn't executed in an externalities
209	/// provided environment.
210	pub fn get<P: PalletInfoAccess>() -> Self {
211		let key = Self::storage_key::<P>();
212
213		crate::storage::unhashed::get_or_default(&key)
214	}
215
216	/// Returns if the storage version key for the given pallet exists in storage.
217	///
218	/// See [`STORAGE_VERSION_STORAGE_KEY_POSTFIX`] on how this key is built.
219	///
220	/// # Panics
221	///
222	/// This function will panic iff `Pallet` can not be found by `PalletInfo`.
223	/// In a runtime that is put together using
224	/// [`construct_runtime!`](crate::construct_runtime) this should never happen.
225	///
226	/// It will also panic if this function isn't executed in an externalities
227	/// provided environment.
228	pub fn exists<P: PalletInfoAccess>() -> bool {
229		let key = Self::storage_key::<P>();
230		crate::storage::unhashed::exists(&key)
231	}
232}
233
234impl PartialEq<u16> for StorageVersion {
235	fn eq(&self, other: &u16) -> bool {
236		self.0 == *other
237	}
238}
239
240impl PartialOrd<u16> for StorageVersion {
241	fn partial_cmp(&self, other: &u16) -> Option<core::cmp::Ordering> {
242		Some(self.0.cmp(other))
243	}
244}
245
246impl Add<u16> for StorageVersion {
247	type Output = StorageVersion;
248
249	fn add(self, rhs: u16) -> Self::Output {
250		Self::new(self.0 + rhs)
251	}
252}
253
254/// Special marker struct used when [`storage_version`](crate::pallet_macros::storage_version) is
255/// not defined for a pallet.
256///
257/// If you (the reader) end up here, it probably means that you tried to compare
258/// [`GetStorageVersion::on_chain_storage_version`] against
259/// [`GetStorageVersion::in_code_storage_version`]. This basically means that the
260/// [`storage_version`](crate::pallet_macros::storage_version) is missing from the pallet where the
261/// mentioned functions are being called, and needs to be defined.
262#[derive(Debug, Default)]
263pub struct NoStorageVersionSet;
264
265/// Provides information about a pallet's storage versions.
266///
267/// Every pallet has two storage versions:
268/// 1. An in-code storage version
269/// 2. An on-chain storage version
270///
271/// The in-code storage version is the version of the pallet as defined in the runtime blob, and the
272/// on-chain storage version is the version of the pallet stored on-chain.
273///
274/// Storage versions should be only ever be out of sync when a pallet has been updated to a new
275/// version and the in-code version is incremented, but the migration has not yet been executed
276/// on-chain as part of a runtime upgrade.
277///
278/// It is the responsibility of the developer to ensure that the on-chain storage version is set
279/// correctly during a migration so that it matches the in-code storage version.
280pub trait GetStorageVersion {
281	/// This type is generated by the [`pallet`](crate::pallet) macro.
282	///
283	/// If the [`storage_version`](crate::pallet_macros::storage_version) attribute isn't specified,
284	/// this is set to [`NoStorageVersionSet`] to signify that it is missing.
285	///
286	/// If the [`storage_version`](crate::pallet_macros::storage_version) attribute is specified,
287	/// this is be set to a [`StorageVersion`] corresponding to the attribute.
288	///
289	/// The intention of using [`NoStorageVersionSet`] instead of defaulting to a [`StorageVersion`]
290	/// of zero is to prevent developers from forgetting to set
291	/// [`storage_version`](crate::pallet_macros::storage_version) when it is required, like in the
292	/// case that they wish to compare the in-code storage version to the on-chain storage version.
293	type InCodeStorageVersion;
294
295	#[deprecated(
296		note = "This method has been renamed to `in_code_storage_version` and will be removed after March 2024."
297	)]
298	/// DEPRECATED: Use [`Self::current_storage_version`] instead.
299	///
300	/// Returns the in-code storage version as specified in the
301	/// [`storage_version`](crate::pallet_macros::storage_version) attribute, or
302	/// [`NoStorageVersionSet`] if the attribute is missing.
303	fn current_storage_version() -> Self::InCodeStorageVersion {
304		Self::in_code_storage_version()
305	}
306
307	/// Returns the in-code storage version as specified in the
308	/// [`storage_version`](crate::pallet_macros::storage_version) attribute, or
309	/// [`NoStorageVersionSet`] if the attribute is missing.
310	fn in_code_storage_version() -> Self::InCodeStorageVersion;
311	/// Returns the storage version of the pallet as last set in the actual on-chain storage.
312	fn on_chain_storage_version() -> StorageVersion;
313}
314
315#[cfg(test)]
316mod tests {
317	use super::*;
318	use subsoil_crypto_hashing::twox_128;
319
320	#[allow(dead_code)]
321	struct Pallet1;
322	impl PalletInfoAccess for Pallet1 {
323		fn index() -> usize {
324			1
325		}
326		fn name() -> &'static str {
327			"Pallet1"
328		}
329		fn name_hash() -> [u8; 16] {
330			twox_128(Self::name().as_bytes())
331		}
332		fn module_name() -> &'static str {
333			"pallet1"
334		}
335		fn crate_version() -> CrateVersion {
336			CrateVersion::new(1, 0, 0)
337		}
338	}
339	#[allow(dead_code)]
340	struct Pallet2;
341	impl PalletInfoAccess for Pallet2 {
342		fn index() -> usize {
343			2
344		}
345		fn name() -> &'static str {
346			"Pallet2"
347		}
348
349		fn name_hash() -> [u8; 16] {
350			twox_128(Self::name().as_bytes())
351		}
352
353		fn module_name() -> &'static str {
354			"pallet2"
355		}
356		fn crate_version() -> CrateVersion {
357			CrateVersion::new(1, 0, 0)
358		}
359	}
360
361	#[test]
362	fn check_storage_version_ordering() {
363		let version = StorageVersion::new(1);
364		assert!(version == StorageVersion::new(1));
365		assert!(version < StorageVersion::new(2));
366		assert!(version < StorageVersion::new(3));
367
368		let version = StorageVersion::new(2);
369		assert!(version < StorageVersion::new(3));
370		assert!(version > StorageVersion::new(1));
371		assert!(version < StorageVersion::new(5));
372	}
373}