Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 11 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
6 changes: 6 additions & 0 deletions frame/benchmarking/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,12 @@ pub use v1::*;
/// The underscore will be substituted with the name of the benchmark (i.e. the name of the
/// function in the benchmark function definition).
///
/// In case of a `force_origin`, this is the general syntax:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps a sentence or two explaining what force_origin means or a link to elsewhere in the docs where this is explained

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated. Please let me know if this works.

/// ```ignore
/// #[extrinsic_call]
/// _(force_origin as T::RuntimeOrigin, 0u32.into(), 0);
/// ```
///
/// Regardless of whether `#[extrinsic_call]` or `#[block]` is used, this attribute also serves
/// the purpose of designating the boundary between the setup code portion of the benchmark
/// (everything before the `#[extrinsic_call]` or `#[block]` attribute) and the verification
Expand Down
128 changes: 86 additions & 42 deletions frame/lottery/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,20 @@

use super::*;

use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller, BenchmarkError};
use crate::Pallet as Lottery;
use frame_benchmarking::{
impl_benchmark_test_suite,
v1::{account, whitelisted_caller, BenchmarkError},
v2::*,
};
use frame_support::{
dispatch::UnfilteredDispatchable,
storage::bounded_vec::BoundedVec,
traits::{EnsureOrigin, OnInitialize},
};
use frame_system::RawOrigin;
use sp_runtime::traits::{Bounded, Zero};

use crate::Pallet as Lottery;

// Set up and start a lottery
fn setup_lottery<T: Config>(repeat: bool) -> Result<(), &'static str> {
let price = T::Currency::minimum_balance();
Expand All @@ -50,72 +54,100 @@ fn setup_lottery<T: Config>(repeat: bool) -> Result<(), &'static str> {
Ok(())
}

benchmarks! {
buy_ticket {
#[benchmarks]
mod benchmarks {
use super::*;

#[benchmark]
fn buy_ticket() -> Result<(), BenchmarkError> {
let caller = whitelisted_caller();
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
setup_lottery::<T>(false)?;
// force user to have a long vec of calls participating
let set_code_index: CallIndex = Lottery::<T>::call_to_index(
&frame_system::Call::<T>::set_code{ code: vec![] }.into()
&frame_system::Call::<T>::set_code { code: vec![] }.into(),
)?;
let already_called: (u32, BoundedVec<CallIndex, T::MaxCalls>) = (
LotteryIndex::<T>::get(),
BoundedVec::<CallIndex, T::MaxCalls>::try_from(vec![
set_code_index;
T::MaxCalls::get().saturating_sub(1) as usize
]).unwrap(),
T::MaxCalls::get().saturating_sub(1)
as usize
])
.unwrap(),
);
Participants::<T>::insert(&caller, already_called);

let call = frame_system::Call::<T>::remark { remark: vec![] };
}: _(RawOrigin::Signed(caller), Box::new(call.into()))
verify {

#[extrinsic_call]
_(RawOrigin::Signed(caller), Box::new(call.into()));

assert_eq!(TicketsCount::<T>::get(), 1);

Ok(())
}

set_calls {
let n in 0 .. T::MaxCalls::get() as u32;
#[benchmark]
fn set_calls(n: Linear<0, { T::MaxCalls::get() }>) -> Result<(), BenchmarkError> {
let calls = vec![frame_system::Call::<T>::remark { remark: vec![] }.into(); n as usize];
let origin =
T::ManagerOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
assert!(CallIndices::<T>::get().is_empty());
}: _<T::RuntimeOrigin>(origin, calls)
verify {

#[extrinsic_call]
_(origin as T::RuntimeOrigin, calls);

if !n.is_zero() {
assert!(!CallIndices::<T>::get().is_empty());
}

Ok(())
}

start_lottery {
#[benchmark]
fn start_lottery() -> Result<(), BenchmarkError> {
let price = BalanceOf::<T>::max_value();
let end = 10u32.into();
let payout = 5u32.into();
let origin =
T::ManagerOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
}: _<T::RuntimeOrigin>(origin, price, end, payout, true)
verify {

#[extrinsic_call]
_(origin as T::RuntimeOrigin, price, end, payout, true);

assert!(crate::Lottery::<T>::get().is_some());

Ok(())
}

stop_repeat {
#[benchmark]
fn stop_repeat() -> Result<(), BenchmarkError> {
setup_lottery::<T>(true)?;
assert_eq!(crate::Lottery::<T>::get().unwrap().repeat, true);
let origin =
T::ManagerOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
}: _<T::RuntimeOrigin>(origin)
verify {

#[extrinsic_call]
_(origin as T::RuntimeOrigin);

assert_eq!(crate::Lottery::<T>::get().unwrap().repeat, false);

Ok(())
}

on_initialize_end {
#[benchmark]
fn on_initialize_end() -> Result<(), BenchmarkError> {
setup_lottery::<T>(false)?;
let winner = account("winner", 0, 0);
// User needs more than min balance to get ticket
T::Currency::make_free_balance_be(&winner, T::Currency::minimum_balance() * 10u32.into());
// Make sure lottery account has at least min balance too
let lottery_account = Lottery::<T>::account_id();
T::Currency::make_free_balance_be(&lottery_account, T::Currency::minimum_balance() * 10u32.into());
T::Currency::make_free_balance_be(
&lottery_account,
T::Currency::minimum_balance() * 10u32.into(),
);
// Buy a ticket
let call = frame_system::Call::<T>::remark { remark: vec![] };
Lottery::<T>::buy_ticket(RawOrigin::Signed(winner.clone()).into(), Box::new(call.into()))?;
Expand All @@ -124,29 +156,37 @@ benchmarks! {
// Assert that lotto is set up for winner
assert_eq!(TicketsCount::<T>::get(), 1);
assert!(!Lottery::<T>::pot().1.is_zero());
}: {
// Generate `MaxGenerateRandom` numbers for worst case scenario
for i in 0 .. T::MaxGenerateRandom::get() {
Lottery::<T>::generate_random_number(i);

#[block]
{
// Generate `MaxGenerateRandom` numbers for worst case scenario
for i in 0..T::MaxGenerateRandom::get() {
Lottery::<T>::generate_random_number(i);
}
// Start lottery has block 15 configured for payout
Lottery::<T>::on_initialize(15u32.into());
}
// Start lottery has block 15 configured for payout
Lottery::<T>::on_initialize(15u32.into());
}
verify {

assert!(crate::Lottery::<T>::get().is_none());
assert_eq!(TicketsCount::<T>::get(), 0);
assert_eq!(Lottery::<T>::pot().1, 0u32.into());
assert!(!T::Currency::free_balance(&winner).is_zero())
assert!(!T::Currency::free_balance(&winner).is_zero());

Ok(())
}

on_initialize_repeat {
#[benchmark]
fn on_initialize_repeat() -> Result<(), BenchmarkError> {
setup_lottery::<T>(true)?;
let winner = account("winner", 0, 0);
// User needs more than min balance to get ticket
T::Currency::make_free_balance_be(&winner, T::Currency::minimum_balance() * 10u32.into());
// Make sure lottery account has at least min balance too
let lottery_account = Lottery::<T>::account_id();
T::Currency::make_free_balance_be(&lottery_account, T::Currency::minimum_balance() * 10u32.into());
T::Currency::make_free_balance_be(
&lottery_account,
T::Currency::minimum_balance() * 10u32.into(),
);
// Buy a ticket
let call = frame_system::Call::<T>::remark { remark: vec![] };
Lottery::<T>::buy_ticket(RawOrigin::Signed(winner.clone()).into(), Box::new(call.into()))?;
Expand All @@ -155,20 +195,24 @@ benchmarks! {
// Assert that lotto is set up for winner
assert_eq!(TicketsCount::<T>::get(), 1);
assert!(!Lottery::<T>::pot().1.is_zero());
}: {
// Generate `MaxGenerateRandom` numbers for worst case scenario
for i in 0 .. T::MaxGenerateRandom::get() {
Lottery::<T>::generate_random_number(i);

#[block]
{
// Generate `MaxGenerateRandom` numbers for worst case scenario
for i in 0..T::MaxGenerateRandom::get() {
Lottery::<T>::generate_random_number(i);
}
// Start lottery has block 15 configured for payout
Lottery::<T>::on_initialize(15u32.into());
}
// Start lottery has block 15 configured for payout
Lottery::<T>::on_initialize(15u32.into());
}
verify {

assert!(crate::Lottery::<T>::get().is_some());
assert_eq!(LotteryIndex::<T>::get(), 2);
assert_eq!(TicketsCount::<T>::get(), 0);
assert_eq!(Lottery::<T>::pot().1, 0u32.into());
assert!(!T::Currency::free_balance(&winner).is_zero())
assert!(!T::Currency::free_balance(&winner).is_zero());

Ok(())
}

impl_benchmark_test_suite!(Lottery, crate::mock::new_test_ext(), crate::mock::Test);
Expand Down
14 changes: 13 additions & 1 deletion frame/support/procedural/src/benchmark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,18 @@ fn expand_benchmark(
}
expr_call.args = final_args;

let origin = match origin {
Expr::Cast(t) => {
let ty = t.ty.clone();
quote! {
<<T as frame_system::Config>::RuntimeOrigin as From<#ty>>::from(#origin);
}
},
_ => quote! {
#origin.into();
},
};

// determine call name (handles `_` and normal call syntax)
let expr_span = expr_call.span();
let call_err = || {
Expand Down Expand Up @@ -803,7 +815,7 @@ fn expand_benchmark(
let __call_decoded = <Call<#type_use_generics> as #codec::Decode>
::decode(&mut &__benchmarked_call_encoded[..])
.expect("call is encoded above, encoding must be correct");
let __origin = #origin.into();
let __origin = #origin;
<Call<#type_use_generics> as #traits::UnfilteredDispatchable>::dispatch_bypass_filter(
__call_decoded,
__origin,
Expand Down
17 changes: 17 additions & 0 deletions frame/support/test/tests/benchmark_ui/invalid_origin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use frame_benchmarking::v2::*;
#[allow(unused_imports)]
use frame_support_test::Config;
use frame_support_test::Call;

#[benchmarks]
mod benches {
use super::*;

#[benchmark]
fn bench() {
#[extrinsic_call]
thing(1);
}
}

fn main() {}
18 changes: 18 additions & 0 deletions frame/support/test/tests/benchmark_ui/invalid_origin.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
error[E0599]: no variant or associated item named `new_call_variant_thing` found for enum `frame_support_test::Call` in the current scope
--> tests/benchmark_ui/invalid_origin.rs:6:1
|
6 | #[benchmarks]
| ^^^^^^^^^^^^^ variant or associated item not found in `frame_support_test::Call<T>`
|
= note: this error originates in the attribute macro `benchmarks` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: the trait bound `<T as frame_support_test::Config>::RuntimeOrigin: From<{integer}>` is not satisfied
--> tests/benchmark_ui/invalid_origin.rs:13:9
|
6 | #[benchmarks]
| ------------- required by a bound introduced by this call
...
13 | thing(1);
| ^ the trait `From<{integer}>` is not implemented for `<T as frame_support_test::Config>::RuntimeOrigin`
|
= note: required for `{integer}` to implement `Into<<T as frame_support_test::Config>::RuntimeOrigin>`