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:
Shaun Wang
2021-05-13 00:50:22 +12:00
committed by GitHub
parent 31c5ddc1ce
commit 4f7c1df31e
+143 -122
View File
@@ -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])