Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix and migrated benchmarking of vesting. #1609

Merged
merged 1 commit into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
244 changes: 164 additions & 80 deletions pallets/vesting/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,13 @@

#![cfg(feature = "runtime-benchmarks")]

use frame_benchmarking::{account, benchmarks, whitelisted_caller};
use frame_system::{Pallet as System, RawOrigin};
use sp_runtime::traits::Bounded;

use super::*;
use crate::Pallet as Vesting;
use frame_benchmarking::v2::*;
use frame_system::RawOrigin;
use sp_runtime::traits::Bounded;

const SEED: u32 = 0;

type BalanceOf<T> =
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;

fn add_locks<T: Config>(who: &T::AccountId, n: u8) {
for id in 0..n {
let lock_id = [id; 8];
Expand All @@ -41,190 +36,279 @@ fn add_locks<T: Config>(who: &T::AccountId, n: u8) {
}
}

fn add_vesting_schedule<T: Config>(who: &T::AccountId) -> Result<(), &'static str> {
fn add_vesting_schedule<T: Config>(who: &T::AccountId) -> Result<BalanceOf<T>, BenchmarkError> {
let locked = 100u32;
let per_block = 10u32;
let starting_block = 1u32;

System::<T>::set_block_number(0u32.into());
frame_system::Pallet::<T>::set_block_number(0u32.into());
Pallet::<T>::init_vesting_start_at(RawOrigin::Root.into(), 0u32.into())
.map_err(|_| BenchmarkError::Stop("Failed to init vesting start"))?;

Vesting::<T>::init_vesting_start_at(RawOrigin::Root.into(), 0u32.into())?;
Pallet::<T>::add_vesting_schedule(&who, locked.into(), per_block.into(), starting_block.into())
.map_err(|_| BenchmarkError::Stop("Failed to add vesting schedule"))?;

// Add schedule to avoid `NotVesting` error.
Vesting::<T>::add_vesting_schedule(
&who,
locked.into(),
per_block.into(),
starting_block.into(),
)?;
Ok(())
Ok(locked.into())
}

benchmarks! {
vest_locked {
let l in 0 .. MaxLocksOf::<T>::get();
#[benchmarks]
mod benchmarks {
use super::*;

let caller = whitelisted_caller();
#[benchmark]
fn vest_locked(
l: Linear<0, { MaxLocksOf::<T>::get() }>,
s: Linear<2, { T::MAX_VESTING_SCHEDULES }>,
) -> Result<(), BenchmarkError> {
let caller: T::AccountId = account("seed", 1, 1);
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());

add_locks::<T>(&caller, l as u8);
add_vesting_schedule::<T>(&caller)?;
// At block zero, everything is vested.
System::<T>::set_block_number(BlockNumberFor::<T>::zero());

frame_system::Pallet::<T>::set_block_number(BlockNumberFor::<T>::zero());
assert_eq!(
Vesting::<T>::vesting_balance(&caller),
Pallet::<T>::vesting_balance(&caller),
Some(100u32.into()),
"Vesting schedule not added",
);
}: vest(RawOrigin::Signed(caller.clone()))
verify {
// Nothing happened since everything is still vested.

#[extrinsic_call]
vest(RawOrigin::Signed(caller.clone()));

assert_eq!(
Vesting::<T>::vesting_balance(&caller),
Pallet::<T>::vesting_balance(&caller),
Some(100u32.into()),
"Vesting schedule was removed",
);
}

vest_unlocked {
let l in 0 .. MaxLocksOf::<T>::get();
Ok(())
}

let caller = whitelisted_caller();
#[benchmark]
fn vest_unlocked(
l: Linear<0, { MaxLocksOf::<T>::get() }>,
s: Linear<2, { T::MAX_VESTING_SCHEDULES }>,
) -> Result<(), BenchmarkError> {
let caller: T::AccountId = account("seed", 1, 1);
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());

add_locks::<T>(&caller, l as u8);
add_vesting_schedule::<T>(&caller)?;
// At block 20, everything is unvested.
System::<T>::set_block_number(20u32.into());

frame_system::Pallet::<T>::set_block_number(20u32.into());
assert_eq!(
Vesting::<T>::vesting_balance(&caller),
Pallet::<T>::vesting_balance(&caller),
Some(BalanceOf::<T>::zero()),
"Vesting schedule still active",
);
}: vest(RawOrigin::Signed(caller.clone()))
verify {
// Vesting schedule is removed!

#[extrinsic_call]
vest(RawOrigin::Signed(caller.clone()));

assert_eq!(
Vesting::<T>::vesting_balance(&caller),
Pallet::<T>::vesting_balance(&caller),
None,
"Vesting schedule was not removed",
);
}

vest_other_locked {
let l in 0 .. MaxLocksOf::<T>::get();
Ok(())
}

#[benchmark]
fn vest_other_locked(
l: Linear<0, { MaxLocksOf::<T>::get() }>,
s: Linear<2, { T::MAX_VESTING_SCHEDULES }>,
) -> Result<(), BenchmarkError> {
let other: T::AccountId = account("other", 0, SEED);
let other_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(other.clone());
T::Currency::make_free_balance_be(&other, BalanceOf::<T>::max_value());

add_locks::<T>(&other, l as u8);
add_vesting_schedule::<T>(&other)?;
// At block zero, everything is vested.
System::<T>::set_block_number(BlockNumberFor::<T>::zero());

frame_system::Pallet::<T>::set_block_number(BlockNumberFor::<T>::zero());
assert_eq!(
Vesting::<T>::vesting_balance(&other),
Pallet::<T>::vesting_balance(&other),
Some(100u32.into()),
"Vesting schedule not added",
);

let caller: T::AccountId = whitelisted_caller();
}: vest_other(RawOrigin::Signed(caller.clone()), other_lookup)
verify {
// Nothing happened since everything is still vested.

#[extrinsic_call]
vest_other(RawOrigin::Signed(caller), other_lookup);

assert_eq!(
Vesting::<T>::vesting_balance(&other),
Pallet::<T>::vesting_balance(&other),
Some(100u32.into()),
"Vesting schedule was removed",
);
}

vest_other_unlocked {
let l in 0 .. MaxLocksOf::<T>::get();
Ok(())
}

#[benchmark]
fn vest_other_unlocked(
l: Linear<0, { MaxLocksOf::<T>::get() }>,
s: Linear<2, { T::MAX_VESTING_SCHEDULES }>,
) -> Result<(), BenchmarkError> {
let other: T::AccountId = account("other", 0, SEED);
let other_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(other.clone());
T::Currency::make_free_balance_be(&other, BalanceOf::<T>::max_value());

add_locks::<T>(&other, l as u8);
add_vesting_schedule::<T>(&other)?;
// At block 20, everything is unvested.
System::<T>::set_block_number(20u32.into());

frame_system::Pallet::<T>::set_block_number(20u32.into());
assert_eq!(
Vesting::<T>::vesting_balance(&other),
Pallet::<T>::vesting_balance(&other),
Some(BalanceOf::<T>::zero()),
"Vesting schedule still active",
);

let caller: T::AccountId = whitelisted_caller();
}: vest_other(RawOrigin::Signed(caller.clone()), other_lookup)
verify {
// Vesting schedule is removed!

#[extrinsic_call]
vest_other(RawOrigin::Signed(caller), other_lookup);

assert_eq!(
Vesting::<T>::vesting_balance(&other),
Pallet::<T>::vesting_balance(&other),
None,
"Vesting schedule was not removed",
);
}

vested_transfer {
let l in 0 .. MaxLocksOf::<T>::get();
Ok(())
}

#[benchmark]
fn vested_transfer(
l: Linear<0, { MaxLocksOf::<T>::get() }>,
s: Linear<2, { T::MAX_VESTING_SCHEDULES }>,
) -> Result<(), BenchmarkError> {
let caller: T::AccountId = whitelisted_caller();
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());

let target: T::AccountId = account("target", 0, SEED);
let target_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(target.clone());
let target_lookup: <T::Lookup as StaticLookup>::Source =
T::Lookup::unlookup(target.clone());

let transfer_amount = T::MinVestedTransfer::get();

let vesting_schedule = VestingInfo {
locked: transfer_amount,
per_block: 10u32.into(),
starting_block: 1u32.into(),
};
}: _(RawOrigin::Signed(caller), target_lookup, vesting_schedule)
verify {

#[extrinsic_call]
_(RawOrigin::Signed(caller), target_lookup, vesting_schedule);

assert_eq!(
T::MinVestedTransfer::get(),
T::Currency::free_balance(&target),
"Transfer didn't happen",
);
assert_eq!(
Vesting::<T>::vesting_balance(&target),
Pallet::<T>::vesting_balance(&target),
Some(T::MinVestedTransfer::get()),
"Lock not created",
);
}

force_vested_transfer {
let l in 0 .. MaxLocksOf::<T>::get();
Ok(())
}

#[benchmark]
fn force_vested_transfer(
l: Linear<0, { MaxLocksOf::<T>::get() }>,
s: Linear<2, { T::MAX_VESTING_SCHEDULES }>,
) -> Result<(), BenchmarkError> {
let source: T::AccountId = account("source", 0, SEED);
let source_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(source.clone());
let source_lookup: <T::Lookup as StaticLookup>::Source =
T::Lookup::unlookup(source.clone());
T::Currency::make_free_balance_be(&source, BalanceOf::<T>::max_value());

let target: T::AccountId = account("target", 0, SEED);
let target_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(target.clone());
let target_lookup: <T::Lookup as StaticLookup>::Source =
T::Lookup::unlookup(target.clone());

let transfer_amount = T::MinVestedTransfer::get();

let vesting_schedule = VestingInfo {
locked: transfer_amount,
per_block: 10u32.into(),
starting_block: 1u32.into(),
};
}: _(RawOrigin::Root, source_lookup, target_lookup, vesting_schedule)
verify {

#[extrinsic_call]
_(
RawOrigin::Root,
source_lookup,
target_lookup,
vesting_schedule,
);

assert_eq!(
T::MinVestedTransfer::get(),
T::Currency::free_balance(&target),
"Transfer didn't happen",
);
assert_eq!(
Vesting::<T>::vesting_balance(&target),
Pallet::<T>::vesting_balance(&target),
Some(T::MinVestedTransfer::get()),
"Lock not created",
);

Ok(())
}

#[benchmark]
fn not_unlocking_merge_schedules(
l: Linear<0, { MaxLocksOf::<T>::get() }>,
s: Linear<2, { T::MAX_VESTING_SCHEDULES }>,
) -> Result<(), BenchmarkError> {
let caller: T::AccountId = account("seed", 1, 1);

T::Currency::make_free_balance_be(&caller, T::Currency::minimum_balance());

add_locks::<T>(&caller, l as u8);

add_vesting_schedule::<T>(&caller)?;
add_vesting_schedule::<T>(&caller)?;

assert_eq!(
frame_system::Pallet::<T>::block_number(),
BlockNumberFor::<T>::zero()
);
assert_eq!(
Pallet::<T>::vesting_balance(&caller),
Some(200u32.into()),
"Vesting balance should equal sum locked of all schedules",
);
assert_eq!(
Vesting::<T>::get(&caller).unwrap().len(),
2,
"There should be exactly two vesting schedules"
);

#[extrinsic_call]
merge_schedules(RawOrigin::Signed(caller.clone()), 0, 1);

let schedules = Vesting::<T>::get(&caller).unwrap();
assert_eq!(schedules.len(), 1, "Schedule count should be 1 after merge");

assert_eq!(
Pallet::<T>::vesting_balance(&caller),
Some(200u32.into()),
"Vesting balance should remain the same after merge",
);

Ok(())
}

impl_benchmark_test_suite!(
Vesting,
crate::mock::ExtBuilder::default().existential_deposit(256).build(),
Pallet,
crate::mock::ExtBuilder::default()
.existential_deposit(256)
.build(),
crate::mock::Test,
);
}
5 changes: 5 additions & 0 deletions pallets/vesting/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,8 @@ impl ExtBuilder {
ext
}
}

#[cfg(feature = "runtime-benchmarks")]
pub fn new_test_ext_benchmark() -> sp_io::TestExternalities {
ExtBuilder::default().build()
}
Loading