// 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. //! Lottery pallet benchmarking. #![cfg(feature = "runtime-benchmarks")] use super::*; use crate::Pallet as Lottery; use frame_benchmarking::{ v1::{account, whitelisted_caller, BenchmarkError}, v2::*, }; use frame_support::{ storage::bounded_vec::BoundedVec, traits::{EnsureOrigin, OnInitialize}, }; use frame_system::RawOrigin; use sp_runtime::traits::{Bounded, Zero}; // Set up and start a lottery fn setup_lottery(repeat: bool) -> Result<(), &'static str> { let price = T::Currency::minimum_balance(); let length = 10u32.into(); let delay = 5u32.into(); // Calls will be maximum length... let mut calls = vec![ frame_system::Call::::set_code { code: vec![] }.into(); T::MaxCalls::get().saturating_sub(1) as usize ]; // Last call will be the match for worst case scenario. calls.push(frame_system::Call::::remark { remark: vec![] }.into()); let origin = T::ManagerOrigin::try_successful_origin() .expect("ManagerOrigin has no successful origin required for the benchmark"); Lottery::::set_calls(origin.clone(), calls)?; Lottery::::start_lottery(origin, price, length, delay, repeat)?; Ok(()) } #[benchmarks] mod benchmarks { use super::*; #[benchmark] fn buy_ticket() -> Result<(), BenchmarkError> { let caller = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); setup_lottery::(false)?; // force user to have a long vec of calls participating let set_code_index: CallIndex = Lottery::::call_to_index( &frame_system::Call::::set_code { code: vec![] }.into(), )?; let already_called: (u32, BoundedVec) = ( LotteryIndex::::get(), BoundedVec::::try_from(vec![ set_code_index; T::MaxCalls::get().saturating_sub(1) as usize ]) .unwrap(), ); Participants::::insert(&caller, already_called); let call = frame_system::Call::::remark { remark: vec![] }; #[extrinsic_call] _(RawOrigin::Signed(caller), Box::new(call.into())); assert_eq!(TicketsCount::::get(), 1); Ok(()) } #[benchmark] fn set_calls(n: Linear<0, { T::MaxCalls::get() }>) -> Result<(), BenchmarkError> { let calls = vec![frame_system::Call::::remark { remark: vec![] }.into(); n as usize]; let origin = T::ManagerOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; assert!(CallIndices::::get().is_empty()); #[extrinsic_call] _(origin as T::RuntimeOrigin, calls); if !n.is_zero() { assert!(!CallIndices::::get().is_empty()); } Ok(()) } #[benchmark] fn start_lottery() -> Result<(), BenchmarkError> { let price = BalanceOf::::max_value(); let end = 10u32.into(); let payout = 5u32.into(); let origin = T::ManagerOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; #[extrinsic_call] _(origin as T::RuntimeOrigin, price, end, payout, true); assert!(crate::Lottery::::get().is_some()); Ok(()) } #[benchmark] fn stop_repeat() -> Result<(), BenchmarkError> { setup_lottery::(true)?; assert_eq!(crate::Lottery::::get().unwrap().repeat, true); let origin = T::ManagerOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; #[extrinsic_call] _(origin as T::RuntimeOrigin); assert_eq!(crate::Lottery::::get().unwrap().repeat, false); Ok(()) } #[benchmark] fn on_initialize_end() -> Result<(), BenchmarkError> { setup_lottery::(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::::account_id(); T::Currency::make_free_balance_be( &lottery_account, T::Currency::minimum_balance() * 10u32.into(), ); // Buy a ticket let call = frame_system::Call::::remark { remark: vec![] }; Lottery::::buy_ticket(RawOrigin::Signed(winner.clone()).into(), Box::new(call.into()))?; // Kill user account for worst case T::Currency::make_free_balance_be(&winner, 0u32.into()); // Assert that lotto is set up for winner assert_eq!(TicketsCount::::get(), 1); assert!(!Lottery::::pot().1.is_zero()); #[block] { // Generate `MaxGenerateRandom` numbers for worst case scenario for i in 0..T::MaxGenerateRandom::get() { Lottery::::generate_random_number(i); } // Start lottery has block 15 configured for payout Lottery::::on_initialize(15u32.into()); } assert!(crate::Lottery::::get().is_none()); assert_eq!(TicketsCount::::get(), 0); assert_eq!(Lottery::::pot().1, 0u32.into()); assert!(!T::Currency::free_balance(&winner).is_zero()); Ok(()) } #[benchmark] fn on_initialize_repeat() -> Result<(), BenchmarkError> { setup_lottery::(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::::account_id(); T::Currency::make_free_balance_be( &lottery_account, T::Currency::minimum_balance() * 10u32.into(), ); // Buy a ticket let call = frame_system::Call::::remark { remark: vec![] }; Lottery::::buy_ticket(RawOrigin::Signed(winner.clone()).into(), Box::new(call.into()))?; // Kill user account for worst case T::Currency::make_free_balance_be(&winner, 0u32.into()); // Assert that lotto is set up for winner assert_eq!(TicketsCount::::get(), 1); assert!(!Lottery::::pot().1.is_zero()); #[block] { // Generate `MaxGenerateRandom` numbers for worst case scenario for i in 0..T::MaxGenerateRandom::get() { Lottery::::generate_random_number(i); } // Start lottery has block 15 configured for payout Lottery::::on_initialize(15u32.into()); } assert!(crate::Lottery::::get().is_some()); assert_eq!(LotteryIndex::::get(), 2); assert_eq!(TicketsCount::::get(), 0); assert_eq!(Lottery::::pot().1, 0u32.into()); assert!(!T::Currency::free_balance(&winner).is_zero()); Ok(()) } impl_benchmark_test_suite!(Lottery, crate::mock::new_test_ext(), crate::mock::Test); }