mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 09:57:56 +00:00
Improve FRAME storage docs (#1714)
This is a port (and hopefully a small improvement) of @kianenigma's PR from the old Substrate repo: https://github.com/paritytech/substrate/pull/13987. Following #1689 I moved the documentation of all macros relevant to this PR from `frame_support_procedural` to `pallet_macros` while including a hint for RA users. Question: Again with respect to #1689: Is there a good reason why we should *not* enhance paths with links to our current rustdocs? For example, instead of ```rust /// **Rust-Analyzer users**: See the documentation of the Rust item in /// `frame_support::pallet_macros::storage`. ``` we could write ```rust /// **Rust-Analyzer users**: See the documentation of the Rust item in /// [`frame_support::pallet_macros::storage`](https://paritytech.github.io/polkadot-sdk/master/frame_support/pallet_macros/attr.storage.html). ``` This results in a clickable link like this: <img width="674" alt="image" src="https://github.com/paritytech/polkadot-sdk/assets/10713977/c129e622-3942-4eeb-8acf-93ee4efdc99d"> I don't really expect the links to become outdated any time soon, but I think this would be a great UX improvement over just having paths. TODOs: - [ ] Add documentation for `constant_name` macro - [x] Add proper documentation for different `QueryKinds`, i.e. `OptionQuery`, `ValueQuery`, `ResultQuery`. One example for each. Custom `OnEmpty` should be moved to `QueryKinds` trait doc page. - [ ] Rework `type_value` docs --------- Co-authored-by: kianenigma <kian@parity.io>
This commit is contained in:
@@ -35,8 +35,8 @@ use sp_metadata_ir::StorageEntryMetadataIR;
|
||||
use sp_runtime::traits::Saturating;
|
||||
use sp_std::prelude::*;
|
||||
|
||||
/// A wrapper around a `StorageMap` and a `StorageValue<Value=u32>` to keep track of how many items
|
||||
/// are in a map, without needing to iterate all the values.
|
||||
/// A wrapper around a [`StorageMap`] and a [`StorageValue`] (with the value being `u32`) to keep
|
||||
/// track of how many items are in a map, without needing to iterate all the values.
|
||||
///
|
||||
/// This storage item has additional storage read and write overhead when manipulating values
|
||||
/// compared to a regular storage map.
|
||||
@@ -47,6 +47,51 @@ use sp_std::prelude::*;
|
||||
///
|
||||
/// Whenever the counter needs to be updated, an additional read and write occurs to update that
|
||||
/// counter.
|
||||
///
|
||||
/// The total number of items currently stored in the map can be retrieved with the
|
||||
/// [`CountedStorageMap::count`] method.
|
||||
///
|
||||
/// For general information regarding the `#[pallet::storage]` attribute, refer to
|
||||
/// [`crate::pallet_macros::storage`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Declaring a counted map:
|
||||
///
|
||||
/// ```
|
||||
/// #[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 CountedStorageMap, with all possible additional attributes.
|
||||
/// #[pallet::storage]
|
||||
/// #[pallet::getter(fn foo)]
|
||||
/// #[pallet::storage_prefix = "OtherFoo"]
|
||||
/// #[pallet::unbounded]
|
||||
/// pub type Foo<T> = CountedStorageMap<
|
||||
/// _,
|
||||
/// Blake2_128Concat,
|
||||
/// u32,
|
||||
/// u32,
|
||||
/// ValueQuery,
|
||||
/// >;
|
||||
///
|
||||
/// /// Alternative named syntax.
|
||||
/// #[pallet::storage]
|
||||
/// pub type Bar<T> = CountedStorageMap<
|
||||
/// Hasher = Blake2_128Concat,
|
||||
/// Key = u32,
|
||||
/// Value = u32,
|
||||
/// QueryKind = ValueQuery
|
||||
/// >;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Using a counted map in action:
|
||||
#[doc = docify::embed!("src/storage/types/counted_map.rs", test_simple_count_works)]
|
||||
pub struct CountedStorageMap<
|
||||
Prefix,
|
||||
Hasher,
|
||||
@@ -1173,4 +1218,15 @@ mod test {
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[docify::export]
|
||||
#[test]
|
||||
fn test_simple_count_works() {
|
||||
type FooCountedMap = CountedStorageMap<Prefix, Twox64Concat, u16, u32>;
|
||||
TestExternalities::default().execute_with(|| {
|
||||
FooCountedMap::insert(1, 1);
|
||||
FooCountedMap::insert(2, 2);
|
||||
assert_eq!(FooCountedMap::count(), 2);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,8 +33,8 @@ use sp_metadata_ir::StorageEntryMetadataIR;
|
||||
use sp_runtime::traits::Saturating;
|
||||
use sp_std::prelude::*;
|
||||
|
||||
/// A wrapper around a `StorageNMap` and a `StorageValue<Value=u32>` to keep track of how many items
|
||||
/// are in a map, without needing to iterate over all of the values.
|
||||
/// A wrapper around a [`StorageNMap`] and a [`StorageValue`] (with the value being `u32`) to keep
|
||||
/// track of how many items are in a map, without needing to iterate all the values.
|
||||
///
|
||||
/// This storage item has some additional storage read and write overhead when manipulating values
|
||||
/// compared to a regular storage map.
|
||||
@@ -45,6 +45,49 @@ use sp_std::prelude::*;
|
||||
///
|
||||
/// Whenever the counter needs to be updated, an additional read and write occurs to update that
|
||||
/// counter.
|
||||
///
|
||||
/// For general information regarding the `#[pallet::storage]` attribute, refer to
|
||||
/// [`crate::pallet_macros::storage`].
|
||||
///
|
||||
/// # 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 CountedStorageNMap, with all possible additional attributes.
|
||||
/// #[pallet::storage]
|
||||
/// #[pallet::getter(fn foo)]
|
||||
/// #[pallet::storage_prefix = "OtherFoo"]
|
||||
/// #[pallet::unbounded]
|
||||
/// pub type Foo<T> = CountedStorageNMap<
|
||||
/// _,
|
||||
/// (
|
||||
/// NMapKey<Blake2_128Concat, u8>,
|
||||
/// NMapKey<Identity, u16>,
|
||||
/// NMapKey<Twox64Concat, u32>
|
||||
/// ),
|
||||
/// u64,
|
||||
/// ValueQuery,
|
||||
/// >;
|
||||
///
|
||||
/// /// Alternative named syntax.
|
||||
/// #[pallet::storage]
|
||||
/// pub type Bar<T> = CountedStorageNMap<
|
||||
/// Key = (
|
||||
/// NMapKey<Blake2_128Concat, u8>,
|
||||
/// NMapKey<Identity, u16>,
|
||||
/// NMapKey<Twox64Concat, u32>
|
||||
/// ),
|
||||
/// Value = u64,
|
||||
/// QueryKind = ValueQuery,
|
||||
/// >;
|
||||
/// }
|
||||
/// ```
|
||||
pub struct CountedStorageNMap<
|
||||
Prefix,
|
||||
Key,
|
||||
|
||||
@@ -31,22 +31,66 @@ use sp_arithmetic::traits::SaturatedConversion;
|
||||
use sp_metadata_ir::{StorageEntryMetadataIR, StorageEntryTypeIR};
|
||||
use sp_std::prelude::*;
|
||||
|
||||
/// A type that allow to store values for `(key1, key2)` couple. Similar to `StorageMap` but allow
|
||||
/// to iterate and remove value associated to first key.
|
||||
/// A type representing a *double map* in storage. This structure associates a pair of keys with a
|
||||
/// value of a specified type stored on-chain.
|
||||
///
|
||||
/// Each value is stored at:
|
||||
/// ```nocompile
|
||||
/// Twox128(Prefix::pallet_prefix())
|
||||
/// ++ Twox128(Prefix::STORAGE_PREFIX)
|
||||
/// ++ Hasher1(encode(key1))
|
||||
/// ++ Hasher2(encode(key2))
|
||||
/// A double map with keys `k1` and `k2` can be likened to a
|
||||
/// [`StorageMap`](frame_support::storage::types::StorageMap) with a key of type `(k1, k2)`.
|
||||
/// However, a double map offers functions specific to each key, enabling partial iteration and
|
||||
/// deletion based on one key alone.
|
||||
///
|
||||
/// Also, conceptually, a double map is a special case of a
|
||||
/// [`StorageNMap`](frame_support::storage::types::StorageNMap) using two keys.
|
||||
///
|
||||
/// For general information regarding the `#[pallet::storage]` attribute, refer to
|
||||
/// [`crate::pallet_macros::storage`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ### Kitchen-sink
|
||||
///
|
||||
/// ```
|
||||
/// #[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 StorageDoubleMap, with all possible additional attributes.
|
||||
/// #[pallet::storage]
|
||||
/// #[pallet::getter(fn foo)]
|
||||
/// #[pallet::storage_prefix = "OtherFoo"]
|
||||
/// #[pallet::unbounded]
|
||||
/// pub type Foo<T> = StorageDoubleMap<
|
||||
/// _,
|
||||
/// Blake2_128Concat,
|
||||
/// u8,
|
||||
/// Twox64Concat,
|
||||
/// u16,
|
||||
/// u32,
|
||||
/// ValueQuery
|
||||
/// >;
|
||||
///
|
||||
/// /// Alternative named syntax.
|
||||
/// #[pallet::storage]
|
||||
/// pub type Bar<T> = StorageDoubleMap<
|
||||
/// Hasher1 = Blake2_128Concat,
|
||||
/// Key1 = u8,
|
||||
/// Hasher2 = Twox64Concat,
|
||||
/// Key2 = u16,
|
||||
/// Value = u32,
|
||||
/// QueryKind = ValueQuery
|
||||
/// >;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Warning
|
||||
/// ### Partial Iteration & Removal
|
||||
///
|
||||
/// If the key1s (or key2s) are not trusted (e.g. can be set by a user), a cryptographic `hasher`
|
||||
/// such as `blake2_128_concat` must be used for Hasher1 (resp. Hasher2). Otherwise, other values
|
||||
/// in storage can be compromised.
|
||||
/// When `Hasher1` and `Hasher2` implement the
|
||||
/// [`ReversibleStorageHasher`](frame_support::ReversibleStorageHasher) trait, the first key `k1`
|
||||
/// can be used to partially iterate over keys and values of the double map, and to delete items.
|
||||
#[doc = docify::embed!("src/storage/types/double_map.rs", example_double_map_partial_operations)]
|
||||
pub struct StorageDoubleMap<
|
||||
Prefix,
|
||||
Hasher1,
|
||||
@@ -742,6 +786,7 @@ mod test {
|
||||
use crate::{hash::*, storage::types::ValueQuery};
|
||||
use sp_io::{hashing::twox_128, TestExternalities};
|
||||
use sp_metadata_ir::{StorageEntryModifierIR, StorageEntryTypeIR, StorageHasherIR};
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
struct Prefix;
|
||||
impl StorageInstance for Prefix {
|
||||
@@ -972,4 +1017,30 @@ mod test {
|
||||
assert_eq!(A::drain_prefix(4).collect::<Vec<_>>(), vec![]);
|
||||
})
|
||||
}
|
||||
|
||||
#[docify::export]
|
||||
#[test]
|
||||
fn example_double_map_partial_operations() {
|
||||
type FooDoubleMap =
|
||||
StorageDoubleMap<Prefix, Blake2_128Concat, u32, Blake2_128Concat, u32, u32, ValueQuery>;
|
||||
|
||||
TestExternalities::default().execute_with(|| {
|
||||
FooDoubleMap::insert(0, 0, 42);
|
||||
FooDoubleMap::insert(0, 1, 43);
|
||||
FooDoubleMap::insert(1, 0, 314);
|
||||
|
||||
// should be equal to {0,1} (ordering is random)
|
||||
let collected_k2_keys: BTreeSet<_> = FooDoubleMap::iter_key_prefix(0).collect();
|
||||
assert_eq!(collected_k2_keys, [0, 1].iter().copied().collect::<BTreeSet<_>>());
|
||||
|
||||
// should be equal to {42,43} (ordering is random)
|
||||
let collected_k2_values: BTreeSet<_> = FooDoubleMap::iter_prefix_values(0).collect();
|
||||
assert_eq!(collected_k2_values, [42, 43].iter().copied().collect::<BTreeSet<_>>());
|
||||
|
||||
// Remove items from the map using k1 = 0
|
||||
let _ = FooDoubleMap::clear_prefix(0, u32::max_value(), None);
|
||||
// Values associated with (0, _) should have been removed
|
||||
assert_eq!(FooDoubleMap::iter_prefix(0).collect::<Vec<_>>(), vec![]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,19 +31,45 @@ use sp_arithmetic::traits::SaturatedConversion;
|
||||
use sp_metadata_ir::{StorageEntryMetadataIR, StorageEntryTypeIR};
|
||||
use sp_std::prelude::*;
|
||||
|
||||
/// A type that allow to store value for given key. Allowing to insert/remove/iterate on values.
|
||||
/// A type representing a *map* in storage. A *storage map* is a mapping of keys to values of a
|
||||
/// given type stored on-chain.
|
||||
///
|
||||
/// For general information regarding the `#[pallet::storage]` attribute, refer to
|
||||
/// [`crate::pallet_macros::storage`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Each value is stored at:
|
||||
/// ```nocompile
|
||||
/// Twox128(Prefix::pallet_prefix())
|
||||
/// ++ Twox128(Prefix::STORAGE_PREFIX)
|
||||
/// ++ Hasher1(encode(key))
|
||||
/// ```
|
||||
/// #[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 StorageMap, with all possible additional attributes.
|
||||
/// #[pallet::storage]
|
||||
/// #[pallet::getter(fn foo)]
|
||||
/// #[pallet::storage_prefix = "OtherFoo"]
|
||||
/// #[pallet::unbounded]
|
||||
/// pub type Foo<T> = StorageMap<
|
||||
/// _
|
||||
/// Blake2_128Concat,
|
||||
/// u32,
|
||||
/// u32,
|
||||
/// ValueQuery
|
||||
/// >;
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// If the keys are not trusted (e.g. can be set by a user), a cryptographic `hasher` such as
|
||||
/// `blake2_128_concat` must be used. Otherwise, other values in storage can be compromised.
|
||||
/// /// Alternative named syntax.
|
||||
/// #[pallet::storage]
|
||||
/// pub type Bar<T> = StorageMap<
|
||||
/// Hasher = Blake2_128Concat,
|
||||
/// Key = u32,
|
||||
/// Value = u32,
|
||||
/// QueryKind = ValueQuery
|
||||
/// >;
|
||||
/// }
|
||||
/// ```
|
||||
pub struct StorageMap<
|
||||
Prefix,
|
||||
Hasher,
|
||||
|
||||
@@ -43,13 +43,17 @@ pub use value::StorageValue;
|
||||
|
||||
/// Trait implementing how the storage optional value is converted into the queried type.
|
||||
///
|
||||
/// It is implemented by:
|
||||
/// * `OptionQuery` which converts an optional value to an optional value, used when querying
|
||||
/// It is implemented most notable by:
|
||||
///
|
||||
/// * [`OptionQuery`] which converts an optional value to an optional value, used when querying
|
||||
/// storage returns an optional value.
|
||||
/// * `ResultQuery` which converts an optional value to a result value, used when querying storage
|
||||
/// * [`ResultQuery`] which converts an optional value to a result value, used when querying storage
|
||||
/// returns a result value.
|
||||
/// * `ValueQuery` which converts an optional value to a value, used when querying storage returns a
|
||||
/// value.
|
||||
/// * [`ValueQuery`] which converts an optional value to a value, used when querying storage returns
|
||||
/// a value.
|
||||
///
|
||||
/// ## Example
|
||||
#[doc = docify::embed!("src/storage/types/mod.rs", value_query_examples)]
|
||||
pub trait QueryKindTrait<Value, OnEmpty> {
|
||||
/// Metadata for the storage kind.
|
||||
const METADATA: StorageEntryModifierIR;
|
||||
@@ -65,11 +69,10 @@ pub trait QueryKindTrait<Value, OnEmpty> {
|
||||
fn from_query_to_optional_value(v: Self::Query) -> Option<Value>;
|
||||
}
|
||||
|
||||
/// Implement QueryKindTrait with query being `Option<Value>`
|
||||
/// Implements [`QueryKindTrait`] with `Query` type being `Option<_>`.
|
||||
///
|
||||
/// NOTE: it doesn't support a generic `OnEmpty`. This means only `None` can be
|
||||
/// returned when no value is found. To use another `OnEmpty` implementation, `ValueQuery` can be
|
||||
/// used instead.
|
||||
/// NOTE: it doesn't support a generic `OnEmpty`. This means only `None` can be returned when no
|
||||
/// value is found. To use another `OnEmpty` implementation, `ValueQuery` can be used instead.
|
||||
pub struct OptionQuery;
|
||||
impl<Value> QueryKindTrait<Value, crate::traits::GetDefault> for OptionQuery
|
||||
where
|
||||
@@ -89,7 +92,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Implement QueryKindTrait with query being `Result<Value, PalletError>`
|
||||
/// Implements [`QueryKindTrait`] with `Query` type being `Result<Value, PalletError>`.
|
||||
pub struct ResultQuery<Error>(sp_std::marker::PhantomData<Error>);
|
||||
impl<Value, Error, OnEmpty> QueryKindTrait<Value, OnEmpty> for ResultQuery<Error>
|
||||
where
|
||||
@@ -113,7 +116,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Implement QueryKindTrait with query being `Value`
|
||||
/// Implements [`QueryKindTrait`] with `Query` type being `Value`.
|
||||
pub struct ValueQuery;
|
||||
impl<Value, OnEmpty> QueryKindTrait<Value, OnEmpty> for ValueQuery
|
||||
where
|
||||
@@ -140,3 +143,60 @@ pub trait StorageEntryMetadataBuilder {
|
||||
/// Build into `entries` the storage metadata entries of a storage given some `docs`.
|
||||
fn build_metadata(doc: Vec<&'static str>, entries: &mut Vec<StorageEntryMetadataIR>);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::{
|
||||
storage::types::ValueQuery,
|
||||
traits::{Get, StorageInstance},
|
||||
};
|
||||
use sp_io::TestExternalities;
|
||||
|
||||
struct Prefix;
|
||||
impl StorageInstance for Prefix {
|
||||
fn pallet_prefix() -> &'static str {
|
||||
"test"
|
||||
}
|
||||
const STORAGE_PREFIX: &'static str = "foo";
|
||||
}
|
||||
|
||||
#[docify::export]
|
||||
#[test]
|
||||
pub fn value_query_examples() {
|
||||
/// Custom default impl to be used with `ValueQuery`.
|
||||
struct UniverseSecret;
|
||||
impl Get<u32> for UniverseSecret {
|
||||
fn get() -> u32 {
|
||||
42
|
||||
}
|
||||
}
|
||||
|
||||
/// Custom default impl to be used with `ResultQuery`.
|
||||
struct GetDefaultForResult;
|
||||
impl Get<Result<u32, ()>> for GetDefaultForResult {
|
||||
fn get() -> Result<u32, ()> {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
type A = StorageValue<Prefix, u32, ValueQuery>;
|
||||
type B = StorageValue<Prefix, u32, OptionQuery>;
|
||||
type C = StorageValue<Prefix, u32, ResultQuery<()>, GetDefaultForResult>;
|
||||
type D = StorageValue<Prefix, u32, ValueQuery, UniverseSecret>;
|
||||
|
||||
TestExternalities::default().execute_with(|| {
|
||||
// normal value query returns default
|
||||
assert_eq!(A::get(), 0);
|
||||
|
||||
// option query returns none
|
||||
assert_eq!(B::get(), None);
|
||||
|
||||
// result query returns error
|
||||
assert_eq!(C::get(), Err(()));
|
||||
|
||||
// value query with custom onempty returns 42
|
||||
assert_eq!(D::get(), 42);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,24 +33,54 @@ use sp_metadata_ir::{StorageEntryMetadataIR, StorageEntryTypeIR};
|
||||
use sp_runtime::SaturatedConversion;
|
||||
use sp_std::prelude::*;
|
||||
|
||||
/// A type that allow to store values for an arbitrary number of keys in the form of
|
||||
/// `(Key<Hasher1, key1>, Key<Hasher2, key2>, ..., Key<HasherN, keyN>)`.
|
||||
/// A type representing an *NMap* in storage. This structure associates an arbitrary number of keys
|
||||
/// with a value of a specified type stored on-chain.
|
||||
///
|
||||
/// For example, [`StorageDoubleMap`](frame_support::storage::types::StorageDoubleMap) is a special
|
||||
/// case of an *NMap* with N = 2.
|
||||
///
|
||||
/// For general information regarding the `#[pallet::storage]` attribute, refer to
|
||||
/// [`crate::pallet_macros::storage`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Each value is stored at:
|
||||
/// ```nocompile
|
||||
/// Twox128(Prefix::pallet_prefix())
|
||||
/// ++ Twox128(Prefix::STORAGE_PREFIX)
|
||||
/// ++ Hasher1(encode(key1))
|
||||
/// ++ Hasher2(encode(key2))
|
||||
/// ++ ...
|
||||
/// ++ HasherN(encode(keyN))
|
||||
/// ```
|
||||
/// #[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 StorageNMap, with all possible additional attributes.
|
||||
/// #[pallet::storage]
|
||||
/// #[pallet::getter(fn foo)]
|
||||
/// #[pallet::storage_prefix = "OtherFoo"]
|
||||
/// #[pallet::unbounded]
|
||||
/// pub type Foo<T> = StorageNMap<
|
||||
/// _,
|
||||
/// (
|
||||
/// NMapKey<Blake2_128Concat, u8>,
|
||||
/// NMapKey<Identity, u16>,
|
||||
/// NMapKey<Twox64Concat, u32>
|
||||
/// ),
|
||||
/// u64,
|
||||
/// ValueQuery,
|
||||
/// >;
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// If the keys are not trusted (e.g. can be set by a user), a cryptographic `hasher`
|
||||
/// such as `blake2_128_concat` must be used for the key hashers. Otherwise, other values
|
||||
/// in storage can be compromised.
|
||||
/// /// Named alternative syntax.
|
||||
/// #[pallet::storage]
|
||||
/// pub type Bar<T> = StorageNMap<
|
||||
/// Key = (
|
||||
/// NMapKey<Blake2_128Concat, u8>,
|
||||
/// NMapKey<Identity, u16>,
|
||||
/// NMapKey<Twox64Concat, u32>
|
||||
/// ),
|
||||
/// Value = u64,
|
||||
/// QueryKind = ValueQuery,
|
||||
/// >;
|
||||
/// }
|
||||
/// ```
|
||||
pub struct StorageNMap<
|
||||
Prefix,
|
||||
Key,
|
||||
|
||||
@@ -30,11 +30,36 @@ use sp_arithmetic::traits::SaturatedConversion;
|
||||
use sp_metadata_ir::{StorageEntryMetadataIR, StorageEntryTypeIR};
|
||||
use sp_std::prelude::*;
|
||||
|
||||
/// A type that allow to store a value.
|
||||
/// A type representing a *value* in storage. A *storage value* is a single value of a given type
|
||||
/// stored on-chain.
|
||||
///
|
||||
/// Each value is stored at:
|
||||
/// ```nocompile
|
||||
/// Twox128(Prefix::pallet_prefix()) ++ Twox128(Prefix::STORAGE_PREFIX)
|
||||
/// For general information regarding the `#[pallet::storage]` attribute, refer to
|
||||
/// [`crate::pallet_macros::storage`].
|
||||
///
|
||||
/// # 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]
|
||||
/// pub type Foo<T> = StorageValue<_, u32,ValueQuery>;
|
||||
///
|
||||
/// /// Named alternative syntax.
|
||||
/// #[pallet::storage]
|
||||
/// pub type Bar<T> = StorageValue<
|
||||
/// Value = u32,
|
||||
/// QueryKind = ValueQuery
|
||||
/// >;
|
||||
/// }
|
||||
/// ```
|
||||
pub struct StorageValue<Prefix, Value, QueryKind = OptionQuery, OnEmpty = GetDefault>(
|
||||
core::marker::PhantomData<(Prefix, Value, QueryKind, OnEmpty)>,
|
||||
|
||||
Reference in New Issue
Block a user