123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426 |
- #![allow(clippy::redundant_closure_call)]
-
- use codec::{Decode, Encode};
- use common::currency::{BalanceOf, GovernanceCurrency};
- use rstd::prelude::*;
- use sr_primitives::traits::{Bounded, Zero};
- use srml_support::traits::{
- Currency, LockIdentifier, LockableCurrency, WithdrawReason, WithdrawReasons,
- };
- use srml_support::{decl_event, decl_module, decl_storage, ensure};
- use system::{self, ensure_root, ensure_signed};
- #[cfg(feature = "std")]
- use serde::{Deserialize, Serialize};
- pub use crate::Role;
- const STAKING_ID: LockIdentifier = *b"role_stk";
- #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
- #[derive(Encode, Decode, Copy, Clone, Eq, PartialEq, Debug)]
- pub struct RoleParameters<Balance, BlockNumber> {
-
- pub min_stake: Balance,
-
-
- pub min_actors: u32,
-
- pub max_actors: u32,
-
- pub reward: Balance,
-
- pub reward_period: BlockNumber,
-
- pub bonding_period: BlockNumber,
-
- pub unbonding_period: BlockNumber,
-
- pub min_service_period: BlockNumber,
-
-
-
- pub startup_grace_period: BlockNumber,
-
- pub entry_request_fee: Balance,
- }
- impl<Balance: From<u32>, BlockNumber: From<u32>> Default for RoleParameters<Balance, BlockNumber> {
- fn default() -> Self {
- Self {
- min_stake: Balance::from(3000),
- max_actors: 10,
- reward: Balance::from(10),
- reward_period: BlockNumber::from(600),
- unbonding_period: BlockNumber::from(600),
- entry_request_fee: Balance::from(50),
-
- min_actors: 1,
- bonding_period: BlockNumber::from(600),
- min_service_period: BlockNumber::from(600),
- startup_grace_period: BlockNumber::from(600),
- }
- }
- }
- #[derive(Encode, Decode, Clone)]
- pub struct Actor<T: Trait> {
- pub member_id: MemberId<T>,
- pub role: Role,
- pub account: T::AccountId,
- pub joined_at: T::BlockNumber,
- }
- pub trait ActorRemoved<T: Trait> {
- fn actor_removed(actor: &T::AccountId);
- }
- pub trait Trait: system::Trait + GovernanceCurrency + membership::Trait {
- type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
- type OnActorRemoved: ActorRemoved<Self>;
- }
- pub type MemberId<T> = <T as membership::Trait>::MemberId;
- pub type Request<T> = (
- <T as system::Trait>::AccountId,
- MemberId<T>,
- Role,
- <T as system::Trait>::BlockNumber,
- );
- pub type Requests<T> = Vec<Request<T>>;
- pub const DEFAULT_REQUEST_LIFETIME: u32 = 300;
- pub const REQUEST_CLEARING_INTERVAL: u32 = 100;
- decl_storage! {
- trait Store for Module<T: Trait> as Actors {
-
- pub Parameters get(parameters) build(|config: &GenesisConfig| {
- if config.enable_storage_role {
- let storage_params: RoleParameters<BalanceOf<T>, T::BlockNumber> = Default::default();
- vec![(Role::StorageProvider, storage_params)]
- } else {
- vec![]
- }
- }): map Role => Option<RoleParameters<BalanceOf<T>, T::BlockNumber>>;
-
- pub AvailableRoles get(available_roles) build(|config: &GenesisConfig| {
- if config.enable_storage_role {
- vec![(Role::StorageProvider)]
- } else {
- vec![]
- }
- }): Vec<Role>;
-
- pub ActorAccountIds get(actor_account_ids) : Vec<T::AccountId>;
-
- pub ActorByAccountId get(actor_by_account_id) : map T::AccountId => Option<Actor<T>>;
-
- pub AccountIdsByRole get(account_ids_by_role) : map Role => Vec<T::AccountId>;
-
- pub AccountIdsByMemberId get(account_ids_by_member_id) : map MemberId<T> => Vec<T::AccountId>;
-
-
-
-
-
-
- pub RoleEntryRequests get(role_entry_requests) : Requests<T>;
-
- pub RequestLifeTime get(request_life_time) config(request_life_time) : u32 = DEFAULT_REQUEST_LIFETIME;
- }
- add_extra_genesis {
- config(enable_storage_role): bool;
- }
- }
- decl_event! {
- pub enum Event<T> where
- <T as system::Trait>::AccountId {
- EntryRequested(AccountId, Role),
- Staked(AccountId, Role),
- Unstaked(AccountId, Role),
- }
- }
- impl<T: Trait> Module<T> {
- fn is_role_available(role: Role) -> bool {
- Self::available_roles().into_iter().any(|r| role == r)
- }
- fn ensure_actor(role_key: &T::AccountId) -> Result<Actor<T>, &'static str> {
- Self::actor_by_account_id(role_key).ok_or("not role key")
- }
- fn ensure_role_parameters(
- role: Role,
- ) -> Result<RoleParameters<BalanceOf<T>, T::BlockNumber>, &'static str> {
- Self::parameters(role).ok_or("no parameters for role")
- }
-
- fn remove_actor_from_service(actor_account: T::AccountId, role: Role, member_id: MemberId<T>) {
- let accounts: Vec<T::AccountId> = Self::account_ids_by_role(role)
- .into_iter()
- .filter(|account| *account != actor_account)
- .collect();
- <AccountIdsByRole<T>>::insert(role, accounts);
- let accounts: Vec<T::AccountId> = Self::account_ids_by_member_id(&member_id)
- .into_iter()
- .filter(|account| *account != actor_account)
- .collect();
- <AccountIdsByMemberId<T>>::insert(&member_id, accounts);
- let accounts: Vec<T::AccountId> = Self::actor_account_ids()
- .into_iter()
- .filter(|account| *account != actor_account)
- .collect();
- <ActorAccountIds<T>>::put(accounts);
- <ActorByAccountId<T>>::remove(&actor_account);
- T::OnActorRemoved::actor_removed(&actor_account);
- }
- fn apply_unstake(
- actor_account: T::AccountId,
- role: Role,
- member_id: MemberId<T>,
- unbonding_period: T::BlockNumber,
- stake: BalanceOf<T>,
- ) {
-
- Self::update_lock(
- &actor_account,
- stake,
- <system::Module<T>>::block_number() + unbonding_period,
- );
- Self::remove_actor_from_service(actor_account, role, member_id);
- }
-
-
- fn update_lock(account: &T::AccountId, stake: BalanceOf<T>, until: T::BlockNumber) {
- T::Currency::set_lock(
- STAKING_ID,
- account,
- stake,
- until,
- WithdrawReasons::all() & !(WithdrawReason::TransactionPayment | WithdrawReason::Fee),
- );
- }
- pub fn is_role_account(account_id: &T::AccountId) -> bool {
- <ActorByAccountId<T>>::exists(account_id)
- }
- pub fn account_has_role(account_id: &T::AccountId, role: Role) -> bool {
- Self::actor_by_account_id(account_id).map_or(false, |actor| actor.role == role)
- }
- }
- decl_module! {
- pub struct Module<T: Trait> for enum Call where origin: T::Origin {
- fn deposit_event() = default;
- fn on_initialize(now: T::BlockNumber) {
-
- if now % T::BlockNumber::from(REQUEST_CLEARING_INTERVAL) == T::BlockNumber::zero() {
- let requests: Requests<T> = Self::role_entry_requests()
- .into_iter()
- .filter(|request| request.3 > now)
- .collect();
- <RoleEntryRequests<T>>::put(requests);
- }
- }
- fn on_finalize(now: T::BlockNumber) {
-
- for role in Self::available_roles().iter() {
- if let Some(params) = Self::parameters(role) {
- if !(now % params.reward_period == T::BlockNumber::zero()) { continue }
- let accounts = Self::account_ids_by_role(role);
- for actor in accounts.into_iter().map(Self::actor_by_account_id) {
- if let Some(actor) = actor {
- if now > actor.joined_at + params.reward_period {
-
-
-
- let balance = T::Currency::free_balance(&actor.account);
- if balance < params.min_stake {
- let _ = T::Currency::deposit_into_existing(&actor.account, params.reward);
- } else {
-
- if <membership::MembershipById<T>>::exists(actor.member_id) {
- let profile = <membership::Module<T>>::membership(&actor.member_id);
- let _ = T::Currency::deposit_into_existing(&profile.root_account, params.reward);
- }
- }
- }
- }
- }
- }
- }
- }
- pub fn role_entry_request(origin, role: Role, member_id: MemberId<T>) {
- let sender = ensure_signed(origin)?;
- ensure!(!Self::is_role_account(&sender), "account already used");
- ensure!(Self::is_role_available(role), "inactive role");
- let role_parameters = Self::ensure_role_parameters(role)?;
- <membership::Module<T>>::ensure_membership(member_id)?;
-
- let fee = role_parameters.entry_request_fee;
- ensure!(T::Currency::can_slash(&sender, fee), "cannot pay role entry request fee");
- let _ = T::Currency::slash(&sender, fee);
- <RoleEntryRequests<T>>::mutate(|requests| {
- let expires = <system::Module<T>>::block_number()+ T::BlockNumber::from(Self::request_life_time());
- requests.push((sender.clone(), member_id, role, expires));
- });
- Self::deposit_event(RawEvent::EntryRequested(sender, role));
- }
-
- pub fn stake(origin, role: Role, actor_account: T::AccountId) {
- let sender = ensure_signed(origin)?;
- ensure!(<membership::Module<T>>::is_member_account(&sender), "members only can accept storage entry request");
-
- let ids = Self::role_entry_requests()
- .iter()
- .filter(|request| request.0 == actor_account && request.2 == role)
- .map(|request| request.1)
- .filter(|member_id|
- <membership::Module<T>>::ensure_membership(*member_id)
- .ok()
- .map_or(false, |profile| profile.root_account == sender || profile.controller_account == sender)
- )
- .collect::<Vec<_>>();
- ensure!(!ids.is_empty(), "no role entry request matches");
-
- let member_id = ids[0];
- ensure!(!Self::is_role_account(&actor_account), "account already used");
-
- ensure!(Self::is_role_available(role), "inactive role");
- let role_parameters = Self::ensure_role_parameters(role)?;
- let accounts_in_role = Self::account_ids_by_role(role);
-
- ensure!(accounts_in_role.len() < role_parameters.max_actors as usize, "role slots full");
-
- ensure!(T::Currency::free_balance(&actor_account) >= role_parameters.min_stake, "not enough balance to stake");
- <AccountIdsByRole<T>>::mutate(role, |accounts| accounts.push(actor_account.clone()));
- <AccountIdsByMemberId<T>>::mutate(&member_id, |accounts| accounts.push(actor_account.clone()));
-
- Self::update_lock(&actor_account, role_parameters.min_stake, T::BlockNumber::max_value());
- <ActorByAccountId<T>>::insert(&actor_account, Actor {
- member_id,
- account: actor_account.clone(),
- role,
- joined_at: <system::Module<T>>::block_number()
- });
- <ActorAccountIds<T>>::mutate(|accounts| accounts.push(actor_account.clone()));
- let requests: Requests<T> = Self::role_entry_requests()
- .into_iter()
- .filter(|request| request.0 != actor_account)
- .collect();
- <RoleEntryRequests<T>>::put(requests);
- Self::deposit_event(RawEvent::Staked(actor_account, role));
- }
- pub fn unstake(origin, actor_account: T::AccountId) {
- let sender = ensure_signed(origin)?;
- let actor = Self::ensure_actor(&actor_account)?;
- let profile = <membership::Module<T>>::ensure_membership(actor.member_id)?;
- ensure!(profile.root_account == sender || profile.controller_account == sender, "only member can unstake storage provider");
- let role_parameters = Self::ensure_role_parameters(actor.role)?;
- Self::apply_unstake(actor.account.clone(), actor.role, actor.member_id, role_parameters.unbonding_period, role_parameters.min_stake);
- Self::deposit_event(RawEvent::Unstaked(actor.account, actor.role));
- }
- pub fn set_role_parameters(origin, role: Role, params: RoleParameters<BalanceOf<T>, T::BlockNumber>) {
- ensure_root(origin)?;
- let new_stake = params.min_stake;
- <Parameters<T>>::insert(role, params);
-
-
- let accounts = Self::account_ids_by_role(role);
- for account in accounts.into_iter() {
- Self::update_lock(&account, new_stake, T::BlockNumber::max_value());
- }
- }
- pub fn set_available_roles(origin, roles: Vec<Role>) {
- ensure_root(origin)?;
- AvailableRoles::put(roles);
- }
- pub fn add_to_available_roles(origin, role: Role) {
- ensure_root(origin)?;
- if !Self::available_roles().into_iter().any(|r| r == role) {
- AvailableRoles::mutate(|roles| roles.push(role));
- }
- }
- pub fn remove_from_available_roles(origin, role: Role) {
- ensure_root(origin)?;
-
- let roles: Vec<Role> = Self::available_roles().into_iter().filter(|r| role != *r).collect();
- AvailableRoles::put(roles);
- }
- pub fn remove_actor(origin, actor_account: T::AccountId) {
- ensure_root(origin)?;
- ensure!(<ActorByAccountId<T>>::exists(&actor_account), "error trying to remove non actor account");
- let actor = Self::actor_by_account_id(&actor_account).unwrap();
- let role_parameters = Self::ensure_role_parameters(actor.role)?;
- Self::apply_unstake(actor_account, actor.role, actor.member_id, role_parameters.unbonding_period, role_parameters.min_stake);
- }
- }
- }
|