actors.rs 17 KB

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