// 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. #![cfg(feature = "runtime-benchmarks")] use super::*; use crate::Pallet; use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller}; use frame_support::traits::{Currency, Get}; use frame_system::RawOrigin; use sp_runtime::traits::Bounded; const SEED: u32 = 0; const DEFAULT_DELAY: u32 = 0; fn assert_last_event(generic_event: ::RuntimeEvent) { frame_system::Pallet::::assert_last_event(generic_event.into()); } fn get_total_deposit( bounded_friends: &FriendsOf, ) -> Option<<::Currency as Currency<::AccountId>>::Balance> { let friend_deposit = T::FriendDepositFactor::get() .checked_mul(&bounded_friends.len().saturated_into()) .unwrap(); T::ConfigDepositBase::get().checked_add(&friend_deposit) } fn generate_friends(num: u32) -> Vec<::AccountId> { // Create friends let mut friends = (0..num).map(|x| account("friend", x, SEED)).collect::>(); // Sort friends.sort(); for friend in 0..friends.len() { // Top up accounts of friends T::Currency::make_free_balance_be( &friends.get(friend).unwrap(), BalanceOf::::max_value(), ); } friends } fn add_caller_and_generate_friends( caller: T::AccountId, num: u32, ) -> Vec<::AccountId> { // Create friends let mut friends = generate_friends::(num - 1); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); friends.push(caller); // Sort friends.sort(); friends } fn insert_recovery_account(caller: &T::AccountId, account: &T::AccountId) { T::Currency::make_free_balance_be(&account, BalanceOf::::max_value()); let n = T::MaxFriends::get(); let friends = generate_friends::(n); let bounded_friends: FriendsOf = friends.try_into().unwrap(); // Get deposit for recovery let total_deposit = get_total_deposit::(&bounded_friends).unwrap(); let recovery_config = RecoveryConfig { delay_period: DEFAULT_DELAY.into(), deposit: total_deposit, friends: bounded_friends, threshold: n as u16, }; // Reserve deposit for recovery T::Currency::reserve(&caller, total_deposit).unwrap(); >::insert(&account, recovery_config); } benchmarks! { as_recovered { let caller: T::AccountId = whitelisted_caller(); let recovered_account: T::AccountId = account("recovered_account", 0, SEED); let recovered_account_lookup = T::Lookup::unlookup(recovered_account.clone()); let call: ::RuntimeCall = frame_system::Call::::remark { remark: vec![] }.into(); Proxy::::insert(&caller, &recovered_account); }: _( RawOrigin::Signed(caller), recovered_account_lookup, Box::new(call) ) set_recovered { let lost: T::AccountId = whitelisted_caller(); let lost_lookup = T::Lookup::unlookup(lost.clone()); let rescuer: T::AccountId = whitelisted_caller(); let rescuer_lookup = T::Lookup::unlookup(rescuer.clone()); }: _( RawOrigin::Root, lost_lookup, rescuer_lookup ) verify { assert_last_event::( Event::AccountRecovered { lost_account: lost, rescuer_account: rescuer, }.into() ); } create_recovery { let n in 1 .. T::MaxFriends::get(); let caller: T::AccountId = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); // Create friends let friends = generate_friends::(n); }: _( RawOrigin::Signed(caller.clone()), friends, n as u16, DEFAULT_DELAY.into() ) verify { assert_last_event::(Event::RecoveryCreated { account: caller }.into()); } initiate_recovery { let caller: T::AccountId = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); let lost_account: T::AccountId = account("lost_account", 0, SEED); let lost_account_lookup = T::Lookup::unlookup(lost_account.clone()); insert_recovery_account::(&caller, &lost_account); }: _( RawOrigin::Signed(caller.clone()), lost_account_lookup ) verify { assert_last_event::( Event::RecoveryInitiated { lost_account: lost_account, rescuer_account: caller, }.into() ); } vouch_recovery { let n in 1 .. T::MaxFriends::get(); let caller: T::AccountId = whitelisted_caller(); let lost_account: T::AccountId = account("lost_account", 0, SEED); let lost_account_lookup = T::Lookup::unlookup(lost_account.clone()); let rescuer_account: T::AccountId = account("rescuer_account", 0, SEED); let rescuer_account_lookup = T::Lookup::unlookup(rescuer_account.clone()); // Create friends let friends = add_caller_and_generate_friends::(caller.clone(), n); let bounded_friends: FriendsOf = friends.try_into().unwrap(); // Get deposit for recovery let total_deposit = get_total_deposit::(&bounded_friends).unwrap(); let recovery_config = RecoveryConfig { delay_period: DEFAULT_DELAY.into(), deposit: total_deposit, friends: bounded_friends.clone(), threshold: n as u16, }; // Create the recovery config storage item >::insert(&lost_account, recovery_config.clone()); // Reserve deposit for recovery T::Currency::reserve(&caller, total_deposit).unwrap(); // Create an active recovery status let recovery_status = ActiveRecovery { created: DEFAULT_DELAY.into(), deposit: total_deposit, friends: generate_friends::(n - 1).try_into().unwrap(), }; // Create the active recovery storage item >::insert(&lost_account, &rescuer_account, recovery_status); }: _( RawOrigin::Signed(caller.clone()), lost_account_lookup, rescuer_account_lookup ) verify { assert_last_event::( Event::RecoveryVouched { lost_account: lost_account, rescuer_account: rescuer_account, sender: caller, }.into() ); } claim_recovery { let n in 1 .. T::MaxFriends::get(); let caller: T::AccountId = whitelisted_caller(); let lost_account: T::AccountId = account("lost_account", 0, SEED); let lost_account_lookup = T::Lookup::unlookup(lost_account.clone()); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); // Create friends let friends = generate_friends::(n); let bounded_friends: FriendsOf = friends.try_into().unwrap(); // Get deposit for recovery let total_deposit = get_total_deposit::(&bounded_friends).unwrap(); let recovery_config = RecoveryConfig { delay_period: 0u32.into(), deposit: total_deposit, friends: bounded_friends.clone(), threshold: n as u16, }; // Create the recovery config storage item >::insert(&lost_account, recovery_config.clone()); // Reserve deposit for recovery T::Currency::reserve(&caller, total_deposit).unwrap(); // Create an active recovery status let recovery_status = ActiveRecovery { created: 0u32.into(), deposit: total_deposit, friends: bounded_friends.clone(), }; // Create the active recovery storage item >::insert(&lost_account, &caller, recovery_status); }: _( RawOrigin::Signed(caller.clone()), lost_account_lookup ) verify { assert_last_event::( Event::AccountRecovered { lost_account: lost_account, rescuer_account: caller, }.into() ); } close_recovery { let caller: T::AccountId = whitelisted_caller(); let rescuer_account: T::AccountId = account("rescuer_account", 0, SEED); let rescuer_account_lookup = T::Lookup::unlookup(rescuer_account.clone()); let n in 1 .. T::MaxFriends::get(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); T::Currency::make_free_balance_be(&rescuer_account, BalanceOf::::max_value()); // Create friends let friends = generate_friends::(n); let bounded_friends: FriendsOf = friends.try_into().unwrap(); // Get deposit for recovery let total_deposit = get_total_deposit::(&bounded_friends).unwrap(); let recovery_config = RecoveryConfig { delay_period: DEFAULT_DELAY.into(), deposit: total_deposit, friends: bounded_friends.clone(), threshold: n as u16, }; // Create the recovery config storage item >::insert(&caller, recovery_config.clone()); // Reserve deposit for recovery T::Currency::reserve(&caller, total_deposit).unwrap(); // Create an active recovery status let recovery_status = ActiveRecovery { created: DEFAULT_DELAY.into(), deposit: total_deposit, friends: bounded_friends.clone(), }; // Create the active recovery storage item >::insert(&caller, &rescuer_account, recovery_status); }: _( RawOrigin::Signed(caller.clone()), rescuer_account_lookup ) verify { assert_last_event::( Event::RecoveryClosed { lost_account: caller, rescuer_account: rescuer_account, }.into() ); } remove_recovery { let n in 1 .. T::MaxFriends::get(); let caller: T::AccountId = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); // Create friends let friends = generate_friends::(n); let bounded_friends: FriendsOf = friends.try_into().unwrap(); // Get deposit for recovery let total_deposit = get_total_deposit::(&bounded_friends).unwrap(); let recovery_config = RecoveryConfig { delay_period: DEFAULT_DELAY.into(), deposit: total_deposit, friends: bounded_friends.clone(), threshold: n as u16, }; // Create the recovery config storage item >::insert(&caller, recovery_config); // Reserve deposit for recovery T::Currency::reserve(&caller, total_deposit).unwrap(); }: _( RawOrigin::Signed(caller.clone()) ) verify { assert_last_event::( Event::RecoveryRemoved { lost_account: caller }.into() ); } cancel_recovered { let caller: T::AccountId = whitelisted_caller(); let account: T::AccountId = account("account", 0, SEED); let account_lookup = T::Lookup::unlookup(account.clone()); frame_system::Pallet::::inc_providers(&caller); frame_system::Pallet::::inc_consumers(&caller)?; Proxy::::insert(&caller, &account); }: _( RawOrigin::Signed(caller), account_lookup ) impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test); }