Newer
Older
pub use frame_support_procedural::call;
/// Enforce the index of a variant in the generated `enum Call`.
///
/// See [`call`] for more information.
///
/// All call indexes start from 0, until it encounters a dispatchable function with a
/// defined call index. The dispatchable function that lexically follows the function with
/// a defined call index will have that call index, but incremented by 1, e.g. if there are
/// 3 dispatchable functions `fn foo`, `fn bar` and `fn qux` in that order, and only `fn
/// bar` has a call index of 10, then `fn qux` will have an index of 11, instead of 1.
pub use frame_support_procedural::call_index;
/// Declares the arguments of a [`call`] function to be encoded using
/// [`codec::Compact`].
///
/// This will results in smaller extrinsic encoding.
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
///
/// A common example of `compact` is for numeric values that are often times far far away
/// from their theoretical maximum. For example, in the context of a crypto-currency, the
/// balance of an individual account is oftentimes way less than what the numeric type
/// allows. In all such cases, using `compact` is sensible.
///
/// ```
/// #[frame_support::pallet(dev_mode)]
/// pub mod custom_pallet {
/// # use frame_support::pallet_prelude::*;
/// # use frame_system::pallet_prelude::*;
/// # #[pallet::config]
/// # pub trait Config: frame_system::Config {}
/// # #[pallet::pallet]
/// # pub struct Pallet<T>(_);
/// # use frame_support::traits::BuildGenesisConfig;
/// #[pallet::call]
/// impl<T: Config> Pallet<T> {
/// pub fn some_dispatchable(_origin: OriginFor<T>, #[pallet::compact] _input: u32) -> DispatchResult {
/// Ok(())
/// }
/// }
/// }
pub use frame_support_procedural::compact;
/// Allows you to define the genesis configuration for the pallet.
///
/// Item is defined as either an enum or a struct. It needs to be public and implement the
/// trait [`frame_support::traits::BuildGenesisConfig`].
///
/// See [`genesis_build`] for an example.
pub use frame_support_procedural::genesis_config;
/// Allows you to define how the state of your pallet at genesis is built. This
/// takes as input the `GenesisConfig` type (as `self`) and constructs the pallet's initial
/// state.
///
/// The fields of the `GenesisConfig` can in turn be populated by the chain-spec.
///
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
///
/// ```
/// #[frame_support::pallet]
/// pub mod pallet {
/// # #[pallet::config]
/// # pub trait Config: frame_system::Config {}
/// # #[pallet::pallet]
/// # pub struct Pallet<T>(_);
/// # use frame_support::traits::BuildGenesisConfig;
/// #[pallet::genesis_config]
/// #[derive(frame_support::DefaultNoBound)]
/// pub struct GenesisConfig<T: Config> {
/// foo: Vec<T::AccountId>
/// }
///
/// #[pallet::genesis_build]
/// impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
/// fn build(&self) {
/// // use &self to access fields.
/// let foo = &self.foo;
/// todo!()
/// }
/// }
/// }
/// ```
///
/// ## Former Usage
///
/// Prior to <https://github.com/paritytech/substrate/pull/14306>, the following syntax was used.
/// This is deprecated and will soon be removed.
///
/// ```
/// #[frame_support::pallet]
/// pub mod pallet {
/// # #[pallet::config]
/// # pub trait Config: frame_system::Config {}
/// # #[pallet::pallet]
/// # pub struct Pallet<T>(_);
/// # use frame_support::traits::GenesisBuild;
/// #[pallet::genesis_config]
/// #[derive(frame_support::DefaultNoBound)]
/// pub struct GenesisConfig<T: Config> {
/// foo: Vec<T::AccountId>
/// }
///
/// #[pallet::genesis_build]
/// impl<T: Config> GenesisBuild<T> for GenesisConfig<T> {
/// fn build(&self) {
/// todo!()
/// }
/// }
/// }
/// ```
pub use frame_support_procedural::genesis_build;
/// Allows adding an associated type trait bounded by
/// [`Get`](frame_support::pallet_prelude::Get) from [`pallet::config`](`macro@config`)
/// into metadata.
///
/// ## Example
///
/// ```
/// #[frame_support::pallet]
/// mod pallet {
/// use frame_support::pallet_prelude::*;
/// # #[pallet::pallet]
/// # pub struct Pallet<T>(_);
/// #[pallet::config]
/// pub trait Config: frame_system::Config {
/// /// This is like a normal `Get` trait, but it will be added into metadata.
/// #[pallet::constant]
/// type Foo: Get<u32>;
/// }
/// }
/// ```
pub use frame_support_procedural::constant;
/// Declares a type alias as a storage item.
///
/// Storage items are pointers to data stored on-chain (the *blockchain state*), under a
/// specific key. The exact key is dependent on the type of the storage.
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
///
/// > From the perspective of this pallet, the entire blockchain state is abstracted behind
/// > a key-value api, namely [`sp_io::storage`].
///
/// ## Storage Types
///
/// The following storage types are supported by the `#[storage]` macro. For specific
/// information about each storage type, refer to the documentation of the respective type.
///
/// * [`StorageValue`](crate::storage::types::StorageValue)
/// * [`StorageMap`](crate::storage::types::StorageMap)
/// * [`CountedStorageMap`](crate::storage::types::CountedStorageMap)
/// * [`StorageDoubleMap`](crate::storage::types::StorageDoubleMap)
/// * [`StorageNMap`](crate::storage::types::StorageNMap)
/// * [`CountedStorageNMap`](crate::storage::types::CountedStorageNMap)
///
/// ## Storage Type Usage
///
/// The following details are relevant to all of the aforementioned storage types.
/// Depending on the exact storage type, it may require the following generic parameters:
///
/// * [`Prefix`](#prefixes) - Used to give the storage item a unique key in the underlying
/// storage.
/// * `Key` - Type of the keys used to store the values,
/// * `Value` - Type of the value being stored,
/// * [`Hasher`](#hashers) - Used to ensure the keys of a map are uniformly distributed,
/// * [`QueryKind`](#querykind) - Used to configure how to handle queries to the underlying
/// storage,
/// * `OnEmpty` - Used to handle missing values when querying the underlying storage,
/// * `MaxValues` - _not currently used_.
///
/// Each `Key` type requires its own designated `Hasher` declaration, so that
/// [`StorageDoubleMap`](frame_support::storage::types::StorageDoubleMap) needs two of
/// each, and [`StorageNMap`](frame_support::storage::types::StorageNMap) needs `N` such
/// pairs. Since [`StorageValue`](frame_support::storage::types::StorageValue) only stores
/// a single element, no configuration of hashers is needed.
///
/// ### Syntax
///
/// Two general syntaxes are supported, as demonstrated below:
///
/// 1. Named type parameters, e.g., `type Foo<T> = StorageValue<Value = u32>`.
/// 2. Positional type parameters, e.g., `type Foo<T> = StorageValue<_, u32>`.
///
/// In both instances, declaring the generic parameter `<T>` is mandatory. Optionally, it
/// can also be explicitly declared as `<T: Config>`. In the compiled code, `T` will
/// automatically include the trait bound `Config`.
///
/// Note that in positional syntax, the first generic type parameter must be `_`.
///
/// #### Example
///
/// ```
/// #[frame_support::pallet]
/// mod pallet {
/// # use frame_support::pallet_prelude::*;
/// # #[pallet::config]
/// # pub trait Config: frame_system::Config {}
/// # #[pallet::pallet]
/// # pub struct Pallet<T>(_);
/// /// Positional syntax, without bounding `T`.
/// #[pallet::storage]
/// pub type Foo<T> = StorageValue<_, u32>;
///
/// /// Positional syntax, with bounding `T`.
/// #[pallet::storage]
/// pub type Bar<T: Config> = StorageValue<_, u32>;
///
/// /// Named syntax.
/// #[pallet::storage]
/// pub type Baz<T> = StorageMap<Hasher = Blake2_128Concat, Key = u32, Value = u32>;
/// }
/// ```
///
/// ### QueryKind
///
/// Every storage type mentioned above has a generic type called
/// [`QueryKind`](frame_support::storage::types::QueryKindTrait) that determines its
/// "query" type. This refers to the kind of value returned when querying the storage, for
/// instance, through a `::get()` method.
///
/// There are three types of queries:
///
/// 1. [`OptionQuery`](frame_support::storage::types::OptionQuery): The default query type.
/// It returns `Some(V)` if the value is present, or `None` if it isn't, where `V` is
/// the value type.
/// 2. [`ValueQuery`](frame_support::storage::types::ValueQuery): Returns the value itself
/// if present; otherwise, it returns `Default::default()`. This behavior can be
/// adjusted with the `OnEmpty` generic parameter, which defaults to `OnEmpty =
/// GetDefault`.
/// 3. [`ResultQuery`](frame_support::storage::types::ResultQuery): Returns `Result<V, E>`,
/// where `V` is the value type.
///
/// See [`QueryKind`](frame_support::storage::types::QueryKindTrait) for further examples.
///
/// ### Optimized Appending
///
/// All storage items — such as
/// [`StorageValue`](frame_support::storage::types::StorageValue),
/// [`StorageMap`](frame_support::storage::types::StorageMap), and their variants—offer an
/// `::append()` method optimized for collections. Using this method avoids the
/// inefficiency of decoding and re-encoding entire collections when adding items. For
/// instance, consider the storage declaration `type MyVal<T> = StorageValue<_, Vec<u8>,
/// ValueQuery>`. With `MyVal` storing a large list of bytes, `::append()` lets you
/// directly add bytes to the end in storage without processing the full list. Depending on
/// the storage type, additional key specifications may be needed.
///
/// #### Example
#[doc = docify::embed!("src/lib.rs", example_storage_value_append)]
/// Similarly, there also exists a `::try_append()` method, which can be used when handling
/// types where an append operation might fail, such as a
/// [`BoundedVec`](frame_support::BoundedVec).
///
/// #### Example
#[doc = docify::embed!("src/lib.rs", example_storage_value_try_append)]
/// ### Optimized Length Decoding
///
/// All storage items — such as
/// [`StorageValue`](frame_support::storage::types::StorageValue),
/// [`StorageMap`](frame_support::storage::types::StorageMap), and their counterparts —
/// incorporate the `::decode_len()` method. This method allows for efficient retrieval of
/// a collection's length without the necessity of decoding the entire dataset.
/// #### Example
#[doc = docify::embed!("src/lib.rs", example_storage_value_decode_len)]
/// ### Hashers
///
/// For all storage types, except
/// [`StorageValue`](frame_support::storage::types::StorageValue), a set of hashers needs
/// to be specified. The choice of hashers is crucial, especially in production chains. The
/// purpose of storage hashers in maps is to ensure the keys of a map are
/// uniformly distributed. An unbalanced map/trie can lead to inefficient performance.
///
/// In general, hashers are categorized as either cryptographically secure or not. The
/// former is slower than the latter. `Blake2` and `Twox` serve as examples of each,
/// respectively.
///
/// As a rule of thumb:
///
/// 1. If the map keys are not controlled by end users, or are cryptographically secure by
/// definition (e.g., `AccountId`), then the use of cryptographically secure hashers is NOT
/// required.
/// 2. If the map keys are controllable by the end users, cryptographically secure hashers
/// should be used.
///
/// For more information, look at the types that implement
/// [`frame_support::StorageHasher`](frame_support::StorageHasher).
///
/// Lastly, it's recommended for hashers with "concat" to have reversible hashes. Refer to
/// the implementors section of
/// [`hash::ReversibleStorageHasher`](frame_support::hash::ReversibleStorageHasher).
///
/// ### Prefixes
///
/// Internally, every storage type generates a "prefix". This prefix serves as the initial
/// segment of the key utilized to store values in the on-chain state (i.e., the final key
/// used in [`sp_io::storage`](sp_io::storage)). For all storage types, the following rule
/// applies:
///
/// > The storage prefix begins with `twox128(pallet_prefix) ++ twox128(STORAGE_PREFIX)`,
/// > where
/// > `pallet_prefix` is the name assigned to the pallet instance in
/// > [`frame_support::construct_runtime`](frame_support::construct_runtime), and
/// > `STORAGE_PREFIX` is the name of the `type` aliased to a particular storage type, such
/// > as
/// > `Foo` in `type Foo<T> = StorageValue<..>`.
///
/// For [`StorageValue`](frame_support::storage::types::StorageValue), no additional key is
/// required. For map types, the prefix is extended with one or more keys defined by the
/// map.
///
/// #### Example
#[doc = docify::embed!("src/lib.rs", example_storage_value_map_prefixes)]
/// ## Related Macros
///
/// The following attribute macros can be used in conjunction with the `#[storage]` macro:
///
/// * [`macro@getter`]: Creates a custom getter function.
/// * [`macro@storage_prefix`]: Overrides the default prefix of the storage item.
/// * [`macro@unbounded`]: Declares the storage item as unbounded.
/// * [`macro@disable_try_decode_storage`]: Declares that try-runtime checks should not
/// attempt to decode the storage item.
///
/// #### Example
/// ```
/// #[frame_support::pallet]
/// mod pallet {
/// # use frame_support::pallet_prelude::*;
/// # #[pallet::config]
/// # pub trait Config: frame_system::Config {}
/// # #[pallet::pallet]
/// # pub struct Pallet<T>(_);
/// /// A kitchen-sink StorageValue, with all possible additional attributes.
/// #[pallet::storage]
/// #[pallet::getter(fn foo)]
/// #[pallet::storage_prefix = "OtherFoo"]
/// #[pallet::unbounded]
/// #[pallet::disable_try_decode_storage]
/// pub type Foo<T> = StorageValue<_, u32, ValueQuery>;
/// }
/// ```
pub use frame_support_procedural::storage;
/// Allows defining conditions for a task to run.
///
/// This attribute is attached to a function inside an `impl` block annoated with
/// [`pallet::tasks_experimental`](`tasks_experimental`) to define the conditions for a
/// given work item to be valid.
///
/// It takes a closure as input, which is then used to define the condition. The closure
/// should have the same signature as the function it is attached to, except that it should
/// return a `bool` instead.
pub use frame_support_procedural::task_condition;
/// Allows defining an index for a task.
///
/// This attribute is attached to a function inside an `impl` block annoated with
/// [`pallet::tasks_experimental`](`tasks_experimental`) to define the index of a given
/// work item.
///
/// It takes an integer literal as input, which is then used to define the index. This
/// index should be unique for each function in the `impl` block.
pub use frame_support_procedural::task_index;
/// Allows defining an iterator over available work items for a task.
///
/// This attribute is attached to a function inside an `impl` block annoated with
/// [`pallet::tasks_experimental`](`tasks_experimental`).
///
/// It takes an iterator as input that yields a tuple with same types as the function
/// arguments.
pub use frame_support_procedural::task_list;
/// Allows defining the weight of a task.
///
/// This attribute is attached to a function inside an `impl` block annoated with
/// [`pallet::tasks_experimental`](`tasks_experimental`) define the weight of a given work
/// item.
///
/// It takes a closure as input, which should return a `Weight` value.
pub use frame_support_procedural::task_weight;
/// Allows you to define some service work that can be recognized by a script or an
/// off-chain worker.
///
/// Such a script can then create and submit all such work items at any given time.
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
///
/// These work items are defined as instances of the [`Task`](frame_support::traits::Task)
/// trait. [`pallet:tasks_experimental`](`tasks_experimental`) when attached to an `impl`
/// block inside a pallet, will generate an enum `Task<T>` whose variants are mapped to
/// functions inside this `impl` block.
///
/// Each such function must have the following set of attributes:
///
/// * [`pallet::task_list`](`task_list`)
/// * [`pallet::task_condition`](`task_condition`)
/// * [`pallet::task_weight`](`task_weight`)
/// * [`pallet::task_index`](`task_index`)
///
/// All of such Tasks are then aggregated into a `RuntimeTask` by
/// [`construct_runtime`](frame_support::construct_runtime).
///
/// Finally, the `RuntimeTask` can then used by a script or off-chain worker to create and
/// submit such tasks via an extrinsic defined in `frame_system` called `do_task`.
///
/// ## Example
#[doc = docify::embed!("src/tests/tasks.rs", tasks_example)]
/// Now, this can be executed as follows:
#[doc = docify::embed!("src/tests/tasks.rs", tasks_work)]
pub use frame_support_procedural::tasks_experimental;
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
/// Allows a pallet to declare a type as an origin.
///
/// If defined as such, this type will be amalgamated at the runtime level into
/// `RuntimeOrigin`, very similar to [`call`], [`error`] and [`event`]. See
/// [`composite_enum`] for similar cases.
///
/// Origin is a complex FRAME topics and is further explained in `polkadot_sdk_docs`.
///
/// ## Syntax Variants
///
/// ```
/// #[frame_support::pallet]
/// mod pallet {
/// # use frame_support::pallet_prelude::*;
/// # #[pallet::config]
/// # pub trait Config: frame_system::Config {}
/// # #[pallet::pallet]
/// # pub struct Pallet<T>(_);
/// /// On the spot declaration.
/// #[pallet::origin]
/// #[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)]
/// pub enum Origin {
/// Foo,
/// Bar,
/// }
/// }
/// ```
///
/// Or, more commonly used:
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
///
/// ```
/// #[frame_support::pallet]
/// mod pallet {
/// # use frame_support::pallet_prelude::*;
/// # #[pallet::config]
/// # pub trait Config: frame_system::Config {}
/// # #[pallet::pallet]
/// # pub struct Pallet<T>(_);
/// #[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)]
/// pub enum RawOrigin {
/// Foo,
/// Bar,
/// }
///
/// #[pallet::origin]
/// pub type Origin = RawOrigin;
/// }
/// ```
///
/// ## Warning
///
/// Modifying any pallet's origin type will cause the runtime level origin type to also
/// change in encoding. If stored anywhere on-chain, this will require a data migration.
///
/// Read more about origins at the [Origin Reference
/// Docs](../../polkadot_sdk_docs/reference_docs/frame_origin/index.html).
pub use frame_support_procedural::origin;
![Sam Johnson Sam Johnson's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sam Johnson
committed
}
#[deprecated(note = "Will be removed after July 2023; Use `sp_runtime::traits` directly instead.")]
pub mod error {
#[doc(hidden)]
pub use sp_runtime::traits::{BadOrigin, LookupError};
}
#[doc(inline)]
pub use frame_support_procedural::register_default_impl;
// Generate a macro that will enable/disable code based on `std` feature being active.
sp_core::generate_feature_enabled_macro!(std_enabled, feature = "std", $);
// Helper for implementing GenesisBuilder runtime API
pub mod genesis_builder_helper;
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
#[cfg(test)]
mod test {
// use super::*;
use crate::{
hash::*,
storage::types::{StorageMap, StorageValue, ValueQuery},
traits::{ConstU32, StorageInstance},
BoundedVec,
};
use sp_io::{hashing::twox_128, TestExternalities};
struct Prefix;
impl StorageInstance for Prefix {
fn pallet_prefix() -> &'static str {
"test"
}
const STORAGE_PREFIX: &'static str = "foo";
}
struct Prefix1;
impl StorageInstance for Prefix1 {
fn pallet_prefix() -> &'static str {
"test"
}
const STORAGE_PREFIX: &'static str = "MyVal";
}
struct Prefix2;
impl StorageInstance for Prefix2 {
fn pallet_prefix() -> &'static str {
"test"
}
const STORAGE_PREFIX: &'static str = "MyMap";
}
#[docify::export]
#[test]
pub fn example_storage_value_try_append() {
type MyVal = StorageValue<Prefix, BoundedVec<u8, ConstU32<10>>, ValueQuery>;
TestExternalities::default().execute_with(|| {
MyVal::set(BoundedVec::try_from(vec![42, 43]).unwrap());
assert_eq!(MyVal::get(), vec![42, 43]);
// Try to append a single u32 to BoundedVec stored in `MyVal`
assert_ok!(MyVal::try_append(40));
assert_eq!(MyVal::get(), vec![42, 43, 40]);
});
}
#[docify::export]
#[test]
pub fn example_storage_value_append() {
type MyVal = StorageValue<Prefix, Vec<u8>, ValueQuery>;
TestExternalities::default().execute_with(|| {
MyVal::set(vec![42, 43]);
assert_eq!(MyVal::get(), vec![42, 43]);
// Append a single u32 to Vec stored in `MyVal`
MyVal::append(40);
assert_eq!(MyVal::get(), vec![42, 43, 40]);
});
}
#[docify::export]
#[test]
pub fn example_storage_value_decode_len() {
type MyVal = StorageValue<Prefix, BoundedVec<u8, ConstU32<10>>, ValueQuery>;
TestExternalities::default().execute_with(|| {
MyVal::set(BoundedVec::try_from(vec![42, 43]).unwrap());
assert_eq!(MyVal::decode_len().unwrap(), 2);
});
}
#[docify::export]
#[test]
pub fn example_storage_value_map_prefixes() {
type MyVal = StorageValue<Prefix1, u32, ValueQuery>;
type MyMap = StorageMap<Prefix2, Blake2_128Concat, u16, u32, ValueQuery>;
TestExternalities::default().execute_with(|| {
// This example assumes `pallet_prefix` to be "test"
// Get storage key for `MyVal` StorageValue
assert_eq!(
MyVal::hashed_key().to_vec(),
[twox_128(b"test"), twox_128(b"MyVal")].concat()
);
// Get storage key for `MyMap` StorageMap and `key` = 1
let mut k: Vec<u8> = vec![];
k.extend(&twox_128(b"test"));
k.extend(&twox_128(b"MyMap"));
k.extend(&1u16.blake2_128_concat());
assert_eq!(MyMap::hashed_key_for(1).to_vec(), k);
});
}
}