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}