From a3428d47b5f27e28752e0d85901f4586a2508eb5 Mon Sep 17 00:00:00 2001 From: Henrique Nogara Date: Mon, 6 Jan 2025 17:48:45 -0300 Subject: [PATCH 1/3] Improve docs - part 1 --- pallets/pips/src/lib.rs | 300 +++++++++++++++++++++++++++------------- 1 file changed, 202 insertions(+), 98 deletions(-) diff --git a/pallets/pips/src/lib.rs b/pallets/pips/src/lib.rs index f66f35b88..2e036319c 100644 --- a/pallets/pips/src/lib.rs +++ b/pallets/pips/src/lib.rs @@ -15,40 +15,28 @@ //! # Pips Module //! -//! Polymesh Improvement Proposals (PIPs) are dispatchables that can be `propose`d for execution. -//! These PIPs can either be proposed by a committee, or they can be proposed by a community member, -//! in which case they can `vote`d on by all POLYX token holders. +//! The Pips module allows for the creation, voting, and management of Polymesh Improvement Proposals (PIPs). +//! PIPs can be proposed by either a committee or a community member. Community proposals can be voted on by all POLYX token holders. //! -//! Voting, or rather "signalling", which currently scales linearly with POLX, -//! in this system is used to direct the Governance Councils (GCs) -//! attention by moving proposals up and down a review queue, specific to community proposals. +//! ## Voting and Signalling //! -//! From time to time, the GC will take a `snapshot` of this queue, -//! meet and review PIPs, and reject, approve, or skip the proposal (via `enact_snapshot_results`). -//! Any approved PIPs from this snapshot will then be scheduled, -//! in order of signal value, to be executed automatically on the blockchain. -//! However, using `reschedule_execution`, a special Release Coordinator (RC), a member of the GC, -//! can reschedule approved PIPs at will, except for a PIP to replace the RC. -//! Once no longer relevant, the snapshot can be cleared by the GC through `clear_snapshot`. +//! Voting in this system is used to direct the Governance Councils' (GCs) attention by moving proposals up and down a review queue. +//! The GC periodically takes a snapshot of this queue, reviews the PIPs, and decides to reject, approve, or skip them. +//! Approved PIPs are scheduled for execution on the blockchain. The Release Coordinator (RC), a GC member, can reschedule approved PIPs, except for those replacing the RC. +//! The snapshot can be cleared by the GC once it is no longer relevant. //! -//! As aforementioned, the GC can skip a PIP, which will increments its "skipped count". -//! Should a configurable limit for the skipped count be exceeded, a PIP can no longer be skipped. -//! -//! Committee proposals, as noted before, do not enter the snapshot or receive votes. -//! However, the GC can at any moment approve such a PIP via `approve_committee_proposal`. -//! -//! Should the GC want to reject an active (scheduled or pending) proposal, -//! they can do so at any time using `reject_proposal`. -//! For garbage collection purposes, it is also possible to use `prune_proposal`, -//! which will, without any restrictions on its state, remove the PIP's storage. +//! ## Skipping and Limits //! +//! The GC can skip a PIP, which increments its "skipped count". If a configurable limit for the skipped count is exceeded, the PIP can no longer be skipped. +//! Committee proposals do not enter the snapshot or receive votes but can be approved by the GC at any time. +//! The GC can also reject active proposals or prune them to remove their storage. //! //! ## Overview //! //! The Pips module provides functions for: //! //! - Proposing and amending PIPs -//! - Signalling (voting) on them for adjusting priority in the review queue +//! - Voting on PIPs to adjust their priority in the review queue //! - Taking and clearing snapshots of the queue //! - Approving, rejecting, skipping, and rescheduling PIPs //! @@ -56,31 +44,28 @@ //! //! ### Dispatchable Functions //! -//! #### Configuration changes -//! -//! - `set_prune_historical_pips` change whether historical PIPs are pruned -//! - `set_min_proposal_deposit` change min deposit to create a proposal -//! - `set_default_enactment_period` change the period after enactment after which the proposal is executed -//! - `set_max_pip_skip_count` change the maximum times a PIP can be skipped -//! - `set_active_pip_limit` change the maximum number of concurrently active PIPs +//! #### Configuration Changes //! -//! #### Other +//! - `set_prune_historical_pips` - Change whether historical PIPs are pruned +//! - `set_min_proposal_deposit` - Change the minimum deposit to create a proposal +//! - `set_pending_pip_expiry` - Change the amount of blocks after which a pending PIP is expired +//! - `set_default_enactment_period` - Change the period after which the proposal is executed +//! - `set_max_pip_skip_count` - Change the maximum times a PIP can be skipped +//! - `set_active_pip_limit` - Change the maximum number of concurrently active PIPs //! -//! - `propose` - token holders can propose a new PIP. -//! - `amend_proposal` - allows the creator of a proposal to amend the proposal details -//! - `cancel_proposal` - allows the creator of a proposal to cancel the proposal -//! - `vote` - token holders, including the PIP's proposer, can vote on a PIP. -//! - `approve_committee_proposal` - allows the GC to approve a committee proposal -//! - `reject_proposal` - reject an active proposal and refund deposits -//! - `prune_proposal` - prune all storage associated with proposal and refund deposits -//! - `reschedule_execution` - release coordinator can reschedule a PIPs execution -//! - `clear_snapshot` - clears the snapshot -//! - `snapshot` - takes a new snapshot of the review queue -//! - `enact_snapshot_results` - enters results (approve, reject, and skip) for PIPs in snapshot +//! #### Other Functions //! -//! ### Public Functions -//! -//! - `end_block` - executes scheduled proposals +//! - `propose` - Token holders can propose a new PIP +//! - `amend_proposal` - Allows the creator of a proposal to amend the proposal details +//! - `cancel_proposal` - Allows the creator of a proposal to cancel the proposal +//! - `vote` - Token holders can vote on a PIP +//! - `approve_committee_proposal` - Allows the GC to approve a committee proposal +//! - `reject_proposal` - Reject an active proposal and refund deposits +//! - `prune_proposal` - Prune all storage associated with a proposal and refund deposits +//! - `reschedule_execution` - Release Coordinator can reschedule a PIP's execution +//! - `clear_snapshot` - Clears the snapshot +//! - `snapshot` - Takes a new snapshot of the review queue +//! - `enact_snapshot_results` - Enters results (approve, reject, and skip) for PIPs in the snapshot #![cfg_attr(not(feature = "std"), no_std)] @@ -165,34 +150,34 @@ pub mod pallet { /// The proposal must be from the community, but isn't. NotFromCommunity, /// The given dispatchable call is not valid for this proposal. - /// The proposal must be by community, but isn't. + /// The proposal must be from a committee, but isn't. NotByCommittee, - /// The current number of active (pending | scheduled) PIPs exceed the maximum + /// The current number of active (pending or scheduled) PIPs exceeds the maximum /// and the proposal is not by a committee. TooManyActivePips, - /// Proposer specifies an incorrect deposit + /// Proposer specifies an incorrect deposit amount. IncorrectDeposit, - /// Proposer can't afford to lock minimum deposit + /// Proposer cannot afford to lock the minimum deposit. InsufficientDeposit, /// The proposal does not exist. NoSuchProposal, - /// Not part of governance committee. + /// The caller is not a member of the governance committee. NotACommitteeMember, - /// When a block number is less than current block number. + /// The specified block number is less than the current block number. InvalidFutureBlockNumber, - /// When number of votes overflows. + /// The number of votes exceeds the allowed limit. NumberOfVotesExceeded, - /// When stake amount of a vote overflows. + /// The stake amount of a vote exceeds the allowed limit. StakeAmountOfVotesExceeded, - /// Missing current DID + /// The current DID is missing. MissingCurrentIdentity, - /// Proposal is not in the correct state + /// The proposal is not in the correct state for the requested operation. IncorrectProposalState, /// When enacting snapshot results, an unskippable PIP was skipped. CannotSkipPip, /// Tried to enact results for the snapshot queue overflowing its length. SnapshotResultTooLarge, - /// Tried to enact result for PIP with id different from that at the position in the queue. + /// Tried to enact result for PIP with an ID different from that at the position in the queue. SnapshotIdMismatch, /// Execution of a scheduled proposal failed because it is missing. ScheduledProposalDoesntExist, @@ -203,13 +188,24 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - /// Pruning Historical PIPs is enabled or disabled (caller DID, old value, new value) - HistoricalPipsPruned(IdentityId, bool, bool), - /// A PIP was made with a `Balance` stake. + /// Historical PIPs Pruning has been set. /// - /// # Parameters: + /// Parameters: + /// - `IdentityId`: The DID of the caller. + /// - `bool`: The old value of the pruning setting. + /// - `bool`: The new value of the pruning setting. + HistoricalPipsPruned(IdentityId, bool, bool), + /// A PIP was created with a specified `Balance` stake. /// - /// Caller DID, Proposer, PIP ID, deposit, URL, description, expiry time, proposal data. + /// Parameters: + /// - `IdentityId`: The DID of the caller. + /// - `Proposer`: The proposer of the PIP. + /// - `PipId`: The ID of the PIP. + /// - `Balance`: The deposit amount. + /// - `Option`: The URL for proposal discussion. + /// - `Option`: The description of the proposal. + /// - `MaybeBlock`: The expiry time of the proposal. + /// - `ProposalData`: The data of the proposal. ProposalCreated( IdentityId, Proposer, @@ -220,45 +216,110 @@ pub mod pallet { MaybeBlock, ProposalData, ), - /// Triggered each time the state of a proposal is amended + /// The state of a proposal was updated. + /// + /// Parameters: + /// - `IdentityId`: The DID of the caller. + /// - `PipId`: The ID of the PIP. + /// - `ProposalState`: The new state of the proposal. ProposalStateUpdated(IdentityId, PipId, ProposalState), - /// `AccountId` voted `bool` on the proposal referenced by `PipId` + /// An account voted on a proposal. + /// + /// Parameters: + /// - `IdentityId`: The DID of the caller. + /// - `T::AccountId`: The account that voted. + /// - `PipId`: The ID of the PIP. + /// - `bool`: The vote (true for aye, false for nay). + /// - `Balance`: The deposit amount of the vote. Voted(IdentityId, T::AccountId, PipId, bool, Balance), - /// Pip has been closed, bool indicates whether data is pruned + /// A PIP was closed. + /// + /// Parameters: + /// - `IdentityId`: The DID of the caller. + /// - `PipId`: The ID of the PIP. + /// - `bool`: Indicates whether the data was pruned. PipClosed(IdentityId, PipId, bool), - /// Execution of a PIP has been scheduled at specific block. + /// The execution of a PIP was scheduled. + /// + /// Parameters: + /// - `IdentityId`: The DID of the caller. + /// - `PipId`: The ID of the PIP. + /// - `T::BlockNumber`: The block number at which the PIP is scheduled for execution. ExecutionScheduled(IdentityId, PipId, T::BlockNumber), - /// Default enactment period (in blocks) has been changed. - /// (caller DID, old period, new period) + /// The default enactment period was changed. + /// + /// Parameters: + /// - `IdentityId`: The DID of the caller. + /// - `T::BlockNumber`: The old enactment period. + /// - `T::BlockNumber`: The new enactment period. DefaultEnactmentPeriodChanged(IdentityId, T::BlockNumber, T::BlockNumber), - /// Minimum deposit amount modified - /// (caller DID, old amount, new amount) + /// The minimum deposit amount for proposals was changed. + /// + /// Parameters: + /// - `IdentityId`: The DID of the caller. + /// - `Balance`: The old deposit amount. + /// - `Balance`: The new deposit amount. MinimumProposalDepositChanged(IdentityId, Balance, Balance), - /// Amount of blocks after which a pending PIP expires. - /// (caller DID, old expiry, new expiry) + /// The expiry time for pending PIPs was changed. + /// + /// Parameters: + /// - `IdentityId`: The DID of the caller. + /// - `MaybeBlock`: The old expiry time. + /// - `MaybeBlock`: The new expiry time. PendingPipExpiryChanged( IdentityId, MaybeBlock, MaybeBlock, ), - /// The maximum times a PIP can be skipped was changed. - /// (caller DID, old value, new value) + /// The maximum number of times a PIP can be skipped was changed. + /// + /// Parameters: + /// - `IdentityId`: The DID of the caller. + /// - `SkippedCount`: The old skip count. + /// - `SkippedCount`: The new skip count. MaxPipSkipCountChanged(IdentityId, SkippedCount, SkippedCount), /// The maximum number of active PIPs was changed. - /// (caller DID, old value, new value) + /// + /// Parameters: + /// - `IdentityId`: The DID of the caller. + /// - `u32`: The old active PIP limit. + /// - `u32`: The new active PIP limit. ActivePipLimitChanged(IdentityId, u32, u32), - /// Refund proposal - /// (id, total amount) + /// A proposal was refunded. + /// + /// Parameters: + /// - `IdentityId`: The DID of the caller. + /// - `PipId`: The ID of the PIP. + /// - `Balance`: The total amount refunded. ProposalRefund(IdentityId, PipId, Balance), /// The snapshot was cleared. + /// + /// Parameters: + /// - `IdentityId`: The DID of the caller. + /// - `SnapshotId`: The ID of the snapshot. SnapshotCleared(IdentityId, SnapshotId), /// A new snapshot was taken. + /// + /// Parameters: + /// - `IdentityId`: The DID of the caller. + /// - `SnapshotId`: The ID of the snapshot. + /// - `Vec`: The list of PIPs in the snapshot. SnapshotTaken(IdentityId, SnapshotId, Vec), /// A PIP in the snapshot queue was skipped. - /// (gc_did, pip_id, new_skip_count) + /// + /// Parameters: + /// - `IdentityId`: The DID of the caller. + /// - `PipId`: The ID of the PIP. + /// - `SkippedCount`: The new skip count. PipSkipped(IdentityId, PipId, SkippedCount), - /// Results (e.g., approved, rejected, and skipped), were enacted for some PIPs. - /// (gc_did, snapshot_id_opt, skipped_pips_with_new_count, rejected_pips, approved_pips) + /// Results were enacted for some PIPs in the snapshot queue. + /// + /// Parameters: + /// - `IdentityId`: The DID of the caller. + /// - `Option`: The ID of the snapshot, if any. + /// - `Vec<(PipId, SkippedCount)>`: The list of skipped PIPs with their new skip counts. + /// - `Vec`: The list of rejected PIPs. + /// - `Vec`: The list of approved PIPs. SnapshotResultsEnacted( IdentityId, Option, @@ -267,12 +328,30 @@ pub mod pallet { Vec, ), /// Scheduling of the PIP for execution failed in the scheduler pallet. + /// + /// Parameters: + /// - `IdentityId`: The DID of the caller. + /// - `PipId`: The ID of the PIP. + /// - `T::BlockNumber`: The block number at which the PIP was scheduled for execution. ExecutionSchedulingFailed(IdentityId, PipId, T::BlockNumber), /// The PIP has been scheduled for expiry. + /// + /// Parameters: + /// - `IdentityId`: The DID of the caller. + /// - `PipId`: The ID of the PIP. + /// - `T::BlockNumber`: The block number at which the PIP is scheduled for expiry. ExpiryScheduled(IdentityId, PipId, T::BlockNumber), /// Scheduling of the PIP for expiry failed in the scheduler pallet. + /// + /// Parameters: + /// - `IdentityId`: The DID of the caller. + /// - `PipId`: The ID of the PIP. + /// - `T::BlockNumber`: The block number at which the PIP was scheduled for expiry. ExpirySchedulingFailed(IdentityId, PipId, T::BlockNumber), /// Cancelling the PIP execution failed in the scheduler pallet. + /// + /// Parameters: + /// - `PipId`: The ID of the PIP. ExecutionCancellingFailed(PipId), } @@ -282,9 +361,9 @@ pub mod pallet { { /// Currency type for this module. type Currency: LockableCurrencyExt; - /// Origin for enacting results for PIPs (reject, approve, skip, etc.). + /// Origin type for enacting results for PIPs (e.g., reject, approve, skip). type VotingMajorityOrigin: EnsureOrigin; - /// Committee + /// Governance committee responsible for overseeing the PIPs. type GovernanceCommittee: GovernanceGroupTrait<::Moment>; /// Voting majority origin for Technical Committee. type TechnicalCommitteeVMO: EnsureOrigin; @@ -292,12 +371,11 @@ pub mod pallet { type UpgradeCommitteeVMO: EnsureOrigin; /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// Weight calaculation. + /// Weight calculation for extrinsics in this pallet. type WeightInfo: WeightInfo; - /// Scheduler of executed or expired proposals. Since the scheduler module does not have - /// instances, the names of scheduled tasks should be guaranteed to be unique in this - /// pallet. Names cannot be just PIP IDs because names of executed and expired PIPs should be - /// different. + /// Scheduler for executed or expired proposals. The scheduler module does not have instances, + /// so the names of scheduled tasks must be unique within this pallet. Names cannot be just PIP + /// IDs because names of executed and expired PIPs should be different. type Scheduler: Named; /// A call type used by the scheduler. type SchedulerCall: From> + Into<::Proposal>; @@ -471,11 +549,18 @@ pub mod pallet { #[pallet::call] impl Pallet { - /// Change whether completed PIPs are pruned. - /// Can only be called by root. + /// Change whether completed PIPs (Polymesh Improvement Proposals) are pruned from storage. + /// This function can only be called by the root origin. /// /// # Arguments - /// * `prune` specifies whether completed PIPs should be pruned. + /// * `origin` - The origin of the call, which must be the root. + /// * `prune` - A boolean flag indicating whether completed PIPs should be pruned (`true`) or retained (`false`). + /// + /// # Events + /// * `HistoricalPipsPruned` - Emitted when the pruning setting is changed, containing the old and new values. + /// + /// # Errors + /// * `BadOrigin` - If the call is not made by the root origin. #[pallet::call_index(0)] #[pallet::weight((::WeightInfo::set_prune_historical_pips(), Operational))] pub fn set_prune_historical_pips(origin: OriginFor, prune: bool) -> DispatchResult { @@ -486,11 +571,17 @@ pub mod pallet { Ok(()) } - /// Change the minimum proposal deposit amount required to start a proposal. - /// Can only be called by root. + /// Change the minimum proposal deposit amount required to start a proposal. Can only be called by root. /// /// # Arguments - /// * `deposit` the new min deposit required to start a proposal + /// * `origin` - The origin of the call, which must be the root. + /// * `deposit` - The new minimum deposit required to start a proposal. + /// + /// # Events + /// * `MinimumProposalDepositChanged` - Emitted when the minimum proposal deposit is changed, containing the old and new values. + /// + /// # Errors + /// * `BadOrigin` - If the call is not made by the root origin. #[pallet::call_index(1)] #[pallet::weight((::WeightInfo::set_min_proposal_deposit(), Operational))] pub fn set_min_proposal_deposit(origin: OriginFor, deposit: Balance) -> DispatchResult { @@ -503,11 +594,17 @@ pub mod pallet { Ok(()) } - /// Change the default enactment period. - /// Can only be called by root. + /// Change the default enactment period. Can only be called by root. /// /// # Arguments - /// * `duration` the new default enactment period it takes for a scheduled PIP to be executed. + /// * `origin` - The origin of the call, which must be the root. + /// * `duration` - The new default enactment period it takes for a scheduled PIP to be executed. + /// + /// # Events + /// * `DefaultEnactmentPeriodChanged` - Emitted when the default enactment period is changed, containing the old and new values. + /// + /// # Errors + /// * `BadOrigin` - If the call is not made by the root origin. #[pallet::call_index(2)] #[pallet::weight((::WeightInfo::set_default_enactment_period(), Operational))] pub fn set_default_enactment_period( @@ -543,10 +640,17 @@ pub mod pallet { } /// Change the maximum skip count (`max_pip_skip_count`). - /// Can only be called by root. + /// This function can only be called by the root origin. /// /// # Arguments - /// * `max` skips before a PIP cannot be skipped by GC anymore. + /// * `origin` - The origin of the call, which must be the root. + /// * `max` - The new maximum number of skips allowed before a PIP cannot be skipped by the Governance Committee (GC) anymore. + /// + /// # Events + /// * `MaxPipSkipCountChanged` - Emitted when the maximum skip count is changed, containing the old and new values. + /// + /// # Errors + /// * `BadOrigin` - If the call is not made by the root origin. #[pallet::call_index(4)] #[pallet::weight((::WeightInfo::set_max_pip_skip_count(), Operational))] pub fn set_max_pip_skip_count(origin: OriginFor, max: SkippedCount) -> DispatchResult { From a3c3e930b1725f2e70b83699b7ff870c10cc0110 Mon Sep 17 00:00:00 2001 From: Henrique Nogara Date: Tue, 7 Jan 2025 10:13:39 -0300 Subject: [PATCH 2/3] Improve extrinsic documentation --- pallets/pips/src/lib.rs | 242 ++++++++++++++++++++++++++++------------ 1 file changed, 170 insertions(+), 72 deletions(-) diff --git a/pallets/pips/src/lib.rs b/pallets/pips/src/lib.rs index 2e036319c..988cf34a6 100644 --- a/pallets/pips/src/lib.rs +++ b/pallets/pips/src/lib.rs @@ -48,16 +48,14 @@ //! //! - `set_prune_historical_pips` - Change whether historical PIPs are pruned //! - `set_min_proposal_deposit` - Change the minimum deposit to create a proposal -//! - `set_pending_pip_expiry` - Change the amount of blocks after which a pending PIP is expired //! - `set_default_enactment_period` - Change the period after which the proposal is executed +//! - `set_pending_pip_expiry` - Change the amount of blocks after which a pending PIP is expired //! - `set_max_pip_skip_count` - Change the maximum times a PIP can be skipped //! - `set_active_pip_limit` - Change the maximum number of concurrently active PIPs //! //! #### Other Functions //! //! - `propose` - Token holders can propose a new PIP -//! - `amend_proposal` - Allows the creator of a proposal to amend the proposal details -//! - `cancel_proposal` - Allows the creator of a proposal to cancel the proposal //! - `vote` - Token holders can vote on a PIP //! - `approve_committee_proposal` - Allows the GC to approve a committee proposal //! - `reject_proposal` - Reject an active proposal and refund deposits @@ -549,8 +547,7 @@ pub mod pallet { #[pallet::call] impl Pallet { - /// Change whether completed PIPs (Polymesh Improvement Proposals) are pruned from storage. - /// This function can only be called by the root origin. + /// Sets the pruning setting for historical PIPs. This function can only be called by the root origin. /// /// # Arguments /// * `origin` - The origin of the call, which must be the root. @@ -571,7 +568,7 @@ pub mod pallet { Ok(()) } - /// Change the minimum proposal deposit amount required to start a proposal. Can only be called by root. + /// Changes the minimum proposal deposit amount required to start a proposal. This function can only be called by the root origin. /// /// # Arguments /// * `origin` - The origin of the call, which must be the root. @@ -594,11 +591,11 @@ pub mod pallet { Ok(()) } - /// Change the default enactment period. Can only be called by root. + /// Changes the default enactment period. This function can only be called by the root origin. /// /// # Arguments /// * `origin` - The origin of the call, which must be the root. - /// * `duration` - The new default enactment period it takes for a scheduled PIP to be executed. + /// * `period` - The new default enactment period. /// /// # Events /// * `DefaultEnactmentPeriodChanged` - Emitted when the default enactment period is changed, containing the old and new values. @@ -620,12 +617,17 @@ pub mod pallet { Ok(()) } - /// Change the amount of blocks after which a pending PIP is expired. - /// If `expiry` is `None` then PIPs never expire. - /// Can only be called by root. + /// Sets the expiry duration (in blocks) for pending PIPs. This function can only be called by the root origin. /// /// # Arguments - /// * `expiry` the block-time it takes for a still-`Pending` PIP to expire. + /// * `origin` - The origin of the call, which must be the root. + /// * `expiry` - The new expiry duration for pending PIPs. If `None`, PIPs never expire. + /// + /// # Events + /// * `PendingPipExpiryChanged` - Emitted when the pending PIP expiry duration is changed, containing the old and new values. + /// + /// # Errors + /// * `BadOrigin` - If the call is not made by the root origin. #[pallet::call_index(3)] #[pallet::weight((::WeightInfo::set_pending_pip_expiry(), Operational))] pub fn set_pending_pip_expiry( @@ -639,15 +641,14 @@ pub mod pallet { Ok(()) } - /// Change the maximum skip count (`max_pip_skip_count`). - /// This function can only be called by the root origin. + /// Sets the maximum number of times a PIP can be skipped. This function can only be called by the root origin. /// /// # Arguments /// * `origin` - The origin of the call, which must be the root. - /// * `max` - The new maximum number of skips allowed before a PIP cannot be skipped by the Governance Committee (GC) anymore. + /// * `max` - The new maximum skip count for PIPs. /// /// # Events - /// * `MaxPipSkipCountChanged` - Emitted when the maximum skip count is changed, containing the old and new values. + /// * `MaxPipSkipCountChanged` - Emitted when the maximum PIP skip count is changed, containing the old and new values. /// /// # Errors /// * `BadOrigin` - If the call is not made by the root origin. @@ -661,11 +662,17 @@ pub mod pallet { Ok(()) } - /// Change the maximum number of active PIPs before community members cannot propose anything. - /// Can only be called by root. + /// Sets the limit on the number of active PIPs. This function can only be called by the root origin. /// /// # Arguments - /// * `limit` of concurrent active PIPs. + /// * `origin` - The origin of the call, which must be the root. + /// * `limit` - The new limit on the number of active PIPs. + /// + /// # Events + /// * `ActivePipLimitChanged` - Emitted when the active PIP limit is changed, containing the old and new values. + /// + /// # Errors + /// * `BadOrigin` - If the call is not made by the root origin. #[pallet::call_index(5)] #[pallet::weight((::WeightInfo::set_active_pip_limit(), Operational))] pub fn set_active_pip_limit(origin: OriginFor, limit: u32) -> DispatchResult { @@ -676,15 +683,21 @@ pub mod pallet { Ok(()) } - /// A network member creates a PIP by submitting a dispatchable which - /// changes the network in someway. A minimum deposit is required to open a new proposal. + /// Proposes a new PIP by submitting a dispatchable which changes the network. /// /// # Arguments - /// * `proposer` is either a signing key or committee. - /// Used to understand whether this is a committee proposal and verified against `origin`. - /// * `proposal` a dispatchable call - /// * `deposit` minimum deposit value, which is ignored if `proposer` is a committee. - /// * `url` a link to a website for proposal discussion + /// * `origin` - The origin of the call. + /// * `proposal` - The dispatchable call. + /// * `deposit` - The deposit amount for the proposal. + /// * `url` - A link to a website for proposal discussion. + /// * `description` - A short description of the proposal. + /// + /// # Events + /// * `ProposalCreated`. + /// + /// # Errors + /// * `IncorrectDeposit` - If the deposit amount is less than the required minimum. + /// * `TooManyActivePips` - If the number of active PIPs exceeds the maximum. #[pallet::call_index(6)] #[pallet::weight(::WeightInfo::propose_from_community())] pub fn propose( @@ -806,8 +819,8 @@ pub mod pallet { Ok(()) } - /// Vote either in favor (`aye_or_nay` == true) or against a PIP with `id`. - /// The "convinction" or strength of the vote is given by `deposit`, which is reserved. + /// Casts a vote either in favor or against a PIP with `id`. + /// The "conviction" or strength of the vote is given by `deposit`, which is reserved. /// /// Note that `vote` is *not* additive. /// That is, `vote(id, true, 50)` followed by `vote(id, true, 40)` @@ -815,15 +828,20 @@ pub mod pallet { /// To add atop of existing votes, you'll need `existing_deposit + addition`. /// /// # Arguments - /// * `id`, proposal id - /// * `aye_or_nay`, a bool representing for or against vote - /// * `deposit`, the "conviction" with which the vote is made. + /// * `origin` - The origin of the call. + /// * `id` - The proposal ID to vote on. + /// * `aye_or_nay` - A boolean representing a vote in favor (`true`) or against (`false`). + /// * `deposit` - The "conviction" or strength of the vote, represented by the amount of deposit. + /// + /// # Events + /// * `Voted` - Emitted when a vote is successfully cast. /// /// # Errors - /// * `NoSuchProposal` if `id` doesn't reference a valid PIP. - /// * `NotFromCommunity` if proposal was made by a committee. - /// * `IncorrectProposalState` if PIP isn't pending. - /// * `InsufficientDeposit` if `origin` cannot reserve `deposit - old_deposit`. + /// * `NoSuchProposal` - If the `id` does not reference a valid PIP. + /// * `NotFromCommunity` - If the proposal was made by a committee. + /// * `IncorrectProposalState` - If the PIP is not in a pending state. + /// * `InsufficientDeposit` - If the `origin` cannot reserve the required deposit. + /// * `IncorrectDeposit` - If the deposit amount is less than the required minimum. #[pallet::call_index(7)] #[pallet::weight(::WeightInfo::vote())] pub fn vote( @@ -893,11 +911,20 @@ pub mod pallet { /// Approves the pending committee PIP given by the `id`. /// + /// This function can only be called by a Governance Committee (GC) voting majority. + /// + /// # Arguments + /// * `origin` - The origin of the call, which must be a GC voting majority. + /// * `id` - The proposal ID of the PIP to be approved. + /// /// # Errors - /// * `BadOrigin` unless a GC voting majority executes this function. - /// * `NoSuchProposal` if the PIP with `id` doesn't exist. - /// * `IncorrectProposalState` if the proposal isn't pending. - /// * `NotByCommittee` if the proposal isn't by a committee. + /// * `BadOrigin` - If the call is not made by a GC voting majority. + /// * `NoSuchProposal` - If the PIP with the given `id` does not exist. + /// * `IncorrectProposalState` - If the proposal is not in a pending state. + /// * `NotByCommittee` - If the proposal was not made by a committee. + /// + /// # Notes + /// This function schedules the PIP for execution if all checks pass. #[pallet::call_index(8)] #[pallet::weight((::WeightInfo::approve_committee_proposal(), Operational))] pub fn approve_committee_proposal(origin: OriginFor, id: PipId) -> DispatchResult { @@ -919,14 +946,24 @@ pub mod pallet { Ok(()) } - /// Rejects the PIP given by the `id`, refunding any bonded funds, - /// assuming it hasn't been cancelled or executed. - /// Note that proposals scheduled-for-execution can also be rejected. + /// Rejects the PIP given by the `id`. Bonded funds will be refunded, assuming it hasn't + /// been cancelled or executed. + /// + /// This function can only be called by a Governance Committee (GC) voting majority. + /// + /// # Arguments + /// * `origin` - The origin of the call, which must be a GC voting majority. + /// * `id` - The proposal ID of the PIP to be rejected. /// /// # Errors - /// * `BadOrigin` unless a GC voting majority executes this function. - /// * `NoSuchProposal` if the PIP with `id` doesn't exist. - /// * `IncorrectProposalState` if the proposal was cancelled or executed. + /// * `BadOrigin` - If the call is not made by a GC voting majority. + /// * `NoSuchProposal` - If the PIP with the given `id` does not exist. + /// * `IncorrectProposalState` - If the proposal was cancelled or executed. + /// + /// # Notes + /// This function will unschedule the PIP if it was scheduled for execution and will + /// unsnapshot the PIP if it was part of a snapshot. It will also handle the rejection + /// of the proposal and refund any bonded funds. #[pallet::call_index(9)] #[pallet::weight((::WeightInfo::reject_proposal(), Operational))] pub fn reject_proposal(origin: OriginFor, id: PipId) -> DispatchResult { @@ -942,15 +979,21 @@ pub mod pallet { Ok(()) } - /// Prune the PIP given by the `id`, refunding any funds not already refunded. - /// The PIP may not be active + /// Prunes the PIP given by the `id`. The PIP must not be active. /// - /// This function is intended for storage garbage collection purposes. + /// This function is intended for storage garbage collection purposes and can only be called by a Governance Committee (GC) voting majority. + /// + /// # Arguments + /// * `origin` - The origin of the call, which must be a GC voting majority. + /// * `id` - The proposal ID of the PIP to be pruned. /// /// # Errors - /// * `BadOrigin` unless a GC voting majority executes this function. - /// * `NoSuchProposal` if the PIP with `id` doesn't exist. - /// * `IncorrectProposalState` if the proposal is active. + /// * `BadOrigin` - If the call is not made by a GC voting majority. + /// * `NoSuchProposal` - If the PIP with the given `id` does not exist. + /// * `IncorrectProposalState` - If the proposal is active. + /// + /// # Notes + /// This function will remove the PIP from storage and refund any remaining bonded funds. #[pallet::call_index(10)] #[pallet::weight((::WeightInfo::prune_proposal(), Operational))] pub fn prune_proposal(origin: OriginFor, id: PipId) -> DispatchResult { @@ -965,14 +1008,18 @@ pub mod pallet { } /// Updates the execution schedule of the PIP given by `id`. + /// This function can only be called by the release coordinator. /// /// # Arguments - /// * `until` defines the future block where the enactment period will finished. - /// `None` value means that enactment period is going to finish in the next block. + /// * `origin` - The origin of the call, which must be the release coordinator. + /// * `id` - The proposal ID of the PIP to be rescheduled. + /// * `until` - An optional future block number where the enactment period will finish. + /// If `None`, the enactment period will finish in the next block. /// /// # Errors - /// * `RescheduleNotByReleaseCoordinator` unless triggered by release coordinator. - /// * `IncorrectProposalState` unless the proposal was in a scheduled state. + /// * `RescheduleNotByReleaseCoordinator` - If the call is not made by the release coordinator. + /// * `IncorrectProposalState` - If the proposal is not in a scheduled state. + /// * `InvalidFutureBlockNumber` - If the provided block number is not a valid future block number. #[pallet::call_index(11)] #[pallet::weight((::WeightInfo::reschedule_execution(), Operational))] pub fn reschedule_execution( @@ -1009,8 +1056,16 @@ pub mod pallet { /// Clears the snapshot and emits the event `SnapshotCleared`. /// + /// This function can only be called by a Governance Committee (GC) member. + /// + /// # Arguments + /// * `origin` - The origin of the call, which must be a GC member. + /// + /// # Events + /// * `SnapshotCleared` - Emitted when the snapshot is successfully cleared, containing the ID of the cleared snapshot. + /// /// # Errors - /// * `NotACommitteeMember` - triggered when a non-GC-member executes the function. + /// * `NotACommitteeMember` - If the call is not made by a GC member. #[pallet::call_index(12)] #[pallet::weight((::WeightInfo::clear_snapshot(), Operational))] pub fn clear_snapshot(origin: OriginFor) -> DispatchResult { @@ -1033,11 +1088,19 @@ pub mod pallet { Ok(()) } - /// Takes a new snapshot of the current list of active && pending PIPs. + /// Takes a new snapshot of the current list of active and pending PIPs. /// The PIPs are then sorted into a priority queue based on each PIP's weight. /// + /// This function can only be called by a Governance Committee (GC) member. + /// + /// # Arguments + /// * `origin` - The origin of the call, which must be a GC member. + /// + /// # Events + /// * `SnapshotTaken` - Emitted when a snapshot is successfully taken, containing the ID of the snapshot and the queue of PIPs. + /// /// # Errors - /// * `NotACommitteeMember` - triggered when a non-GC-member executes the function. + /// * `NotACommitteeMember` - If the call is not made by a GC member. #[pallet::call_index(13)] #[pallet::weight((::WeightInfo::snapshot(), Operational))] pub fn snapshot(origin: OriginFor) -> DispatchResult { @@ -1069,23 +1132,32 @@ pub mod pallet { Ok(()) } - /// Enacts `results` for the PIPs in the snapshot queue. + /// Enacts the results for the PIPs in the snapshot queue. + /// /// The snapshot will be available for further enactments until it is cleared. /// - /// The `results` are encoded a list of `(id, result)` where `result` is applied to `id`. + /// The `results` parameter is a list of `(id, result)` tuples where `result` is applied to the PIP with the given `id`. /// Note that the snapshot priority queue is encoded with the *lowest priority first*. - /// so `results = [(id, Approve)]` will approve `SnapshotQueue[SnapshotQueue.len() - 1]`. + /// For example, `results = [(id, Approve)]` will approve `SnapshotQueue[SnapshotQueue.len() - 1]`. + /// + /// # Arguments + /// * `origin` - The origin of the call, which must be a GC voting majority. + /// * `results` - A vector of tuples where each tuple contains a PIP ID and a `SnapshotResult` (either `Approve`, `Reject`, or `Skip`). + /// + /// # Events + /// * `SnapshotResultsEnacted` - Emitted when the snapshot results are successfully enacted, containing the ID of the snapshot and the actions taken. /// /// # Errors - /// * `BadOrigin` - unless a GC voting majority executes this function. - /// * `CannotSkipPip` - a given PIP has already been skipped too many times. - /// * `SnapshotResultTooLarge` - on len(results) > len(snapshot_queue). - /// * `SnapshotIdMismatch` - if: - /// ```text - /// ∃ (i ∈ 0..SnapshotQueue.len()). - /// results[i].0 ≠ SnapshotQueue[SnapshotQueue.len() - i].id - /// ``` - /// This is protects against clearing queue while GC is voting. + /// * `BadOrigin` - If the call is not made by a GC voting majority. + /// * `CannotSkipPip` - If a given PIP has already been skipped too many times. + /// * `SnapshotResultTooLarge` - If the length of `results` is greater than the length of the snapshot queue. + /// * `SnapshotIdMismatch` - If there is a mismatch between the PIP IDs in `results` and the snapshot queue. + /// + /// # Notes + /// This function will: + /// - Update the skip counts for PIPs that are skipped. + /// - Reject PIPs that are marked for rejection and refund any bonded funds. + /// - Approve PIPs that are marked for approval and schedule them for execution. #[pallet::call_index(14)] #[pallet::weight((enact_snapshot_results_weight::(&results), Operational))] pub fn enact_snapshot_results( @@ -1171,7 +1243,19 @@ pub mod pallet { Ok(()) } - /// Internal dispatchable that handles execution of a PIP. + /// Executes a scheduled PIP (Polymesh Improvement Proposal). + /// + /// # Arguments + /// * `origin` - The origin of the call, which must be the root. + /// * `id` - The unique identifier of the PIP to be executed. + /// + /// # Errors + /// * `BadOrigin` - If the call is not made by the root origin. + /// + /// # Notes + /// This function will: + /// - Remove the PIP from the scheduling queue. + /// - Execute the proposal associated with the PIP. #[pallet::call_index(15)] #[pallet::weight((::WeightInfo::execute_scheduled_pip(), Operational))] pub fn execute_scheduled_pip( @@ -1183,7 +1267,21 @@ pub mod pallet { Self::execute_proposal(id) } - /// Internal dispatchable that handles expiration of a PIP. + /// Expires a scheduled PIP (Polymesh Improvement Proposal). + /// + /// # Arguments + /// * `origin` - The origin of the call, which must be the root. + /// * `did` - The identity ID of the entity initiating the expiration. + /// * `id` - The unique identifier of the PIP to be expired. + /// + /// # Errors + /// * `BadOrigin` - If the call is not made by the root origin. + /// + /// # Notes + /// This function will: + /// - Check if the PIP is in a pending state. + /// - Unsnapshot the PIP if it was part of a snapshot. + /// - Prune the PIP data if it is in an expired state. #[pallet::call_index(16)] #[pallet::weight((::WeightInfo::expire_scheduled_pip(), Operational))] pub fn expire_scheduled_pip( From be7c87792cf638f75feb0bfc37f582c981e46e1a Mon Sep 17 00:00:00 2001 From: Henrique Nogara Date: Thu, 6 Feb 2025 11:59:23 -0300 Subject: [PATCH 3/3] Add bound to pips (#1784) * Solve merge conflicts - import path * Improve insert_live_queue function * Add unit test; Improve weight calculation (return max between refunds and votes) * Prune and refund only on_idle * Remove const and error --- pallets/pips/src/benchmarking.rs | 256 +++++----- pallets/pips/src/lib.rs | 262 ++++++---- pallets/runtime/develop/src/runtime.rs | 4 + pallets/runtime/mainnet/src/runtime.rs | 4 + pallets/runtime/testnet/src/runtime.rs | 4 + pallets/runtime/tests/src/committee_test.rs | 7 +- pallets/runtime/tests/src/pips_test.rs | 502 ++++++++++++-------- pallets/runtime/tests/src/staking/mock.rs | 3 + pallets/runtime/tests/src/staking/mod.rs | 5 +- pallets/runtime/tests/src/storage.rs | 4 + pallets/runtime/tests/src/utility_test.rs | 14 +- pallets/weights/src/pallet_pips.rs | 352 +++++++------- 12 files changed, 802 insertions(+), 615 deletions(-) diff --git a/pallets/pips/src/benchmarking.rs b/pallets/pips/src/benchmarking.rs index 651f6b8e5..d021951c6 100644 --- a/pallets/pips/src/benchmarking.rs +++ b/pallets/pips/src/benchmarking.rs @@ -16,20 +16,17 @@ use crate::*; use frame_benchmarking::benchmarks; -use frame_support::{ - dispatch::{DispatchError, DispatchResult}, - traits::UnfilteredDispatchable, -}; +use frame_support::{dispatch::DispatchResult, traits::UnfilteredDispatchable}; use frame_system::RawOrigin; -use pallet_identity::benchmarking::{user, User}; -use polymesh_primitives::{MaybeBlock, SystematicIssuers, GC_DID}; use rand::{seq::SliceRandom, SeedableRng}; use rand_chacha::ChaCha20Rng; -use sp_std::{ - convert::{TryFrom, TryInto}, - iter, - prelude::*, -}; +use scale_info::prelude::format; +use sp_std::convert::{TryFrom, TryInto}; +use sp_std::iter; +use sp_std::prelude::*; + +use pallet_identity::benchmarking::{user, User, UserBuilder}; +use polymesh_primitives::{MaybeBlock, SystematicIssuers, GC_DID}; #[cfg(feature = "running-ci")] mod limits { @@ -38,7 +35,7 @@ mod limits { pub const PROPOSAL_PADDING_LEN: usize = 100; pub const VOTERS_A_NUM: usize = 10; pub const VOTERS_B_NUM: usize = 10; - pub const PROPOSALS_NUM: usize = 5; + pub const PROPOSALS_NUM: u32 = 5; } #[cfg(not(feature = "running-ci"))] @@ -48,7 +45,7 @@ mod limits { pub const PROPOSAL_PADDING_LEN: usize = 10_000; pub const VOTERS_A_NUM: usize = 200; pub const VOTERS_B_NUM: usize = 200; - pub const PROPOSALS_NUM: usize = 100; + pub const PROPOSALS_NUM: u32 = 100; } use limits::*; @@ -98,43 +95,30 @@ fn cast_votes( Ok(()) } -/// Sets up PIPs and votes. -fn pips_and_votes_setup( - approve_only: bool, -) -> Result<(RawOrigin, IdentityId), DispatchError> { +/// Creates `number_of_proposals` proposals and casts `number_of_voter` votes for each. +fn vote_setup(number_of_proposals: u32, number_of_voter: u32) { Pallet::::set_active_pip_limit(RawOrigin::Root.into(), PROPOSALS_NUM as u32).unwrap(); - zeroize_deposit::(); - let (voters_a_num, voters_b_num) = if approve_only { - (VOTERS_A_NUM + VOTERS_B_NUM, 0) - } else { - (VOTERS_A_NUM, VOTERS_B_NUM) - }; - let hi_voters = make_voters::(voters_a_num, "hi"); - let bye_voters = make_voters::(voters_b_num, "bye"); - let User { origin, did, .. } = user::("initial", 0); - let did = did.ok_or("no did in pips_and_votes_setup").unwrap(); - for i in 0..PROPOSALS_NUM { + Pallet::::set_min_proposal_deposit(RawOrigin::Root.into(), 1_000).unwrap(); + for i in 0..number_of_proposals { let (proposal, url, description) = make_proposal::(); - // Pick a proposer, diversifying like a poor man. - let (proposer_origin, _) = if hi_voters.len() >= i + 1 { - (hi_voters[i].1.clone(), hi_voters[i].2.clone()) - } else { - (origin.clone(), did) - }; + let proposer = UserBuilder::::default() + .generate_did() + .build(&format!("Proposer{}", i)); Pallet::::propose( - proposer_origin.into(), + proposer.origin().into(), proposal, - 42u32.into(), - Some(url.clone()), - Some(description.clone()), + 1_000, + Some(url), + Some(description), ) .unwrap(); - let id = PipId(i as u32); - // Alternate aye and nay voters with every iteration unless only approve votes are cast. - cast_votes::(id, hi_voters.as_slice(), approve_only || i % 2 == 0).unwrap(); - cast_votes::(id, bye_voters.as_slice(), i % 2 != 0).unwrap(); + for j in 0..number_of_voter { + let voter = UserBuilder::::default() + .generate_did() + .build(&format!("User{}", j)); + Pallet::::vote(voter.origin().into(), PipId(i as u32), true, 1_000).unwrap(); + } } - Ok((origin, did)) } fn enact_call(num_approves: usize, num_rejects: usize, num_skips: usize) -> Call { @@ -146,7 +130,7 @@ fn enact_call(num_approves: usize, num_rejects: usize, num_skips: usi .chain(iter::repeat(SnapshotResult::Skip).take(num_skips)) .collect(); snapshot_results.shuffle(&mut rng); - let results = Pallet::::snapshot_queue() + let results = SnapshotQueue::::get() .iter() .rev() .map(|s| s.id) @@ -156,7 +140,7 @@ fn enact_call(num_approves: usize, num_rejects: usize, num_skips: usi } fn propose_verify(url: Url, description: PipDescription) -> DispatchResult { - let meta = Pallet::::proposal_metadata(PipId(0)).unwrap(); + let meta = ProposalMetadata::::get(PipId(0)).unwrap(); assert_eq!(PipId(0), meta.id, "incorrect meta.id"); assert_eq!(Some(url), meta.url, "incorrect meta.url"); assert_eq!( @@ -167,18 +151,6 @@ fn propose_verify(url: Url, description: PipDescription) -> DispatchR Ok(()) } -fn execute_verify(state: ProposalState, err: &'static str) -> DispatchResult { - if Proposals::::contains_key(PipId(0)) { - assert_eq!( - state, - Pallet::::proposal_state(PipId(0)).unwrap(), - "{}", - err - ); - } - Ok(()) -} - benchmarks! { set_prune_historical_pips { let origin = RawOrigin::Root; @@ -306,52 +278,32 @@ benchmarks! { } reject_proposal { + vote_setup::(1, T::MaxRefundsAndVotesPruned::get()); + Pallet::::set_prune_historical_pips(RawOrigin::Root.into(), true).unwrap(); - let user = user::("proposer", 0); - zeroize_deposit::(); - let (proposal, url, description) = make_proposal::(); - let deposit = 42u32.into(); - Pallet::::propose( - user.origin().into(), - proposal, - deposit, - Some(url), - Some(description) - ).unwrap(); - let id = PipId(0); - assert_eq!(deposit, Deposits::::get(id, &user.account()).expect("Deposit").amount, "incorrect deposit in reject_proposal"); - let vmo_origin = T::VotingMajorityOrigin::try_successful_origin().unwrap(); - let call = Call::::reject_proposal { id }; + + let origin = T::VotingMajorityOrigin::try_successful_origin().unwrap(); + let call = Call::::reject_proposal { id: PipId(0) }; }: { - call.dispatch_bypass_filter(vmo_origin).unwrap(); + call.dispatch_bypass_filter(origin).unwrap(); } verify { - assert!(!Deposits::::contains_key(id, &user.account()), "deposit of the rejected proposal is present"); + assert!(!Proposals::::contains_key(PipId(0))); } prune_proposal { - Pallet::::set_prune_historical_pips(RawOrigin::Root.into(), false).unwrap(); - let user = user::("proposer", 0); - zeroize_deposit::(); - let (proposal, url, description) = make_proposal::(); - Pallet::::propose( - user.origin().into(), - proposal, - 42u32.into(), - Some(url), - Some(description) - ).unwrap(); - let id = PipId(0); - let vmo_origin = T::VotingMajorityOrigin::try_successful_origin().unwrap(); - let reject_call = Call::::reject_proposal { id }; - reject_call.dispatch_bypass_filter(vmo_origin.clone()).unwrap(); - let call = Call::::prune_proposal { id }; + vote_setup::(1, T::MaxRefundsAndVotesPruned::get()); + + let origin = T::VotingMajorityOrigin::try_successful_origin().unwrap(); + let reject_call = Call::::reject_proposal { id: PipId(0) }; + reject_call.dispatch_bypass_filter(origin.clone()).unwrap(); + let call = Call::::prune_proposal { id: PipId(0) }; }: { - call.dispatch_bypass_filter(vmo_origin).unwrap(); + call.dispatch_bypass_filter(origin).unwrap(); } verify { - assert!(!Proposals::::contains_key(id), "pruned proposal is present"); - assert!(!ProposalMetadata::::contains_key(id), "pruned proposal metadata is present"); + assert!(!Proposals::::contains_key(PipId(0))); + assert!(!ProposalMetadata::::contains_key(PipId(0))); } reschedule_execution { @@ -399,85 +351,97 @@ benchmarks! { } snapshot { - let (origin0, did0) = pips_and_votes_setup::(true).unwrap(); - T::GovernanceCommittee::bench_set_release_coordinator(did0); - }: _(origin0) + vote_setup::(1, T::MaxRefundsAndVotesPruned::get()); + let user = user::("release_coordinator", 0); + T::GovernanceCommittee::bench_set_release_coordinator(user.did()); + }: _(user.origin()) verify { - assert!(SnapshotMeta::::get().is_some(), "snapshot finished incorrectly"); + assert!(SnapshotMeta::::get().is_some()); } enact_snapshot_results { // The number of Approve results. - let a in 0..PROPOSALS_NUM as u32 / 3; + let a in 0..PROPOSALS_NUM / 3; // The number of Reject results. - let r in 0..PROPOSALS_NUM as u32 / 3; + let r in 0..PROPOSALS_NUM / 3; // The number of Skip results. - let s in 0..PROPOSALS_NUM as u32 / 3; + let s in 0..PROPOSALS_NUM / 3; Pallet::::set_max_pip_skip_count(RawOrigin::Root.into(), MAX_SKIPPED_COUNT).unwrap(); - let (origin0, did0) = pips_and_votes_setup::(true).unwrap(); + vote_setup::(PROPOSALS_NUM, T::MaxRefundsAndVotesPruned::get()); - // snapshot - T::GovernanceCommittee::bench_set_release_coordinator(did0); - Pallet::::snapshot(origin0.into()).unwrap(); + // Adds a user to the governance committee and captures a snapshot + let user = user::("release_coordinator", 0); + T::GovernanceCommittee::bench_set_release_coordinator(user.did()); + Pallet::::snapshot(user.origin().into()).unwrap(); - // enact - let enact_origin = T::VotingMajorityOrigin::try_successful_origin().unwrap(); - let enact_call = enact_call::(a as usize, r as usize, s as usize); + // Set up the results parameter based on the number of Approve, Reject, and Skip results. + let origin = T::VotingMajorityOrigin::try_successful_origin().unwrap(); + let call = enact_call::(a as usize, r as usize, s as usize); }: { - enact_call.dispatch_bypass_filter(enact_origin).unwrap(); + call.dispatch_bypass_filter(origin).unwrap(); } verify { - assert_eq!( - Pallet::::snapshot_queue().len(), PROPOSALS_NUM - (a + r + s) as usize, - "incorrect snapshot queue after enact_snapshot_results" - ); + assert_eq!(SnapshotQueue::::get().len(), (PROPOSALS_NUM - (a + r + s)) as usize); } execute_scheduled_pip { - // set up Pallet::::set_prune_historical_pips(RawOrigin::Root.into(), true).unwrap(); - let (origin0, did0) = pips_and_votes_setup::(true).unwrap(); - - // snapshot - T::GovernanceCommittee::bench_set_release_coordinator(did0); - Pallet::::snapshot(origin0.into()).unwrap(); - assert!( - Pallet::::snapshot_queue().len() == PROPOSALS_NUM as usize, - "wrong snapshot queue length" - ); - - // enact - let enact_origin = T::VotingMajorityOrigin::try_successful_origin().unwrap(); - let enact_call = enact_call::(PROPOSALS_NUM, 0, 0); - enact_call.dispatch_bypass_filter(enact_origin).unwrap(); - - // execute - let origin = RawOrigin::Root; - }: _(origin, PipId(0)) + vote_setup::(PROPOSALS_NUM, T::MaxRefundsAndVotesPruned::get()); + + // Adds a user to the governance committee and captures a snapshot + let user = user::("release_coordinator", 0); + T::GovernanceCommittee::bench_set_release_coordinator(user.did()); + Pallet::::snapshot(user.origin().into()).unwrap(); + assert!(SnapshotQueue::::get().len() == PROPOSALS_NUM as usize); + + // Set up the results parameter where all proposals are approved + let origin = T::VotingMajorityOrigin::try_successful_origin().unwrap(); + let call = enact_call::(PROPOSALS_NUM as usize, 0, 0); + call.dispatch_bypass_filter(origin).unwrap(); + }: _(RawOrigin::Root, PipId(0)) verify { - execute_verify::(ProposalState::Failed, "incorrect proposal state after execution").unwrap(); + assert!(!Proposals::::contains_key(PipId(0))); } expire_scheduled_pip { - // set up Pallet::::set_prune_historical_pips(RawOrigin::Root.into(), true).unwrap(); - let (origin0, did0) = pips_and_votes_setup::(true).unwrap(); + vote_setup::(PROPOSALS_NUM, T::MaxRefundsAndVotesPruned::get()); - // snapshot - T::GovernanceCommittee::bench_set_release_coordinator(did0); - Pallet::::snapshot(origin0.into()).unwrap(); + // Adds a user to the governance committee and captures a snapshot + let user = user::("release_coordinator", 0); + T::GovernanceCommittee::bench_set_release_coordinator(user.did()); + Pallet::::snapshot(user.origin().into()).unwrap(); - let id = PipId(0); + assert_eq!(ProposalState::Pending, ProposalStates::::get(PipId(0)).unwrap()); + }: _(RawOrigin::Root, GC_DID, PipId(0)) + verify { + assert!(!Proposals::::contains_key(PipId(0))); + } - assert_eq!( - ProposalState::Pending, Pallet::::proposal_state(id).unwrap(), - "incorrect proposal state before expiration" - ); + remove_pending_storage { + let r in 0..T::MaxRefundsAndVotesPruned::get() as u32; + let v in 0..T::MaxRefundsAndVotesPruned::get() as u32; - let origin = RawOrigin::Root; - }: _(origin, GC_DID, id) - verify { - execute_verify::(ProposalState::Expired, "incorrect proposal state after expiration").unwrap(); + for i in 0..r { + let user = UserBuilder::::default().generate_did().build(&format!("Voter{}", i)); + Pallet::::increase_lock(&user.account(), 1_000).unwrap(); + Deposits::::insert(PipId(0), user.account(), DepositInfo { owner: user.account(), amount: 1_000 }); + } + + for i in 0..v { + let user = UserBuilder::::default().generate_did().build(&format!("Voter{}", i)); + ProposalVotes::::insert(PipId(0), user.account(), Vote(true, 1_000)); + } + + if r > 0 { + PendingRefunds::::insert(PipId(0), true); + } + + if v > 0 { + VotesToBePruned::::insert(PipId(0), true); + } + }: { + Pallet::::remove_pending_storage(); } } diff --git a/pallets/pips/src/lib.rs b/pallets/pips/src/lib.rs index 988cf34a6..5d8dff480 100644 --- a/pallets/pips/src/lib.rs +++ b/pallets/pips/src/lib.rs @@ -80,7 +80,7 @@ use frame_support::pallet_prelude::*; use frame_support::storage::types::StorageValue; use frame_support::traits::schedule::{DispatchTime, Named}; use frame_support::traits::{Currency, EnsureOrigin, Get, WithdrawReasons}; -use frame_system::pallet_prelude::OriginFor; +use frame_system::pallet_prelude::{BlockNumberFor, OriginFor}; use frame_system::{ensure_root, ensure_signed, RawOrigin}; use sp_runtime::traits::{BlakeTwo256, Dispatchable, Hash, One, Saturating, Zero}; use sp_runtime::DispatchError; @@ -131,6 +131,7 @@ pub trait WeightInfo { fn enact_snapshot_results(a: u32, r: u32, s: u32) -> Weight; fn execute_scheduled_pip() -> Weight; fn expire_scheduled_pip() -> Weight; + fn remove_pending_storage(r: u32, v: u32) -> Weight; } #[frame_support::pallet] @@ -181,6 +182,8 @@ pub mod pallet { ScheduledProposalDoesntExist, /// A proposal that is not in a scheduled state cannot be executed. ProposalNotInScheduledState, + /// Invalid PIP ID. Pip id was not expected to be in the live queue. + InvalidPipId, } #[pallet::event] @@ -377,64 +380,56 @@ pub mod pallet { type Scheduler: Named; /// A call type used by the scheduler. type SchedulerCall: From> + Into<::Proposal>; + /// The maximum number of votes that can be pruned at once. + #[pallet::constant] + type MaxRefundsAndVotesPruned: Get; } /// Set to `true` if historical PIPs data must be removed. #[pallet::storage] - #[pallet::getter(fn prune_historical_pips)] pub type PruneHistoricalPips = StorageValue<_, bool, ValueQuery>; /// The minimum amount to be used as a deposit for community PIP creation. #[pallet::storage] - #[pallet::getter(fn min_proposal_deposit)] pub type MinimumProposalDeposit = StorageValue<_, Balance, ValueQuery>; /// Default enactment period that will be use after a proposal is accepted by GC. #[pallet::storage] - #[pallet::getter(fn default_enactment_period)] pub type DefaultEnactmentPeriod = StorageValue<_, T::BlockNumber, ValueQuery>; /// Number of blocks it will take, after a `Pending` PIP expires, assuming it has not transitioned to another `ProposalState`. #[pallet::storage] #[pallet::unbounded] - #[pallet::getter(fn pending_pip_expiry)] pub type PendingPipExpiry = StorageValue<_, MaybeBlock, ValueQuery>; /// Maximum times a PIP can be skipped before triggering `CannotSkipPip` in `enact_snapshot_results`. #[pallet::storage] - #[pallet::getter(fn max_pip_skip_count)] pub type MaxPipSkipCount = StorageValue<_, SkippedCount, ValueQuery>; /// The maximum allowed number for active PIPs. Once reached, new PIPs cannot be proposed by community members. #[pallet::storage] - #[pallet::getter(fn active_pip_limit)] pub type ActivePipLimit = StorageValue<_, u32, ValueQuery>; /// Proposal's identifier. #[pallet::storage] - #[pallet::getter(fn pip_id_sequence)] pub type PipIdSequence = StorageValue<_, PipId, ValueQuery>; /// Snaphot's identifier. #[pallet::storage] - #[pallet::getter(fn snapshot_id_sequence)] pub type SnapshotIdSequence = StorageValue<_, SnapshotId, ValueQuery>; /// Total count of pending or scheduled PIPs. #[pallet::storage] - #[pallet::getter(fn active_pip_count)] pub type ActivePipCount = StorageValue<_, u32, ValueQuery>; /// The [`PipsMetadata`] for each proposal ([`PipId`]). #[pallet::storage] #[pallet::unbounded] - #[pallet::getter(fn proposal_metadata)] pub type ProposalMetadata = StorageMap<_, Twox64Concat, PipId, PipsMetadata, OptionQuery>; /// All locked [`DepositInfo`] per [`PipId`] for each account. #[pallet::storage] - #[pallet::getter(fn deposits)] pub type Deposits = StorageDoubleMap< _, Twox64Concat, @@ -448,25 +443,21 @@ pub mod pallet { /// The [`Pip`] for each proposal ([`PipId`]). #[pallet::storage] #[pallet::unbounded] - #[pallet::getter(fn proposals)] pub type Proposals = StorageMap<_, Twox64Concat, PipId, Pip, OptionQuery>; /// The [`VotingResult`] for each proposal ([`PipId`]). #[pallet::storage] - #[pallet::getter(fn proposal_result)] pub type ProposalResult = StorageMap<_, Twox64Concat, PipId, VotingResult, ValueQuery>; /// The Votes ([`Vote`]) for each proposal ([`PipId`]) per account. #[pallet::storage] - #[pallet::getter(fn proposal_vote)] pub type ProposalVotes = StorageDoubleMap<_, Twox64Concat, PipId, Twox64Concat, T::AccountId, Vote, OptionQuery>; /// Maps PIPs to the block at which they will be executed. #[pallet::storage] - #[pallet::getter(fn pip_to_schedule)] pub type PipToSchedule = StorageMap<_, Twox64Concat, PipId, T::BlockNumber, OptionQuery>; @@ -478,7 +469,6 @@ pub mod pallet { /// The snapshot is therefore essentially a point-in-time clone of this queue. #[pallet::storage] #[pallet::unbounded] - #[pallet::getter(fn live_queue)] pub type LiveQueue = StorageValue<_, Vec, ValueQuery>; /// The priority queue (lowest priority at index 0) of PIPs at the point of snapshotting. @@ -488,37 +478,39 @@ pub mod pallet { /// Once a (configurable) threshhold is exceeded, a PIP cannot be skipped again. #[pallet::storage] #[pallet::unbounded] - #[pallet::getter(fn snapshot_queue)] pub type SnapshotQueue = StorageValue<_, Vec, ValueQuery>; /// The [`SnapshotMetadata`]. #[pallet::storage] - #[pallet::getter(fn snapshot_metadata)] pub type SnapshotMeta = StorageValue<_, SnapshotMetadata, OptionQuery>; /// The number of times a certain PIP has been skipped. /// Once a (configurable) threshhold is exceeded, a PIP cannot be skipped again. #[pallet::storage] - #[pallet::getter(fn pip_skip_count)] pub type PipSkipCount = StorageMap<_, Twox64Concat, PipId, SkippedCount, ValueQuery>; /// All existing PIPs where the proposer is a committee. /// This list is a cache of all ids in `Proposals` with `Proposer::Committee(_)`. #[pallet::storage] #[pallet::unbounded] - #[pallet::getter(fn committee_pips)] pub type CommitteePips = StorageValue<_, Vec, ValueQuery>; /// The ([`ProposalState`]) of a given PIP ([`PipId`]). #[pallet::storage] - #[pallet::getter(fn proposal_state)] pub type ProposalStates = StorageMap<_, Twox64Concat, PipId, ProposalState, OptionQuery>; + /// All PIPs that still require refunds. + #[pallet::storage] + pub type PendingRefunds = StorageMap<_, Twox64Concat, PipId, bool, OptionQuery>; + + /// All PIPs that still require votes to be pruned. + #[pallet::storage] + pub type VotesToBePruned = StorageMap<_, Twox64Concat, PipId, bool, OptionQuery>; + /// Storage version. #[pallet::storage] - #[pallet::getter(fn storage_version)] pub(super) type StorageVersion = StorageValue<_, Version, ValueQuery>; #[derive(frame_support::DefaultNoBound)] @@ -545,6 +537,13 @@ pub mod pallet { } } + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_idle(_now: BlockNumberFor, _remaining_weight: Weight) -> Weight { + Self::remove_pending_storage() + } + } + #[pallet::call] impl Pallet { /// Sets the pruning setting for historical PIPs. This function can only be called by the root origin. @@ -733,7 +732,7 @@ pub mod pallet { // Pre conditions: caller must have min balance. ensure!( - deposit >= Self::min_proposal_deposit(), + deposit >= MinimumProposalDeposit::::get(), Error::::IncorrectDeposit ); @@ -752,7 +751,7 @@ pub mod pallet { // Construct and add PIP to storage. let created_at = System::::block_number(); - let expiry = Self::pending_pip_expiry() + created_at; + let expiry = PendingPipExpiry::::get() + created_at; let transaction_version = >::get().transaction_version; let proposal_data = Self::reportable_proposal_data(&*proposal); @@ -800,7 +799,7 @@ pub mod pallet { Self::unsafe_vote(id, proposer.clone(), Vote(true, deposit))?; // Adjust live queue. - Self::insert_live_queue(id); + Self::insert_live_queue(id)?; } else { CommitteePips::::append(id); } @@ -856,7 +855,7 @@ pub mod pallet { .. } = pallet_identity::Pallet::::ensure_origin_call_permissions(origin)?; - let pip = Self::proposals(id).ok_or(Error::::NoSuchProposal)?; + let pip = Proposals::::get(id).ok_or(Error::::NoSuchProposal)?; // Proposal must be from the community. let proposer = match pip.proposer { @@ -868,7 +867,7 @@ pub mod pallet { // a) Deposit must be above minimum. // Note that proposer can still vote against their own PIP. ensure!( - deposit >= Self::min_proposal_deposit(), + deposit >= MinimumProposalDeposit::::get(), Error::::IncorrectDeposit ); } @@ -880,7 +879,7 @@ pub mod pallet { with_transaction(|| { // Reserve the deposit, or refund if needed. - let curr_deposit = Self::deposits(id, &voter) + let curr_deposit = Deposits::::get(id, &voter) .map(|d| d.amount) .unwrap_or_default(); if deposit < curr_deposit { @@ -935,7 +934,7 @@ pub mod pallet { Self::is_proposal_state(id, ProposalState::Pending)?; // Ensure proposal is by committee. - let pip = Self::proposals(id).ok_or(Error::::NoSuchProposal)?; + let pip = Proposals::::get(id).ok_or(Error::::NoSuchProposal)?; ensure!( matches!(pip.proposer, Proposer::Committee(_)), Error::::NotByCommittee @@ -968,14 +967,14 @@ pub mod pallet { #[pallet::weight((::WeightInfo::reject_proposal(), Operational))] pub fn reject_proposal(origin: OriginFor, id: PipId) -> DispatchResult { T::VotingMajorityOrigin::ensure_origin(origin)?; - let proposal_state = Self::proposal_state(id).ok_or(Error::::NoSuchProposal)?; + let proposal_state = ProposalStates::::get(id).ok_or(Error::::NoSuchProposal)?; ensure!( Self::is_active(proposal_state), Error::::IncorrectProposalState ); Self::maybe_unschedule_pip(id, proposal_state); Self::maybe_unsnapshot_pip(id, proposal_state); - Self::unsafe_reject_proposal(GC_DID, id)?; + Self::unsafe_reject_proposal(GC_DID, id); Ok(()) } @@ -998,12 +997,12 @@ pub mod pallet { #[pallet::weight((::WeightInfo::prune_proposal(), Operational))] pub fn prune_proposal(origin: OriginFor, id: PipId) -> DispatchResult { T::VotingMajorityOrigin::ensure_origin(origin)?; - let proposal_state = Self::proposal_state(id).ok_or(Error::::NoSuchProposal)?; + let proposal_state = ProposalStates::::get(id).ok_or(Error::::NoSuchProposal)?; ensure!( !Self::is_active(proposal_state), Error::::IncorrectProposalState ); - Self::prune_data(GC_DID, id, proposal_state, true)?; + Self::prune_data(GC_DID, id, proposal_state, true); Ok(()) } @@ -1159,14 +1158,14 @@ pub mod pallet { /// - Reject PIPs that are marked for rejection and refund any bonded funds. /// - Approve PIPs that are marked for approval and schedule them for execution. #[pallet::call_index(14)] - #[pallet::weight((enact_snapshot_results_weight::(&results), Operational))] + #[pallet::weight((enact_snapshot_results_weight::(results), Operational))] pub fn enact_snapshot_results( origin: OriginFor, results: Vec<(PipId, SnapshotResult)>, ) -> DispatchResult { T::VotingMajorityOrigin::ensure_origin(origin)?; - let max_pip_skip_count = Self::max_pip_skip_count(); + let max_pip_skip_count = MaxPipSkipCount::::get(); SnapshotQueue::::try_mutate(|queue| { let mut to_bump_skipped = Vec::new(); @@ -1219,7 +1218,7 @@ pub mod pallet { // Reject proposals as instructed & refund. for pip_id in to_reject.iter().copied() { - Self::unsafe_reject_proposal(GC_DID, pip_id)?; + Self::unsafe_reject_proposal(GC_DID, pip_id); } // Approve proposals as instructed. @@ -1227,7 +1226,7 @@ pub mod pallet { Self::schedule_pip_for_execution(pip_id); } - let id = Self::snapshot_metadata().map(|m| m.id); + let id = SnapshotMeta::::get().map(|m| m.id); let event = Event::SnapshotResultsEnacted( GC_DID, id, @@ -1290,10 +1289,12 @@ pub mod pallet { id: PipId, ) -> DispatchResult { ensure_root(origin)?; + if Self::is_proposal_state(id, ProposalState::Pending).is_ok() { Self::maybe_unsnapshot_pip(id, ProposalState::Pending); - Self::maybe_prune(did, id, ProposalState::Expired)?; + Self::maybe_prune(did, id, ProposalState::Expired); } + Ok(()) } } @@ -1379,7 +1380,7 @@ impl Pallet { /// Changes the vote of `voter` to `vote`, if any. fn unsafe_vote(id: PipId, voter: T::AccountId, vote: Vote) -> DispatchResult { - let mut stats = Self::proposal_result(id); + let mut stats = ProposalResult::::get(id); // Update the vote and get the old one, if any, in which case also remove it from stats. if let Some(Vote(direction, deposit)) = ProposalVotes::::get(id, voter.clone()) { @@ -1411,20 +1412,25 @@ impl Pallet { Ok(()) } - /// Insert a new PIP into the live queue. - /// - /// The `id` should not exist in the queue previously. - /// Panics if it did. - fn insert_live_queue(id: PipId) { - let new = Self::aggregate_result(id); - LiveQueue::::mutate(|queue| { - // Inserting a new PIP entails that `id` is nowhere to be found. - // It follows that binary search will return `Err(_)`. - let pos = queue - .binary_search_by(|res| compare_spip(res, &new)) - .unwrap_err(); - queue.insert(pos, new); - }); + /// Insert a new PIP into the live queue. Returns [`Error::::InvalidPIPID`] if `pip_id` is not unique. + fn insert_live_queue(pip_id: PipId) -> DispatchResult { + let new_snapshotted_pip = Self::aggregate_result(pip_id); + + LiveQueue::::try_mutate(|live_queue| -> DispatchResult { + match live_queue.binary_search_by(|res| compare_spip(res, &new_snapshotted_pip)) { + Ok(_) => { + // This is a bug, as the PIP ID should be unique + Err(Error::::InvalidPipId.into()) + } + Err(pos) => { + // PIP ID is new. Insert it into the queue + live_queue.insert(pos, new_snapshotted_pip); + Ok(()) + } + } + })?; + + Ok(()) } /// Construct a `SnapshottedPip` from a `PipId`. @@ -1443,9 +1449,9 @@ impl Pallet { SnapshottedPip { id, weight } } - /// Returns `Ok(_)` iff `id` has `state`. - fn is_proposal_state(id: PipId, state: ProposalState) -> DispatchResult { - let proposal_state = Self::proposal_state(id).ok_or(Error::::NoSuchProposal)?; + /// Returns `Ok` if the proposal's state identified `pip_id` equals `state`. + fn is_proposal_state(pip_id: PipId, state: ProposalState) -> DispatchResult { + let proposal_state = ProposalStates::::get(pip_id).ok_or(Error::::NoSuchProposal)?; ensure!(proposal_state == state, Error::::IncorrectProposalState); Ok(()) } @@ -1480,7 +1486,7 @@ impl Pallet { fn schedule_pip_for_execution(id: PipId) { // The enactment period is at least 1 block, // as you can only schedule calls for future blocks. - let at = Self::default_enactment_period() + let at = DefaultEnactmentPeriod::::get() .max(One::one()) .saturating_add(System::::block_number()); @@ -1565,10 +1571,10 @@ impl Pallet { } } - /// Rejects the given `id`, refunding the deposit, and possibly pruning the proposal's data. - fn unsafe_reject_proposal(did: IdentityId, id: PipId) -> DispatchResult { - Self::maybe_prune(did, id, ProposalState::Rejected)?; - Ok(()) + /// Sets the proposal state to [`ProposalState::Rejected`], adds the proposal to the pending + /// refunds queue and prunes it if [`PruneHistoricalPips`] is set to true. + fn unsafe_reject_proposal(did: IdentityId, pip_id: PipId) { + Self::maybe_prune(did, pip_id, ProposalState::Rejected); } /// Remove the PIP with `id` from the `ExecutionSchedule` at `block_no`. @@ -1579,57 +1585,61 @@ impl Pallet { } } - /// First set the state to `new_state` - /// and then possibly prune (nearly) all the PIP data, if configuration allows. - fn maybe_prune(did: IdentityId, id: PipId, new_state: ProposalState) -> DispatchResult { - Self::update_proposal_state(did, id, new_state); - Self::prune_data(did, id, new_state, Self::prune_historical_pips())?; - Ok(()) + /// Sets the proposal state to `new_state`, adds the proposal to the pending refunds queue and + /// prunes it if [`PruneHistoricalPips`] is set to true. + fn maybe_prune(did: IdentityId, pip_id: PipId, new_state: ProposalState) { + Self::update_proposal_state(did, pip_id, new_state); + Self::prune_data(did, pip_id, new_state, PruneHistoricalPips::::get()); } - /// Prunes (nearly) all data associated with a proposal, removing it from storage. + /// This function performs the following steps: + /// 1. Adds the proposal to the refund pending queue. + /// 2. Decrements the active proposal count if the proposal's state is active. + /// 3. If `prune` is true, removes various data associated with the proposal from storage. + /// + /// The data removed includes: + /// - Proposal results + /// - Votes associated with the proposal + /// - Proposal metadata + /// - Committee PIPs if the proposal was made by a committee + /// - The proposal itself + /// - PIP skip count + /// - Proposal states /// - /// For efficiency, some data (e.g., re. execution schedules) is not removed in this function, - /// but is removed in functions executing this one. - fn prune_data(did: IdentityId, id: PipId, state: ProposalState, prune: bool) -> DispatchResult { - Self::refund_proposal(did, id)?; + /// For efficiency, some data (e.g., related to execution schedules) is not removed in this function, + /// but is removed in functions that execute this one. + fn prune_data(did: IdentityId, pip_id: PipId, state: ProposalState, prune: bool) { + Self::add_pip_to_pending_queue(pip_id, prune); Self::decrement_count_if_active(state); + if prune { - ProposalResult::::remove(id); - #[allow(deprecated)] - ProposalVotes::::remove_prefix(id, None); - ProposalMetadata::::remove(id); - if let Some(Proposer::Committee(_)) = Self::proposals(id).map(|p| p.proposer) { - CommitteePips::::mutate(|list| list.retain(|&i| i != id)); + ProposalResult::::remove(pip_id); + ProposalMetadata::::remove(pip_id); + if let Some(Proposer::Committee(_)) = Proposals::::get(pip_id).map(|p| p.proposer) { + CommitteePips::::mutate(|list| list.retain(|&i| i != pip_id)); } - Proposals::::remove(id); - PipSkipCount::::remove(id); - ProposalStates::::remove(id); + Proposals::::remove(pip_id); + PipSkipCount::::remove(pip_id); + ProposalStates::::remove(pip_id); } - Self::deposit_event(Event::PipClosed(did, id, prune)); - Ok(()) + + Self::deposit_event(Event::PipClosed(did, pip_id, prune)); } - /// Refunds any tokens used to vote or bond a proposal. - /// - /// This operation is idempotent wrt. chain state, - /// i.e., once run, refunding again will refund nothing. - fn refund_proposal(did: IdentityId, id: PipId) -> DispatchResult { - let mut total_refund = 0; - for (_, deposit) in Deposits::::drain_prefix(id) { - Self::reduce_lock(&deposit.owner, deposit.amount)?; - total_refund = total_refund.saturating_add(deposit.amount); + /// Adds `pip_id` to the refund pending queue. If `prune` is true, also adds it to the votes to pruned queue. + fn add_pip_to_pending_queue(pip_id: PipId, prune: bool) { + PendingRefunds::::insert(pip_id, true); + if prune { + VotesToBePruned::::insert(pip_id, true); } - Self::deposit_event(Event::ProposalRefund(did, id, total_refund)); - Ok(()) } /// Execute the PIP given by `id`. /// Returns an error if the PIP doesn't exist or is not scheduled. fn execute_proposal(id: PipId) -> DispatchResultWithPostInfo { - let proposal = Self::proposals(id).ok_or(Error::::ScheduledProposalDoesntExist)?; + let proposal = Proposals::::get(id).ok_or(Error::::ScheduledProposalDoesntExist)?; let proposal_state = - Self::proposal_state(id).ok_or(Error::::ScheduledProposalDoesntExist)?; + ProposalStates::::get(id).ok_or(Error::::ScheduledProposalDoesntExist)?; ensure!( proposal_state == ProposalState::Scheduled, Error::::ProposalNotInScheduledState @@ -1639,8 +1649,9 @@ impl Pallet { .dispatch(frame_system::RawOrigin::Root.into()); let weight = res.unwrap_or_else(|e| e.post_info).actual_weight; let new_state = res.map_or(ProposalState::Failed, |_| ProposalState::Executed); - Self::maybe_prune(GC_DID, id, new_state)?; - Ok(Some(weight.unwrap_or(Weight::zero())).into()) + Self::maybe_prune(GC_DID, id, new_state); + let weight = weight.map(|v| v.max(::WeightInfo::execute_scheduled_pip())); + Ok(Some(weight.unwrap_or(::WeightInfo::execute_scheduled_pip())).into()) } /// Retrieve votes for a proposal represented by PipId `id`. @@ -1652,7 +1663,7 @@ impl Pallet { return VoteCount::ProposalNotFound; } - let voting = Self::proposal_result(id); + let voting = ProposalResult::::get(id); VoteCount::ProposalFound { ayes: voting.ayes_stake, nays: voting.nays_stake, @@ -1662,7 +1673,7 @@ impl Pallet { /// Retrieve proposals `address` voted on pub fn voted_on(address: T::AccountId) -> Vec { Proposals::::iter() - .filter_map(|(_, pip)| Self::proposal_vote(pip.id, &address).map(|_| pip.id)) + .filter_map(|(_, pip)| ProposalVotes::::get(pip.id, &address).map(|_| pip.id)) .collect::>() } @@ -1673,6 +1684,55 @@ impl Pallet { .map(|(_, pip)| pip.id) .collect() } + + /// Clears from storage any pending refunds and votes. + pub fn remove_pending_storage() -> Weight { + let clear_max = T::MaxRefundsAndVotesPruned::get(); + + let mut n_refunds: u32 = 0; + // Checks if there are any pending refunds to be processed + if let Some(pip_id) = PendingRefunds::::iter_keys().next() { + let mut refunded_amount = 0; + for (_, deposit_info) in Deposits::::drain_prefix(pip_id).take(clear_max as usize) { + n_refunds += 1; + match Self::reduce_lock(&deposit_info.owner, deposit_info.amount) { + Ok(_) => { + refunded_amount = refunded_amount.saturating_add(deposit_info.amount); + } + Err(_) => { + log::error!( + "Failed to refund deposit for PIP: {:?} Owner: {:?} Amount: {}", + pip_id, + deposit_info.owner, + deposit_info.amount + ); + } + } + } + + // Checks if all deposits have been refunded. If so, removes the PIP from the queue. + if Deposits::::iter_prefix(pip_id).next().is_none() { + PendingRefunds::::remove(pip_id); + } + + Self::deposit_event(Event::ProposalRefund(GC_DID, pip_id, refunded_amount)); + } + + // Checks if there are any votes to be pruned + let mut n_pruned_votes: u32 = 0; + if let Some(pip_id) = VotesToBePruned::::iter_keys().next() { + n_pruned_votes = ProposalVotes::::drain_prefix(pip_id) + .take(clear_max as usize) + .count() as u32; + + // Checks if all votes have been pruned. If so, removes the PIP from the queue. + if ProposalVotes::::iter_prefix(pip_id).next().is_none() { + VotesToBePruned::::remove(pip_id); + } + } + + ::WeightInfo::remove_pending_storage(n_refunds, n_pruned_votes) + } } /// Returns the `Weight` based on the number of approves, rejects, and skips from `results`. diff --git a/pallets/runtime/develop/src/runtime.rs b/pallets/runtime/develop/src/runtime.rs index 7392d38bc..dc1b255cb 100644 --- a/pallets/runtime/develop/src/runtime.rs +++ b/pallets/runtime/develop/src/runtime.rs @@ -152,6 +152,9 @@ parameter_types! { pub const MigrationSignedDepositPerItem: Balance = 1_000; pub const MigrationSignedDepositBase: Balance = 1_000_000; pub const MaxKeyLen: u32 = 2048; + + // PIPs + pub const MaxRefundsAndVotesPruned: u32 = 128; } /// 100% goes to the block author. @@ -274,6 +277,7 @@ impl pallet_pips::Config for Runtime { type WeightInfo = polymesh_weights::pallet_pips::SubstrateWeight; type Scheduler = Scheduler; type SchedulerCall = RuntimeCall; + type MaxRefundsAndVotesPruned = MaxRefundsAndVotesPruned; } /// CddProviders instance of group diff --git a/pallets/runtime/mainnet/src/runtime.rs b/pallets/runtime/mainnet/src/runtime.rs index 9085eb6c1..941e0270a 100644 --- a/pallets/runtime/mainnet/src/runtime.rs +++ b/pallets/runtime/mainnet/src/runtime.rs @@ -144,6 +144,9 @@ parameter_types! { // Portfolio: pub const MaxNumberOfFungibleMoves: u32 = 10; pub const MaxNumberOfNFTsMoves: u32 = 100; + + // PIPs + pub const MaxRefundsAndVotesPruned: u32 = 128; } /// 100% goes to the block author. @@ -273,6 +276,7 @@ impl pallet_pips::Config for Runtime { type WeightInfo = polymesh_weights::pallet_pips::SubstrateWeight; type Scheduler = Scheduler; type SchedulerCall = RuntimeCall; + type MaxRefundsAndVotesPruned = MaxRefundsAndVotesPruned; } /// CddProviders instance of group diff --git a/pallets/runtime/testnet/src/runtime.rs b/pallets/runtime/testnet/src/runtime.rs index dda3487da..95a25dd73 100644 --- a/pallets/runtime/testnet/src/runtime.rs +++ b/pallets/runtime/testnet/src/runtime.rs @@ -152,6 +152,9 @@ parameter_types! { pub const MigrationSignedDepositPerItem: Balance = 0; pub const MigrationSignedDepositBase: Balance = 0; pub const MaxKeyLen: u32 = 2048; + + // PIPs + pub const MaxRefundsAndVotesPruned: u32 = 128; } /// 100% goes to the block author. @@ -276,6 +279,7 @@ impl pallet_pips::Config for Runtime { type WeightInfo = polymesh_weights::pallet_pips::SubstrateWeight; type Scheduler = Scheduler; type SchedulerCall = RuntimeCall; + type MaxRefundsAndVotesPruned = MaxRefundsAndVotesPruned; } /// CddProviders instance of group diff --git a/pallets/runtime/tests/src/committee_test.rs b/pallets/runtime/tests/src/committee_test.rs index 90dae300f..7166f4567 100644 --- a/pallets/runtime/tests/src/committee_test.rs +++ b/pallets/runtime/tests/src/committee_test.rs @@ -13,7 +13,7 @@ use frame_system::{EventRecord, Phase}; use pallet_committee::{self as committee, Event as CommitteeRawEvent, PolymeshVotes}; use pallet_group as group; use pallet_identity as identity; -use pallet_pips::{PipId, ProposalState, SnapshotResult}; +use pallet_pips::{PipId, ProposalState, ProposalStates, SnapshotResult}; use polymesh_primitives::{IdentityId, MaybeBlock}; use sp_core::H256; use sp_keyring::AccountKeyring; @@ -95,7 +95,10 @@ fn prepare_proposal(ring: AccountKeyring) { } fn check_scheduled(id: PipId) { - assert_eq!(Pips::proposal_state(id).unwrap(), ProposalState::Scheduled); + assert_eq!( + ProposalStates::::get(id).unwrap(), + ProposalState::Scheduled + ); } fn enact_snapshot_results_call() -> RuntimeCall { diff --git a/pallets/runtime/tests/src/pips_test.rs b/pallets/runtime/tests/src/pips_test.rs index d846f6394..6bb1b7f23 100644 --- a/pallets/runtime/tests/src/pips_test.rs +++ b/pallets/runtime/tests/src/pips_test.rs @@ -14,9 +14,12 @@ use frame_support::{ }; use frame_system::{self, EventRecord}; use pallet_pips::{ - DepositInfo, Event, LiveQueue, Pip, PipDescription, PipId, PipsMetadata, ProposalState, - Proposer, SnapshotId, SnapshotMetadata, SnapshotResult, SnapshottedPip, Vote, VoteCount, - VotingResult, + ActivePipCount, ActivePipLimit, CommitteePips, DefaultEnactmentPeriod, DepositInfo, Event, + LiveQueue, MaxPipSkipCount, MinimumProposalDeposit, PendingPipExpiry, PendingRefunds, Pip, + PipDescription, PipId, PipIdSequence, PipSkipCount, PipToSchedule, PipsMetadata, + ProposalMetadata, ProposalResult, ProposalState, ProposalStates, ProposalVotes, Proposals, + Proposer, PruneHistoricalPips, SnapshotId, SnapshotMeta, SnapshotMetadata, SnapshotQueue, + SnapshotResult, SnapshottedPip, Vote, VoteCount, VotesToBePruned, VotingResult, }; use pallet_treasury as treasury; use polymesh_primitives::{AccountId, BlockNumber, MaybeBlock, Url, GC_DID}; @@ -92,21 +95,24 @@ fn proposal( url: Option, desc: Option, ) -> DispatchResult { - let before = Pips::pip_id_sequence(); - let active = Pips::active_pip_count(); + let before = PipIdSequence::::get(); + let active = ActivePipCount::::get(); let signer = signer.clone(); let result = Pips::propose(signer, Box::new(proposal), deposit, url, desc); let add = result.map_or(0, |_| 1); if let Ok(_) = result { assert_last_event!(Event::ProposalCreated(_, _, id, ..), *id == before); assert_eq!( - Pips::committee_pips().contains(&before), + CommitteePips::::get().contains(&before), matches!(proposer, Proposer::Committee(_)) ); - assert_eq!(&Pips::proposals(before).unwrap().proposer, proposer); + assert_eq!( + &Proposals::::get(before).unwrap().proposer, + proposer + ); } - assert_eq!(Pips::pip_id_sequence(), PipId(before.0 + add)); - assert_eq!(Pips::active_pip_count(), active + add); + assert_eq!(PipIdSequence::::get(), PipId(before.0 + add)); + assert_eq!(ActivePipCount::::get(), active + add); result } @@ -154,11 +160,11 @@ fn consensus_call(call: pallet_pips::Call, signers: &[&Origin]) { } pub fn assert_state(id: PipId, care_about_pruned: bool, state: ProposalState) { - let prop = Pips::proposals(id); - if care_about_pruned && Pips::prune_historical_pips() { + let prop = Proposals::::get(id); + if care_about_pruned && PruneHistoricalPips::::get() { assert_eq!(prop, None); } else { - assert_eq!(Pips::proposal_state(id).unwrap(), state); + assert_eq!(ProposalStates::::get(id).unwrap(), state); } } @@ -172,39 +178,39 @@ fn updating_pips_variables_works() { ExtBuilder::default().build().execute_with(|| { System::set_block_number(1); - assert_eq!(Pips::prune_historical_pips(), false); + assert_eq!(PruneHistoricalPips::::get(), false); assert_ok!(Pips::set_prune_historical_pips(root(), true)); assert_last_event!(Event::HistoricalPipsPruned(_, false, true)); - assert_eq!(Pips::prune_historical_pips(), true); + assert_eq!(PruneHistoricalPips::::get(), true); - assert_eq!(Pips::min_proposal_deposit(), 50); + assert_eq!(MinimumProposalDeposit::::get(), 50); assert_ok!(Pips::set_min_proposal_deposit(root(), 10)); assert_last_event!(Event::MinimumProposalDepositChanged(_, 50, 10)); - assert_eq!(Pips::min_proposal_deposit(), 10); + assert_eq!(MinimumProposalDeposit::::get(), 10); - assert_eq!(Pips::default_enactment_period(), 100); + assert_eq!(DefaultEnactmentPeriod::::get(), 100); assert_ok!(Pips::set_default_enactment_period(root(), 10)); assert_last_event!(Event::DefaultEnactmentPeriodChanged(_, 100, 10)); - assert_eq!(Pips::default_enactment_period(), 10); + assert_eq!(DefaultEnactmentPeriod::::get(), 10); - assert_eq!(Pips::pending_pip_expiry(), MaybeBlock::None); + assert_eq!(PendingPipExpiry::::get(), MaybeBlock::None); assert_ok!(Pips::set_pending_pip_expiry(root(), MaybeBlock::Some(13))); assert_last_event!(Event::PendingPipExpiryChanged( _, MaybeBlock::None, MaybeBlock::Some(13) )); - assert_eq!(Pips::pending_pip_expiry(), MaybeBlock::Some(13)); + assert_eq!(PendingPipExpiry::::get(), MaybeBlock::Some(13)); - assert_eq!(Pips::max_pip_skip_count(), 1); + assert_eq!(MaxPipSkipCount::::get(), 1); assert_ok!(Pips::set_max_pip_skip_count(root(), 42)); assert_last_event!(Event::MaxPipSkipCountChanged(_, 1, 42)); - assert_eq!(Pips::max_pip_skip_count(), 42); + assert_eq!(MaxPipSkipCount::::get(), 42); - assert_eq!(Pips::active_pip_limit(), 5); + assert_eq!(ActivePipLimit::::get(), 5); assert_ok!(Pips::set_active_pip_limit(root(), 42)); assert_last_event!(Event::ActivePipLimitChanged(_, 5, 42)); - assert_eq!(Pips::active_pip_limit(), 42); + assert_eq!(ActivePipLimit::::get(), 42); }); } @@ -267,22 +273,25 @@ fn min_deposit_works() { let alice = User::new(AccountKeyring::Alice).balance(300); // Error when min deposit requirements are not met. - assert_eq!(Pips::pip_id_sequence(), PipId(0)); - assert_eq!(Pips::active_pip_count(), 0); + assert_eq!(PipIdSequence::::get(), PipId(0)); + assert_eq!(ActivePipCount::::get(), 0); assert_noop!(community_proposal(alice, deposit), Error::IncorrectDeposit); // Now let's use enough. assert_ok!(community_proposal(alice, deposit + 1)); assert_state(PipId(0), false, ProposalState::Pending); assert_eq!( - Pips::proposals(PipId(0)).unwrap().proposer, + Proposals::::get(PipId(0)).unwrap().proposer, Proposer::Community(alice.acc()) ); // Committees are exempt from min deposit. assert_ok!(committee_proposal(0)); assert_state(PipId(1), false, ProposalState::Pending); - assert_eq!(Pips::proposals(PipId(1)).unwrap().proposer, THE_COMMITTEE); + assert_eq!( + Proposals::::get(PipId(1)).unwrap().proposer, + THE_COMMITTEE + ); assert_vote_details(PipId(1), VotingResult::default(), vec![], vec![]); }) } @@ -295,34 +304,34 @@ fn active_limit_works() { let proposer = User::new(AccountKeyring::Alice); - assert_eq!(Pips::pip_id_sequence(), PipId(0)); - assert_eq!(Pips::active_pip_count(), 0); + assert_eq!(PipIdSequence::::get(), PipId(0)); + assert_eq!(ActivePipCount::::get(), 0); assert_ok!(community_proposal(proposer, 0)); - assert_eq!(Pips::active_pip_count(), 1); + assert_eq!(ActivePipCount::::get(), 1); // Limit reached, so error. assert_ok!(Pips::set_active_pip_limit(root(), 1)); assert_noop!(community_proposal(proposer, 0), Error::TooManyActivePips); - assert_eq!(Pips::active_pip_count(), 1); + assert_eq!(ActivePipCount::::get(), 1); // Bump limit; ok again. assert_ok!(Pips::set_active_pip_limit(root(), 2)); assert_ok!(community_proposal(proposer, 0)); - assert_eq!(Pips::active_pip_count(), 2); + assert_eq!(ActivePipCount::::get(), 2); // Reached again, so error. assert_noop!(community_proposal(proposer, 0), Error::TooManyActivePips); - assert_eq!(Pips::active_pip_count(), 2); + assert_eq!(ActivePipCount::::get(), 2); // Committees are exempt from limit. assert_ok!(committee_proposal(0)); - assert_eq!(Pips::active_pip_count(), 3); + assert_eq!(ActivePipCount::::get(), 3); // Remove limit completely, and let's add more. assert_ok!(Pips::set_active_pip_limit(root(), 0)); assert_ok!(community_proposal(proposer, 0)); - assert_eq!(Pips::active_pip_count(), 4); + assert_eq!(ActivePipCount::::get(), 4); }) } @@ -337,7 +346,7 @@ fn default_enactment_period_works_community() { let check_community = |period| { assert_ok!(Pips::set_min_proposal_deposit(root(), 0)); assert_ok!(community_proposal(alice, 0)); - let last_id = PipId(Pips::pip_id_sequence().0 - 1); + let last_id = PipId(PipIdSequence::::get().0 - 1); fast_forward_blocks(1); assert_ok!(Pips::snapshot(alice.origin())); assert_ok!(Pips::set_default_enactment_period(root(), period)); @@ -346,7 +355,7 @@ fn default_enactment_period_works_community() { gc_vmo(), vec![(last_id, SnapshotResult::Approve)] )); - let expected = Pips::pip_to_schedule(last_id).unwrap(); + let expected = PipToSchedule::::get(last_id).unwrap(); let period = period.max(1); assert_eq!(expected, block_at_approval + period); assert_eq!(1, Agenda::get(expected).len()); @@ -368,12 +377,12 @@ fn default_enactment_period_works_committee() { let check_committee = |period| { assert_ok!(committee_proposal(0)); - let last_id = PipId(Pips::pip_id_sequence().0 - 1); + let last_id = PipId(PipIdSequence::::get().0 - 1); fast_forward_blocks(1); assert_ok!(Pips::set_default_enactment_period(root(), period)); let block_at_approval = System::block_number(); assert_ok!(Pips::approve_committee_proposal(gc_vmo(), last_id)); - let expected = Pips::pip_to_schedule(last_id).unwrap(); + let expected = PipToSchedule::::get(last_id).unwrap(); let period = period.max(1); assert_eq!(expected, block_at_approval + period); assert_eq!(1, Agenda::get(expected).len()); @@ -425,7 +434,7 @@ fn assert_vote_details( deposits: Vec>, votes: Vec, ) { - assert_eq!(results, Pips::proposal_result(id)); + assert_eq!(results, ProposalResult::::get(id)); assert_eq!( deposits, Deposits::iter_prefix_values(id).collect::>(), @@ -476,9 +485,9 @@ fn proposal_details_are_correct() { proposal: call, proposer, }; - assert_eq!(Pips::proposals(PipId(0)).unwrap(), expected); + assert_eq!(Proposals::::get(PipId(0)).unwrap(), expected); assert_eq!( - Pips::proposal_state(PipId(0)).unwrap(), + ProposalStates::::get(PipId(0)).unwrap(), ProposalState::Pending ); @@ -490,7 +499,10 @@ fn proposal_details_are_correct() { transaction_version: 7, expiry: <_>::default(), }; - assert_eq!(Pips::proposal_metadata(PipId(0)).unwrap(), expected); + assert_eq!( + ProposalMetadata::::get(PipId(0)).unwrap(), + expected + ); assert_balance(alice.acc(), 300, 60); assert_votes(PipId(0), alice.acc(), 60); @@ -671,7 +683,7 @@ fn vote_duplicate_ok() { let proposer = User::new(AccountKeyring::Alice); let vote = |aye, power| Pips::vote(proposer.origin(), PipId(0), aye, power); - let res = || Pips::proposal_result(PipId(0)); + let res = || ProposalResult::::get(PipId(0)); assert_ok!(community_proposal(proposer, 42)); assert_eq!( @@ -732,7 +744,7 @@ fn vote_stake_overflow() { assert_ok!(community_proposal(alice, u128::MAX)); assert_eq!( - Pips::proposal_result(id), + ProposalResult::::get(id), VotingResult { ayes_count: 1, ayes_stake: u128::MAX, @@ -899,9 +911,9 @@ fn only_gc_majority_stuff() { set_members(vec![bob.did, charlie.did]); // Make a proposal - let id = Pips::pip_id_sequence(); + let id = PipIdSequence::::get(); assert_eq!(id, PipId(0)); - assert_eq!(Pips::active_pip_count(), 0); + assert_eq!(ActivePipCount::::get(), 0); assert_ok!(community_proposal(proposer, 0)); // Alice not part of GC and cannot reject. assert_bad_origin!(Pips::reject_proposal(alice.origin(), id)); @@ -927,20 +939,20 @@ fn only_gc_majority_stuff() { // VMO can reject. assert_ok!(Pips::set_prune_historical_pips(root(), false)); assert_ok!(Pips::reject_proposal(gc_vmo(), id)); - assert_eq!(Pips::pip_id_sequence(), PipId(1)); - assert_eq!(Pips::active_pip_count(), 0); + assert_eq!(PipIdSequence::::get(), PipId(1)); + assert_eq!(ActivePipCount::::get(), 0); assert_state(id, false, ProposalState::Rejected); // VMO can also prune. assert_ok!(Pips::prune_proposal(gc_vmo(), id)); - assert_eq!(Pips::proposals(id), None); + assert_eq!(Proposals::::get(id), None); // VMO can also `approve_committee_proposal`. - let id = Pips::pip_id_sequence(); + let id = PipIdSequence::::get(); assert_ok!(committee_proposal(0)); assert_ok!(Pips::approve_committee_proposal(gc_vmo(), id)); assert_ok!(Pips::reject_proposal(gc_vmo(), id)); assert_ok!(Pips::prune_proposal(gc_vmo(), id)); // VMO can also `enact_snapshot_results`. - let id = Pips::pip_id_sequence(); + let id = PipIdSequence::::get(); assert_ok!(community_proposal(proposer, 0)); assert_ok!(Pips::snapshot(bob.origin())); assert_ok!(Pips::enact_snapshot_results( @@ -951,23 +963,23 @@ fn only_gc_majority_stuff() { let consensus_call = |call| consensus_call(call, &[&bob.origin(), &charlie.origin()]); // Bob & Charlie seek consensus and successfully reject. - let id = Pips::pip_id_sequence(); + let id = PipIdSequence::::get(); assert_ok!(community_proposal(proposer, 0)); - assert_eq!(Pips::pip_id_sequence().0, id.0 + 1); - assert_eq!(Pips::active_pip_count(), 1); + assert_eq!(PipIdSequence::::get().0, id.0 + 1); + assert_eq!(ActivePipCount::::get(), 1); consensus_call(pallet_pips::Call::reject_proposal { id }); - assert_eq!(Pips::pip_id_sequence().0, id.0 + 1); - assert_eq!(Pips::active_pip_count(), 0); + assert_eq!(PipIdSequence::::get().0, id.0 + 1); + assert_eq!(ActivePipCount::::get(), 0); assert_state(id, false, ProposalState::Rejected); // And now they seek consensus to and do prune. consensus_call(pallet_pips::Call::prune_proposal { id }); - assert_eq!(Pips::proposals(id), None); + assert_eq!(Proposals::::get(id), None); // Bob & Charlie seek consensus. // They successfully do `approve_committee_proposal` & `enact_snapshot_results`. - let id_committee = Pips::pip_id_sequence(); + let id_committee = PipIdSequence::::get(); assert_ok!(committee_proposal(0)); - let id_snapshot = Pips::pip_id_sequence(); + let id_snapshot = PipIdSequence::::get(); assert_ok!(community_proposal(proposer, 0)); assert_ok!(Pips::snapshot(bob.origin())); consensus_call(pallet_pips::Call::approve_committee_proposal { id: id_committee }); @@ -984,21 +996,21 @@ fn cannot_reject_no_such_proposal() { ExtBuilder::default().build().execute_with(|| { // Rejecting PIP that doesn't exist errors. let id = PipId(0); - assert_eq!(Pips::pip_id_sequence(), id); - assert_eq!(Pips::active_pip_count(), 0); + assert_eq!(PipIdSequence::::get(), id); + assert_eq!(ActivePipCount::::get(), 0); assert_no_pip!(Pips::reject_proposal(gc_vmo(), id)); - assert_eq!(Pips::pip_id_sequence(), id); - assert_eq!(Pips::active_pip_count(), 0); + assert_eq!(PipIdSequence::::get(), id); + assert_eq!(ActivePipCount::::get(), 0); assert_no_pip!(Pips::prune_proposal(gc_vmo(), id)); - assert_eq!(Pips::pip_id_sequence(), id); - assert_eq!(Pips::active_pip_count(), 0); + assert_eq!(PipIdSequence::::get(), id); + assert_eq!(ActivePipCount::::get(), 0); }); } fn scheduled_proposal(proposer: User, member: User, deposit: u128) -> PipId { - let next_id = Pips::pip_id_sequence(); + let next_id = PipIdSequence::::get(); assert_ok!(community_proposal(proposer, deposit)); - let active = Pips::active_pip_count(); + let active = ActivePipCount::::get(); assert_ok!(Pips::snapshot(member.origin())); assert_ok!(Pips::enact_snapshot_results( gc_vmo(), @@ -1006,27 +1018,27 @@ fn scheduled_proposal(proposer: User, member: User, deposit: u128) -> PipId { )); assert_event_exists!( EventTest::Scheduler(pallet_scheduler::Event::Scheduled { when, .. }), - *when == System::block_number() + Pips::default_enactment_period() + *when == System::block_number() + DefaultEnactmentPeriod::::get() ); assert_state(next_id, false, ProposalState::Scheduled); - assert_eq!(Pips::active_pip_count(), active); + assert_eq!(ActivePipCount::::get(), active); next_id } fn executed_community_proposal(proposer: User, member: User) -> PipId { - let deposit = Pips::min_proposal_deposit(); + let deposit = MinimumProposalDeposit::::get(); let next_id = scheduled_proposal(proposer, member, deposit); - let active = Pips::active_pip_count(); - fast_forward_blocks(Pips::default_enactment_period() + 1); + let active = ActivePipCount::::get(); + fast_forward_blocks(DefaultEnactmentPeriod::::get() + 1); assert_ok!(Pips::set_min_proposal_deposit(root(), deposit)); assert_state(next_id, true, ProposalState::Executed); - assert_eq!(Pips::active_pip_count(), active - 1); + assert_eq!(ActivePipCount::::get(), active - 1); next_id } fn failed_community_proposal(proposer: User, member: User, bad_id: PipId) -> PipId { - let next_id = Pips::pip_id_sequence(); - let deposit = Pips::min_proposal_deposit(); + let next_id = PipIdSequence::::get(); + let deposit = MinimumProposalDeposit::::get(); assert_ok!(proposal( &proposer.origin(), &Proposer::Community(proposer.acc()), @@ -1035,53 +1047,61 @@ fn failed_community_proposal(proposer: User, member: User, bad_id: PipId) -> Pip None, None )); - let active = Pips::active_pip_count(); + let active = ActivePipCount::::get(); assert_ok!(Pips::snapshot(member.origin())); assert_ok!(Pips::enact_snapshot_results( gc_vmo(), vec![(next_id, SnapshotResult::Approve)] )); assert_state(next_id, false, ProposalState::Scheduled); - assert_eq!(Pips::active_pip_count(), active); - fast_forward_blocks(Pips::default_enactment_period() + 1); + assert_eq!(ActivePipCount::::get(), active); + fast_forward_blocks(DefaultEnactmentPeriod::::get() + 1); assert_state(next_id, true, ProposalState::Failed); - assert_eq!(Pips::active_pip_count(), active - 1); + assert_eq!(ActivePipCount::::get(), active - 1); next_id } fn rejected_proposal(proposer: User) -> PipId { - let next_id = Pips::pip_id_sequence(); - assert_ok!(community_proposal(proposer, Pips::min_proposal_deposit())); - let active = Pips::active_pip_count(); + let next_id = PipIdSequence::::get(); + assert_ok!(community_proposal( + proposer, + MinimumProposalDeposit::::get() + )); + let active = ActivePipCount::::get(); assert_ok!(Pips::reject_proposal(gc_vmo(), next_id)); assert_state(next_id, true, ProposalState::Rejected); - assert_eq!(Pips::active_pip_count(), active - 1); - assert_eq!(Pips::pip_id_sequence().0, next_id.0 + 1); + assert_eq!(ActivePipCount::::get(), active - 1); + assert_eq!(PipIdSequence::::get().0, next_id.0 + 1); next_id } fn expired_proposal(proposer: User, expiry: BlockNumber) -> PipId { - let next_id = Pips::pip_id_sequence(); + let next_id = PipIdSequence::::get(); // Save old config data and set new ones for expiry. - let old_expiry = Pips::pending_pip_expiry(); + let old_expiry = PendingPipExpiry::::get(); assert_ok!(Pips::set_pending_pip_expiry( root(), MaybeBlock::Some(expiry) )); // Create a proposal and verify its pending. - let active = Pips::active_pip_count(); - assert_ok!(community_proposal(proposer, Pips::min_proposal_deposit())); + let active = ActivePipCount::::get(); + assert_ok!(community_proposal( + proposer, + MinimumProposalDeposit::::get() + )); assert_state(next_id, false, ProposalState::Pending); assert_eq!( - Pips::proposal_metadata(next_id).unwrap().expiry, + ProposalMetadata::::get(next_id) + .unwrap() + .expiry, MaybeBlock::Some(expiry + System::block_number()) ); // Now fast forward. fast_forward_blocks(expiry + 1); // Forward exactly to expiry point + 1. - assert_eq!(Pips::active_pip_count(), active); + assert_eq!(ActivePipCount::::get(), active); // Restore config to before function was called. assert_ok!(Pips::set_pending_pip_expiry(root(), old_expiry)); @@ -1111,15 +1131,24 @@ fn cannot_reject_incorrect_state() { } fn assert_pruned(id: PipId) { - assert_eq!(Pips::proposal_metadata(id), None); - assert_eq!(Deposits::iter_prefix_values(id).count(), 0); - assert_eq!(Pips::proposals(id), None); - assert_vote_details(id, VotingResult::default(), vec![], vec![]); - assert_eq!(Pips::pip_to_schedule(id), None); + assert_eq!(ProposalMetadata::::get(id), None); + assert_eq!(Deposits::iter_prefix_values(id).count(), 1); + assert_eq!(PendingRefunds::::get(id), Some(true)); + assert_eq!(VotesToBePruned::::get(id), Some(true)); + assert_eq!(Proposals::::get(id), None); + assert_vote_details( + id, + VotingResult::default(), + vec![Deposits::iter_prefix_values(id).next().unwrap()], + Votes::iter_prefix_values(id).collect::>(), + ); + assert_eq!(PipToSchedule::::get(id), None); // TODO: Check that the PIP has been removed from the schedule. This should be easily done after // fixing this issue: https://github.com/PolymeshAssociation/substrate/issues/7449 - assert!(Pips::snapshot_queue().iter().all(|p| p.id != id)); - assert_eq!(Pips::pip_skip_count(id), 0); + assert!(SnapshotQueue::::get() + .iter() + .all(|p| p.id != id)); + assert_eq!(PipSkipCount::::get(id), 0); } #[test] @@ -1139,8 +1168,8 @@ fn can_prune_states_that_cannot_be_rejected() { let id = PipId(0); assert_ok!(community_proposal(proposer, 200)); assert_balance(proposer.acc(), init_bal, 200); - assert_eq!(Pips::pip_id_sequence(), PipId(1)); - assert_eq!(Pips::active_pip_count(), 1); + assert_eq!(PipIdSequence::::get(), PipId(1)); + assert_eq!(ActivePipCount::::get(), 1); assert_ok!(Pips::snapshot(member.origin())); assert_ok!(Pips::enact_snapshot_results( gc_vmo(), @@ -1148,13 +1177,13 @@ fn can_prune_states_that_cannot_be_rejected() { )); assert_state(id, false, ProposalState::Scheduled); assert_balance(proposer.acc(), init_bal, 200); - assert_eq!(Pips::active_pip_count(), 1); - fast_forward_blocks(Pips::default_enactment_period() + 1); + assert_eq!(ActivePipCount::::get(), 1); + fast_forward_blocks(DefaultEnactmentPeriod::::get() + 1); assert_state(id, false, ProposalState::Executed); - assert_balance(proposer.acc(), init_bal, 0); - assert_eq!(Pips::active_pip_count(), 0); + assert_balance(proposer.acc(), init_bal, 200); + assert_eq!(ActivePipCount::::get(), 0); assert_ok!(Pips::prune_proposal(gc_vmo(), id)); - assert_balance(proposer.acc(), init_bal, 0); + assert_balance(proposer.acc(), init_bal, 200); assert_pruned(id); // Can prune failed: @@ -1167,37 +1196,37 @@ fn can_prune_states_that_cannot_be_rejected() { None, None )); - assert_balance(proposer.acc(), init_bal, 300); - assert_eq!(Pips::pip_id_sequence(), PipId(2)); - assert_eq!(Pips::active_pip_count(), 1); + assert_balance(proposer.acc(), init_bal, 300 + 200); + assert_eq!(PipIdSequence::::get(), PipId(2)); + assert_eq!(ActivePipCount::::get(), 1); assert_ok!(Pips::snapshot(member.origin())); assert_ok!(Pips::enact_snapshot_results( gc_vmo(), vec![(id, SnapshotResult::Approve)] )); assert_state(id, false, ProposalState::Scheduled); - assert_balance(proposer.acc(), init_bal, 300); - assert_eq!(Pips::active_pip_count(), 1); - fast_forward_blocks(Pips::default_enactment_period() + 1); + assert_balance(proposer.acc(), init_bal, 300 + 200); + assert_eq!(ActivePipCount::::get(), 1); + fast_forward_blocks(DefaultEnactmentPeriod::::get() + 1); assert_state(id, false, ProposalState::Failed); - assert_balance(proposer.acc(), init_bal, 0); - assert_eq!(Pips::active_pip_count(), 0); + assert_balance(proposer.acc(), init_bal, 300 + 200); + assert_eq!(ActivePipCount::::get(), 0); assert_ok!(Pips::prune_proposal(gc_vmo(), id)); - assert_balance(proposer.acc(), init_bal, 0); + assert_balance(proposer.acc(), init_bal, 300 + 200); assert_pruned(id); // Can prune rejected: let id = PipId(2); assert_ok!(community_proposal(proposer, 400)); - assert_balance(proposer.acc(), init_bal, 400); - assert_eq!(Pips::pip_id_sequence(), PipId(3)); - assert_eq!(Pips::active_pip_count(), 1); + assert_balance(proposer.acc(), init_bal, 300 + 200 + 400); + assert_eq!(PipIdSequence::::get(), PipId(3)); + assert_eq!(ActivePipCount::::get(), 1); assert_ok!(Pips::reject_proposal(gc_vmo(), id)); - assert_balance(proposer.acc(), init_bal, 0); + assert_balance(proposer.acc(), init_bal, 300 + 200 + 400); assert_state(id, false, ProposalState::Rejected); - assert_eq!(Pips::active_pip_count(), 0); + assert_eq!(ActivePipCount::::get(), 0); assert_ok!(Pips::prune_proposal(gc_vmo(), id)); - assert_balance(proposer.acc(), init_bal, 0); + assert_balance(proposer.acc(), init_bal, 300 + 200 + 400); assert_pruned(id); }); } @@ -1221,8 +1250,8 @@ fn cannot_prune_active() { assert_state(id, false, ProposalState::Pending); assert_bad_state!(Pips::prune_proposal(gc_vmo(), id)); let id = PipId(1); - assert_eq!(Pips::pip_id_sequence(), id); - assert_eq!(Pips::active_pip_count(), 1); + assert_eq!(PipIdSequence::::get(), id); + assert_eq!(ActivePipCount::::get(), 1); assert_balance(proposer.acc(), init_bal, 50); // Alice starts a proposal with some deposit. @@ -1237,8 +1266,8 @@ fn cannot_prune_active() { assert_state(id, false, ProposalState::Scheduled); // Now remove that PIP and check that funds are back. assert_bad_state!(Pips::prune_proposal(gc_vmo(), id)); - assert_eq!(Pips::pip_id_sequence(), PipId(2)); - assert_eq!(Pips::active_pip_count(), 2); + assert_eq!(PipIdSequence::::get(), PipId(2)); + assert_eq!(ActivePipCount::::get(), 2); assert_balance(proposer.acc(), init_bal, 50 + 60); }); } @@ -1263,40 +1292,44 @@ fn reject_proposal_works() { nays_count: 0, nays_stake: 0, }; - assert_eq!(Pips::proposal_result(id), result); + assert_eq!(ProposalResult::::get(id), result); // Now remove that PIP and check that funds are back. assert_ok!(Pips::set_prune_historical_pips(root(), false)); assert_state(id, false, ProposalState::Pending); assert_ok!(Pips::reject_proposal(gc_vmo(), id)); - assert_eq!(Pips::active_pip_count(), 0); + assert_eq!(ActivePipCount::::get(), 0); assert_eq!( - Pips::proposals(id).unwrap(), + Proposals::::get(id).unwrap(), Pip { id, proposal: make_proposal(42), proposer: Proposer::Community(proposer.acc()), } ); - assert_eq!(Pips::proposal_state(id).unwrap(), ProposalState::Rejected); - assert_balance(proposer.acc(), init_bal, 0); - assert_eq!(Deposits::iter_prefix_values(id).count(), 0); + assert_eq!( + ProposalStates::::get(id).unwrap(), + ProposalState::Rejected + ); + assert_balance(proposer.acc(), init_bal, 50); + assert_eq!(Deposits::iter_prefix_values(id).count(), 1); + assert_eq!(PendingRefunds::::get(id), Some(true)); // We keep this info for posterity. assert_eq!(Votes::iter_prefix_values(id).count(), 1); - assert_eq!(Pips::proposal_result(id), result); + assert_eq!(ProposalResult::::get(id), result); let id = PipId(1); - assert_eq!(Pips::pip_id_sequence(), id); + assert_eq!(PipIdSequence::::get(), id); // Alice starts a proposal with some deposit. assert_ok!(community_proposal(proposer, 60)); - assert_balance(proposer.acc(), init_bal, 60); + assert_balance(proposer.acc(), init_bal, 60 + 50); let result = VotingResult { ayes_count: 1, ayes_stake: 60, nays_count: 0, nays_stake: 0, }; - assert_eq!(Pips::proposal_result(id), result); + assert_eq!(ProposalResult::::get(id), result); // Schedule the PIP. assert_ok!(Pips::snapshot(member.origin())); @@ -1308,22 +1341,26 @@ fn reject_proposal_works() { // Now remove that PIP and check that funds are back. assert_ok!(Pips::reject_proposal(gc_vmo(), id)); - assert_eq!(Pips::pip_id_sequence(), PipId(2)); - assert_eq!(Pips::active_pip_count(), 0); + assert_eq!(PipIdSequence::::get(), PipId(2)); + assert_eq!(ActivePipCount::::get(), 0); assert_eq!( - Pips::proposals(id).unwrap(), + Proposals::::get(id).unwrap(), Pip { id, proposal: make_proposal(42), proposer: Proposer::Community(proposer.acc()), } ); - assert_eq!(Pips::proposal_state(id).unwrap(), ProposalState::Rejected); - assert_balance(proposer.acc(), init_bal, 0); - assert_eq!(Deposits::iter_prefix_values(id).count(), 0); + assert_eq!( + ProposalStates::::get(id).unwrap(), + ProposalState::Rejected + ); + assert_balance(proposer.acc(), init_bal, 60 + 50); + assert_eq!(Deposits::iter_prefix_values(id).count(), 1); // We keep this info for posterity. assert_eq!(Votes::iter_prefix_values(id).count(), 1); - assert_eq!(Pips::proposal_result(id), result); + assert_eq!(ProposalResult::::get(id), result); + assert_eq!(PendingRefunds::::get(id), Some(true)); }); } @@ -1341,9 +1378,9 @@ fn reject_proposal_will_unsnapshot() { let id = PipId(0); assert_ok!(community_proposal(proposer, 0)); assert_ok!(Pips::snapshot(member.origin())); - assert_eq!(Pips::snapshot_queue()[0].id, id); + assert_eq!(SnapshotQueue::::get()[0].id, id); assert_ok!(Pips::reject_proposal(gc_vmo(), id)); - assert_eq!(Pips::snapshot_queue(), vec![]); + assert_eq!(SnapshotQueue::::get(), vec![]); }); } @@ -1358,9 +1395,9 @@ fn reject_proposal_will_unschedule() { set_members(vec![alice.did]); let check = |id: PipId| { - let scheduled_at = Pips::pip_to_schedule(id).unwrap(); + let scheduled_at = PipToSchedule::::get(id).unwrap(); assert_ok!(Pips::reject_proposal(gc_vmo(), id)); - assert_eq!(Pips::pip_to_schedule(id), None); + assert_eq!(PipToSchedule::::get(id), None); assert_event_exists!( EventTest::Scheduler(pallet_scheduler::Event::Canceled { when, .. }), *when == scheduled_at @@ -1407,14 +1444,14 @@ fn reschedule_execution_only_release_coordinator() { assert_ok!(Pips::set_min_proposal_deposit(root(), 0)); let id = scheduled_proposal(alice, alice, 0); - let scheduled_at = Pips::pip_to_schedule(id); + let scheduled_at = PipToSchedule::::get(id); consensus_call( pallet_pips::Call::reschedule_execution { id, until: None }, &[&alice.origin(), &bob.origin(), &charlie.origin()], ); - assert_eq!(scheduled_at, Pips::pip_to_schedule(id)); + assert_eq!(scheduled_at, PipToSchedule::::get(id)); assert_ok!(Pips::reschedule_execution(charlie.origin(), id, None)); - assert_ne!(scheduled_at, Pips::pip_to_schedule(id)); + assert_ne!(scheduled_at, PipToSchedule::::get(id)); }); } @@ -1441,7 +1478,7 @@ fn reschedule_execution_not_scheduled() { let rc = init_rc(); let proposer = User::new(AccountKeyring::Bob); assert_ok!(Pips::set_min_proposal_deposit(root(), 0)); - let id = Pips::pip_id_sequence(); + let id = PipIdSequence::::get(); assert_ok!(community_proposal(proposer, 0)); assert_bad_state!(Pips::reschedule_execution(rc.origin(), id, None)); assert_ok!(Pips::reject_proposal(gc_vmo(), id)); @@ -1488,29 +1525,29 @@ fn reschedule_execution_works() { assert_ok!(Pips::set_min_proposal_deposit(root(), 0)); // Schedule a proposal and verify that it is. - assert_eq!(Pips::active_pip_count(), 0); + assert_eq!(ActivePipCount::::get(), 0); let id = scheduled_proposal(proposer, rc, 0); - assert_eq!(Pips::active_pip_count(), 1); - let scheduled_at = Pips::pip_to_schedule(id).unwrap(); + assert_eq!(ActivePipCount::::get(), 1); + let scheduled_at = PipToSchedule::::get(id).unwrap(); assert!(matches!(Agenda::get(scheduled_at).deref()[..], [Some(_)])); // Reschedule execution for next block. let next = System::block_number() + 1; assert_ok!(Pips::reschedule_execution(rc.origin(), id, None)); // Regression test for . - assert_eq!(Pips::active_pip_count(), 1); + assert_eq!(ActivePipCount::::get(), 1); // Rescheduling currently works by cancelling + then scheduling again. Verify this. assert_event_exists!(EventTest::Scheduler( pallet_scheduler::Event::Canceled { .. } )); - assert_eq!(Pips::pip_to_schedule(id).unwrap(), next); + assert_eq!(PipToSchedule::::get(id).unwrap(), next); assert_eq!(Agenda::get(scheduled_at), vec![]); assert!(matches!(Agenda::get(next).deref()[..], [Some(_)])); // Reschedule execution for 50 blocks ahead. assert_ok!(Pips::reschedule_execution(rc.origin(), id, Some(next + 50))); - assert_eq!(Pips::active_pip_count(), 1); - assert_eq!(Pips::pip_to_schedule(id).unwrap(), next + 50); + assert_eq!(ActivePipCount::::get(), 1); + assert_eq!(PipToSchedule::::get(id).unwrap(), next + 50); assert_eq!(Agenda::get(scheduled_at), vec![]); assert_eq!(Agenda::get(next), vec![]); assert!(matches!(Agenda::get(next + 50).deref()[..], [Some(_)])); @@ -1537,11 +1574,11 @@ fn clear_snapshot_works() { System::set_block_number(1); let rc = init_rc(); // No snapshot, but we can still clear. - assert_eq!(Pips::snapshot_queue(), vec![]); - assert_eq!(Pips::snapshot_metadata(), None); + assert_eq!(SnapshotQueue::::get(), vec![]); + assert_eq!(SnapshotMeta::::get(), None); assert_ok!(Pips::clear_snapshot(rc.origin())); - assert_eq!(Pips::snapshot_queue(), vec![]); - assert_eq!(Pips::snapshot_metadata(), None); + assert_eq!(SnapshotQueue::::get(), vec![]); + assert_eq!(SnapshotMeta::::get(), None); // Make a snapshot with something and clear it. let proposer = User::new(AccountKeyring::Bob); @@ -1549,11 +1586,11 @@ fn clear_snapshot_works() { assert_ok!(community_proposal(proposer, 200)); assert_ok!(community_proposal(proposer, 400)); assert_ok!(Pips::snapshot(rc.origin())); - assert_ne!(Pips::snapshot_queue(), vec![]); - assert_ne!(Pips::snapshot_metadata(), None); + assert_ne!(SnapshotQueue::::get(), vec![]); + assert_ne!(SnapshotMeta::::get(), None); assert_ok!(Pips::clear_snapshot(rc.origin())); - assert_eq!(Pips::snapshot_queue(), vec![]); - assert_eq!(Pips::snapshot_metadata(), None); + assert_eq!(SnapshotQueue::::get(), vec![]); + assert_eq!(SnapshotMeta::::get(), None); }); } @@ -1584,18 +1621,21 @@ fn snapshot_only_pending_hot_community() { let s = scheduled_proposal(proposer, rc, 0); let ex = expired_proposal(proposer, 1); assert_ok!(community_proposal(proposer, 0)); - let p = PipId(Pips::pip_id_sequence().0 - 1); + let p = PipId(PipIdSequence::::get().0 - 1); for id in &[r, e, f, s, p, ex] { assert_eq!( - Pips::proposals(*id).unwrap().proposer, + Proposals::::get(*id).unwrap().proposer, Proposer::Community(proposer.acc()) ); } assert_ok!(committee_proposal(0)); assert_ok!(Pips::snapshot(rc.origin())); - assert_eq!(Pips::snapshot_queue(), vec![spip(p.0, true, 0)]); - assert_ne!(Pips::snapshot_metadata(), None); + assert_eq!( + SnapshotQueue::::get(), + vec![spip(p.0, true, 0)] + ); + assert_ne!(SnapshotMeta::::get(), None); }); } @@ -1634,7 +1674,7 @@ fn snapshot_works() { assert_ok!(snapshot()); assert_eq!( - Pips::snapshot_queue(), + SnapshotQueue::::get(), vec![ spip(1, false, 100), spip(5, false, 50), @@ -1647,7 +1687,7 @@ fn snapshot_works() { let assert_snapshot = |id| { assert_eq!( - Pips::snapshot_metadata(), + SnapshotMeta::::get(), Some(SnapshotMetadata { created_at: 1, made_by: member.acc(), @@ -1763,8 +1803,8 @@ fn enact_snapshot_results_works() { propose(); propose(); assert_ok!(Pips::snapshot(member.origin())); - assert_eq!(Pips::snapshot_queue(), mk_queue(&[2, 1, 0])); - assert_eq!(Pips::pip_skip_count(PipId(1)), 0); + assert_eq!(SnapshotQueue::::get(), mk_queue(&[2, 1, 0])); + assert_eq!(PipSkipCount::::get(PipId(1)), 0); assert_ok!(Pips::enact_snapshot_results( gc_vmo(), vec![ @@ -1775,16 +1815,16 @@ fn enact_snapshot_results_works() { )); assert_state(PipId(0), false, ProposalState::Rejected); assert_state(PipId(1), false, ProposalState::Pending); - assert_eq!(Pips::pip_skip_count(PipId(1)), 1); + assert_eq!(PipSkipCount::::get(PipId(1)), 1); assert_state(PipId(2), false, ProposalState::Scheduled); - assert_eq!(Pips::snapshot_queue(), vec![]); - assert_ne!(Pips::snapshot_metadata(), None); + assert_eq!(SnapshotQueue::::get(), vec![]); + assert_ne!(SnapshotMeta::::get(), None); // Add another proposal; we previously skipped one, so queue size is 2. // Only enact for 1 proposal, leaving the last added PIP in the queue. propose(); assert_ok!(Pips::snapshot(member.origin())); - assert_eq!(Pips::snapshot_queue(), mk_queue(&[3, 1])); + assert_eq!(SnapshotQueue::::get(), mk_queue(&[3, 1])); assert_ok!(Pips::enact_snapshot_results( gc_vmo(), vec![(PipId(1), SnapshotResult::Approve)] @@ -1794,7 +1834,7 @@ fn enact_snapshot_results_works() { a.is_empty() && b.is_empty() && c == &[PipId(1)] ); assert_state(PipId(1), false, ProposalState::Scheduled); - assert_eq!(Pips::snapshot_queue(), mk_queue(&[3])); + assert_eq!(SnapshotQueue::::get(), mk_queue(&[3])); // Cleared queue + enacting zero-length results => noop. assert_ok!(Pips::clear_snapshot(member.origin())); @@ -1833,13 +1873,15 @@ fn expiry_works() { let s = scheduled_proposal(proposer, member, 0); fast_forward_blocks(13 + 100); for id in &[r, e, f, s] { - assert_ne!(Pips::proposal_state(id).unwrap(), ProposalState::Expired); + assert_ne!( + ProposalStates::::get(id).unwrap(), + ProposalState::Expired + ); } }); } #[test] -#[should_panic = "called `Result::unwrap_err()` on an `Ok` value: 0"] fn propose_dupe_live_insert_panics() { ExtBuilder::default().build().execute_with(|| { System::set_block_number(1); @@ -1848,8 +1890,11 @@ fn propose_dupe_live_insert_panics() { // Manipulate storage to provoke panic in `insert_live_queue`. LiveQueue::::mutate(|queue| *queue = vec![spip(0, true, 0)]); - // Triggers a panic, assertion never reached. - assert_ok!(community_proposal(User::new(AccountKeyring::Alice), 0)); + // Returns an error since pip_id is already in the live queue + assert_eq!( + community_proposal(User::new(AccountKeyring::Alice), 0).unwrap_err(), + Error::InvalidPipId.into() + ); }); } @@ -1859,7 +1904,7 @@ fn execute_scheduled_pip() { System::set_block_number(1); assert_ok!(Pips::set_min_proposal_deposit(root(), 0)); assert_ok!(Pips::set_prune_historical_pips(root(), true)); - let pip_id = Pips::pip_id_sequence(); + let pip_id = PipIdSequence::::get(); let user = User::new(AccountKeyring::Alice); assert_ok!(remark_proposal(user, 0)); set_members(vec![user.did]); @@ -1880,7 +1925,7 @@ fn expire_scheduled_pip() { System::set_block_number(1); assert_ok!(Pips::set_min_proposal_deposit(root(), 0)); assert_ok!(Pips::set_prune_historical_pips(root(), true)); - let pip_id = Pips::pip_id_sequence(); + let pip_id = PipIdSequence::::get(); let user = User::new(AccountKeyring::Alice); assert_ok!(remark_proposal(user, 0)); assert_state(pip_id, false, ProposalState::Pending); @@ -1898,11 +1943,17 @@ fn live_queue_off_by_one_insertion_regression_test() { let proposer = User::new(AccountKeyring::Alice); assert_ok!(community_proposal(proposer, 2)); assert_ok!(community_proposal(proposer, 4)); - assert_eq!(Pips::live_queue(), vec![spip(0, true, 2), spip(1, true, 4)]); + assert_eq!( + LiveQueue::::get(), + vec![spip(0, true, 2), spip(1, true, 4)] + ); let user = User::new(AccountKeyring::Bob); assert_ok!(Pips::vote(user.origin(), PipId(0), true, 1)); - assert_eq!(Pips::live_queue(), vec![spip(0, true, 3), spip(1, true, 4)]); + assert_eq!( + LiveQueue::::get(), + vec![spip(0, true, 3), spip(1, true, 4)] + ); }); } @@ -1922,7 +1973,7 @@ fn live_queue_off_by_one_insertion_regression_test2() { assert_ok!(Pips::vote(voter.origin(), PipId(0), false, 100)); assert_ok!(Pips::vote(voter.origin(), PipId(2), false, 100)); assert_eq!( - Pips::live_queue(), + LiveQueue::::get(), vec![spip(0, false, 100), spip(2, false, 50), spip(1, true, 0)] ); }); @@ -1969,3 +2020,80 @@ fn pips_rpcs() { assert_eq!(Pips::voted_on(bob.acc()), vec![pip_id1, pip_id0]); }); } + +#[test] +fn prune_data_with_leftover() { + ExtBuilder::default().monied(true).build().execute_with(|| { + System::set_block_number(1); + + // Creates a proposal and 3 users vote on it + let bob = User::new(AccountKeyring::Bob); + let dave = User::new(AccountKeyring::Dave); + let alice = User::new(AccountKeyring::Alice); + let charlie = User::new(AccountKeyring::Charlie); + + assert_ok!(Pips::set_min_proposal_deposit(root(), 0)); + assert_ok!(community_proposal(alice, 0)); + assert_ok!(Pips::vote(bob.origin(), PipId(0), true, 100)); + assert_ok!(Pips::vote(dave.origin(), PipId(0), true, 100)); + assert_ok!(Pips::vote(charlie.origin(), PipId(0), true, 100)); + assert_eq!(Deposits::iter_prefix_values(PipId(0)).count(), 4); + assert_eq!( + ProposalVotes::::iter_prefix_values(PipId(0)).count(), + 4 + ); + assert!(Proposals::::get(PipId(0)).is_some()); + assert_eq!(VotesToBePruned::::get(PipId(0)), None); + assert_eq!(PendingRefunds::::get(PipId(0)), None); + + // Reject proposal - Adds the proposal to the pending refund queue + assert_ok!(Pips::set_prune_historical_pips(root(), false)); + assert_ok!(Pips::reject_proposal(gc_vmo(), PipId(0))); + assert_eq!(PendingRefunds::::get(PipId(0)), Some(true)); + assert_eq!(VotesToBePruned::::get(PipId(0)), None); + assert_eq!(Deposits::iter_prefix_values(PipId(0)).count(), 4); + assert_eq!( + ProposalVotes::::iter_prefix_values(PipId(0)).count(), + 4 + ); + assert!(Proposals::::get(PipId(0)).is_some()); + + // Prune proposal - Adds the poposal to the Votes to be pruned queue + assert_ok!(Pips::prune_proposal(gc_vmo(), PipId(0))); + assert_eq!(VotesToBePruned::::get(PipId(0)), Some(true)); + assert_eq!(PendingRefunds::::get(PipId(0)), Some(true)); + assert_eq!( + ProposalVotes::::iter_prefix_values(PipId(0)).count(), + 4 + ); + assert_eq!(Deposits::iter_prefix_values(PipId(0)).count(), 4); + assert_eq!(ProposalMetadata::::get(PipId(0)), None); + assert_eq!(Proposals::::get(PipId(0)), None); + assert_eq!(PipSkipCount::::get(PipId(0)), 0); + assert_eq!(ProposalStates::::get(PipId(0)), None); + assert_eq!( + ProposalResult::::get(PipId(0)), + VotingResult::default() + ); + + // On idle execution, Refunds 2 accounts and prune 2 votes + Pips::remove_pending_storage(); + assert_eq!(PendingRefunds::::get(PipId(0)), Some(true)); + assert_eq!(Deposits::iter_prefix_values(PipId(0)).count(), 2); + assert_eq!(VotesToBePruned::::get(PipId(0)), Some(true)); + assert_eq!( + ProposalVotes::::iter_prefix_values(PipId(0)).count(), + 2 + ); + + // On idle execution, Refunds 2 accounts and prune 2 votes + Pips::remove_pending_storage(); + assert_eq!(PendingRefunds::::get(PipId(0)), None); + assert_eq!(Deposits::iter_prefix_values(PipId(0)).count(), 0); + assert_eq!(VotesToBePruned::::get(PipId(0)), None); + assert_eq!( + ProposalVotes::::iter_prefix_values(PipId(0)).count(), + 0 + ); + }); +} diff --git a/pallets/runtime/tests/src/staking/mock.rs b/pallets/runtime/tests/src/staking/mock.rs index 42bdbb402..879199a78 100644 --- a/pallets/runtime/tests/src/staking/mock.rs +++ b/pallets/runtime/tests/src/staking/mock.rs @@ -245,6 +245,7 @@ impl pallet_pips::Config for Test { type WeightInfo = polymesh_weights::pallet_pips::SubstrateWeight; type Scheduler = Scheduler; type SchedulerCall = RuntimeCall; + type MaxRefundsAndVotesPruned = MaxRefundsAndVotesPruned; } impl pallet_treasury::Config for Test { @@ -260,6 +261,8 @@ impl pallet_authorship::Config for Test { parameter_types! { pub const MinimumPeriod: u64 = 5; + // PIPs + pub const MaxRefundsAndVotesPruned: u32 = 128; } impl pallet_timestamp::Config for Test { diff --git a/pallets/runtime/tests/src/staking/mod.rs b/pallets/runtime/tests/src/staking/mod.rs index bc796ae10..d8666c3b7 100644 --- a/pallets/runtime/tests/src/staking/mod.rs +++ b/pallets/runtime/tests/src/staking/mod.rs @@ -28,6 +28,7 @@ use frame_support::{ }; use mock::*; use pallet_balances::Error as BalancesError; +use pallet_pips::{ProposalVotes, Proposals}; use polymesh_primitives::IdentityId; use sp_runtime::{ assert_eq_error_rate, @@ -6838,8 +6839,8 @@ fn slashing_leaves_pips_untouched() { }; let id = PipId(0); let vote_is = |bal| { - Pips::proposals(id).unwrap(); - assert_eq!(Pips::proposal_vote(id, acc).unwrap().1, bal); + Proposals::::get(id).unwrap(); + assert_eq!(ProposalVotes::::get(id, acc).unwrap().1, bal); }; let vote = |bal| Pips::vote(Origin::signed(acc), id, true, bal); diff --git a/pallets/runtime/tests/src/storage.rs b/pallets/runtime/tests/src/storage.rs index 6b57f438e..b14389c5f 100644 --- a/pallets/runtime/tests/src/storage.rs +++ b/pallets/runtime/tests/src/storage.rs @@ -238,6 +238,9 @@ parameter_types! { pub const MigrationSignedDepositPerItem: Balance = 0; pub const MigrationSignedDepositBase: Balance = 0; pub const MaxKeyLen: u32 = 2048; + + // PIPs + pub const MaxRefundsAndVotesPruned: u32 = 2; } frame_support::construct_runtime!( @@ -687,6 +690,7 @@ impl pips::Config for TestStorage { type WeightInfo = polymesh_weights::pallet_pips::SubstrateWeight; type Scheduler = Scheduler; type SchedulerCall = RuntimeCall; + type MaxRefundsAndVotesPruned = MaxRefundsAndVotesPruned; } impl pallet_sudo::Config for Runtime { diff --git a/pallets/runtime/tests/src/utility_test.rs b/pallets/runtime/tests/src/utility_test.rs index b6dda5a06..9dc9bc666 100644 --- a/pallets/runtime/tests/src/utility_test.rs +++ b/pallets/runtime/tests/src/utility_test.rs @@ -13,7 +13,7 @@ use pallet_timestamp::Call as TimestampCall; use pallet_asset::UniqueTickerRegistration; use pallet_balances::Call as BalancesCall; -use pallet_pips::{ProposalState, SnapshotResult}; +use pallet_pips::{PipIdSequence, ProposalState, SnapshotResult}; use pallet_portfolio::Call as PortfolioCall; use pallet_utility::{ self as utility, Call as UtilityCall, Config as UtilityConfig, Event, UniqueCall, WeightInfo, @@ -898,9 +898,9 @@ fn batch_works_with_committee_origin() { ) }; - let id_committee = Pips::pip_id_sequence(); + let id_committee = PipIdSequence::::get(); assert_ok!(committee_proposal(0)); - let id_snapshot = Pips::pip_id_sequence(); + let id_snapshot = PipIdSequence::::get(); assert_ok!(community_proposal(proposer, 10)); assert_ok!(Pips::snapshot(bob.origin())); consensus_batch(vec![ @@ -932,9 +932,9 @@ fn force_batch_works_with_committee_origin() { ) }; - let id_committee = Pips::pip_id_sequence(); + let id_committee = PipIdSequence::::get(); assert_ok!(committee_proposal(0)); - let id_snapshot = Pips::pip_id_sequence(); + let id_snapshot = PipIdSequence::::get(); assert_ok!(community_proposal(proposer, 10)); assert_ok!(Pips::snapshot(bob.origin())); consensus_batch(vec![ @@ -966,9 +966,9 @@ fn batch_all_works_with_committee_origin() { ) }; - let id_committee = Pips::pip_id_sequence(); + let id_committee = PipIdSequence::::get(); assert_ok!(committee_proposal(0)); - let id_snapshot = Pips::pip_id_sequence(); + let id_snapshot = PipIdSequence::::get(); assert_ok!(community_proposal(proposer, 10)); assert_ok!(Pips::snapshot(bob.origin())); consensus_batch(vec![ diff --git a/pallets/weights/src/pallet_pips.rs b/pallets/weights/src/pallet_pips.rs index 911e80584..2ba7a31bb 100644 --- a/pallets/weights/src/pallet_pips.rs +++ b/pallets/weights/src/pallet_pips.rs @@ -18,19 +18,19 @@ //! Autogenerated weights for pallet_pips //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2024-11-03, STEPS: `100`, REPEAT: 5, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2025-01-20, STEPS: `100`, REPEAT: 5, LOW RANGE: `[]`, HIGH RANGE: `[]` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 512 -//! HOSTNAME: `ubuntu-8gb-nbg1-1-bench2`, CPU: `AMD EPYC-Milan Processor` +//! HOSTNAME: `HNDesktop`, CPU: `Intel(R) Core(TM) i5-10600K CPU @ 4.10GHz` // Executed Command: -// ./polymesh +// ./target/release/polymesh // benchmark // pallet // -s // 100 // -r // 5 -// -p=* +// -p=pallet_pips // -e=* // --heap-pages // 4096 @@ -41,9 +41,9 @@ // --wasm-execution // compiled // --output -// ./Polymesh/pallets/weights/src/ +// ./pallets/weights/src/ // --template -// ./Polymesh/.maintain/frame-weight-template.hbs +// ./.maintain/frame-weight-template.hbs #![allow(unused_parens)] #![allow(unused_imports)] @@ -54,103 +54,103 @@ use polymesh_runtime_common::{RocksDbWeight as DbWeight, Weight}; pub struct SubstrateWeight; impl pallet_pips::WeightInfo for SubstrateWeight { // Storage: Pips PruneHistoricalPips (r:1 w:1) - // Proof Skipped: Pips PruneHistoricalPips (max_values: Some(1), max_size: None, mode: Measured) + // Proof: Pips PruneHistoricalPips (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) fn set_prune_historical_pips() -> Weight { - // Minimum execution time: 13_651 nanoseconds. - Weight::from_ref_time(15_423_000) + // Minimum execution time: 17_519 nanoseconds. + Weight::from_ref_time(17_652_000) .saturating_add(DbWeight::get().reads(1)) .saturating_add(DbWeight::get().writes(1)) } // Storage: Pips MinimumProposalDeposit (r:1 w:1) - // Proof Skipped: Pips MinimumProposalDeposit (max_values: Some(1), max_size: None, mode: Measured) + // Proof: Pips MinimumProposalDeposit (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) fn set_min_proposal_deposit() -> Weight { - // Minimum execution time: 15_974 nanoseconds. - Weight::from_ref_time(18_488_000) + // Minimum execution time: 20_637 nanoseconds. + Weight::from_ref_time(21_244_000) .saturating_add(DbWeight::get().reads(1)) .saturating_add(DbWeight::get().writes(1)) } // Storage: Pips DefaultEnactmentPeriod (r:1 w:1) - // Proof Skipped: Pips DefaultEnactmentPeriod (max_values: Some(1), max_size: None, mode: Measured) + // Proof: Pips DefaultEnactmentPeriod (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn set_default_enactment_period() -> Weight { - // Minimum execution time: 16_775 nanoseconds. - Weight::from_ref_time(19_199_000) + // Minimum execution time: 20_395 nanoseconds. + Weight::from_ref_time(20_535_000) .saturating_add(DbWeight::get().reads(1)) .saturating_add(DbWeight::get().writes(1)) } // Storage: Pips PendingPipExpiry (r:1 w:1) // Proof Skipped: Pips PendingPipExpiry (max_values: Some(1), max_size: None, mode: Measured) fn set_pending_pip_expiry() -> Weight { - // Minimum execution time: 17_005 nanoseconds. - Weight::from_ref_time(19_139_000) + // Minimum execution time: 21_154 nanoseconds. + Weight::from_ref_time(21_741_000) .saturating_add(DbWeight::get().reads(1)) .saturating_add(DbWeight::get().writes(1)) } // Storage: Pips MaxPipSkipCount (r:1 w:1) - // Proof Skipped: Pips MaxPipSkipCount (max_values: Some(1), max_size: None, mode: Measured) + // Proof: Pips MaxPipSkipCount (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) fn set_max_pip_skip_count() -> Weight { - // Minimum execution time: 13_270 nanoseconds. - Weight::from_ref_time(13_480_000) + // Minimum execution time: 17_035 nanoseconds. + Weight::from_ref_time(17_344_000) .saturating_add(DbWeight::get().reads(1)) .saturating_add(DbWeight::get().writes(1)) } // Storage: Pips ActivePipLimit (r:1 w:1) - // Proof Skipped: Pips ActivePipLimit (max_values: Some(1), max_size: None, mode: Measured) + // Proof: Pips ActivePipLimit (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn set_active_pip_limit() -> Weight { - // Minimum execution time: 14_882 nanoseconds. - Weight::from_ref_time(15_773_000) + // Minimum execution time: 19_938 nanoseconds. + Weight::from_ref_time(20_366_000) .saturating_add(DbWeight::get().reads(1)) .saturating_add(DbWeight::get().writes(1)) } // Storage: Identity KeyRecords (r:1 w:0) - // Proof Skipped: Identity KeyRecords (max_values: None, max_size: None, mode: Measured) + // Proof: Identity KeyRecords (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) // Storage: Pips PipIdSequence (r:1 w:1) - // Proof Skipped: Pips PipIdSequence (max_values: Some(1), max_size: None, mode: Measured) + // Proof: Pips PipIdSequence (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) // Storage: Pips ActivePipLimit (r:1 w:0) - // Proof Skipped: Pips ActivePipLimit (max_values: Some(1), max_size: None, mode: Measured) + // Proof: Pips ActivePipLimit (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) // Storage: Pips ActivePipCount (r:1 w:1) - // Proof Skipped: Pips ActivePipCount (max_values: Some(1), max_size: None, mode: Measured) + // Proof: Pips ActivePipCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) // Storage: Pips MinimumProposalDeposit (r:1 w:0) - // Proof Skipped: Pips MinimumProposalDeposit (max_values: Some(1), max_size: None, mode: Measured) + // Proof: Pips MinimumProposalDeposit (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) // Storage: Balances Locks (r:1 w:1) // Proof Skipped: Balances Locks (max_values: None, max_size: None, mode: Measured) // Storage: System Account (r:1 w:1) // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) // Storage: ProtocolFee Coefficient (r:1 w:0) - // Proof Skipped: ProtocolFee Coefficient (max_values: Some(1), max_size: None, mode: Measured) + // Proof: ProtocolFee Coefficient (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) // Storage: ProtocolFee BaseFees (r:1 w:0) - // Proof Skipped: ProtocolFee BaseFees (max_values: None, max_size: None, mode: Measured) + // Proof: ProtocolFee BaseFees (max_values: None, max_size: Some(25), added: 2500, mode: MaxEncodedLen) // Storage: Pips PendingPipExpiry (r:1 w:0) // Proof Skipped: Pips PendingPipExpiry (max_values: Some(1), max_size: None, mode: Measured) // Storage: Pips ProposalResult (r:1 w:1) - // Proof Skipped: Pips ProposalResult (max_values: None, max_size: None, mode: Measured) + // Proof: Pips ProposalResult (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) // Storage: Pips ProposalVotes (r:1 w:1) - // Proof Skipped: Pips ProposalVotes (max_values: None, max_size: None, mode: Measured) + // Proof: Pips ProposalVotes (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) // Storage: Pips LiveQueue (r:1 w:1) // Proof Skipped: Pips LiveQueue (max_values: Some(1), max_size: None, mode: Measured) // Storage: Pips ProposalMetadata (r:0 w:1) // Proof Skipped: Pips ProposalMetadata (max_values: None, max_size: None, mode: Measured) // Storage: Pips Deposits (r:0 w:1) - // Proof Skipped: Pips Deposits (max_values: None, max_size: None, mode: Measured) + // Proof: Pips Deposits (max_values: None, max_size: Some(100), added: 2575, mode: MaxEncodedLen) // Storage: Pips Proposals (r:0 w:1) // Proof Skipped: Pips Proposals (max_values: None, max_size: None, mode: Measured) // Storage: Pips ProposalStates (r:0 w:1) - // Proof Skipped: Pips ProposalStates (max_values: None, max_size: None, mode: Measured) + // Proof: Pips ProposalStates (max_values: None, max_size: Some(13), added: 2488, mode: MaxEncodedLen) fn propose_from_community() -> Weight { - // Minimum execution time: 109_675 nanoseconds. - Weight::from_ref_time(112_487_000) + // Minimum execution time: 129_932 nanoseconds. + Weight::from_ref_time(130_345_000) .saturating_add(DbWeight::get().reads(13)) .saturating_add(DbWeight::get().writes(11)) } // Storage: Pips PipIdSequence (r:1 w:1) - // Proof Skipped: Pips PipIdSequence (max_values: Some(1), max_size: None, mode: Measured) + // Proof: Pips PipIdSequence (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) // Storage: ProtocolFee Coefficient (r:1 w:0) - // Proof Skipped: ProtocolFee Coefficient (max_values: Some(1), max_size: None, mode: Measured) + // Proof: ProtocolFee Coefficient (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) // Storage: ProtocolFee BaseFees (r:1 w:0) - // Proof Skipped: ProtocolFee BaseFees (max_values: None, max_size: None, mode: Measured) + // Proof: ProtocolFee BaseFees (max_values: None, max_size: Some(25), added: 2500, mode: MaxEncodedLen) // Storage: Pips PendingPipExpiry (r:1 w:0) // Proof Skipped: Pips PendingPipExpiry (max_values: Some(1), max_size: None, mode: Measured) // Storage: Pips ActivePipCount (r:1 w:1) - // Proof Skipped: Pips ActivePipCount (max_values: Some(1), max_size: None, mode: Measured) + // Proof: Pips ActivePipCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) // Storage: Pips CommitteePips (r:1 w:1) // Proof Skipped: Pips CommitteePips (max_values: Some(1), max_size: None, mode: Measured) // Storage: Pips ProposalMetadata (r:0 w:1) @@ -158,266 +158,278 @@ impl pallet_pips::WeightInfo for SubstrateWeight { // Storage: Pips Proposals (r:0 w:1) // Proof Skipped: Pips Proposals (max_values: None, max_size: None, mode: Measured) // Storage: Pips ProposalStates (r:0 w:1) - // Proof Skipped: Pips ProposalStates (max_values: None, max_size: None, mode: Measured) + // Proof: Pips ProposalStates (max_values: None, max_size: Some(13), added: 2488, mode: MaxEncodedLen) fn propose_from_committee() -> Weight { - // Minimum execution time: 53_711 nanoseconds. - Weight::from_ref_time(55_283_000) + // Minimum execution time: 65_140 nanoseconds. + Weight::from_ref_time(65_704_000) .saturating_add(DbWeight::get().reads(6)) .saturating_add(DbWeight::get().writes(6)) } // Storage: Identity KeyRecords (r:1 w:0) - // Proof Skipped: Identity KeyRecords (max_values: None, max_size: None, mode: Measured) + // Proof: Identity KeyRecords (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) // Storage: Pips Proposals (r:1 w:0) // Proof Skipped: Pips Proposals (max_values: None, max_size: None, mode: Measured) // Storage: Pips ProposalStates (r:1 w:0) - // Proof Skipped: Pips ProposalStates (max_values: None, max_size: None, mode: Measured) + // Proof: Pips ProposalStates (max_values: None, max_size: Some(13), added: 2488, mode: MaxEncodedLen) // Storage: Pips ProposalResult (r:1 w:1) - // Proof Skipped: Pips ProposalResult (max_values: None, max_size: None, mode: Measured) + // Proof: Pips ProposalResult (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) // Storage: Pips Deposits (r:1 w:1) - // Proof Skipped: Pips Deposits (max_values: None, max_size: None, mode: Measured) + // Proof: Pips Deposits (max_values: None, max_size: Some(100), added: 2575, mode: MaxEncodedLen) // Storage: Pips ProposalVotes (r:1 w:1) - // Proof Skipped: Pips ProposalVotes (max_values: None, max_size: None, mode: Measured) + // Proof: Pips ProposalVotes (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) // Storage: Pips LiveQueue (r:1 w:1) // Proof Skipped: Pips LiveQueue (max_values: Some(1), max_size: None, mode: Measured) fn vote() -> Weight { - // Minimum execution time: 97_947 nanoseconds. - Weight::from_ref_time(105_979_000) + // Minimum execution time: 108_219 nanoseconds. + Weight::from_ref_time(115_627_000) .saturating_add(DbWeight::get().reads(7)) .saturating_add(DbWeight::get().writes(4)) } // Storage: Pips ProposalStates (r:1 w:1) - // Proof Skipped: Pips ProposalStates (max_values: None, max_size: None, mode: Measured) + // Proof: Pips ProposalStates (max_values: None, max_size: Some(13), added: 2488, mode: MaxEncodedLen) // Storage: Pips Proposals (r:1 w:0) // Proof Skipped: Pips Proposals (max_values: None, max_size: None, mode: Measured) // Storage: Pips DefaultEnactmentPeriod (r:1 w:0) - // Proof Skipped: Pips DefaultEnactmentPeriod (max_values: Some(1), max_size: None, mode: Measured) + // Proof: Pips DefaultEnactmentPeriod (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) // Storage: Scheduler Lookup (r:1 w:1) // Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) // Storage: Scheduler Agenda (r:1 w:1) // Proof: Scheduler Agenda (max_values: None, max_size: Some(10463), added: 12938, mode: MaxEncodedLen) // Storage: Pips PipToSchedule (r:0 w:1) - // Proof Skipped: Pips PipToSchedule (max_values: None, max_size: None, mode: Measured) + // Proof: Pips PipToSchedule (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) fn approve_committee_proposal() -> Weight { - // Minimum execution time: 52_168 nanoseconds. - Weight::from_ref_time(59_849_000) + // Minimum execution time: 65_605 nanoseconds. + Weight::from_ref_time(67_197_000) .saturating_add(DbWeight::get().reads(5)) .saturating_add(DbWeight::get().writes(4)) } // Storage: Pips ProposalStates (r:1 w:1) - // Proof Skipped: Pips ProposalStates (max_values: None, max_size: None, mode: Measured) + // Proof: Pips ProposalStates (max_values: None, max_size: Some(13), added: 2488, mode: MaxEncodedLen) // Storage: Pips LiveQueue (r:1 w:1) // Proof Skipped: Pips LiveQueue (max_values: Some(1), max_size: None, mode: Measured) // Storage: Pips SnapshotMeta (r:1 w:0) - // Proof Skipped: Pips SnapshotMeta (max_values: Some(1), max_size: None, mode: Measured) + // Proof: Pips SnapshotMeta (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) // Storage: Pips ActivePipCount (r:1 w:1) - // Proof Skipped: Pips ActivePipCount (max_values: Some(1), max_size: None, mode: Measured) + // Proof: Pips ActivePipCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) // Storage: Pips PruneHistoricalPips (r:1 w:0) - // Proof Skipped: Pips PruneHistoricalPips (max_values: Some(1), max_size: None, mode: Measured) - // Storage: Pips Deposits (r:2 w:1) - // Proof Skipped: Pips Deposits (max_values: None, max_size: None, mode: Measured) - // Storage: Balances Locks (r:1 w:1) - // Proof Skipped: Balances Locks (max_values: None, max_size: None, mode: Measured) - // Storage: System Account (r:1 w:1) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - // Storage: Pips ProposalVotes (r:1 w:1) - // Proof Skipped: Pips ProposalVotes (max_values: None, max_size: None, mode: Measured) + // Proof: Pips PruneHistoricalPips (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) // Storage: Pips Proposals (r:1 w:1) // Proof Skipped: Pips Proposals (max_values: None, max_size: None, mode: Measured) // Storage: Pips ProposalMetadata (r:0 w:1) // Proof Skipped: Pips ProposalMetadata (max_values: None, max_size: None, mode: Measured) + // Storage: Pips PendingRefunds (r:0 w:1) + // Proof: Pips PendingRefunds (max_values: None, max_size: Some(13), added: 2488, mode: MaxEncodedLen) // Storage: Pips PipSkipCount (r:0 w:1) - // Proof Skipped: Pips PipSkipCount (max_values: None, max_size: None, mode: Measured) + // Proof: Pips PipSkipCount (max_values: None, max_size: Some(13), added: 2488, mode: MaxEncodedLen) + // Storage: Pips VotesToBePruned (r:0 w:1) + // Proof: Pips VotesToBePruned (max_values: None, max_size: Some(13), added: 2488, mode: MaxEncodedLen) // Storage: Pips ProposalResult (r:0 w:1) - // Proof Skipped: Pips ProposalResult (max_values: None, max_size: None, mode: Measured) + // Proof: Pips ProposalResult (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) fn reject_proposal() -> Weight { - // Minimum execution time: 95_354 nanoseconds. - Weight::from_ref_time(100_941_000) - .saturating_add(DbWeight::get().reads(11)) - .saturating_add(DbWeight::get().writes(11)) + // Minimum execution time: 85_835 nanoseconds. + Weight::from_ref_time(87_787_000) + .saturating_add(DbWeight::get().reads(6)) + .saturating_add(DbWeight::get().writes(9)) } // Storage: Pips ProposalStates (r:1 w:1) - // Proof Skipped: Pips ProposalStates (max_values: None, max_size: None, mode: Measured) - // Storage: Pips Deposits (r:1 w:0) - // Proof Skipped: Pips Deposits (max_values: None, max_size: None, mode: Measured) - // Storage: Pips ProposalVotes (r:1 w:1) - // Proof Skipped: Pips ProposalVotes (max_values: None, max_size: None, mode: Measured) + // Proof: Pips ProposalStates (max_values: None, max_size: Some(13), added: 2488, mode: MaxEncodedLen) // Storage: Pips Proposals (r:1 w:1) // Proof Skipped: Pips Proposals (max_values: None, max_size: None, mode: Measured) // Storage: Pips ProposalMetadata (r:0 w:1) // Proof Skipped: Pips ProposalMetadata (max_values: None, max_size: None, mode: Measured) + // Storage: Pips PendingRefunds (r:0 w:1) + // Proof: Pips PendingRefunds (max_values: None, max_size: Some(13), added: 2488, mode: MaxEncodedLen) // Storage: Pips PipSkipCount (r:0 w:1) - // Proof Skipped: Pips PipSkipCount (max_values: None, max_size: None, mode: Measured) + // Proof: Pips PipSkipCount (max_values: None, max_size: Some(13), added: 2488, mode: MaxEncodedLen) + // Storage: Pips VotesToBePruned (r:0 w:1) + // Proof: Pips VotesToBePruned (max_values: None, max_size: Some(13), added: 2488, mode: MaxEncodedLen) // Storage: Pips ProposalResult (r:0 w:1) - // Proof Skipped: Pips ProposalResult (max_values: None, max_size: None, mode: Measured) + // Proof: Pips ProposalResult (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) fn prune_proposal() -> Weight { - // Minimum execution time: 51_487 nanoseconds. - Weight::from_ref_time(53_941_000) - .saturating_add(DbWeight::get().reads(4)) - .saturating_add(DbWeight::get().writes(6)) + // Minimum execution time: 61_597 nanoseconds. + Weight::from_ref_time(62_681_000) + .saturating_add(DbWeight::get().reads(2)) + .saturating_add(DbWeight::get().writes(7)) } // Storage: Identity KeyRecords (r:1 w:0) - // Proof Skipped: Identity KeyRecords (max_values: None, max_size: None, mode: Measured) - // Storage: Instance1Committee ReleaseCoordinator (r:1 w:0) - // Proof Skipped: Instance1Committee ReleaseCoordinator (max_values: Some(1), max_size: None, mode: Measured) + // Proof: Identity KeyRecords (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + // Storage: PolymeshCommittee ReleaseCoordinator (r:1 w:0) + // Proof: PolymeshCommittee ReleaseCoordinator (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) // Storage: Pips ProposalStates (r:1 w:0) - // Proof Skipped: Pips ProposalStates (max_values: None, max_size: None, mode: Measured) + // Proof: Pips ProposalStates (max_values: None, max_size: Some(13), added: 2488, mode: MaxEncodedLen) // Storage: Scheduler Lookup (r:1 w:1) // Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) // Storage: Scheduler Agenda (r:2 w:2) // Proof: Scheduler Agenda (max_values: None, max_size: Some(10463), added: 12938, mode: MaxEncodedLen) // Storage: Pips PipToSchedule (r:0 w:1) - // Proof Skipped: Pips PipToSchedule (max_values: None, max_size: None, mode: Measured) + // Proof: Pips PipToSchedule (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) fn reschedule_execution() -> Weight { - // Minimum execution time: 66_459 nanoseconds. - Weight::from_ref_time(69_594_000) + // Minimum execution time: 77_090 nanoseconds. + Weight::from_ref_time(77_456_000) .saturating_add(DbWeight::get().reads(6)) .saturating_add(DbWeight::get().writes(4)) } // Storage: Identity KeyRecords (r:1 w:0) - // Proof Skipped: Identity KeyRecords (max_values: None, max_size: None, mode: Measured) - // Storage: Instance1Committee Members (r:1 w:0) - // Proof Skipped: Instance1Committee Members (max_values: Some(1), max_size: None, mode: Measured) + // Proof: Identity KeyRecords (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + // Storage: PolymeshCommittee Members (r:1 w:0) + // Proof Skipped: PolymeshCommittee Members (max_values: Some(1), max_size: None, mode: Measured) // Storage: Pips SnapshotMeta (r:1 w:1) - // Proof Skipped: Pips SnapshotMeta (max_values: Some(1), max_size: None, mode: Measured) + // Proof: Pips SnapshotMeta (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) // Storage: Pips SnapshotQueue (r:0 w:1) // Proof Skipped: Pips SnapshotQueue (max_values: Some(1), max_size: None, mode: Measured) fn clear_snapshot() -> Weight { - // Minimum execution time: 33_560 nanoseconds. - Weight::from_ref_time(36_505_000) + // Minimum execution time: 39_778 nanoseconds. + Weight::from_ref_time(40_192_000) .saturating_add(DbWeight::get().reads(3)) .saturating_add(DbWeight::get().writes(2)) } // Storage: Identity KeyRecords (r:1 w:0) - // Proof Skipped: Identity KeyRecords (max_values: None, max_size: None, mode: Measured) - // Storage: Instance1Committee Members (r:1 w:0) - // Proof Skipped: Instance1Committee Members (max_values: Some(1), max_size: None, mode: Measured) + // Proof: Identity KeyRecords (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + // Storage: PolymeshCommittee Members (r:1 w:0) + // Proof Skipped: PolymeshCommittee Members (max_values: Some(1), max_size: None, mode: Measured) // Storage: Pips SnapshotIdSequence (r:1 w:1) - // Proof Skipped: Pips SnapshotIdSequence (max_values: Some(1), max_size: None, mode: Measured) + // Proof: Pips SnapshotIdSequence (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) // Storage: Pips LiveQueue (r:1 w:0) // Proof Skipped: Pips LiveQueue (max_values: Some(1), max_size: None, mode: Measured) // Storage: Pips SnapshotQueue (r:0 w:1) // Proof Skipped: Pips SnapshotQueue (max_values: Some(1), max_size: None, mode: Measured) // Storage: Pips SnapshotMeta (r:0 w:1) - // Proof Skipped: Pips SnapshotMeta (max_values: Some(1), max_size: None, mode: Measured) + // Proof: Pips SnapshotMeta (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) fn snapshot() -> Weight { - // Minimum execution time: 97_827 nanoseconds. - Weight::from_ref_time(101_082_000) + // Minimum execution time: 62_355 nanoseconds. + Weight::from_ref_time(70_260_000) .saturating_add(DbWeight::get().reads(4)) .saturating_add(DbWeight::get().writes(3)) } // Storage: Pips MaxPipSkipCount (r:1 w:0) - // Proof Skipped: Pips MaxPipSkipCount (max_values: Some(1), max_size: None, mode: Measured) + // Proof: Pips MaxPipSkipCount (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) // Storage: Pips SnapshotQueue (r:1 w:1) // Proof Skipped: Pips SnapshotQueue (max_values: Some(1), max_size: None, mode: Measured) // Storage: Pips PipSkipCount (r:33 w:33) - // Proof Skipped: Pips PipSkipCount (max_values: None, max_size: None, mode: Measured) + // Proof: Pips PipSkipCount (max_values: None, max_size: Some(13), added: 2488, mode: MaxEncodedLen) // Storage: Pips LiveQueue (r:1 w:1) // Proof Skipped: Pips LiveQueue (max_values: Some(1), max_size: None, mode: Measured) // Storage: Pips ProposalStates (r:66 w:66) - // Proof Skipped: Pips ProposalStates (max_values: None, max_size: None, mode: Measured) + // Proof: Pips ProposalStates (max_values: None, max_size: Some(13), added: 2488, mode: MaxEncodedLen) // Storage: Pips ActivePipCount (r:1 w:1) - // Proof Skipped: Pips ActivePipCount (max_values: Some(1), max_size: None, mode: Measured) + // Proof: Pips ActivePipCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) // Storage: Pips PruneHistoricalPips (r:1 w:0) - // Proof Skipped: Pips PruneHistoricalPips (max_values: Some(1), max_size: None, mode: Measured) - // Storage: Pips Deposits (r:13233 w:13200) - // Proof Skipped: Pips Deposits (max_values: None, max_size: None, mode: Measured) - // Storage: Balances Locks (r:400 w:400) - // Proof Skipped: Balances Locks (max_values: None, max_size: None, mode: Measured) - // Storage: System Account (r:400 w:400) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Proof: Pips PruneHistoricalPips (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) // Storage: Pips DefaultEnactmentPeriod (r:1 w:0) - // Proof Skipped: Pips DefaultEnactmentPeriod (max_values: Some(1), max_size: None, mode: Measured) + // Proof: Pips DefaultEnactmentPeriod (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) // Storage: Scheduler Lookup (r:33 w:33) // Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) // Storage: Scheduler Agenda (r:1 w:1) // Proof: Scheduler Agenda (max_values: None, max_size: Some(10463), added: 12938, mode: MaxEncodedLen) // Storage: Pips SnapshotMeta (r:1 w:0) - // Proof Skipped: Pips SnapshotMeta (max_values: Some(1), max_size: None, mode: Measured) + // Proof: Pips SnapshotMeta (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) + // Storage: Pips PendingRefunds (r:0 w:33) + // Proof: Pips PendingRefunds (max_values: None, max_size: Some(13), added: 2488, mode: MaxEncodedLen) // Storage: Pips PipToSchedule (r:0 w:33) - // Proof Skipped: Pips PipToSchedule (max_values: None, max_size: None, mode: Measured) + // Proof: Pips PipToSchedule (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) /// The range of component `a` is `[0, 33]`. /// The range of component `r` is `[0, 33]`. /// The range of component `s` is `[0, 33]`. fn enact_snapshot_results(a: u32, r: u32, s: u32) -> Weight { - // Minimum execution time: 1_649_642 nanoseconds. - Weight::from_ref_time(1_657_344_000) - // Standard Error: 5_391_730 - .saturating_add(Weight::from_ref_time(54_095_498).saturating_mul(a.into())) - // Standard Error: 5_391_730 - .saturating_add(Weight::from_ref_time(7_306_498_155).saturating_mul(r.into())) - // Standard Error: 5_391_730 - .saturating_add(Weight::from_ref_time(13_777_923).saturating_mul(s.into())) - .saturating_add(DbWeight::get().reads(685)) - .saturating_add(DbWeight::get().reads((3_u64).saturating_mul(a.into()))) - .saturating_add(DbWeight::get().reads((405_u64).saturating_mul(r.into()))) - .saturating_add(DbWeight::get().reads((2_u64).saturating_mul(s.into()))) - .saturating_add(DbWeight::get().writes(681)) - .saturating_add(DbWeight::get().writes((4_u64).saturating_mul(a.into()))) - .saturating_add(DbWeight::get().writes((404_u64).saturating_mul(r.into()))) - .saturating_add(DbWeight::get().writes((2_u64).saturating_mul(s.into()))) + // Minimum execution time: 977_352 nanoseconds. + Weight::from_ref_time(159_135_641) + // Standard Error: 45_263 + .saturating_add(Weight::from_ref_time(38_431_937).saturating_mul(a.into())) + // Standard Error: 45_263 + .saturating_add(Weight::from_ref_time(16_926_524).saturating_mul(r.into())) + // Standard Error: 45_263 + .saturating_add(Weight::from_ref_time(7_970_390).saturating_mul(s.into())) + .saturating_add(DbWeight::get().reads(7)) + .saturating_add(DbWeight::get().reads((2_u64).saturating_mul(a.into()))) + .saturating_add(DbWeight::get().reads((1_u64).saturating_mul(r.into()))) + .saturating_add(DbWeight::get().reads((1_u64).saturating_mul(s.into()))) + .saturating_add(DbWeight::get().writes(4)) + .saturating_add(DbWeight::get().writes((3_u64).saturating_mul(a.into()))) + .saturating_add(DbWeight::get().writes((2_u64).saturating_mul(r.into()))) + .saturating_add(DbWeight::get().writes((1_u64).saturating_mul(s.into()))) } // Storage: Pips Proposals (r:1 w:1) // Proof Skipped: Pips Proposals (max_values: None, max_size: None, mode: Measured) // Storage: Pips ProposalStates (r:1 w:1) - // Proof Skipped: Pips ProposalStates (max_values: None, max_size: None, mode: Measured) + // Proof: Pips ProposalStates (max_values: None, max_size: Some(13), added: 2488, mode: MaxEncodedLen) // Storage: Pips ActivePipCount (r:1 w:1) - // Proof Skipped: Pips ActivePipCount (max_values: Some(1), max_size: None, mode: Measured) + // Proof: Pips ActivePipCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) // Storage: Pips PruneHistoricalPips (r:1 w:0) - // Proof Skipped: Pips PruneHistoricalPips (max_values: Some(1), max_size: None, mode: Measured) - // Storage: Pips Deposits (r:401 w:400) - // Proof Skipped: Pips Deposits (max_values: None, max_size: None, mode: Measured) - // Storage: Balances Locks (r:400 w:400) - // Proof Skipped: Balances Locks (max_values: None, max_size: None, mode: Measured) - // Storage: System Account (r:400 w:400) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - // Storage: Pips ProposalVotes (r:400 w:400) - // Proof Skipped: Pips ProposalVotes (max_values: None, max_size: None, mode: Measured) + // Proof: Pips PruneHistoricalPips (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) // Storage: Pips ProposalMetadata (r:0 w:1) // Proof Skipped: Pips ProposalMetadata (max_values: None, max_size: None, mode: Measured) + // Storage: Pips PendingRefunds (r:0 w:1) + // Proof: Pips PendingRefunds (max_values: None, max_size: Some(13), added: 2488, mode: MaxEncodedLen) // Storage: Pips PipSkipCount (r:0 w:1) - // Proof Skipped: Pips PipSkipCount (max_values: None, max_size: None, mode: Measured) + // Proof: Pips PipSkipCount (max_values: None, max_size: Some(13), added: 2488, mode: MaxEncodedLen) + // Storage: Pips VotesToBePruned (r:0 w:1) + // Proof: Pips VotesToBePruned (max_values: None, max_size: Some(13), added: 2488, mode: MaxEncodedLen) // Storage: Pips PipToSchedule (r:0 w:1) - // Proof Skipped: Pips PipToSchedule (max_values: None, max_size: None, mode: Measured) + // Proof: Pips PipToSchedule (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) // Storage: Pips ProposalResult (r:0 w:1) - // Proof Skipped: Pips ProposalResult (max_values: None, max_size: None, mode: Measured) + // Proof: Pips ProposalResult (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) fn execute_scheduled_pip() -> Weight { - // Minimum execution time: 11_167_440 nanoseconds. - Weight::from_ref_time(11_456_713_000) - .saturating_add(DbWeight::get().reads(1605)) - .saturating_add(DbWeight::get().writes(1607)) + // Minimum execution time: 110_098 nanoseconds. + Weight::from_ref_time(111_819_000) + .saturating_add(DbWeight::get().reads(4)) + .saturating_add(DbWeight::get().writes(9)) } // Storage: Pips ProposalStates (r:1 w:1) - // Proof Skipped: Pips ProposalStates (max_values: None, max_size: None, mode: Measured) + // Proof: Pips ProposalStates (max_values: None, max_size: Some(13), added: 2488, mode: MaxEncodedLen) // Storage: Pips LiveQueue (r:1 w:1) // Proof Skipped: Pips LiveQueue (max_values: Some(1), max_size: None, mode: Measured) // Storage: Pips SnapshotMeta (r:1 w:0) - // Proof Skipped: Pips SnapshotMeta (max_values: Some(1), max_size: None, mode: Measured) + // Proof: Pips SnapshotMeta (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) // Storage: Pips SnapshotQueue (r:1 w:1) // Proof Skipped: Pips SnapshotQueue (max_values: Some(1), max_size: None, mode: Measured) // Storage: Pips ActivePipCount (r:1 w:1) - // Proof Skipped: Pips ActivePipCount (max_values: Some(1), max_size: None, mode: Measured) + // Proof: Pips ActivePipCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) // Storage: Pips PruneHistoricalPips (r:1 w:0) - // Proof Skipped: Pips PruneHistoricalPips (max_values: Some(1), max_size: None, mode: Measured) - // Storage: Pips Deposits (r:401 w:400) - // Proof Skipped: Pips Deposits (max_values: None, max_size: None, mode: Measured) - // Storage: Balances Locks (r:400 w:400) - // Proof Skipped: Balances Locks (max_values: None, max_size: None, mode: Measured) - // Storage: System Account (r:400 w:400) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - // Storage: Pips ProposalVotes (r:400 w:400) - // Proof Skipped: Pips ProposalVotes (max_values: None, max_size: None, mode: Measured) + // Proof: Pips PruneHistoricalPips (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) // Storage: Pips Proposals (r:1 w:1) // Proof Skipped: Pips Proposals (max_values: None, max_size: None, mode: Measured) // Storage: Pips ProposalMetadata (r:0 w:1) // Proof Skipped: Pips ProposalMetadata (max_values: None, max_size: None, mode: Measured) + // Storage: Pips PendingRefunds (r:0 w:1) + // Proof: Pips PendingRefunds (max_values: None, max_size: Some(13), added: 2488, mode: MaxEncodedLen) // Storage: Pips PipSkipCount (r:0 w:1) - // Proof Skipped: Pips PipSkipCount (max_values: None, max_size: None, mode: Measured) + // Proof: Pips PipSkipCount (max_values: None, max_size: Some(13), added: 2488, mode: MaxEncodedLen) + // Storage: Pips VotesToBePruned (r:0 w:1) + // Proof: Pips VotesToBePruned (max_values: None, max_size: Some(13), added: 2488, mode: MaxEncodedLen) // Storage: Pips ProposalResult (r:0 w:1) - // Proof Skipped: Pips ProposalResult (max_values: None, max_size: None, mode: Measured) + // Proof: Pips ProposalResult (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) fn expire_scheduled_pip() -> Weight { - // Minimum execution time: 11_294_591 nanoseconds. - Weight::from_ref_time(11_411_686_000) - .saturating_add(DbWeight::get().reads(1608)) - .saturating_add(DbWeight::get().writes(1608)) + // Minimum execution time: 128_188 nanoseconds. + Weight::from_ref_time(129_249_000) + .saturating_add(DbWeight::get().reads(7)) + .saturating_add(DbWeight::get().writes(10)) + } + // Storage: Pips PendingRefunds (r:1 w:1) + // Proof: Pips PendingRefunds (max_values: None, max_size: Some(13), added: 2488, mode: MaxEncodedLen) + // Storage: Pips Deposits (r:129 w:128) + // Proof: Pips Deposits (max_values: None, max_size: Some(100), added: 2575, mode: MaxEncodedLen) + // Storage: Balances Locks (r:128 w:128) + // Proof Skipped: Balances Locks (max_values: None, max_size: None, mode: Measured) + // Storage: System Account (r:128 w:128) + // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: Pips VotesToBePruned (r:1 w:1) + // Proof: Pips VotesToBePruned (max_values: None, max_size: Some(13), added: 2488, mode: MaxEncodedLen) + // Storage: Pips ProposalVotes (r:129 w:128) + // Proof: Pips ProposalVotes (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// The range of component `r` is `[0, 128]`. + /// The range of component `v` is `[0, 128]`. + fn remove_pending_storage(r: u32, v: u32) -> Weight { + // Minimum execution time: 739_279 nanoseconds. + Weight::from_ref_time(63_144_485) + // Standard Error: 18_226 + .saturating_add(Weight::from_ref_time(24_421_417).saturating_mul(r.into())) + // Standard Error: 18_226 + .saturating_add(Weight::from_ref_time(5_613_829).saturating_mul(v.into())) + .saturating_add(DbWeight::get().reads(4)) + .saturating_add(DbWeight::get().reads((3_u64).saturating_mul(r.into()))) + .saturating_add(DbWeight::get().reads((1_u64).saturating_mul(v.into()))) + .saturating_add(DbWeight::get().writes(2)) + .saturating_add(DbWeight::get().writes((3_u64).saturating_mul(r.into()))) + .saturating_add(DbWeight::get().writes((1_u64).saturating_mul(v.into()))) } }