diff --git a/substrate/frame/society/src/lib.rs b/substrate/frame/society/src/lib.rs index c6d63eed20..83b1c42037 100644 --- a/substrate/frame/society/src/lib.rs +++ b/substrate/frame/society/src/lib.rs @@ -15,14 +15,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! # Society Module +//! # Society Pallet //! //! - [`Config`] //! - [`Call`] //! //! ## Overview //! -//! The Society module is an economic game which incentivizes users to participate +//! The Society pallet is an economic game which incentivizes users to participate //! and maintain a membership society. //! //! ### User Types @@ -77,7 +77,7 @@ //! #### Society Treasury //! //! The membership society is independently funded by a treasury managed by this -//! module. Some subset of this treasury is placed in a Society Pot, which is used +//! pallet. Some subset of this treasury is placed in a Society Pot, which is used //! to determine the number of accepted bids. //! //! #### Rate of Growth @@ -132,7 +132,7 @@ //! the society. A vouching bid can additionally request some portion of that reward as a tip //! to the voucher for vouching for the prospective candidate. //! -//! Every rotation period, Bids are ordered by reward amount, and the module +//! Every rotation period, Bids are ordered by reward amount, and the pallet //! selects as many bids the Society Pot can support for that period. //! //! These selected bids become candidates and move on to the Candidate phase. @@ -251,19 +251,15 @@ mod mock; #[cfg(test)] mod tests; -use codec::{Decode, Encode}; use frame_support::{ - decl_error, decl_event, decl_module, decl_storage, - dispatch::DispatchResult, - ensure, + pallet_prelude::*, traits::{ BalanceStatus, ChangeMembers, Currency, EnsureOrigin, ExistenceRequirement::AllowDeath, - Get, Imbalance, OnUnbalanced, Randomness, ReservableCurrency, + Imbalance, OnUnbalanced, Randomness, ReservableCurrency, }, - weights::Weight, PalletId, }; -use frame_system::{self as system, ensure_root, ensure_signed}; +use frame_system::pallet_prelude::*; use rand_chacha::{ rand_core::{RngCore, SeedableRng}, ChaChaRng, @@ -278,62 +274,14 @@ use sp_runtime::{ }; use sp_std::prelude::*; +pub use pallet::*; + type BalanceOf = - <>::Currency as Currency<::AccountId>>::Balance; -type NegativeImbalanceOf = <::Currency as Currency< + <>::Currency as Currency<::AccountId>>::Balance; +type NegativeImbalanceOf = <>::Currency as Currency< ::AccountId, >>::NegativeImbalance; -/// The module's configuration trait. -pub trait Config: system::Config { - /// The overarching event type. - type Event: From> + Into<::Event>; - - /// The societies's module id - type PalletId: Get; - - /// The currency type used for bidding. - type Currency: ReservableCurrency; - - /// Something that provides randomness in the runtime. - type Randomness: Randomness; - - /// The minimum amount of a deposit required for a bid to be made. - type CandidateDeposit: Get>; - - /// The amount of the unpaid reward that gets deducted in the case that either a skeptic - /// doesn't vote or someone votes in the wrong way. - type WrongSideDeduction: Get>; - - /// The number of times a member may vote the wrong way (or not at all, when they are a skeptic) - /// before they become suspended. - type MaxStrikes: Get; - - /// The amount of incentive paid within each period. Doesn't include VoterTip. - type PeriodSpend: Get>; - - /// The receiver of the signal for when the members have changed. - type MembershipChanged: ChangeMembers; - - /// The number of blocks between candidate/membership rotation periods. - type RotationPeriod: Get; - - /// The maximum duration of the payout lock. - type MaxLockDuration: Get; - - /// The origin that is allowed to call `found`. - type FounderSetOrigin: EnsureOrigin; - - /// The origin that is allowed to make suspension judgements. - type SuspensionJudgementOrigin: EnsureOrigin; - - /// The number of blocks between membership challenges. - type ChallengePeriod: Get; - - /// The maximum number of candidates that we accept per round. - type MaxCandidateIntake: Get; -} - /// A vote by a member on a candidate application. #[derive(Encode, Decode, Copy, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] pub enum Vote { @@ -417,108 +365,320 @@ impl BidKind { } } -// This module's storage items. -decl_storage! { - trait Store for Module, I: Instance=DefaultInstance> as Society { - /// The first member. - pub Founder get(fn founder) build(|config: &GenesisConfig| config.members.first().cloned()): - Option; +#[frame_support::pallet] +pub mod pallet { + use super::*; - /// A hash of the rules of this society concerning membership. Can only be set once and - /// only by the founder. - pub Rules get(fn rules): Option; + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(_); - /// The current set of candidates; bidders that are attempting to become members. - pub Candidates get(fn candidates): Vec>>; + #[pallet::config] + pub trait Config: frame_system::Config { + /// The overarching event type. + type Event: From> + IsType<::Event>; - /// The set of suspended candidates. - pub SuspendedCandidates get(fn suspended_candidate): - map hasher(twox_64_concat) T::AccountId - => Option<(BalanceOf, BidKind>)>; + /// The societies's pallet id + #[pallet::constant] + type PalletId: Get; - /// Amount of our account balance that is specifically for the next round's bid(s). - pub Pot get(fn pot) config(): BalanceOf; + /// The currency type used for bidding. + type Currency: ReservableCurrency; - /// The most primary from the most recently approved members. - pub Head get(fn head) build(|config: &GenesisConfig| config.members.first().cloned()): - Option; + /// Something that provides randomness in the runtime. + type Randomness: Randomness; - /// The current set of members, ordered. - pub Members get(fn members) build(|config: &GenesisConfig| { - let mut m = config.members.clone(); - m.sort(); - m - }): Vec; - - /// The set of suspended members. - pub SuspendedMembers get(fn suspended_member): map hasher(twox_64_concat) T::AccountId => bool; - - /// The current bids, stored ordered by the value of the bid. - Bids: Vec>>; - - /// Members currently vouching or banned from vouching again - Vouching get(fn vouching): map hasher(twox_64_concat) T::AccountId => Option; - - /// Pending payouts; ordered by block number, with the amount that should be paid out. - Payouts: map hasher(twox_64_concat) T::AccountId => Vec<(T::BlockNumber, BalanceOf)>; - - /// The ongoing number of losing votes cast by the member. - Strikes: map hasher(twox_64_concat) T::AccountId => StrikeCount; - - /// Double map from Candidate -> Voter -> (Maybe) Vote. - Votes: double_map - hasher(twox_64_concat) T::AccountId, - hasher(twox_64_concat) T::AccountId - => Option; - - /// The defending member currently being challenged. - Defender get(fn defender): Option; - - /// Votes for the defender. - DefenderVotes: map hasher(twox_64_concat) T::AccountId => Option; - - /// The max number of members for the society at one time. - MaxMembers get(fn max_members) config(): u32; - } - add_extra_genesis { - config(members): Vec; - } -} - -// The module's dispatchable functions. -decl_module! { - /// The module declaration. - pub struct Module, I: Instance=DefaultInstance> for enum Call where origin: T::Origin { - type Error = Error; /// The minimum amount of a deposit required for a bid to be made. - const CandidateDeposit: BalanceOf = T::CandidateDeposit::get(); + #[pallet::constant] + type CandidateDeposit: Get>; /// The amount of the unpaid reward that gets deducted in the case that either a skeptic /// doesn't vote or someone votes in the wrong way. - const WrongSideDeduction: BalanceOf = T::WrongSideDeduction::get(); + #[pallet::constant] + type WrongSideDeduction: Get>; - /// The number of times a member may vote the wrong way (or not at all, when they are a skeptic) - /// before they become suspended. - const MaxStrikes: u32 = T::MaxStrikes::get(); + /// The number of times a member may vote the wrong way (or not at all, when they are a + /// skeptic) before they become suspended. + #[pallet::constant] + type MaxStrikes: Get; /// The amount of incentive paid within each period. Doesn't include VoterTip. - const PeriodSpend: BalanceOf = T::PeriodSpend::get(); + #[pallet::constant] + type PeriodSpend: Get>; + + /// The receiver of the signal for when the members have changed. + type MembershipChanged: ChangeMembers; /// The number of blocks between candidate/membership rotation periods. - const RotationPeriod: T::BlockNumber = T::RotationPeriod::get(); + #[pallet::constant] + type RotationPeriod: Get; + + /// The maximum duration of the payout lock. + #[pallet::constant] + type MaxLockDuration: Get; + + /// The origin that is allowed to call `found`. + type FounderSetOrigin: EnsureOrigin; + + /// The origin that is allowed to make suspension judgements. + type SuspensionJudgementOrigin: EnsureOrigin; /// The number of blocks between membership challenges. - const ChallengePeriod: T::BlockNumber = T::ChallengePeriod::get(); + #[pallet::constant] + type ChallengePeriod: Get; - /// The societies's module id - const PalletId: PalletId = T::PalletId::get(); + /// The maximum number of candidates that we accept per round. + #[pallet::constant] + type MaxCandidateIntake: Get; + } - /// Maximum candidate intake per round. - const MaxCandidateIntake: u32 = T::MaxCandidateIntake::get(); + #[pallet::error] + pub enum Error { + /// An incorrect position was provided. + BadPosition, + /// User is not a member. + NotMember, + /// User is already a member. + AlreadyMember, + /// User is suspended. + Suspended, + /// User is not suspended. + NotSuspended, + /// Nothing to payout. + NoPayout, + /// Society already founded. + AlreadyFounded, + /// Not enough in pot to accept candidate. + InsufficientPot, + /// Member is already vouching or banned from vouching again. + AlreadyVouching, + /// Member is not vouching. + NotVouching, + /// Cannot remove the head of the chain. + Head, + /// Cannot remove the founder. + Founder, + /// User has already made a bid. + AlreadyBid, + /// User is already a candidate. + AlreadyCandidate, + /// User is not a candidate. + NotCandidate, + /// Too many members in the society. + MaxMembers, + /// The caller is not the founder. + NotFounder, + /// The caller is not the head. + NotHead, + } - // Used for handling module events. - fn deposit_event() = default; + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event, I: 'static = ()> { + /// The society is founded by the given identity. \[founder\] + Founded(T::AccountId), + /// A membership bid just happened. The given account is the candidate's ID and their offer + /// is the second. \[candidate_id, offer\] + Bid(T::AccountId, BalanceOf), + /// A membership bid just happened by vouching. The given account is the candidate's ID and + /// their offer is the second. The vouching party is the third. \[candidate_id, offer, + /// vouching\] + Vouch(T::AccountId, BalanceOf, T::AccountId), + /// A \[candidate\] was dropped (due to an excess of bids in the system). + AutoUnbid(T::AccountId), + /// A \[candidate\] was dropped (by their request). + Unbid(T::AccountId), + /// A \[candidate\] was dropped (by request of who vouched for them). + Unvouch(T::AccountId), + /// A group of candidates have been inducted. The batch's primary is the first value, the + /// batch in full is the second. \[primary, candidates\] + Inducted(T::AccountId, Vec), + /// A suspended member has been judged. \[who, judged\] + SuspendedMemberJudgement(T::AccountId, bool), + /// A \[candidate\] has been suspended + CandidateSuspended(T::AccountId), + /// A \[member\] has been suspended + MemberSuspended(T::AccountId), + /// A \[member\] has been challenged + Challenged(T::AccountId), + /// A vote has been placed \[candidate, voter, vote\] + Vote(T::AccountId, T::AccountId, bool), + /// A vote has been placed for a defending member \[voter, vote\] + DefenderVote(T::AccountId, bool), + /// A new \[max\] member count has been set + NewMaxMembers(u32), + /// Society is unfounded. \[founder\] + Unfounded(T::AccountId), + /// Some funds were deposited into the society account. \[value\] + Deposit(BalanceOf), + } + /// Old name generated by `decl_event`. + #[deprecated(note = "use `Event` instead")] + pub type RawEvent = Event; + + /// The first member. + #[pallet::storage] + #[pallet::getter(fn founder)] + pub type Founder, I: 'static = ()> = StorageValue<_, T::AccountId>; + + /// A hash of the rules of this society concerning membership. Can only be set once and + /// only by the founder. + #[pallet::storage] + #[pallet::getter(fn rules)] + pub type Rules, I: 'static = ()> = StorageValue<_, T::Hash>; + + /// The current set of candidates; bidders that are attempting to become members. + #[pallet::storage] + #[pallet::getter(fn candidates)] + pub type Candidates, I: 'static = ()> = + StorageValue<_, Vec>>, ValueQuery>; + + /// The set of suspended candidates. + #[pallet::storage] + #[pallet::getter(fn suspended_candidate)] + pub type SuspendedCandidates, I: 'static = ()> = StorageMap< + _, + Twox64Concat, + T::AccountId, + (BalanceOf, BidKind>), + >; + + /// Amount of our account balance that is specifically for the next round's bid(s). + #[pallet::storage] + #[pallet::getter(fn pot)] + pub type Pot, I: 'static = ()> = StorageValue<_, BalanceOf, ValueQuery>; + + /// The most primary from the most recently approved members. + #[pallet::storage] + #[pallet::getter(fn head)] + pub type Head, I: 'static = ()> = StorageValue<_, T::AccountId>; + + /// The current set of members, ordered. + #[pallet::storage] + #[pallet::getter(fn members)] + pub type Members, I: 'static = ()> = + StorageValue<_, Vec, ValueQuery>; + + /// The set of suspended members. + #[pallet::storage] + #[pallet::getter(fn suspended_member)] + pub type SuspendedMembers, I: 'static = ()> = + StorageMap<_, Twox64Concat, T::AccountId, bool, ValueQuery>; + + /// The current bids, stored ordered by the value of the bid. + #[pallet::storage] + pub(super) type Bids, I: 'static = ()> = + StorageValue<_, Vec>>, ValueQuery>; + + /// Members currently vouching or banned from vouching again + #[pallet::storage] + #[pallet::getter(fn vouching)] + pub(super) type Vouching, I: 'static = ()> = + StorageMap<_, Twox64Concat, T::AccountId, VouchingStatus>; + + /// Pending payouts; ordered by block number, with the amount that should be paid out. + #[pallet::storage] + pub(super) type Payouts, I: 'static = ()> = StorageMap< + _, + Twox64Concat, + T::AccountId, + Vec<(T::BlockNumber, BalanceOf)>, + ValueQuery, + >; + + /// The ongoing number of losing votes cast by the member. + #[pallet::storage] + pub(super) type Strikes, I: 'static = ()> = + StorageMap<_, Twox64Concat, T::AccountId, StrikeCount, ValueQuery>; + + /// Double map from Candidate -> Voter -> (Maybe) Vote. + #[pallet::storage] + pub(super) type Votes, I: 'static = ()> = + StorageDoubleMap<_, Twox64Concat, T::AccountId, Twox64Concat, T::AccountId, Vote>; + + /// The defending member currently being challenged. + #[pallet::storage] + #[pallet::getter(fn defender)] + pub(super) type Defender, I: 'static = ()> = StorageValue<_, T::AccountId>; + + /// Votes for the defender. + #[pallet::storage] + pub(super) type DefenderVotes, I: 'static = ()> = + StorageMap<_, Twox64Concat, T::AccountId, Vote>; + + /// The max number of members for the society at one time. + #[pallet::storage] + #[pallet::getter(fn max_members)] + pub(super) type MaxMembers, I: 'static = ()> = StorageValue<_, u32, ValueQuery>; + + #[pallet::hooks] + impl, I: 'static> Hooks> for Pallet { + fn on_initialize(n: T::BlockNumber) -> Weight { + let mut members = vec![]; + + let mut weight = 0; + let weights = T::BlockWeights::get(); + + // Run a candidate/membership rotation + if (n % T::RotationPeriod::get()).is_zero() { + members = >::get(); + Self::rotate_period(&mut members); + + weight += weights.max_block / 20; + } + + // Run a challenge rotation + if (n % T::ChallengePeriod::get()).is_zero() { + // Only read members if not already read. + if members.is_empty() { + members = >::get(); + } + Self::rotate_challenge(&mut members); + + weight += weights.max_block / 20; + } + + weight + } + } + + #[pallet::genesis_config] + pub struct GenesisConfig, I: 'static = ()> { + pub pot: BalanceOf, + pub members: Vec, + pub max_members: u32, + } + + #[cfg(feature = "std")] + impl, I: 'static> Default for GenesisConfig { + fn default() -> Self { + Self { + pot: Default::default(), + members: Default::default(), + max_members: Default::default(), + } + } + } + + #[pallet::genesis_build] + impl, I: 'static> GenesisBuild for GenesisConfig { + fn build(&self) { + Pot::::put(self.pot); + MaxMembers::::put(self.max_members); + let first_member = self.members.first(); + if let Some(member) = first_member { + Founder::::put(member.clone()); + Head::::put(member.clone()); + }; + let mut m = self.members.clone(); + m.sort(); + Members::::put(m); + } + } + + #[pallet::call] + impl, I: 'static> Pallet { /// A user outside of the society can make a bid for entry. /// /// Payment: `CandidateDeposit` will be reserved for making a bid. It is returned @@ -538,12 +698,13 @@ decl_module! { /// - One storage read to retrieve all current candidates. O(C) /// - One storage read to retrieve all members. O(M) /// - Storage Writes: - /// - One storage mutate to add a new bid to the vector O(B) (TODO: possible optimization w/ read) + /// - One storage mutate to add a new bid to the vector O(B) (TODO: possible optimization + /// w/ read) /// - Up to one storage removal if bid.len() > MAX_BID_COUNT. O(1) /// - Notable Computation: /// - O(B + C + log M) search to check user is not already a part of society. /// - O(log B) search to insert the new bid sorted. - /// - External Module Operations: + /// - External Pallet Operations: /// - One balance reserve operation. O(X) /// - Up to one balance unreserve operation if bids.len() > MAX_BID_COUNT. /// - Events: @@ -552,8 +713,8 @@ decl_module! { /// /// Total Complexity: O(M + B + C + logM + logB + X) /// # - #[weight = T::BlockWeights::get().max_block / 10] - pub fn bid(origin, value: BalanceOf) -> DispatchResult { + #[pallet::weight(T::BlockWeights::get().max_block / 10)] + pub fn bid(origin: OriginFor, value: BalanceOf) -> DispatchResult { let who = ensure_signed(origin)?; ensure!(!>::contains_key(&who), Error::::Suspended); ensure!(!>::contains_key(&who), Error::::Suspended); @@ -562,13 +723,13 @@ decl_module! { let candidates = >::get(); ensure!(!Self::is_candidate(&candidates, &who), Error::::AlreadyCandidate); let members = >::get(); - ensure!(!Self::is_member(&members ,&who), Error::::AlreadyMember); + ensure!(!Self::is_member(&members, &who), Error::::AlreadyMember); let deposit = T::CandidateDeposit::get(); T::Currency::reserve(&who, deposit)?; Self::put_bid(bids, &who, value.clone(), BidKind::Deposit(deposit)); - Self::deposit_event(RawEvent::Bid(who, value)); + Self::deposit_event(Event::::Bid(who, value)); Ok(()) } @@ -591,12 +752,12 @@ decl_module! { /// /// Total Complexity: O(B + X) /// # - #[weight = T::BlockWeights::get().max_block / 10] - pub fn unbid(origin, pos: u32) -> DispatchResult { + #[pallet::weight(T::BlockWeights::get().max_block / 10)] + pub fn unbid(origin: OriginFor, pos: u32) -> DispatchResult { let who = ensure_signed(origin)?; let pos = pos as usize; - >::mutate(|b| + >::mutate(|b| { if pos < b.len() && b[pos].who == who { // Either unreserve the deposit or free up the vouching member. // In neither case can we do much if the action isn't completable, but there's @@ -605,17 +766,17 @@ decl_module! { BidKind::Deposit(deposit) => { let err_amount = T::Currency::unreserve(&who, deposit); debug_assert!(err_amount.is_zero()); - } + }, BidKind::Vouch(voucher, _) => { >::remove(&voucher); - } + }, } - Self::deposit_event(RawEvent::Unbid(who)); + Self::deposit_event(Event::::Unbid(who)); Ok(()) } else { Err(Error::::BadPosition)? } - ) + }) } /// As a member, vouch for someone to join society by placing a bid on their behalf. @@ -647,13 +808,14 @@ decl_module! { /// - One storage read to retrieve all current candidates. O(C) /// - Storage Writes: /// - One storage write to insert vouching status to the member. O(1) - /// - One storage mutate to add a new bid to the vector O(B) (TODO: possible optimization w/ read) + /// - One storage mutate to add a new bid to the vector O(B) (TODO: possible optimization + /// w/ read) /// - Up to one storage removal if bid.len() > MAX_BID_COUNT. O(1) /// - Notable Computation: /// - O(log M) search to check sender is a member. /// - O(B + C + log M) search to check user is not already a part of society. /// - O(log B) search to insert the new bid sorted. - /// - External Module Operations: + /// - External Pallet Operations: /// - One balance reserve operation. O(X) /// - Up to one balance unreserve operation if bids.len() > MAX_BID_COUNT. /// - Events: @@ -662,8 +824,13 @@ decl_module! { /// /// Total Complexity: O(M + B + C + logM + logB + X) /// # - #[weight = T::BlockWeights::get().max_block / 10] - pub fn vouch(origin, who: T::AccountId, value: BalanceOf, tip: BalanceOf) -> DispatchResult { + #[pallet::weight(T::BlockWeights::get().max_block / 10)] + pub fn vouch( + origin: OriginFor, + who: T::AccountId, + value: BalanceOf, + tip: BalanceOf, + ) -> DispatchResult { let voucher = ensure_signed(origin)?; // Check user is not suspended. ensure!(!>::contains_key(&who), Error::::Suspended); @@ -682,7 +849,7 @@ decl_module! { >::insert(&voucher, VouchingStatus::Vouching); Self::put_bid(bids, &who, value.clone(), BidKind::Vouch(voucher.clone(), tip)); - Self::deposit_event(RawEvent::Vouch(who, value, voucher)); + Self::deposit_event(Event::::Vouch(who, value, voucher)); Ok(()) } @@ -703,23 +870,26 @@ decl_module! { /// /// Total Complexity: O(B) /// # - #[weight = T::BlockWeights::get().max_block / 10] - pub fn unvouch(origin, pos: u32) -> DispatchResult { + #[pallet::weight(T::BlockWeights::get().max_block / 10)] + pub fn unvouch(origin: OriginFor, pos: u32) -> DispatchResult { let voucher = ensure_signed(origin)?; - ensure!(Self::vouching(&voucher) == Some(VouchingStatus::Vouching), Error::::NotVouching); + ensure!( + Self::vouching(&voucher) == Some(VouchingStatus::Vouching), + Error::::NotVouching + ); let pos = pos as usize; - >::mutate(|b| + >::mutate(|b| { if pos < b.len() { b[pos].kind.check_voucher(&voucher)?; >::remove(&voucher); let who = b.remove(pos).who; - Self::deposit_event(RawEvent::Unvouch(who)); + Self::deposit_event(Event::::Unvouch(who)); Ok(()) } else { Err(Error::::BadPosition)? } - ) + }) } /// As a member, vote on a candidate. @@ -728,8 +898,8 @@ decl_module! { /// /// Parameters: /// - `candidate`: The candidate that the member would like to bid on. - /// - `approve`: A boolean which says if the candidate should be - /// approved (`true`) or rejected (`false`). + /// - `approve`: A boolean which says if the candidate should be approved (`true`) or + /// rejected (`false`). /// /// # /// Key: C (len of candidates), M (len of members) @@ -741,8 +911,12 @@ decl_module! { /// /// Total Complexity: O(M + logM + C) /// # - #[weight = T::BlockWeights::get().max_block / 10] - pub fn vote(origin, candidate: ::Source, approve: bool) { + #[pallet::weight(T::BlockWeights::get().max_block / 10)] + pub fn vote( + origin: OriginFor, + candidate: ::Source, + approve: bool, + ) -> DispatchResult { let voter = ensure_signed(origin)?; let candidate = T::Lookup::lookup(candidate)?; let candidates = >::get(); @@ -753,7 +927,8 @@ decl_module! { let vote = if approve { Vote::Approve } else { Vote::Reject }; >::insert(&candidate, &voter, vote); - Self::deposit_event(RawEvent::Vote(candidate, voter, approve)); + Self::deposit_event(Event::::Vote(candidate, voter, approve)); + Ok(()) } /// As a member, vote on the defender. @@ -772,8 +947,8 @@ decl_module! { /// /// Total Complexity: O(M + logM) /// # - #[weight = T::BlockWeights::get().max_block / 10] - pub fn defender_vote(origin, approve: bool) { + #[pallet::weight(T::BlockWeights::get().max_block / 10)] + pub fn defender_vote(origin: OriginFor, approve: bool) -> DispatchResult { let voter = ensure_signed(origin)?; let members = >::get(); ensure!(Self::is_member(&members, &voter), Error::::NotMember); @@ -781,12 +956,14 @@ decl_module! { let vote = if approve { Vote::Approve } else { Vote::Reject }; >::insert(&voter, vote); - Self::deposit_event(RawEvent::DefenderVote(voter, approve)); + Self::deposit_event(Event::::DefenderVote(voter, approve)); + Ok(()) } /// Transfer the first matured payout for the sender and remove it from the records. /// - /// NOTE: This extrinsic needs to be called multiple times to claim multiple matured payouts. + /// NOTE: This extrinsic needs to be called multiple times to claim multiple matured + /// payouts. /// /// Payment: The member will receive a payment equal to their first matured /// payout to their free balance. @@ -804,8 +981,8 @@ decl_module! { /// /// Total Complexity: O(M + logM + P + X) /// # - #[weight = T::BlockWeights::get().max_block / 10] - pub fn payout(origin) { + #[pallet::weight(T::BlockWeights::get().max_block / 10)] + pub fn payout(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; let members = >::get(); @@ -813,7 +990,7 @@ decl_module! { let mut payouts = >::get(&who); if let Some((when, amount)) = payouts.first() { - if when <= &>::block_number() { + if when <= &>::block_number() { T::Currency::transfer(&Self::payouts(), &who, *amount, AllowDeath)?; payouts.remove(0); if payouts.is_empty() { @@ -830,7 +1007,7 @@ decl_module! { /// Found the society. /// /// This is done as a discrete action in order to allow for the - /// module to be included into a running chain and can only be done once. + /// pallet to be included into a running chain and can only be done once. /// /// The dispatch origin for this call must be from the _FounderSetOrigin_. /// @@ -846,18 +1023,24 @@ decl_module! { /// /// Total Complexity: O(1) /// # - #[weight = T::BlockWeights::get().max_block / 10] - fn found(origin, founder: T::AccountId, max_members: u32, rules: Vec) { + #[pallet::weight(T::BlockWeights::get().max_block / 10)] + pub fn found( + origin: OriginFor, + founder: T::AccountId, + max_members: u32, + rules: Vec, + ) -> DispatchResult { T::FounderSetOrigin::ensure_origin(origin)?; ensure!(!>::exists(), Error::::AlreadyFounded); ensure!(max_members > 1, Error::::MaxMembers); // This should never fail in the context of this function... - >::put(max_members); + >::put(max_members); Self::add_member(&founder)?; >::put(&founder); >::put(&founder); Rules::::put(T::Hashing::hash(&rules)); - Self::deposit_event(RawEvent::Founded(founder)); + Self::deposit_event(Event::::Founded(founder)); + Ok(()) } /// Annul the founding of the society. @@ -873,8 +1056,8 @@ decl_module! { /// /// Total Complexity: O(1) /// # - #[weight = T::BlockWeights::get().max_block / 10] - fn unfound(origin) { + #[pallet::weight(T::BlockWeights::get().max_block / 10)] + pub fn unfound(origin: OriginFor) -> DispatchResult { let founder = ensure_signed(origin)?; ensure!(Founder::::get() == Some(founder.clone()), Error::::NotFounder); ensure!(Head::::get() == Some(founder.clone()), Error::::NotHead); @@ -885,7 +1068,8 @@ decl_module! { Rules::::kill(); Candidates::::kill(); SuspendedCandidates::::remove_all(None); - Self::deposit_event(RawEvent::Unfounded(founder)); + Self::deposit_event(Event::::Unfounded(founder)); + Ok(()) } /// Allow suspension judgement origin to make judgement on a suspended member. @@ -900,13 +1084,14 @@ decl_module! { /// /// Parameters: /// - `who` - The suspended member to be judged. - /// - `forgive` - A boolean representing whether the suspension judgement origin - /// forgives (`true`) or rejects (`false`) a suspended member. + /// - `forgive` - A boolean representing whether the suspension judgement origin forgives + /// (`true`) or rejects (`false`) a suspended member. /// /// # /// Key: B (len of bids), M (len of members) /// - One storage read to check `who` is a suspended member. O(1) - /// - Up to one storage write O(M) with O(log M) binary search to add a member back to society. + /// - Up to one storage write O(M) with O(log M) binary search to add a member back to + /// society. /// - Up to 3 storage removals O(1) to clean up a removed member. /// - Up to one storage write O(B) with O(B) search to remove vouched bid from bids. /// - Up to one additional event if unvouch takes place. @@ -915,8 +1100,12 @@ decl_module! { /// /// Total Complexity: O(M + logM + B) /// # - #[weight = T::BlockWeights::get().max_block / 10] - fn judge_suspended_member(origin, who: T::AccountId, forgive: bool) { + #[pallet::weight(T::BlockWeights::get().max_block / 10)] + pub fn judge_suspended_member( + origin: OriginFor, + who: T::AccountId, + forgive: bool, + ) -> DispatchResult { T::SuspensionJudgementOrigin::ensure_origin(origin)?; ensure!(>::contains_key(&who), Error::::NotSuspended); @@ -936,14 +1125,15 @@ decl_module! { if let Some(pos) = bids.iter().position(|b| b.kind.check_voucher(&who).is_ok()) { // Remove the bid, and emit an event let vouched = bids.remove(pos).who; - Self::deposit_event(RawEvent::Unvouch(vouched)); + Self::deposit_event(Event::::Unvouch(vouched)); } ); } } >::remove(&who); - Self::deposit_event(RawEvent::SuspendedMemberJudgement(who, forgive)); + Self::deposit_event(Event::::SuspendedMemberJudgement(who, forgive)); + Ok(()) } /// Allow suspended judgement origin to make judgement on a suspended candidate. @@ -986,8 +1176,12 @@ decl_module! { /// /// Total Complexity: O(M + logM + B + X) /// # - #[weight = T::BlockWeights::get().max_block / 10] - fn judge_suspended_candidate(origin, who: T::AccountId, judgement: Judgement) { + #[pallet::weight(T::BlockWeights::get().max_block / 10)] + pub fn judge_suspended_candidate( + origin: OriginFor, + who: T::AccountId, + judgement: Judgement, + ) -> DispatchResult { T::SuspensionJudgementOrigin::ensure_origin(origin)?; if let Some((value, kind)) = >::get(&who) { match judgement { @@ -1001,29 +1195,35 @@ decl_module! { // Reduce next pot by payout >::put(pot - value); // Add payout for new candidate - let maturity = >::block_number() - + Self::lock_duration(Self::members().len() as u32); + let maturity = >::block_number() + + Self::lock_duration(Self::members().len() as u32); Self::pay_accepted_candidate(&who, value, kind, maturity); - } + }, Judgement::Reject => { // Founder has rejected this candidate match kind { BidKind::Deposit(deposit) => { // Slash deposit and move it to the society account - let res = T::Currency::repatriate_reserved(&who, &Self::account_id(), deposit, BalanceStatus::Free); + let res = T::Currency::repatriate_reserved( + &who, + &Self::account_id(), + deposit, + BalanceStatus::Free, + ); debug_assert!(res.is_ok()); - } + }, BidKind::Vouch(voucher, _) => { // Ban the voucher from vouching again >::insert(&voucher, VouchingStatus::Banned); - } + }, } - } + }, Judgement::Rebid => { - // Founder has taken no judgement, and candidate is placed back into the pool. + // Founder has taken no judgement, and candidate is placed back into the + // pool. let bids = >::get(); Self::put_bid(bids, &who, value, kind); - } + }, } // Remove suspended candidate @@ -1031,6 +1231,7 @@ decl_module! { } else { Err(Error::::NotSuspended)? } + Ok(()) } /// Allows root origin to change the maximum number of members in society. @@ -1047,127 +1248,14 @@ decl_module! { /// /// Total Complexity: O(1) /// # - #[weight = T::BlockWeights::get().max_block / 10] - fn set_max_members(origin, max: u32) { + #[pallet::weight(T::BlockWeights::get().max_block / 10)] + pub fn set_max_members(origin: OriginFor, max: u32) -> DispatchResult { ensure_root(origin)?; ensure!(max > 1, Error::::MaxMembers); - MaxMembers::::put(max); - Self::deposit_event(RawEvent::NewMaxMembers(max)); + MaxMembers::::put(max); + Self::deposit_event(Event::::NewMaxMembers(max)); + Ok(()) } - - fn on_initialize(n: T::BlockNumber) -> Weight { - let mut members = vec![]; - - let mut weight = 0; - let weights = T::BlockWeights::get(); - - // Run a candidate/membership rotation - if (n % T::RotationPeriod::get()).is_zero() { - members = >::get(); - Self::rotate_period(&mut members); - - weight += weights.max_block / 20; - } - - // Run a challenge rotation - if (n % T::ChallengePeriod::get()).is_zero() { - // Only read members if not already read. - if members.is_empty() { - members = >::get(); - } - Self::rotate_challenge(&mut members); - - weight += weights.max_block / 20; - } - - weight - } - } -} - -decl_error! { - /// Errors for this module. - pub enum Error for Module, I: Instance> { - /// An incorrect position was provided. - BadPosition, - /// User is not a member. - NotMember, - /// User is already a member. - AlreadyMember, - /// User is suspended. - Suspended, - /// User is not suspended. - NotSuspended, - /// Nothing to payout. - NoPayout, - /// Society already founded. - AlreadyFounded, - /// Not enough in pot to accept candidate. - InsufficientPot, - /// Member is already vouching or banned from vouching again. - AlreadyVouching, - /// Member is not vouching. - NotVouching, - /// Cannot remove the head of the chain. - Head, - /// Cannot remove the founder. - Founder, - /// User has already made a bid. - AlreadyBid, - /// User is already a candidate. - AlreadyCandidate, - /// User is not a candidate. - NotCandidate, - /// Too many members in the society. - MaxMembers, - /// The caller is not the founder. - NotFounder, - /// The caller is not the head. - NotHead, - } -} - -decl_event! { - /// Events for this module. - pub enum Event where - AccountId = ::AccountId, - Balance = BalanceOf - { - /// The society is founded by the given identity. \[founder\] - Founded(AccountId), - /// A membership bid just happened. The given account is the candidate's ID and their offer - /// is the second. \[candidate_id, offer\] - Bid(AccountId, Balance), - /// A membership bid just happened by vouching. The given account is the candidate's ID and - /// their offer is the second. The vouching party is the third. \[candidate_id, offer, vouching\] - Vouch(AccountId, Balance, AccountId), - /// A \[candidate\] was dropped (due to an excess of bids in the system). - AutoUnbid(AccountId), - /// A \[candidate\] was dropped (by their request). - Unbid(AccountId), - /// A \[candidate\] was dropped (by request of who vouched for them). - Unvouch(AccountId), - /// A group of candidates have been inducted. The batch's primary is the first value, the - /// batch in full is the second. \[primary, candidates\] - Inducted(AccountId, Vec), - /// A suspended member has been judged. \[who, judged\] - SuspendedMemberJudgement(AccountId, bool), - /// A \[candidate\] has been suspended - CandidateSuspended(AccountId), - /// A \[member\] has been suspended - MemberSuspended(AccountId), - /// A \[member\] has been challenged - Challenged(AccountId), - /// A vote has been placed \[candidate, voter, vote\] - Vote(AccountId, AccountId, bool), - /// A vote has been placed for a defending member \[voter, vote\] - DefenderVote(AccountId, bool), - /// A new \[max\] member count has been set - NewMaxMembers(u32), - /// Society is unfounded. \[founder\] - Unfounded(AccountId), - /// Some funds were deposited into the society account. \[value\] - Deposit(Balance), } } @@ -1177,7 +1265,7 @@ impl EnsureOrigin for EnsureFounder { type Success = T::AccountId; fn try_origin(o: T::Origin) -> Result { o.into().and_then(|o| match (o, Founder::::get()) { - (system::RawOrigin::Signed(ref who), Some(ref f)) if who == f => Ok(who.clone()), + (frame_system::RawOrigin::Signed(ref who), Some(ref f)) if who == f => Ok(who.clone()), (r, _) => Err(T::Origin::from(r)), }) } @@ -1185,7 +1273,7 @@ impl EnsureOrigin for EnsureFounder { #[cfg(feature = "runtime-benchmarks")] fn successful_origin() -> T::Origin { let founder = Founder::::get().expect("society founder should exist"); - T::Origin::from(system::RawOrigin::Signed(founder)) + T::Origin::from(frame_system::RawOrigin::Signed(founder)) } } @@ -1203,7 +1291,7 @@ fn pick_usize<'a, R: RngCore>(rng: &mut R, max: usize) -> usize { (rng.next_u32() % (max as u32 + 1)) as usize } -impl, I: Instance> Module { +impl, I: 'static> Pallet { /// Puts a bid into storage ordered by smallest to largest value. /// Allows a maximum of 1000 bids in queue, removing largest value people first. fn put_bid( @@ -1251,7 +1339,7 @@ impl, I: Instance> Module { >::remove(&voucher); }, } - Self::deposit_event(RawEvent::AutoUnbid(popped)); + Self::deposit_event(Event::::AutoUnbid(popped)); } >::put(bids); @@ -1281,7 +1369,7 @@ impl, I: Instance> Module { /// Can fail when `MaxMember` limit is reached, but has no side-effects. fn add_member(who: &T::AccountId) -> DispatchResult { let mut members = >::get(); - ensure!(members.len() < MaxMembers::::get() as usize, Error::::MaxMembers); + ensure!(members.len() < MaxMembers::::get() as usize, Error::::MaxMembers); match members.binary_search(who) { // Add the new member Err(i) => { @@ -1338,8 +1426,8 @@ impl, I: Instance> Module { // out of society. members.reserve(candidates.len()); - let maturity = - >::block_number() + Self::lock_duration(members.len() as u32); + let maturity = >::block_number() + + Self::lock_duration(members.len() as u32); let mut rewardees = Vec::new(); let mut total_approvals = 0; @@ -1416,7 +1504,7 @@ impl, I: Instance> Module { } else { // Suspend Candidate >::insert(&candidate, (value, kind)); - Self::deposit_event(RawEvent::CandidateSuspended(candidate)); + Self::deposit_event(Event::::CandidateSuspended(candidate)); None } }) @@ -1485,7 +1573,7 @@ impl, I: Instance> Module { >::put(&primary); T::MembershipChanged::change_members_sorted(&accounts, &[], &members); - Self::deposit_event(RawEvent::Inducted(primary, accounts)); + Self::deposit_event(Event::::Inducted(primary, accounts)); } // Bump the pot by at most PeriodSpend, but less if there's not very much left in our @@ -1550,7 +1638,7 @@ impl, I: Instance> Module { if Self::remove_member(&who).is_ok() { >::insert(who, true); >::remove(who); - Self::deposit_event(RawEvent::MemberSuspended(who.clone())); + Self::deposit_event(Event::::MemberSuspended(who.clone())); } } @@ -1628,7 +1716,7 @@ impl, I: Instance> Module { let chosen = pick_item(&mut rng, &members[1..members.len() - 1]) .expect("exited if members empty; qed"); >::put(&chosen); - Self::deposit_event(RawEvent::Challenged(chosen.clone())); + Self::deposit_event(Event::::Challenged(chosen.clone())); } else { >::kill(); } @@ -1668,7 +1756,7 @@ impl, I: Instance> Module { members_len: usize, pot: BalanceOf, ) -> Vec>> { - let max_members = MaxMembers::::get() as usize; + let max_members = MaxMembers::::get() as usize; let mut max_selections: usize = (T::MaxCandidateIntake::get() as usize).min(max_members.saturating_sub(members_len)); @@ -1725,13 +1813,13 @@ impl, I: Instance> Module { } } -impl OnUnbalanced> for Module { - fn on_nonzero_unbalanced(amount: NegativeImbalanceOf) { +impl, I: 'static> OnUnbalanced> for Pallet { + fn on_nonzero_unbalanced(amount: NegativeImbalanceOf) { let numeric_amount = amount.peek(); // Must resolve into existing but better to be safe. let _ = T::Currency::resolve_creating(&Self::account_id(), amount); - Self::deposit_event(RawEvent::Deposit(numeric_amount)); + Self::deposit_event(Event::::Deposit(numeric_amount)); } } diff --git a/substrate/frame/society/src/mock.rs b/substrate/frame/society/src/mock.rs index 38c2586323..9356c083f2 100644 --- a/substrate/frame/society/src/mock.rs +++ b/substrate/frame/society/src/mock.rs @@ -20,10 +20,7 @@ use super::*; use crate as pallet_society; -use frame_support::{ - ord_parameter_types, parameter_types, - traits::{OnFinalize, OnInitialize}, -}; +use frame_support::{ord_parameter_types, parameter_types}; use frame_support_test::TestRandomness; use frame_system::EnsureSignedBy; use sp_core::H256;