Logic for the (Core) Fellowship (#13503)

* More drafting

* Paymaster pallet

* Fix build

* More tests

* Rename

* Rename

* Renaming

* Revert old changes

* Multi-phase payouts to avoid bank-runs

* Tests

* Tests

* Allow payment to be targeted elsewhere

* Proper ssync payment failure handling

* Test for repayment

* Docs

* Impl RankedMembers for RankedCollective

* Implement Pay for Pot (i.e. basic account).

* Benchmarks

* Weights

* Introduce Salary benchmark into node

* Fix warning

* ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_salary

* Update primitives/arithmetic/src/traits.rs

Co-authored-by: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com>

* Update frame/salary/src/lib.rs

Co-authored-by: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com>

* Update lib.rs

* Update frame/salary/src/lib.rs

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>

* Docs

* Update frame/salary/src/lib.rs

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>

* Update frame/salary/src/lib.rs

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>

* Fix

* Fixes

* Fixes

* Move some salary traits stuff to a shared location

* Initial draft

* Comment out bits

* Fix

* First couple of tests

* One more test

* Update frame/salary/src/lib.rs

Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Update frame/salary/src/lib.rs

Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Mul floor

* Tests

* Mul floor

* Fix warnings

* Fix test

* Tests

* Last tests

* Docs

* Fix warnings

* Benchmarks

* Weights

* Integrate benchmark

* Fixes

* Fix

* ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_core_fellowship

* Better process flow

* Fix benchmarks & tests

* Docs

* Fixes

* Fixes

* docs

* ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_core_fellowship

* Docs and allow custom evidence size

* ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_core_fellowship

* Update frame/core-fellowship/src/lib.rs

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Update frame/core-fellowship/src/tests.rs

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Update frame/core-fellowship/src/benchmarking.rs

* Update frame/core-fellowship/src/benchmarking.rs

* Apply suggestions from code review

* Rename

* Update primitives/arithmetic/src/traits.rs

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>

* Reduce magic numbers

* Update frame/core-fellowship/src/lib.rs

Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Update frame/core-fellowship/src/lib.rs

Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Benchmark result

* Remove dependency

* set_params should pay

* induct should pay

* Remove some other free calls

---------

Co-authored-by: command-bot <>
Co-authored-by: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com>
Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>
Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
This commit is contained in:
Gavin Wood
2023-03-11 13:11:15 +01:00
committed by GitHub
parent 689c2f6d4e
commit 4ad1ad2c60
14 changed files with 1716 additions and 7 deletions
+20
View File
@@ -3522,6 +3522,7 @@ dependencies = [
"pallet-contracts",
"pallet-contracts-primitives",
"pallet-conviction-voting",
"pallet-core-fellowship",
"pallet-democracy",
"pallet-election-provider-multi-phase",
"pallet-election-provider-support-benchmarking",
@@ -5777,6 +5778,25 @@ dependencies = [
"sp-std",
]
[[package]]
name = "pallet-core-fellowship"
version = "4.0.0-dev"
dependencies = [
"frame-benchmarking",
"frame-support",
"frame-system",
"log",
"pallet-ranked-collective",
"pallet-salary",
"parity-scale-codec",
"scale-info",
"sp-arithmetic",
"sp-core",
"sp-io",
"sp-runtime",
"sp-std",
]
[[package]]
name = "pallet-democracy"
version = "4.0.0-dev"
+1
View File
@@ -94,6 +94,7 @@ members = [
"frame/contracts/proc-macro",
"frame/contracts/primitives",
"frame/conviction-voting",
"frame/core-fellowship",
"frame/democracy",
"frame/fast-unstake",
"frame/try-runtime",
+4
View File
@@ -65,6 +65,7 @@ pallet-collective = { version = "4.0.0-dev", default-features = false, path = ".
pallet-contracts = { version = "4.0.0-dev", default-features = false, path = "../../../frame/contracts" }
pallet-contracts-primitives = { version = "7.0.0", default-features = false, path = "../../../frame/contracts/primitives/" }
pallet-conviction-voting = { version = "4.0.0-dev", default-features = false, path = "../../../frame/conviction-voting" }
pallet-core-fellowship = { version = "4.0.0-dev", default-features = false, path = "../../../frame/core-fellowship" }
pallet-democracy = { version = "4.0.0-dev", default-features = false, path = "../../../frame/democracy" }
pallet-election-provider-multi-phase = { version = "4.0.0-dev", default-features = false, path = "../../../frame/election-provider-multi-phase" }
pallet-election-provider-support-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../../../frame/election-provider-support/benchmarking", optional = true }
@@ -148,6 +149,7 @@ std = [
"pallet-contracts/std",
"pallet-contracts-primitives/std",
"pallet-conviction-voting/std",
"pallet-core-fellowship/std",
"pallet-democracy/std",
"pallet-elections-phragmen/std",
"pallet-fast-unstake/std",
@@ -235,6 +237,7 @@ runtime-benchmarks = [
"pallet-collective/runtime-benchmarks",
"pallet-contracts/runtime-benchmarks",
"pallet-conviction-voting/runtime-benchmarks",
"pallet-core-fellowship/runtime-benchmarks",
"pallet-democracy/runtime-benchmarks",
"pallet-election-provider-multi-phase/runtime-benchmarks",
"pallet-election-provider-support-benchmarking/runtime-benchmarks",
@@ -294,6 +297,7 @@ try-runtime = [
"pallet-collective/try-runtime",
"pallet-contracts/try-runtime",
"pallet-conviction-voting/try-runtime",
"pallet-core-fellowship/try-runtime",
"pallet-democracy/try-runtime",
"pallet-election-provider-multi-phase/try-runtime",
"pallet-elections-phragmen/try-runtime",
+14
View File
@@ -1574,6 +1574,18 @@ impl pallet_salary::Config for Runtime {
type Budget = Budget;
}
impl pallet_core_fellowship::Config for Runtime {
type WeightInfo = ();
type RuntimeEvent = RuntimeEvent;
type Members = RankedCollective;
type Balance = Balance;
type ParamsOrigin = frame_system::EnsureRoot<AccountId>;
type InductOrigin = pallet_core_fellowship::EnsureInducted<Runtime, (), 1>;
type ApproveOrigin = frame_system::EnsureRootWithSuccess<AccountId, ConstU16<9>>;
type PromoteOrigin = frame_system::EnsureRootWithSuccess<AccountId, ConstU16<9>>;
type EvidenceSize = ConstU32<16_384>;
}
parameter_types! {
pub Features: PalletFeatures = PalletFeatures::all_enabled();
pub const MaxAttributesPerCall: u32 = 10;
@@ -1770,6 +1782,7 @@ construct_runtime!(
Uniques: pallet_uniques,
Nfts: pallet_nfts,
Salary: pallet_salary,
CoreFellowship: pallet_core_fellowship,
TransactionStorage: pallet_transaction_storage,
VoterList: pallet_bags_list::<Instance1>,
StateTrieMigration: pallet_state_trie_migration,
@@ -1866,6 +1879,7 @@ mod benches {
[pallet_collective, Council]
[pallet_conviction_voting, ConvictionVoting]
[pallet_contracts, Contracts]
[pallet_core_fellowship, CoreFellowship]
[pallet_democracy, Democracy]
[pallet_election_provider_multi_phase, ElectionProviderMultiPhase]
[pallet_election_provider_support_benchmarking, EPSBench::<Runtime>]
@@ -0,0 +1,55 @@
[package]
name = "pallet-core-fellowship"
version = "4.0.0-dev"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2021"
license = "Apache-2.0"
homepage = "https://substrate.io"
repository = "https://github.com/paritytech/substrate/"
description = "Logic as per the description of The Fellowship for core Polkadot technology"
readme = "README.md"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] }
log = { version = "0.4.16", default-features = false }
scale-info = { version = "2.0.1", default-features = false, features = ["derive"] }
frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" }
frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" }
frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" }
sp-arithmetic = { version = "6.0.0", default-features = false, path = "../../primitives/arithmetic" }
sp-core = { version = "7.0.0", default-features = false, path = "../../primitives/core" }
sp-io = { version = "7.0.0", default-features = false, path = "../../primitives/io" }
sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" }
sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" }
[dev-dependencies]
pallet-ranked-collective = { version = "4.0.0-dev", default-features = false, path = "../ranked-collective" }
pallet-salary = { version = "4.0.0-dev", default-features = false, path = "../salary" }
[features]
default = ["std"]
std = [
"codec/std",
"frame-benchmarking?/std",
"frame-support/std",
"frame-system/std",
"log/std",
"scale-info/std",
"sp-arithmetic/std",
"sp-core/std",
"sp-io/std",
"sp-runtime/std",
"sp-std/std",
]
runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"sp-runtime/runtime-benchmarks",
"pallet-ranked-collective/runtime-benchmarks",
"pallet-salary/runtime-benchmarks",
]
try-runtime = ["frame-support/try-runtime"]
@@ -0,0 +1,3 @@
# Core Fellowship
Logic specific to the core Polkadot Fellowship.
@@ -0,0 +1,216 @@
// This file is part of Substrate.
// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Salary pallet benchmarking.
#![cfg(feature = "runtime-benchmarks")]
use super::*;
use crate::Pallet as CoreFellowship;
use frame_benchmarking::v2::*;
use frame_system::RawOrigin;
use sp_arithmetic::traits::Bounded;
const SEED: u32 = 0;
type BenchResult = Result<(), BenchmarkError>;
#[instance_benchmarks]
mod benchmarks {
use super::*;
fn ensure_evidence<T: Config<I>, I: 'static>(who: &T::AccountId) -> BenchResult {
let evidence = BoundedVec::try_from(vec![0; Evidence::<T, I>::bound()]).unwrap();
let wish = Wish::Retention;
let origin = RawOrigin::Signed(who.clone()).into();
CoreFellowship::<T, I>::submit_evidence(origin, wish, evidence)?;
assert!(MemberEvidence::<T, I>::contains_key(who));
Ok(())
}
fn make_member<T: Config<I>, I: 'static>(rank: u16) -> Result<T::AccountId, BenchmarkError> {
let member = account("member", 0, SEED);
T::Members::induct(&member)?;
for _ in 0..rank {
T::Members::promote(&member)?;
}
CoreFellowship::<T, I>::import(RawOrigin::Signed(member.clone()).into())?;
Ok(member)
}
#[benchmark]
fn set_params() -> Result<(), BenchmarkError> {
let params = ParamsType {
active_salary: [100u32.into(); 9],
passive_salary: [10u32.into(); 9],
demotion_period: [100u32.into(); 9],
min_promotion_period: [100u32.into(); 9],
offboard_timeout: 1u32.into(),
};
#[extrinsic_call]
_(RawOrigin::Root, Box::new(params.clone()));
assert_eq!(Params::<T, I>::get(), params);
Ok(())
}
#[benchmark]
fn bump_offboard() -> Result<(), BenchmarkError> {
let member = make_member::<T, I>(0)?;
// Set it to the max value to ensure that any possible auto-demotion period has passed.
frame_system::Pallet::<T>::set_block_number(T::BlockNumber::max_value());
ensure_evidence::<T, I>(&member)?;
assert!(Member::<T, I>::contains_key(&member));
#[extrinsic_call]
CoreFellowship::<T, I>::bump(RawOrigin::Signed(member.clone()), member.clone());
assert!(!Member::<T, I>::contains_key(&member));
assert!(!MemberEvidence::<T, I>::contains_key(&member));
Ok(())
}
#[benchmark]
fn bump_demote() -> Result<(), BenchmarkError> {
let member = make_member::<T, I>(2)?;
// Set it to the max value to ensure that any possible auto-demotion period has passed.
frame_system::Pallet::<T>::set_block_number(T::BlockNumber::max_value());
ensure_evidence::<T, I>(&member)?;
assert!(Member::<T, I>::contains_key(&member));
assert_eq!(T::Members::rank_of(&member), Some(2));
#[extrinsic_call]
CoreFellowship::<T, I>::bump(RawOrigin::Signed(member.clone()), member.clone());
assert!(Member::<T, I>::contains_key(&member));
assert_eq!(T::Members::rank_of(&member), Some(1));
assert!(!MemberEvidence::<T, I>::contains_key(&member));
Ok(())
}
#[benchmark]
fn set_active() -> Result<(), BenchmarkError> {
let member = make_member::<T, I>(1)?;
assert!(Member::<T, I>::get(&member).unwrap().is_active);
#[extrinsic_call]
_(RawOrigin::Signed(member.clone()), false);
assert!(!Member::<T, I>::get(&member).unwrap().is_active);
Ok(())
}
#[benchmark]
fn induct() -> Result<(), BenchmarkError> {
let candidate: T::AccountId = account("candidate", 0, SEED);
#[extrinsic_call]
_(RawOrigin::Root, candidate.clone());
assert_eq!(T::Members::rank_of(&candidate), Some(0));
assert!(Member::<T, I>::contains_key(&candidate));
Ok(())
}
#[benchmark]
fn promote() -> Result<(), BenchmarkError> {
let member = make_member::<T, I>(1)?;
ensure_evidence::<T, I>(&member)?;
#[extrinsic_call]
_(RawOrigin::Root, member.clone(), 2u8.into());
assert_eq!(T::Members::rank_of(&member), Some(2));
assert!(!MemberEvidence::<T, I>::contains_key(&member));
Ok(())
}
#[benchmark]
fn offboard() -> Result<(), BenchmarkError> {
let member = make_member::<T, I>(0)?;
T::Members::demote(&member)?;
ensure_evidence::<T, I>(&member)?;
assert!(T::Members::rank_of(&member).is_none());
assert!(Member::<T, I>::contains_key(&member));
assert!(MemberEvidence::<T, I>::contains_key(&member));
#[extrinsic_call]
_(RawOrigin::Signed(member.clone()), member.clone());
assert!(!Member::<T, I>::contains_key(&member));
assert!(!MemberEvidence::<T, I>::contains_key(&member));
Ok(())
}
#[benchmark]
fn import() -> Result<(), BenchmarkError> {
let member = account("member", 0, SEED);
T::Members::induct(&member)?;
T::Members::promote(&member)?;
assert!(!Member::<T, I>::contains_key(&member));
#[extrinsic_call]
_(RawOrigin::Signed(member.clone()));
assert!(Member::<T, I>::contains_key(&member));
Ok(())
}
#[benchmark]
fn approve() -> Result<(), BenchmarkError> {
let member = make_member::<T, I>(1)?;
let then = frame_system::Pallet::<T>::block_number();
let now = then.saturating_plus_one();
frame_system::Pallet::<T>::set_block_number(now);
ensure_evidence::<T, I>(&member)?;
assert_eq!(Member::<T, I>::get(&member).unwrap().last_proof, then);
#[extrinsic_call]
_(RawOrigin::Root, member.clone(), 1u8.into());
assert_eq!(Member::<T, I>::get(&member).unwrap().last_proof, now);
assert!(!MemberEvidence::<T, I>::contains_key(&member));
Ok(())
}
#[benchmark]
fn submit_evidence() -> Result<(), BenchmarkError> {
let member = make_member::<T, I>(1)?;
let evidence = vec![0; Evidence::<T, I>::bound()].try_into().unwrap();
assert!(!MemberEvidence::<T, I>::contains_key(&member));
#[extrinsic_call]
_(RawOrigin::Signed(member.clone()), Wish::Retention, evidence);
assert!(MemberEvidence::<T, I>::contains_key(&member));
Ok(())
}
impl_benchmark_test_suite! {
CoreFellowship,
crate::tests::new_test_ext(),
crate::tests::Test,
}
}
+594
View File
@@ -0,0 +1,594 @@
// This file is part of Substrate.
// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Additional logic for the Core Fellowship. This determines salary, registers activity/passivity
//! and handles promotion and demotion periods.
//!
//! This only handles members of non-zero rank.
//!
//! # Process Flow
//! - Begin with a call to `induct`, where some privileged origin (perhaps a pre-existing member of
//! `rank > 1`) is able to make a candidate from an account and introduce it to be tracked in this
//! pallet in order to allow evidence to be submitted and promotion voted on.
//! - The candidate then calls `submit_evidence` to apply for their promotion to rank 1.
//! - A `PromoteOrigin` of at least rank 1 calls `promote` on the candidate to elevate it to rank 1.
//! - Some time later but before rank 1's `demotion_period` elapses, candidate calls
//! `submit_evidence` with evidence of their efforts to apply for approval to stay at rank 1.
//! - An `ApproveOrigin` of at least rank 1 calls `approve` on the candidate to avoid imminent
//! demotion and keep it at rank 1.
//! - These last two steps continue until the candidate is ready to apply for a promotion, at which
//! point the previous two steps are repeated with a higher rank.
//! - If the member fails to get an approval within the `demotion_period` then anyone may call
//! `bump` to demote the candidate by one rank.
//! - If a candidate fails to be promoted to a member within the `offboard_timeout` period, then
//! anyone may call `bump` to remove the account's candidacy.
//! - Pre-existing members may call `import` to have their rank recognised and be inducted into this
//! pallet (to gain a salary and allow for eventual promotion).
//! - If, externally to this pallet, a member or candidate has their rank removed completely, then
//! `offboard` may be called to remove them entirely from this pallet.
//!
//! Note there is a difference between having a rank of 0 (whereby the account is a *candidate*) and
//! having no rank at all (whereby we consider it *unranked*). An account can be demoted from rank
//! 0 to become unranked. This process is called being offboarded and there is an extrinsic to do
//! this explicitly when external factors to this pallet have caused the tracked account to become
//! unranked. At rank 0, there is not a "demotion" period after which the account may be bumped to
//! become offboarded but rather an "offboard timeout".
//!
//! Candidates may be introduced (i.e. an account to go from unranked to rank of 0) by an origin
//! of a different privilege to that for promotion. This allows the possibility for even a single
//! existing member to introduce a new candidate without payment.
//!
//! Only tracked/ranked accounts may submit evidence for their proof and promotion. Candidates
//! cannot be approved - they must proceed only to promotion prior to the offboard timeout elapsing.
#![cfg_attr(not(feature = "std"), no_std)]
#![recursion_limit = "128"]
use codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
use sp_arithmetic::traits::{Saturating, Zero};
use sp_std::{marker::PhantomData, prelude::*};
use frame_support::{
dispatch::DispatchResultWithPostInfo,
ensure,
traits::{tokens::Balance as BalanceTrait, EnsureOrigin, Get, RankedMembers},
BoundedVec, RuntimeDebug,
};
#[cfg(test)]
mod tests;
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
pub mod weights;
pub use pallet::*;
pub use weights::WeightInfo;
/// The desired outcome for which evidence is presented.
#[derive(Encode, Decode, Eq, PartialEq, Copy, Clone, TypeInfo, MaxEncodedLen, RuntimeDebug)]
pub enum Wish {
/// Member wishes only to retain their current rank.
Retention,
/// Member wishes to be promoted.
Promotion,
}
/// A piece of evidence to underpin a [Wish].
///
/// From the pallet's perspective, this is just a blob of data without meaning. The fellows can
/// decide how to concretely utilise it. This could be an IPFS hash, a URL or structured data.
pub type Evidence<T, I> = BoundedVec<u8, <T as Config<I>>::EvidenceSize>;
/// The status of the pallet instance.
#[derive(Encode, Decode, Eq, PartialEq, Clone, TypeInfo, MaxEncodedLen, RuntimeDebug)]
pub struct ParamsType<Balance, BlockNumber, const RANKS: usize> {
/// The amounts to be paid when a member of a given rank (-1) is active.
active_salary: [Balance; RANKS],
/// The amounts to be paid when a member of a given rank (-1) is passive.
passive_salary: [Balance; RANKS],
/// The period between which unproven members become demoted.
demotion_period: [BlockNumber; RANKS],
/// The period between which members must wait before they may proceed to this rank.
min_promotion_period: [BlockNumber; RANKS],
/// Amount by which an account can remain at rank 0 (candidate before being offboard entirely).
offboard_timeout: BlockNumber,
}
impl<Balance: Default + Copy, BlockNumber: Default + Copy, const RANKS: usize> Default
for ParamsType<Balance, BlockNumber, RANKS>
{
fn default() -> Self {
Self {
active_salary: [Balance::default(); RANKS],
passive_salary: [Balance::default(); RANKS],
demotion_period: [BlockNumber::default(); RANKS],
min_promotion_period: [BlockNumber::default(); RANKS],
offboard_timeout: BlockNumber::default(),
}
}
}
/// The status of a single member.
#[derive(Encode, Decode, Eq, PartialEq, Clone, TypeInfo, MaxEncodedLen, RuntimeDebug)]
pub struct MemberStatus<BlockNumber> {
/// Are they currently active?
is_active: bool,
/// The block number at which we last promoted them.
last_promotion: BlockNumber,
/// The last time a member was demoted, promoted or proved their rank.
last_proof: BlockNumber,
}
#[frame_support::pallet]
pub mod pallet {
use super::*;
use frame_support::{
dispatch::Pays,
pallet_prelude::*,
traits::{tokens::GetSalary, EnsureOrigin},
};
use frame_system::{ensure_root, pallet_prelude::*};
const RANK_COUNT: usize = 9;
#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
pub struct Pallet<T, I = ()>(PhantomData<(T, I)>);
#[pallet::config]
pub trait Config<I: 'static = ()>: frame_system::Config {
/// Weight information for extrinsics in this pallet.
type WeightInfo: WeightInfo;
/// The runtime event type.
type RuntimeEvent: From<Event<Self, I>>
+ IsType<<Self as frame_system::Config>::RuntimeEvent>;
/// The current membership of the fellowship.
type Members: RankedMembers<
AccountId = <Self as frame_system::Config>::AccountId,
Rank = u16,
>;
/// The type in which salaries/budgets are measured.
type Balance: BalanceTrait;
/// The origin which has permission update the parameters.
type ParamsOrigin: EnsureOrigin<Self::RuntimeOrigin>;
/// The origin which has permission to move a candidate into being tracked in this pallet.
/// Generally a very low-permission, such as a pre-existing member of rank 1 or above.
///
/// This allows the candidate to deposit evidence for their request to be promoted to a
/// member.
type InductOrigin: EnsureOrigin<Self::RuntimeOrigin>;
/// The origin which has permission to issue a proof that a member may retain their rank.
/// The `Success` value is the maximum rank of members it is able to prove.
type ApproveOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = RankOf<Self, I>>;
/// The origin which has permission to promote a member. The `Success` value is the maximum
/// rank to which it can promote.
type PromoteOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = RankOf<Self, I>>;
/// The maximum size in bytes submitted evidence is allowed to be.
#[pallet::constant]
type EvidenceSize: Get<u32>;
}
pub type ParamsOf<T, I> =
ParamsType<<T as Config<I>>::Balance, <T as frame_system::Config>::BlockNumber, RANK_COUNT>;
pub type MemberStatusOf<T> = MemberStatus<<T as frame_system::Config>::BlockNumber>;
pub type RankOf<T, I> = <<T as Config<I>>::Members as RankedMembers>::Rank;
/// The overall status of the system.
#[pallet::storage]
pub(super) type Params<T: Config<I>, I: 'static = ()> =
StorageValue<_, ParamsOf<T, I>, ValueQuery>;
/// The status of a claimant.
#[pallet::storage]
pub(super) type Member<T: Config<I>, I: 'static = ()> =
StorageMap<_, Twox64Concat, T::AccountId, MemberStatusOf<T>, OptionQuery>;
/// Some evidence together with the desired outcome for which it was presented.
#[pallet::storage]
pub(super) type MemberEvidence<T: Config<I>, I: 'static = ()> =
StorageMap<_, Twox64Concat, T::AccountId, (Wish, Evidence<T, I>), OptionQuery>;
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config<I>, I: 'static = ()> {
/// Parameters for the pallet have changed.
ParamsChanged { params: ParamsOf<T, I> },
/// Member activity flag has been set.
ActiveChanged { who: T::AccountId, is_active: bool },
/// Member has begun being tracked in this pallet.
Inducted { who: T::AccountId },
/// Member has been removed from being tracked in this pallet (i.e. because rank is now
/// zero).
Offboarded { who: T::AccountId },
/// Member has been promoted to the given rank.
Promoted { who: T::AccountId, to_rank: RankOf<T, I> },
/// Member has been demoted to the given (non-zero) rank.
Demoted { who: T::AccountId, to_rank: RankOf<T, I> },
/// Member has been proven at their current rank, postponing auto-demotion.
Proven { who: T::AccountId, at_rank: RankOf<T, I> },
/// Member has stated evidence of their efforts their request for rank.
Requested { who: T::AccountId, wish: Wish },
/// Some submitted evidence was judged and removed. There may or may not have been a change
/// to the rank, but in any case, `last_proof` is reset.
EvidenceJudged {
/// The member/candidate.
who: T::AccountId,
/// The desired outcome for which the evidence was presented.
wish: Wish,
/// The evidence of efforts.
evidence: Evidence<T, I>,
/// The old rank, prior to this change.
old_rank: u16,
/// New rank. If `None` then candidate record was removed entirely.
new_rank: Option<u16>,
},
/// Pre-ranked account has been inducted at their current rank.
Imported { who: T::AccountId, rank: RankOf<T, I> },
}
#[pallet::error]
pub enum Error<T, I = ()> {
/// Member's rank is too low.
Unranked,
/// Member's rank is not zero.
Ranked,
/// Member's rank is not as expected - generally means that the rank provided to the call
/// does not agree with the state of the system.
UnexpectedRank,
/// The given rank is invalid - this generally means it's not between 1 and `RANK_COUNT`.
InvalidRank,
/// The origin does not have enough permission to do this operation.
NoPermission,
/// No work needs to be done at present for this member.
NothingDoing,
/// The candidate has already been inducted. This should never happen since it would
/// require a candidate (rank 0) to already be tracked in the pallet.
AlreadyInducted,
/// The candidate has not been inducted, so cannot be offboarded from this pallet.
NotTracked,
/// Operation cannot be done yet since not enough time has passed.
TooSoon,
}
#[pallet::call]
impl<T: Config<I>, I: 'static> Pallet<T, I> {
/// Bump the state of a member.
///
/// This will demote a member whose `last_proof` is now beyond their rank's
/// `demotion_period`.
///
/// - `origin`: A `Signed` origin of an account.
/// - `who`: A member account whose state is to be updated.
#[pallet::weight(T::WeightInfo::bump_offboard().max(T::WeightInfo::bump_demote()))]
#[pallet::call_index(0)]
pub fn bump(origin: OriginFor<T>, who: T::AccountId) -> DispatchResultWithPostInfo {
let _ = ensure_signed(origin)?;
let mut member = Member::<T, I>::get(&who).ok_or(Error::<T, I>::NotTracked)?;
let rank = T::Members::rank_of(&who).ok_or(Error::<T, I>::Unranked)?;
let params = Params::<T, I>::get();
let demotion_period = if rank == 0 {
params.offboard_timeout
} else {
let rank_index = Self::rank_to_index(rank).ok_or(Error::<T, I>::InvalidRank)?;
params.demotion_period[rank_index]
};
let demotion_block = member.last_proof.saturating_add(demotion_period);
// Ensure enough time has passed.
let now = frame_system::Pallet::<T>::block_number();
if now >= demotion_block {
T::Members::demote(&who)?;
let maybe_to_rank = T::Members::rank_of(&who);
Self::dispose_evidence(who.clone(), rank, maybe_to_rank);
let event = if let Some(to_rank) = maybe_to_rank {
member.last_proof = now;
Member::<T, I>::insert(&who, &member);
Event::<T, I>::Demoted { who, to_rank }
} else {
Member::<T, I>::remove(&who);
Event::<T, I>::Offboarded { who }
};
Self::deposit_event(event);
return Ok(Pays::No.into())
}
Err(Error::<T, I>::NothingDoing.into())
}
/// Set the parameters.
///
/// - `origin`: A origin complying with `ParamsOrigin` or root.
/// - `params`: The new parameters for the pallet.
#[pallet::weight(T::WeightInfo::set_params())]
#[pallet::call_index(1)]
pub fn set_params(origin: OriginFor<T>, params: Box<ParamsOf<T, I>>) -> DispatchResult {
T::ParamsOrigin::try_origin(origin).map(|_| ()).or_else(|o| ensure_root(o))?;
Params::<T, I>::put(params.as_ref());
Self::deposit_event(Event::<T, I>::ParamsChanged { params: *params });
Ok(())
}
/// Set whether a member is active or not.
///
/// - `origin`: A `Signed` origin of a member's account.
/// - `is_active`: `true` iff the member is active.
#[pallet::weight(T::WeightInfo::set_active())]
#[pallet::call_index(2)]
pub fn set_active(origin: OriginFor<T>, is_active: bool) -> DispatchResult {
let who = ensure_signed(origin)?;
ensure!(
T::Members::rank_of(&who).map_or(false, |r| !r.is_zero()),
Error::<T, I>::Unranked
);
let mut member = Member::<T, I>::get(&who).ok_or(Error::<T, I>::NotTracked)?;
member.is_active = is_active;
Member::<T, I>::insert(&who, &member);
Self::deposit_event(Event::<T, I>::ActiveChanged { who, is_active });
Ok(())
}
/// Approve a member to continue at their rank.
///
/// This resets `last_proof` to the current block, thereby delaying any automatic demotion.
///
/// If `who` is not already tracked by this pallet, then it will become tracked.
/// `last_promotion` will be set to zero.
///
/// - `origin`: An origin which satisfies `ApproveOrigin` or root.
/// - `who`: A member (i.e. of non-zero rank).
/// - `at_rank`: The rank of member.
#[pallet::weight(T::WeightInfo::approve())]
#[pallet::call_index(3)]
pub fn approve(
origin: OriginFor<T>,
who: T::AccountId,
at_rank: RankOf<T, I>,
) -> DispatchResult {
match T::PromoteOrigin::try_origin(origin) {
Ok(allow_rank) => ensure!(allow_rank >= at_rank, Error::<T, I>::NoPermission),
Err(origin) => ensure_root(origin)?,
}
ensure!(at_rank > 0, Error::<T, I>::InvalidRank);
let rank = T::Members::rank_of(&who).ok_or(Error::<T, I>::Unranked)?;
ensure!(rank == at_rank, Error::<T, I>::UnexpectedRank);
let mut member = Member::<T, I>::get(&who).ok_or(Error::<T, I>::NotTracked)?;
member.last_proof = frame_system::Pallet::<T>::block_number();
Member::<T, I>::insert(&who, &member);
Self::dispose_evidence(who.clone(), at_rank, Some(at_rank));
Self::deposit_event(Event::<T, I>::Proven { who, at_rank });
Ok(())
}
/// Introduce a new and unranked candidate (rank zero).
///
/// - `origin`: An origin which satisfies `InductOrigin` or root.
/// - `who`: The account ID of the candidate to be inducted and become a member.
#[pallet::weight(T::WeightInfo::induct())]
#[pallet::call_index(4)]
pub fn induct(origin: OriginFor<T>, who: T::AccountId) -> DispatchResult {
match T::InductOrigin::try_origin(origin) {
Ok(_) => {},
Err(origin) => ensure_root(origin)?,
}
ensure!(!Member::<T, I>::contains_key(&who), Error::<T, I>::AlreadyInducted);
ensure!(T::Members::rank_of(&who).is_none(), Error::<T, I>::Ranked);
T::Members::induct(&who)?;
let now = frame_system::Pallet::<T>::block_number();
Member::<T, I>::insert(
&who,
MemberStatus { is_active: true, last_promotion: now, last_proof: now },
);
Self::deposit_event(Event::<T, I>::Inducted { who });
Ok(())
}
/// Increment the rank of a ranked and tracked account.
///
/// - `origin`: An origin which satisfies `PromoteOrigin` with a `Success` result of
/// `to_rank` or more or root.
/// - `who`: The account ID of the member to be promoted to `to_rank`.
/// - `to_rank`: One more than the current rank of `who`.
#[pallet::weight(T::WeightInfo::promote())]
#[pallet::call_index(5)]
pub fn promote(
origin: OriginFor<T>,
who: T::AccountId,
to_rank: RankOf<T, I>,
) -> DispatchResult {
match T::PromoteOrigin::try_origin(origin) {
Ok(allow_rank) => ensure!(allow_rank >= to_rank, Error::<T, I>::NoPermission),
Err(origin) => ensure_root(origin)?,
}
let rank = T::Members::rank_of(&who).ok_or(Error::<T, I>::Unranked)?;
ensure!(
rank.checked_add(1).map_or(false, |i| i == to_rank),
Error::<T, I>::UnexpectedRank
);
let mut member = Member::<T, I>::get(&who).ok_or(Error::<T, I>::NotTracked)?;
let now = frame_system::Pallet::<T>::block_number();
let params = Params::<T, I>::get();
let rank_index = Self::rank_to_index(to_rank).ok_or(Error::<T, I>::InvalidRank)?;
let min_period = params.min_promotion_period[rank_index];
// Ensure enough time has passed.
ensure!(
member.last_promotion.saturating_add(min_period) <= now,
Error::<T, I>::TooSoon,
);
T::Members::promote(&who)?;
member.last_promotion = now;
member.last_proof = now;
Member::<T, I>::insert(&who, &member);
Self::dispose_evidence(who.clone(), rank, Some(to_rank));
Self::deposit_event(Event::<T, I>::Promoted { who, to_rank });
Ok(())
}
/// Stop tracking a prior member who is now not a ranked member of the collective.
///
/// - `origin`: A `Signed` origin of an account.
/// - `who`: The ID of an account which was tracked in this pallet but which is now not a
/// ranked member of the collective.
#[pallet::weight(T::WeightInfo::offboard())]
#[pallet::call_index(6)]
pub fn offboard(origin: OriginFor<T>, who: T::AccountId) -> DispatchResultWithPostInfo {
let _ = ensure_signed(origin)?;
ensure!(T::Members::rank_of(&who).is_none(), Error::<T, I>::Ranked);
ensure!(Member::<T, I>::contains_key(&who), Error::<T, I>::NotTracked);
Member::<T, I>::remove(&who);
MemberEvidence::<T, I>::remove(&who);
Self::deposit_event(Event::<T, I>::Offboarded { who });
Ok(Pays::No.into())
}
/// Provide evidence that a rank is deserved.
///
/// This is free as long as no evidence for the forthcoming judgement is already submitted.
/// Evidence is cleared after an outcome (either demotion, promotion of approval).
///
/// - `origin`: A `Signed` origin of an inducted and ranked account.
/// - `wish`: The stated desire of the member.
/// - `evidence`: A dump of evidence to be considered. This should generally be either a
/// Markdown-encoded document or a series of 32-byte hashes which can be found on a
/// decentralised content-based-indexing system such as IPFS.
#[pallet::weight(T::WeightInfo::submit_evidence())]
#[pallet::call_index(7)]
pub fn submit_evidence(
origin: OriginFor<T>,
wish: Wish,
evidence: Evidence<T, I>,
) -> DispatchResultWithPostInfo {
let who = ensure_signed(origin)?;
ensure!(Member::<T, I>::contains_key(&who), Error::<T, I>::NotTracked);
let replaced = MemberEvidence::<T, I>::contains_key(&who);
MemberEvidence::<T, I>::insert(&who, (wish, evidence));
Self::deposit_event(Event::<T, I>::Requested { who, wish });
Ok(if replaced { Pays::Yes } else { Pays::No }.into())
}
/// Introduce an already-ranked individual of the collective into this pallet. The rank may
/// still be zero.
///
/// This resets `last_proof` to the current block and `last_promotion` will be set to zero,
/// thereby delaying any automatic demotion but allowing immediate promotion.
///
/// - `origin`: A signed origin of a ranked, but not tracked, account.
#[pallet::weight(T::WeightInfo::import())]
#[pallet::call_index(8)]
pub fn import(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
let who = ensure_signed(origin)?;
ensure!(!Member::<T, I>::contains_key(&who), Error::<T, I>::AlreadyInducted);
let rank = T::Members::rank_of(&who).ok_or(Error::<T, I>::Unranked)?;
let now = frame_system::Pallet::<T>::block_number();
Member::<T, I>::insert(
&who,
MemberStatus { is_active: true, last_promotion: 0u32.into(), last_proof: now },
);
Self::deposit_event(Event::<T, I>::Imported { who, rank });
Ok(Pays::No.into())
}
}
impl<T: Config<I>, I: 'static> Pallet<T, I> {
/// Convert a rank into a `0..RANK_COUNT` index suitable for the arrays in Params.
///
/// Rank 1 becomes index 0, rank `RANK_COUNT` becomes index `RANK_COUNT - 1`. Any rank not
/// in the range `1..=RANK_COUNT` is `None`.
pub(crate) fn rank_to_index(rank: RankOf<T, I>) -> Option<usize> {
match TryInto::<usize>::try_into(rank) {
Ok(r) if r <= RANK_COUNT && r > 0 => Some(r - 1),
_ => return None,
}
}
fn dispose_evidence(who: T::AccountId, old_rank: u16, new_rank: Option<u16>) {
if let Some((wish, evidence)) = MemberEvidence::<T, I>::take(&who) {
let e = Event::<T, I>::EvidenceJudged { who, wish, evidence, old_rank, new_rank };
Self::deposit_event(e);
}
}
}
impl<T: Config<I>, I: 'static> GetSalary<RankOf<T, I>, T::AccountId, T::Balance> for Pallet<T, I> {
fn get_salary(rank: RankOf<T, I>, who: &T::AccountId) -> T::Balance {
let index = match Self::rank_to_index(rank) {
Some(i) => i,
None => return Zero::zero(),
};
let member = match Member::<T, I>::get(who) {
Some(m) => m,
None => return Zero::zero(),
};
let params = Params::<T, I>::get();
let salary =
if member.is_active { params.active_salary } else { params.passive_salary };
salary[index]
}
}
}
/// Guard to ensure that the given origin is inducted into this pallet with a given minimum rank.
/// The account ID of the member is the `Success` value.
pub struct EnsureInducted<T, I, const MIN_RANK: u16>(PhantomData<(T, I)>);
impl<T: Config<I>, I: 'static, const MIN_RANK: u16> EnsureOrigin<T::RuntimeOrigin>
for EnsureInducted<T, I, MIN_RANK>
{
type Success = T::AccountId;
fn try_origin(o: T::RuntimeOrigin) -> Result<Self::Success, T::RuntimeOrigin> {
let who = frame_system::EnsureSigned::try_origin(o)?;
match T::Members::rank_of(&who) {
Some(rank) if rank >= MIN_RANK && Member::<T, I>::contains_key(&who) => Ok(who),
_ => Err(frame_system::RawOrigin::Signed(who).into()),
}
}
#[cfg(feature = "runtime-benchmarks")]
fn try_successful_origin() -> Result<T::RuntimeOrigin, ()> {
let who = frame_benchmarking::account::<T::AccountId>("successful_origin", 0, 0);
if T::Members::rank_of(&who).is_none() {
T::Members::induct(&who).map_err(|_| ())?;
}
for _ in 0..MIN_RANK {
if T::Members::rank_of(&who).ok_or(())? < MIN_RANK {
T::Members::promote(&who).map_err(|_| ())?;
}
}
Ok(frame_system::RawOrigin::Signed(who).into())
}
}
@@ -0,0 +1,362 @@
// This file is part of Substrate.
// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! The crate's tests.
use std::collections::BTreeMap;
use frame_support::{
assert_noop, assert_ok, ord_parameter_types,
pallet_prelude::Weight,
parameter_types,
traits::{tokens::GetSalary, ConstU32, ConstU64, Everything, IsInVec, TryMapSuccess},
};
use frame_system::EnsureSignedBy;
use sp_core::H256;
use sp_runtime::{
testing::Header,
traits::{BlakeTwo256, IdentityLookup, TryMorphInto},
DispatchError, DispatchResult,
};
use sp_std::cell::RefCell;
use super::*;
use crate as pallet_core_fellowship;
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
type Block = frame_system::mocking::MockBlock<Test>;
frame_support::construct_runtime!(
pub enum Test where
Block = Block,
NodeBlock = Block,
UncheckedExtrinsic = UncheckedExtrinsic,
{
System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
CoreFellowship: pallet_core_fellowship::{Pallet, Call, Storage, Event<T>},
}
);
parameter_types! {
pub BlockWeights: frame_system::limits::BlockWeights =
frame_system::limits::BlockWeights::simple_max(Weight::from_parts(1_000_000, u64::max_value()));
}
impl frame_system::Config for Test {
type BaseCallFilter = Everything;
type BlockWeights = ();
type BlockLength = ();
type DbWeight = ();
type RuntimeOrigin = RuntimeOrigin;
type Index = u64;
type BlockNumber = u64;
type RuntimeCall = RuntimeCall;
type Hash = H256;
type Hashing = BlakeTwo256;
type AccountId = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type RuntimeEvent = RuntimeEvent;
type BlockHashCount = ConstU64<250>;
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = ();
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = ConstU32<16>;
}
thread_local! {
pub static CLUB: RefCell<BTreeMap<u64, u16>> = RefCell::new(BTreeMap::new());
}
pub struct TestClub;
impl RankedMembers for TestClub {
type AccountId = u64;
type Rank = u16;
fn min_rank() -> Self::Rank {
0
}
fn rank_of(who: &Self::AccountId) -> Option<Self::Rank> {
CLUB.with(|club| club.borrow().get(who).cloned())
}
fn induct(who: &Self::AccountId) -> DispatchResult {
CLUB.with(|club| club.borrow_mut().insert(*who, 0));
Ok(())
}
fn promote(who: &Self::AccountId) -> DispatchResult {
CLUB.with(|club| {
club.borrow_mut().entry(*who).and_modify(|r| *r += 1);
});
Ok(())
}
fn demote(who: &Self::AccountId) -> DispatchResult {
CLUB.with(|club| match Self::rank_of(who) {
None => Err(sp_runtime::DispatchError::Unavailable),
Some(0) => {
club.borrow_mut().remove(&who);
Ok(())
},
Some(_) => {
club.borrow_mut().entry(*who).and_modify(|x| *x -= 1);
Ok(())
},
})
}
}
fn set_rank(who: u64, rank: u16) {
CLUB.with(|club| club.borrow_mut().insert(who, rank));
}
fn unrank(who: u64) {
CLUB.with(|club| club.borrow_mut().remove(&who));
}
parameter_types! {
pub ZeroToNine: Vec<u64> = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
}
ord_parameter_types! {
pub const One: u64 = 1;
}
impl Config for Test {
type WeightInfo = ();
type RuntimeEvent = RuntimeEvent;
type Members = TestClub;
type Balance = u64;
type ParamsOrigin = EnsureSignedBy<One, u64>;
type InductOrigin = EnsureInducted<Test, (), 1>;
type ApproveOrigin = TryMapSuccess<EnsureSignedBy<IsInVec<ZeroToNine>, u64>, TryMorphInto<u16>>;
type PromoteOrigin = TryMapSuccess<EnsureSignedBy<IsInVec<ZeroToNine>, u64>, TryMorphInto<u16>>;
type EvidenceSize = ConstU32<1024>;
}
pub fn new_test_ext() -> sp_io::TestExternalities {
let t = frame_system::GenesisConfig::default().build_storage::<Test>().unwrap();
let mut ext = sp_io::TestExternalities::new(t);
ext.execute_with(|| {
let params = ParamsType {
active_salary: [10, 20, 30, 40, 50, 60, 70, 80, 90],
passive_salary: [1, 2, 3, 4, 5, 6, 7, 8, 9],
demotion_period: [2, 4, 6, 8, 10, 12, 14, 16, 18],
min_promotion_period: [3, 6, 9, 12, 15, 18, 21, 24, 27],
offboard_timeout: 1,
};
assert_ok!(CoreFellowship::set_params(signed(1), Box::new(params)));
System::set_block_number(1);
});
ext
}
fn next_block() {
System::set_block_number(System::block_number() + 1);
}
fn run_to(n: u64) {
while System::block_number() < n {
next_block();
}
}
fn signed(who: u64) -> RuntimeOrigin {
RuntimeOrigin::signed(who)
}
fn next_demotion(who: u64) -> u64 {
let member = Member::<Test>::get(who).unwrap();
let demotion_period = Params::<Test>::get().demotion_period;
member.last_proof + demotion_period[TestClub::rank_of(&who).unwrap() as usize - 1]
}
#[test]
fn basic_stuff() {
new_test_ext().execute_with(|| {
assert_eq!(CoreFellowship::rank_to_index(0), None);
assert_eq!(CoreFellowship::rank_to_index(1), Some(0));
assert_eq!(CoreFellowship::rank_to_index(9), Some(8));
assert_eq!(CoreFellowship::rank_to_index(10), None);
assert_eq!(CoreFellowship::get_salary(0, &1), 0);
});
}
#[test]
fn set_params_works() {
new_test_ext().execute_with(|| {
let params = ParamsType {
active_salary: [10, 20, 30, 40, 50, 60, 70, 80, 90],
passive_salary: [1, 2, 3, 4, 5, 6, 7, 8, 9],
demotion_period: [1, 2, 3, 4, 5, 6, 7, 8, 9],
min_promotion_period: [1, 2, 3, 4, 5, 10, 15, 20, 30],
offboard_timeout: 1,
};
assert_noop!(
CoreFellowship::set_params(signed(2), Box::new(params.clone())),
DispatchError::BadOrigin
);
assert_ok!(CoreFellowship::set_params(signed(1), Box::new(params)));
});
}
#[test]
fn induct_works() {
new_test_ext().execute_with(|| {
set_rank(0, 0);
assert_ok!(CoreFellowship::import(signed(0)));
set_rank(1, 1);
assert_ok!(CoreFellowship::import(signed(1)));
assert_noop!(CoreFellowship::induct(signed(10), 10), DispatchError::BadOrigin);
assert_noop!(CoreFellowship::induct(signed(0), 10), DispatchError::BadOrigin);
assert_ok!(CoreFellowship::induct(signed(1), 10));
assert_noop!(CoreFellowship::induct(signed(1), 10), Error::<Test>::AlreadyInducted);
});
}
#[test]
fn promote_works() {
new_test_ext().execute_with(|| {
set_rank(1, 1);
assert_ok!(CoreFellowship::import(signed(1)));
assert_noop!(CoreFellowship::promote(signed(1), 10, 1), Error::<Test>::Unranked);
assert_ok!(CoreFellowship::induct(signed(1), 10));
assert_noop!(CoreFellowship::promote(signed(10), 10, 1), DispatchError::BadOrigin);
assert_noop!(CoreFellowship::promote(signed(0), 10, 1), Error::<Test>::NoPermission);
assert_noop!(CoreFellowship::promote(signed(3), 10, 2), Error::<Test>::UnexpectedRank);
run_to(3);
assert_noop!(CoreFellowship::promote(signed(1), 10, 1), Error::<Test>::TooSoon);
run_to(4);
assert_ok!(CoreFellowship::promote(signed(1), 10, 1));
set_rank(11, 0);
assert_noop!(CoreFellowship::promote(signed(1), 11, 1), Error::<Test>::NotTracked);
});
}
#[test]
fn sync_works() {
new_test_ext().execute_with(|| {
set_rank(10, 5);
assert_noop!(CoreFellowship::approve(signed(4), 10, 5), Error::<Test>::NoPermission);
assert_noop!(CoreFellowship::approve(signed(6), 10, 6), Error::<Test>::UnexpectedRank);
assert_ok!(CoreFellowship::import(signed(10)));
assert!(Member::<Test>::contains_key(10));
assert_eq!(next_demotion(10), 11);
});
}
#[test]
fn auto_demote_works() {
new_test_ext().execute_with(|| {
set_rank(10, 5);
assert_ok!(CoreFellowship::import(signed(10)));
run_to(10);
assert_noop!(CoreFellowship::bump(signed(0), 10), Error::<Test>::NothingDoing);
run_to(11);
assert_ok!(CoreFellowship::bump(signed(0), 10));
assert_eq!(TestClub::rank_of(&10), Some(4));
assert_noop!(CoreFellowship::bump(signed(0), 10), Error::<Test>::NothingDoing);
assert_eq!(next_demotion(10), 19);
});
}
#[test]
fn auto_demote_offboard_works() {
new_test_ext().execute_with(|| {
set_rank(10, 1);
assert_ok!(CoreFellowship::import(signed(10)));
run_to(3);
assert_ok!(CoreFellowship::bump(signed(0), 10));
assert_eq!(TestClub::rank_of(&10), Some(0));
assert_noop!(CoreFellowship::bump(signed(0), 10), Error::<Test>::NothingDoing);
run_to(4);
assert_ok!(CoreFellowship::bump(signed(0), 10));
assert_noop!(CoreFellowship::bump(signed(0), 10), Error::<Test>::NotTracked);
});
}
#[test]
fn offboard_works() {
new_test_ext().execute_with(|| {
assert_noop!(CoreFellowship::offboard(signed(0), 10), Error::<Test>::NotTracked);
set_rank(10, 0);
assert_noop!(CoreFellowship::offboard(signed(0), 10), Error::<Test>::Ranked);
assert_ok!(CoreFellowship::import(signed(10)));
assert_noop!(CoreFellowship::offboard(signed(0), 10), Error::<Test>::Ranked);
unrank(10);
assert_ok!(CoreFellowship::offboard(signed(0), 10));
assert_noop!(CoreFellowship::offboard(signed(0), 10), Error::<Test>::NotTracked);
assert_noop!(CoreFellowship::bump(signed(0), 10), Error::<Test>::NotTracked);
});
}
#[test]
fn proof_postpones_auto_demote() {
new_test_ext().execute_with(|| {
set_rank(10, 5);
assert_ok!(CoreFellowship::import(signed(10)));
run_to(11);
assert_ok!(CoreFellowship::approve(signed(5), 10, 5));
assert_eq!(next_demotion(10), 21);
assert_noop!(CoreFellowship::bump(signed(0), 10), Error::<Test>::NothingDoing);
});
}
#[test]
fn promote_postpones_auto_demote() {
new_test_ext().execute_with(|| {
set_rank(10, 5);
assert_ok!(CoreFellowship::import(signed(10)));
run_to(19);
assert_ok!(CoreFellowship::promote(signed(6), 10, 6));
assert_eq!(next_demotion(10), 31);
assert_noop!(CoreFellowship::bump(signed(0), 10), Error::<Test>::NothingDoing);
});
}
#[test]
fn get_salary_works() {
new_test_ext().execute_with(|| {
for i in 1..=9u64 {
set_rank(10 + i, i as u16);
assert_ok!(CoreFellowship::import(signed(10 + i)));
assert_eq!(CoreFellowship::get_salary(i as u16, &(10 + i)), i * 10);
}
});
}
#[test]
fn active_changing_get_salary_works() {
new_test_ext().execute_with(|| {
for i in 1..=9u64 {
set_rank(10 + i, i as u16);
assert_ok!(CoreFellowship::import(signed(10 + i)));
assert_ok!(CoreFellowship::set_active(signed(10 + i), false));
assert_eq!(CoreFellowship::get_salary(i as u16, &(10 + i)), i);
assert_ok!(CoreFellowship::set_active(signed(10 + i), true));
assert_eq!(CoreFellowship::get_salary(i as u16, &(10 + i)), i * 10);
}
});
}
@@ -0,0 +1,393 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Autogenerated weights for pallet_core_fellowship
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2023-03-05, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz`
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024
// Executed Command:
// target/production/substrate
// benchmark
// pallet
// --steps=50
// --repeat=20
// --extrinsic=*
// --execution=wasm
// --wasm-execution=compiled
// --heap-pages=4096
// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json
// --pallet=pallet_core_fellowship
// --chain=dev
// --header=./HEADER-APACHE2
// --output=./frame/core-fellowship/src/weights.rs
// --template=./.maintain/frame-weight-template.hbs
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
#![allow(unused_imports)]
use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
use sp_std::marker::PhantomData;
/// Weight functions needed for pallet_core_fellowship.
pub trait WeightInfo {
fn set_params() -> Weight;
fn bump_offboard() -> Weight;
fn bump_demote() -> Weight;
fn set_active() -> Weight;
fn induct() -> Weight;
fn promote() -> Weight;
fn offboard() -> Weight;
fn import() -> Weight;
fn approve() -> Weight;
fn submit_evidence() -> Weight;
}
/// Weights for pallet_core_fellowship using the Substrate node and recommended hardware.
pub struct SubstrateWeight<T>(PhantomData<T>);
impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
/// Storage: CoreFellowship Params (r:0 w:1)
/// Proof: CoreFellowship Params (max_values: Some(1), max_size: Some(364), added: 859, mode: MaxEncodedLen)
fn set_params() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 10_489_000 picoseconds.
Weight::from_parts(10_873_000, 0)
.saturating_add(T::DbWeight::get().writes(1_u64))
}
/// Storage: CoreFellowship Member (r:1 w:1)
/// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen)
/// Storage: RankedCollective Members (r:1 w:1)
/// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen)
/// Storage: CoreFellowship Params (r:1 w:0)
/// Proof: CoreFellowship Params (max_values: Some(1), max_size: Some(364), added: 859, mode: MaxEncodedLen)
/// Storage: RankedCollective MemberCount (r:1 w:1)
/// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen)
/// Storage: RankedCollective IdToIndex (r:1 w:0)
/// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen)
/// Storage: CoreFellowship MemberEvidence (r:1 w:1)
/// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen)
fn bump_offboard() -> Weight {
// Proof Size summary in bytes:
// Measured: `16886`
// Estimated: `35762`
// Minimum execution time: 61_794_000 picoseconds.
Weight::from_parts(62_455_000, 35762)
.saturating_add(T::DbWeight::get().reads(6_u64))
.saturating_add(T::DbWeight::get().writes(4_u64))
}
/// Storage: CoreFellowship Member (r:1 w:1)
/// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen)
/// Storage: RankedCollective Members (r:1 w:1)
/// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen)
/// Storage: CoreFellowship Params (r:1 w:0)
/// Proof: CoreFellowship Params (max_values: Some(1), max_size: Some(364), added: 859, mode: MaxEncodedLen)
/// Storage: RankedCollective MemberCount (r:1 w:1)
/// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen)
/// Storage: RankedCollective IdToIndex (r:1 w:0)
/// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen)
/// Storage: CoreFellowship MemberEvidence (r:1 w:1)
/// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen)
fn bump_demote() -> Weight {
// Proof Size summary in bytes:
// Measured: `16996`
// Estimated: `35762`
// Minimum execution time: 63_921_000 picoseconds.
Weight::from_parts(64_871_000, 35762)
.saturating_add(T::DbWeight::get().reads(6_u64))
.saturating_add(T::DbWeight::get().writes(4_u64))
}
/// Storage: RankedCollective Members (r:1 w:0)
/// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen)
/// Storage: CoreFellowship Member (r:1 w:1)
/// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen)
fn set_active() -> Weight {
// Proof Size summary in bytes:
// Measured: `355`
// Estimated: `7021`
// Minimum execution time: 19_023_000 picoseconds.
Weight::from_parts(19_270_000, 7021)
.saturating_add(T::DbWeight::get().reads(2_u64))
.saturating_add(T::DbWeight::get().writes(1_u64))
}
/// Storage: CoreFellowship Member (r:1 w:1)
/// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen)
/// Storage: RankedCollective Members (r:1 w:1)
/// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen)
/// Storage: RankedCollective MemberCount (r:1 w:1)
/// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen)
/// Storage: RankedCollective IndexToId (r:0 w:1)
/// Proof: RankedCollective IndexToId (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen)
/// Storage: RankedCollective IdToIndex (r:0 w:1)
/// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen)
fn induct() -> Weight {
// Proof Size summary in bytes:
// Measured: `113`
// Estimated: `10500`
// Minimum execution time: 29_089_000 picoseconds.
Weight::from_parts(29_718_000, 10500)
.saturating_add(T::DbWeight::get().reads(3_u64))
.saturating_add(T::DbWeight::get().writes(5_u64))
}
/// Storage: RankedCollective Members (r:1 w:1)
/// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen)
/// Storage: CoreFellowship Member (r:1 w:1)
/// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen)
/// Storage: CoreFellowship Params (r:1 w:0)
/// Proof: CoreFellowship Params (max_values: Some(1), max_size: Some(364), added: 859, mode: MaxEncodedLen)
/// Storage: RankedCollective MemberCount (r:1 w:1)
/// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen)
/// Storage: CoreFellowship MemberEvidence (r:1 w:1)
/// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen)
/// Storage: RankedCollective IndexToId (r:0 w:1)
/// Proof: RankedCollective IndexToId (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen)
/// Storage: RankedCollective IdToIndex (r:0 w:1)
/// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen)
fn promote() -> Weight {
// Proof Size summary in bytes:
// Measured: `16864`
// Estimated: `32243`
// Minimum execution time: 59_529_000 picoseconds.
Weight::from_parts(60_057_000, 32243)
.saturating_add(T::DbWeight::get().reads(5_u64))
.saturating_add(T::DbWeight::get().writes(6_u64))
}
/// Storage: RankedCollective Members (r:1 w:0)
/// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen)
/// Storage: CoreFellowship Member (r:1 w:1)
/// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen)
fn offboard() -> Weight {
// Proof Size summary in bytes:
// Measured: `292`
// Estimated: `7021`
// Minimum execution time: 17_221_000 picoseconds.
Weight::from_parts(17_585_000, 7021)
.saturating_add(T::DbWeight::get().reads(2_u64))
.saturating_add(T::DbWeight::get().writes(1_u64))
}
/// Storage: CoreFellowship Member (r:1 w:1)
/// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen)
/// Storage: RankedCollective Members (r:1 w:0)
/// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen)
fn import() -> Weight {
// Proof Size summary in bytes:
// Measured: `280`
// Estimated: `7021`
// Minimum execution time: 18_602_000 picoseconds.
Weight::from_parts(18_813_000, 7021)
.saturating_add(T::DbWeight::get().reads(2_u64))
.saturating_add(T::DbWeight::get().writes(1_u64))
}
/// Storage: RankedCollective Members (r:1 w:0)
/// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen)
/// Storage: CoreFellowship Member (r:1 w:1)
/// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen)
/// Storage: CoreFellowship MemberEvidence (r:1 w:1)
/// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen)
fn approve() -> Weight {
// Proof Size summary in bytes:
// Measured: `16842`
// Estimated: `26915`
// Minimum execution time: 43_525_000 picoseconds.
Weight::from_parts(43_994_000, 26915)
.saturating_add(T::DbWeight::get().reads(3_u64))
.saturating_add(T::DbWeight::get().writes(2_u64))
}
/// Storage: CoreFellowship Member (r:1 w:0)
/// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen)
/// Storage: CoreFellowship MemberEvidence (r:1 w:1)
/// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen)
fn submit_evidence() -> Weight {
// Proof Size summary in bytes:
// Measured: `79`
// Estimated: `23408`
// Minimum execution time: 27_960_000 picoseconds.
Weight::from_parts(28_331_000, 23408)
.saturating_add(T::DbWeight::get().reads(2_u64))
.saturating_add(T::DbWeight::get().writes(1_u64))
}
}
// For backwards compatibility and tests
impl WeightInfo for () {
/// Storage: CoreFellowship Params (r:0 w:1)
/// Proof: CoreFellowship Params (max_values: Some(1), max_size: Some(364), added: 859, mode: MaxEncodedLen)
fn set_params() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 10_489_000 picoseconds.
Weight::from_parts(10_873_000, 0)
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
/// Storage: CoreFellowship Member (r:1 w:1)
/// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen)
/// Storage: RankedCollective Members (r:1 w:1)
/// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen)
/// Storage: CoreFellowship Params (r:1 w:0)
/// Proof: CoreFellowship Params (max_values: Some(1), max_size: Some(364), added: 859, mode: MaxEncodedLen)
/// Storage: RankedCollective MemberCount (r:1 w:1)
/// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen)
/// Storage: RankedCollective IdToIndex (r:1 w:0)
/// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen)
/// Storage: CoreFellowship MemberEvidence (r:1 w:1)
/// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen)
fn bump_offboard() -> Weight {
// Proof Size summary in bytes:
// Measured: `16886`
// Estimated: `35762`
// Minimum execution time: 61_794_000 picoseconds.
Weight::from_parts(62_455_000, 35762)
.saturating_add(RocksDbWeight::get().reads(6_u64))
.saturating_add(RocksDbWeight::get().writes(4_u64))
}
/// Storage: CoreFellowship Member (r:1 w:1)
/// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen)
/// Storage: RankedCollective Members (r:1 w:1)
/// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen)
/// Storage: CoreFellowship Params (r:1 w:0)
/// Proof: CoreFellowship Params (max_values: Some(1), max_size: Some(364), added: 859, mode: MaxEncodedLen)
/// Storage: RankedCollective MemberCount (r:1 w:1)
/// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen)
/// Storage: RankedCollective IdToIndex (r:1 w:0)
/// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen)
/// Storage: CoreFellowship MemberEvidence (r:1 w:1)
/// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen)
fn bump_demote() -> Weight {
// Proof Size summary in bytes:
// Measured: `16996`
// Estimated: `35762`
// Minimum execution time: 63_921_000 picoseconds.
Weight::from_parts(64_871_000, 35762)
.saturating_add(RocksDbWeight::get().reads(6_u64))
.saturating_add(RocksDbWeight::get().writes(4_u64))
}
/// Storage: RankedCollective Members (r:1 w:0)
/// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen)
/// Storage: CoreFellowship Member (r:1 w:1)
/// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen)
fn set_active() -> Weight {
// Proof Size summary in bytes:
// Measured: `355`
// Estimated: `7021`
// Minimum execution time: 19_023_000 picoseconds.
Weight::from_parts(19_270_000, 7021)
.saturating_add(RocksDbWeight::get().reads(2_u64))
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
/// Storage: CoreFellowship Member (r:1 w:1)
/// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen)
/// Storage: RankedCollective Members (r:1 w:1)
/// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen)
/// Storage: RankedCollective MemberCount (r:1 w:1)
/// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen)
/// Storage: RankedCollective IndexToId (r:0 w:1)
/// Proof: RankedCollective IndexToId (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen)
/// Storage: RankedCollective IdToIndex (r:0 w:1)
/// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen)
fn induct() -> Weight {
// Proof Size summary in bytes:
// Measured: `113`
// Estimated: `10500`
// Minimum execution time: 29_089_000 picoseconds.
Weight::from_parts(29_718_000, 10500)
.saturating_add(RocksDbWeight::get().reads(3_u64))
.saturating_add(RocksDbWeight::get().writes(5_u64))
}
/// Storage: RankedCollective Members (r:1 w:1)
/// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen)
/// Storage: CoreFellowship Member (r:1 w:1)
/// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen)
/// Storage: CoreFellowship Params (r:1 w:0)
/// Proof: CoreFellowship Params (max_values: Some(1), max_size: Some(364), added: 859, mode: MaxEncodedLen)
/// Storage: RankedCollective MemberCount (r:1 w:1)
/// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen)
/// Storage: CoreFellowship MemberEvidence (r:1 w:1)
/// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen)
/// Storage: RankedCollective IndexToId (r:0 w:1)
/// Proof: RankedCollective IndexToId (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen)
/// Storage: RankedCollective IdToIndex (r:0 w:1)
/// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen)
fn promote() -> Weight {
// Proof Size summary in bytes:
// Measured: `16864`
// Estimated: `32243`
// Minimum execution time: 59_529_000 picoseconds.
Weight::from_parts(60_057_000, 32243)
.saturating_add(RocksDbWeight::get().reads(5_u64))
.saturating_add(RocksDbWeight::get().writes(6_u64))
}
/// Storage: RankedCollective Members (r:1 w:0)
/// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen)
/// Storage: CoreFellowship Member (r:1 w:1)
/// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen)
fn offboard() -> Weight {
// Proof Size summary in bytes:
// Measured: `292`
// Estimated: `7021`
// Minimum execution time: 17_221_000 picoseconds.
Weight::from_parts(17_585_000, 7021)
.saturating_add(RocksDbWeight::get().reads(2_u64))
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
/// Storage: CoreFellowship Member (r:1 w:1)
/// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen)
/// Storage: RankedCollective Members (r:1 w:0)
/// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen)
fn import() -> Weight {
// Proof Size summary in bytes:
// Measured: `280`
// Estimated: `7021`
// Minimum execution time: 18_602_000 picoseconds.
Weight::from_parts(18_813_000, 7021)
.saturating_add(RocksDbWeight::get().reads(2_u64))
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
/// Storage: RankedCollective Members (r:1 w:0)
/// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen)
/// Storage: CoreFellowship Member (r:1 w:1)
/// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen)
/// Storage: CoreFellowship MemberEvidence (r:1 w:1)
/// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen)
fn approve() -> Weight {
// Proof Size summary in bytes:
// Measured: `16842`
// Estimated: `26915`
// Minimum execution time: 43_525_000 picoseconds.
Weight::from_parts(43_994_000, 26915)
.saturating_add(RocksDbWeight::get().reads(3_u64))
.saturating_add(RocksDbWeight::get().writes(2_u64))
}
/// Storage: CoreFellowship Member (r:1 w:0)
/// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen)
/// Storage: CoreFellowship MemberEvidence (r:1 w:1)
/// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen)
fn submit_evidence() -> Weight {
// Proof Size summary in bytes:
// Measured: `79`
// Estimated: `23408`
// Minimum execution time: 27_960_000 picoseconds.
Weight::from_parts(28_331_000, 23408)
.saturating_add(RocksDbWeight::get().reads(2_u64))
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
}
+2 -2
View File
@@ -82,8 +82,8 @@ pub trait Pay {
/// after this call. Used in benchmarking code.
#[cfg(feature = "runtime-benchmarks")]
fn ensure_successful(who: &Self::AccountId, amount: Self::Balance);
/// Ensure that a call to check_payment with the given parameters will return either Success
/// or Failure.
/// Ensure that a call to `check_payment` with the given parameters will return either `Success`
/// or `Failure`.
#[cfg(feature = "runtime-benchmarks")]
fn ensure_concluded(id: Self::Id);
}
+2 -5
View File
@@ -878,12 +878,9 @@ impl<
#[cfg(feature = "runtime-benchmarks")]
fn try_successful_origin() -> Result<O, ()> {
let zero_account_id =
AccountId::decode(&mut TrailingZeroInput::zeroes()).map_err(|_| ())?;
let members = Who::sorted_members();
let first_member = match members.get(0) {
let first_member = match Who::sorted_members().first() {
Some(account) => account.clone(),
None => zero_account_id,
None => AccountId::decode(&mut TrailingZeroInput::zeroes()).map_err(|_| ())?,
};
Ok(O::from(RawOrigin::Signed(first_member)))
}
@@ -148,6 +148,20 @@ impl<
{
}
/// A meta trait for arithmetic.
///
/// Arithmetic types do all the usual stuff you'd expect numbers to do. They are guaranteed to
/// be able to represent at least `u8` values without loss, hence the trait implies `From<u8>`
/// and smaller integers. All other conversions are fallible.
pub trait AtLeast8Bit: BaseArithmetic + From<u8> {}
impl<T: BaseArithmetic + From<u8>> AtLeast8Bit for T {}
/// A meta trait for arithmetic. Same as [`AtLeast8Bit `], but also bounded to be unsigned.
pub trait AtLeast8BitUnsigned: AtLeast8Bit + Unsigned {}
impl<T: AtLeast8Bit + Unsigned> AtLeast8BitUnsigned for T {}
/// A meta trait for arithmetic.
///
/// Arithmetic types do all the usual stuff you'd expect numbers to do. They are guaranteed to
@@ -220,6 +234,24 @@ pub trait Saturating {
/// instead of overflowing.
fn saturating_pow(self, exp: usize) -> Self;
/// Decrement self by one, saturating at zero.
fn saturating_less_one(mut self) -> Self
where
Self: One,
{
self.saturating_dec();
self
}
/// Increment self by one, saturating at the numeric bounds instead of overflowing.
fn saturating_plus_one(mut self) -> Self
where
Self: One,
{
self.saturating_inc();
self
}
/// Increment self by one, saturating.
fn saturating_inc(&mut self)
where
@@ -319,6 +319,24 @@ impl<T> TryMorph<T> for Identity {
}
}
/// Implementation of `Morph` which converts between types using `Into`.
pub struct MorphInto<T>(sp_std::marker::PhantomData<T>);
impl<T, A: Into<T>> Morph<A> for MorphInto<T> {
type Outcome = T;
fn morph(a: A) -> T {
a.into()
}
}
/// Implementation of `TryMorph` which attmepts to convert between types using `TryInto`.
pub struct TryMorphInto<T>(sp_std::marker::PhantomData<T>);
impl<T, A: TryInto<T>> TryMorph<A> for TryMorphInto<T> {
type Outcome = T;
fn try_morph(a: A) -> Result<T, ()> {
a.try_into().map_err(|_| ())
}
}
/// Create a `Morph` and/or `TryMorph` impls with a simple closure-like expression.
///
/// # Examples