// Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Pezkuwi. // Pezkuwi is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Pezkuwi is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Pezkuwi. If not, see . //! Benchmarking for auctions pezpallet #![cfg(feature = "runtime-benchmarks")] use super::{Pezpallet as Auctions, *}; use pezframe_support::{ assert_ok, traits::{EnsureOrigin, OnInitialize}, }; use pezframe_system::RawOrigin; use pezkuwi_runtime_teyrchains::paras; use pezsp_runtime::{traits::Bounded, SaturatedConversion}; use pezframe_benchmarking::v2::*; fn assert_last_event(generic_event: ::RuntimeEvent) { let events = pezframe_system::Pezpallet::::events(); let system_event: ::RuntimeEvent = generic_event.into(); // compare to the last event record let pezframe_system::EventRecord { event, .. } = &events[events.len() - 1]; assert_eq!(event, &system_event); } fn fill_winners(lease_period_index: LeasePeriodOf) { let auction_index = AuctionCounter::::get(); let minimum_balance = CurrencyOf::::minimum_balance(); for n in 1..=SlotRange::SLOT_RANGE_COUNT as u32 { let owner = account("owner", n, 0); let worst_validation_code = T::Registrar::worst_validation_code(); let worst_head_data = T::Registrar::worst_head_data(); CurrencyOf::::make_free_balance_be(&owner, BalanceOf::::max_value()); assert!(T::Registrar::register( owner, ParaId::from(n), worst_head_data, worst_validation_code ) .is_ok()); } assert_ok!(paras::Pezpallet::::add_trusted_validation_code( pezframe_system::Origin::::Root.into(), T::Registrar::worst_validation_code(), )); T::Registrar::execute_pending_transitions(); for n in 1..=SlotRange::SLOT_RANGE_COUNT as u32 { let bidder = account("bidder", n, 0); CurrencyOf::::make_free_balance_be(&bidder, BalanceOf::::max_value()); let slot_range = SlotRange::n((n - 1) as u8).unwrap(); let (start, end) = slot_range.as_pair(); assert!(Auctions::::bid( RawOrigin::Signed(bidder).into(), ParaId::from(n), auction_index, lease_period_index + start.into(), // First Slot lease_period_index + end.into(), // Last slot minimum_balance.saturating_mul(n.into()), // Amount ) .is_ok()); } } #[benchmarks( where T: pezpallet_babe::Config + paras::Config, )] mod benchmarks { use super::*; #[benchmark] fn new_auction() -> Result<(), BenchmarkError> { let duration = BlockNumberFor::::max_value(); let lease_period_index = LeasePeriodOf::::max_value(); let origin = T::InitiateOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; #[extrinsic_call] _(origin as T::RuntimeOrigin, duration, lease_period_index); assert_last_event::( Event::::AuctionStarted { auction_index: AuctionCounter::::get(), lease_period: LeasePeriodOf::::max_value(), ending: BlockNumberFor::::max_value(), } .into(), ); Ok(()) } // Worst case scenario a new bid comes in which kicks out an existing bid for the same slot. #[benchmark] fn bid() -> Result<(), BenchmarkError> { // If there is an offset, we need to be on that block to be able to do lease things. let (_, offset) = T::Leaser::lease_period_length(); pezframe_system::Pezpallet::::set_block_number(offset + One::one()); // Create a new auction let duration = BlockNumberFor::::max_value(); let lease_period_index = LeasePeriodOf::::zero(); let origin = T::InitiateOrigin::try_successful_origin() .expect("InitiateOrigin has no successful origin required for the benchmark"); Auctions::::new_auction(origin, duration, lease_period_index)?; let para = ParaId::from(0); let new_para = ParaId::from(1_u32); // Register the paras let owner = account("owner", 0, 0); CurrencyOf::::make_free_balance_be(&owner, BalanceOf::::max_value()); let worst_head_data = T::Registrar::worst_head_data(); let worst_validation_code = T::Registrar::worst_validation_code(); T::Registrar::register( owner.clone(), para, worst_head_data.clone(), worst_validation_code.clone(), )?; T::Registrar::register(owner, new_para, worst_head_data, worst_validation_code.clone())?; assert_ok!(paras::Pezpallet::::add_trusted_validation_code( pezframe_system::Origin::::Root.into(), worst_validation_code, )); T::Registrar::execute_pending_transitions(); // Make an existing bid let auction_index = AuctionCounter::::get(); let first_slot = AuctionInfo::::get().unwrap().0; let last_slot = first_slot + 3u32.into(); let first_amount = CurrencyOf::::minimum_balance(); let first_bidder: T::AccountId = account("first_bidder", 0, 0); CurrencyOf::::make_free_balance_be(&first_bidder, BalanceOf::::max_value()); Auctions::::bid( RawOrigin::Signed(first_bidder.clone()).into(), para, auction_index, first_slot, last_slot, first_amount, )?; let caller: T::AccountId = whitelisted_caller(); CurrencyOf::::make_free_balance_be(&caller, BalanceOf::::max_value()); let bigger_amount = CurrencyOf::::minimum_balance().saturating_mul(10u32.into()); assert_eq!(CurrencyOf::::reserved_balance(&first_bidder), first_amount); #[extrinsic_call] _( RawOrigin::Signed(caller.clone()), new_para, auction_index, first_slot, last_slot, bigger_amount, ); // Confirms that we unreserved funds from a previous bidder, which is worst case // scenario. assert_eq!(CurrencyOf::::reserved_balance(&caller), bigger_amount); Ok(()) } // Worst case: 10 bidders taking all wining spots, and we need to calculate the winner for // auction end. Entire winner map should be full and removed at the end of the benchmark. #[benchmark] fn on_initialize() -> Result<(), BenchmarkError> { // If there is an offset, we need to be on that block to be able to do lease things. let (lease_length, offset) = T::Leaser::lease_period_length(); pezframe_system::Pezpallet::::set_block_number(offset + One::one()); // Create a new auction let duration: BlockNumberFor = lease_length / 2u32.into(); let lease_period_index = LeasePeriodOf::::zero(); let now = pezframe_system::Pezpallet::::block_number(); let origin = T::InitiateOrigin::try_successful_origin() .expect("InitiateOrigin has no successful origin required for the benchmark"); Auctions::::new_auction(origin, duration, lease_period_index)?; fill_winners::(lease_period_index); for winner in Winning::::get(BlockNumberFor::::from(0u32)).unwrap().iter() { assert!(winner.is_some()); } let winning_data = Winning::::get(BlockNumberFor::::from(0u32)).unwrap(); // Make winning map full for i in 0u32..(T::EndingPeriod::get() / T::SampleLength::get()).saturated_into() { Winning::::insert(BlockNumberFor::::from(i), winning_data.clone()); } // Move ahead to the block we want to initialize pezframe_system::Pezpallet::::set_block_number(duration + now + T::EndingPeriod::get()); // Trigger epoch change for new random number value: { pezpallet_babe::EpochStart::::set((Zero::zero(), u32::MAX.into())); pezpallet_babe::Pezpallet::::on_initialize(duration + now + T::EndingPeriod::get()); let authorities = pezpallet_babe::Pezpallet::::authorities(); // Check for non empty authority set since it otherwise emits a No-OP warning. if !authorities.is_empty() { pezpallet_babe::Pezpallet::::enact_epoch_change( authorities.clone(), authorities, None, ); } } #[block] { Auctions::::on_initialize(duration + now + T::EndingPeriod::get()); } let auction_index = AuctionCounter::::get(); assert_last_event::(Event::::AuctionClosed { auction_index }.into()); assert!(Winning::::iter().count().is_zero()); Ok(()) } // Worst case: 10 bidders taking all wining spots, and winning data is full. #[benchmark] fn cancel_auction() -> Result<(), BenchmarkError> { // If there is an offset, we need to be on that block to be able to do lease things. let (lease_length, offset) = T::Leaser::lease_period_length(); pezframe_system::Pezpallet::::set_block_number(offset + One::one()); // Create a new auction let duration: BlockNumberFor = lease_length / 2u32.into(); let lease_period_index = LeasePeriodOf::::zero(); let origin = T::InitiateOrigin::try_successful_origin() .expect("InitiateOrigin has no successful origin required for the benchmark"); Auctions::::new_auction(origin, duration, lease_period_index)?; fill_winners::(lease_period_index); let winning_data = Winning::::get(BlockNumberFor::::from(0u32)).unwrap(); for winner in winning_data.iter() { assert!(winner.is_some()); } // Make winning map full for i in 0u32..(T::EndingPeriod::get() / T::SampleLength::get()).saturated_into() { Winning::::insert(BlockNumberFor::::from(i), winning_data.clone()); } assert!(AuctionInfo::::get().is_some()); #[extrinsic_call] _(RawOrigin::Root); assert!(AuctionInfo::::get().is_none()); Ok(()) } impl_benchmark_test_suite!( Auctions, crate::integration_tests::new_test_ext(), crate::integration_tests::Test, ); }