mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 02:21:14 +00:00
Migrate pallet-recovery to pallet attribute macro (#8761)
* Migrate pallet-recovery to pallet attribute macro. * Fix metadata inconsistency. * Use DispatchResult as call returns. * Apply review suggestions.
This commit is contained in:
+143
-122
@@ -152,19 +152,16 @@
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
use sp_std::prelude::*;
|
||||
use sp_runtime::{
|
||||
traits::{Dispatchable, SaturatedConversion, CheckedAdd, CheckedMul},
|
||||
DispatchResult, ArithmeticError,
|
||||
};
|
||||
use sp_runtime::traits::{Dispatchable, SaturatedConversion, CheckedAdd, CheckedMul};
|
||||
use codec::{Encode, Decode};
|
||||
|
||||
use frame_support::{
|
||||
decl_module, decl_event, decl_storage, decl_error, ensure,
|
||||
Parameter, RuntimeDebug, weights::GetDispatchInfo,
|
||||
traits::{Currency, ReservableCurrency, Get, BalanceStatus},
|
||||
RuntimeDebug, weights::GetDispatchInfo,
|
||||
traits::{Currency, ReservableCurrency, BalanceStatus},
|
||||
dispatch::PostDispatchInfo,
|
||||
};
|
||||
use frame_system::{self as system, ensure_signed, ensure_root};
|
||||
|
||||
pub use pallet::*;
|
||||
|
||||
#[cfg(test)]
|
||||
mod mock;
|
||||
@@ -174,41 +171,6 @@ mod tests;
|
||||
type BalanceOf<T> =
|
||||
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
|
||||
|
||||
/// Configuration trait.
|
||||
pub trait Config: frame_system::Config {
|
||||
/// The overarching event type.
|
||||
type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;
|
||||
|
||||
/// The overarching call type.
|
||||
type Call: Parameter + Dispatchable<Origin=Self::Origin, PostInfo=PostDispatchInfo> + GetDispatchInfo;
|
||||
|
||||
/// The currency mechanism.
|
||||
type Currency: ReservableCurrency<Self::AccountId>;
|
||||
|
||||
/// The base amount of currency needed to reserve for creating a recovery configuration.
|
||||
///
|
||||
/// This is held for an additional storage item whose value size is
|
||||
/// `2 + sizeof(BlockNumber, Balance)` bytes.
|
||||
type ConfigDepositBase: Get<BalanceOf<Self>>;
|
||||
|
||||
/// The amount of currency needed per additional user when creating a recovery configuration.
|
||||
///
|
||||
/// This is held for adding `sizeof(AccountId)` bytes more into a pre-existing storage value.
|
||||
type FriendDepositFactor: Get<BalanceOf<Self>>;
|
||||
|
||||
/// The maximum amount of friends allowed in a recovery configuration.
|
||||
type MaxFriends: Get<u16>;
|
||||
|
||||
/// The base amount of currency needed to reserve for starting a recovery.
|
||||
///
|
||||
/// This is primarily held for deterring malicious recovery attempts, and should
|
||||
/// have a value large enough that a bad actor would choose not to place this
|
||||
/// deposit. It also acts to fund additional storage item whose value size is
|
||||
/// `sizeof(BlockNumber, Balance + T * AccountId)` bytes. Where T is a configurable
|
||||
/// threshold.
|
||||
type RecoveryDeposit: Get<BalanceOf<Self>>;
|
||||
}
|
||||
|
||||
/// An active recovery process.
|
||||
#[derive(Clone, Eq, PartialEq, Encode, Decode, Default, RuntimeDebug)]
|
||||
pub struct ActiveRecovery<BlockNumber, Balance, AccountId> {
|
||||
@@ -236,55 +198,82 @@ pub struct RecoveryConfig<BlockNumber, Balance, AccountId> {
|
||||
threshold: u16,
|
||||
}
|
||||
|
||||
decl_storage! {
|
||||
trait Store for Module<T: Config> as Recovery {
|
||||
/// The set of recoverable accounts and their recovery configuration.
|
||||
pub Recoverable get(fn recovery_config):
|
||||
map hasher(twox_64_concat) T::AccountId
|
||||
=> Option<RecoveryConfig<T::BlockNumber, BalanceOf<T>, T::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::*;
|
||||
|
||||
/// Active recovery attempts.
|
||||
///
|
||||
/// First account is the account to be recovered, and the second account
|
||||
/// is the user trying to recover the account.
|
||||
pub ActiveRecoveries get(fn active_recovery):
|
||||
double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::AccountId =>
|
||||
Option<ActiveRecovery<T::BlockNumber, BalanceOf<T>, T::AccountId>>;
|
||||
#[pallet::pallet]
|
||||
#[pallet::generate_store(pub(super) trait Store)]
|
||||
pub struct Pallet<T>(_);
|
||||
|
||||
/// The list of allowed proxy accounts.
|
||||
/// Configuration trait.
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {
|
||||
/// The overarching event type.
|
||||
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;
|
||||
|
||||
/// The currency mechanism.
|
||||
type Currency: ReservableCurrency<Self::AccountId>;
|
||||
|
||||
/// The base amount of currency needed to reserve for creating a recovery configuration.
|
||||
///
|
||||
/// Map from the user who can access it to the recovered account.
|
||||
pub Proxy get(fn proxy):
|
||||
map hasher(blake2_128_concat) T::AccountId => Option<T::AccountId>;
|
||||
/// This is held for an additional storage item whose value size is
|
||||
/// `2 + sizeof(BlockNumber, Balance)` bytes.
|
||||
#[pallet::constant]
|
||||
type ConfigDepositBase: Get<BalanceOf<Self>>;
|
||||
|
||||
/// The amount of currency needed per additional user when creating a recovery configuration.
|
||||
///
|
||||
/// This is held for adding `sizeof(AccountId)` bytes more into a pre-existing storage value.
|
||||
#[pallet::constant]
|
||||
type FriendDepositFactor: Get<BalanceOf<Self>>;
|
||||
|
||||
/// The maximum amount of friends allowed in a recovery configuration.
|
||||
#[pallet::constant]
|
||||
type MaxFriends: Get<u16>;
|
||||
|
||||
/// The base amount of currency needed to reserve for starting a recovery.
|
||||
///
|
||||
/// This is primarily held for deterring malicious recovery attempts, and should
|
||||
/// have a value large enough that a bad actor would choose not to place this
|
||||
/// deposit. It also acts to fund additional storage item whose value size is
|
||||
/// `sizeof(BlockNumber, Balance + T * AccountId)` bytes. Where T is a configurable
|
||||
/// threshold.
|
||||
#[pallet::constant]
|
||||
type RecoveryDeposit: Get<BalanceOf<Self>>;
|
||||
}
|
||||
}
|
||||
|
||||
decl_event! {
|
||||
/// Events type.
|
||||
pub enum Event<T> where
|
||||
AccountId = <T as system::Config>::AccountId,
|
||||
{
|
||||
#[pallet::event]
|
||||
#[pallet::generate_deposit(pub(super) fn deposit_event)]
|
||||
#[pallet::metadata(T::AccountId = "AccountId")]
|
||||
pub enum Event<T: Config> {
|
||||
/// A recovery process has been set up for an \[account\].
|
||||
RecoveryCreated(AccountId),
|
||||
RecoveryCreated(T::AccountId),
|
||||
/// A recovery process has been initiated for lost account by rescuer account.
|
||||
/// \[lost, rescuer\]
|
||||
RecoveryInitiated(AccountId, AccountId),
|
||||
RecoveryInitiated(T::AccountId, T::AccountId),
|
||||
/// A recovery process for lost account by rescuer account has been vouched for by sender.
|
||||
/// \[lost, rescuer, sender\]
|
||||
RecoveryVouched(AccountId, AccountId, AccountId),
|
||||
RecoveryVouched(T::AccountId, T::AccountId, T::AccountId),
|
||||
/// A recovery process for lost account by rescuer account has been closed.
|
||||
/// \[lost, rescuer\]
|
||||
RecoveryClosed(AccountId, AccountId),
|
||||
RecoveryClosed(T::AccountId, T::AccountId),
|
||||
/// Lost account has been successfully recovered by rescuer account.
|
||||
/// \[lost, rescuer\]
|
||||
AccountRecovered(AccountId, AccountId),
|
||||
AccountRecovered(T::AccountId, T::AccountId),
|
||||
/// A recovery process has been removed for an \[account\].
|
||||
RecoveryRemoved(AccountId),
|
||||
RecoveryRemoved(T::AccountId),
|
||||
}
|
||||
}
|
||||
|
||||
decl_error! {
|
||||
pub enum Error for Module<T: Config> {
|
||||
#[pallet::error]
|
||||
pub enum Error<T> {
|
||||
/// User is not allowed to make a call on behalf of this account
|
||||
NotAllowed,
|
||||
/// Threshold must be greater than zero
|
||||
@@ -318,27 +307,41 @@ decl_error! {
|
||||
/// Some internal state is broken.
|
||||
BadState,
|
||||
}
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Config> for enum Call where origin: T::Origin {
|
||||
type Error = Error<T>;
|
||||
/// The set of recoverable accounts and their recovery configuration.
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn recovery_config)]
|
||||
pub type Recoverable<T: Config> = StorageMap<
|
||||
_,
|
||||
Twox64Concat, T::AccountId,
|
||||
RecoveryConfig<T::BlockNumber, BalanceOf<T>, T::AccountId>,
|
||||
>;
|
||||
|
||||
/// The base amount of currency needed to reserve for creating a recovery configuration.
|
||||
const ConfigDepositBase: BalanceOf<T> = T::ConfigDepositBase::get();
|
||||
/// Active recovery attempts.
|
||||
///
|
||||
/// First account is the account to be recovered, and the second account
|
||||
/// is the user trying to recover the account.
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn active_recovery)]
|
||||
pub type ActiveRecoveries<T: Config>= StorageDoubleMap<
|
||||
_,
|
||||
Twox64Concat, T::AccountId,
|
||||
Twox64Concat, T::AccountId,
|
||||
ActiveRecovery<T::BlockNumber, BalanceOf<T>, T::AccountId>,
|
||||
>;
|
||||
|
||||
/// The amount of currency needed per additional user when creating a recovery configuration.
|
||||
const FriendDepositFactor: BalanceOf<T> = T::FriendDepositFactor::get();
|
||||
/// The list of allowed proxy accounts.
|
||||
///
|
||||
/// Map from the user who can access it to the recovered account.
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn proxy)]
|
||||
pub type Proxy<T: Config> = StorageMap<_, Blake2_128Concat, T::AccountId, T::AccountId>;
|
||||
|
||||
/// The maximum amount of friends allowed in a recovery configuration.
|
||||
const MaxFriends: u16 = T::MaxFriends::get();
|
||||
|
||||
/// The base amount of currency needed to reserve for starting a recovery.
|
||||
const RecoveryDeposit: BalanceOf<T> = T::RecoveryDeposit::get();
|
||||
|
||||
/// Deposit one of this module's events by using the default implementation.
|
||||
fn deposit_event() = default;
|
||||
#[pallet::hooks]
|
||||
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}
|
||||
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {
|
||||
/// Send a call through a recovered account.
|
||||
///
|
||||
/// The dispatch origin for this call must be _Signed_ and registered to
|
||||
@@ -352,7 +355,7 @@ decl_module! {
|
||||
/// - The weight of the `call` + 10,000.
|
||||
/// - One storage lookup to check account is recovered by `who`. O(1)
|
||||
/// # </weight>
|
||||
#[weight = {
|
||||
#[pallet::weight({
|
||||
let dispatch_info = call.get_dispatch_info();
|
||||
(
|
||||
dispatch_info.weight
|
||||
@@ -361,8 +364,9 @@ decl_module! {
|
||||
.saturating_add(T::DbWeight::get().reads_writes(1, 1)),
|
||||
dispatch_info.class,
|
||||
)
|
||||
}]
|
||||
fn as_recovered(origin,
|
||||
})]
|
||||
pub(crate) fn as_recovered(
|
||||
origin: OriginFor<T>,
|
||||
account: T::AccountId,
|
||||
call: Box<<T as Config>::Call>
|
||||
) -> DispatchResult {
|
||||
@@ -387,12 +391,17 @@ decl_module! {
|
||||
/// - One storage write O(1)
|
||||
/// - One event
|
||||
/// # </weight>
|
||||
#[weight = 0]
|
||||
fn set_recovered(origin, lost: T::AccountId, rescuer: T::AccountId) {
|
||||
#[pallet::weight(30_000_000)]
|
||||
pub(crate) fn set_recovered(
|
||||
origin: OriginFor<T>,
|
||||
lost: T::AccountId,
|
||||
rescuer: T::AccountId,
|
||||
) -> DispatchResult {
|
||||
ensure_root(origin)?;
|
||||
// Create the recovery storage item.
|
||||
<Proxy<T>>::insert(&rescuer, &lost);
|
||||
Self::deposit_event(RawEvent::AccountRecovered(lost, rescuer));
|
||||
Self::deposit_event(Event::<T>::AccountRecovered(lost, rescuer));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create a recovery configuration for your account. This makes your account recoverable.
|
||||
@@ -422,12 +431,13 @@ decl_module! {
|
||||
///
|
||||
/// Total Complexity: O(F + X)
|
||||
/// # </weight>
|
||||
#[weight = 100_000_000]
|
||||
fn create_recovery(origin,
|
||||
#[pallet::weight(100_000_000)]
|
||||
pub(crate) fn create_recovery(
|
||||
origin: OriginFor<T>,
|
||||
friends: Vec<T::AccountId>,
|
||||
threshold: u16,
|
||||
delay_period: T::BlockNumber
|
||||
) {
|
||||
) -> DispatchResult {
|
||||
let who = ensure_signed(origin)?;
|
||||
// Check account is not already set up for recovery
|
||||
ensure!(!<Recoverable<T>>::contains_key(&who), Error::<T>::AlreadyRecoverable);
|
||||
@@ -457,7 +467,8 @@ decl_module! {
|
||||
// Create the recovery configuration storage item
|
||||
<Recoverable<T>>::insert(&who, recovery_config);
|
||||
|
||||
Self::deposit_event(RawEvent::RecoveryCreated(who));
|
||||
Self::deposit_event(Event::<T>::RecoveryCreated(who));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Initiate the process for recovering a recoverable account.
|
||||
@@ -482,8 +493,8 @@ decl_module! {
|
||||
///
|
||||
/// Total Complexity: O(F + X)
|
||||
/// # </weight>
|
||||
#[weight = 100_000_000]
|
||||
fn initiate_recovery(origin, account: T::AccountId) {
|
||||
#[pallet::weight(100_000_000)]
|
||||
pub(crate) fn initiate_recovery(origin: OriginFor<T>, account: T::AccountId) -> DispatchResult {
|
||||
let who = ensure_signed(origin)?;
|
||||
// Check that the account is recoverable
|
||||
ensure!(<Recoverable<T>>::contains_key(&account), Error::<T>::NotRecoverable);
|
||||
@@ -494,13 +505,14 @@ decl_module! {
|
||||
T::Currency::reserve(&who, recovery_deposit)?;
|
||||
// Create an active recovery status
|
||||
let recovery_status = ActiveRecovery {
|
||||
created: <system::Pallet<T>>::block_number(),
|
||||
created: <frame_system::Pallet<T>>::block_number(),
|
||||
deposit: recovery_deposit,
|
||||
friends: vec![],
|
||||
};
|
||||
// Create the active recovery storage item
|
||||
<ActiveRecoveries<T>>::insert(&account, &who, recovery_status);
|
||||
Self::deposit_event(RawEvent::RecoveryInitiated(account, who));
|
||||
Self::deposit_event(Event::<T>::RecoveryInitiated(account, who));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Allow a "friend" of a recoverable account to vouch for an active recovery
|
||||
@@ -528,8 +540,12 @@ decl_module! {
|
||||
///
|
||||
/// Total Complexity: O(F + logF + V + logV)
|
||||
/// # </weight>
|
||||
#[weight = 100_000_000]
|
||||
fn vouch_recovery(origin, lost: T::AccountId, rescuer: T::AccountId) {
|
||||
#[pallet::weight(100_000_000)]
|
||||
pub(crate) fn vouch_recovery(
|
||||
origin: OriginFor<T>,
|
||||
lost: 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)?;
|
||||
@@ -544,7 +560,8 @@ decl_module! {
|
||||
}
|
||||
// Update storage with the latest details
|
||||
<ActiveRecoveries<T>>::insert(&lost, &rescuer, active_recovery);
|
||||
Self::deposit_event(RawEvent::RecoveryVouched(lost, rescuer, who));
|
||||
Self::deposit_event(Event::<T>::RecoveryVouched(lost, rescuer, who));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Allow a successful rescuer to claim their recovered account.
|
||||
@@ -567,8 +584,8 @@ decl_module! {
|
||||
///
|
||||
/// Total Complexity: O(F + V)
|
||||
/// # </weight>
|
||||
#[weight = 100_000_000]
|
||||
fn claim_recovery(origin, account: T::AccountId) {
|
||||
#[pallet::weight(100_000_000)]
|
||||
pub(crate) 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)?;
|
||||
@@ -576,7 +593,7 @@ decl_module! {
|
||||
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 = <system::Pallet<T>>::block_number();
|
||||
let current_block_number = <frame_system::Pallet<T>>::block_number();
|
||||
let recoverable_block_number = active_recovery.created
|
||||
.checked_add(&recovery_config.delay_period)
|
||||
.ok_or(ArithmeticError::Overflow)?;
|
||||
@@ -586,10 +603,11 @@ decl_module! {
|
||||
recovery_config.threshold as usize <= active_recovery.friends.len(),
|
||||
Error::<T>::Threshold
|
||||
);
|
||||
system::Pallet::<T>::inc_consumers(&who).map_err(|_| Error::<T>::BadState)?;
|
||||
frame_system::Pallet::<T>::inc_consumers(&who).map_err(|_| Error::<T>::BadState)?;
|
||||
// Create the recovery storage item
|
||||
Proxy::<T>::insert(&who, &account);
|
||||
Self::deposit_event(RawEvent::AccountRecovered(account, who));
|
||||
Self::deposit_event(Event::<T>::AccountRecovered(account, who));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// As the controller of a recoverable account, close an active recovery
|
||||
@@ -612,8 +630,8 @@ decl_module! {
|
||||
///
|
||||
/// Total Complexity: O(V + X)
|
||||
/// # </weight>
|
||||
#[weight = 30_000_000]
|
||||
fn close_recovery(origin, rescuer: T::AccountId) {
|
||||
#[pallet::weight(30_000_000)]
|
||||
pub(crate) 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)?;
|
||||
@@ -621,7 +639,8 @@ decl_module! {
|
||||
// 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);
|
||||
debug_assert!(res.is_ok());
|
||||
Self::deposit_event(RawEvent::RecoveryClosed(who, rescuer));
|
||||
Self::deposit_event(Event::<T>::RecoveryClosed(who, rescuer));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Remove the recovery process for your account. Recovered accounts are still accessible.
|
||||
@@ -645,8 +664,8 @@ decl_module! {
|
||||
///
|
||||
/// Total Complexity: O(F + X)
|
||||
/// # </weight>
|
||||
#[weight = 30_000_000]
|
||||
fn remove_recovery(origin) {
|
||||
#[pallet::weight(30_000_000)]
|
||||
pub(crate) fn remove_recovery(origin: OriginFor<T>) -> DispatchResult {
|
||||
let who = ensure_signed(origin)?;
|
||||
// Check there are no active recoveries
|
||||
let mut active_recoveries = <ActiveRecoveries<T>>::iter_prefix_values(&who);
|
||||
@@ -656,7 +675,8 @@ decl_module! {
|
||||
|
||||
// Unreserve the initial deposit for the recovery configuration.
|
||||
T::Currency::unreserve(&who, recovery_config.deposit);
|
||||
Self::deposit_event(RawEvent::RecoveryRemoved(who));
|
||||
Self::deposit_event(Event::<T>::RecoveryRemoved(who));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Cancel the ability to use `as_recovered` for `account`.
|
||||
@@ -670,18 +690,19 @@ decl_module! {
|
||||
/// # <weight>
|
||||
/// - One storage mutation to check account is recovered by `who`. O(1)
|
||||
/// # </weight>
|
||||
#[weight = 0]
|
||||
fn cancel_recovered(origin, account: T::AccountId) {
|
||||
#[pallet::weight(30_000_000)]
|
||||
pub(crate) fn cancel_recovered(origin: OriginFor<T>, account: T::AccountId) -> DispatchResult {
|
||||
let who = ensure_signed(origin)?;
|
||||
// Check `who` is allowed to make a call on behalf of `account`
|
||||
ensure!(Self::proxy(&who) == Some(account), Error::<T>::NotAllowed);
|
||||
Proxy::<T>::remove(&who);
|
||||
system::Pallet::<T>::dec_consumers(&who);
|
||||
frame_system::Pallet::<T>::dec_consumers(&who);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> Module<T> {
|
||||
impl<T: Config> Pallet<T> {
|
||||
/// Check that friends list is sorted and has no duplicates.
|
||||
fn is_sorted_and_unique(friends: &Vec<T::AccountId>) -> bool {
|
||||
friends.windows(2).all(|w| w[0] < w[1])
|
||||
|
||||
Reference in New Issue
Block a user