diff --git a/frame/recovery/src/lib.rs b/frame/recovery/src/lib.rs index cf81e7b033c7d..89b6b6692647d 100644 --- a/frame/recovery/src/lib.rs +++ b/frame/recovery/src/lib.rs @@ -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 = <::Currency as Currency<::AccountId>>::Balance; -/// Configuration trait. -pub trait Config: frame_system::Config { - /// The overarching event type. - type Event: From> + Into<::Event>; - - /// The overarching call type. - type Call: Parameter + Dispatchable + GetDispatchInfo; - - /// The currency mechanism. - type Currency: ReservableCurrency; - - /// 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>; - - /// 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>; - - /// The maximum amount of friends allowed in a recovery configuration. - type MaxFriends: Get; - - /// 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>; -} - /// An active recovery process. #[derive(Clone, Eq, PartialEq, Encode, Decode, Default, RuntimeDebug)] pub struct ActiveRecovery { @@ -236,55 +198,82 @@ pub struct RecoveryConfig { threshold: u16, } -decl_storage! { - trait Store for Module 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, 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::*; + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(_); + + /// Configuration trait. + #[pallet::config] + pub trait Config: frame_system::Config { + /// The overarching event type. + type Event: From> + IsType<::Event>; - /// Active recovery attempts. + /// The overarching call type. + type Call: Parameter + Dispatchable + GetDispatchInfo; + + /// The currency mechanism. + type Currency: ReservableCurrency; + + /// The base amount of currency needed to reserve for creating a recovery configuration. /// - /// 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, T::AccountId>>; + /// This is held for an additional storage item whose value size is + /// `2 + sizeof(BlockNumber, Balance)` bytes. + #[pallet::constant] + type ConfigDepositBase: Get>; - /// The list of allowed proxy accounts. + /// The amount of currency needed per additional user when 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; + /// This is held for adding `sizeof(AccountId)` bytes more into a pre-existing storage value. + #[pallet::constant] + type FriendDepositFactor: Get>; + + /// The maximum amount of friends allowed in a recovery configuration. + #[pallet::constant] + type MaxFriends: Get; + + /// 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>; } -} -decl_event! { /// Events type. - pub enum Event where - AccountId = ::AccountId, - { + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + #[pallet::metadata(T::AccountId = "AccountId")] + pub enum Event { /// 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 { + #[pallet::error] + pub enum Error { /// 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 for enum Call where origin: T::Origin { - type Error = Error; + /// The set of recoverable accounts and their recovery configuration. + #[pallet::storage] + #[pallet::getter(fn recovery_config)] + pub type Recoverable = StorageMap< + _, + Twox64Concat, T::AccountId, + RecoveryConfig, T::AccountId>, + >; - /// The base amount of currency needed to reserve for creating a recovery configuration. - const ConfigDepositBase: BalanceOf = T::ConfigDepositBase::get(); - - /// The amount of currency needed per additional user when creating a recovery configuration. - const FriendDepositFactor: BalanceOf = T::FriendDepositFactor::get(); - - /// 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::RecoveryDeposit::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= StorageDoubleMap< + _, + Twox64Concat, T::AccountId, + Twox64Concat, T::AccountId, + ActiveRecovery, T::AccountId>, + >; + + /// 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 = StorageMap<_, Blake2_128Concat, T::AccountId, T::AccountId>; - /// Deposit one of this module's events by using the default implementation. - fn deposit_event() = default; + #[pallet::hooks] + impl Hooks> for Pallet {} + #[pallet::call] + impl Pallet { /// 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 = { + #[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, account: T::AccountId, call: Box<::Call> ) -> DispatchResult { @@ -387,12 +391,17 @@ decl_module! { /// - One storage write O(1) /// - One event /// # - #[weight = 0] - fn set_recovered(origin, lost: T::AccountId, rescuer: T::AccountId) { + #[pallet::weight(30_000_000)] + pub(crate) fn set_recovered( + origin: OriginFor, + lost: T::AccountId, + rescuer: T::AccountId, + ) -> DispatchResult { ensure_root(origin)?; // Create the recovery storage item. >::insert(&rescuer, &lost); - Self::deposit_event(RawEvent::AccountRecovered(lost, rescuer)); + Self::deposit_event(Event::::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 = 100_000_000] - fn create_recovery(origin, + #[pallet::weight(100_000_000)] + pub(crate) fn create_recovery( + origin: OriginFor, friends: Vec, threshold: u16, delay_period: T::BlockNumber - ) { + ) -> DispatchResult { let who = ensure_signed(origin)?; // Check account is not already set up for recovery ensure!(!>::contains_key(&who), Error::::AlreadyRecoverable); @@ -457,7 +467,8 @@ decl_module! { // Create the recovery configuration storage item >::insert(&who, recovery_config); - Self::deposit_event(RawEvent::RecoveryCreated(who)); + Self::deposit_event(Event::::RecoveryCreated(who)); + Ok(()) } /// Initiate the process for recovering a recoverable account. @@ -482,8 +493,8 @@ decl_module! { /// /// Total Complexity: O(F + X) /// # - #[weight = 100_000_000] - fn initiate_recovery(origin, account: T::AccountId) { + #[pallet::weight(100_000_000)] + pub(crate) fn initiate_recovery(origin: OriginFor, account: T::AccountId) -> DispatchResult { let who = ensure_signed(origin)?; // Check that the account is recoverable ensure!(>::contains_key(&account), Error::::NotRecoverable); @@ -494,13 +505,14 @@ decl_module! { T::Currency::reserve(&who, recovery_deposit)?; // Create an active recovery status let recovery_status = ActiveRecovery { - created: >::block_number(), + created: >::block_number(), deposit: recovery_deposit, friends: vec![], }; // Create the active recovery storage item >::insert(&account, &who, recovery_status); - Self::deposit_event(RawEvent::RecoveryInitiated(account, who)); + Self::deposit_event(Event::::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 = 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, + 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::::NotRecoverable)?; @@ -544,7 +560,8 @@ decl_module! { } // Update storage with the latest details >::insert(&lost, &rescuer, active_recovery); - Self::deposit_event(RawEvent::RecoveryVouched(lost, rescuer, who)); + Self::deposit_event(Event::::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 = 100_000_000] - fn claim_recovery(origin, account: T::AccountId) { + #[pallet::weight(100_000_000)] + pub(crate) fn claim_recovery(origin: OriginFor, 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::::NotRecoverable)?; @@ -576,7 +593,7 @@ decl_module! { let active_recovery = Self::active_recovery(&account, &who).ok_or(Error::::NotStarted)?; ensure!(!Proxy::::contains_key(&who), Error::::AlreadyProxy); // Make sure the delay period has passed - let current_block_number = >::block_number(); + let current_block_number = >::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::::Threshold ); - system::Pallet::::inc_consumers(&who).map_err(|_| Error::::BadState)?; + frame_system::Pallet::::inc_consumers(&who).map_err(|_| Error::::BadState)?; // Create the recovery storage item Proxy::::insert(&who, &account); - Self::deposit_event(RawEvent::AccountRecovered(account, who)); + Self::deposit_event(Event::::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 = 30_000_000] - fn close_recovery(origin, rescuer: T::AccountId) { + #[pallet::weight(30_000_000)] + pub(crate) fn close_recovery(origin: OriginFor, rescuer: T::AccountId) -> DispatchResult { let who = ensure_signed(origin)?; // Take the active recovery process started by the rescuer for this account. let active_recovery = >::take(&who, &rescuer).ok_or(Error::::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::::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 = 30_000_000] - fn remove_recovery(origin) { + #[pallet::weight(30_000_000)] + pub(crate) fn remove_recovery(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; // Check there are no active recoveries let mut active_recoveries = >::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::::RecoveryRemoved(who)); + Ok(()) } /// Cancel the ability to use `as_recovered` for `account`. @@ -670,18 +690,19 @@ decl_module! { /// # /// - One storage mutation to check account is recovered by `who`. O(1) /// # - #[weight = 0] - fn cancel_recovered(origin, account: T::AccountId) { + #[pallet::weight(30_000_000)] + pub(crate) fn cancel_recovered(origin: OriginFor, 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::::NotAllowed); Proxy::::remove(&who); - system::Pallet::::dec_consumers(&who); + frame_system::Pallet::::dec_consumers(&who); + Ok(()) } } } -impl Module { +impl Pallet { /// Check that friends list is sorted and has no duplicates. fn is_sorted_and_unique(friends: &Vec) -> bool { friends.windows(2).all(|w| w[0] < w[1])