// 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::{Pallet as SafeMode, *}; use frame_benchmarking::v2::*; use frame_support::traits::{fungible::Mutate as FunMutate, UnfilteredDispatchable}; use frame_system::{Pallet as System, RawOrigin}; use sp_runtime::traits::{Bounded, One, Zero}; #[benchmarks(where T::Currency: FunMutate)] mod benchmarks { use super::*; /// `on_initialize` doing nothing. #[benchmark] fn on_initialize_noop() { #[block] { SafeMode::::on_initialize(1u32.into()); } } /// `on_initialize` exiting since the until block is in the past. #[benchmark] fn on_initialize_exit() { EnteredUntil::::put(&BlockNumberFor::::zero()); assert!(SafeMode::::is_entered()); #[block] { SafeMode::::on_initialize(1u32.into()); } assert!(!SafeMode::::is_entered()); } /// Permissionless enter - if configured. #[benchmark] fn enter() -> Result<(), BenchmarkError> { T::EnterDepositAmount::get().ok_or_else(|| BenchmarkError::Weightless)?; let caller: T::AccountId = whitelisted_caller(); let origin = RawOrigin::Signed(caller.clone()); T::Currency::set_balance(&caller, init_bal::()); #[extrinsic_call] _(origin); assert_eq!( EnteredUntil::::get().unwrap(), System::::block_number() + T::EnterDuration::get() ); Ok(()) } /// Forceful enter - if configured. #[benchmark] fn force_enter() -> Result<(), BenchmarkError> { let force_origin = T::ForceEnterOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let duration = T::ForceEnterOrigin::ensure_origin(force_origin.clone()).unwrap(); #[extrinsic_call] _(force_origin as T::RuntimeOrigin); assert_eq!(EnteredUntil::::get().unwrap(), System::::block_number() + duration); Ok(()) } /// Permissionless extend - if configured. #[benchmark] fn extend() -> Result<(), BenchmarkError> { T::ExtendDepositAmount::get().ok_or_else(|| BenchmarkError::Weightless)?; let alice: T::AccountId = whitelisted_caller(); T::Currency::set_balance(&alice, init_bal::()); System::::set_block_number(1u32.into()); assert!(SafeMode::::do_enter(None, 1u32.into()).is_ok()); #[extrinsic_call] _(RawOrigin::Signed(alice)); assert_eq!( EnteredUntil::::get().unwrap(), System::::block_number() + 1u32.into() + T::ExtendDuration::get() ); Ok(()) } /// Forceful extend - if configured. #[benchmark] fn force_extend() -> Result<(), BenchmarkError> { let force_origin = T::ForceExtendOrigin::try_successful_origin() .map_err(|_| BenchmarkError::Weightless)?; System::::set_block_number(1u32.into()); assert!(SafeMode::::do_enter(None, 1u32.into()).is_ok()); let duration = T::ForceExtendOrigin::ensure_origin(force_origin.clone()).unwrap(); let call = Call::::force_extend {}; #[block] { call.dispatch_bypass_filter(force_origin)?; } assert_eq!( EnteredUntil::::get().unwrap(), System::::block_number() + 1u32.into() + duration ); Ok(()) } /// Forceful exit - if configured. #[benchmark] fn force_exit() -> Result<(), BenchmarkError> { let force_origin = T::ForceExitOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; assert!(SafeMode::::do_enter(None, 1u32.into()).is_ok()); #[extrinsic_call] _(force_origin as T::RuntimeOrigin); assert_eq!(EnteredUntil::::get(), None); Ok(()) } /// Permissionless release of a deposit - if configured. #[benchmark] fn release_deposit() -> Result<(), BenchmarkError> { let delay = T::ReleaseDelay::get().ok_or_else(|| BenchmarkError::Weightless)?; let alice: T::AccountId = whitelisted_caller(); let origin = RawOrigin::Signed(alice.clone()); T::Currency::set_balance(&alice, init_bal::()); // Mock the storage. This is needed in case the `EnterDepositAmount` is zero. let block: BlockNumberFor = 1u32.into(); let bal: BalanceOf = 1u32.into(); Deposits::::insert(&alice, &block, &bal); T::Currency::hold(&HoldReason::EnterOrExtend.into(), &alice, bal)?; EnteredUntil::::put(&block); assert!(SafeMode::::do_exit(ExitReason::Force).is_ok()); System::::set_block_number(delay + One::one() + 2u32.into()); System::::on_initialize(System::::block_number()); SafeMode::::on_initialize(System::::block_number()); #[extrinsic_call] _(origin, alice.clone(), 1u32.into()); assert!(!Deposits::::contains_key(&alice, &block)); assert_eq!(T::Currency::balance(&alice), init_bal::()); Ok(()) } /// Forceful release of a deposit - if configured. #[benchmark] fn force_release_deposit() -> Result<(), BenchmarkError> { let force_origin = T::ForceDepositOrigin::try_successful_origin() .map_err(|_| BenchmarkError::Weightless)?; let alice: T::AccountId = whitelisted_caller(); T::Currency::set_balance(&alice, init_bal::()); // Mock the storage. This is needed in case the `EnterDepositAmount` is zero. let block: BlockNumberFor = 1u32.into(); let bal: BalanceOf = 1u32.into(); Deposits::::insert(&alice, &block, &bal); T::Currency::hold(&&HoldReason::EnterOrExtend.into(), &alice, bal)?; EnteredUntil::::put(&block); assert_eq!(T::Currency::balance(&alice), init_bal::() - 1u32.into()); assert!(SafeMode::::do_exit(ExitReason::Force).is_ok()); System::::set_block_number(System::::block_number() + One::one()); System::::on_initialize(System::::block_number()); SafeMode::::on_initialize(System::::block_number()); #[extrinsic_call] _(force_origin as T::RuntimeOrigin, alice.clone(), block); assert!(!Deposits::::contains_key(&alice, block)); assert_eq!(T::Currency::balance(&alice), init_bal::()); Ok(()) } #[benchmark] fn force_slash_deposit() -> Result<(), BenchmarkError> { let force_origin = T::ForceDepositOrigin::try_successful_origin() .map_err(|_| BenchmarkError::Weightless)?; let alice: T::AccountId = whitelisted_caller(); T::Currency::set_balance(&alice, init_bal::()); // Mock the storage. This is needed in case the `EnterDepositAmount` is zero. let block: BlockNumberFor = 1u32.into(); let bal: BalanceOf = 1u32.into(); Deposits::::insert(&alice, &block, &bal); T::Currency::hold(&&HoldReason::EnterOrExtend.into(), &alice, bal)?; EnteredUntil::::put(&block); assert!(SafeMode::::do_exit(ExitReason::Force).is_ok()); #[extrinsic_call] _(force_origin as T::RuntimeOrigin, alice.clone(), block); assert!(!Deposits::::contains_key(&alice, block)); assert_eq!(T::Currency::balance(&alice), init_bal::() - 1u32.into()); Ok(()) } fn init_bal() -> BalanceOf { BalanceOf::::max_value() / 10u32.into() } impl_benchmark_test_suite!(SafeMode, crate::mock::new_test_ext(), crate::mock::Test); }