// This file is part of Substrate. // Copyright (C) 2020-2022 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. //! Assets pallet benchmarking. #![cfg(feature = "runtime-benchmarks")] use super::*; use frame_benchmarking::{ account, benchmarks_instance_pallet, whitelist_account, whitelisted_caller, }; use frame_support::{ dispatch::UnfilteredDispatchable, traits::{EnsureOrigin, Get}, BoundedVec, }; use frame_system::RawOrigin as SystemOrigin; use sp_runtime::traits::Bounded; use sp_std::{convert::TryInto, prelude::*}; use crate::Pallet as Uniques; const SEED: u32 = 0; fn create_class, I: 'static>( ) -> (T::ClassId, T::AccountId, ::Source) { let caller: T::AccountId = whitelisted_caller(); let caller_lookup = T::Lookup::unlookup(caller.clone()); let class = Default::default(); T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); assert!(Uniques::::create( SystemOrigin::Signed(caller.clone()).into(), class, caller_lookup.clone(), ) .is_ok()); (class, caller, caller_lookup) } fn add_class_metadata, I: 'static>( ) -> (T::AccountId, ::Source) { let caller = Class::::get(T::ClassId::default()).unwrap().owner; if caller != whitelisted_caller() { whitelist_account!(caller); } let caller_lookup = T::Lookup::unlookup(caller.clone()); assert!(Uniques::::set_class_metadata( SystemOrigin::Signed(caller.clone()).into(), Default::default(), vec![0; T::StringLimit::get() as usize].try_into().unwrap(), false, ) .is_ok()); (caller, caller_lookup) } fn mint_instance, I: 'static>( index: u16, ) -> (T::InstanceId, T::AccountId, ::Source) { let caller = Class::::get(T::ClassId::default()).unwrap().admin; if caller != whitelisted_caller() { whitelist_account!(caller); } let caller_lookup = T::Lookup::unlookup(caller.clone()); let instance = index.into(); assert!(Uniques::::mint( SystemOrigin::Signed(caller.clone()).into(), Default::default(), instance, caller_lookup.clone(), ) .is_ok()); (instance, caller, caller_lookup) } fn add_instance_metadata, I: 'static>( instance: T::InstanceId, ) -> (T::AccountId, ::Source) { let caller = Class::::get(T::ClassId::default()).unwrap().owner; if caller != whitelisted_caller() { whitelist_account!(caller); } let caller_lookup = T::Lookup::unlookup(caller.clone()); assert!(Uniques::::set_metadata( SystemOrigin::Signed(caller.clone()).into(), Default::default(), instance, vec![0; T::StringLimit::get() as usize].try_into().unwrap(), false, ) .is_ok()); (caller, caller_lookup) } fn add_instance_attribute, I: 'static>( instance: T::InstanceId, ) -> (BoundedVec, T::AccountId, ::Source) { let caller = Class::::get(T::ClassId::default()).unwrap().owner; if caller != whitelisted_caller() { whitelist_account!(caller); } let caller_lookup = T::Lookup::unlookup(caller.clone()); let key: BoundedVec<_, _> = vec![0; T::KeyLimit::get() as usize].try_into().unwrap(); assert!(Uniques::::set_attribute( SystemOrigin::Signed(caller.clone()).into(), Default::default(), Some(instance), key.clone(), vec![0; T::ValueLimit::get() as usize].try_into().unwrap(), ) .is_ok()); (key, caller, caller_lookup) } fn assert_last_event, I: 'static>(generic_event: >::Event) { let events = frame_system::Pallet::::events(); let system_event: ::Event = generic_event.into(); // compare to the last event record let frame_system::EventRecord { event, .. } = &events[events.len() - 1]; assert_eq!(event, &system_event); } benchmarks_instance_pallet! { create { let caller: T::AccountId = whitelisted_caller(); let caller_lookup = T::Lookup::unlookup(caller.clone()); T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); }: _(SystemOrigin::Signed(caller.clone()), Default::default(), caller_lookup) verify { assert_last_event::(Event::Created { class: Default::default(), creator: caller.clone(), owner: caller }.into()); } force_create { let caller: T::AccountId = whitelisted_caller(); let caller_lookup = T::Lookup::unlookup(caller.clone()); }: _(SystemOrigin::Root, Default::default(), caller_lookup, true) verify { assert_last_event::(Event::ForceCreated { class: Default::default(), owner: caller }.into()); } destroy { let n in 0 .. 1_000; let m in 0 .. 1_000; let a in 0 .. 1_000; let (class, caller, caller_lookup) = create_class::(); add_class_metadata::(); for i in 0..n { mint_instance::(i as u16); } for i in 0..m { add_instance_metadata::((i as u16).into()); } for i in 0..a { add_instance_attribute::((i as u16).into()); } let witness = Class::::get(class).unwrap().destroy_witness(); }: _(SystemOrigin::Signed(caller), class, witness) verify { assert_last_event::(Event::Destroyed { class: class }.into()); } mint { let (class, caller, caller_lookup) = create_class::(); let instance = Default::default(); }: _(SystemOrigin::Signed(caller.clone()), class, instance, caller_lookup) verify { assert_last_event::(Event::Issued { class, instance, owner: caller }.into()); } burn { let (class, caller, caller_lookup) = create_class::(); let (instance, ..) = mint_instance::(0); }: _(SystemOrigin::Signed(caller.clone()), class, instance, Some(caller_lookup)) verify { assert_last_event::(Event::Burned { class, instance, owner: caller }.into()); } transfer { let (class, caller, caller_lookup) = create_class::(); let (instance, ..) = mint_instance::(Default::default()); let target: T::AccountId = account("target", 0, SEED); let target_lookup = T::Lookup::unlookup(target.clone()); }: _(SystemOrigin::Signed(caller.clone()), class, instance, target_lookup) verify { assert_last_event::(Event::Transferred { class, instance, from: caller, to: target }.into()); } redeposit { let i in 0 .. 5_000; let (class, caller, caller_lookup) = create_class::(); let instances = (0..i).map(|x| mint_instance::(x as u16).0).collect::>(); Uniques::::force_asset_status( SystemOrigin::Root.into(), class, caller_lookup.clone(), caller_lookup.clone(), caller_lookup.clone(), caller_lookup.clone(), true, false, )?; }: _(SystemOrigin::Signed(caller.clone()), class, instances.clone()) verify { assert_last_event::(Event::Redeposited { class, successful_instances: instances }.into()); } freeze { let (class, caller, caller_lookup) = create_class::(); let (instance, ..) = mint_instance::(Default::default()); }: _(SystemOrigin::Signed(caller.clone()), Default::default(), Default::default()) verify { assert_last_event::(Event::Frozen { class: Default::default(), instance: Default::default() }.into()); } thaw { let (class, caller, caller_lookup) = create_class::(); let (instance, ..) = mint_instance::(Default::default()); Uniques::::freeze( SystemOrigin::Signed(caller.clone()).into(), class, instance, )?; }: _(SystemOrigin::Signed(caller.clone()), class, instance) verify { assert_last_event::(Event::Thawed { class, instance }.into()); } freeze_class { let (class, caller, caller_lookup) = create_class::(); }: _(SystemOrigin::Signed(caller.clone()), class) verify { assert_last_event::(Event::ClassFrozen { class }.into()); } thaw_class { let (class, caller, caller_lookup) = create_class::(); let origin = SystemOrigin::Signed(caller.clone()).into(); Uniques::::freeze_class(origin, class)?; }: _(SystemOrigin::Signed(caller.clone()), class) verify { assert_last_event::(Event::ClassThawed { class }.into()); } transfer_ownership { let (class, caller, _) = create_class::(); let target: T::AccountId = account("target", 0, SEED); let target_lookup = T::Lookup::unlookup(target.clone()); T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance()); }: _(SystemOrigin::Signed(caller), class, target_lookup) verify { assert_last_event::(Event::OwnerChanged { class, new_owner: target }.into()); } set_team { let (class, caller, _) = create_class::(); let target0 = T::Lookup::unlookup(account("target", 0, SEED)); let target1 = T::Lookup::unlookup(account("target", 1, SEED)); let target2 = T::Lookup::unlookup(account("target", 2, SEED)); }: _(SystemOrigin::Signed(caller), Default::default(), target0.clone(), target1.clone(), target2.clone()) verify { assert_last_event::(Event::TeamChanged{ class, issuer: account("target", 0, SEED), admin: account("target", 1, SEED), freezer: account("target", 2, SEED), }.into()); } force_asset_status { let (class, caller, caller_lookup) = create_class::(); let origin = T::ForceOrigin::successful_origin(); let call = Call::::force_asset_status { class, owner: caller_lookup.clone(), issuer: caller_lookup.clone(), admin: caller_lookup.clone(), freezer: caller_lookup.clone(), free_holding: true, is_frozen: false, }; }: { call.dispatch_bypass_filter(origin)? } verify { assert_last_event::(Event::AssetStatusChanged { class }.into()); } set_attribute { let key: BoundedVec<_, _> = vec![0u8; T::KeyLimit::get() as usize].try_into().unwrap(); let value: BoundedVec<_, _> = vec![0u8; T::ValueLimit::get() as usize].try_into().unwrap(); let (class, caller, _) = create_class::(); let (instance, ..) = mint_instance::(0); add_instance_metadata::(instance); }: _(SystemOrigin::Signed(caller), class, Some(instance), key.clone(), value.clone()) verify { assert_last_event::(Event::AttributeSet { class, maybe_instance: Some(instance), key, value }.into()); } clear_attribute { let (class, caller, _) = create_class::(); let (instance, ..) = mint_instance::(0); add_instance_metadata::(instance); let (key, ..) = add_instance_attribute::(instance); }: _(SystemOrigin::Signed(caller), class, Some(instance), key.clone()) verify { assert_last_event::(Event::AttributeCleared { class, maybe_instance: Some(instance), key }.into()); } set_metadata { let data: BoundedVec<_, _> = vec![0u8; T::StringLimit::get() as usize].try_into().unwrap(); let (class, caller, _) = create_class::(); let (instance, ..) = mint_instance::(0); }: _(SystemOrigin::Signed(caller), class, instance, data.clone(), false) verify { assert_last_event::(Event::MetadataSet { class, instance, data, is_frozen: false }.into()); } clear_metadata { let (class, caller, _) = create_class::(); let (instance, ..) = mint_instance::(0); add_instance_metadata::(instance); }: _(SystemOrigin::Signed(caller), class, instance) verify { assert_last_event::(Event::MetadataCleared { class, instance }.into()); } set_class_metadata { let data: BoundedVec<_, _> = vec![0u8; T::StringLimit::get() as usize].try_into().unwrap(); let (class, caller, _) = create_class::(); }: _(SystemOrigin::Signed(caller), class, data.clone(), false) verify { assert_last_event::(Event::ClassMetadataSet { class, data, is_frozen: false }.into()); } clear_class_metadata { let (class, caller, _) = create_class::(); add_class_metadata::(); }: _(SystemOrigin::Signed(caller), class) verify { assert_last_event::(Event::ClassMetadataCleared { class }.into()); } approve_transfer { let (class, caller, _) = create_class::(); let (instance, ..) = mint_instance::(0); let delegate: T::AccountId = account("delegate", 0, SEED); let delegate_lookup = T::Lookup::unlookup(delegate.clone()); }: _(SystemOrigin::Signed(caller.clone()), class, instance, delegate_lookup) verify { assert_last_event::(Event::ApprovedTransfer { class, instance, owner: caller, delegate }.into()); } cancel_approval { let (class, caller, _) = create_class::(); let (instance, ..) = mint_instance::(0); let delegate: T::AccountId = account("delegate", 0, SEED); let delegate_lookup = T::Lookup::unlookup(delegate.clone()); let origin = SystemOrigin::Signed(caller.clone()).into(); Uniques::::approve_transfer(origin, class, instance, delegate_lookup.clone())?; }: _(SystemOrigin::Signed(caller.clone()), class, instance, Some(delegate_lookup)) verify { assert_last_event::(Event::ApprovalCancelled { class, instance, owner: caller, delegate }.into()); } impl_benchmark_test_suite!(Uniques, crate::mock::new_test_ext(), crate::mock::Test); }