// 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,
);
}