mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 01:11:04 +00:00
Run cargo fmt on the whole code base (#9394)
* Run cargo fmt on the whole code base * Second run * Add CI check * Fix compilation * More unnecessary braces * Handle weights * Use --all * Use correct attributes... * Fix UI tests * AHHHHHHHHH * 🤦 * Docs * Fix compilation * 🤷 * Please stop * 🤦 x 2 * More * make rustfmt.toml consistent with polkadot Co-authored-by: André Silva <andrerfosilva@gmail.com>
This commit is contained in:
@@ -151,14 +151,15 @@
|
||||
// Ensure we're `no_std` when compiling for Wasm.
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
use codec::{Decode, Encode};
|
||||
use sp_runtime::traits::{CheckedAdd, CheckedMul, Dispatchable, SaturatedConversion};
|
||||
use sp_std::prelude::*;
|
||||
use sp_runtime::traits::{Dispatchable, SaturatedConversion, CheckedAdd, CheckedMul};
|
||||
use codec::{Encode, Decode};
|
||||
|
||||
use frame_support::{
|
||||
RuntimeDebug, weights::GetDispatchInfo,
|
||||
traits::{Currency, ReservableCurrency, BalanceStatus},
|
||||
dispatch::PostDispatchInfo,
|
||||
traits::{BalanceStatus, Currency, ReservableCurrency},
|
||||
weights::GetDispatchInfo,
|
||||
RuntimeDebug,
|
||||
};
|
||||
|
||||
pub use pallet::*;
|
||||
@@ -200,10 +201,10 @@ pub struct RecoveryConfig<BlockNumber, Balance, AccountId> {
|
||||
|
||||
#[frame_support::pallet]
|
||||
pub mod pallet {
|
||||
use frame_support::{ensure, Parameter, pallet_prelude::*, traits::Get};
|
||||
use frame_system::{pallet_prelude::*, ensure_signed, ensure_root};
|
||||
use sp_runtime::ArithmeticError;
|
||||
use super::*;
|
||||
use frame_support::{ensure, pallet_prelude::*, traits::Get, Parameter};
|
||||
use frame_system::{ensure_root, ensure_signed, pallet_prelude::*};
|
||||
use sp_runtime::ArithmeticError;
|
||||
|
||||
#[pallet::pallet]
|
||||
#[pallet::generate_store(pub(super) trait Store)]
|
||||
@@ -216,7 +217,9 @@ pub mod pallet {
|
||||
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
|
||||
|
||||
/// The overarching call type.
|
||||
type Call: Parameter + Dispatchable<Origin=Self::Origin, PostInfo=PostDispatchInfo> + GetDispatchInfo;
|
||||
type Call: Parameter
|
||||
+ Dispatchable<Origin = Self::Origin, PostInfo = PostDispatchInfo>
|
||||
+ GetDispatchInfo;
|
||||
|
||||
/// The currency mechanism.
|
||||
type Currency: ReservableCurrency<Self::AccountId>;
|
||||
@@ -313,7 +316,8 @@ pub mod pallet {
|
||||
#[pallet::getter(fn recovery_config)]
|
||||
pub type Recoverable<T: Config> = StorageMap<
|
||||
_,
|
||||
Twox64Concat, T::AccountId,
|
||||
Twox64Concat,
|
||||
T::AccountId,
|
||||
RecoveryConfig<T::BlockNumber, BalanceOf<T>, T::AccountId>,
|
||||
>;
|
||||
|
||||
@@ -323,10 +327,12 @@ pub mod pallet {
|
||||
/// is the user trying to recover the account.
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn active_recovery)]
|
||||
pub type ActiveRecoveries<T: Config>= StorageDoubleMap<
|
||||
pub type ActiveRecoveries<T: Config> = StorageDoubleMap<
|
||||
_,
|
||||
Twox64Concat, T::AccountId,
|
||||
Twox64Concat, T::AccountId,
|
||||
Twox64Concat,
|
||||
T::AccountId,
|
||||
Twox64Concat,
|
||||
T::AccountId,
|
||||
ActiveRecovery<T::BlockNumber, BalanceOf<T>, T::AccountId>,
|
||||
>;
|
||||
|
||||
@@ -365,14 +371,15 @@ pub mod pallet {
|
||||
pub fn as_recovered(
|
||||
origin: OriginFor<T>,
|
||||
account: T::AccountId,
|
||||
call: Box<<T as Config>::Call>
|
||||
call: Box<<T as Config>::Call>,
|
||||
) -> DispatchResult {
|
||||
let who = ensure_signed(origin)?;
|
||||
// Check `who` is allowed to make a call on behalf of `account`
|
||||
let target = Self::proxy(&who).ok_or(Error::<T>::NotAllowed)?;
|
||||
ensure!(&target == &account, Error::<T>::NotAllowed);
|
||||
call.dispatch(frame_system::RawOrigin::Signed(account).into())
|
||||
.map(|_| ()).map_err(|e| e.error)
|
||||
.map(|_| ())
|
||||
.map_err(|e| e.error)
|
||||
}
|
||||
|
||||
/// Allow ROOT to bypass the recovery process and set an a rescuer account
|
||||
@@ -433,7 +440,7 @@ pub mod pallet {
|
||||
origin: OriginFor<T>,
|
||||
friends: Vec<T::AccountId>,
|
||||
threshold: u16,
|
||||
delay_period: T::BlockNumber
|
||||
delay_period: T::BlockNumber,
|
||||
) -> DispatchResult {
|
||||
let who = ensure_signed(origin)?;
|
||||
// Check account is not already set up for recovery
|
||||
@@ -455,12 +462,8 @@ pub mod pallet {
|
||||
// Reserve the deposit
|
||||
T::Currency::reserve(&who, total_deposit)?;
|
||||
// Create the recovery configuration
|
||||
let recovery_config = RecoveryConfig {
|
||||
delay_period,
|
||||
deposit: total_deposit,
|
||||
friends,
|
||||
threshold,
|
||||
};
|
||||
let recovery_config =
|
||||
RecoveryConfig { delay_period, deposit: total_deposit, friends, threshold };
|
||||
// Create the recovery configuration storage item
|
||||
<Recoverable<T>>::insert(&who, recovery_config);
|
||||
|
||||
@@ -496,7 +499,10 @@ pub mod pallet {
|
||||
// Check that the account is recoverable
|
||||
ensure!(<Recoverable<T>>::contains_key(&account), Error::<T>::NotRecoverable);
|
||||
// Check that the recovery process has not already been started
|
||||
ensure!(!<ActiveRecoveries<T>>::contains_key(&account, &who), Error::<T>::AlreadyStarted);
|
||||
ensure!(
|
||||
!<ActiveRecoveries<T>>::contains_key(&account, &who),
|
||||
Error::<T>::AlreadyStarted
|
||||
);
|
||||
// Take recovery deposit
|
||||
let recovery_deposit = T::RecoveryDeposit::get();
|
||||
T::Currency::reserve(&who, recovery_deposit)?;
|
||||
@@ -541,13 +547,14 @@ pub mod pallet {
|
||||
pub fn vouch_recovery(
|
||||
origin: OriginFor<T>,
|
||||
lost: T::AccountId,
|
||||
rescuer: T::AccountId
|
||||
rescuer: T::AccountId,
|
||||
) -> DispatchResult {
|
||||
let who = ensure_signed(origin)?;
|
||||
// Get the recovery configuration for the lost account.
|
||||
let recovery_config = Self::recovery_config(&lost).ok_or(Error::<T>::NotRecoverable)?;
|
||||
// Get the active recovery process for the rescuer.
|
||||
let mut active_recovery = Self::active_recovery(&lost, &rescuer).ok_or(Error::<T>::NotStarted)?;
|
||||
let mut active_recovery =
|
||||
Self::active_recovery(&lost, &rescuer).ok_or(Error::<T>::NotStarted)?;
|
||||
// Make sure the voter is a friend
|
||||
ensure!(Self::is_friend(&recovery_config.friends, &who), Error::<T>::NotFriend);
|
||||
// Either insert the vouch, or return an error that the user already vouched.
|
||||
@@ -585,13 +592,16 @@ pub mod pallet {
|
||||
pub fn claim_recovery(origin: OriginFor<T>, account: T::AccountId) -> DispatchResult {
|
||||
let who = ensure_signed(origin)?;
|
||||
// Get the recovery configuration for the lost account
|
||||
let recovery_config = Self::recovery_config(&account).ok_or(Error::<T>::NotRecoverable)?;
|
||||
let recovery_config =
|
||||
Self::recovery_config(&account).ok_or(Error::<T>::NotRecoverable)?;
|
||||
// Get the active recovery process for the rescuer
|
||||
let active_recovery = Self::active_recovery(&account, &who).ok_or(Error::<T>::NotStarted)?;
|
||||
let active_recovery =
|
||||
Self::active_recovery(&account, &who).ok_or(Error::<T>::NotStarted)?;
|
||||
ensure!(!Proxy::<T>::contains_key(&who), Error::<T>::AlreadyProxy);
|
||||
// Make sure the delay period has passed
|
||||
let current_block_number = <frame_system::Pallet<T>>::block_number();
|
||||
let recoverable_block_number = active_recovery.created
|
||||
let recoverable_block_number = active_recovery
|
||||
.created
|
||||
.checked_add(&recovery_config.delay_period)
|
||||
.ok_or(ArithmeticError::Overflow)?;
|
||||
ensure!(recoverable_block_number <= current_block_number, Error::<T>::DelayPeriod);
|
||||
@@ -631,10 +641,16 @@ pub mod pallet {
|
||||
pub fn close_recovery(origin: OriginFor<T>, rescuer: T::AccountId) -> DispatchResult {
|
||||
let who = ensure_signed(origin)?;
|
||||
// Take the active recovery process started by the rescuer for this account.
|
||||
let active_recovery = <ActiveRecoveries<T>>::take(&who, &rescuer).ok_or(Error::<T>::NotStarted)?;
|
||||
let active_recovery =
|
||||
<ActiveRecoveries<T>>::take(&who, &rescuer).ok_or(Error::<T>::NotStarted)?;
|
||||
// Move the reserved funds from the rescuer to the rescued account.
|
||||
// Acts like a slashing mechanism for those who try to maliciously recover accounts.
|
||||
let res = T::Currency::repatriate_reserved(&rescuer, &who, active_recovery.deposit, BalanceStatus::Free);
|
||||
let res = T::Currency::repatriate_reserved(
|
||||
&rescuer,
|
||||
&who,
|
||||
active_recovery.deposit,
|
||||
BalanceStatus::Free,
|
||||
);
|
||||
debug_assert!(res.is_ok());
|
||||
Self::deposit_event(Event::<T>::RecoveryClosed(who, rescuer));
|
||||
Ok(())
|
||||
|
||||
@@ -19,12 +19,16 @@
|
||||
|
||||
use super::*;
|
||||
|
||||
use frame_support::{parameter_types, traits::{OnInitialize, OnFinalize}};
|
||||
use crate as recovery;
|
||||
use frame_support::{
|
||||
parameter_types,
|
||||
traits::{OnFinalize, OnInitialize},
|
||||
};
|
||||
use sp_core::H256;
|
||||
use sp_runtime::{
|
||||
traits::{BlakeTwo256, IdentityLookup}, testing::Header,
|
||||
testing::Header,
|
||||
traits::{BlakeTwo256, IdentityLookup},
|
||||
};
|
||||
use crate as recovery;
|
||||
|
||||
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
|
||||
type Block = frame_system::mocking::MockBlock<Test>;
|
||||
@@ -113,7 +117,9 @@ pub fn new_test_ext() -> sp_io::TestExternalities {
|
||||
let mut t = frame_system::GenesisConfig::default().build_storage::<Test>().unwrap();
|
||||
pallet_balances::GenesisConfig::<Test> {
|
||||
balances: vec![(1, 100), (2, 100), (3, 100), (4, 100), (5, 100)],
|
||||
}.assimilate_storage(&mut t).unwrap();
|
||||
}
|
||||
.assimilate_storage(&mut t)
|
||||
.unwrap();
|
||||
t.into()
|
||||
}
|
||||
|
||||
|
||||
@@ -18,15 +18,11 @@
|
||||
//! Tests for the module.
|
||||
|
||||
use super::*;
|
||||
use frame_support::{assert_noop, assert_ok, traits::Currency};
|
||||
use mock::{
|
||||
Recovery, Balances, Test, Origin, Call, BalancesCall, RecoveryCall,
|
||||
new_test_ext, run_to_block
|
||||
};
|
||||
use sp_runtime::traits::{BadOrigin};
|
||||
use frame_support::{
|
||||
assert_noop, assert_ok,
|
||||
traits::{Currency},
|
||||
new_test_ext, run_to_block, Balances, BalancesCall, Call, Origin, Recovery, RecoveryCall, Test,
|
||||
};
|
||||
use sp_runtime::traits::BadOrigin;
|
||||
|
||||
#[test]
|
||||
fn basic_setup_works() {
|
||||
@@ -118,7 +114,7 @@ fn malicious_recovery_fails() {
|
||||
assert_ok!(Recovery::vouch_recovery(Origin::signed(2), 5, 1)); // shame on you
|
||||
assert_ok!(Recovery::vouch_recovery(Origin::signed(3), 5, 1)); // shame on you
|
||||
assert_ok!(Recovery::vouch_recovery(Origin::signed(4), 5, 1)); // shame on you
|
||||
// We met the threshold, lets try to recover the account...?
|
||||
// We met the threshold, lets try to recover the account...?
|
||||
assert_noop!(Recovery::claim_recovery(Origin::signed(1), 5), Error::<Test>::DelayPeriod);
|
||||
// Account 1 needs to wait...
|
||||
run_to_block(19);
|
||||
@@ -136,7 +132,12 @@ fn malicious_recovery_fails() {
|
||||
assert_noop!(Recovery::claim_recovery(Origin::signed(1), 5), Error::<Test>::NotStarted);
|
||||
// Account 5 can remove their recovery config and pick some better friends
|
||||
assert_ok!(Recovery::remove_recovery(Origin::signed(5)));
|
||||
assert_ok!(Recovery::create_recovery(Origin::signed(5), vec![22, 33, 44], threshold, delay_period));
|
||||
assert_ok!(Recovery::create_recovery(
|
||||
Origin::signed(5),
|
||||
vec![22, 33, 44],
|
||||
threshold,
|
||||
delay_period
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -174,9 +175,7 @@ fn create_recovery_handles_basic_errors() {
|
||||
Error::<Test>::NotSorted
|
||||
);
|
||||
// Already configured
|
||||
assert_ok!(
|
||||
Recovery::create_recovery(Origin::signed(5), vec![2, 3, 4], 3, 10)
|
||||
);
|
||||
assert_ok!(Recovery::create_recovery(Origin::signed(5), vec![2, 3, 4], 3, 10));
|
||||
assert_noop!(
|
||||
Recovery::create_recovery(Origin::signed(5), vec![2, 3, 4], 3, 10),
|
||||
Error::<Test>::AlreadyRecoverable
|
||||
@@ -191,17 +190,18 @@ fn create_recovery_works() {
|
||||
let threshold = 3;
|
||||
let delay_period = 10;
|
||||
// Account 5 sets up a recovery configuration on their account
|
||||
assert_ok!(Recovery::create_recovery(Origin::signed(5), friends.clone(), threshold, delay_period));
|
||||
assert_ok!(Recovery::create_recovery(
|
||||
Origin::signed(5),
|
||||
friends.clone(),
|
||||
threshold,
|
||||
delay_period
|
||||
));
|
||||
// Deposit is taken, and scales with the number of friends they pick
|
||||
// Base 10 + 1 per friends = 13 total reserved
|
||||
assert_eq!(Balances::reserved_balance(5), 13);
|
||||
// Recovery configuration is correctly stored
|
||||
let recovery_config = RecoveryConfig {
|
||||
delay_period,
|
||||
deposit: 13,
|
||||
friends: friends.clone(),
|
||||
threshold,
|
||||
};
|
||||
let recovery_config =
|
||||
RecoveryConfig { delay_period, deposit: 13, friends: friends.clone(), threshold };
|
||||
assert_eq!(Recovery::recovery_config(5), Some(recovery_config));
|
||||
});
|
||||
}
|
||||
@@ -218,10 +218,18 @@ fn initiate_recovery_handles_basic_errors() {
|
||||
let friends = vec![2, 3, 4];
|
||||
let threshold = 3;
|
||||
let delay_period = 10;
|
||||
assert_ok!(Recovery::create_recovery(Origin::signed(5), friends.clone(), threshold, delay_period));
|
||||
assert_ok!(Recovery::create_recovery(
|
||||
Origin::signed(5),
|
||||
friends.clone(),
|
||||
threshold,
|
||||
delay_period
|
||||
));
|
||||
// Same user cannot recover same account twice
|
||||
assert_ok!(Recovery::initiate_recovery(Origin::signed(1), 5));
|
||||
assert_noop!(Recovery::initiate_recovery(Origin::signed(1), 5), Error::<Test>::AlreadyStarted);
|
||||
assert_noop!(
|
||||
Recovery::initiate_recovery(Origin::signed(1), 5),
|
||||
Error::<Test>::AlreadyStarted
|
||||
);
|
||||
// No double deposit
|
||||
assert_eq!(Balances::reserved_balance(1), 10);
|
||||
});
|
||||
@@ -234,17 +242,18 @@ fn initiate_recovery_works() {
|
||||
let friends = vec![2, 3, 4];
|
||||
let threshold = 3;
|
||||
let delay_period = 10;
|
||||
assert_ok!(Recovery::create_recovery(Origin::signed(5), friends.clone(), threshold, delay_period));
|
||||
assert_ok!(Recovery::create_recovery(
|
||||
Origin::signed(5),
|
||||
friends.clone(),
|
||||
threshold,
|
||||
delay_period
|
||||
));
|
||||
// Recovery can be initiated
|
||||
assert_ok!(Recovery::initiate_recovery(Origin::signed(1), 5));
|
||||
// Deposit is reserved
|
||||
assert_eq!(Balances::reserved_balance(1), 10);
|
||||
// Recovery status object is created correctly
|
||||
let recovery_status = ActiveRecovery {
|
||||
created: 0,
|
||||
deposit: 10,
|
||||
friends: vec![],
|
||||
};
|
||||
let recovery_status = ActiveRecovery { created: 0, deposit: 10, friends: vec![] };
|
||||
assert_eq!(<ActiveRecoveries<Test>>::get(&5, &1), Some(recovery_status));
|
||||
// Multiple users can attempt to recover the same account
|
||||
assert_ok!(Recovery::initiate_recovery(Origin::signed(2), 5));
|
||||
@@ -255,12 +264,20 @@ fn initiate_recovery_works() {
|
||||
fn vouch_recovery_handles_basic_errors() {
|
||||
new_test_ext().execute_with(|| {
|
||||
// Cannot vouch for non-recoverable account
|
||||
assert_noop!(Recovery::vouch_recovery(Origin::signed(2), 5, 1), Error::<Test>::NotRecoverable);
|
||||
assert_noop!(
|
||||
Recovery::vouch_recovery(Origin::signed(2), 5, 1),
|
||||
Error::<Test>::NotRecoverable
|
||||
);
|
||||
// Create a recovery process for next tests
|
||||
let friends = vec![2, 3, 4];
|
||||
let threshold = 3;
|
||||
let delay_period = 10;
|
||||
assert_ok!(Recovery::create_recovery(Origin::signed(5), friends.clone(), threshold, delay_period));
|
||||
assert_ok!(Recovery::create_recovery(
|
||||
Origin::signed(5),
|
||||
friends.clone(),
|
||||
threshold,
|
||||
delay_period
|
||||
));
|
||||
// Cannot vouch a recovery process that has not started
|
||||
assert_noop!(Recovery::vouch_recovery(Origin::signed(2), 5, 1), Error::<Test>::NotStarted);
|
||||
// Initiate a recovery process
|
||||
@@ -269,7 +286,10 @@ fn vouch_recovery_handles_basic_errors() {
|
||||
assert_noop!(Recovery::vouch_recovery(Origin::signed(22), 5, 1), Error::<Test>::NotFriend);
|
||||
// Cannot vouch twice
|
||||
assert_ok!(Recovery::vouch_recovery(Origin::signed(2), 5, 1));
|
||||
assert_noop!(Recovery::vouch_recovery(Origin::signed(2), 5, 1), Error::<Test>::AlreadyVouched);
|
||||
assert_noop!(
|
||||
Recovery::vouch_recovery(Origin::signed(2), 5, 1),
|
||||
Error::<Test>::AlreadyVouched
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -280,7 +300,12 @@ fn vouch_recovery_works() {
|
||||
let friends = vec![2, 3, 4];
|
||||
let threshold = 3;
|
||||
let delay_period = 10;
|
||||
assert_ok!(Recovery::create_recovery(Origin::signed(5), friends.clone(), threshold, delay_period));
|
||||
assert_ok!(Recovery::create_recovery(
|
||||
Origin::signed(5),
|
||||
friends.clone(),
|
||||
threshold,
|
||||
delay_period
|
||||
));
|
||||
assert_ok!(Recovery::initiate_recovery(Origin::signed(1), 5));
|
||||
// Vouching works
|
||||
assert_ok!(Recovery::vouch_recovery(Origin::signed(2), 5, 1));
|
||||
@@ -288,11 +313,7 @@ fn vouch_recovery_works() {
|
||||
assert_ok!(Recovery::vouch_recovery(Origin::signed(4), 5, 1));
|
||||
assert_ok!(Recovery::vouch_recovery(Origin::signed(3), 5, 1));
|
||||
// Final recovery status object is updated correctly
|
||||
let recovery_status = ActiveRecovery {
|
||||
created: 0,
|
||||
deposit: 10,
|
||||
friends: vec![2, 3, 4],
|
||||
};
|
||||
let recovery_status = ActiveRecovery { created: 0, deposit: 10, friends: vec![2, 3, 4] };
|
||||
assert_eq!(<ActiveRecoveries<Test>>::get(&5, &1), Some(recovery_status));
|
||||
});
|
||||
}
|
||||
@@ -306,7 +327,12 @@ fn claim_recovery_handles_basic_errors() {
|
||||
let friends = vec![2, 3, 4];
|
||||
let threshold = 3;
|
||||
let delay_period = 10;
|
||||
assert_ok!(Recovery::create_recovery(Origin::signed(5), friends.clone(), threshold, delay_period));
|
||||
assert_ok!(Recovery::create_recovery(
|
||||
Origin::signed(5),
|
||||
friends.clone(),
|
||||
threshold,
|
||||
delay_period
|
||||
));
|
||||
// Cannot claim an account which has not started the recovery process
|
||||
assert_noop!(Recovery::claim_recovery(Origin::signed(1), 5), Error::<Test>::NotStarted);
|
||||
assert_ok!(Recovery::initiate_recovery(Origin::signed(1), 5));
|
||||
@@ -328,7 +354,12 @@ fn claim_recovery_works() {
|
||||
let friends = vec![2, 3, 4];
|
||||
let threshold = 3;
|
||||
let delay_period = 10;
|
||||
assert_ok!(Recovery::create_recovery(Origin::signed(5), friends.clone(), threshold, delay_period));
|
||||
assert_ok!(Recovery::create_recovery(
|
||||
Origin::signed(5),
|
||||
friends.clone(),
|
||||
threshold,
|
||||
delay_period
|
||||
));
|
||||
assert_ok!(Recovery::initiate_recovery(Origin::signed(1), 5));
|
||||
assert_ok!(Recovery::vouch_recovery(Origin::signed(2), 5, 1));
|
||||
assert_ok!(Recovery::vouch_recovery(Origin::signed(3), 5, 1));
|
||||
@@ -372,7 +403,12 @@ fn remove_recovery_works() {
|
||||
let friends = vec![2, 3, 4];
|
||||
let threshold = 3;
|
||||
let delay_period = 10;
|
||||
assert_ok!(Recovery::create_recovery(Origin::signed(5), friends.clone(), threshold, delay_period));
|
||||
assert_ok!(Recovery::create_recovery(
|
||||
Origin::signed(5),
|
||||
friends.clone(),
|
||||
threshold,
|
||||
delay_period
|
||||
));
|
||||
assert_ok!(Recovery::initiate_recovery(Origin::signed(1), 5));
|
||||
assert_ok!(Recovery::initiate_recovery(Origin::signed(2), 5));
|
||||
// Cannot remove a recovery when there are active recoveries.
|
||||
|
||||
Reference in New Issue
Block a user