Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
256 changes: 110 additions & 146 deletions pallets/pips/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,17 @@
use crate::*;

use frame_benchmarking::benchmarks;
use frame_support::{
dispatch::{DispatchError, DispatchResult},
traits::UnfilteredDispatchable,
};
use frame_support::{dispatch::DispatchResult, traits::UnfilteredDispatchable};
use frame_system::RawOrigin;
use pallet_identity::benchmarking::{user, User};
use polymesh_primitives::{MaybeBlock, SystematicIssuers, GC_DID};
use rand::{seq::SliceRandom, SeedableRng};
use rand_chacha::ChaCha20Rng;
use sp_std::{
convert::{TryFrom, TryInto},
iter,
prelude::*,
};
use scale_info::prelude::format;
use sp_std::convert::{TryFrom, TryInto};
use sp_std::iter;
use sp_std::prelude::*;

use pallet_identity::benchmarking::{user, User, UserBuilder};
use polymesh_primitives::{MaybeBlock, SystematicIssuers, GC_DID};

#[cfg(feature = "running-ci")]
mod limits {
Expand All @@ -38,7 +35,7 @@ mod limits {
pub const PROPOSAL_PADDING_LEN: usize = 100;
pub const VOTERS_A_NUM: usize = 10;
pub const VOTERS_B_NUM: usize = 10;
pub const PROPOSALS_NUM: usize = 5;
pub const PROPOSALS_NUM: u32 = 5;
}

#[cfg(not(feature = "running-ci"))]
Expand All @@ -48,7 +45,7 @@ mod limits {
pub const PROPOSAL_PADDING_LEN: usize = 10_000;
pub const VOTERS_A_NUM: usize = 200;
pub const VOTERS_B_NUM: usize = 200;
pub const PROPOSALS_NUM: usize = 100;
pub const PROPOSALS_NUM: u32 = 100;
}

use limits::*;
Expand Down Expand Up @@ -98,43 +95,30 @@ fn cast_votes<T: Config>(
Ok(())
}

/// Sets up PIPs and votes.
fn pips_and_votes_setup<T: Config>(
approve_only: bool,
) -> Result<(RawOrigin<T::AccountId>, IdentityId), DispatchError> {
/// Creates `number_of_proposals` proposals and casts `number_of_voter` votes for each.
fn vote_setup<T: Config>(number_of_proposals: u32, number_of_voter: u32) {
Pallet::<T>::set_active_pip_limit(RawOrigin::Root.into(), PROPOSALS_NUM as u32).unwrap();
zeroize_deposit::<T>();
let (voters_a_num, voters_b_num) = if approve_only {
(VOTERS_A_NUM + VOTERS_B_NUM, 0)
} else {
(VOTERS_A_NUM, VOTERS_B_NUM)
};
let hi_voters = make_voters::<T>(voters_a_num, "hi");
let bye_voters = make_voters::<T>(voters_b_num, "bye");
let User { origin, did, .. } = user::<T>("initial", 0);
let did = did.ok_or("no did in pips_and_votes_setup").unwrap();
for i in 0..PROPOSALS_NUM {
Pallet::<T>::set_min_proposal_deposit(RawOrigin::Root.into(), 1_000).unwrap();
for i in 0..number_of_proposals {
let (proposal, url, description) = make_proposal::<T>();
// Pick a proposer, diversifying like a poor man.
let (proposer_origin, _) = if hi_voters.len() >= i + 1 {
(hi_voters[i].1.clone(), hi_voters[i].2.clone())
} else {
(origin.clone(), did)
};
let proposer = UserBuilder::<T>::default()
.generate_did()
.build(&format!("Proposer{}", i));
Pallet::<T>::propose(
proposer_origin.into(),
proposer.origin().into(),
proposal,
42u32.into(),
Some(url.clone()),
Some(description.clone()),
1_000,
Some(url),
Some(description),
)
.unwrap();
let id = PipId(i as u32);
// Alternate aye and nay voters with every iteration unless only approve votes are cast.
cast_votes::<T>(id, hi_voters.as_slice(), approve_only || i % 2 == 0).unwrap();
cast_votes::<T>(id, bye_voters.as_slice(), i % 2 != 0).unwrap();
for j in 0..number_of_voter {
let voter = UserBuilder::<T>::default()
.generate_did()
.build(&format!("User{}", j));
Pallet::<T>::vote(voter.origin().into(), PipId(i as u32), true, 1_000).unwrap();
}
}
Ok((origin, did))
}

fn enact_call<T: Config>(num_approves: usize, num_rejects: usize, num_skips: usize) -> Call<T> {
Expand All @@ -146,7 +130,7 @@ fn enact_call<T: Config>(num_approves: usize, num_rejects: usize, num_skips: usi
.chain(iter::repeat(SnapshotResult::Skip).take(num_skips))
.collect();
snapshot_results.shuffle(&mut rng);
let results = Pallet::<T>::snapshot_queue()
let results = SnapshotQueue::<T>::get()
.iter()
.rev()
.map(|s| s.id)
Expand All @@ -156,7 +140,7 @@ fn enact_call<T: Config>(num_approves: usize, num_rejects: usize, num_skips: usi
}

fn propose_verify<T: Config>(url: Url, description: PipDescription) -> DispatchResult {
let meta = Pallet::<T>::proposal_metadata(PipId(0)).unwrap();
let meta = ProposalMetadata::<T>::get(PipId(0)).unwrap();
assert_eq!(PipId(0), meta.id, "incorrect meta.id");
assert_eq!(Some(url), meta.url, "incorrect meta.url");
assert_eq!(
Expand All @@ -167,18 +151,6 @@ fn propose_verify<T: Config>(url: Url, description: PipDescription) -> DispatchR
Ok(())
}

fn execute_verify<T: Config>(state: ProposalState, err: &'static str) -> DispatchResult {
if Proposals::<T>::contains_key(PipId(0)) {
assert_eq!(
state,
Pallet::<T>::proposal_state(PipId(0)).unwrap(),
"{}",
err
);
}
Ok(())
}

benchmarks! {
set_prune_historical_pips {
let origin = RawOrigin::Root;
Expand Down Expand Up @@ -306,52 +278,32 @@ benchmarks! {
}

reject_proposal {
vote_setup::<T>(1, T::MaxRefundsAndVotesPruned::get());

Pallet::<T>::set_prune_historical_pips(RawOrigin::Root.into(), true).unwrap();
let user = user::<T>("proposer", 0);
zeroize_deposit::<T>();
let (proposal, url, description) = make_proposal::<T>();
let deposit = 42u32.into();
Pallet::<T>::propose(
user.origin().into(),
proposal,
deposit,
Some(url),
Some(description)
).unwrap();
let id = PipId(0);
assert_eq!(deposit, Deposits::<T>::get(id, &user.account()).expect("Deposit").amount, "incorrect deposit in reject_proposal");
let vmo_origin = T::VotingMajorityOrigin::try_successful_origin().unwrap();
let call = Call::<T>::reject_proposal { id };

let origin = T::VotingMajorityOrigin::try_successful_origin().unwrap();
let call = Call::<T>::reject_proposal { id: PipId(0) };
}: {
call.dispatch_bypass_filter(vmo_origin).unwrap();
call.dispatch_bypass_filter(origin).unwrap();
}
verify {
assert!(!Deposits::<T>::contains_key(id, &user.account()), "deposit of the rejected proposal is present");
assert!(!Proposals::<T>::contains_key(PipId(0)));
}

prune_proposal {
Pallet::<T>::set_prune_historical_pips(RawOrigin::Root.into(), false).unwrap();
let user = user::<T>("proposer", 0);
zeroize_deposit::<T>();
let (proposal, url, description) = make_proposal::<T>();
Pallet::<T>::propose(
user.origin().into(),
proposal,
42u32.into(),
Some(url),
Some(description)
).unwrap();
let id = PipId(0);
let vmo_origin = T::VotingMajorityOrigin::try_successful_origin().unwrap();
let reject_call = Call::<T>::reject_proposal { id };
reject_call.dispatch_bypass_filter(vmo_origin.clone()).unwrap();
let call = Call::<T>::prune_proposal { id };
vote_setup::<T>(1, T::MaxRefundsAndVotesPruned::get());

let origin = T::VotingMajorityOrigin::try_successful_origin().unwrap();
let reject_call = Call::<T>::reject_proposal { id: PipId(0) };
reject_call.dispatch_bypass_filter(origin.clone()).unwrap();
let call = Call::<T>::prune_proposal { id: PipId(0) };
}: {
call.dispatch_bypass_filter(vmo_origin).unwrap();
call.dispatch_bypass_filter(origin).unwrap();
Comment thread
HenriqueNogara marked this conversation as resolved.
}
verify {
assert!(!Proposals::<T>::contains_key(id), "pruned proposal is present");
assert!(!ProposalMetadata::<T>::contains_key(id), "pruned proposal metadata is present");
assert!(!Proposals::<T>::contains_key(PipId(0)));
assert!(!ProposalMetadata::<T>::contains_key(PipId(0)));
}

reschedule_execution {
Expand Down Expand Up @@ -399,85 +351,97 @@ benchmarks! {
}

snapshot {
let (origin0, did0) = pips_and_votes_setup::<T>(true).unwrap();
T::GovernanceCommittee::bench_set_release_coordinator(did0);
}: _(origin0)
vote_setup::<T>(1, T::MaxRefundsAndVotesPruned::get());
let user = user::<T>("release_coordinator", 0);
T::GovernanceCommittee::bench_set_release_coordinator(user.did());
}: _(user.origin())
verify {
assert!(SnapshotMeta::<T>::get().is_some(), "snapshot finished incorrectly");
assert!(SnapshotMeta::<T>::get().is_some());
}

enact_snapshot_results {
// The number of Approve results.
let a in 0..PROPOSALS_NUM as u32 / 3;
let a in 0..PROPOSALS_NUM / 3;
// The number of Reject results.
let r in 0..PROPOSALS_NUM as u32 / 3;
let r in 0..PROPOSALS_NUM / 3;
// The number of Skip results.
let s in 0..PROPOSALS_NUM as u32 / 3;
let s in 0..PROPOSALS_NUM / 3;

Pallet::<T>::set_max_pip_skip_count(RawOrigin::Root.into(), MAX_SKIPPED_COUNT).unwrap();
let (origin0, did0) = pips_and_votes_setup::<T>(true).unwrap();
vote_setup::<T>(PROPOSALS_NUM, T::MaxRefundsAndVotesPruned::get());

// snapshot
T::GovernanceCommittee::bench_set_release_coordinator(did0);
Pallet::<T>::snapshot(origin0.into()).unwrap();
// Adds a user to the governance committee and captures a snapshot
let user = user::<T>("release_coordinator", 0);
T::GovernanceCommittee::bench_set_release_coordinator(user.did());
Pallet::<T>::snapshot(user.origin().into()).unwrap();

// enact
let enact_origin = T::VotingMajorityOrigin::try_successful_origin().unwrap();
let enact_call = enact_call::<T>(a as usize, r as usize, s as usize);
// Set up the results parameter based on the number of Approve, Reject, and Skip results.
let origin = T::VotingMajorityOrigin::try_successful_origin().unwrap();
let call = enact_call::<T>(a as usize, r as usize, s as usize);
}: {
enact_call.dispatch_bypass_filter(enact_origin).unwrap();
call.dispatch_bypass_filter(origin).unwrap();
}
verify {
assert_eq!(
Pallet::<T>::snapshot_queue().len(), PROPOSALS_NUM - (a + r + s) as usize,
"incorrect snapshot queue after enact_snapshot_results"
);
assert_eq!(SnapshotQueue::<T>::get().len(), (PROPOSALS_NUM - (a + r + s)) as usize);
}

execute_scheduled_pip {
// set up
Pallet::<T>::set_prune_historical_pips(RawOrigin::Root.into(), true).unwrap();
let (origin0, did0) = pips_and_votes_setup::<T>(true).unwrap();

// snapshot
T::GovernanceCommittee::bench_set_release_coordinator(did0);
Pallet::<T>::snapshot(origin0.into()).unwrap();
assert!(
Pallet::<T>::snapshot_queue().len() == PROPOSALS_NUM as usize,
"wrong snapshot queue length"
);

// enact
let enact_origin = T::VotingMajorityOrigin::try_successful_origin().unwrap();
let enact_call = enact_call::<T>(PROPOSALS_NUM, 0, 0);
enact_call.dispatch_bypass_filter(enact_origin).unwrap();

// execute
let origin = RawOrigin::Root;
}: _(origin, PipId(0))
vote_setup::<T>(PROPOSALS_NUM, T::MaxRefundsAndVotesPruned::get());

// Adds a user to the governance committee and captures a snapshot
let user = user::<T>("release_coordinator", 0);
T::GovernanceCommittee::bench_set_release_coordinator(user.did());
Pallet::<T>::snapshot(user.origin().into()).unwrap();
assert!(SnapshotQueue::<T>::get().len() == PROPOSALS_NUM as usize);

// Set up the results parameter where all proposals are approved
let origin = T::VotingMajorityOrigin::try_successful_origin().unwrap();
let call = enact_call::<T>(PROPOSALS_NUM as usize, 0, 0);
call.dispatch_bypass_filter(origin).unwrap();
}: _(RawOrigin::Root, PipId(0))
verify {
execute_verify::<T>(ProposalState::Failed, "incorrect proposal state after execution").unwrap();
assert!(!Proposals::<T>::contains_key(PipId(0)));
}

expire_scheduled_pip {
// set up
Pallet::<T>::set_prune_historical_pips(RawOrigin::Root.into(), true).unwrap();
let (origin0, did0) = pips_and_votes_setup::<T>(true).unwrap();
vote_setup::<T>(PROPOSALS_NUM, T::MaxRefundsAndVotesPruned::get());

// snapshot
T::GovernanceCommittee::bench_set_release_coordinator(did0);
Pallet::<T>::snapshot(origin0.into()).unwrap();
// Adds a user to the governance committee and captures a snapshot
let user = user::<T>("release_coordinator", 0);
T::GovernanceCommittee::bench_set_release_coordinator(user.did());
Pallet::<T>::snapshot(user.origin().into()).unwrap();

let id = PipId(0);
assert_eq!(ProposalState::Pending, ProposalStates::<T>::get(PipId(0)).unwrap());
}: _(RawOrigin::Root, GC_DID, PipId(0))
verify {
assert!(!Proposals::<T>::contains_key(PipId(0)));
}

assert_eq!(
ProposalState::Pending, Pallet::<T>::proposal_state(id).unwrap(),
"incorrect proposal state before expiration"
);
remove_pending_storage {
let r in 0..T::MaxRefundsAndVotesPruned::get() as u32;
let v in 0..T::MaxRefundsAndVotesPruned::get() as u32;

let origin = RawOrigin::Root;
}: _(origin, GC_DID, id)
verify {
execute_verify::<T>(ProposalState::Expired, "incorrect proposal state after expiration").unwrap();
for i in 0..r {
let user = UserBuilder::<T>::default().generate_did().build(&format!("Voter{}", i));
Pallet::<T>::increase_lock(&user.account(), 1_000).unwrap();
Deposits::<T>::insert(PipId(0), user.account(), DepositInfo { owner: user.account(), amount: 1_000 });
}

for i in 0..v {
let user = UserBuilder::<T>::default().generate_did().build(&format!("Voter{}", i));
ProposalVotes::<T>::insert(PipId(0), user.account(), Vote(true, 1_000));
}

if r > 0 {
PendingRefunds::<T>::insert(PipId(0), true);
}

if v > 0 {
VotesToBePruned::<T>::insert(PipId(0), true);
}
}: {
Pallet::<T>::remove_pending_storage();
}
}
Loading