Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

improve FRAME storage docs #13987

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

250 changes: 201 additions & 49 deletions frame/support/procedural/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -820,18 +820,66 @@ pub fn config(_: TokenStream, _: TokenStream) -> TokenStream {
/// The `#[pallet::constant]` attribute can be used to add an associated type trait bounded by `Get`
/// from [`pallet::config`](`macro@config`) into metadata, e.g.:
///
/// ```ignore
/// #[pallet::config]
/// pub trait Config: frame_system::Config {
/// #[pallet::constant]
/// type Foo: Get<u32>;
/// ## 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>;
/// }
/// }
/// ```
#[proc_macro_attribute]
pub fn constant(_: TokenStream, _: TokenStream) -> TokenStream {
pallet_macro_stub()
}

/// Declares an implementation block to be the dispatchable portion of a pallet. In other words,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Declares an implementation block to be the dispatchable portion of a pallet. In other words,
/// Declares an implementation block to be the callable portion of a pallet. In other words,

I see below that the trait Dispatchable shows up, but my initial intuition was to leave this word out of the docs. People understand callable. Dispatchable less so.

/// each function in the implementation block of this section is an extrinsic that can be called
/// externally.
///
/// Other than than the `fn` attached to `Pallet`, this block will also generate an `enum Call`
/// which encapsulates the different functions, alongside their arguments, except for `origin`.
/// [`sp_runtime::traits::Dispatchable`] is then implemented for `enum Call`.
///
/// ## 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>(_);
///
/// #[pallet::call]
/// impl<T: Config> Pallet<T> {
/// fn do_stuff(_origin: OriginFor<T>, arg: u32) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is weight no longer required? I think that is a good thing, but was surprised there wasn't dev mode stuff going on

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be worth noting in the docs what call functions require? For example, it seems they all require an origin as the first argument.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in dev_mode it should no longer be needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #14115 for exactly what you said about call.

/// unimplemented!()
/// }
/// }
/// }
/// ```
///
/// TODO: once we have default pallet config, it would also be easy to create types in the example
/// that implement `Config`.
#[proc_macro_attribute]
pub fn call(_: TokenStream, _: TokenStream) -> TokenStream {
pallet_macro_stub()
}

/// To bypass the `frame_system::Config` supertrait check, use the attribute
/// `pallet::disable_frame_system_supertrait_check`, e.g.:
///
Expand Down Expand Up @@ -1023,6 +1071,11 @@ pub fn extra_constants(_: TokenStream, _: TokenStream) -> TokenStream {
pallet_macro_stub()
}

#[proc_macro_attribute]
pub fn constant_name(_: TokenStream, _: TokenStream) -> TokenStream {
pallet_macro_stub()
}

/// The `#[pallet::error]` attribute allows you to define an error enum that will be returned
/// from the dispatchable when an error occurs. The information for this error type is then
/// stored in metadata.
Expand Down Expand Up @@ -1124,60 +1177,159 @@ pub fn generate_deposit(_: TokenStream, _: TokenStream) -> TokenStream {
pallet_macro_stub()
}

/// The `#[pallet::storage]` attribute lets you define some abstract storage inside of runtime
/// storage and also set its metadata. This attribute can be used multiple times.
/// Declares a type alias as a storage item. Storage items are pointers to data stored onchain (the
/// *blockchain state*), under a specific key. The exact key is dependant on the type of the
/// storage.
///
/// Item should be defined as:
/// > Hypothetically, one can directly manipulate the state via [`sp_io::storage`]. However, this is
/// > an advance usage and is not recommended.
///
/// ```ignore
/// #[pallet::storage]
/// #[pallet::getter(fn $getter_name)] // optional
/// $vis type $StorageName<$some_generic> $optional_where_clause
/// = $StorageType<$generic_name = $some_generics, $other_name = $some_other, ...>;
/// ```
/// ## Storage Types
///
/// or with unnamed generic:
/// The following storage types are supported by the FRAME macros.
///
/// * `StorageValue`.
/// * `StorageMap`.
/// * `CountedStorageMap`.
/// * `StorageDoubleMap`.
/// * `StorageNMap`.
///
/// TODO: properly link to `frame_support::storage` types.
///
/// The FRAME macros always generate a type alias to either of these types, as indicated by the eg.
/// `type Foo = StorageValue<..>` syntax. For specific information about each storage type, see each
/// respective type's documentation.
///
/// ### Example:
///
/// ```ignore
/// #[pallet::storage]
/// #[pallet::getter(fn $getter_name)] // optional
/// $vis type $StorageName<$some_generic> $optional_where_clause
/// = $StorageType<_, $some_generics, ...>;
/// ```
/// #[frame_support::pallet]
/// mod pallet {
/// # use frame_support::pallet_prelude::*;
/// # #[pallet::config]
/// # pub trait Config: frame_system::Config {}
/// # #[pallet::pallet]
/// # pub struct Pallet<T>(_);
/// #[pallet::storage]
Comment on lines +1208 to +1213
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whats going on here with extra hashtags?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will hide this line in the generated rust-docs. Try cargo doc to see examples.

/// type Foo<T> = StorageValue<_, u32>;
///
/// #[pallet::storage]
/// type Bar<T> = StorageMap<_, Blake2_128Concat, u32, u32>;
///
/// #[pallet::storage]
/// type Bar<T> = StorageMap<_, Blake2_128Concat, u32, u32>;
///
/// #[pallet::storage]
/// type Bar<T> = CountedStorageMap<_, Blake2_128Concat, u32, u32>;
/// }
/// ```
///
/// I.e. it must be a type alias, with generics: `T` or `T: Config`. The aliased type must be
/// one of `StorageValue`, `StorageMap` or `StorageDoubleMap`. The generic arguments of the
/// storage type can be given in two manners: named and unnamed. For named generic arguments,
/// the name for each argument should match the name defined for it on the storage struct:
/// * `StorageValue` expects `Value` and optionally `QueryKind` and `OnEmpty`,
/// * `StorageMap` expects `Hasher`, `Key`, `Value` and optionally `QueryKind` and `OnEmpty`,
/// * `CountedStorageMap` expects `Hasher`, `Key`, `Value` and optionally `QueryKind` and `OnEmpty`,
/// * `StorageDoubleMap` expects `Hasher1`, `Key1`, `Hasher2`, `Key2`, `Value` and optionally
/// `QueryKind` and `OnEmpty`.
///
/// For unnamed generic arguments: Their first generic must be `_` as it is replaced by the
/// macro and other generic must declared as a normal generic type declaration.
///
/// The `Prefix` generic written by the macro is generated using
/// `PalletInfo::name::<Pallet<..>>()` and the name of the storage type. E.g. if runtime names
/// the pallet "MyExample" then the storage `type Foo<T> = ...` should use the prefix:
/// `Twox128(b"MyExample") ++ Twox128(b"Foo")`.
///
/// For the `CountedStorageMap` variant, the `Prefix` also implements
/// `CountedStorageMapInstance`. It also associates a `CounterPrefix`, which is implemented the
/// same as above, but the storage prefix is prepend with `"CounterFor"`. E.g. if runtime names
/// the pallet "MyExample" then the storage `type Foo<T> = CountedStorageaMap<...>` will store
/// its counter at the prefix: `Twox128(b"MyExample") ++ Twox128(b"CounterForFoo")`.
/// ## Related Macros
///
/// E.g:
/// The following macros are related to the storage macro and can be used in combination of it.
///
/// * [`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.
///
/// ## Common Details to All Storage Types.
///
/// The following details are relevant to all of the aforementioned storage types.
///
/// ### Syntax
///
/// Two general syntaxes are supported, as illuminated below:
///
/// 1. Named generics, eg. `type Foo<T> = StorageValue<Value = u32>`.
/// 2. Unnamed generics, eg. `type Foo<T> = StorageValue<_, u32>`.
///
/// In both cases, declaring `<T>` is mandatory, and it can be optionally `<T: Config>`. In the code
/// generated, it is always `<T: Config>`.
///
/// #### Example:
///
/// ```ignore
/// #[pallet::storage]
/// pub(super) type MyStorage<T> = StorageMap<Hasher = Blake2_128Concat, Key = u32, Value = u32>;
/// ```
/// #[frame_support::pallet]
/// mod pallet {
/// # use frame_support::pallet_prelude::*;
/// # #[pallet::config]
/// # pub trait Config: frame_system::Config {}
/// # #[pallet::pallet]
/// # pub struct Pallet<T>(_);
/// /// Unnamed syntax, without bounding `T`.
/// #[pallet::storage]
/// pub type Foo<T> = StorageValue<_, u32>;
///
/// /// Unnamed 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>;
/// }
/// ```
///
/// ### Query Type
///
/// All storage types defined above have one generic type that specifies the type of "query". This
/// refers to the type of value that is returned when _querying_ the storage, for example via a
/// `::get()` method.
///
/// 3 types of queries can be used:
///
/// 1. `OptionQuery`: This is the default query type. It returns `Some(_)` if the value is present,
/// `None` otherwise. 1. `ValueQuery`: It returns `T`, where `T` is the type
/// 2. `DefaultQuery`: it returns the value itself if the value is present, `Default::default`
/// otherwise.
/// 3. `ResultQuery`: it returns `Ok(_)` if the value is present, `Err(_)` otherwise.
///
/// TODO: check the documentation of each these.
///
/// ### Appending
///
/// TODO
///
/// ### Optimized Length Decoding.
///
/// TODO
///
/// ### Hashers
///
/// For all storage types, except `StorageValue`, a set of hashers need to be specified. The choice
/// of hashers is not something to be taken lightly, particularly in production chains. The point of
/// storage hashers in maps is to ensure the keys of an map are uniformly distributed, as an
/// unbalanced map/trie would lead to inefficient performance.
///
/// In general, hashers are either cryptographically secure or not. The former is slower than the
/// latter. `Blake2` and `Twox` are 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 (eg. `AccountId`), then use of cryptographically secure hashers are NOT required.
/// 2. If the map keys are controllable by the end users, cryptographically secure hashers should
/// be used.
///
/// see [`frame_support::hash`] for more information, namely the types that implement
/// [`frame_support::hash::StorageHasher`].
///
/// Lastly, usage of hashers with "concat" are suggested to have reversible hashes. See
/// [`hash::ReversibleStorageHasher`] implementors.
///
/// ### Prefixes
///
/// Under the hood, all storage types generate a "prefix". This prefix is used as the first part of
/// the key that is used to store value in the onchain state (ie final key used in
/// `sp_io::storage`). For all storage types, the following rule holds:
///
/// The storage prefix starts with `twox128(pallet_prefix) ++ twox128(storage_prefix)`, where
/// `pallet_prefix` is the chosen name of the instance of the pallet in
/// [`frame_support::construct_runtime`], and `storage_prefix` is the name of the `type` aliased to
/// a storage type, for example `Foo` in `type Foo<T> = StorageValue<..>`.
///
/// In this case the final prefix used by the map is `Twox128(b"MyExample") ++
/// Twox128(b"OtherName")`.
/// For [`frame_support::storage::StorageValue`], no further key is needed. For all maps types the
/// aforementioned key is appended by one or more keys defined by the map.
#[proc_macro_attribute]
pub fn storage(_: TokenStream, _: TokenStream) -> TokenStream {
pallet_macro_stub()
Expand Down
31 changes: 29 additions & 2 deletions frame/support/src/storage/types/counted_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ use sp_io::MultiRemovalResults;
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.
Expand All @@ -47,6 +47,33 @@ use sp_std::prelude::*;
///
/// Whenever the counter needs to be updated, an additional read and write occurs to update that
/// counter.
///
/// For common information about the `#[pallet::storage]` attribute, see
/// [`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 counted storage map, with all possible additional attributes.
/// #[pallet::storage]
/// #[pallet::getter(fn foo)]
/// #[pallet::storage_prefix = "OtherFoo"]
/// #[pallet::unbounded]
/// pub type Foo<T> = CountedStorageMap<
/// Hasher = Blake2_128Concat,
/// Key = u32,
/// Value = u32,
/// QueryKind = ValueQuery
/// >;
/// }
/// ```
pub struct CountedStorageMap<
Prefix,
Hasher,
Expand Down
47 changes: 34 additions & 13 deletions frame/support/src/storage/types/double_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,43 @@ use codec::{Decode, Encode, EncodeLike, FullCodec, MaxEncodedLen};
use sp_arithmetic::traits::SaturatedConversion;
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.
/// Type representing a "double map" in storage. A "double map" is a mapping of a set of two keys to
/// a value of a given type stored onchain.
///
/// 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` is somewhat similar to a map with key `(k1, k2)` as its
/// key. But, a double map provides function specific to each key individually, allowing partial
/// iteration and deletion based on one key.
///
/// Moreover, a double map is an alias for an [`crate::storage::StorageMMap`] with two keys.
///
/// For common information about the `#[pallet::storage]` attribute, see
/// [`crate::pallet_macros::storage`].
///
/// # Warning
/// # Example
///
/// 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.
/// ```
/// #[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 storage map, with all possible additional attributes.
/// #[pallet::storage]
/// #[pallet::getter(fn foo)]
/// #[pallet::storage_prefix = "OtherFoo"]
/// #[pallet::unbounded]
/// pub type Foo<T> = StorageDoubleMap<
/// Hasher1 = Blake2_128Concat,
/// Key1 = u8,
/// Hasher2 = Twox64Concat,
/// Key2 = u16,
/// Value = u32,
/// QueryKind = ValueQuery
/// >;
/// }
/// ```
pub struct StorageDoubleMap<
Prefix,
Hasher1,
Expand Down
Loading