actors.rs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. // Clippy linter warning
  2. #![allow(clippy::redundant_closure_call)] // disable it because of the substrate lib design
  3. // example: pub Parameters get(parameters) build(|config: &GenesisConfig| {..}
  4. use codec::{Decode, Encode};
  5. use common::currency::{BalanceOf, GovernanceCurrency};
  6. use rstd::prelude::*;
  7. use sr_primitives::traits::{Bounded, Zero};
  8. use srml_support::traits::{
  9. Currency, LockIdentifier, LockableCurrency, WithdrawReason, WithdrawReasons,
  10. };
  11. use srml_support::{decl_event, decl_module, decl_storage, ensure};
  12. use system::{self, ensure_root, ensure_signed};
  13. #[cfg(feature = "std")]
  14. use serde::{Deserialize, Serialize};
  15. pub use crate::Role;
  16. const STAKING_ID: LockIdentifier = *b"role_stk";
  17. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
  18. #[derive(Encode, Decode, Copy, Clone, Eq, PartialEq, Debug)]
  19. pub struct RoleParameters<Balance, BlockNumber> {
  20. // minium balance required to stake to enter a role
  21. pub min_stake: Balance,
  22. // minimum actors to maintain - if role is unstaking
  23. // and remaining actors would be less that this value - prevent or punish for unstaking
  24. pub min_actors: u32,
  25. // the maximum number of spots available to fill for a role
  26. pub max_actors: u32,
  27. // fixed amount of tokens paid to actors' primary account
  28. pub reward: Balance,
  29. // payouts are made at this block interval
  30. pub reward_period: BlockNumber,
  31. // minimum amount of time before being able to unstake
  32. pub bonding_period: BlockNumber,
  33. // how long tokens remain locked for after unstaking
  34. pub unbonding_period: BlockNumber,
  35. // minimum period required to be in service. unbonding before this time is highly penalized
  36. pub min_service_period: BlockNumber,
  37. // "startup" time allowed for roles that need to sync their infrastructure
  38. // with other providers before they are considered in service and punishable for
  39. // not delivering required level of service.
  40. pub startup_grace_period: BlockNumber,
  41. // small fee burned to make a request to enter role
  42. pub entry_request_fee: Balance,
  43. }
  44. impl<Balance: From<u32>, BlockNumber: From<u32>> Default for RoleParameters<Balance, BlockNumber> {
  45. fn default() -> Self {
  46. Self {
  47. min_stake: Balance::from(3000),
  48. max_actors: 10,
  49. reward: Balance::from(10),
  50. reward_period: BlockNumber::from(600),
  51. unbonding_period: BlockNumber::from(600),
  52. entry_request_fee: Balance::from(50),
  53. // not currently used
  54. min_actors: 1,
  55. bonding_period: BlockNumber::from(600),
  56. min_service_period: BlockNumber::from(600),
  57. startup_grace_period: BlockNumber::from(600),
  58. }
  59. }
  60. }
  61. #[derive(Encode, Decode, Clone)]
  62. pub struct Actor<T: Trait> {
  63. pub member_id: MemberId<T>,
  64. pub role: Role,
  65. pub account: T::AccountId,
  66. pub joined_at: T::BlockNumber,
  67. }
  68. pub trait ActorRemoved<T: Trait> {
  69. fn actor_removed(actor: &T::AccountId);
  70. }
  71. pub trait Trait: system::Trait + GovernanceCurrency + membership::Trait {
  72. type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
  73. type OnActorRemoved: ActorRemoved<Self>;
  74. }
  75. pub type MemberId<T> = <T as membership::Trait>::MemberId;
  76. // actor account, memberid, role, expires
  77. pub type Request<T> = (
  78. <T as system::Trait>::AccountId,
  79. MemberId<T>,
  80. Role,
  81. <T as system::Trait>::BlockNumber,
  82. );
  83. pub type Requests<T> = Vec<Request<T>>;
  84. pub const DEFAULT_REQUEST_LIFETIME: u32 = 300;
  85. pub const REQUEST_CLEARING_INTERVAL: u32 = 100;
  86. decl_storage! {
  87. trait Store for Module<T: Trait> as Actors {
  88. /// requirements to enter and maintain status in roles
  89. pub Parameters get(parameters) build(|config: &GenesisConfig| {
  90. if config.enable_storage_role {
  91. let storage_params: RoleParameters<BalanceOf<T>, T::BlockNumber> = Default::default();
  92. vec![(Role::StorageProvider, storage_params)]
  93. } else {
  94. vec![]
  95. }
  96. }): map Role => Option<RoleParameters<BalanceOf<T>, T::BlockNumber>>;
  97. /// the roles members can enter into
  98. pub AvailableRoles get(available_roles) build(|config: &GenesisConfig| {
  99. if config.enable_storage_role {
  100. vec![(Role::StorageProvider)]
  101. } else {
  102. vec![]
  103. }
  104. }): Vec<Role>;
  105. /// Actors list
  106. pub ActorAccountIds get(actor_account_ids) : Vec<T::AccountId>;
  107. /// actor accounts mapped to their actor
  108. pub ActorByAccountId get(actor_by_account_id) : map T::AccountId => Option<Actor<T>>;
  109. /// actor accounts associated with a role
  110. pub AccountIdsByRole get(account_ids_by_role) : map Role => Vec<T::AccountId>;
  111. /// actor accounts associated with a member id
  112. pub AccountIdsByMemberId get(account_ids_by_member_id) : map MemberId<T> => Vec<T::AccountId>;
  113. /// First step before enter a role is registering intent with a new account/key.
  114. /// This is done by sending a role_entry_request() from the new account.
  115. /// The member must then send a stake() transaction to approve the request and enter the desired role.
  116. /// The account making the request will be bonded and must have
  117. /// sufficient balance to cover the minimum stake for the role.
  118. /// Bonding only occurs after successful entry into a role.
  119. pub RoleEntryRequests get(role_entry_requests) : Requests<T>;
  120. /// Entry request expires after this number of blocks
  121. pub RequestLifeTime get(request_life_time) config(request_life_time) : u32 = DEFAULT_REQUEST_LIFETIME;
  122. }
  123. add_extra_genesis {
  124. config(enable_storage_role): bool;
  125. }
  126. }
  127. decl_event! {
  128. pub enum Event<T> where
  129. <T as system::Trait>::AccountId {
  130. EntryRequested(AccountId, Role),
  131. Staked(AccountId, Role),
  132. Unstaked(AccountId, Role),
  133. }
  134. }
  135. impl<T: Trait> Module<T> {
  136. fn is_role_available(role: Role) -> bool {
  137. Self::available_roles().into_iter().any(|r| role == r)
  138. }
  139. fn ensure_actor(role_key: &T::AccountId) -> Result<Actor<T>, &'static str> {
  140. Self::actor_by_account_id(role_key).ok_or("not role key")
  141. }
  142. fn ensure_role_parameters(
  143. role: Role,
  144. ) -> Result<RoleParameters<BalanceOf<T>, T::BlockNumber>, &'static str> {
  145. Self::parameters(role).ok_or("no parameters for role")
  146. }
  147. // Mutating
  148. fn remove_actor_from_service(actor_account: T::AccountId, role: Role, member_id: MemberId<T>) {
  149. let accounts: Vec<T::AccountId> = Self::account_ids_by_role(role)
  150. .into_iter()
  151. .filter(|account| *account != actor_account)
  152. .collect();
  153. <AccountIdsByRole<T>>::insert(role, accounts);
  154. let accounts: Vec<T::AccountId> = Self::account_ids_by_member_id(&member_id)
  155. .into_iter()
  156. .filter(|account| *account != actor_account)
  157. .collect();
  158. <AccountIdsByMemberId<T>>::insert(&member_id, accounts);
  159. let accounts: Vec<T::AccountId> = Self::actor_account_ids()
  160. .into_iter()
  161. .filter(|account| *account != actor_account)
  162. .collect();
  163. <ActorAccountIds<T>>::put(accounts);
  164. <ActorByAccountId<T>>::remove(&actor_account);
  165. T::OnActorRemoved::actor_removed(&actor_account);
  166. }
  167. fn apply_unstake(
  168. actor_account: T::AccountId,
  169. role: Role,
  170. member_id: MemberId<T>,
  171. unbonding_period: T::BlockNumber,
  172. stake: BalanceOf<T>,
  173. ) {
  174. // simple unstaking ...only applying unbonding period
  175. Self::update_lock(
  176. &actor_account,
  177. stake,
  178. <system::Module<T>>::block_number() + unbonding_period,
  179. );
  180. Self::remove_actor_from_service(actor_account, role, member_id);
  181. }
  182. // Locks account and only allows paying for transaction fees. Account cannot
  183. // transfer or reserve funds.
  184. fn update_lock(account: &T::AccountId, stake: BalanceOf<T>, until: T::BlockNumber) {
  185. T::Currency::set_lock(
  186. STAKING_ID,
  187. account,
  188. stake,
  189. until,
  190. WithdrawReasons::all() & !(WithdrawReason::TransactionPayment | WithdrawReason::Fee),
  191. );
  192. }
  193. pub fn is_role_account(account_id: &T::AccountId) -> bool {
  194. <ActorByAccountId<T>>::exists(account_id)
  195. }
  196. pub fn account_has_role(account_id: &T::AccountId, role: Role) -> bool {
  197. Self::actor_by_account_id(account_id).map_or(false, |actor| actor.role == role)
  198. }
  199. }
  200. decl_module! {
  201. pub struct Module<T: Trait> for enum Call where origin: T::Origin {
  202. fn deposit_event() = default;
  203. fn on_initialize(now: T::BlockNumber) {
  204. // clear expired requests
  205. if now % T::BlockNumber::from(REQUEST_CLEARING_INTERVAL) == T::BlockNumber::zero() {
  206. let requests: Requests<T> = Self::role_entry_requests()
  207. .into_iter()
  208. .filter(|request| request.3 > now)
  209. .collect();
  210. <RoleEntryRequests<T>>::put(requests);
  211. }
  212. }
  213. fn on_finalize(now: T::BlockNumber) {
  214. // payout rewards to actors
  215. for role in Self::available_roles().iter() {
  216. if let Some(params) = Self::parameters(role) {
  217. if !(now % params.reward_period == T::BlockNumber::zero()) { continue }
  218. let accounts = Self::account_ids_by_role(role);
  219. for actor in accounts.into_iter().map(Self::actor_by_account_id) {
  220. if let Some(actor) = actor {
  221. if now > actor.joined_at + params.reward_period {
  222. // reward can top up balance if it is below minimum stake requirement
  223. // this guarantees overtime that actor always covers the minimum stake and
  224. // has enough balance to pay for tx fees
  225. let balance = T::Currency::free_balance(&actor.account);
  226. if balance < params.min_stake {
  227. let _ = T::Currency::deposit_into_existing(&actor.account, params.reward);
  228. } else {
  229. // otherwise it should go the the member's root account
  230. if <membership::MembershipById<T>>::exists(actor.member_id) {
  231. let profile = <membership::Module<T>>::membership(&actor.member_id);
  232. let _ = T::Currency::deposit_into_existing(&profile.root_account, params.reward);
  233. }
  234. }
  235. }
  236. }
  237. }
  238. }
  239. }
  240. }
  241. pub fn role_entry_request(origin, role: Role, member_id: MemberId<T>) {
  242. let sender = ensure_signed(origin)?;
  243. ensure!(!Self::is_role_account(&sender), "account already used");
  244. ensure!(Self::is_role_available(role), "inactive role");
  245. let role_parameters = Self::ensure_role_parameters(role)?;
  246. <membership::Module<T>>::ensure_membership(member_id)?;
  247. // pay (burn) entry fee - spam filter
  248. let fee = role_parameters.entry_request_fee;
  249. ensure!(T::Currency::can_slash(&sender, fee), "cannot pay role entry request fee");
  250. let _ = T::Currency::slash(&sender, fee);
  251. <RoleEntryRequests<T>>::mutate(|requests| {
  252. let expires = <system::Module<T>>::block_number()+ T::BlockNumber::from(Self::request_life_time());
  253. requests.push((sender.clone(), member_id, role, expires));
  254. });
  255. Self::deposit_event(RawEvent::EntryRequested(sender, role));
  256. }
  257. /// Member activating entry request
  258. pub fn stake(origin, role: Role, actor_account: T::AccountId) {
  259. let sender = ensure_signed(origin)?;
  260. ensure!(<membership::Module<T>>::is_member_account(&sender), "members only can accept storage entry request");
  261. // get member ids from requests that are controller by origin
  262. let ids = Self::role_entry_requests()
  263. .iter()
  264. .filter(|request| request.0 == actor_account && request.2 == role)
  265. .map(|request| request.1)
  266. .filter(|member_id|
  267. <membership::Module<T>>::ensure_membership(*member_id)
  268. .ok()
  269. .map_or(false, |profile| profile.root_account == sender || profile.controller_account == sender)
  270. )
  271. .collect::<Vec<_>>();
  272. ensure!(!ids.is_empty(), "no role entry request matches");
  273. // take first matching id
  274. let member_id = ids[0];
  275. ensure!(!Self::is_role_account(&actor_account), "account already used");
  276. // make sure role is still available
  277. ensure!(Self::is_role_available(role), "inactive role");
  278. let role_parameters = Self::ensure_role_parameters(role)?;
  279. let accounts_in_role = Self::account_ids_by_role(role);
  280. // ensure there is an empty slot for the role
  281. ensure!(accounts_in_role.len() < role_parameters.max_actors as usize, "role slots full");
  282. // ensure the actor account has enough balance
  283. ensure!(T::Currency::free_balance(&actor_account) >= role_parameters.min_stake, "not enough balance to stake");
  284. <AccountIdsByRole<T>>::mutate(role, |accounts| accounts.push(actor_account.clone()));
  285. <AccountIdsByMemberId<T>>::mutate(&member_id, |accounts| accounts.push(actor_account.clone()));
  286. // Lock minimum stake, but allow spending for transaction fees
  287. Self::update_lock(&actor_account, role_parameters.min_stake, T::BlockNumber::max_value());
  288. <ActorByAccountId<T>>::insert(&actor_account, Actor {
  289. member_id,
  290. account: actor_account.clone(),
  291. role,
  292. joined_at: <system::Module<T>>::block_number()
  293. });
  294. <ActorAccountIds<T>>::mutate(|accounts| accounts.push(actor_account.clone()));
  295. let requests: Requests<T> = Self::role_entry_requests()
  296. .into_iter()
  297. .filter(|request| request.0 != actor_account)
  298. .collect();
  299. <RoleEntryRequests<T>>::put(requests);
  300. Self::deposit_event(RawEvent::Staked(actor_account, role));
  301. }
  302. pub fn unstake(origin, actor_account: T::AccountId) {
  303. let sender = ensure_signed(origin)?;
  304. let actor = Self::ensure_actor(&actor_account)?;
  305. let profile = <membership::Module<T>>::ensure_membership(actor.member_id)?;
  306. ensure!(profile.root_account == sender || profile.controller_account == sender, "only member can unstake storage provider");
  307. let role_parameters = Self::ensure_role_parameters(actor.role)?;
  308. Self::apply_unstake(actor.account.clone(), actor.role, actor.member_id, role_parameters.unbonding_period, role_parameters.min_stake);
  309. Self::deposit_event(RawEvent::Unstaked(actor.account, actor.role));
  310. }
  311. pub fn set_role_parameters(origin, role: Role, params: RoleParameters<BalanceOf<T>, T::BlockNumber>) {
  312. ensure_root(origin)?;
  313. let new_stake = params.min_stake;
  314. <Parameters<T>>::insert(role, params);
  315. // Update locks for all actors in the role. The lock for each account is already until max_value
  316. // It doesn't affect actors which are unbonding, they should have already been removed from AccountIdsByRole
  317. let accounts = Self::account_ids_by_role(role);
  318. for account in accounts.into_iter() {
  319. Self::update_lock(&account, new_stake, T::BlockNumber::max_value());
  320. }
  321. }
  322. pub fn set_available_roles(origin, roles: Vec<Role>) {
  323. ensure_root(origin)?;
  324. AvailableRoles::put(roles);
  325. }
  326. pub fn add_to_available_roles(origin, role: Role) {
  327. ensure_root(origin)?;
  328. if !Self::available_roles().into_iter().any(|r| r == role) {
  329. AvailableRoles::mutate(|roles| roles.push(role));
  330. }
  331. }
  332. pub fn remove_from_available_roles(origin, role: Role) {
  333. ensure_root(origin)?;
  334. // Should we eject actors in the role being removed?
  335. let roles: Vec<Role> = Self::available_roles().into_iter().filter(|r| role != *r).collect();
  336. AvailableRoles::put(roles);
  337. }
  338. pub fn remove_actor(origin, actor_account: T::AccountId) {
  339. ensure_root(origin)?;
  340. ensure!(<ActorByAccountId<T>>::exists(&actor_account), "error trying to remove non actor account");
  341. let actor = Self::actor_by_account_id(&actor_account).unwrap();
  342. let role_parameters = Self::ensure_role_parameters(actor.role)?;
  343. Self::apply_unstake(actor_account, actor.role, actor.member_id, role_parameters.unbonding_period, role_parameters.min_stake);
  344. }
  345. }
  346. }