Society v2 (#11324)

* New Society

* More logic drafting

* More work

* Building

* Some tests

* Fixes

* Improvements to the voting process

* More tests

* Test number 20

* Tests

* 30 tests

* Another test]

* All tests enabled

* Minor stuff

* generate_storage_alias: Rewrite as proc macro attribute

This rewrites the `generate_storage_alias!` declarative macro as proc-macro attribute. While doing
this the name is changed to `storage_alias`. The prefix can now also be the name of a pallet. This
makes storage aliases work in migrations for all kind of chains and not just for the ones that use
predefined prefixes.

* Maintenance operations don't pay fee

* Fix compilation and FMT

* Moare fixes

* Migrations

* Fix tests and add migration testing

* Introduce lazy-cleanup and avoid unbounded prefix removal

* Fixes

* Fixes

* [WIP][Society] Adding benchmarking to the v2. (#11776)

* [Society] Adding benchmarking to the v2.

* [Society] Code review.

* [Society] Better code.

* Using clear() + clear_prefix() and adding more tests.

* Benchmarking again...

* Fix Cargo

* Fixes

* Fixes

* Spelling

* Fix benchmarks

* Another fix

* Remove println

---------

Co-authored-by: Bastian Köcher <info@kchr.de>
Co-authored-by: Artur Gontijo <arturgontijo@users.noreply.github.com>
This commit is contained in:
Gavin Wood
2023-06-18 18:22:17 +02:00
committed by GitHub
parent 116b6e65dc
commit 33a6536299
25 changed files with 3416 additions and 1444 deletions
+6 -2
View File
@@ -979,9 +979,9 @@ checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
[[package]]
name = "bounded-collections"
version = "0.1.7"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07fbd1d11282a1eb134d3c3b7cf8ce213b5161c6e5f73fb1b98618482c606b64"
checksum = "eb5b05133427c07c4776906f673ccf36c21b102c9829c641a5b56bd151d44fd6"
dependencies = [
"log",
"parity-scale-codec",
@@ -7300,13 +7300,17 @@ dependencies = [
name = "pallet-society"
version = "4.0.0-dev"
dependencies = [
"frame-benchmarking",
"frame-support",
"frame-support-test",
"frame-system",
"hex-literal",
"log",
"pallet-balances",
"parity-scale-codec",
"rand_chacha 0.2.2",
"scale-info",
"sp-arithmetic",
"sp-core",
"sp-io",
"sp-runtime",
+1 -9
View File
@@ -348,15 +348,7 @@ pub fn testnet_genesis(
grandpa: GrandpaConfig { authorities: vec![] },
technical_membership: Default::default(),
treasury: Default::default(),
society: SocietyConfig {
members: endowed_accounts
.iter()
.take((num_endowed_accounts + 1) / 2)
.cloned()
.collect(),
pot: 0,
max_members: 999,
},
society: SocietyConfig { pot: 0 },
vesting: Default::default(),
assets: pallet_assets::GenesisConfig {
// This asset is used by the NIS pallet as counterpart currency.
+12 -12
View File
@@ -1398,14 +1398,14 @@ impl pallet_recovery::Config for Runtime {
}
parameter_types! {
pub const CandidateDeposit: Balance = 10 * DOLLARS;
pub const WrongSideDeduction: Balance = 2 * DOLLARS;
pub const MaxStrikes: u32 = 10;
pub const RotationPeriod: BlockNumber = 80 * HOURS;
pub const GraceStrikes: u32 = 10;
pub const SocietyVotingPeriod: BlockNumber = 80 * HOURS;
pub const ClaimPeriod: BlockNumber = 80 * HOURS;
pub const PeriodSpend: Balance = 500 * DOLLARS;
pub const MaxLockDuration: BlockNumber = 36 * 30 * DAYS;
pub const ChallengePeriod: BlockNumber = 7 * DAYS;
pub const MaxCandidateIntake: u32 = 10;
pub const MaxPayouts: u32 = 10;
pub const MaxBids: u32 = 10;
pub const SocietyPalletId: PalletId = PalletId(*b"py/socie");
}
@@ -1414,18 +1414,17 @@ impl pallet_society::Config for Runtime {
type PalletId = SocietyPalletId;
type Currency = Balances;
type Randomness = RandomnessCollectiveFlip;
type CandidateDeposit = CandidateDeposit;
type WrongSideDeduction = WrongSideDeduction;
type MaxStrikes = MaxStrikes;
type GraceStrikes = GraceStrikes;
type PeriodSpend = PeriodSpend;
type MembershipChanged = ();
type RotationPeriod = RotationPeriod;
type VotingPeriod = SocietyVotingPeriod;
type ClaimPeriod = ClaimPeriod;
type MaxLockDuration = MaxLockDuration;
type FounderSetOrigin =
pallet_collective::EnsureProportionMoreThan<AccountId, CouncilCollective, 1, 2>;
type SuspensionJudgementOrigin = pallet_society::EnsureFounder<Runtime>;
type MaxCandidateIntake = MaxCandidateIntake;
type ChallengePeriod = ChallengePeriod;
type MaxPayouts = MaxPayouts;
type MaxBids = MaxBids;
type WeightInfo = pallet_society::weights::SubstrateWeight<Runtime>;
}
parameter_types! {
@@ -2048,6 +2047,7 @@ mod benches {
[pallet_scheduler, Scheduler]
[pallet_glutton, Glutton]
[pallet_session, SessionBench::<Runtime>]
[pallet_society, Society]
[pallet_staking, Staking]
[pallet_state_trie_migration, StateTrieMigration]
[pallet_sudo, Sudo]
+1 -1
View File
@@ -87,7 +87,7 @@ pub fn config_endowed(code: Option<&[u8]>, extra_endowed: Vec<AccountId>) -> Run
elections: Default::default(),
sudo: Default::default(),
treasury: Default::default(),
society: SocietyConfig { members: vec![alice(), bob()], pot: 0, max_members: 999 },
society: SocietyConfig { pot: 0 },
vesting: Default::default(),
assets: AssetsConfig { assets: vec![(9, alice(), true, 1)], ..Default::default() },
pool_assets: Default::default(),
@@ -1,6 +1,6 @@
// This file is part of Substrate.
// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,6 +1,6 @@
// This file is part of Substrate.
// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
+1 -1
View File
@@ -202,7 +202,7 @@ impl<
.rejig(now);
}
/// The amount of this account's balance that much currently be locked due to voting.
/// The amount of this account's balance that must currently be locked due to voting.
pub fn locked_balance(&self) -> Balance {
match self {
Voting::Direct { votes, prior, .. } =>
@@ -1,6 +1,6 @@
// This file is part of Substrate.
// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
+1 -1
View File
@@ -1,6 +1,6 @@
// This file is part of Substrate.
// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
+16 -4
View File
@@ -13,13 +13,19 @@ readme = "README.md"
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] }
hex-literal = "0.3.4"
log = { version = "0.4.17", default-features = false }
rand_chacha = { version = "0.2", default-features = false }
scale-info = { version = "2.5.0", default-features = false, features = ["derive"] }
codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] }
sp-std = { version = "8.0.0", default-features = false, path = "../../primitives/std" }
sp-io = { version = "23.0.0", default-features = false, path = "../../primitives/io" }
sp-arithmetic = { version = "16.0.0", default-features = false, path = "../../primitives/arithmetic" }
sp-runtime = { version = "24.0.0", default-features = false, path = "../../primitives/runtime" }
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-runtime = { version = "24.0.0", default-features = false, path = "../../primitives/runtime" }
sp-std = { version = "8.0.0", default-features = false, path = "../../primitives/std" }
[dev-dependencies]
frame-support-test = { version = "3.0.0", path = "../support/test" }
@@ -31,15 +37,21 @@ sp-io = { version = "23.0.0", path = "../../primitives/io" }
default = ["std"]
std = [
"codec/std",
"frame-benchmarking?/std",
"frame-support/std",
"frame-system/std",
"rand_chacha/std",
"scale-info/std",
"sp-runtime/std",
"sp-std/std",
"sp-io/std",
]
runtime-benchmarks = [
"frame-system/runtime-benchmarks",
"frame-benchmarking",
"sp-runtime/runtime-benchmarks",
"frame-benchmarking/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"pallet-balances/runtime-benchmarks",
]
try-runtime = ["frame-support/try-runtime"]
+376
View File
@@ -0,0 +1,376 @@
// 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.
//! Society pallet benchmarking.
#![cfg(feature = "runtime-benchmarks")]
use super::*;
use frame_benchmarking::{account, benchmarks_instance_pallet, whitelisted_caller};
use frame_system::RawOrigin;
use sp_runtime::traits::Bounded;
use crate::Pallet as Society;
fn mock_balance_deposit<T: Config<I>, I: 'static>() -> BalanceOf<T, I> {
T::Currency::minimum_balance().saturating_mul(1_000u32.into())
}
fn make_deposit<T: Config<I>, I: 'static>(who: &T::AccountId) -> BalanceOf<T, I> {
let amount = mock_balance_deposit::<T, I>();
let required = amount.saturating_add(T::Currency::minimum_balance());
if T::Currency::free_balance(who) < required {
T::Currency::make_free_balance_be(who, required);
}
T::Currency::reserve(who, amount).expect("Pre-funded account; qed");
amount
}
fn make_bid<T: Config<I>, I: 'static>(
who: &T::AccountId,
) -> BidKind<T::AccountId, BalanceOf<T, I>> {
BidKind::Deposit(make_deposit::<T, I>(who))
}
fn fund_society<T: Config<I>, I: 'static>() {
T::Currency::make_free_balance_be(
&Society::<T, I>::account_id(),
BalanceOf::<T, I>::max_value(),
);
Pot::<T, I>::put(&BalanceOf::<T, I>::max_value());
}
// Set up Society
fn setup_society<T: Config<I>, I: 'static>() -> Result<T::AccountId, &'static str> {
let origin = T::FounderSetOrigin::try_successful_origin().map_err(|_| "No origin")?;
let founder: T::AccountId = account("founder", 0, 0);
let founder_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(founder.clone());
let max_members = 5u32;
let max_intake = 3u32;
let max_strikes = 3u32;
Society::<T, I>::found_society(
origin,
founder_lookup,
max_members,
max_intake,
max_strikes,
mock_balance_deposit::<T, I>(),
b"benchmarking-society".to_vec(),
)?;
T::Currency::make_free_balance_be(
&Society::<T, I>::account_id(),
T::Currency::minimum_balance(),
);
T::Currency::make_free_balance_be(&Society::<T, I>::payouts(), T::Currency::minimum_balance());
Ok(founder)
}
fn setup_funded_society<T: Config<I>, I: 'static>() -> Result<T::AccountId, &'static str> {
let founder = setup_society::<T, I>()?;
fund_society::<T, I>();
Ok(founder)
}
fn add_candidate<T: Config<I>, I: 'static>(
name: &'static str,
tally: Tally,
skeptic_struck: bool,
) -> T::AccountId {
let candidate: T::AccountId = account(name, 0, 0);
let candidacy = Candidacy {
round: RoundCount::<T, I>::get(),
kind: make_bid::<T, I>(&candidate),
bid: 0u32.into(),
tally,
skeptic_struck,
};
Candidates::<T, I>::insert(&candidate, &candidacy);
candidate
}
fn increment_round<T: Config<I>, I: 'static>() {
let mut round_count = RoundCount::<T, I>::get();
round_count.saturating_inc();
RoundCount::<T, I>::put(round_count);
}
benchmarks_instance_pallet! {
bid {
let founder = setup_society::<T, I>()?;
let caller: T::AccountId = whitelisted_caller();
T::Currency::make_free_balance_be(&caller, BalanceOf::<T, I>::max_value());
}: _(RawOrigin::Signed(caller.clone()), 10u32.into())
verify {
let first_bid: Bid<T::AccountId, BalanceOf<T, I>> = Bid {
who: caller.clone(),
kind: BidKind::Deposit(mock_balance_deposit::<T, I>()),
value: 10u32.into(),
};
assert_eq!(Bids::<T, I>::get(), vec![first_bid]);
}
unbid {
let founder = setup_society::<T, I>()?;
let caller: T::AccountId = whitelisted_caller();
T::Currency::make_free_balance_be(&caller, BalanceOf::<T, I>::max_value());
let mut bids = Bids::<T, I>::get();
Society::<T, I>::insert_bid(&mut bids, &caller, 10u32.into(), make_bid::<T, I>(&caller));
Bids::<T, I>::put(bids);
}: _(RawOrigin::Signed(caller.clone()))
verify {
assert_eq!(Bids::<T, I>::get(), vec![]);
}
vouch {
let founder = setup_society::<T, I>()?;
let caller: T::AccountId = whitelisted_caller();
let vouched: T::AccountId = account("vouched", 0, 0);
T::Currency::make_free_balance_be(&caller, BalanceOf::<T, I>::max_value());
let _ = Society::<T, I>::insert_member(&caller, 1u32.into());
let vouched_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(vouched.clone());
}: _(RawOrigin::Signed(caller.clone()), vouched_lookup, 0u32.into(), 0u32.into())
verify {
let bids = Bids::<T, I>::get();
let vouched_bid: Bid<T::AccountId, BalanceOf<T, I>> = Bid {
who: vouched.clone(),
kind: BidKind::Vouch(caller.clone(), 0u32.into()),
value: 0u32.into(),
};
assert_eq!(bids, vec![vouched_bid]);
}
unvouch {
let founder = setup_society::<T, I>()?;
let caller: T::AccountId = whitelisted_caller();
let vouched: T::AccountId = account("vouched", 0, 0);
T::Currency::make_free_balance_be(&caller, BalanceOf::<T, I>::max_value());
let mut bids = Bids::<T, I>::get();
Society::<T, I>::insert_bid(&mut bids, &caller, 10u32.into(), BidKind::Vouch(caller.clone(), 0u32.into()));
Bids::<T, I>::put(bids);
}: _(RawOrigin::Signed(caller.clone()))
verify {
assert_eq!(Bids::<T, I>::get(), vec![]);
}
vote {
let founder = setup_society::<T, I>()?;
let caller: T::AccountId = whitelisted_caller();
T::Currency::make_free_balance_be(&caller, BalanceOf::<T, I>::max_value());
let _ = Society::<T, I>::insert_member(&caller, 1u32.into());
let candidate = add_candidate::<T, I>("candidate", Default::default(), false);
let candidate_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(candidate.clone());
}: _(RawOrigin::Signed(caller.clone()), candidate_lookup, true)
verify {
let maybe_vote: Vote = <Votes<T, I>>::get(candidate.clone(), caller).unwrap();
assert_eq!(maybe_vote.approve, true);
}
defender_vote {
let founder = setup_society::<T, I>()?;
let caller: T::AccountId = whitelisted_caller();
T::Currency::make_free_balance_be(&caller, BalanceOf::<T, I>::max_value());
let _ = Society::<T, I>::insert_member(&caller, 1u32.into());
let defender: T::AccountId = account("defender", 0, 0);
Defending::<T, I>::put((defender, caller.clone(), Tally::default()));
}: _(RawOrigin::Signed(caller.clone()), false)
verify {
let round = RoundCount::<T, I>::get();
let skeptic_vote: Vote = DefenderVotes::<T, I>::get(round, &caller).unwrap();
assert_eq!(skeptic_vote.approve, false);
}
payout {
let founder = setup_funded_society::<T, I>()?;
// Payee's account already exists and is a member.
let caller: T::AccountId = whitelisted_caller();
T::Currency::make_free_balance_be(&caller, mock_balance_deposit::<T, I>());
let _ = Society::<T, I>::insert_member(&caller, 0u32.into());
// Introduce payout.
Society::<T, I>::bump_payout(&caller, 0u32.into(), 1u32.into());
}: _(RawOrigin::Signed(caller.clone()))
verify {
let record = Payouts::<T, I>::get(caller);
assert!(record.payouts.is_empty());
}
waive_repay {
let founder = setup_funded_society::<T, I>()?;
let caller: T::AccountId = whitelisted_caller();
T::Currency::make_free_balance_be(&caller, BalanceOf::<T, I>::max_value());
let _ = Society::<T, I>::insert_member(&caller, 0u32.into());
Society::<T, I>::bump_payout(&caller, 0u32.into(), 1u32.into());
}: _(RawOrigin::Signed(caller.clone()), 1u32.into())
verify {
let record = Payouts::<T, I>::get(caller);
assert!(record.payouts.is_empty());
}
found_society {
let founder: T::AccountId = whitelisted_caller();
let can_found = T::FounderSetOrigin::try_successful_origin().map_err(|_| "No origin")?;
let founder_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(founder.clone());
}: _<T::RuntimeOrigin>(can_found, founder_lookup, 5, 3, 3, mock_balance_deposit::<T, I>(), b"benchmarking-society".to_vec())
verify {
assert_eq!(Founder::<T, I>::get(), Some(founder.clone()));
}
dissolve {
let founder = setup_society::<T, I>()?;
let members_and_candidates = vec![("m1", "c1"), ("m2", "c2"), ("m3", "c3"), ("m4", "c4")];
let members_count = members_and_candidates.clone().len() as u32;
for (m, c) in members_and_candidates {
let member: T::AccountId = account(m, 0, 0);
let _ = Society::<T, I>::insert_member(&member, 100u32.into());
let candidate = add_candidate::<T, I>(c, Tally { approvals: 1u32.into(), rejections: 1u32.into() }, false);
let candidate_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(candidate);
let _ = Society::<T, I>::vote(RawOrigin::Signed(member).into(), candidate_lookup, true);
}
// Leaving only Founder member.
MemberCount::<T, I>::mutate(|i| { i.saturating_reduce(members_count) });
}: _(RawOrigin::Signed(founder))
verify {
assert_eq!(Founder::<T, I>::get(), None);
}
judge_suspended_member {
let founder = setup_society::<T, I>()?;
let caller: T::AccountId = whitelisted_caller();
let caller_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(caller.clone());
let _ = Society::<T, I>::insert_member(&caller, 0u32.into());
let _ = Society::<T, I>::suspend_member(&caller);
}: _(RawOrigin::Signed(founder), caller_lookup, false)
verify {
assert_eq!(SuspendedMembers::<T, I>::contains_key(&caller), false);
}
set_parameters {
let founder = setup_society::<T, I>()?;
let max_members = 10u32;
let max_intake = 10u32;
let max_strikes = 10u32;
let candidate_deposit: BalanceOf<T, I> = 10u32.into();
let params = GroupParams { max_members, max_intake, max_strikes, candidate_deposit };
}: _(RawOrigin::Signed(founder), max_members, max_intake, max_strikes, candidate_deposit)
verify {
assert_eq!(Parameters::<T, I>::get(), Some(params));
}
punish_skeptic {
let founder = setup_society::<T, I>()?;
let candidate = add_candidate::<T, I>("candidate", Default::default(), false);
let skeptic: T::AccountId = account("skeptic", 0, 0);
let _ = Society::<T, I>::insert_member(&skeptic, 0u32.into());
Skeptic::<T, I>::put(&skeptic);
if let Period::Voting { more, .. } = Society::<T, I>::period() {
frame_system::Pallet::<T>::set_block_number(frame_system::Pallet::<T>::block_number() + more);
}
}: _(RawOrigin::Signed(candidate.clone()))
verify {
let candidacy = Candidates::<T, I>::get(&candidate).unwrap();
assert_eq!(candidacy.skeptic_struck, true);
}
claim_membership {
let founder = setup_society::<T, I>()?;
let candidate = add_candidate::<T, I>("candidate", Tally { approvals: 3u32.into(), rejections: 0u32.into() }, false);
increment_round::<T, I>();
}: _(RawOrigin::Signed(candidate.clone()))
verify {
assert!(!Candidates::<T, I>::contains_key(&candidate));
assert!(Members::<T, I>::contains_key(&candidate));
}
bestow_membership {
let founder = setup_society::<T, I>()?;
let candidate = add_candidate::<T, I>("candidate", Tally { approvals: 3u32.into(), rejections: 1u32.into() }, false);
increment_round::<T, I>();
}: _(RawOrigin::Signed(founder), candidate.clone())
verify {
assert!(!Candidates::<T, I>::contains_key(&candidate));
assert!(Members::<T, I>::contains_key(&candidate));
}
kick_candidate {
let founder = setup_society::<T, I>()?;
let candidate = add_candidate::<T, I>("candidate", Tally { approvals: 1u32.into(), rejections: 1u32.into() }, false);
increment_round::<T, I>();
}: _(RawOrigin::Signed(founder), candidate.clone())
verify {
assert!(!Candidates::<T, I>::contains_key(&candidate));
}
resign_candidacy {
let founder = setup_society::<T, I>()?;
let candidate = add_candidate::<T, I>("candidate", Tally { approvals: 0u32.into(), rejections: 0u32.into() }, false);
}: _(RawOrigin::Signed(candidate.clone()))
verify {
assert!(!Candidates::<T, I>::contains_key(&candidate));
}
drop_candidate {
let founder = setup_society::<T, I>()?;
let candidate = add_candidate::<T, I>("candidate", Tally { approvals: 0u32.into(), rejections: 3u32.into() }, false);
let caller: T::AccountId = whitelisted_caller();
let _ = Society::<T, I>::insert_member(&caller, 0u32.into());
let mut round_count = RoundCount::<T, I>::get();
round_count = round_count.saturating_add(2u32);
RoundCount::<T, I>::put(round_count);
}: _(RawOrigin::Signed(caller), candidate.clone())
verify {
assert!(!Candidates::<T, I>::contains_key(&candidate));
}
cleanup_candidacy {
let founder = setup_society::<T, I>()?;
let candidate = add_candidate::<T, I>("candidate", Tally { approvals: 0u32.into(), rejections: 0u32.into() }, false);
let member_one: T::AccountId = account("one", 0, 0);
let member_two: T::AccountId = account("two", 0, 0);
let _ = Society::<T, I>::insert_member(&member_one, 0u32.into());
let _ = Society::<T, I>::insert_member(&member_two, 0u32.into());
let candidate_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(candidate.clone());
let _ = Society::<T, I>::vote(RawOrigin::Signed(member_one.clone()).into(), candidate_lookup.clone(), true);
let _ = Society::<T, I>::vote(RawOrigin::Signed(member_two.clone()).into(), candidate_lookup, true);
Candidates::<T, I>::remove(&candidate);
}: _(RawOrigin::Signed(member_one), candidate.clone(), 5)
verify {
assert_eq!(Votes::<T, I>::get(&candidate, &member_two), None);
}
cleanup_challenge {
let founder = setup_society::<T, I>()?;
ChallengeRoundCount::<T, I>::put(1u32);
let member: T::AccountId = whitelisted_caller();
let _ = Society::<T, I>::insert_member(&member, 0u32.into());
let defender: T::AccountId = account("defender", 0, 0);
Defending::<T, I>::put((defender.clone(), member.clone(), Tally::default()));
let _ = Society::<T, I>::defender_vote(RawOrigin::Signed(member.clone()).into(), true);
ChallengeRoundCount::<T, I>::put(2u32);
let mut challenge_round = ChallengeRoundCount::<T, I>::get();
challenge_round = challenge_round.saturating_sub(1u32);
}: _(RawOrigin::Signed(member.clone()), challenge_round, 1u32)
verify {
assert_eq!(DefenderVotes::<T, I>::get(challenge_round, &defender), None);
}
impl_benchmark_test_suite!(
Society,
sp_io::TestExternalities::from(frame_system::GenesisConfig::default().build_storage::<crate::mock::Test>().unwrap()),
crate::mock::Test
);
}
File diff suppressed because it is too large Load Diff
+329
View File
@@ -0,0 +1,329 @@
// 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.
//! # Migrations for Society Pallet
use super::*;
use codec::{Decode, Encode};
use frame_support::traits::{Instance, OnRuntimeUpgrade};
#[cfg(feature = "try-runtime")]
use sp_runtime::TryRuntimeError;
/// The log target.
const TARGET: &'static str = "runtime::society::migration";
/// This migration moves all the state to v2 of Society.
pub struct MigrateToV2<T: Config<I>, I: 'static, PastPayouts>(
sp_std::marker::PhantomData<(T, I, PastPayouts)>,
);
impl<
T: Config<I>,
I: Instance + 'static,
PastPayouts: Get<Vec<(<T as frame_system::Config>::AccountId, BalanceOf<T, I>)>>,
> OnRuntimeUpgrade for MigrateToV2<T, I, PastPayouts>
{
#[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
ensure!(can_migrate::<T, I>(), "pallet_society: already upgraded");
let current = Pallet::<T, I>::current_storage_version();
let onchain = Pallet::<T, I>::on_chain_storage_version();
ensure!(onchain == 0 && current == 2, "pallet_society: invalid version");
Ok((old::Candidates::<T, I>::get(), old::Members::<T, I>::get()).encode())
}
fn on_runtime_upgrade() -> Weight {
let current = Pallet::<T, I>::current_storage_version();
let onchain = Pallet::<T, I>::on_chain_storage_version();
if current == 2 && onchain == 0 {
from_original::<T, I>(&mut PastPayouts::get())
} else {
log::info!(
"Running migration with current storage version {:?} / onchain {:?}",
current,
onchain
);
T::DbWeight::get().reads(1)
}
}
#[cfg(feature = "try-runtime")]
fn post_upgrade(data: Vec<u8>) -> Result<(), TryRuntimeError> {
let old: (
Vec<Bid<<T as frame_system::Config>::AccountId, BalanceOf<T, I>>>,
Vec<<T as frame_system::Config>::AccountId>,
) = Decode::decode(&mut &data[..]).expect("Bad data");
let mut old_candidates =
old.0.into_iter().map(|x| (x.who, x.kind, x.value)).collect::<Vec<_>>();
let mut old_members = old.1;
let mut candidates =
Candidates::<T, I>::iter().map(|(k, v)| (k, v.kind, v.bid)).collect::<Vec<_>>();
let mut members = Members::<T, I>::iter_keys().collect::<Vec<_>>();
old_candidates.sort_by_key(|x| x.0.clone());
candidates.sort_by_key(|x| x.0.clone());
assert_eq!(candidates, old_candidates);
members.sort();
old_members.sort();
assert_eq!(members, old_members);
ensure!(
Pallet::<T, I>::on_chain_storage_version() == 2,
"The onchain version must be updated after the migration."
);
assert_internal_consistency::<T, I>();
Ok(())
}
}
pub(crate) mod old {
use super::*;
use frame_support::storage_alias;
/// A vote by a member on a candidate application.
#[derive(Encode, Decode, Copy, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)]
pub enum Vote {
/// The member has been chosen to be skeptic and has not yet taken any action.
Skeptic,
/// The member has rejected the candidate's application.
Reject,
/// The member approves of the candidate's application.
Approve,
}
#[storage_alias]
pub type Bids<T: Config<I>, I: 'static> = StorageValue<
Pallet<T, I>,
Vec<Bid<<T as frame_system::Config>::AccountId, BalanceOf<T, I>>>,
ValueQuery,
>;
#[storage_alias]
pub type Candidates<T: Config<I>, I: 'static> = StorageValue<
Pallet<T, I>,
Vec<Bid<<T as frame_system::Config>::AccountId, BalanceOf<T, I>>>,
ValueQuery,
>;
#[storage_alias]
pub type Votes<T: Config<I>, I: 'static> = StorageDoubleMap<
Pallet<T, I>,
Twox64Concat,
<T as frame_system::Config>::AccountId,
Twox64Concat,
<T as frame_system::Config>::AccountId,
Vote,
>;
#[storage_alias]
pub type SuspendedCandidates<T: Config<I>, I: 'static> = StorageMap<
Pallet<T, I>,
Twox64Concat,
<T as frame_system::Config>::AccountId,
(BalanceOf<T, I>, BidKind<<T as frame_system::Config>::AccountId, BalanceOf<T, I>>),
>;
#[storage_alias]
pub type Members<T: Config<I>, I: 'static> =
StorageValue<Pallet<T, I>, Vec<<T as frame_system::Config>::AccountId>, ValueQuery>;
#[storage_alias]
pub type Vouching<T: Config<I>, I: 'static> = StorageMap<
Pallet<T, I>,
Twox64Concat,
<T as frame_system::Config>::AccountId,
VouchingStatus,
>;
#[storage_alias]
pub type Strikes<T: Config<I>, I: 'static> = StorageMap<
Pallet<T, I>,
Twox64Concat,
<T as frame_system::Config>::AccountId,
StrikeCount,
ValueQuery,
>;
#[storage_alias]
pub type Payouts<T: Config<I>, I: 'static> = StorageMap<
Pallet<T, I>,
Twox64Concat,
<T as frame_system::Config>::AccountId,
Vec<(<T as frame_system::Config>::BlockNumber, BalanceOf<T, I>)>,
ValueQuery,
>;
#[storage_alias]
pub type SuspendedMembers<T: Config<I>, I: 'static> = StorageMap<
Pallet<T, I>,
Twox64Concat,
<T as frame_system::Config>::AccountId,
bool,
ValueQuery,
>;
#[storage_alias]
pub type Defender<T: Config<I>, I: 'static> =
StorageValue<Pallet<T, I>, <T as frame_system::Config>::AccountId>;
#[storage_alias]
pub type DefenderVotes<T: Config<I>, I: 'static> =
StorageMap<Pallet<T, I>, Twox64Concat, <T as frame_system::Config>::AccountId, Vote>;
}
pub fn can_migrate<T: Config<I>, I: Instance + 'static>() -> bool {
old::Members::<T, I>::exists()
}
/// Will panic if there are any inconsistencies in the pallet's state or old keys remaining.
pub fn assert_internal_consistency<T: Config<I>, I: Instance + 'static>() {
// Check all members are valid data.
let mut members = vec![];
for m in Members::<T, I>::iter_keys() {
let r = Members::<T, I>::get(&m).expect("Member data must be valid");
members.push((m, r));
}
assert_eq!(MemberCount::<T, I>::get(), members.len() as u32);
for (who, record) in members.iter() {
assert_eq!(MemberByIndex::<T, I>::get(record.index).as_ref(), Some(who));
}
if let Some(founder) = Founder::<T, I>::get() {
assert_eq!(Members::<T, I>::get(founder).expect("founder is member").index, 0);
}
if let Some(head) = Head::<T, I>::get() {
assert!(Members::<T, I>::contains_key(head));
}
// Check all votes are valid data.
for (k1, k2) in Votes::<T, I>::iter_keys() {
assert!(Votes::<T, I>::get(k1, k2).is_some());
}
// Check all defender votes are valid data.
for (k1, k2) in DefenderVotes::<T, I>::iter_keys() {
assert!(DefenderVotes::<T, I>::get(k1, k2).is_some());
}
// Check all candidates are valid data.
for k in Candidates::<T, I>::iter_keys() {
assert!(Candidates::<T, I>::get(k).is_some());
}
// Check all suspended members are valid data.
for m in SuspendedMembers::<T, I>::iter_keys() {
assert!(SuspendedMembers::<T, I>::get(m).is_some());
}
// Check all payouts are valid data.
for p in Payouts::<T, I>::iter_keys() {
let k = Payouts::<T, I>::hashed_key_for(&p);
let v = frame_support::storage::unhashed::get_raw(&k[..]).expect("value is in map");
assert!(PayoutRecordFor::<T, I>::decode(&mut &v[..]).is_ok());
}
// We don't use these - make sure they don't exist.
assert_eq!(old::SuspendedCandidates::<T, I>::iter().count(), 0);
assert_eq!(old::Strikes::<T, I>::iter().count(), 0);
assert_eq!(old::Vouching::<T, I>::iter().count(), 0);
assert!(!old::Defender::<T, I>::exists());
assert!(!old::Members::<T, I>::exists());
}
pub fn from_original<T: Config<I>, I: Instance + 'static>(
past_payouts: &mut [(<T as frame_system::Config>::AccountId, BalanceOf<T, I>)],
) -> Weight {
// First check that this is the original state layout. This is easy since the original layout
// contained the Members value, and this value no longer exists in the new layout.
if !old::Members::<T, I>::exists() {
log::warn!(target: TARGET, "Skipping MigrateToV2 migration since it appears unapplicable");
// Already migrated or no data to migrate: Bail.
return T::DbWeight::get().reads(1)
}
// Migrate Bids from old::Bids (just a trunctation).
Bids::<T, I>::put(BoundedVec::<_, T::MaxBids>::truncate_from(old::Bids::<T, I>::take()));
// Initialise round counter.
RoundCount::<T, I>::put(0);
// Migrate Candidates from old::Candidates
for Bid { who: candidate, kind, value } in old::Candidates::<T, I>::take().into_iter() {
let mut tally = Tally::default();
// Migrate Votes from old::Votes
// No need to drain, since we're overwriting values.
for (voter, vote) in old::Votes::<T, I>::iter_prefix(&candidate) {
Votes::<T, I>::insert(
&candidate,
&voter,
Vote { approve: vote == old::Vote::Approve, weight: 1 },
);
match vote {
old::Vote::Approve => tally.approvals.saturating_inc(),
old::Vote::Reject => tally.rejections.saturating_inc(),
old::Vote::Skeptic => Skeptic::<T, I>::put(&voter),
}
}
Candidates::<T, I>::insert(
&candidate,
Candidacy { round: 0, kind, tally, skeptic_struck: false, bid: value },
);
}
// Migrate Members from old::Members old::Strikes old::Vouching
let mut member_count = 0;
for member in old::Members::<T, I>::take() {
let strikes = old::Strikes::<T, I>::take(&member);
let vouching = old::Vouching::<T, I>::take(&member);
let record = MemberRecord { index: member_count, rank: 0, strikes, vouching };
Members::<T, I>::insert(&member, record);
MemberByIndex::<T, I>::insert(member_count, &member);
member_count.saturating_inc();
}
MemberCount::<T, I>::put(member_count);
// Migrate Payouts from: old::Payouts and raw info (needed since we can't query old chain
// state).
past_payouts.sort();
for (who, mut payouts) in old::Payouts::<T, I>::iter() {
payouts.truncate(T::MaxPayouts::get() as usize);
// ^^ Safe since we already truncated.
let paid = past_payouts
.binary_search_by_key(&&who, |x| &x.0)
.ok()
.map(|p| past_payouts[p].1)
.unwrap_or(Zero::zero());
match BoundedVec::try_from(payouts) {
Ok(payouts) => Payouts::<T, I>::insert(who, PayoutRecord { paid, payouts }),
Err(_) => debug_assert!(false, "Truncation of Payouts ineffective??"),
}
}
// Migrate SuspendedMembers from old::SuspendedMembers old::Strikes old::Vouching.
for who in old::SuspendedMembers::<T, I>::iter_keys() {
let strikes = old::Strikes::<T, I>::take(&who);
let vouching = old::Vouching::<T, I>::take(&who);
let record = MemberRecord { index: 0, rank: 0, strikes, vouching };
SuspendedMembers::<T, I>::insert(&who, record);
}
// Any suspended candidates remaining are rejected.
let _ = old::SuspendedCandidates::<T, I>::clear(u32::MAX, None);
// We give the current defender the benefit of the doubt.
old::Defender::<T, I>::kill();
let _ = old::DefenderVotes::<T, I>::clear(u32::MAX, None);
T::BlockWeights::get().max_block
}
pub fn from_raw_past_payouts<T: Config<I>, I: Instance + 'static>(
past_payouts_raw: impl Iterator<Item = ([u8; 32], u128)>,
) -> Vec<(<T as frame_system::Config>::AccountId, BalanceOf<T, I>)> {
past_payouts_raw
.filter_map(|(x, y)| Some((Decode::decode(&mut &x[..]).ok()?, y.try_into().ok()?)))
.collect()
}
+144 -47
View File
@@ -21,7 +21,7 @@ use super::*;
use crate as pallet_society;
use frame_support::{
ord_parameter_types, parameter_types,
assert_noop, assert_ok, ord_parameter_types, parameter_types,
traits::{ConstU32, ConstU64},
};
use frame_support_test::TestRandomness;
@@ -32,6 +32,8 @@ use sp_runtime::{
traits::{BlakeTwo256, IdentityLookup},
};
use RuntimeOrigin as Origin;
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
type Block = frame_system::mocking::MockBlock<Test>;
@@ -52,8 +54,12 @@ parameter_types! {
}
ord_parameter_types! {
pub const ChallengePeriod: u64 = 8;
pub const ClaimPeriod: u64 = 1;
pub const FounderSetAccount: u128 = 1;
pub const SuspensionJudgementSetAccount: u128 = 2;
pub const MaxPayouts: u32 = 10;
pub const MaxBids: u32 = 10;
}
impl frame_system::Config for Test {
@@ -101,34 +107,31 @@ impl pallet_balances::Config for Test {
impl Config for Test {
type RuntimeEvent = RuntimeEvent;
type PalletId = SocietyPalletId;
type Currency = pallet_balances::Pallet<Self>;
type Randomness = TestRandomness<Self>;
type CandidateDeposit = ConstU64<25>;
type WrongSideDeduction = ConstU64<2>;
type MaxStrikes = ConstU32<2>;
type GraceStrikes = ConstU32<1>;
type PeriodSpend = ConstU64<1000>;
type MembershipChanged = ();
type RotationPeriod = ConstU64<4>;
type VotingPeriod = ConstU64<3>;
type ClaimPeriod = ClaimPeriod;
type MaxLockDuration = ConstU64<100>;
type FounderSetOrigin = EnsureSignedBy<FounderSetAccount, u128>;
type SuspensionJudgementOrigin = EnsureSignedBy<SuspensionJudgementSetAccount, u128>;
type ChallengePeriod = ConstU64<8>;
type MaxCandidateIntake = ConstU32<10>;
type PalletId = SocietyPalletId;
type ChallengePeriod = ChallengePeriod;
type MaxPayouts = MaxPayouts;
type MaxBids = MaxBids;
type WeightInfo = ();
}
pub struct EnvBuilder {
members: Vec<u128>,
balance: u64,
balances: Vec<(u128, u64)>,
pot: u64,
max_members: u32,
founded: bool,
}
impl EnvBuilder {
pub fn new() -> Self {
Self {
members: vec![10],
balance: 10_000,
balances: vec![
(10, 50),
@@ -142,7 +145,7 @@ impl EnvBuilder {
(90, 50),
],
pot: 0,
max_members: 100,
founded: true,
}
}
@@ -152,39 +155,22 @@ impl EnvBuilder {
pallet_balances::GenesisConfig::<Test> { balances: self.balances }
.assimilate_storage(&mut t)
.unwrap();
pallet_society::GenesisConfig::<Test> {
members: self.members,
pot: self.pot,
max_members: self.max_members,
}
.assimilate_storage(&mut t)
.unwrap();
pallet_society::GenesisConfig::<Test> { pot: self.pot }
.assimilate_storage(&mut t)
.unwrap();
let mut ext: sp_io::TestExternalities = t.into();
ext.execute_with(f)
ext.execute_with(|| {
if self.founded {
let r = b"be cool".to_vec();
assert!(Society::found_society(Origin::signed(1), 10, 10, 8, 2, 25, r).is_ok());
}
let r = f();
migrations::assert_internal_consistency::<Test, ()>();
r
})
}
#[allow(dead_code)]
pub fn with_members(mut self, m: Vec<u128>) -> Self {
self.members = m;
self
}
#[allow(dead_code)]
pub fn with_balances(mut self, b: Vec<(u128, u64)>) -> Self {
self.balances = b;
self
}
#[allow(dead_code)]
pub fn with_pot(mut self, p: u64) -> Self {
self.pot = p;
self
}
#[allow(dead_code)]
pub fn with_balance(mut self, b: u64) -> Self {
self.balance = b;
self
}
#[allow(dead_code)]
pub fn with_max_members(mut self, n: u32) -> Self {
self.max_members = n;
pub fn founded(mut self, f: bool) -> Self {
self.founded = f;
self
}
}
@@ -202,10 +188,121 @@ pub fn run_to_block(n: u64) {
}
/// Creates a bid struct using input parameters.
pub fn create_bid<AccountId, Balance>(
value: Balance,
pub fn bid<AccountId, Balance>(
who: AccountId,
kind: BidKind<AccountId, Balance>,
value: Balance,
) -> Bid<AccountId, Balance> {
Bid { who, kind, value }
}
/// Creates a candidate struct using input parameters.
pub fn candidacy<AccountId, Balance>(
round: RoundIndex,
bid: Balance,
kind: BidKind<AccountId, Balance>,
approvals: VoteCount,
rejections: VoteCount,
) -> Candidacy<AccountId, Balance> {
Candidacy { round, kind, bid, tally: Tally { approvals, rejections }, skeptic_struck: false }
}
pub fn next_challenge() {
let challenge_period: u64 = <Test as Config>::ChallengePeriod::get();
let now = System::block_number();
run_to_block(now + challenge_period - now % challenge_period);
}
pub fn next_voting() {
if let Period::Voting { more, .. } = Society::period() {
run_to_block(System::block_number() + more);
}
}
pub fn conclude_intake(allow_resignation: bool, judge_intake: Option<bool>) {
next_voting();
let round = RoundCount::<Test>::get();
for (who, candidacy) in Candidates::<Test>::iter() {
if candidacy.tally.clear_approval() {
assert_ok!(Society::claim_membership(Origin::signed(who)));
assert_noop!(
Society::claim_membership(Origin::signed(who)),
Error::<Test>::NotCandidate
);
continue
}
if candidacy.tally.clear_rejection() && allow_resignation {
assert_noop!(
Society::claim_membership(Origin::signed(who)),
Error::<Test>::NotApproved
);
assert_ok!(Society::resign_candidacy(Origin::signed(who)));
continue
}
if let (Some(founder), Some(approve)) = (Founder::<Test>::get(), judge_intake) {
if !candidacy.tally.clear_approval() && !approve {
// can be rejected by founder
assert_ok!(Society::kick_candidate(Origin::signed(founder), who));
continue
}
if !candidacy.tally.clear_rejection() && approve {
// can be rejected by founder
assert_ok!(Society::bestow_membership(Origin::signed(founder), who));
continue
}
}
if candidacy.tally.clear_rejection() && round > candidacy.round + 1 {
assert_noop!(
Society::claim_membership(Origin::signed(who)),
Error::<Test>::NotApproved
);
assert_ok!(Society::drop_candidate(Origin::signed(0), who));
assert_noop!(
Society::drop_candidate(Origin::signed(0), who),
Error::<Test>::NotCandidate
);
continue
}
if !candidacy.skeptic_struck {
assert_ok!(Society::punish_skeptic(Origin::signed(who)));
}
}
}
pub fn next_intake() {
let claim_period: u64 = <Test as Config>::ClaimPeriod::get();
match Society::period() {
Period::Voting { more, .. } => run_to_block(System::block_number() + more + claim_period),
Period::Claim { more, .. } => run_to_block(System::block_number() + more),
}
}
pub fn place_members(members: impl AsRef<[u128]>) {
for who in members.as_ref() {
assert_ok!(Society::insert_member(who, 0));
}
}
pub fn members() -> Vec<u128> {
let mut r = Members::<Test>::iter_keys().collect::<Vec<_>>();
r.sort();
r
}
pub fn membership() -> Vec<(u128, MemberRecord)> {
let mut r = Members::<Test>::iter().collect::<Vec<_>>();
r.sort_by_key(|x| x.0);
r
}
pub fn candidacies() -> Vec<(u128, Candidacy<u128, u64>)> {
let mut r = Candidates::<Test>::iter().collect::<Vec<_>>();
r.sort_by_key(|x| x.0);
r
}
pub fn candidates() -> Vec<u128> {
let mut r = Candidates::<Test>::iter_keys().collect::<Vec<_>>();
r.sort();
r
}
File diff suppressed because it is too large Load Diff
+375
View File
@@ -0,0 +1,375 @@
// This file is part of Substrate.
// Copyright (C) 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.
//! Autogenerated weights for pallet_society
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2022-09-13, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024
// Executed Command:
// ./target/production/substrate
// benchmark
// pallet
// --chain=dev
// --steps=50
// --repeat=20
// --pallet=pallet_society
// --extrinsic=*
// --execution=wasm
// --wasm-execution=compiled
// --template=./.maintain/frame-weight-template.hbs
// --header=./HEADER-APACHE2
// --output=./frame/society/src/weights.rs
#![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_society.
pub trait WeightInfo {
fn bid() -> Weight;
fn unbid() -> Weight;
fn vouch() -> Weight;
fn unvouch() -> Weight;
fn vote() -> Weight;
fn defender_vote() -> Weight;
fn payout() -> Weight;
fn waive_repay() -> Weight;
fn found_society() -> Weight;
fn dissolve() -> Weight;
fn judge_suspended_member() -> Weight;
fn set_parameters() -> Weight;
fn punish_skeptic() -> Weight;
fn claim_membership() -> Weight;
fn bestow_membership() -> Weight;
fn kick_candidate() -> Weight;
fn resign_candidacy() -> Weight;
fn drop_candidate() -> Weight;
fn cleanup_candidacy() -> Weight;
fn cleanup_challenge() -> Weight;
}
/// Weights for pallet_society using the Substrate node and recommended hardware.
pub struct SubstrateWeight<T>(PhantomData<T>);
impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Storage: Society Bids (r:1 w:1)
// Storage: Society Candidates (r:1 w:0)
// Storage: Society Members (r:1 w:0)
// Storage: Society SuspendedMembers (r:1 w:0)
// Storage: Society Parameters (r:1 w:0)
fn bid() -> Weight {
Weight::zero()
}
// Storage: Society Bids (r:1 w:1)
fn unbid() -> Weight {
Weight::zero()
}
// Storage: Society Bids (r:1 w:1)
// Storage: Society Candidates (r:1 w:0)
// Storage: Society Members (r:2 w:1)
// Storage: Society SuspendedMembers (r:1 w:0)
fn vouch() -> Weight {
Weight::zero()
}
// Storage: Society Bids (r:1 w:1)
// Storage: Society Members (r:1 w:1)
fn unvouch() -> Weight {
Weight::zero()
}
// Storage: Society Candidates (r:1 w:1)
// Storage: Society Members (r:1 w:0)
// Storage: Society Votes (r:1 w:1)
fn vote() -> Weight {
Weight::zero()
}
// Storage: Society Defending (r:1 w:1)
// Storage: Society Members (r:1 w:0)
// Storage: Society ChallengeRoundCount (r:1 w:0)
// Storage: Society DefenderVotes (r:1 w:1)
fn defender_vote() -> Weight {
Weight::zero()
}
// Storage: Society Members (r:1 w:0)
// Storage: Society Payouts (r:1 w:1)
// Storage: System Account (r:1 w:1)
fn payout() -> Weight {
Weight::zero()
}
// Storage: Society Members (r:1 w:1)
// Storage: Society Payouts (r:1 w:1)
fn waive_repay() -> Weight {
Weight::zero()
}
// Storage: Society Head (r:1 w:1)
// Storage: Society MemberCount (r:1 w:1)
// Storage: Society MemberByIndex (r:0 w:1)
// Storage: Society Founder (r:0 w:1)
// Storage: Society Rules (r:0 w:1)
// Storage: Society Members (r:0 w:1)
// Storage: Society Parameters (r:0 w:1)
fn found_society() -> Weight {
Weight::zero()
}
// Storage: Society Founder (r:1 w:1)
// Storage: Society MemberCount (r:1 w:1)
// Storage: Society Head (r:0 w:1)
// Storage: Society Defending (r:0 w:1)
// Storage: Society ChallengeRoundCount (r:0 w:1)
// Storage: Society MemberByIndex (r:0 w:5)
// Storage: Society Skeptic (r:0 w:1)
// Storage: Society Candidates (r:0 w:4)
// Storage: Society Pot (r:0 w:1)
// Storage: Society Rules (r:0 w:1)
// Storage: Society Votes (r:0 w:4)
// Storage: Society Members (r:0 w:5)
// Storage: Society RoundCount (r:0 w:1)
// Storage: Society Bids (r:0 w:1)
// Storage: Society Parameters (r:0 w:1)
// Storage: Society NextHead (r:0 w:1)
fn dissolve() -> Weight {
Weight::zero()
}
// Storage: Society Founder (r:1 w:0)
// Storage: Society SuspendedMembers (r:1 w:1)
// Storage: Society Payouts (r:1 w:0)
// Storage: Society Pot (r:1 w:1)
fn judge_suspended_member() -> Weight {
Weight::zero()
}
// Storage: Society Founder (r:1 w:0)
// Storage: Society MemberCount (r:1 w:0)
// Storage: Society Parameters (r:0 w:1)
fn set_parameters() -> Weight {
Weight::zero()
}
// Storage: Society Candidates (r:1 w:1)
// Storage: Society RoundCount (r:1 w:0)
// Storage: Society Skeptic (r:1 w:0)
// Storage: Society Votes (r:1 w:0)
// Storage: Society Members (r:1 w:1)
// Storage: Society Parameters (r:1 w:0)
fn punish_skeptic() -> Weight {
Weight::zero()
}
// Storage: Society Candidates (r:1 w:1)
// Storage: Society RoundCount (r:1 w:0)
// Storage: Society Parameters (r:1 w:0)
// Storage: Society MemberCount (r:1 w:1)
// Storage: Society NextHead (r:1 w:1)
// Storage: System Account (r:1 w:1)
// Storage: Society MemberByIndex (r:0 w:1)
// Storage: Society Members (r:0 w:1)
fn claim_membership() -> Weight {
Weight::zero()
}
// Storage: Society Founder (r:1 w:0)
// Storage: Society Candidates (r:1 w:1)
// Storage: Society RoundCount (r:1 w:0)
// Storage: Society Parameters (r:1 w:0)
// Storage: Society MemberCount (r:1 w:1)
// Storage: Society NextHead (r:1 w:1)
// Storage: System Account (r:1 w:1)
// Storage: Society MemberByIndex (r:0 w:1)
// Storage: Society Members (r:0 w:1)
fn bestow_membership() -> Weight {
Weight::zero()
}
// Storage: Society Founder (r:1 w:0)
// Storage: Society Candidates (r:1 w:1)
// Storage: Society RoundCount (r:1 w:0)
fn kick_candidate() -> Weight {
Weight::zero()
}
// Storage: Society Candidates (r:1 w:1)
// Storage: Society RoundCount (r:1 w:0)
fn resign_candidacy() -> Weight {
Weight::zero()
}
// Storage: Society Candidates (r:1 w:1)
// Storage: Society RoundCount (r:1 w:0)
fn drop_candidate() -> Weight {
Weight::zero()
}
// Storage: Society Candidates (r:1 w:0)
// Storage: Society VoteClearCursor (r:1 w:0)
// Storage: Society Votes (r:0 w:2)
fn cleanup_candidacy() -> Weight {
Weight::zero()
}
// Storage: Society ChallengeRoundCount (r:1 w:0)
// Storage: Society DefenderVotes (r:0 w:1)
fn cleanup_challenge() -> Weight {
Weight::zero()
}
}
// For backwards compatibility and tests
impl WeightInfo for () {
// Storage: Society Bids (r:1 w:1)
// Storage: Society Candidates (r:1 w:0)
// Storage: Society Members (r:1 w:0)
// Storage: Society SuspendedMembers (r:1 w:0)
// Storage: Society Parameters (r:1 w:0)
fn bid() -> Weight {
Weight::zero()
}
// Storage: Society Bids (r:1 w:1)
fn unbid() -> Weight {
Weight::zero()
}
// Storage: Society Bids (r:1 w:1)
// Storage: Society Candidates (r:1 w:0)
// Storage: Society Members (r:2 w:1)
// Storage: Society SuspendedMembers (r:1 w:0)
fn vouch() -> Weight {
Weight::zero()
}
// Storage: Society Bids (r:1 w:1)
// Storage: Society Members (r:1 w:1)
fn unvouch() -> Weight {
Weight::zero()
}
// Storage: Society Candidates (r:1 w:1)
// Storage: Society Members (r:1 w:0)
// Storage: Society Votes (r:1 w:1)
fn vote() -> Weight {
Weight::zero()
}
// Storage: Society Defending (r:1 w:1)
// Storage: Society Members (r:1 w:0)
// Storage: Society ChallengeRoundCount (r:1 w:0)
// Storage: Society DefenderVotes (r:1 w:1)
fn defender_vote() -> Weight {
Weight::zero()
}
// Storage: Society Members (r:1 w:0)
// Storage: Society Payouts (r:1 w:1)
// Storage: System Account (r:1 w:1)
fn payout() -> Weight {
Weight::zero()
}
// Storage: Society Members (r:1 w:1)
// Storage: Society Payouts (r:1 w:1)
fn waive_repay() -> Weight {
Weight::zero()
}
// Storage: Society Head (r:1 w:1)
// Storage: Society MemberCount (r:1 w:1)
// Storage: Society MemberByIndex (r:0 w:1)
// Storage: Society Founder (r:0 w:1)
// Storage: Society Rules (r:0 w:1)
// Storage: Society Members (r:0 w:1)
// Storage: Society Parameters (r:0 w:1)
fn found_society() -> Weight {
Weight::zero()
}
// Storage: Society Founder (r:1 w:1)
// Storage: Society MemberCount (r:1 w:1)
// Storage: Society Head (r:0 w:1)
// Storage: Society Defending (r:0 w:1)
// Storage: Society ChallengeRoundCount (r:0 w:1)
// Storage: Society MemberByIndex (r:0 w:5)
// Storage: Society Skeptic (r:0 w:1)
// Storage: Society Candidates (r:0 w:4)
// Storage: Society Pot (r:0 w:1)
// Storage: Society Rules (r:0 w:1)
// Storage: Society Votes (r:0 w:4)
// Storage: Society Members (r:0 w:5)
// Storage: Society RoundCount (r:0 w:1)
// Storage: Society Bids (r:0 w:1)
// Storage: Society Parameters (r:0 w:1)
// Storage: Society NextHead (r:0 w:1)
fn dissolve() -> Weight {
Weight::zero()
}
// Storage: Society Founder (r:1 w:0)
// Storage: Society SuspendedMembers (r:1 w:1)
// Storage: Society Payouts (r:1 w:0)
// Storage: Society Pot (r:1 w:1)
fn judge_suspended_member() -> Weight {
Weight::zero()
}
// Storage: Society Founder (r:1 w:0)
// Storage: Society MemberCount (r:1 w:0)
// Storage: Society Parameters (r:0 w:1)
fn set_parameters() -> Weight {
Weight::zero()
}
// Storage: Society Candidates (r:1 w:1)
// Storage: Society RoundCount (r:1 w:0)
// Storage: Society Skeptic (r:1 w:0)
// Storage: Society Votes (r:1 w:0)
// Storage: Society Members (r:1 w:1)
// Storage: Society Parameters (r:1 w:0)
fn punish_skeptic() -> Weight {
Weight::zero()
}
// Storage: Society Candidates (r:1 w:1)
// Storage: Society RoundCount (r:1 w:0)
// Storage: Society Parameters (r:1 w:0)
// Storage: Society MemberCount (r:1 w:1)
// Storage: Society NextHead (r:1 w:1)
// Storage: System Account (r:1 w:1)
// Storage: Society MemberByIndex (r:0 w:1)
// Storage: Society Members (r:0 w:1)
fn claim_membership() -> Weight {
Weight::zero()
}
// Storage: Society Founder (r:1 w:0)
// Storage: Society Candidates (r:1 w:1)
// Storage: Society RoundCount (r:1 w:0)
// Storage: Society Parameters (r:1 w:0)
// Storage: Society MemberCount (r:1 w:1)
// Storage: Society NextHead (r:1 w:1)
// Storage: System Account (r:1 w:1)
// Storage: Society MemberByIndex (r:0 w:1)
// Storage: Society Members (r:0 w:1)
fn bestow_membership() -> Weight {
Weight::zero()
}
// Storage: Society Founder (r:1 w:0)
// Storage: Society Candidates (r:1 w:1)
// Storage: Society RoundCount (r:1 w:0)
fn kick_candidate() -> Weight {
Weight::zero()
}
// Storage: Society Candidates (r:1 w:1)
// Storage: Society RoundCount (r:1 w:0)
fn resign_candidacy() -> Weight {
Weight::zero()
}
// Storage: Society Candidates (r:1 w:1)
// Storage: Society RoundCount (r:1 w:0)
fn drop_candidate() -> Weight {
Weight::zero()
}
// Storage: Society Candidates (r:1 w:0)
// Storage: Society VoteClearCursor (r:1 w:0)
// Storage: Society Votes (r:0 w:2)
fn cleanup_candidacy() -> Weight {
Weight::zero()
}
// Storage: Society ChallengeRoundCount (r:1 w:0)
// Storage: Society DefenderVotes (r:0 w:1)
fn cleanup_challenge() -> Weight {
Weight::zero()
}
}
@@ -202,6 +202,18 @@ pub trait StorageMap<K: FullEncode, V: FullCodec> {
f: F,
) -> Result<R, E>;
/// Mutate the value under a key if the value already exists. Do nothing and return the default
/// value if not.
fn mutate_extant<KeyArg: EncodeLike<K>, R: Default, F: FnOnce(&mut V) -> R>(
key: KeyArg,
f: F,
) -> R {
Self::mutate_exists(key, |maybe_v| match maybe_v {
Some(ref mut value) => f(value),
None => R::default(),
})
}
/// Mutate the value under a key.
///
/// Deletes the item if mutated to a `None`.
@@ -186,6 +186,14 @@ where
<Self as crate::storage::StorageMap<Key, Value>>::try_mutate(key, f)
}
/// Mutate the value under a key iff it exists. Do nothing and return the default value if not.
pub fn mutate_extant<KeyArg: EncodeLike<Key>, R: Default, F: FnOnce(&mut Value) -> R>(
key: KeyArg,
f: F,
) -> R {
<Self as crate::storage::StorageMap<Key, Value>>::mutate_extant(key, f)
}
/// Mutate the value under a key. Deletes the item if mutated to a `None`.
pub fn mutate_exists<KeyArg: EncodeLike<Key>, R, F: FnOnce(&mut Option<Value>) -> R>(
key: KeyArg,
@@ -366,6 +374,16 @@ where
<Self as crate::storage::IterableStorageMap<Key, Value>>::iter_from(starting_raw_key)
}
/// Enumerate all elements in the map after a specified `starting_key` in no
/// particular order.
///
/// If you alter the map while doing this, you'll get undefined results.
pub fn iter_from_key(
starting_key: impl EncodeLike<Key>,
) -> crate::storage::PrefixIterator<(Key, Value)> {
Self::iter_from(Self::hashed_key_for(starting_key))
}
/// Enumerate all keys in the map in no particular order.
///
/// If you alter the map while doing this, you'll get undefined results.
@@ -381,6 +399,16 @@ where
<Self as crate::storage::IterableStorageMap<Key, Value>>::iter_keys_from(starting_raw_key)
}
/// Enumerate all keys in the map after a specified `starting_key` in no particular
/// order.
///
/// If you alter the map while doing this, you'll get undefined results.
pub fn iter_keys_from_key(
starting_key: impl EncodeLike<Key>,
) -> crate::storage::KeyPrefixIterator<Key> {
Self::iter_keys_from(Self::hashed_key_for(starting_key))
}
/// Remove all elements from the map and iterate through them in no particular order.
///
/// If you add elements to the map while doing this, you'll get undefined results.
@@ -37,6 +37,12 @@ pub trait Instance: 'static {
const INDEX: u8;
}
// Dummy implementation for `()`.
impl Instance for () {
const PREFIX: &'static str = "";
const INDEX: u8 = 0;
}
/// An instance of a storage in a pallet.
///
/// Define an instance for an individual storage inside a pallet.
+1 -1
View File
@@ -17,7 +17,7 @@ codec = { package = "parity-scale-codec", version = "3.2.2", default-features =
scale-info = { version = "2.5.0", default-features = false, features = ["derive"] }
log = { version = "0.4.17", default-features = false }
serde = { version = "1.0.163", optional = true, default-features = false, features = ["derive", "alloc"] }
bounded-collections = { version = "0.1.7", default-features = false }
bounded-collections = { version = "0.1.8", default-features = false }
primitive-types = { version = "0.12.0", default-features = false, features = ["codec", "scale-info"] }
impl-serde = { version = "0.4.0", default-features = false, optional = true }
hash-db = { version = "0.16.0", default-features = false }
+46
View File
@@ -1158,6 +1158,52 @@ pub mod key_types {
pub const DUMMY: KeyTypeId = KeyTypeId(*b"dumy");
}
/// Create random values of `Self` given a stream of entropy.
pub trait FromEntropy: Sized {
/// Create a random value of `Self` given a stream of random bytes on `input`. May only fail if
/// `input` has an error.
fn from_entropy(input: &mut impl codec::Input) -> Result<Self, codec::Error>;
}
impl FromEntropy for bool {
fn from_entropy(input: &mut impl codec::Input) -> Result<Self, codec::Error> {
Ok(input.read_byte()? % 2 == 1)
}
}
macro_rules! impl_from_entropy {
($type:ty , $( $others:tt )*) => {
impl_from_entropy!($type);
impl_from_entropy!($( $others )*);
};
($type:ty) => {
impl FromEntropy for $type {
fn from_entropy(input: &mut impl codec::Input) -> Result<Self, codec::Error> {
<Self as codec::Decode>::decode(input)
}
}
}
}
macro_rules! impl_from_entropy_base {
($type:ty , $( $others:tt )*) => {
impl_from_entropy_base!($type);
impl_from_entropy_base!($( $others )*);
};
($type:ty) => {
impl_from_entropy!($type,
[$type; 1], [$type; 2], [$type; 3], [$type; 4], [$type; 5], [$type; 6], [$type; 7], [$type; 8],
[$type; 9], [$type; 10], [$type; 11], [$type; 12], [$type; 13], [$type; 14], [$type; 15], [$type; 16],
[$type; 17], [$type; 18], [$type; 19], [$type; 20], [$type; 21], [$type; 22], [$type; 23], [$type; 24],
[$type; 25], [$type; 26], [$type; 27], [$type; 28], [$type; 29], [$type; 30], [$type; 31], [$type; 32],
[$type; 36], [$type; 40], [$type; 44], [$type; 48], [$type; 56], [$type; 64], [$type; 72], [$type; 80],
[$type; 96], [$type; 112], [$type; 128], [$type; 160], [$type; 192], [$type; 224], [$type; 256]
);
}
}
impl_from_entropy_base!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128);
#[cfg(test)]
mod tests {
use super::*;
+8
View File
@@ -73,6 +73,14 @@ type Seed = [u8; 32];
)]
pub struct Public(pub [u8; 33]);
impl crate::crypto::FromEntropy for Public {
fn from_entropy(input: &mut impl codec::Input) -> Result<Self, codec::Error> {
let mut result = Self([0u8; 33]);
input.read(&mut result.0[..])?;
Ok(result)
}
}
impl Public {
/// A new instance from the given 33-byte `data`.
///
+11 -1
View File
@@ -31,7 +31,9 @@ use scale_info::TypeInfo;
#[cfg(feature = "serde")]
use crate::crypto::Ss58Codec;
use crate::crypto::{CryptoType, CryptoTypeId, Derive, Public as TraitPublic, UncheckedFrom};
use crate::crypto::{
CryptoType, CryptoTypeId, Derive, FromEntropy, Public as TraitPublic, UncheckedFrom,
};
#[cfg(feature = "full_crypto")]
use crate::crypto::{DeriveError, DeriveJunction, Pair as TraitPair, SecretStringError};
#[cfg(feature = "full_crypto")]
@@ -79,6 +81,14 @@ pub struct Pair {
secret: SigningKey,
}
impl FromEntropy for Public {
fn from_entropy(input: &mut impl codec::Input) -> Result<Self, codec::Error> {
let mut result = Self([0u8; 32]);
input.read(&mut result.0[..])?;
Ok(result)
}
}
impl AsRef<[u8; 32]> for Public {
fn as_ref(&self) -> &[u8; 32] {
&self.0
+12 -1
View File
@@ -37,7 +37,10 @@ use schnorrkel::{
use sp_std::vec::Vec;
use crate::{
crypto::{ByteArray, CryptoType, CryptoTypeId, Derive, Public as TraitPublic, UncheckedFrom},
crypto::{
ByteArray, CryptoType, CryptoTypeId, Derive, FromEntropy, Public as TraitPublic,
UncheckedFrom,
},
hash::{H256, H512},
};
use codec::{Decode, Encode, MaxEncodedLen};
@@ -91,6 +94,14 @@ impl Clone for Pair {
}
}
impl FromEntropy for Public {
fn from_entropy(input: &mut impl codec::Input) -> Result<Self, codec::Error> {
let mut result = Self([0u8; 32]);
input.read(&mut result.0[..])?;
Ok(result)
}
}
impl AsRef<[u8; 32]> for Public {
fn as_ref(&self) -> &[u8; 32] {
&self.0
+11 -1
View File
@@ -43,7 +43,7 @@ pub use sp_core::storage::StateVersion;
pub use sp_core::storage::{Storage, StorageChild};
use sp_core::{
crypto::{self, ByteArray},
crypto::{self, ByteArray, FromEntropy},
ecdsa, ed25519,
hash::{H256, H512},
sr25519,
@@ -311,6 +311,16 @@ pub enum MultiSigner {
Ecdsa(ecdsa::Public),
}
impl FromEntropy for MultiSigner {
fn from_entropy(input: &mut impl codec::Input) -> Result<Self, codec::Error> {
Ok(match input.read_byte()? % 3 {
0 => Self::Ed25519(FromEntropy::from_entropy(input)?),
1 => Self::Sr25519(FromEntropy::from_entropy(input)?),
2.. => Self::Ecdsa(FromEntropy::from_entropy(input)?),
})
}
}
/// NOTE: This implementations is required by `SimpleAddressDeterminer`,
/// we convert the hash into some AccountId, it's fine to use any scheme.
impl<T: Into<H256>> crypto::UncheckedFrom<T> for MultiSigner {