mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-20 02:21:03 +00:00
Improve documentation for fast-unstake pallet (#14101)
* improve documentation of fast-unstake pallet * using Sam's crate now * fix * remove stuff not needed * Some updates * use new prelude. * update * some other fancy docs * Update frame/fast-unstake/src/lib.rs Co-authored-by: Liam Aharon <liam.aharon@hotmail.com> * Update frame/support/procedural/src/pallet/expand/mod.rs Co-authored-by: Liam Aharon <liam.aharon@hotmail.com> * update * Update frame/fast-unstake/src/lib.rs Co-authored-by: Keith Yeung <kungfukeith11@gmail.com> * update * fix no_std issue by updating to latest version of docify * get things compiling by adding a use for StakingInterface * fix docify paths to their proper values, still not working because bug * embed working 🎉 * update syn * upgrade to docify v0.1.10 for some fixes * Apply suggestions from code review Co-authored-by: Juan <juangirini@gmail.com> Co-authored-by: Gonçalo Pestana <g6pestana@gmail.com> * improve docs * Update frame/support/procedural/src/pallet/expand/doc_only.rs Co-authored-by: Juan <juangirini@gmail.com> * fmt * fix * Update frame/support/procedural/src/pallet/expand/doc_only.rs Co-authored-by: Muharem Ismailov <ismailov.m.h@gmail.com> * Update frame/support/procedural/src/pallet/expand/config.rs Co-authored-by: Muharem Ismailov <ismailov.m.h@gmail.com> * Update frame/support/procedural/src/pallet/expand/config.rs Co-authored-by: Muharem Ismailov <ismailov.m.h@gmail.com> * remove redundant * update docify rev * update. * update. * update lock file --------- Co-authored-by: Liam Aharon <liam.aharon@hotmail.com> Co-authored-by: Keith Yeung <kungfukeith11@gmail.com> Co-authored-by: Sam Johnson <sam@durosoft.com> Co-authored-by: Juan <juangirini@gmail.com> Co-authored-by: Gonçalo Pestana <g6pestana@gmail.com> Co-authored-by: Muharem Ismailov <ismailov.m.h@gmail.com> Co-authored-by: parity-processbot <>
This commit is contained in:
Generated
+34
@@ -1289,6 +1289,12 @@ dependencies = [
|
|||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "common-path"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2382f75942f4b3be3690fe4f86365e9c853c1587d6ee58212cebf6e2a9ccd101"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "concurrent-queue"
|
name = "concurrent-queue"
|
||||||
version = "2.2.0"
|
version = "2.2.0"
|
||||||
@@ -2028,6 +2034,33 @@ version = "0.3.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "docify"
|
||||||
|
version = "0.1.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "18b972b74c30cbe838fc6a07665132ff94f257350e26fd01d80bc59ee7fcf129"
|
||||||
|
dependencies = [
|
||||||
|
"docify_macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "docify_macros"
|
||||||
|
version = "0.1.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c93004d1011191c56df9e853dca42f2012e7488638bcd5078935f5ce43e06cf3"
|
||||||
|
dependencies = [
|
||||||
|
"common-path",
|
||||||
|
"derive-syn-parse",
|
||||||
|
"lazy_static",
|
||||||
|
"prettyplease 0.2.4",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"regex",
|
||||||
|
"syn 2.0.16",
|
||||||
|
"termcolor",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "downcast"
|
name = "downcast"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
@@ -6418,6 +6451,7 @@ dependencies = [
|
|||||||
name = "pallet-fast-unstake"
|
name = "pallet-fast-unstake"
|
||||||
version = "4.0.0-dev"
|
version = "4.0.0-dev"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"docify",
|
||||||
"frame-benchmarking",
|
"frame-benchmarking",
|
||||||
"frame-election-provider-support",
|
"frame-election-provider-support",
|
||||||
"frame-support",
|
"frame-support",
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ frame-election-provider-support = { default-features = false, path = "../electio
|
|||||||
|
|
||||||
frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" }
|
frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" }
|
||||||
|
|
||||||
|
docify = "0.1.13"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pallet-staking-reward-curve = { version = "4.0.0-dev", path = "../staking/reward-curve" }
|
pallet-staking-reward-curve = { version = "4.0.0-dev", path = "../staking/reward-curve" }
|
||||||
sp-core = { version = "8.0.0", default-features = false, path = "../../primitives/core" }
|
sp-core = { version = "8.0.0", default-features = false, path = "../../primitives/core" }
|
||||||
|
|||||||
@@ -15,37 +15,100 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
//! A pallet that's designed to JUST do the following:
|
//! > Made with *Substrate*, for *Polkadot*.
|
||||||
//!
|
//!
|
||||||
//! If a nominator is not exposed in any `ErasStakers` (i.e. "has not actively backed any
|
//! [![github]](https://github.com/paritytech/substrate/frame/fast-unstake) -
|
||||||
//! validators in the last `BondingDuration` days"), then they can register themselves in this
|
//! [![polkadot]](https://polkadot.network)
|
||||||
//! pallet, unstake faster than having to wait an entire bonding duration.
|
|
||||||
//!
|
//!
|
||||||
//! Appearing in the exposure of a validator means being exposed equal to that validator from the
|
//! [polkadot]: https://img.shields.io/badge/polkadot-E6007A?style=for-the-badge&logo=polkadot&logoColor=white
|
||||||
//! point of view of the staking system. This usually means earning rewards with the validator, and
|
//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
|
||||||
//! also being at the risk of slashing with the validator. This is equivalent to the "Active
|
//!
|
||||||
//! Nominator" role explained in the
|
//! # Fast Unstake Pallet
|
||||||
//! [February Staking Update](https://polkadot.network/blog/staking-update-february-2022/).
|
//!
|
||||||
|
//! A pallet to allow participants of the staking system (represented by [`Config::Staking`], being
|
||||||
|
//! [`StakingInterface`]) to unstake quicker, if and only if they meet the condition of not being
|
||||||
|
//! exposed to any slashes.
|
||||||
|
//!
|
||||||
|
//! ## Overview
|
||||||
|
//!
|
||||||
|
//! If a nominator is not exposed anywhere in the staking system, checked via
|
||||||
|
//! [`StakingInterface::is_exposed_in_era`] (i.e. "has not actively backed any validators in the
|
||||||
|
//! last [`StakingInterface::bonding_duration`] days"), then they can register themselves in this
|
||||||
|
//! pallet and unstake faster than having to wait an entire bonding duration.
|
||||||
|
//!
|
||||||
|
//! *Being exposed with validator* from the point of view of the staking system means earning
|
||||||
|
//! rewards with the validator, and also being at the risk of slashing with the validator. This is
|
||||||
|
//! equivalent to the "Active Nominator" role explained in
|
||||||
|
//! [here](https://polkadot.network/blog/staking-update-february-2022/).
|
||||||
|
//!
|
||||||
|
//! Stakers who are certain about NOT being exposed can register themselves with
|
||||||
|
//! [`Pallet::register_fast_unstake`]. This will chill, fully unbond the staker and place them
|
||||||
|
//! in the queue to be checked.
|
||||||
|
//!
|
||||||
|
//! A successful registration implies being fully unbonded and chilled in the staking system. These
|
||||||
|
//! effects persist even if the fast-unstake registration is retracted (see [`Pallet::deregister`]
|
||||||
|
//! and further).
|
||||||
|
//!
|
||||||
|
//! Once registered as a fast-unstaker, the staker will be queued and checked by the system. This
|
||||||
|
//! can take a variable number of blocks based on demand, but will almost certainly be "faster" (as
|
||||||
|
//! the name suggest) than waiting the standard bonding duration.
|
||||||
|
//!
|
||||||
|
//! A fast-unstaker is either in [`Queue`] or actively being checked, at which point it lives in
|
||||||
|
//! [`Head`]. Once in [`Head`], the request cannot be retracted anymore. But, once in [`Queue`], it
|
||||||
|
//! can, via [`Pallet::deregister`].
|
||||||
|
//!
|
||||||
|
//! A deposit equal to [`Config::Deposit`] is collected for this process, and is returned in case a
|
||||||
|
//! successful unstake occurs (`Event::Unstaked` signals that).
|
||||||
|
//!
|
||||||
|
//! Once processed, if successful, no additional fee for the checking process is taken, and the
|
||||||
|
//! staker is instantly unbonded.
|
||||||
|
//!
|
||||||
|
//! If unsuccessful, meaning that the staker was exposed, the aforementioned deposit will be slashed
|
||||||
|
//! for the amount of wasted work they have inflicted on the chain.
|
||||||
|
//!
|
||||||
|
//! All in all, this pallet is meant to provide an easy off-ramp for some stakers.
|
||||||
|
//!
|
||||||
|
//! ### Example
|
||||||
|
//!
|
||||||
|
//! 1. Fast-unstake with multiple participants in the queue.
|
||||||
|
#![doc = docify::embed!("frame/fast-unstake/src/tests.rs", successful_multi_queue)]
|
||||||
|
//!
|
||||||
|
//! 2. Fast unstake failing because a nominator is exposed.
|
||||||
|
#![doc = docify::embed!("frame/fast-unstake/src/tests.rs", exposed_nominator_cannot_unstake)]
|
||||||
|
//!
|
||||||
|
//! ## Pallet API
|
||||||
|
//!
|
||||||
|
//! See the [`pallet`] module for more information about the interfaces this pallet exposes,
|
||||||
|
//! including its configuration trait, dispatchables, storage items, events and errors.
|
||||||
|
//!
|
||||||
|
//! ## Low Level / Implementation Details
|
||||||
//!
|
//!
|
||||||
//! This pallet works off the basis of `on_idle`, meaning that it provides no guarantee about when
|
//! This pallet works off the basis of `on_idle`, meaning that it provides no guarantee about when
|
||||||
//! it will succeed, if at all. Moreover, the queue implementation is unordered. In case of
|
//! it will succeed, if at all. Moreover, the queue implementation is unordered. In case of
|
||||||
//! congestion, no FIFO ordering is provided.
|
//! congestion, no FIFO ordering is provided.
|
||||||
//!
|
//!
|
||||||
//! Stakers who are certain about NOT being exposed can register themselves with
|
//! A few important considerations can be concluded based on the `on_idle`-based implementation:
|
||||||
//! [`Call::register_fast_unstake`]. This will chill, and fully unbond the staker, and place them in
|
|
||||||
//! the queue to be checked.
|
|
||||||
//!
|
//!
|
||||||
//! Once queued, but not being actively processed, stakers can withdraw their request via
|
//! * It is crucial for the weights of this pallet to be correct. The code inside
|
||||||
//! [`Call::deregister`].
|
//! [`Pallet::on_idle`] MUST be able to measure itself and report the remaining weight correctly
|
||||||
|
//! after execution.
|
||||||
//!
|
//!
|
||||||
//! Once queued, a staker wishing to unbond can perform no further action in pallet-staking. This is
|
//! * If the weight measurement is incorrect, it can lead to perpetual overweight (consequently
|
||||||
//! to prevent them from accidentally exposing themselves behind a validator etc.
|
//! slow) blocks.
|
||||||
//!
|
//!
|
||||||
//! Once processed, if successful, no additional fee for the checking process is taken, and the
|
//! * The amount of weight that `on_idle` consumes is a direct function of [`ErasToCheckPerBlock`].
|
||||||
//! staker is instantly unbonded.
|
|
||||||
//!
|
//!
|
||||||
//! If unsuccessful, meaning that the staker was exposed sometime in the last `BondingDuration` eras
|
//! * Thus, a correct value of [`ErasToCheckPerBlock`] (which can be set via [`Pallet::control`])
|
||||||
//! they will end up being slashed for the amount of wasted work they have inflicted on the chian.
|
//! should be chosen, such that a reasonable amount of weight is used `on_idle`. If
|
||||||
|
//! [`ErasToCheckPerBlock`] is too large, `on_idle` will always conclude that it has not enough
|
||||||
|
//! weight to proceed, and will early-return. Nonetheless, this should also be *safe* as long as
|
||||||
|
//! the benchmarking/weights are *accurate*.
|
||||||
|
//!
|
||||||
|
//! * See the inline code-comments on `do_on_idle` (private) for more details.
|
||||||
|
//!
|
||||||
|
//! * For further safety, in case of any unforeseen errors, the pallet will emit
|
||||||
|
//! [`Event::InternalError`] and set [`ErasToCheckPerBlock`] back to 0, which essentially means
|
||||||
|
//! the pallet will halt/disable itself.
|
||||||
|
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
@@ -64,9 +127,15 @@ pub mod migrations;
|
|||||||
pub mod types;
|
pub mod types;
|
||||||
pub mod weights;
|
pub mod weights;
|
||||||
|
|
||||||
|
// some extra imports for docs to link properly.
|
||||||
|
#[cfg(doc)]
|
||||||
|
pub use frame_support::traits::Hooks;
|
||||||
|
#[cfg(doc)]
|
||||||
|
pub use sp_staking::StakingInterface;
|
||||||
|
|
||||||
|
/// The logging target of this pallet.
|
||||||
pub const LOG_TARGET: &'static str = "runtime::fast-unstake";
|
pub const LOG_TARGET: &'static str = "runtime::fast-unstake";
|
||||||
|
|
||||||
// syntactic sugar for logging.
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! log {
|
macro_rules! log {
|
||||||
($level:tt, $patter:expr $(, $values:expr)* $(,)?) => {
|
($level:tt, $patter:expr $(, $values:expr)* $(,)?) => {
|
||||||
@@ -94,16 +163,6 @@ pub mod pallet {
|
|||||||
#[cfg(feature = "try-runtime")]
|
#[cfg(feature = "try-runtime")]
|
||||||
use sp_runtime::TryRuntimeError;
|
use sp_runtime::TryRuntimeError;
|
||||||
|
|
||||||
#[derive(scale_info::TypeInfo, codec::Encode, codec::Decode, codec::MaxEncodedLen)]
|
|
||||||
#[codec(mel_bound(T: Config))]
|
|
||||||
#[scale_info(skip_type_params(T))]
|
|
||||||
pub struct MaxChecking<T: Config>(sp_std::marker::PhantomData<T>);
|
|
||||||
impl<T: Config> frame_support::traits::Get<u32> for MaxChecking<T> {
|
|
||||||
fn get() -> u32 {
|
|
||||||
T::Staking::bonding_duration() + 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
|
const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
|
||||||
|
|
||||||
#[pallet::pallet]
|
#[pallet::pallet]
|
||||||
@@ -125,7 +184,7 @@ pub mod pallet {
|
|||||||
#[pallet::constant]
|
#[pallet::constant]
|
||||||
type Deposit: Get<BalanceOf<Self>>;
|
type Deposit: Get<BalanceOf<Self>>;
|
||||||
|
|
||||||
/// The origin that can control this pallet.
|
/// The origin that can control this pallet, in other words invoke [`Pallet::control`].
|
||||||
type ControlOrigin: frame_support::traits::EnsureOrigin<Self::RuntimeOrigin>;
|
type ControlOrigin: frame_support::traits::EnsureOrigin<Self::RuntimeOrigin>;
|
||||||
|
|
||||||
/// Batch size.
|
/// Batch size.
|
||||||
@@ -136,40 +195,45 @@ pub mod pallet {
|
|||||||
/// The access to staking functionality.
|
/// The access to staking functionality.
|
||||||
type Staking: StakingInterface<Balance = BalanceOf<Self>, AccountId = Self::AccountId>;
|
type Staking: StakingInterface<Balance = BalanceOf<Self>, AccountId = Self::AccountId>;
|
||||||
|
|
||||||
|
/// Maximum value for `ErasToCheckPerBlock`, checked in [`Pallet::control`].
|
||||||
|
///
|
||||||
|
/// This should be slightly bigger than the actual value in order to have accurate
|
||||||
|
/// benchmarks.
|
||||||
|
type MaxErasToCheckPerBlock: Get<u32>;
|
||||||
|
|
||||||
/// The weight information of this pallet.
|
/// The weight information of this pallet.
|
||||||
type WeightInfo: WeightInfo;
|
type WeightInfo: WeightInfo;
|
||||||
|
|
||||||
/// Maximum value for `ErasToCheckPerBlock`. This should be as close as possible, but more
|
|
||||||
/// than the actual value, in order to have accurate benchmarks.
|
|
||||||
type MaxErasToCheckPerBlock: Get<u32>;
|
|
||||||
|
|
||||||
/// Use only for benchmarking.
|
/// Use only for benchmarking.
|
||||||
#[cfg(feature = "runtime-benchmarks")]
|
#[cfg(feature = "runtime-benchmarks")]
|
||||||
type MaxBackersPerValidator: Get<u32>;
|
type MaxBackersPerValidator: Get<u32>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The current "head of the queue" being unstaked.
|
/// The current "head of the queue" being unstaked.
|
||||||
|
///
|
||||||
|
/// The head in itself can be a batch of up to [`Config::BatchSize`] stakers.
|
||||||
#[pallet::storage]
|
#[pallet::storage]
|
||||||
pub type Head<T: Config> = StorageValue<_, UnstakeRequest<T>, OptionQuery>;
|
pub type Head<T: Config> = StorageValue<_, UnstakeRequest<T>, OptionQuery>;
|
||||||
|
|
||||||
/// The map of all accounts wishing to be unstaked.
|
/// The map of all accounts wishing to be unstaked.
|
||||||
///
|
///
|
||||||
/// Keeps track of `AccountId` wishing to unstake and it's corresponding deposit.
|
/// Keeps track of `AccountId` wishing to unstake and it's corresponding deposit.
|
||||||
///
|
// Hasher: Twox safe since `AccountId` is a secure hash.
|
||||||
/// TWOX-NOTE: SAFE since `AccountId` is a secure hash.
|
|
||||||
#[pallet::storage]
|
#[pallet::storage]
|
||||||
pub type Queue<T: Config> = CountedStorageMap<_, Twox64Concat, T::AccountId, BalanceOf<T>>;
|
pub type Queue<T: Config> = CountedStorageMap<_, Twox64Concat, T::AccountId, BalanceOf<T>>;
|
||||||
|
|
||||||
/// Number of eras to check per block.
|
/// Number of eras to check per block.
|
||||||
///
|
///
|
||||||
/// If set to 0, this pallet does absolutely nothing.
|
/// If set to 0, this pallet does absolutely nothing. Cannot be set to more than
|
||||||
|
/// [`Config::MaxErasToCheckPerBlock`].
|
||||||
///
|
///
|
||||||
/// Based on the amount of weight available at `on_idle`, up to this many eras of a single
|
/// Based on the amount of weight available at [`Pallet::on_idle`], up to this many eras are
|
||||||
/// nominator might be checked.
|
/// checked. The checking is represented by updating [`UnstakeRequest::checked`], which is
|
||||||
|
/// stored in [`Head`].
|
||||||
#[pallet::storage]
|
#[pallet::storage]
|
||||||
|
#[pallet::getter(fn eras_to_check_per_block)]
|
||||||
pub type ErasToCheckPerBlock<T: Config> = StorageValue<_, u32, ValueQuery>;
|
pub type ErasToCheckPerBlock<T: Config> = StorageValue<_, u32, ValueQuery>;
|
||||||
|
|
||||||
/// The events of this pallet.
|
|
||||||
#[pallet::event]
|
#[pallet::event]
|
||||||
#[pallet::generate_deposit(pub(super) fn deposit_event)]
|
#[pallet::generate_deposit(pub(super) fn deposit_event)]
|
||||||
pub enum Event<T: Config> {
|
pub enum Event<T: Config> {
|
||||||
@@ -177,8 +241,6 @@ pub mod pallet {
|
|||||||
Unstaked { stash: T::AccountId, result: DispatchResult },
|
Unstaked { stash: T::AccountId, result: DispatchResult },
|
||||||
/// A staker was slashed for requesting fast-unstake whilst being exposed.
|
/// A staker was slashed for requesting fast-unstake whilst being exposed.
|
||||||
Slashed { stash: T::AccountId, amount: BalanceOf<T> },
|
Slashed { stash: T::AccountId, amount: BalanceOf<T> },
|
||||||
/// An internal error happened. Operations will be paused now.
|
|
||||||
InternalError,
|
|
||||||
/// A batch was partially checked for the given eras, but the process did not finish.
|
/// A batch was partially checked for the given eras, but the process did not finish.
|
||||||
BatchChecked { eras: Vec<EraIndex> },
|
BatchChecked { eras: Vec<EraIndex> },
|
||||||
/// A batch of a given size was terminated.
|
/// A batch of a given size was terminated.
|
||||||
@@ -186,6 +248,8 @@ pub mod pallet {
|
|||||||
/// This is always follows by a number of `Unstaked` or `Slashed` events, marking the end
|
/// This is always follows by a number of `Unstaked` or `Slashed` events, marking the end
|
||||||
/// of the batch. A new batch will be created upon next block.
|
/// of the batch. A new batch will be created upon next block.
|
||||||
BatchFinished { size: u32 },
|
BatchFinished { size: u32 },
|
||||||
|
/// An internal error happened. Operations will be paused now.
|
||||||
|
InternalError,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pallet::error]
|
#[pallet::error]
|
||||||
@@ -247,8 +311,12 @@ pub mod pallet {
|
|||||||
impl<T: Config> Pallet<T> {
|
impl<T: Config> Pallet<T> {
|
||||||
/// Register oneself for fast-unstake.
|
/// Register oneself for fast-unstake.
|
||||||
///
|
///
|
||||||
/// The dispatch origin of this call must be signed by the controller account, similar to
|
/// ## Dispatch Origin
|
||||||
/// `staking::unbond`.
|
///
|
||||||
|
/// The dispatch origin of this call must be *signed* by whoever is permitted to call
|
||||||
|
/// unbond funds by the staking system. See [`Config::Staking`].
|
||||||
|
///
|
||||||
|
/// ## Details
|
||||||
///
|
///
|
||||||
/// The stash associated with the origin must have no ongoing unlocking chunks. If
|
/// The stash associated with the origin must have no ongoing unlocking chunks. If
|
||||||
/// successful, this will fully unbond and chill the stash. Then, it will enqueue the stash
|
/// successful, this will fully unbond and chill the stash. Then, it will enqueue the stash
|
||||||
@@ -263,6 +331,10 @@ pub mod pallet {
|
|||||||
/// If the check fails, the stash remains chilled and waiting for being unbonded as in with
|
/// If the check fails, the stash remains chilled and waiting for being unbonded as in with
|
||||||
/// the normal staking system, but they lose part of their unbonding chunks due to consuming
|
/// the normal staking system, but they lose part of their unbonding chunks due to consuming
|
||||||
/// the chain's resources.
|
/// the chain's resources.
|
||||||
|
///
|
||||||
|
/// ## Events
|
||||||
|
///
|
||||||
|
/// Some events from the staking and currency system might be emitted.
|
||||||
#[pallet::call_index(0)]
|
#[pallet::call_index(0)]
|
||||||
#[pallet::weight(<T as Config>::WeightInfo::register_fast_unstake())]
|
#[pallet::weight(<T as Config>::WeightInfo::register_fast_unstake())]
|
||||||
pub fn register_fast_unstake(origin: OriginFor<T>) -> DispatchResult {
|
pub fn register_fast_unstake(origin: OriginFor<T>) -> DispatchResult {
|
||||||
@@ -288,11 +360,22 @@ pub mod pallet {
|
|||||||
|
|
||||||
/// Deregister oneself from the fast-unstake.
|
/// Deregister oneself from the fast-unstake.
|
||||||
///
|
///
|
||||||
|
/// ## Dispatch Origin
|
||||||
|
///
|
||||||
|
/// The dispatch origin of this call must be *signed* by whoever is permitted to call
|
||||||
|
/// unbond funds by the staking system. See [`Config::Staking`].
|
||||||
|
///
|
||||||
|
/// ## Details
|
||||||
|
///
|
||||||
/// This is useful if one is registered, they are still waiting, and they change their mind.
|
/// This is useful if one is registered, they are still waiting, and they change their mind.
|
||||||
///
|
///
|
||||||
/// Note that the associated stash is still fully unbonded and chilled as a consequence of
|
/// Note that the associated stash is still fully unbonded and chilled as a consequence of
|
||||||
/// calling `register_fast_unstake`. This should probably be followed by a call to
|
/// calling [`Pallet::register_fast_unstake`]. Therefore, this should probably be followed
|
||||||
/// `Staking::rebond`.
|
/// by a call to `rebond` in the staking system.
|
||||||
|
///
|
||||||
|
/// ## Events
|
||||||
|
///
|
||||||
|
/// Some events from the staking and currency system might be emitted.
|
||||||
#[pallet::call_index(1)]
|
#[pallet::call_index(1)]
|
||||||
#[pallet::weight(<T as Config>::WeightInfo::deregister())]
|
#[pallet::weight(<T as Config>::WeightInfo::deregister())]
|
||||||
pub fn deregister(origin: OriginFor<T>) -> DispatchResult {
|
pub fn deregister(origin: OriginFor<T>) -> DispatchResult {
|
||||||
@@ -318,7 +401,17 @@ pub mod pallet {
|
|||||||
|
|
||||||
/// Control the operation of this pallet.
|
/// Control the operation of this pallet.
|
||||||
///
|
///
|
||||||
/// Dispatch origin must be signed by the [`Config::ControlOrigin`].
|
/// ## Dispatch Origin
|
||||||
|
///
|
||||||
|
/// The dispatch origin of this call must be [`Config::ControlOrigin`].
|
||||||
|
///
|
||||||
|
/// ## Details
|
||||||
|
///
|
||||||
|
/// Can set the number of eras to check per block, and potentially other admin work.
|
||||||
|
///
|
||||||
|
/// ## Events
|
||||||
|
///
|
||||||
|
/// No events are emitted from this dispatch.
|
||||||
#[pallet::call_index(2)]
|
#[pallet::call_index(2)]
|
||||||
#[pallet::weight(<T as Config>::WeightInfo::control())]
|
#[pallet::weight(<T as Config>::WeightInfo::control())]
|
||||||
pub fn control(origin: OriginFor<T>, eras_to_check: EraIndex) -> DispatchResult {
|
pub fn control(origin: OriginFor<T>, eras_to_check: EraIndex) -> DispatchResult {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{mock::*, types::*, Event};
|
use crate::{mock::*, types::*, Event};
|
||||||
use frame_support::{assert_noop, assert_ok, bounded_vec, pallet_prelude::*, traits::Currency};
|
use frame_support::{pallet_prelude::*, testing_prelude::*, traits::Currency};
|
||||||
use pallet_staking::{CurrentEra, RewardDestination};
|
use pallet_staking::{CurrentEra, RewardDestination};
|
||||||
|
|
||||||
use sp_runtime::traits::BadOrigin;
|
use sp_runtime::traits::BadOrigin;
|
||||||
@@ -303,6 +303,7 @@ mod on_idle {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
#[test]
|
#[test]
|
||||||
fn successful_multi_queue() {
|
fn successful_multi_queue() {
|
||||||
ExtBuilder::default().build_and_execute(|| {
|
ExtBuilder::default().build_and_execute(|| {
|
||||||
@@ -356,6 +357,7 @@ mod on_idle {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
#[test]
|
#[test]
|
||||||
fn successful_unstake() {
|
fn successful_unstake() {
|
||||||
ExtBuilder::default().build_and_execute(|| {
|
ExtBuilder::default().build_and_execute(|| {
|
||||||
@@ -693,6 +695,7 @@ mod on_idle {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
#[test]
|
#[test]
|
||||||
fn exposed_nominator_cannot_unstake() {
|
fn exposed_nominator_cannot_unstake() {
|
||||||
ExtBuilder::default().build_and_execute(|| {
|
ExtBuilder::default().build_and_execute(|| {
|
||||||
|
|||||||
@@ -17,25 +17,41 @@
|
|||||||
|
|
||||||
//! Types used in the Fast Unstake pallet.
|
//! Types used in the Fast Unstake pallet.
|
||||||
|
|
||||||
use crate::{Config, MaxChecking};
|
use crate::Config;
|
||||||
use codec::{Decode, Encode, MaxEncodedLen};
|
use codec::{Decode, Encode, MaxEncodedLen};
|
||||||
use frame_support::{
|
use frame_support::{
|
||||||
traits::Currency, BoundedVec, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound,
|
traits::Currency, BoundedVec, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound,
|
||||||
};
|
};
|
||||||
use scale_info::TypeInfo;
|
use scale_info::TypeInfo;
|
||||||
use sp_staking::EraIndex;
|
use sp_staking::{EraIndex, StakingInterface};
|
||||||
use sp_std::prelude::*;
|
use sp_std::prelude::*;
|
||||||
|
|
||||||
pub type BalanceOf<T> =
|
/// Maximum number of eras that we might check for a single staker.
|
||||||
|
///
|
||||||
|
/// In effect, it is the bonding duration, coming from [`Config::Staking`], plus one.
|
||||||
|
#[derive(scale_info::TypeInfo, codec::Encode, codec::Decode, codec::MaxEncodedLen)]
|
||||||
|
#[codec(mel_bound(T: Config))]
|
||||||
|
#[scale_info(skip_type_params(T))]
|
||||||
|
pub struct MaxChecking<T: Config>(sp_std::marker::PhantomData<T>);
|
||||||
|
impl<T: Config> frame_support::traits::Get<u32> for MaxChecking<T> {
|
||||||
|
fn get() -> u32 {
|
||||||
|
T::Staking::bonding_duration() + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) type BalanceOf<T> =
|
||||||
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
|
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
|
||||||
/// An unstake request.
|
/// An unstake request.
|
||||||
|
///
|
||||||
|
/// This is stored in [`crate::Head`] storage item and points to the current unstake request that is
|
||||||
|
/// being processed.
|
||||||
#[derive(
|
#[derive(
|
||||||
Encode, Decode, EqNoBound, PartialEqNoBound, Clone, TypeInfo, RuntimeDebugNoBound, MaxEncodedLen,
|
Encode, Decode, EqNoBound, PartialEqNoBound, Clone, TypeInfo, RuntimeDebugNoBound, MaxEncodedLen,
|
||||||
)]
|
)]
|
||||||
#[scale_info(skip_type_params(T))]
|
#[scale_info(skip_type_params(T))]
|
||||||
pub struct UnstakeRequest<T: Config> {
|
pub struct UnstakeRequest<T: Config> {
|
||||||
/// This list of stashes being processed in this request, and their corresponding deposit.
|
/// This list of stashes are being processed in this request, and their corresponding deposit.
|
||||||
pub(crate) stashes: BoundedVec<(T::AccountId, BalanceOf<T>), T::BatchSize>,
|
pub stashes: BoundedVec<(T::AccountId, BalanceOf<T>), T::BatchSize>,
|
||||||
/// The list of eras for which they have been checked.
|
/// The list of eras for which they have been checked.
|
||||||
pub(crate) checked: BoundedVec<EraIndex, MaxChecking<T>>,
|
pub checked: BoundedVec<EraIndex, MaxChecking<T>>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,7 +113,13 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream {
|
|||||||
}
|
}
|
||||||
debug_assert_eq!(fn_weight.len(), methods.len());
|
debug_assert_eq!(fn_weight.len(), methods.len());
|
||||||
|
|
||||||
let fn_doc = methods.iter().map(|method| &method.docs).collect::<Vec<_>>();
|
let fn_doc = methods
|
||||||
|
.iter()
|
||||||
|
.map(|method| {
|
||||||
|
let reference = format!("See [`Pallet::{}`].", method.name);
|
||||||
|
quote!(#reference)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let args_name = methods
|
let args_name = methods
|
||||||
.iter()
|
.iter()
|
||||||
@@ -175,9 +181,8 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream {
|
|||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
});
|
});
|
||||||
|
|
||||||
let default_docs = [syn::parse_quote!(
|
let default_docs =
|
||||||
r"Contains one variant per dispatchable that can be called by an extrinsic."
|
[syn::parse_quote!(r"Contains a variant per dispatchable extrinsic that this pallet has.")];
|
||||||
)];
|
|
||||||
let docs = if docs.is_empty() { &default_docs[..] } else { &docs[..] };
|
let docs = if docs.is_empty() { &default_docs[..] } else { &docs[..] };
|
||||||
|
|
||||||
let maybe_compile_error = if def.call.is_none() {
|
let maybe_compile_error = if def.call.is_none() {
|
||||||
@@ -274,7 +279,7 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream {
|
|||||||
#frame_support::Never,
|
#frame_support::Never,
|
||||||
),
|
),
|
||||||
#(
|
#(
|
||||||
#( #[doc = #fn_doc] )*
|
#[doc = #fn_doc]
|
||||||
#[codec(index = #call_index)]
|
#[codec(index = #call_index)]
|
||||||
#fn_name {
|
#fn_name {
|
||||||
#(
|
#(
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::pallet::Def;
|
use crate::pallet::Def;
|
||||||
use frame_support_procedural_tools::get_doc_literals;
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// * Generate default rust doc
|
/// * Generate default rust doc
|
||||||
@@ -31,15 +30,19 @@ pub fn expand_config(def: &mut Def) -> proc_macro2::TokenStream {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if get_doc_literals(&config_item.attrs).is_empty() {
|
config_item.attrs.insert(
|
||||||
config_item.attrs.push(syn::parse_quote!(
|
0,
|
||||||
#[doc = r"
|
syn::parse_quote!(
|
||||||
Configuration trait of this pallet.
|
#[doc = r"Configuration trait of this pallet.
|
||||||
|
|
||||||
Implement this type for a runtime in order to customize this pallet.
|
The main purpose of this trait is to act as an interface between this pallet and the runtime in
|
||||||
"]
|
which it is embedded in. A type, function, or constant in this trait is essentially left to be
|
||||||
));
|
configured by the runtime that includes this pallet.
|
||||||
}
|
|
||||||
|
Consequently, a runtime that wants to include this pallet must implement this trait."
|
||||||
|
]
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,8 +20,6 @@ use proc_macro2::Span;
|
|||||||
use crate::pallet::Def;
|
use crate::pallet::Def;
|
||||||
|
|
||||||
pub fn expand_doc_only(def: &mut Def) -> proc_macro2::TokenStream {
|
pub fn expand_doc_only(def: &mut Def) -> proc_macro2::TokenStream {
|
||||||
let storage_names = def.storages.iter().map(|storage| &storage.ident);
|
|
||||||
let storage_docs = def.storages.iter().map(|storage| &storage.docs);
|
|
||||||
let dispatchables = if let Some(call_def) = &def.call {
|
let dispatchables = if let Some(call_def) = &def.call {
|
||||||
let type_impl_generics = def.type_impl_generics(Span::call_site());
|
let type_impl_generics = def.type_impl_generics(Span::call_site());
|
||||||
call_def
|
call_def
|
||||||
@@ -35,17 +33,16 @@ pub fn expand_doc_only(def: &mut Def) -> proc_macro2::TokenStream {
|
|||||||
.map(|(_, arg_name, arg_type)| quote::quote!( #arg_name: #arg_type, ))
|
.map(|(_, arg_name, arg_type)| quote::quote!( #arg_name: #arg_type, ))
|
||||||
.collect::<proc_macro2::TokenStream>();
|
.collect::<proc_macro2::TokenStream>();
|
||||||
let docs = &method.docs;
|
let docs = &method.docs;
|
||||||
let line_2 =
|
|
||||||
format!(" designed to document the [`{}`][`Call::{}`] variant of", name, name);
|
let real = format!(" [`Pallet::{}`].", name);
|
||||||
quote::quote!(
|
quote::quote!(
|
||||||
#( #[doc = #docs] )*
|
#( #[doc = #docs] )*
|
||||||
///
|
///
|
||||||
/// ---
|
/// # Warning: Doc-Only
|
||||||
///
|
///
|
||||||
/// NOTE: This function is an automatically generated, doc only, uncallable stub.
|
/// This function is an automatically generated, and is doc-only, uncallable
|
||||||
#[ doc = #line_2 ]
|
/// stub. See the real version in
|
||||||
/// the pallet [`Call`] enum. You should not attempt to call this function
|
#[ doc = #real ]
|
||||||
/// directly.
|
|
||||||
pub fn #name<#type_impl_generics>(#args) { unreachable!(); }
|
pub fn #name<#type_impl_generics>(#args) { unreachable!(); }
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -54,22 +51,49 @@ pub fn expand_doc_only(def: &mut Def) -> proc_macro2::TokenStream {
|
|||||||
quote::quote!()
|
quote::quote!()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let storage_types = def
|
||||||
|
.storages
|
||||||
|
.iter()
|
||||||
|
.map(|storage| {
|
||||||
|
let storage_name = &storage.ident;
|
||||||
|
let storage_type_docs = &storage.docs;
|
||||||
|
let real = format!("[`pallet::{}`].", storage_name);
|
||||||
|
quote::quote!(
|
||||||
|
#( #[doc = #storage_type_docs] )*
|
||||||
|
///
|
||||||
|
/// # Warning: Doc-Only
|
||||||
|
///
|
||||||
|
/// This type is automatically generated, and is doc-only. See the real version in
|
||||||
|
#[ doc = #real ]
|
||||||
|
pub struct #storage_name();
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<proc_macro2::TokenStream>();
|
||||||
|
|
||||||
quote::quote!(
|
quote::quote!(
|
||||||
/// Auto-generated docs-only module listing all defined storage types for this pallet.
|
/// Auto-generated docs-only module listing all (public and private) defined storage types
|
||||||
/// Note that members of this module cannot be used directly and are only provided for
|
/// for this pallet.
|
||||||
/// documentation purposes.
|
///
|
||||||
|
/// # Warning: Doc-Only
|
||||||
|
///
|
||||||
|
/// Members of this module cannot be used directly and are only provided for documentation
|
||||||
|
/// purposes.
|
||||||
|
///
|
||||||
|
/// To see the actual storage type, find a struct with the same name at the root of the
|
||||||
|
/// pallet, in the list of [*Type Definitions*](../index.html#types).
|
||||||
#[cfg(doc)]
|
#[cfg(doc)]
|
||||||
pub mod storage_types {
|
pub mod storage_types {
|
||||||
use super::*;
|
use super::*;
|
||||||
#(
|
#storage_types
|
||||||
#( #[doc = #storage_docs] )*
|
|
||||||
pub struct #storage_names();
|
|
||||||
)*
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Auto-generated docs-only module listing all defined dispatchables for this pallet.
|
/// Auto-generated docs-only module listing all defined dispatchables for this pallet.
|
||||||
/// Note that members of this module cannot be used directly and are only provided for
|
///
|
||||||
/// documentation purposes.
|
/// # Warning: Doc-Only
|
||||||
|
///
|
||||||
|
/// Members of this module cannot be used directly and are only provided for documentation
|
||||||
|
/// purposes. To see the real version of each dispatchable, look for them in [`Pallet`] or
|
||||||
|
/// [`Call`].
|
||||||
#[cfg(doc)]
|
#[cfg(doc)]
|
||||||
pub mod dispatchables {
|
pub mod dispatchables {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@@ -110,10 +110,7 @@ pub fn expand_error(def: &mut Def) -> proc_macro2::TokenStream {
|
|||||||
|
|
||||||
if get_doc_literals(&error_item.attrs).is_empty() {
|
if get_doc_literals(&error_item.attrs).is_empty() {
|
||||||
error_item.attrs.push(syn::parse_quote!(
|
error_item.attrs.push(syn::parse_quote!(
|
||||||
#[doc = r"
|
#[doc = "The `Error` enum of this pallet."]
|
||||||
Custom [dispatch errors](https://docs.substrate.io/main-docs/build/events-errors/)
|
|
||||||
of this pallet.
|
|
||||||
"]
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -97,12 +97,9 @@ pub fn expand_event(def: &mut Def) -> proc_macro2::TokenStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if get_doc_literals(&event_item.attrs).is_empty() {
|
if get_doc_literals(&event_item.attrs).is_empty() {
|
||||||
event_item.attrs.push(syn::parse_quote!(
|
event_item
|
||||||
#[doc = r"
|
.attrs
|
||||||
The [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted
|
.push(syn::parse_quote!(#[doc = "The `Event` enum of this pallet"]));
|
||||||
by this pallet.
|
|
||||||
"]
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// derive some traits because system event require Clone, FullCodec, Eq, PartialEq and Debug
|
// derive some traits because system event require Clone, FullCodec, Eq, PartialEq and Debug
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ mod type_value;
|
|||||||
mod validate_unsigned;
|
mod validate_unsigned;
|
||||||
|
|
||||||
use crate::pallet::Def;
|
use crate::pallet::Def;
|
||||||
use frame_support_procedural_tools::get_doc_literals;
|
|
||||||
use quote::ToTokens;
|
use quote::ToTokens;
|
||||||
|
|
||||||
/// Merge where clause together, `where` token span is taken from the first not none one.
|
/// Merge where clause together, `where` token span is taken from the first not none one.
|
||||||
@@ -75,16 +74,24 @@ pub fn expand(mut def: Def) -> proc_macro2::TokenStream {
|
|||||||
let tt_default_parts = tt_default_parts::expand_tt_default_parts(&mut def);
|
let tt_default_parts = tt_default_parts::expand_tt_default_parts(&mut def);
|
||||||
let doc_only = doc_only::expand_doc_only(&mut def);
|
let doc_only = doc_only::expand_doc_only(&mut def);
|
||||||
|
|
||||||
if get_doc_literals(&def.item.attrs).is_empty() {
|
def.item.attrs.insert(
|
||||||
def.item.attrs.push(syn::parse_quote!(
|
0,
|
||||||
#[doc = r"
|
syn::parse_quote!(
|
||||||
The module that hosts all the
|
#[doc = r"The `pallet` module in each FRAME pallet hosts the most important items needed
|
||||||
[FRAME](https://docs.substrate.io/main-docs/build/events-errors/)
|
to construct this pallet.
|
||||||
types needed to add this pallet to a
|
|
||||||
runtime.
|
The main components of this pallet are:
|
||||||
"]
|
- [`Pallet`], which implements all of the dispatchable extrinsics of the pallet, among
|
||||||
));
|
other public functions.
|
||||||
}
|
- The subset of the functions that are dispatchable can be identified either in the
|
||||||
|
[`dispatchables`] module or in the [`Call`] enum.
|
||||||
|
- [`storage_types`], which contains the list of all types that are representing a
|
||||||
|
storage item. Otherwise, all storage items are listed among [*Type Definitions*](#types).
|
||||||
|
- [`Config`], which contains the configuration trait of this pallet.
|
||||||
|
- [`Event`] and [`Error`], which are listed among the [*Enums*](#enums).
|
||||||
|
"]
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
let new_items = quote::quote!(
|
let new_items = quote::quote!(
|
||||||
#metadata_docs
|
#metadata_docs
|
||||||
|
|||||||
@@ -62,8 +62,8 @@ pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream {
|
|||||||
if get_doc_literals(&pallet_item.attrs).is_empty() {
|
if get_doc_literals(&pallet_item.attrs).is_empty() {
|
||||||
pallet_item.attrs.push(syn::parse_quote!(
|
pallet_item.attrs.push(syn::parse_quote!(
|
||||||
#[doc = r"
|
#[doc = r"
|
||||||
The [pallet](https://docs.substrate.io/reference/frame-pallets/#pallets) implementing
|
The `Pallet` struct, the main type that implements traits and standalone
|
||||||
the on-chain logic.
|
functions within the pallet.
|
||||||
"]
|
"]
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ use crate::{
|
|||||||
Def,
|
Def,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
use itertools::Itertools;
|
||||||
use quote::ToTokens;
|
use quote::ToTokens;
|
||||||
use std::{collections::HashMap, ops::IndexMut};
|
use std::{collections::HashMap, ops::IndexMut};
|
||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
@@ -310,6 +311,65 @@ pub fn process_generics(def: &mut Def) -> syn::Result<Vec<ResultOnEmptyStructMet
|
|||||||
Ok(on_empty_struct_metadata)
|
Ok(on_empty_struct_metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn augment_final_docs(def: &mut Def) {
|
||||||
|
// expand the docs with a new line showing the storage type (value, map, double map, etc), and
|
||||||
|
// the key/value type(s).
|
||||||
|
let mut push_string_literal = |doc_line: &str, storage: &mut StorageDef| {
|
||||||
|
let item = &mut def.item.content.as_mut().expect("Checked by def").1[storage.index];
|
||||||
|
let typ_item = match item {
|
||||||
|
syn::Item::Type(t) => t,
|
||||||
|
_ => unreachable!("Checked by def"),
|
||||||
|
};
|
||||||
|
typ_item.attrs.push(syn::parse_quote!(#[doc = ""]));
|
||||||
|
typ_item.attrs.push(syn::parse_quote!(#[doc = #doc_line]));
|
||||||
|
};
|
||||||
|
def.storages.iter_mut().for_each(|storage| match &storage.metadata {
|
||||||
|
Metadata::Value { value } => {
|
||||||
|
let doc_line = format!(
|
||||||
|
"Storage type is [`StorageValue`] with value type `{}`.",
|
||||||
|
value.to_token_stream()
|
||||||
|
);
|
||||||
|
push_string_literal(&doc_line, storage);
|
||||||
|
},
|
||||||
|
Metadata::Map { key, value } => {
|
||||||
|
let doc_line = format!(
|
||||||
|
"Storage type is [`StorageMap`] with key type `{}` and value type `{}`.",
|
||||||
|
key.to_token_stream(),
|
||||||
|
value.to_token_stream()
|
||||||
|
);
|
||||||
|
push_string_literal(&doc_line, storage);
|
||||||
|
},
|
||||||
|
Metadata::DoubleMap { key1, key2, value } => {
|
||||||
|
let doc_line = format!(
|
||||||
|
"Storage type is [`StorageDoubleMap`] with key1 type {}, key2 type {} and value type {}.",
|
||||||
|
key1.to_token_stream(),
|
||||||
|
key2.to_token_stream(),
|
||||||
|
value.to_token_stream()
|
||||||
|
);
|
||||||
|
push_string_literal(&doc_line, storage);
|
||||||
|
},
|
||||||
|
Metadata::NMap { keys, value, .. } => {
|
||||||
|
let doc_line = format!(
|
||||||
|
"Storage type is [`StorageNMap`] with keys type ({}) and value type {}.",
|
||||||
|
keys.iter()
|
||||||
|
.map(|k| k.to_token_stream().to_string())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", "),
|
||||||
|
value.to_token_stream()
|
||||||
|
);
|
||||||
|
push_string_literal(&doc_line, storage);
|
||||||
|
},
|
||||||
|
Metadata::CountedMap { key, value } => {
|
||||||
|
let doc_line = format!(
|
||||||
|
"Storage type is [`CountedStorageMap`] with key type {} and value type {}.",
|
||||||
|
key.to_token_stream(),
|
||||||
|
value.to_token_stream()
|
||||||
|
);
|
||||||
|
push_string_literal(&doc_line, storage);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// * generate StoragePrefix structs (e.g. for a storage `MyStorage` a struct with the name
|
/// * generate StoragePrefix structs (e.g. for a storage `MyStorage` a struct with the name
|
||||||
/// `_GeneratedPrefixForStorage$NameOfStorage` is generated) and implements StorageInstance trait.
|
/// `_GeneratedPrefixForStorage$NameOfStorage` is generated) and implements StorageInstance trait.
|
||||||
@@ -323,6 +383,8 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream {
|
|||||||
Err(e) => return e.into_compile_error(),
|
Err(e) => return e.into_compile_error(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
augment_final_docs(def);
|
||||||
|
|
||||||
// Check for duplicate prefixes
|
// Check for duplicate prefixes
|
||||||
let mut prefix_set = HashMap::new();
|
let mut prefix_set = HashMap::new();
|
||||||
let mut errors = def
|
let mut errors = def
|
||||||
@@ -365,10 +427,6 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream {
|
|||||||
if let Some(getter) = &storage.getter {
|
if let Some(getter) = &storage.getter {
|
||||||
let completed_where_clause =
|
let completed_where_clause =
|
||||||
super::merge_where_clauses(&[&storage.where_clause, &def.config.where_clause]);
|
super::merge_where_clauses(&[&storage.where_clause, &def.config.where_clause]);
|
||||||
let docs = storage
|
|
||||||
.docs
|
|
||||||
.iter()
|
|
||||||
.map(|d| quote::quote_spanned!(storage.attr_span => #[doc = #d]));
|
|
||||||
|
|
||||||
let ident = &storage.ident;
|
let ident = &storage.ident;
|
||||||
let gen = &def.type_use_generics(storage.attr_span);
|
let gen = &def.type_use_generics(storage.attr_span);
|
||||||
@@ -378,6 +436,13 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream {
|
|||||||
|
|
||||||
let cfg_attrs = &storage.cfg_attrs;
|
let cfg_attrs = &storage.cfg_attrs;
|
||||||
|
|
||||||
|
// If the storage item is public, just link to it rather than copy-pasting the docs.
|
||||||
|
let getter_doc_line = if matches!(storage.vis, syn::Visibility::Public(_)) {
|
||||||
|
format!("An auto-generated getter for [`{}`].", storage.ident)
|
||||||
|
} else {
|
||||||
|
storage.docs.iter().map(|d| d.into_token_stream().to_string()).join("\n")
|
||||||
|
};
|
||||||
|
|
||||||
match &storage.metadata {
|
match &storage.metadata {
|
||||||
Metadata::Value { value } => {
|
Metadata::Value { value } => {
|
||||||
let query = match storage.query_kind.as_ref().expect("Checked by def") {
|
let query = match storage.query_kind.as_ref().expect("Checked by def") {
|
||||||
@@ -394,7 +459,7 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream {
|
|||||||
quote::quote_spanned!(storage.attr_span =>
|
quote::quote_spanned!(storage.attr_span =>
|
||||||
#(#cfg_attrs)*
|
#(#cfg_attrs)*
|
||||||
impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause {
|
impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause {
|
||||||
#( #docs )*
|
#[doc = #getter_doc_line]
|
||||||
pub fn #getter() -> #query {
|
pub fn #getter() -> #query {
|
||||||
<
|
<
|
||||||
#full_ident as #frame_support::storage::StorageValue<#value>
|
#full_ident as #frame_support::storage::StorageValue<#value>
|
||||||
@@ -418,7 +483,7 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream {
|
|||||||
quote::quote_spanned!(storage.attr_span =>
|
quote::quote_spanned!(storage.attr_span =>
|
||||||
#(#cfg_attrs)*
|
#(#cfg_attrs)*
|
||||||
impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause {
|
impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause {
|
||||||
#( #docs )*
|
#[doc = #getter_doc_line]
|
||||||
pub fn #getter<KArg>(k: KArg) -> #query where
|
pub fn #getter<KArg>(k: KArg) -> #query where
|
||||||
KArg: #frame_support::codec::EncodeLike<#key>,
|
KArg: #frame_support::codec::EncodeLike<#key>,
|
||||||
{
|
{
|
||||||
@@ -444,7 +509,7 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream {
|
|||||||
quote::quote_spanned!(storage.attr_span =>
|
quote::quote_spanned!(storage.attr_span =>
|
||||||
#(#cfg_attrs)*
|
#(#cfg_attrs)*
|
||||||
impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause {
|
impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause {
|
||||||
#( #docs )*
|
#[doc = #getter_doc_line]
|
||||||
pub fn #getter<KArg>(k: KArg) -> #query where
|
pub fn #getter<KArg>(k: KArg) -> #query where
|
||||||
KArg: #frame_support::codec::EncodeLike<#key>,
|
KArg: #frame_support::codec::EncodeLike<#key>,
|
||||||
{
|
{
|
||||||
@@ -470,7 +535,7 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream {
|
|||||||
quote::quote_spanned!(storage.attr_span =>
|
quote::quote_spanned!(storage.attr_span =>
|
||||||
#(#cfg_attrs)*
|
#(#cfg_attrs)*
|
||||||
impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause {
|
impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause {
|
||||||
#( #docs )*
|
#[doc = #getter_doc_line]
|
||||||
pub fn #getter<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> #query where
|
pub fn #getter<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> #query where
|
||||||
KArg1: #frame_support::codec::EncodeLike<#key1>,
|
KArg1: #frame_support::codec::EncodeLike<#key1>,
|
||||||
KArg2: #frame_support::codec::EncodeLike<#key2>,
|
KArg2: #frame_support::codec::EncodeLike<#key2>,
|
||||||
@@ -498,7 +563,7 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream {
|
|||||||
quote::quote_spanned!(storage.attr_span =>
|
quote::quote_spanned!(storage.attr_span =>
|
||||||
#(#cfg_attrs)*
|
#(#cfg_attrs)*
|
||||||
impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause {
|
impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause {
|
||||||
#( #docs )*
|
#[doc = #getter_doc_line]
|
||||||
pub fn #getter<KArg>(key: KArg) -> #query
|
pub fn #getter<KArg>(key: KArg) -> #query
|
||||||
where
|
where
|
||||||
KArg: #frame_support::storage::types::EncodeLikeTuple<
|
KArg: #frame_support::storage::types::EncodeLikeTuple<
|
||||||
|
|||||||
@@ -1520,6 +1520,17 @@ pub mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Prelude to be used for pallet testing, for ease of use.
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub mod testing_prelude {
|
||||||
|
pub use super::{
|
||||||
|
assert_err, assert_err_ignore_postinfo, assert_err_with_weight, assert_error_encoded_size,
|
||||||
|
assert_noop, assert_ok, assert_storage_noop, bounded_btree_map, bounded_vec,
|
||||||
|
parameter_types, traits::Get,
|
||||||
|
};
|
||||||
|
pub use sp_arithmetic::assert_eq_error_rate;
|
||||||
|
}
|
||||||
|
|
||||||
/// Prelude to be used alongside pallet macro, for ease of use.
|
/// Prelude to be used alongside pallet macro, for ease of use.
|
||||||
pub mod pallet_prelude {
|
pub mod pallet_prelude {
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ pub mod pallet {
|
|||||||
#[pallet::pallet]
|
#[pallet::pallet]
|
||||||
pub struct Pallet<T>(_);
|
pub struct Pallet<T>(_);
|
||||||
|
|
||||||
/// The configuration trait
|
/// The configuration trait.
|
||||||
#[pallet::config]
|
#[pallet::config]
|
||||||
#[pallet::disable_frame_system_supertrait_check]
|
#[pallet::disable_frame_system_supertrait_check]
|
||||||
pub trait Config: 'static + Eq + Clone {
|
pub trait Config: 'static + Eq + Clone {
|
||||||
|
|||||||
Reference in New Issue
Block a user