From 512b3662803547631e16a4877e1c129d6500fc0c Mon Sep 17 00:00:00 2001 From: Shaun Wang Date: Thu, 3 Jun 2021 10:30:55 +1200 Subject: [PATCH] Update purchase pallet to FRAME v2 (#3075) * Migrate purchase wrapper pallet to pallet attribute macro. * Apply review suggestions. * Update runtime/common/src/purchase.rs * Update runtime/common/src/purchase.rs Co-authored-by: Shawn Tabrizi --- polkadot/runtime/common/src/purchase.rs | 221 ++++++++++++++---------- 1 file changed, 125 insertions(+), 96 deletions(-) diff --git a/polkadot/runtime/common/src/purchase.rs b/polkadot/runtime/common/src/purchase.rs index 0dca19a257..46de5ef784 100644 --- a/polkadot/runtime/common/src/purchase.rs +++ b/polkadot/runtime/common/src/purchase.rs @@ -14,38 +14,19 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -//! Module to process purchase of DOTs. +//! Pallet to process purchase of DOTs. use parity_scale_codec::{Encode, Decode}; use sp_runtime::{Permill, RuntimeDebug, DispatchResult, DispatchError, AnySignature}; use sp_runtime::traits::{Zero, CheckedAdd, Verify, Saturating}; -use frame_support::{decl_event, decl_storage, decl_module, decl_error, ensure}; +use frame_support::pallet_prelude::*; use frame_support::traits::{ EnsureOrigin, Currency, ExistenceRequirement, VestingSchedule, Get }; -use frame_system::ensure_signed; +use frame_system::pallet_prelude::*; use sp_core::sr25519; use sp_std::prelude::*; - -/// Configuration trait. -pub trait Config: frame_system::Config { - /// The overarching event type. - type Event: From> + Into<::Event>; - /// Balances Pallet - type Currency: Currency; - /// Vesting Pallet - type VestingSchedule: VestingSchedule; - /// The origin allowed to set account status. - type ValidityOrigin: EnsureOrigin; - /// The origin allowed to make configurations to the pallet. - type ConfigurationOrigin: EnsureOrigin; - /// The maximum statement length for the statement users to sign when creating an account. - type MaxStatementLength: Get; - /// The amount of purchased locked DOTs that we will unlock for basic actions on the chain. - type UnlockedProportion: Get; - /// The maximum amount of locked DOTs that we will unlock. - type MaxUnlocked: Get>; -} +pub use pallet::*; type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; @@ -101,31 +82,70 @@ pub struct AccountStatus { vat: Permill, } -decl_event!( - pub enum Event where - AccountId = ::AccountId, - Balance = BalanceOf, - BlockNumber = ::BlockNumber, - { +#[frame_support::pallet] +pub mod pallet { + use super::*; + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config { + /// The overarching event type. + type Event: From> + IsType<::Event>; + + /// Balances Pallet + type Currency: Currency; + + /// Vesting Pallet + type VestingSchedule: VestingSchedule; + + /// The origin allowed to set account status. + type ValidityOrigin: EnsureOrigin; + + /// The origin allowed to make configurations to the pallet. + type ConfigurationOrigin: EnsureOrigin; + + /// The maximum statement length for the statement users to sign when creating an account. + #[pallet::constant] + type MaxStatementLength: Get; + + /// The amount of purchased locked DOTs that we will unlock for basic actions on the chain. + #[pallet::constant] + type UnlockedProportion: Get; + + /// The maximum amount of locked DOTs that we will unlock. + #[pallet::constant] + type MaxUnlocked: Get>; + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + #[pallet::metadata( + T::AccountId = "AccountId", + T::BlockNumber = "BlockNumber", + BalanceOf = "Balance", + )] + pub enum Event { /// A [new] account was created. - AccountCreated(AccountId), + AccountCreated(T::AccountId), /// Someone's account validity was updated. [who, validity] - ValidityUpdated(AccountId, AccountValidity), + ValidityUpdated(T::AccountId, AccountValidity), /// Someone's purchase balance was updated. [who, free, locked] - BalanceUpdated(AccountId, Balance, Balance), + BalanceUpdated(T::AccountId, BalanceOf, BalanceOf), /// A payout was made to a purchaser. [who, free, locked] - PaymentComplete(AccountId, Balance, Balance), + PaymentComplete(T::AccountId, BalanceOf, BalanceOf), /// A new payment account was set. [who] - PaymentAccountSet(AccountId), + PaymentAccountSet(T::AccountId), /// A new statement was set. StatementUpdated, /// A new statement was set. [block_number] - UnlockBlockUpdated(BlockNumber), + UnlockBlockUpdated(T::BlockNumber), } -); -decl_error! { - pub enum Error for Module { + #[pallet::error] + pub enum Error { /// Account is not currently valid to use. InvalidAccount, /// Account used in the purchase already exists. @@ -143,45 +163,44 @@ decl_error! { /// Vesting schedule already exists for this account. VestingScheduleExists, } -} -decl_storage! { - trait Store for Module as Purchase { - // A map of all participants in the DOT purchase process. - Accounts: map hasher(blake2_128_concat) T::AccountId => AccountStatus>; - // The account that will be used to payout participants of the DOT purchase process. - PaymentAccount: T::AccountId; - // The statement purchasers will need to sign to participate. - Statement: Vec; - // The block where all locked dots will unlock. - UnlockBlock: T::BlockNumber; - } -} + // A map of all participants in the DOT purchase process. + #[pallet::storage] + pub(super) type Accounts = StorageMap< + _, + Blake2_128Concat, T::AccountId, + AccountStatus>, + ValueQuery, + >; -decl_module! { - pub struct Module for enum Call where origin: T::Origin { - type Error = Error; + // The account that will be used to payout participants of the DOT purchase process. + #[pallet::storage] + pub(super) type PaymentAccount = StorageValue<_, T::AccountId, ValueQuery>; - /// The maximum statement length for the statement users to sign when creating an account. - const MaxStatementLength: u32 = T::MaxStatementLength::get() as u32; - /// The amount of purchased locked DOTs that we will unlock for basic actions on the chain. - const UnlockedProportion: Permill = T::UnlockedProportion::get(); - /// The maximum amount of locked DOTs that we will unlock. - const MaxUnlocked: BalanceOf = T::MaxUnlocked::get(); + // The statement purchasers will need to sign to participate. + #[pallet::storage] + pub(super) type Statement = StorageValue<_, Vec, ValueQuery>; - /// Deposit one of this module's events by using the default implementation. - fn deposit_event() = default; + // The block where all locked dots will unlock. + #[pallet::storage] + pub(super) type UnlockBlock = StorageValue<_, T::BlockNumber, ValueQuery>; + #[pallet::hooks] + impl Hooks> for Pallet {} + + #[pallet::call] + impl Pallet { /// Create a new account. Proof of existence through a valid signed message. /// /// We check that the account does not exist at this stage. /// /// Origin must match the `ValidityOrigin`. - #[weight = 200_000_000 + T::DbWeight::get().reads_writes(4, 1)] - fn create_account(origin, + #[pallet::weight(200_000_000 + T::DbWeight::get().reads_writes(4, 1))] + pub(super) fn create_account( + origin: OriginFor, who: T::AccountId, signature: Vec - ) { + ) -> DispatchResult { T::ValidityOrigin::ensure_origin(origin)?; // Account is already being tracked by the pallet. ensure!(!Accounts::::contains_key(&who), Error::::ExistingAccount); @@ -200,7 +219,8 @@ decl_module! { vat: Permill::zero(), }; Accounts::::insert(&who, status); - Self::deposit_event(RawEvent::AccountCreated(who)); + Self::deposit_event(Event::::AccountCreated(who)); + Ok(()) } /// Update the validity status of an existing account. If set to completed, the account @@ -209,11 +229,12 @@ decl_module! { /// We check tht the account exists at this stage, but has not completed the process. /// /// Origin must match the `ValidityOrigin`. - #[weight = T::DbWeight::get().reads_writes(1, 1)] - fn update_validity_status(origin, + #[pallet::weight(T::DbWeight::get().reads_writes(1, 1))] + pub(super) fn update_validity_status( + origin: OriginFor, who: T::AccountId, validity: AccountValidity - ) { + ) -> DispatchResult { T::ValidityOrigin::ensure_origin(origin)?; ensure!(Accounts::::contains_key(&who), Error::::InvalidAccount); Accounts::::try_mutate(&who, |status: &mut AccountStatus>| -> DispatchResult { @@ -221,7 +242,8 @@ decl_module! { status.validity = validity; Ok(()) })?; - Self::deposit_event(RawEvent::ValidityUpdated(who, validity)); + Self::deposit_event(Event::::ValidityUpdated(who, validity)); + Ok(()) } /// Update the balance of a valid account. @@ -229,13 +251,14 @@ decl_module! { /// We check tht the account is valid for a balance transfer at this point. /// /// Origin must match the `ValidityOrigin`. - #[weight = T::DbWeight::get().reads_writes(2, 1)] - fn update_balance(origin, + #[pallet::weight(T::DbWeight::get().reads_writes(2, 1))] + pub(super) fn update_balance( + origin: OriginFor, who: T::AccountId, free_balance: BalanceOf, locked_balance: BalanceOf, vat: Permill, - ) { + ) -> DispatchResult { T::ValidityOrigin::ensure_origin(origin)?; Accounts::::try_mutate(&who, |status: &mut AccountStatus>| -> DispatchResult { @@ -248,7 +271,8 @@ decl_module! { status.vat = vat; Ok(()) })?; - Self::deposit_event(RawEvent::BalanceUpdated(who, free_balance, locked_balance)); + Self::deposit_event(Event::::BalanceUpdated(who, free_balance, locked_balance)); + Ok(()) } /// Pay the user and complete the purchase process. @@ -256,8 +280,8 @@ decl_module! { /// We reverify all assumptions about the state of an account, and complete the process. /// /// Origin must match the configured `PaymentAccount`. - #[weight = T::DbWeight::get().reads_writes(4, 2)] - fn payout(origin, who: T::AccountId) { + #[pallet::weight(T::DbWeight::get().reads_writes(4, 2))] + pub(super) fn payout(origin: OriginFor, who: T::AccountId) -> DispatchResult { // Payments must be made directly by the `PaymentAccount`. let payment_account = ensure_signed(origin)?; ensure!(payment_account == PaymentAccount::::get(), DispatchError::BadOrigin); @@ -296,9 +320,12 @@ decl_module! { // Setting the user account to `Completed` ends the purchase process for this user. status.validity = AccountValidity::Completed; - Self::deposit_event(RawEvent::PaymentComplete(who.clone(), status.free_balance, status.locked_balance)); + Self::deposit_event( + Event::::PaymentComplete(who.clone(), status.free_balance, status.locked_balance) + ); Ok(()) })?; + Ok(()) } /* Configuration Operations */ @@ -306,41 +333,44 @@ decl_module! { /// Set the account that will be used to payout users in the DOT purchase process. /// /// Origin must match the `ConfigurationOrigin` - #[weight = T::DbWeight::get().writes(1)] - fn set_payment_account(origin, who: T::AccountId) { + #[pallet::weight(T::DbWeight::get().writes(1))] + pub(super) fn set_payment_account(origin: OriginFor, who: T::AccountId) -> DispatchResult { T::ConfigurationOrigin::ensure_origin(origin)?; // Possibly this is worse than having the caller account be the payment account? PaymentAccount::::set(who.clone()); - Self::deposit_event(RawEvent::PaymentAccountSet(who)); + Self::deposit_event(Event::::PaymentAccountSet(who)); + Ok(()) } /// Set the statement that must be signed for a user to participate on the DOT sale. /// /// Origin must match the `ConfigurationOrigin` - #[weight = T::DbWeight::get().writes(1)] - fn set_statement(origin, statement: Vec) { + #[pallet::weight(T::DbWeight::get().writes(1))] + pub(super) fn set_statement(origin: OriginFor, statement: Vec) -> DispatchResult { T::ConfigurationOrigin::ensure_origin(origin)?; - ensure!(statement.len() < T::MaxStatementLength::get(), Error::::InvalidStatement); + ensure!((statement.len() as u32) < T::MaxStatementLength::get(), Error::::InvalidStatement); // Possibly this is worse than having the caller account be the payment account? - Statement::set(statement); - Self::deposit_event(RawEvent::StatementUpdated); + Statement::::set(statement); + Self::deposit_event(Event::::StatementUpdated); + Ok(()) } /// Set the block where locked DOTs will become unlocked. /// /// Origin must match the `ConfigurationOrigin` - #[weight = T::DbWeight::get().writes(1)] - fn set_unlock_block(origin, unlock_block: T::BlockNumber) { + #[pallet::weight(T::DbWeight::get().writes(1))] + pub(super) fn set_unlock_block(origin: OriginFor, unlock_block: T::BlockNumber) -> DispatchResult { T::ConfigurationOrigin::ensure_origin(origin)?; ensure!(unlock_block > frame_system::Pallet::::block_number(), Error::::InvalidUnlockBlock); // Possibly this is worse than having the caller account be the payment account? UnlockBlock::::set(unlock_block); - Self::deposit_event(RawEvent::UnlockBlockUpdated(unlock_block)); + Self::deposit_event(Event::::UnlockBlockUpdated(unlock_block)); + Ok(()) } } } -impl Module { +impl Pallet { fn verify_signature(who: &T::AccountId, signature: &[u8]) -> Result<(), DispatchError> { // sr25519 always expects a 64 byte signature. ensure!(signature.len() == 64, Error::::InvalidSignature); @@ -350,7 +380,7 @@ impl Module { let account_bytes: [u8; 32] = account_to_bytes(who)?; let public_key = sr25519::Public::from_raw(account_bytes); - let message = Statement::get(); + let message = Statement::::get(); // Check if everything is good or not. match signature.verify(message.as_slice(), &public_key) { @@ -479,7 +509,7 @@ mod tests { } parameter_types! { - pub const MaxStatementLength: usize = 1_000; + pub const MaxStatementLength: u32 = 1_000; pub const UnlockedProportion: Permill = Permill::from_percent(10); pub const MaxUnlocked: u64 = 10; } @@ -591,7 +621,7 @@ mod tests { ); // Just right... assert_ok!(Purchase::set_statement(Origin::signed(configuration_origin()), statement.clone())); - assert_eq!(Statement::get(), statement); + assert_eq!(Statement::::get(), statement); }); } @@ -1033,13 +1063,13 @@ mod tests { Accounts::::insert(alice(), account_status.clone()); Accounts::::insert(bob(), account_status); PaymentAccount::::put(alice()); - Statement::put(b"hello, world!".to_vec()); + Statement::::put(b"hello, world!".to_vec()); UnlockBlock::::put(4); // Verify storage exists. assert_eq!(Accounts::::iter().count(), 2); assert!(PaymentAccount::::exists()); - assert!(Statement::exists()); + assert!(Statement::::exists()); assert!(UnlockBlock::::exists()); // Remove storage. @@ -1048,9 +1078,8 @@ mod tests { // Verify storage is gone. assert_eq!(Accounts::::iter().count(), 0); assert!(!PaymentAccount::::exists()); - assert!(!Statement::exists()); + assert!(!Statement::::exists()); assert!(!UnlockBlock::::exists()); - }); } }