Files
pezkuwi-subxt/substrate/frame/bounties/src/benchmarking.rs
T
Peter Goodspeed-Niklaus 44d5aba80d Create a macro which automates creation of benchmark test suites. (#8104)
* Create a macro which automates creation of benchmark test suites.

* bump impl_version

* allow unused on test_bench_by_name

* use proper doctest ignore attribute

* Explicitly hand the Module to the test suite

Much better practice than depending on it showing up implicitly in
the namespace.

* explicitly import what we need into `mod tests`

* bench_module is `ident` not `tt`

Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com>

* allow end users to specify arguments for new_test_ext

This turned out to be surprisingly easy. On reflection, it turns out
that of course the compiler can't eagerly evaluate the function call,
but needs to paste it in everywhere desired.

* enable explicitly specifying the path to the benchmarks invocation

also enable optional trailing commas

* Revert "bump impl_version"

This reverts commit 0209e4de33fd43873f8cfc6875815d0fd6151e63.

* list failing benchmark tests and the errors which caused the failure

* harden benchmark tests against internal panics

* suppress warning about ignored profiles

unfortunately, setting the profile here doesn't do anything; we'd
need to set it in every leaf package anyway. However, as this was
just making the default explicit anyway, I think it's safe enough
to remove entirely.

* impl_benchmark_test_suite for assets

* impl_benchmark_test_suite for balances

* impl_benchmark_test_suite for bounties

* impl_benchmark_test_suite for Collective

* impl_benchmark_test_suite for Contracts

* impl_benchmark_test_suite for Democracy

* don't impl_benchmark_test_suite for Elections-Phragmen

* impl_benchmark_test_suite for Identity

Note that Identity tests currently fail. They failed in an identical
way before this change, so as far as I'm concerned, the status quo is
good enough for now.

* impl_benchmark_test_suite for ImOnline

* impl_benchmark_test_suite for indices

For this crate also, the test suite fails identically with and without
this change, so we can say that this change is not the cause of the
tests' failure to compile.

* impl_benchmark_test_suite for lottery

* impl_benchmark_test_suite for merkle-mountain-range

* impl_benchmark_test_suite for Multisig

These tests fail identically with and without the change, so the change
seems unlikely to be the origin of the failures.

* impl_benchmark_test_suite for offences

* impl_benchmark_test_suite for Proxy

Fails identically with and without this change.

* impl_benchmark_test_suite for scheduler

* impl_benchmark_test_suite for session

It turns out to be important to be able to exclude items marked
`#[extra]` sometimes. Who knew?

* impl_benchmark_test_suite for staking

* impl_benchmark_test_suite for system

* impl_benchmark_test_suite for timestamp

* impl_benchmark_test_suite for tips

* impl_benchmark_test_suite for treasury

* impl_benchmark_test_suite for utility

Note that benchmark tests fail identically before and after this change.

* impl_benchmark_test_suite for vesting

* fix wrong module name in impl_benchmark_test_suite in Offences

* address line length nits

* enable optional keyword argument: exec_name

Took a _lot_ of macro-wrangling to get the functionality that I want,
but now you have the option to pass in

```rust
impl_benchmark_test_suite!(
	Elections,
	crate::tests::ExtBuilder::default().desired_members(13).desired_runners_up(7),
	crate::tests::Test,
	exec_name = build_and_execute,
);
```

and have it expand out properly. A selected fragment of the expansion:

```rust
        fn test_benchmarks() {
            crate::tests::ExtBuilder::default()
                .desired_members(13)
                .desired_runners_up(7)
                .build_and_execute(|| {
```

* get rid of dead code

Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com>
2021-02-16 10:01:20 +01:00

228 lines
8.4 KiB
Rust

// This file is part of Substrate.
// Copyright (C) 2020-2021 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.
//! bounties pallet benchmarking.
#![cfg(feature = "runtime-benchmarks")]
use super::*;
use sp_runtime::traits::Bounded;
use frame_system::{EventRecord, RawOrigin};
use frame_benchmarking::{benchmarks, account, whitelisted_caller, impl_benchmark_test_suite};
use frame_support::traits::OnInitialize;
use crate::Module as Bounties;
use pallet_treasury::Module as Treasury;
const SEED: u32 = 0;
// Create bounties that are approved for use in `on_initialize`.
fn create_approved_bounties<T: Config>(n: u32) -> Result<(), &'static str> {
for i in 0 .. n {
let (caller, _curator, _fee, value, reason) = setup_bounty::<T>(i, MAX_BYTES);
Bounties::<T>::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?;
let bounty_id = BountyCount::get() - 1;
Bounties::<T>::approve_bounty(RawOrigin::Root.into(), bounty_id)?;
}
ensure!(BountyApprovals::get().len() == n as usize, "Not all bounty approved");
Ok(())
}
// Create the pre-requisite information needed to create a treasury `propose_bounty`.
fn setup_bounty<T: Config>(u: u32, d: u32) -> (
T::AccountId,
T::AccountId,
BalanceOf<T>,
BalanceOf<T>,
Vec<u8>,
) {
let caller = account("caller", u, SEED);
let value: BalanceOf<T> = T::BountyValueMinimum::get().saturating_mul(100u32.into());
let fee = value / 2u32.into();
let deposit = T::BountyDepositBase::get() + T::DataDepositPerByte::get() * MAX_BYTES.into();
let _ = T::Currency::make_free_balance_be(&caller, deposit);
let curator = account("curator", u, SEED);
let _ = T::Currency::make_free_balance_be(&curator, fee / 2u32.into());
let reason = vec![0; d as usize];
(caller, curator, fee, value, reason)
}
fn create_bounty<T: Config>() -> Result<(
<T::Lookup as StaticLookup>::Source,
BountyIndex,
), &'static str> {
let (caller, curator, fee, value, reason) = setup_bounty::<T>(0, MAX_BYTES);
let curator_lookup = T::Lookup::unlookup(curator.clone());
Bounties::<T>::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?;
let bounty_id = BountyCount::get() - 1;
Bounties::<T>::approve_bounty(RawOrigin::Root.into(), bounty_id)?;
Treasury::<T>::on_initialize(T::BlockNumber::zero());
Bounties::<T>::propose_curator(RawOrigin::Root.into(), bounty_id, curator_lookup.clone(), fee)?;
Bounties::<T>::accept_curator(RawOrigin::Signed(curator).into(), bounty_id)?;
Ok((curator_lookup, bounty_id))
}
fn setup_pot_account<T: Config>() {
let pot_account = Bounties::<T>::account_id();
let value = T::Currency::minimum_balance().saturating_mul(1_000_000_000u32.into());
let _ = T::Currency::make_free_balance_be(&pot_account, value);
}
fn assert_last_event<T: Config>(generic_event: <T as Config>::Event) {
let events = frame_system::Module::<T>::events();
let system_event: <T as frame_system::Config>::Event = generic_event.into();
// compare to the last event record
let EventRecord { event, .. } = &events[events.len() - 1];
assert_eq!(event, &system_event);
}
const MAX_BYTES: u32 = 16384;
benchmarks! {
propose_bounty {
let d in 0 .. MAX_BYTES;
let (caller, curator, fee, value, description) = setup_bounty::<T>(0, d);
}: _(RawOrigin::Signed(caller), value, description)
approve_bounty {
let (caller, curator, fee, value, reason) = setup_bounty::<T>(0, MAX_BYTES);
Bounties::<T>::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?;
let bounty_id = BountyCount::get() - 1;
}: _(RawOrigin::Root, bounty_id)
propose_curator {
setup_pot_account::<T>();
let (caller, curator, fee, value, reason) = setup_bounty::<T>(0, MAX_BYTES);
let curator_lookup = T::Lookup::unlookup(curator.clone());
Bounties::<T>::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?;
let bounty_id = BountyCount::get() - 1;
Bounties::<T>::approve_bounty(RawOrigin::Root.into(), bounty_id)?;
Bounties::<T>::on_initialize(T::BlockNumber::zero());
}: _(RawOrigin::Root, bounty_id, curator_lookup, fee)
// Worst case when curator is inactive and any sender unassigns the curator.
unassign_curator {
setup_pot_account::<T>();
let (curator_lookup, bounty_id) = create_bounty::<T>()?;
Bounties::<T>::on_initialize(T::BlockNumber::zero());
let bounty_id = BountyCount::get() - 1;
frame_system::Module::<T>::set_block_number(T::BountyUpdatePeriod::get() + 1u32.into());
let caller = whitelisted_caller();
}: _(RawOrigin::Signed(caller), bounty_id)
accept_curator {
setup_pot_account::<T>();
let (caller, curator, fee, value, reason) = setup_bounty::<T>(0, MAX_BYTES);
let curator_lookup = T::Lookup::unlookup(curator.clone());
Bounties::<T>::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?;
let bounty_id = BountyCount::get() - 1;
Bounties::<T>::approve_bounty(RawOrigin::Root.into(), bounty_id)?;
Bounties::<T>::on_initialize(T::BlockNumber::zero());
Bounties::<T>::propose_curator(RawOrigin::Root.into(), bounty_id, curator_lookup, fee)?;
}: _(RawOrigin::Signed(curator), bounty_id)
award_bounty {
setup_pot_account::<T>();
let (curator_lookup, bounty_id) = create_bounty::<T>()?;
Bounties::<T>::on_initialize(T::BlockNumber::zero());
let bounty_id = BountyCount::get() - 1;
let curator = T::Lookup::lookup(curator_lookup)?;
let beneficiary = T::Lookup::unlookup(account("beneficiary", 0, SEED));
}: _(RawOrigin::Signed(curator), bounty_id, beneficiary)
claim_bounty {
setup_pot_account::<T>();
let (curator_lookup, bounty_id) = create_bounty::<T>()?;
Bounties::<T>::on_initialize(T::BlockNumber::zero());
let bounty_id = BountyCount::get() - 1;
let curator = T::Lookup::lookup(curator_lookup)?;
let beneficiary_account: T::AccountId = account("beneficiary", 0, SEED);
let beneficiary = T::Lookup::unlookup(beneficiary_account.clone());
Bounties::<T>::award_bounty(RawOrigin::Signed(curator.clone()).into(), bounty_id, beneficiary)?;
frame_system::Module::<T>::set_block_number(T::BountyDepositPayoutDelay::get());
ensure!(T::Currency::free_balance(&beneficiary_account).is_zero(), "Beneficiary already has balance");
}: _(RawOrigin::Signed(curator), bounty_id)
verify {
ensure!(!T::Currency::free_balance(&beneficiary_account).is_zero(), "Beneficiary didn't get paid");
}
close_bounty_proposed {
setup_pot_account::<T>();
let (caller, curator, fee, value, reason) = setup_bounty::<T>(0, 0);
Bounties::<T>::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?;
let bounty_id = BountyCount::get() - 1;
}: close_bounty(RawOrigin::Root, bounty_id)
close_bounty_active {
setup_pot_account::<T>();
let (curator_lookup, bounty_id) = create_bounty::<T>()?;
Bounties::<T>::on_initialize(T::BlockNumber::zero());
let bounty_id = BountyCount::get() - 1;
}: close_bounty(RawOrigin::Root, bounty_id)
verify {
assert_last_event::<T>(RawEvent::BountyCanceled(bounty_id).into())
}
extend_bounty_expiry {
setup_pot_account::<T>();
let (curator_lookup, bounty_id) = create_bounty::<T>()?;
Bounties::<T>::on_initialize(T::BlockNumber::zero());
let bounty_id = BountyCount::get() - 1;
let curator = T::Lookup::lookup(curator_lookup)?;
}: _(RawOrigin::Signed(curator), bounty_id, Vec::new())
verify {
assert_last_event::<T>(RawEvent::BountyExtended(bounty_id).into())
}
spend_funds {
let b in 1 .. 100;
setup_pot_account::<T>();
create_approved_bounties::<T>(b)?;
let mut budget_remaining = BalanceOf::<T>::max_value();
let mut imbalance = PositiveImbalanceOf::<T>::zero();
let mut total_weight = Weight::zero();
let mut missed_any = false;
}: {
<Bounties<T> as pallet_treasury::SpendFunds<T>>::spend_funds(
&mut budget_remaining,
&mut imbalance,
&mut total_weight,
&mut missed_any,
);
}
verify {
ensure!(budget_remaining < BalanceOf::<T>::max_value(), "Budget not used");
ensure!(missed_any == false, "Missed some");
assert_last_event::<T>(RawEvent::BountyBecameActive(b - 1).into())
}
}
impl_benchmark_test_suite!(
Bounties,
crate::tests::new_test_ext(),
crate::tests::Test,
);