lib.rs 64 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683
  1. //! # Working group module
  2. //! Working group module for the Joystream platform. Version 1.
  3. //! Contains abstract working group workflow.
  4. //!
  5. //! ## Overview
  6. //!
  7. //! The working group module provides working group workflow to use in different modules.
  8. //! It contains extrinsics for the hiring workers, their roles lifecycle and stake management.
  9. //! There is a possibility to hire a special worker - the leader of the working group.
  10. //! Some module operations like 'increase_stake' can be invoked by the worker, others
  11. //! like 'terminate_role' can be invoked by the leader only. The leader himself can be hired and
  12. //! managed only by the council via proposals.
  13. //!
  14. //! Exact working group (eg.: forum working group) should create an instance of the Working group module.
  15. //!
  16. //! ## Supported extrinsics
  17. //! ### Hiring flow
  18. //!
  19. //! - [add_opening](./struct.Module.html#method.add_opening) - Add an opening for a worker/lead role.
  20. //! - [accept_applications](./struct.Module.html#method.accept_applications)- Begin accepting worker/lead applications.
  21. //! - [begin_applicant_review](./struct.Module.html#method.begin_applicant_review) - Begin reviewing worker/lead applications.
  22. //! - [fill_opening](./struct.Module.html#method.fill_opening) - Fill opening for worker/lead.
  23. //! - [withdraw_application](./struct.Module.html#method.withdraw_application) - Withdraw the worker/lead application.
  24. //! - [terminate_application](./struct.Module.html#method.terminate_application) - Terminate the worker/lead application.
  25. //! - [apply_on_opening](./struct.Module.html#method.apply_on_opening) - Apply on a worker/lead opening.
  26. //!
  27. //! ### Roles lifecycle
  28. //!
  29. //! - [update_role_account](./struct.Module.html#method.update_role_account) - Update the role account of the worker/lead.
  30. //! - [update_reward_account](./struct.Module.html#method.update_reward_account) - Update the reward account of the worker/lead.
  31. //! - [update_reward_amount](./struct.Module.html#method.update_reward_amount) - Update the reward amount of the worker/lead.
  32. //! - [leave_role](./struct.Module.html#method.leave_role) - Leave the role by the active worker/lead.
  33. //! - [terminate_role](./struct.Module.html#method.terminate_role) - Terminate the worker/lead role.
  34. //! - [set_mint_capacity](./struct.Module.html#method.set_mint_capacity) - Sets the capacity to enable working group budget.
  35. //!
  36. //! ### Stakes
  37. //!
  38. //! - [slash_stake](./struct.Module.html#method.slash_stake) - Slashes the worker/lead stake.
  39. //! - [decrease_stake](./struct.Module.html#method.decrease_stake) - Decreases the worker/lead stake and returns the remainder to the worker _role_account_.
  40. //! - [increase_stake](./struct.Module.html#method.increase_stake) - Increases the worker/lead stake.
  41. //!
  42. // Ensure we're `no_std` when compiling for Wasm.
  43. #![cfg_attr(not(feature = "std"), no_std)]
  44. // Do not delete! Cannot be uncommented by default, because of Parity decl_module! issue.
  45. //#![warn(missing_docs)]
  46. // Internal Substrate warning (decl_event).
  47. #![allow(clippy::unused_unit)]
  48. #[cfg(test)]
  49. mod tests;
  50. mod types;
  51. #[macro_use]
  52. mod errors;
  53. use frame_support::dispatch::{DispatchError, DispatchResult};
  54. use frame_support::storage::IterableStorageMap;
  55. use frame_support::traits::{Currency, ExistenceRequirement, Get, Imbalance, WithdrawReasons};
  56. use frame_support::{decl_event, decl_module, decl_storage, ensure, print, StorageValue};
  57. use frame_system::{ensure_root, ensure_signed};
  58. use sp_arithmetic::traits::{Bounded, One, Zero};
  59. use sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet};
  60. use sp_std::vec;
  61. use sp_std::vec::Vec;
  62. use crate::types::ExitInitiationOrigin;
  63. use common::constraints::InputValidationLengthConstraint;
  64. use errors::WrappedError;
  65. pub use errors::Error;
  66. pub use types::{
  67. Application, Opening, OpeningPolicyCommitment, OpeningType, RewardPolicy, RoleStakeProfile,
  68. Worker,
  69. };
  70. /// Stake identifier in staking module
  71. pub type StakeId<T> = <T as stake::Trait>::StakeId;
  72. /// Member identifier in membership::member module
  73. pub type MemberId<T> = <T as common::MembershipTypes>::MemberId;
  74. /// Workaround for BTreeSet type
  75. pub type ApplicationIdSet<T> = BTreeSet<ApplicationId<T>>;
  76. /// Type for the identifier for an opening for a worker/lead.
  77. pub type OpeningId<T> = <T as hiring::Trait>::OpeningId;
  78. /// Type for the identifier for an application as a worker/lead.
  79. pub type ApplicationId<T> = <T as hiring::Trait>::ApplicationId;
  80. /// Balance type of runtime
  81. pub type BalanceOf<T> =
  82. <<T as stake::Trait>::Currency as Currency<<T as frame_system::Trait>::AccountId>>::Balance;
  83. /// Balance type of runtime reward
  84. pub type BalanceOfMint<T> =
  85. <<T as minting::Trait>::Currency as Currency<<T as frame_system::Trait>::AccountId>>::Balance;
  86. /// Balance type of runtime
  87. pub type CurrencyOf<T> = <T as stake::Trait>::Currency;
  88. /// Negative imbalance of runtime.
  89. pub type NegativeImbalance<T> = <<T as stake::Trait>::Currency as Currency<
  90. <T as frame_system::Trait>::AccountId,
  91. >>::NegativeImbalance;
  92. /// Alias for the worker application id to the worker id dictionary
  93. pub type ApplicationIdToWorkerIdMap<T> = BTreeMap<ApplicationId<T>, WorkerId<T>>;
  94. /// Type identifier for worker role, which must be same as membership actor identifier
  95. pub type WorkerId<T> = <T as common::MembershipTypes>::ActorId;
  96. /// Alias for the application id from the hiring module.
  97. pub type HiringApplicationId<T> = <T as hiring::Trait>::ApplicationId;
  98. // Type simplification
  99. type OpeningInfo<T> = (
  100. OpeningOf<T>,
  101. hiring::Opening<BalanceOf<T>, <T as frame_system::Trait>::BlockNumber, HiringApplicationId<T>>,
  102. );
  103. // Type simplification
  104. type ApplicationInfo<T> = (ApplicationOf<T>, ApplicationId<T>, OpeningOf<T>);
  105. // Type simplification
  106. type RewardSettings<T> = (
  107. <T as minting::Trait>::MintId,
  108. RewardPolicy<BalanceOfMint<T>, <T as frame_system::Trait>::BlockNumber>,
  109. );
  110. // Type simplification
  111. type WorkerOf<T> = Worker<
  112. <T as frame_system::Trait>::AccountId,
  113. <T as recurringrewards::Trait>::RewardRelationshipId,
  114. <T as stake::Trait>::StakeId,
  115. <T as frame_system::Trait>::BlockNumber,
  116. MemberId<T>,
  117. >;
  118. // Type simplification
  119. type OpeningOf<T> = Opening<
  120. <T as hiring::Trait>::OpeningId,
  121. <T as frame_system::Trait>::BlockNumber,
  122. BalanceOf<T>,
  123. ApplicationId<T>,
  124. >;
  125. // Type simplification
  126. type ApplicationOf<T> = Application<
  127. <T as frame_system::Trait>::AccountId,
  128. OpeningId<T>,
  129. MemberId<T>,
  130. HiringApplicationId<T>,
  131. >;
  132. /// The _Working group_ main _Trait_
  133. pub trait Trait<I: Instance>:
  134. frame_system::Trait
  135. + membership::Trait
  136. + hiring::Trait
  137. + minting::Trait
  138. + stake::Trait
  139. + recurringrewards::Trait
  140. {
  141. /// _Working group_ event type.
  142. type Event: From<Event<Self, I>> + Into<<Self as frame_system::Trait>::Event>;
  143. /// Defines max workers number in the working group.
  144. type MaxWorkerNumberLimit: Get<u32>;
  145. }
  146. decl_event!(
  147. /// _Working group_ events
  148. pub enum Event<T, I>
  149. where
  150. WorkerId = WorkerId<T>,
  151. <T as frame_system::Trait>::AccountId,
  152. OpeningId = OpeningId<T>,
  153. ApplicationId = ApplicationId<T>,
  154. ApplicationIdToWorkerIdMap = ApplicationIdToWorkerIdMap<T>,
  155. RationaleText = Vec<u8>,
  156. MintBalanceOf = minting::BalanceOf<T>,
  157. <T as minting::Trait>::MintId,
  158. {
  159. /// Emits on setting the leader.
  160. /// Params:
  161. /// - Worker id.
  162. LeaderSet(WorkerId),
  163. /// Emits on un-setting the leader.
  164. /// Params:
  165. LeaderUnset(),
  166. /// Emits on terminating the worker.
  167. /// Params:
  168. /// - worker id.
  169. /// - termination rationale text
  170. TerminatedWorker(WorkerId, RationaleText),
  171. /// Emits on terminating the leader.
  172. /// Params:
  173. /// - leader worker id.
  174. /// - termination rationale text
  175. TerminatedLeader(WorkerId, RationaleText),
  176. /// Emits on exiting the worker.
  177. /// Params:
  178. /// - worker id.
  179. /// - exit rationale text
  180. WorkerExited(WorkerId, RationaleText),
  181. /// Emits on updating the role account of the worker.
  182. /// Params:
  183. /// - Id of the worker.
  184. /// - Role account id of the worker.
  185. WorkerRoleAccountUpdated(WorkerId, AccountId),
  186. /// Emits on updating the worker storage role.
  187. /// Params:
  188. /// - Id of the worker.
  189. /// - Raw storage field.
  190. WorkerStorageUpdated(WorkerId, Vec<u8>),
  191. /// Emits on updating the reward account of the worker.
  192. /// Params:
  193. /// - Member id of the worker.
  194. /// - Reward account id of the worker.
  195. WorkerRewardAccountUpdated(WorkerId, AccountId),
  196. /// Emits on updating the reward amount of the worker.
  197. /// Params:
  198. /// - Id of the worker.
  199. WorkerRewardAmountUpdated(WorkerId),
  200. /// Emits on adding new worker opening.
  201. /// Params:
  202. /// - Opening id
  203. OpeningAdded(OpeningId),
  204. /// Emits on accepting application for the worker opening.
  205. /// Params:
  206. /// - Opening id
  207. AcceptedApplications(OpeningId),
  208. /// Emits on adding the application for the worker opening.
  209. /// Params:
  210. /// - Opening id
  211. /// - Application id
  212. AppliedOnOpening(OpeningId, ApplicationId),
  213. /// Emits on withdrawing the application for the worker/lead opening.
  214. /// Params:
  215. /// - Worker application id
  216. ApplicationWithdrawn(ApplicationId),
  217. /// Emits on terminating the application for the worker/lead opening.
  218. /// Params:
  219. /// - Worker application id
  220. ApplicationTerminated(ApplicationId),
  221. /// Emits on beginning the application review for the worker/lead opening.
  222. /// Params:
  223. /// - Opening id
  224. BeganApplicationReview(OpeningId),
  225. /// Emits on filling the worker opening.
  226. /// Params:
  227. /// - Worker opening id
  228. /// - Worker application id to the worker id dictionary
  229. OpeningFilled(OpeningId, ApplicationIdToWorkerIdMap),
  230. /// Emits on increasing the worker/lead stake.
  231. /// Params:
  232. /// - worker/lead id.
  233. StakeIncreased(WorkerId),
  234. /// Emits on decreasing the worker/lead stake.
  235. /// Params:
  236. /// - worker/lead id.
  237. StakeDecreased(WorkerId),
  238. /// Emits on slashing the worker/lead stake.
  239. /// Params:
  240. /// - worker/lead id.
  241. StakeSlashed(WorkerId),
  242. /// Emits on changing working group mint capacity.
  243. /// Params:
  244. /// - mint id.
  245. /// - new mint balance.
  246. MintCapacityChanged(MintId, MintBalanceOf),
  247. }
  248. );
  249. decl_storage! {
  250. trait Store for Module<T: Trait<I>, I: Instance> as WorkingGroup {
  251. /// The mint currently funding the rewards for this module.
  252. pub Mint get(fn mint) : <T as minting::Trait>::MintId;
  253. /// The current lead.
  254. pub CurrentLead get(fn current_lead) : Option<WorkerId<T>>;
  255. /// Next identifier value for new worker opening.
  256. pub NextOpeningId get(fn next_opening_id): OpeningId<T>;
  257. /// Maps identifier to worker opening.
  258. pub OpeningById get(fn opening_by_id): map hasher(blake2_128_concat)
  259. OpeningId<T> => OpeningOf<T>;
  260. /// Opening human readable text length limits
  261. pub OpeningHumanReadableText get(fn opening_human_readable_text): InputValidationLengthConstraint;
  262. /// Maps identifier to worker application on opening.
  263. pub ApplicationById get(fn application_by_id) : map hasher(blake2_128_concat)
  264. ApplicationId<T> => ApplicationOf<T>;
  265. /// Next identifier value for new worker application.
  266. pub NextApplicationId get(fn next_application_id) : ApplicationId<T>;
  267. /// Worker application human readable text length limits
  268. pub WorkerApplicationHumanReadableText get(fn application_human_readable_text) : InputValidationLengthConstraint;
  269. /// Maps identifier to corresponding worker.
  270. pub WorkerById get(fn worker_by_id) : map hasher(blake2_128_concat)
  271. WorkerId<T> => WorkerOf<T>;
  272. /// Maps identifier to corresponding worker storage.
  273. pub WorkerStorage get(fn worker_storage): map hasher(blake2_128_concat)
  274. WorkerId<T> => Vec<u8>;
  275. /// Count of active workers.
  276. pub ActiveWorkerCount get(fn active_worker_count): u32;
  277. /// Next identifier for new worker.
  278. pub NextWorkerId get(fn next_worker_id) : WorkerId<T>;
  279. /// Worker exit rationale text length limits.
  280. pub WorkerExitRationaleText get(fn worker_exit_rationale_text) : InputValidationLengthConstraint;
  281. /// Worker storage size upper bound.
  282. pub WorkerStorageSize get(fn worker_storage_size) : u16 = default_storage_size_constraint();
  283. /// Map member id by hiring application id.
  284. /// Required by StakingEventsHandler callback call to refund the balance on unstaking.
  285. pub MemberIdByHiringApplicationId get(fn member_id_by_hiring_application_id):
  286. map hasher(blake2_128_concat) HiringApplicationId<T> => MemberId<T>;
  287. }
  288. add_extra_genesis {
  289. config(phantom): sp_std::marker::PhantomData<I>;
  290. config(working_group_mint_capacity): minting::BalanceOf<T>;
  291. config(opening_human_readable_text_constraint): InputValidationLengthConstraint;
  292. config(worker_application_human_readable_text_constraint): InputValidationLengthConstraint;
  293. config(worker_exit_rationale_text_constraint): InputValidationLengthConstraint;
  294. config(worker_storage_size_constraint): u16;
  295. build(|config: &GenesisConfig<T, I>| {
  296. Module::<T, I>::initialize_working_group(
  297. config.opening_human_readable_text_constraint,
  298. config.worker_application_human_readable_text_constraint,
  299. config.worker_exit_rationale_text_constraint,
  300. config.worker_storage_size_constraint,
  301. config.working_group_mint_capacity)
  302. });
  303. }
  304. }
  305. decl_module! {
  306. /// _Working group_ substrate module.
  307. pub struct Module<T: Trait<I>, I: Instance> for enum Call where origin: T::Origin {
  308. /// Default deposit_event() handler
  309. fn deposit_event() = default;
  310. /// Predefined errors
  311. type Error = Error<T, I>;
  312. /// Exports const - max simultaneous active worker number.
  313. const MaxWorkerNumberLimit: u32 = T::MaxWorkerNumberLimit::get();
  314. // ****************** Roles lifecycle **********************
  315. /// Update the associated role account of the active worker/lead.
  316. #[weight = 10_000_000] // TODO: adjust weight
  317. pub fn update_role_account(
  318. origin,
  319. worker_id: WorkerId<T>,
  320. new_role_account_id: T::AccountId
  321. ) {
  322. // Ensuring worker actually exists
  323. let worker = Self::ensure_worker_exists(&worker_id)?;
  324. // Ensure that origin is signed by member with given id.
  325. ensure_on_wrapped_error!(
  326. membership::Module::<T>::ensure_member_controller_account_signed(origin, &worker.member_id)
  327. )?;
  328. //
  329. // == MUTATION SAFE ==
  330. //
  331. // Update role account
  332. WorkerById::<T, I>::mutate(worker_id, |worker| {
  333. worker.role_account_id = new_role_account_id.clone()
  334. });
  335. // Trigger event
  336. Self::deposit_event(RawEvent::WorkerRoleAccountUpdated(worker_id, new_role_account_id));
  337. }
  338. /// Update the associated role storage.
  339. #[weight = 10_000_000] // TODO: adjust weight
  340. pub fn update_role_storage(
  341. origin,
  342. worker_id: WorkerId<T>,
  343. storage: Vec<u8>
  344. ) {
  345. // Ensure there is a signer which matches role account of worker corresponding to provided id.
  346. Self::ensure_worker_signed(origin, &worker_id)?;
  347. Self::ensure_worker_role_storage_text_is_valid(&storage)?;
  348. //
  349. // == MUTATION SAFE ==
  350. //
  351. // Complete the role storage update
  352. WorkerStorage::<T, I>::insert(worker_id, storage.clone());
  353. // Trigger event
  354. Self::deposit_event(RawEvent::WorkerStorageUpdated(worker_id, storage));
  355. }
  356. /// Update the reward account associated with a set reward relationship for the active worker.
  357. #[weight = 10_000_000] // TODO: adjust weight
  358. pub fn update_reward_account(
  359. origin,
  360. worker_id: WorkerId<T>,
  361. new_reward_account_id: T::AccountId
  362. ) {
  363. // Ensure there is a signer which matches role account of worker corresponding to provided id.
  364. let worker = Self::ensure_worker_signed(origin, &worker_id)?;
  365. // Ensure the worker actually has a recurring reward
  366. let relationship_id = Self::ensure_worker_has_recurring_reward(&worker)?;
  367. //
  368. // == MUTATION SAFE ==
  369. //
  370. // Update only the reward account.
  371. ensure_on_wrapped_error!(
  372. recurringrewards::Module::<T>::set_reward_relationship(
  373. relationship_id,
  374. Some(new_reward_account_id.clone()), // new_account
  375. None, // new_payout
  376. None, //new_next_payment_at
  377. None) //new_payout_interval
  378. )?;
  379. // Trigger event
  380. Self::deposit_event(RawEvent::WorkerRewardAccountUpdated(worker_id, new_reward_account_id));
  381. }
  382. /// Update the reward amount associated with a set reward relationship for the active worker.
  383. /// Require signed leader origin or the root (to update leader reward amount).
  384. #[weight = 10_000_000] // TODO: adjust weight
  385. pub fn update_reward_amount(
  386. origin,
  387. worker_id: WorkerId<T>,
  388. new_amount: BalanceOfMint<T>
  389. ) {
  390. // Ensure lead is set and is origin signer or it is the council.
  391. Self::ensure_origin_for_leader(origin, worker_id)?;
  392. // Ensuring worker actually exists
  393. let worker = Self::ensure_worker_exists(&worker_id)?;
  394. // Ensure the worker actually has a recurring reward
  395. let relationship_id = Self::ensure_worker_has_recurring_reward(&worker)?;
  396. //
  397. // == MUTATION SAFE ==
  398. //
  399. // Update only the reward account.
  400. ensure_on_wrapped_error!(
  401. recurringrewards::Module::<T>::set_reward_relationship(
  402. relationship_id,
  403. None, // new_account
  404. Some(new_amount), // new_payout
  405. None, //new_next_payment_at
  406. None) //new_payout_interval
  407. )?;
  408. // Trigger event
  409. Self::deposit_event(RawEvent::WorkerRewardAmountUpdated(worker_id));
  410. }
  411. /// Leave the role by the active worker.
  412. #[weight = 10_000_000] // TODO: adjust weight
  413. pub fn leave_role(
  414. origin,
  415. worker_id: WorkerId<T>,
  416. rationale_text: Vec<u8>
  417. ) {
  418. // Ensure there is a signer which matches role account of worker corresponding to provided id.
  419. let active_worker = Self::ensure_worker_signed(origin, &worker_id)?;
  420. //
  421. // == MUTATION SAFE ==
  422. //
  423. Self::deactivate_worker(
  424. &worker_id,
  425. &active_worker,
  426. &ExitInitiationOrigin::Worker,
  427. &rationale_text
  428. )?;
  429. }
  430. /// Terminate the active worker by the lead.
  431. /// Require signed leader origin or the root (to terminate the leader role).
  432. #[weight = 10_000_000] // TODO: adjust weight
  433. pub fn terminate_role(
  434. origin,
  435. worker_id: WorkerId<T>,
  436. rationale_text: Vec<u8>,
  437. slash_stake: bool,
  438. ) {
  439. let (cloned_origin1, cloned_origin2) = common::origin::double_origin::<T>(origin);
  440. // Ensure lead is set or it is the council terminating the leader.
  441. let exit_origin = Self::ensure_origin_for_leader(cloned_origin1, worker_id)?;
  442. // Ensuring worker actually exists.
  443. let worker = Self::ensure_worker_exists(&worker_id)?;
  444. // Ensure rationale text is valid.
  445. Self::ensure_worker_exit_rationale_text_is_valid(&rationale_text)?;
  446. //
  447. // == MUTATION SAFE ==
  448. //
  449. if slash_stake {
  450. Self::slash_stake(cloned_origin2, worker_id, BalanceOf::<T>::max_value())?;
  451. }
  452. Self::deactivate_worker(
  453. &worker_id,
  454. &worker,
  455. &exit_origin,
  456. &rationale_text
  457. )?;
  458. }
  459. // ****************** Hiring flow **********************
  460. /// Add an opening for a worker role.
  461. /// Require signed leader origin or the root (to add opening for the leader position).
  462. #[weight = 10_000_000] // TODO: adjust weight
  463. pub fn add_opening(
  464. origin,
  465. activate_at: hiring::ActivateOpeningAt<T::BlockNumber>,
  466. commitment: OpeningPolicyCommitment<T::BlockNumber, BalanceOf<T>>,
  467. human_readable_text: Vec<u8>,
  468. opening_type: OpeningType,
  469. ){
  470. Self::ensure_origin_for_opening_type(origin, opening_type)?;
  471. Self::ensure_opening_human_readable_text_is_valid(&human_readable_text)?;
  472. Self::ensure_opening_policy_commitment_is_valid(&commitment)?;
  473. // Add opening
  474. // NB: This call can in principle fail, because the staking policies
  475. // may not respect the minimum currency requirement.
  476. let policy_commitment = commitment.clone();
  477. //
  478. // == MUTATION SAFE ==
  479. //
  480. let opening_id = ensure_on_wrapped_error!(
  481. hiring::Module::<T>::add_opening(
  482. activate_at,
  483. commitment.max_review_period_length,
  484. commitment.application_rationing_policy,
  485. commitment.application_staking_policy,
  486. commitment.role_staking_policy,
  487. human_readable_text,
  488. ))?;
  489. let new_opening_id = NextOpeningId::<T, I>::get();
  490. // Create and add worker opening.
  491. let new_opening_by_id = Opening::<OpeningId<T>, T::BlockNumber, BalanceOf<T>, ApplicationId<T>> {
  492. hiring_opening_id: opening_id,
  493. applications: BTreeSet::new(),
  494. policy_commitment,
  495. opening_type,
  496. };
  497. OpeningById::<T, I>::insert(new_opening_id, new_opening_by_id);
  498. // Update NextOpeningId
  499. NextOpeningId::<T, I>::mutate(|id| *id += <OpeningId<T> as One>::one());
  500. // Trigger event
  501. Self::deposit_event(RawEvent::OpeningAdded(new_opening_id));
  502. }
  503. /// Begin accepting worker applications to an opening that is active.
  504. /// Require signed leader origin or the root (to accept applications for the leader position).
  505. #[weight = 10_000_000] // TODO: adjust weight
  506. pub fn accept_applications(origin, opening_id: OpeningId<T>) {
  507. // Ensure opening exists in this working group
  508. // NB: Even though call to hiring module will have implicit check for
  509. // existence of opening as well, this check is to make sure that the opening is for
  510. // this working group, not something else.
  511. let (opening, _opening) = Self::ensure_opening_exists(&opening_id)?;
  512. Self::ensure_origin_for_opening_type(origin, opening.opening_type)?;
  513. // Attempt to begin accepting applications
  514. // NB: Combined ensure check and mutation in hiring module
  515. //
  516. // == MUTATION SAFE ==
  517. //
  518. ensure_on_wrapped_error!(
  519. hiring::Module::<T>::begin_accepting_applications(opening.hiring_opening_id)
  520. )?;
  521. // Trigger event
  522. Self::deposit_event(RawEvent::AcceptedApplications(opening_id));
  523. }
  524. /// Apply on a worker opening.
  525. #[weight = 10_000_000] // TODO: adjust weight
  526. pub fn apply_on_opening(
  527. origin,
  528. member_id: T::MemberId,
  529. opening_id: OpeningId<T>,
  530. role_account_id: T::AccountId,
  531. opt_role_stake_balance: Option<BalanceOf<T>>,
  532. opt_application_stake_balance: Option<BalanceOf<T>>,
  533. human_readable_text: Vec<u8>
  534. ) {
  535. // Ensure origin which will server as the source account for staked funds is signed
  536. let source_account = ensure_signed(origin)?;
  537. // In absence of a more general key delegation frame_system which allows an account with some funds to
  538. // grant another account permission to stake from its funds, the origin of this call must have the funds
  539. // and cannot specify another arbitrary account as the source account.
  540. // Ensure the source_account is either the controller or root account of member with given id
  541. ensure!(
  542. membership::Module::<T>::ensure_member_controller_account(&source_account, &member_id).is_ok() ||
  543. membership::Module::<T>::ensure_member_root_account(&source_account, &member_id).is_ok(),
  544. Error::<T, I>::OriginIsNeitherMemberControllerOrRoot
  545. );
  546. // Ensure worker opening exists
  547. let (opening, _opening) = Self::ensure_opening_exists(&opening_id)?;
  548. // Ensure that there is sufficient balance to cover stake proposed
  549. Self::ensure_can_make_stake_imbalance(
  550. vec![&opt_role_stake_balance, &opt_application_stake_balance],
  551. &source_account
  552. )
  553. .map_err(|_| Error::<T, I>::InsufficientBalanceToApply)?;
  554. // Ensure application text is valid
  555. Self::ensure_application_text_is_valid(&human_readable_text)?;
  556. // Ensure application can actually be added
  557. ensure_on_wrapped_error!(
  558. hiring::Module::<T>::ensure_can_add_application(
  559. opening.hiring_opening_id,
  560. opt_role_stake_balance,
  561. opt_application_stake_balance)
  562. )?;
  563. // Ensure member does not have an active application to this opening
  564. Self::ensure_member_has_no_active_application_on_opening(
  565. opening.applications,
  566. member_id
  567. )?;
  568. //
  569. // == MUTATION SAFE ==
  570. //
  571. // Make imbalances for staking
  572. let opt_role_stake_imbalance = Self::make_stake_opt_imbalance(&opt_role_stake_balance, &source_account);
  573. let opt_application_stake_imbalance = Self::make_stake_opt_imbalance(&opt_application_stake_balance, &source_account);
  574. // Call hiring module to add application
  575. let add_application = ensure_on_wrapped_error!(
  576. hiring::Module::<T>::add_application(
  577. opening.hiring_opening_id,
  578. opt_role_stake_imbalance,
  579. opt_application_stake_imbalance,
  580. human_readable_text
  581. )
  582. )?;
  583. let hiring_application_id = add_application.application_id_added;
  584. // Save member id to refund the stakes. This piece of date should outlive the 'worker'.
  585. <MemberIdByHiringApplicationId<T, I>>::insert(hiring_application_id, member_id);
  586. // Get id of new worker/lead application
  587. let new_application_id = NextApplicationId::<T, I>::get();
  588. // Make worker/lead application
  589. let application = Application::new(&role_account_id, &opening_id, &member_id, &hiring_application_id);
  590. // Store application
  591. ApplicationById::<T, I>::insert(new_application_id, application);
  592. // Update next application identifier value
  593. NextApplicationId::<T, I>::mutate(|id| *id += <ApplicationId<T> as One>::one());
  594. // Add application to set of application in worker opening
  595. OpeningById::<T, I>::mutate(opening_id, |opening| {
  596. opening.applications.insert(new_application_id);
  597. });
  598. // Trigger event
  599. Self::deposit_event(RawEvent::AppliedOnOpening(opening_id, new_application_id));
  600. }
  601. /// Withdraw the worker application. Can be done by the worker itself only.
  602. #[weight = 10_000_000] // TODO: adjust weight
  603. pub fn withdraw_application(
  604. origin,
  605. application_id: ApplicationId<T>
  606. ) {
  607. // Ensuring worker application actually exists
  608. let (application, _, opening) = Self::ensure_application_exists(&application_id)?;
  609. // Ensure that it is signed
  610. let signer_account = ensure_signed(origin)?;
  611. // Ensure that signer is applicant role account
  612. ensure!(
  613. signer_account == application.role_account_id,
  614. Error::<T, I>::OriginIsNotApplicant
  615. );
  616. //
  617. // == MUTATION SAFE ==
  618. //
  619. // Attempt to deactivate application
  620. // NB: Combined ensure check and mutation in hiring module
  621. ensure_on_wrapped_error!(
  622. hiring::Module::<T>::deactive_application(
  623. application.hiring_application_id,
  624. opening.policy_commitment.exit_role_application_stake_unstaking_period,
  625. opening.policy_commitment.exit_role_stake_unstaking_period
  626. )
  627. )?;
  628. // Trigger event
  629. Self::deposit_event(RawEvent::ApplicationWithdrawn(application_id));
  630. }
  631. /// Terminate the worker application. Can be done by the lead only.
  632. #[weight = 10_000_000] // TODO: adjust weight
  633. pub fn terminate_application(
  634. origin,
  635. application_id: ApplicationId<T>
  636. ) {
  637. // Ensure lead is set and is origin signer
  638. Self::ensure_origin_is_active_leader(origin)?;
  639. // Ensuring worker application actually exists
  640. let (application, _, opening) = Self::ensure_application_exists(&application_id)?;
  641. // Attempt to deactivate application.
  642. // NB: Combined ensure check and mutation in hiring module.
  643. ensure_on_wrapped_error!(
  644. hiring::Module::<T>::deactive_application(
  645. application.hiring_application_id,
  646. opening.policy_commitment.terminate_application_stake_unstaking_period,
  647. opening.policy_commitment.terminate_role_stake_unstaking_period
  648. )
  649. )?;
  650. //
  651. // == MUTATION SAFE ==
  652. //
  653. // Trigger event
  654. Self::deposit_event(RawEvent::ApplicationTerminated(application_id));
  655. }
  656. /// Begin reviewing, and therefore not accepting new applications.
  657. /// Require signed leader origin or the root (to begin review applications for the leader position).
  658. #[weight = 10_000_000] // TODO: adjust weight
  659. pub fn begin_applicant_review(origin, opening_id: OpeningId<T>) {
  660. // Ensure opening exists
  661. // NB: Even though call to hiring modul will have implicit check for
  662. // existence of opening as well, this check is to make sure that the opening is for
  663. // this working group, not something else.
  664. let (opening, _opening) = Self::ensure_opening_exists(&opening_id)?;
  665. Self::ensure_origin_for_opening_type(origin, opening.opening_type)?;
  666. //
  667. // == MUTATION SAFE ==
  668. //
  669. // Attempt to begin review of applications.
  670. // NB: Combined ensure check and mutation in hiring module.
  671. ensure_on_wrapped_error!(
  672. hiring::Module::<T>::begin_review(opening.hiring_opening_id)
  673. )?;
  674. // Trigger event
  675. Self::deposit_event(RawEvent::BeganApplicationReview(opening_id));
  676. }
  677. /// Fill opening for worker/lead.
  678. /// Require signed leader origin or the root (to fill opening for the leader position).
  679. #[weight = 10_000_000] // TODO: adjust weight
  680. pub fn fill_opening(
  681. origin,
  682. opening_id: OpeningId<T>,
  683. successful_application_ids: ApplicationIdSet<T>,
  684. reward_policy: Option<RewardPolicy<minting::BalanceOf<T>, T::BlockNumber>>
  685. ) {
  686. // Ensure worker opening exists
  687. let (opening, _) = Self::ensure_opening_exists(&opening_id)?;
  688. Self::ensure_origin_for_opening_type(origin, opening.opening_type)?;
  689. let potential_worker_number =
  690. Self::active_worker_count() + (successful_application_ids.len() as u32);
  691. ensure!(
  692. potential_worker_number <= T::MaxWorkerNumberLimit::get(),
  693. Error::<T, I>::MaxActiveWorkerNumberExceeded
  694. );
  695. // Cannot hire a lead when another leader exists.
  696. if matches!(opening.opening_type, OpeningType::Leader) {
  697. ensure!(!<CurrentLead<T,I>>::exists(), Error::<T, I>::CannotHireLeaderWhenLeaderExists);
  698. }
  699. // Ensure a mint exists if lead is providing a reward for positions being filled
  700. let create_reward_settings = if let Some(policy) = reward_policy {
  701. // A reward will need to be created so ensure our configured mint exists
  702. let mint_id = Self::mint();
  703. // Make sure valid parameters are selected for next payment at block number
  704. ensure!(policy.next_payment_at_block > <frame_system::Module<T>>::block_number(),
  705. Error::<T, I>::FillOpeningInvalidNextPaymentBlock);
  706. // The verified reward settings to use
  707. Some((mint_id, policy))
  708. } else {
  709. None
  710. };
  711. // Make iterator over successful worker application
  712. let successful_iter = successful_application_ids
  713. .iter()
  714. // recover worker application from id
  715. .map(|application_id| { Self::ensure_application_exists(application_id)})
  716. // remove Err cases, i.e. non-existing applications
  717. .filter_map(|result| result.ok());
  718. // Count number of successful workers provided
  719. let num_provided_successful_application_ids = successful_application_ids.len();
  720. // Ensure all worker applications exist
  721. let number_of_successful_applications = successful_iter
  722. .clone()
  723. .count();
  724. ensure!(
  725. number_of_successful_applications == num_provided_successful_application_ids,
  726. Error::<T, I>::SuccessfulWorkerApplicationDoesNotExist
  727. );
  728. // Attempt to fill opening
  729. let successful_application_ids = successful_iter
  730. .clone()
  731. .map(|(successful_application, _, _)| successful_application.hiring_application_id)
  732. .collect::<BTreeSet<_>>();
  733. // Check for a single application for a leader.
  734. if matches!(opening.opening_type, OpeningType::Leader) {
  735. ensure!(successful_application_ids.len() == 1, Error::<T, I>::CannotHireMultipleLeaders);
  736. }
  737. // NB: Combined ensure check and mutation in hiring module
  738. ensure_on_wrapped_error!(
  739. hiring::Module::<T>::fill_opening(
  740. opening.hiring_opening_id,
  741. successful_application_ids,
  742. opening.policy_commitment.fill_opening_successful_applicant_application_stake_unstaking_period,
  743. opening.policy_commitment.fill_opening_failed_applicant_application_stake_unstaking_period,
  744. opening.policy_commitment.fill_opening_failed_applicant_role_stake_unstaking_period
  745. )
  746. )?;
  747. //
  748. // == MUTATION SAFE ==
  749. //
  750. // Process successful applications
  751. let application_id_to_worker_id = Self::fulfill_successful_applications(
  752. &opening,
  753. create_reward_settings,
  754. successful_iter.collect()
  755. );
  756. // Trigger event
  757. Self::deposit_event(RawEvent::OpeningFilled(opening_id, application_id_to_worker_id));
  758. }
  759. // ****************** Stakes **********************
  760. /// Slashes the worker stake, demands a leader origin. No limits, no actions on zero stake.
  761. /// If slashing balance greater than the existing stake - stake is slashed to zero.
  762. /// Require signed leader origin or the root (to slash the leader stake).
  763. #[weight = 10_000_000] // TODO: adjust weight
  764. pub fn slash_stake(origin, worker_id: WorkerId<T>, balance: BalanceOf<T>) {
  765. // Ensure lead is set or it is the council terminating the leader.
  766. Self::ensure_origin_for_leader(origin, worker_id)?;
  767. // Ensuring worker actually exists.
  768. let worker = Self::ensure_worker_exists(&worker_id)?;
  769. ensure!(balance != <BalanceOf<T>>::zero(), Error::<T, I>::StakeBalanceCannotBeZero);
  770. let stake_profile = worker.role_stake_profile.ok_or(Error::<T, I>::NoWorkerStakeProfile)?;
  771. //
  772. // == MUTATION SAFE ==
  773. //
  774. // This external module call both checks and mutates the state.
  775. ensure_on_wrapped_error!(
  776. <stake::Module<T>>::slash_immediate(
  777. &stake_profile.stake_id,
  778. balance,
  779. false
  780. )
  781. )?;
  782. Self::deposit_event(RawEvent::StakeSlashed(worker_id));
  783. }
  784. /// Decreases the worker/lead stake and returns the remainder to the worker role_account_id.
  785. /// Can be decreased to zero, no actions on zero stake.
  786. /// Require signed leader origin or the root (to decrease the leader stake).
  787. #[weight = 10_000_000] // TODO: adjust weight
  788. pub fn decrease_stake(origin, worker_id: WorkerId<T>, balance: BalanceOf<T>) {
  789. // Ensure lead is set or it is the council terminating the leader.
  790. Self::ensure_origin_for_leader(origin, worker_id)?;
  791. let worker = Self::ensure_worker_exists(&worker_id)?;
  792. ensure!(balance != <BalanceOf<T>>::zero(), Error::<T, I>::StakeBalanceCannotBeZero);
  793. let stake_profile = worker.role_stake_profile.ok_or(Error::<T, I>::NoWorkerStakeProfile)?;
  794. //
  795. // == MUTATION SAFE ==
  796. //
  797. // This external module call both checks and mutates the state.
  798. ensure_on_wrapped_error!(
  799. <stake::Module<T>>::decrease_stake_to_account(
  800. &stake_profile.stake_id,
  801. &worker.role_account_id,
  802. balance
  803. )
  804. )?;
  805. Self::deposit_event(RawEvent::StakeDecreased(worker_id));
  806. }
  807. /// Increases the worker/lead stake, demands a worker origin. Transfers tokens from the worker
  808. /// role_account_id to the stake. No limits on the stake.
  809. #[weight = 10_000_000] // TODO: adjust weight
  810. pub fn increase_stake(origin, worker_id: WorkerId<T>, balance: BalanceOf<T>) {
  811. // Checks worker origin, worker existence
  812. let worker = Self::ensure_worker_signed(origin, &worker_id)?;
  813. ensure!(balance != <BalanceOf<T>>::zero(), Error::<T, I>::StakeBalanceCannotBeZero);
  814. let stake_profile = worker.role_stake_profile.ok_or(Error::<T, I>::NoWorkerStakeProfile)?;
  815. //
  816. // == MUTATION SAFE ==
  817. //
  818. // This external module call both checks and mutates the state.
  819. ensure_on_wrapped_error!(
  820. <stake::Module<T>>::increase_stake_from_account(
  821. &stake_profile.stake_id,
  822. &worker.role_account_id,
  823. balance
  824. )
  825. )?;
  826. Self::deposit_event(RawEvent::StakeIncreased(worker_id));
  827. }
  828. /// Sets the capacity to enable working group budget. Requires root origin.
  829. #[weight = 10_000_000] // TODO: adjust weight
  830. pub fn set_mint_capacity(
  831. origin,
  832. new_capacity: minting::BalanceOf<T>
  833. ) {
  834. ensure_root(origin)?;
  835. let mint_id = Self::mint();
  836. // Technically this is a bug-check and should not be here.
  837. ensure!(<minting::Mints<T>>::contains_key(mint_id), Error::<T, I>::CannotFindMint);
  838. // Mint must exist - it is set at genesis or migration.
  839. let mint = <minting::Module<T>>::mints(mint_id);
  840. let current_capacity = mint.capacity();
  841. //
  842. // == MUTATION SAFE ==
  843. //
  844. if new_capacity != current_capacity {
  845. ensure_on_wrapped_error!(
  846. <minting::Module<T>>::set_mint_capacity(mint_id, new_capacity)
  847. )?;
  848. Self::deposit_event(RawEvent::MintCapacityChanged(mint_id, new_capacity));
  849. }
  850. }
  851. }
  852. }
  853. // ****************** Ensures **********************
  854. impl<T: Trait<I>, I: Instance> Module<T, I> {
  855. fn ensure_opening_policy_commitment_is_valid(
  856. policy_commitment: &OpeningPolicyCommitment<T::BlockNumber, BalanceOf<T>>,
  857. ) -> Result<(), Error<T, I>> {
  858. // Helper function. Ensures that unstaking period is None or non-zero.
  859. fn check_unstaking_period<BlockNumber: PartialEq + Zero, Error>(
  860. unstaking_period: Option<BlockNumber>,
  861. error: Error,
  862. ) -> Result<(), Error> {
  863. if let Some(unstaking_period) = unstaking_period {
  864. ensure!(unstaking_period != Zero::zero(), error);
  865. }
  866. Ok(())
  867. }
  868. // Helper function. Ensures that unstaking period is None or non-zero in the staking_policy.
  869. fn check_staking_policy<Balance, BlockNumber: PartialEq + Zero, Error>(
  870. staking_policy: Option<hiring::StakingPolicy<Balance, BlockNumber>>,
  871. crowded_out_unstaking_period_error: Error,
  872. review_period_unstaking_period_error: Error,
  873. ) -> Result<(), Error> {
  874. if let Some(staking_policy) = staking_policy {
  875. check_unstaking_period(
  876. staking_policy.crowded_out_unstaking_period_length,
  877. crowded_out_unstaking_period_error,
  878. )?;
  879. check_unstaking_period(
  880. staking_policy.review_period_expired_unstaking_period_length,
  881. review_period_unstaking_period_error,
  882. )?;
  883. }
  884. Ok(())
  885. }
  886. // Check all fill_opening unstaking periods.
  887. check_unstaking_period(
  888. policy_commitment.fill_opening_failed_applicant_role_stake_unstaking_period,
  889. Error::<T, I>::FillOpeningFailedApplicantRoleStakeUnstakingPeriodIsZero,
  890. )?;
  891. check_unstaking_period(
  892. policy_commitment.fill_opening_failed_applicant_application_stake_unstaking_period,
  893. Error::<T, I>::FillOpeningFailedApplicantApplicationStakeUnstakingPeriodIsZero,
  894. )?;
  895. check_unstaking_period(
  896. policy_commitment.fill_opening_successful_applicant_application_stake_unstaking_period,
  897. Error::<T, I>::FillOpeningSuccessfulApplicantApplicationStakeUnstakingPeriodIsZero,
  898. )?;
  899. check_unstaking_period(
  900. policy_commitment.exit_role_stake_unstaking_period,
  901. Error::<T, I>::ExitRoleStakeUnstakingPeriodIsZero,
  902. )?;
  903. check_unstaking_period(
  904. policy_commitment.exit_role_application_stake_unstaking_period,
  905. Error::<T, I>::ExitRoleApplicationStakeUnstakingPeriodIsZero,
  906. )?;
  907. check_unstaking_period(
  908. policy_commitment.terminate_role_stake_unstaking_period,
  909. Error::<T, I>::TerminateRoleStakeUnstakingPeriodIsZero,
  910. )?;
  911. check_unstaking_period(
  912. policy_commitment.terminate_application_stake_unstaking_period,
  913. Error::<T, I>::TerminateApplicationStakeUnstakingPeriodIsZero,
  914. )?;
  915. check_staking_policy(
  916. policy_commitment.role_staking_policy.clone(),
  917. Error::<T, I>::RoleStakingPolicyCrowdedOutUnstakingPeriodIsZero,
  918. Error::<T, I>::RoleStakingPolicyReviewPeriodUnstakingPeriodIsZero,
  919. )?;
  920. check_staking_policy(
  921. policy_commitment.application_staking_policy.clone(),
  922. Error::<T, I>::ApplicationStakingPolicyCrowdedOutUnstakingPeriodIsZero,
  923. Error::<T, I>::ApplicationStakingPolicyReviewPeriodUnstakingPeriodIsZero,
  924. )?;
  925. if let Some(application_rationing_policy) =
  926. policy_commitment.application_rationing_policy.clone()
  927. {
  928. ensure!(
  929. application_rationing_policy.max_active_applicants > 0,
  930. Error::<T, I>::ApplicationRationingPolicyMaxActiveApplicantsIsZero
  931. );
  932. }
  933. Ok(())
  934. }
  935. fn ensure_origin_for_opening_type(
  936. origin: T::Origin,
  937. opening_type: OpeningType,
  938. ) -> DispatchResult {
  939. match opening_type {
  940. OpeningType::Worker => {
  941. // Ensure lead is set and is origin signer.
  942. Self::ensure_origin_is_active_leader(origin)
  943. }
  944. OpeningType::Leader => {
  945. // Council proposal.
  946. ensure_root(origin).map_err(|err| err.into())
  947. }
  948. }
  949. }
  950. fn ensure_origin_for_leader(
  951. origin: T::Origin,
  952. worker_id: WorkerId<T>,
  953. ) -> Result<ExitInitiationOrigin, DispatchError> {
  954. let leader_worker_id = Self::ensure_lead_is_set()?;
  955. let (worker_opening_type, exit_origin) = if leader_worker_id == worker_id {
  956. (OpeningType::Leader, ExitInitiationOrigin::Sudo)
  957. } else {
  958. (OpeningType::Worker, ExitInitiationOrigin::Lead)
  959. };
  960. Self::ensure_origin_for_opening_type(origin, worker_opening_type)?;
  961. Ok(exit_origin)
  962. }
  963. fn ensure_lead_is_set() -> Result<WorkerId<T>, Error<T, I>> {
  964. let leader_worker_id = Self::current_lead();
  965. if let Some(leader_worker_id) = leader_worker_id {
  966. Ok(leader_worker_id)
  967. } else {
  968. Err(Error::<T, I>::CurrentLeadNotSet)
  969. }
  970. }
  971. // Checks that provided lead account id belongs to the current working group leader
  972. fn ensure_is_lead_account(lead_account_id: T::AccountId) -> DispatchResult {
  973. let leader_worker_id = Self::ensure_lead_is_set()?;
  974. let leader = Self::worker_by_id(leader_worker_id);
  975. if leader.role_account_id != lead_account_id {
  976. return Err(Error::<T, I>::IsNotLeadAccount.into());
  977. }
  978. Ok(())
  979. }
  980. fn ensure_opening_human_readable_text_is_valid(text: &[u8]) -> DispatchResult {
  981. <OpeningHumanReadableText<I>>::get().ensure_valid(
  982. text.len(),
  983. Error::<T, I>::OpeningTextTooShort.into(),
  984. Error::<T, I>::OpeningTextTooLong.into(),
  985. )
  986. }
  987. /// Ensures origin is signed by the leader.
  988. pub fn ensure_origin_is_active_leader(origin: T::Origin) -> DispatchResult {
  989. // Ensure is signed
  990. let signer = ensure_signed(origin)?;
  991. Self::ensure_is_lead_account(signer)
  992. }
  993. fn ensure_opening_exists(opening_id: &OpeningId<T>) -> Result<OpeningInfo<T>, Error<T, I>> {
  994. ensure!(
  995. OpeningById::<T, I>::contains_key(opening_id),
  996. Error::<T, I>::OpeningDoesNotExist
  997. );
  998. let opening = OpeningById::<T, I>::get(opening_id);
  999. let hiring_opening = hiring::OpeningById::<T>::get(opening.hiring_opening_id);
  1000. Ok((opening, hiring_opening))
  1001. }
  1002. fn ensure_member_has_no_active_application_on_opening(
  1003. applications: ApplicationIdSet<T>,
  1004. member_id: T::MemberId,
  1005. ) -> Result<(), Error<T, I>> {
  1006. for application_id in applications {
  1007. let application = ApplicationById::<T, I>::get(application_id);
  1008. // Look for application by the member for the opening
  1009. if application.member_id != member_id {
  1010. continue;
  1011. }
  1012. // Get application details
  1013. let application = <hiring::ApplicationById<T>>::get(application.hiring_application_id);
  1014. // Return error if application is in active stage
  1015. if application.stage == hiring::ApplicationStage::Active {
  1016. return Err(Error::<T, I>::MemberHasActiveApplicationOnOpening);
  1017. }
  1018. }
  1019. // Member does not have any active applications to the opening
  1020. Ok(())
  1021. }
  1022. fn ensure_application_text_is_valid(text: &[u8]) -> DispatchResult {
  1023. <WorkerApplicationHumanReadableText<I>>::get().ensure_valid(
  1024. text.len(),
  1025. Error::<T, I>::WorkerApplicationTextTooShort.into(),
  1026. Error::<T, I>::WorkerApplicationTextTooLong.into(),
  1027. )
  1028. }
  1029. // CRITICAL:
  1030. // https://github.com/Joystream/substrate-runtime-joystream/issues/92
  1031. // This assumes that ensure_can_withdraw can be done
  1032. // for a sum of balance that later will be actually withdrawn
  1033. // using individual terms in that sum.
  1034. // This needs to be fully checked across all possibly scenarios
  1035. // of actual balance, minimum balance limit, reservation, vesting and locking.
  1036. fn ensure_can_make_stake_imbalance(
  1037. opt_balances: Vec<&Option<BalanceOf<T>>>,
  1038. source_account: &T::AccountId,
  1039. ) -> DispatchResult {
  1040. let zero_balance = <BalanceOf<T> as Zero>::zero();
  1041. // Total amount to be staked
  1042. let total_amount = opt_balances.iter().fold(zero_balance, |sum, opt_balance| {
  1043. sum + if let Some(balance) = opt_balance {
  1044. *balance
  1045. } else {
  1046. zero_balance
  1047. }
  1048. });
  1049. if total_amount > zero_balance {
  1050. // Ensure that
  1051. if CurrencyOf::<T>::free_balance(source_account) < total_amount {
  1052. Err(Error::<T, I>::InsufficientBalanceToCoverStake.into())
  1053. } else {
  1054. let new_balance = CurrencyOf::<T>::free_balance(source_account) - total_amount;
  1055. CurrencyOf::<T>::ensure_can_withdraw(
  1056. source_account,
  1057. total_amount,
  1058. WithdrawReasons::all(),
  1059. new_balance,
  1060. )
  1061. }
  1062. } else {
  1063. Ok(())
  1064. }
  1065. }
  1066. fn ensure_application_exists(
  1067. application_id: &ApplicationId<T>,
  1068. ) -> Result<ApplicationInfo<T>, Error<T, I>> {
  1069. ensure!(
  1070. ApplicationById::<T, I>::contains_key(application_id),
  1071. Error::<T, I>::WorkerApplicationDoesNotExist
  1072. );
  1073. let application = ApplicationById::<T, I>::get(application_id);
  1074. let opening = OpeningById::<T, I>::get(application.opening_id);
  1075. Ok((application, *application_id, opening))
  1076. }
  1077. /// Ensures the origin contains signed account that belongs to existing worker.
  1078. pub fn ensure_worker_signed(
  1079. origin: T::Origin,
  1080. worker_id: &WorkerId<T>,
  1081. ) -> Result<WorkerOf<T>, DispatchError> {
  1082. // Ensure that it is signed
  1083. let signer_account = ensure_signed(origin)?;
  1084. // Ensure that id corresponds to active worker
  1085. let worker = Self::ensure_worker_exists(&worker_id)?;
  1086. // Ensure that signer is actually role account of worker
  1087. ensure!(
  1088. signer_account == worker.role_account_id,
  1089. Error::<T, I>::SignerIsNotWorkerRoleAccount
  1090. );
  1091. Ok(worker)
  1092. }
  1093. /// Ensures worker under given id already exists
  1094. pub fn ensure_worker_exists(worker_id: &WorkerId<T>) -> Result<WorkerOf<T>, Error<T, I>> {
  1095. ensure!(
  1096. WorkerById::<T, I>::contains_key(worker_id),
  1097. Error::<T, I>::WorkerDoesNotExist
  1098. );
  1099. let worker = WorkerById::<T, I>::get(worker_id);
  1100. Ok(worker)
  1101. }
  1102. fn ensure_worker_has_recurring_reward(
  1103. worker: &WorkerOf<T>,
  1104. ) -> Result<T::RewardRelationshipId, Error<T, I>> {
  1105. if let Some(relationship_id) = worker.reward_relationship {
  1106. Ok(relationship_id)
  1107. } else {
  1108. Err(Error::<T, I>::WorkerHasNoReward)
  1109. }
  1110. }
  1111. fn ensure_worker_exit_rationale_text_is_valid(text: &[u8]) -> DispatchResult {
  1112. Self::worker_exit_rationale_text().ensure_valid(
  1113. text.len(),
  1114. Error::<T, I>::WorkerExitRationaleTextTooShort.into(),
  1115. Error::<T, I>::WorkerExitRationaleTextTooLong.into(),
  1116. )
  1117. }
  1118. fn ensure_worker_role_storage_text_is_valid(text: &[u8]) -> DispatchResult {
  1119. ensure!(
  1120. text.len() as u16 <= Self::worker_storage_size(),
  1121. Error::<T, I>::WorkerStorageValueTooLong
  1122. );
  1123. Ok(())
  1124. }
  1125. }
  1126. /// Creates default text constraint.
  1127. pub fn default_text_constraint() -> InputValidationLengthConstraint {
  1128. InputValidationLengthConstraint::new(1, 1024)
  1129. }
  1130. /// Creates default storage size constraint.
  1131. pub fn default_storage_size_constraint() -> u16 {
  1132. 2048
  1133. }
  1134. impl<T: Trait<I>, I: Instance> Module<T, I> {
  1135. /// Callback from StakingEventsHandler. Refunds unstaked imbalance back to the source account.
  1136. pub fn refund_working_group_stake(
  1137. stake_id: StakeId<T>,
  1138. imbalance: NegativeImbalance<T>,
  1139. ) -> NegativeImbalance<T> {
  1140. if !hiring::ApplicationIdByStakingId::<T>::contains_key(stake_id) {
  1141. print("Working group broken invariant: no stake id in the hiring module.");
  1142. return imbalance;
  1143. }
  1144. let hiring_application_id = hiring::ApplicationIdByStakingId::<T>::get(stake_id);
  1145. if !MemberIdByHiringApplicationId::<T, I>::contains_key(hiring_application_id) {
  1146. // Stake is not related to the working group module.
  1147. return imbalance;
  1148. }
  1149. let member_id = Module::<T, I>::member_id_by_hiring_application_id(hiring_application_id);
  1150. if membership::MembershipById::<T>::contains_key(member_id) {
  1151. let member_profile = membership::MembershipById::<T>::get(member_id);
  1152. let refunding_result = CurrencyOf::<T>::resolve_into_existing(
  1153. &member_profile.controller_account,
  1154. imbalance,
  1155. );
  1156. if refunding_result.is_err() {
  1157. print("Working group broken invariant: cannot refund.");
  1158. // cannot return imbalance here, because of possible double spending.
  1159. return <NegativeImbalance<T>>::zero();
  1160. }
  1161. } else {
  1162. print("Working group broken invariant: no member profile.");
  1163. return imbalance;
  1164. }
  1165. <NegativeImbalance<T>>::zero()
  1166. }
  1167. /// Returns all existing worker id list excluding the current leader worker id.
  1168. pub fn get_regular_worker_ids() -> Vec<WorkerId<T>> {
  1169. let lead_worker_id = Self::current_lead();
  1170. <WorkerById<T, I>>::iter()
  1171. .filter_map(|(worker_id, _)| {
  1172. // Filter the leader worker id if the leader is set.
  1173. lead_worker_id.map_or(Some(worker_id), |lead_worker_id| {
  1174. if worker_id == lead_worker_id {
  1175. None
  1176. } else {
  1177. Some(worker_id)
  1178. }
  1179. })
  1180. })
  1181. .collect()
  1182. }
  1183. /// Returns all existing worker id list.
  1184. pub fn get_all_worker_ids() -> Vec<WorkerId<T>> {
  1185. <WorkerById<T, I>>::iter()
  1186. .map(|(worker_id, _)| worker_id)
  1187. .collect()
  1188. }
  1189. fn make_stake_opt_imbalance(
  1190. opt_balance: &Option<BalanceOf<T>>,
  1191. source_account: &T::AccountId,
  1192. ) -> Option<NegativeImbalance<T>> {
  1193. if let Some(balance) = opt_balance {
  1194. let withdraw_result = CurrencyOf::<T>::withdraw(
  1195. source_account,
  1196. *balance,
  1197. WithdrawReasons::all(),
  1198. ExistenceRequirement::AllowDeath,
  1199. );
  1200. assert!(withdraw_result.is_ok());
  1201. withdraw_result.ok()
  1202. } else {
  1203. None
  1204. }
  1205. }
  1206. fn deactivate_worker(
  1207. worker_id: &WorkerId<T>,
  1208. worker: &WorkerOf<T>,
  1209. exit_initiation_origin: &ExitInitiationOrigin,
  1210. rationale_text: &[u8],
  1211. ) -> Result<(), Error<T, I>> {
  1212. // Stop any possible recurring rewards
  1213. if let Some(reward_relationship_id) = worker.reward_relationship {
  1214. // Attempt to deactivate
  1215. recurringrewards::Module::<T>::try_to_deactivate_relationship(reward_relationship_id)
  1216. .map_err(|_| Error::<T, I>::RelationshipMustExist)?;
  1217. }; // else: Did not deactivate, there was no reward relationship!
  1218. // Unstake if stake profile exists
  1219. if let Some(ref stake_profile) = worker.role_stake_profile {
  1220. // Determine unstaking period based on who initiated deactivation
  1221. let unstaking_period = match exit_initiation_origin {
  1222. ExitInitiationOrigin::Lead => stake_profile.termination_unstaking_period,
  1223. ExitInitiationOrigin::Sudo => stake_profile.termination_unstaking_period,
  1224. ExitInitiationOrigin::Worker => stake_profile.exit_unstaking_period,
  1225. };
  1226. // Unstake
  1227. ensure_on_wrapped_error!(stake::Module::<T>::initiate_unstaking(
  1228. &stake_profile.stake_id,
  1229. unstaking_period
  1230. ))?;
  1231. }
  1232. // Unset lead if the leader is leaving.
  1233. let leader_worker_id = <CurrentLead<T, I>>::get();
  1234. if let Some(leader_worker_id) = leader_worker_id {
  1235. if leader_worker_id == *worker_id {
  1236. Self::unset_lead();
  1237. }
  1238. }
  1239. // Remove the worker from the storage.
  1240. WorkerById::<T, I>::remove(worker_id);
  1241. Self::decrease_active_worker_counter();
  1242. // Trigger the event
  1243. let event = match exit_initiation_origin {
  1244. ExitInitiationOrigin::Lead => {
  1245. RawEvent::TerminatedWorker(*worker_id, rationale_text.to_vec())
  1246. }
  1247. ExitInitiationOrigin::Worker => {
  1248. RawEvent::WorkerExited(*worker_id, rationale_text.to_vec())
  1249. }
  1250. ExitInitiationOrigin::Sudo => {
  1251. RawEvent::TerminatedLeader(*worker_id, rationale_text.to_vec())
  1252. }
  1253. };
  1254. Self::deposit_event(event);
  1255. Ok(())
  1256. }
  1257. /// Initialize working group constraints and mint.
  1258. pub fn initialize_working_group(
  1259. opening_human_readable_text_constraint: InputValidationLengthConstraint,
  1260. worker_application_human_readable_text_constraint: InputValidationLengthConstraint,
  1261. worker_exit_rationale_text_constraint: InputValidationLengthConstraint,
  1262. worker_storage_size_constraint: u16,
  1263. working_group_mint_capacity: minting::BalanceOf<T>,
  1264. ) {
  1265. // Create a mint.
  1266. let mint_id_result = <minting::Module<T>>::add_mint(working_group_mint_capacity, None);
  1267. if let Ok(mint_id) = mint_id_result {
  1268. <Mint<T, I>>::put(mint_id);
  1269. } else {
  1270. panic!("Failed to create a mint for the working group");
  1271. }
  1272. // Create constraints
  1273. <OpeningHumanReadableText<I>>::put(opening_human_readable_text_constraint);
  1274. <WorkerApplicationHumanReadableText<I>>::put(
  1275. worker_application_human_readable_text_constraint,
  1276. );
  1277. <WorkerExitRationaleText<I>>::put(worker_exit_rationale_text_constraint);
  1278. <WorkerStorageSize<I>>::put(worker_storage_size_constraint);
  1279. }
  1280. /// Set storage size constraint
  1281. pub fn set_worker_storage_size_constraint(worker_storage_size_constraint: u16) {
  1282. <WorkerStorageSize<I>>::put(worker_storage_size_constraint);
  1283. }
  1284. // Set worker id as a leader id.
  1285. pub(crate) fn set_lead(worker_id: WorkerId<T>) {
  1286. // Update current lead
  1287. <CurrentLead<T, I>>::put(worker_id);
  1288. // Trigger an event
  1289. Self::deposit_event(RawEvent::LeaderSet(worker_id));
  1290. }
  1291. // Evict the currently set lead.
  1292. pub(crate) fn unset_lead() {
  1293. if Self::ensure_lead_is_set().is_ok() {
  1294. // Update current lead
  1295. <CurrentLead<T, I>>::kill();
  1296. Self::deposit_event(RawEvent::LeaderUnset());
  1297. }
  1298. }
  1299. // Processes successful application during the fill_opening().
  1300. fn fulfill_successful_applications(
  1301. opening: &OpeningOf<T>,
  1302. reward_settings: Option<RewardSettings<T>>,
  1303. successful_applications_info: Vec<ApplicationInfo<T>>,
  1304. ) -> BTreeMap<ApplicationId<T>, WorkerId<T>> {
  1305. let mut application_id_to_worker_id = BTreeMap::new();
  1306. successful_applications_info
  1307. .iter()
  1308. .for_each(|(successful_application, id, _)| {
  1309. // Create a reward relationship.
  1310. let reward_relationship = if let Some((mint_id, checked_policy)) =
  1311. reward_settings.clone()
  1312. {
  1313. // Create a new recipient for the new relationship.
  1314. let recipient = <recurringrewards::Module<T>>::add_recipient();
  1315. // Member must exist, since it was checked that it can enter the role.
  1316. let member_profile =
  1317. <membership::Module<T>>::membership(successful_application.member_id);
  1318. // Rewards are deposited in the member's root account.
  1319. let reward_destination_account = member_profile.root_account;
  1320. // Values have been checked so this should not fail!
  1321. let relationship_id = <recurringrewards::Module<T>>::add_reward_relationship(
  1322. mint_id,
  1323. recipient,
  1324. reward_destination_account,
  1325. checked_policy.amount_per_payout,
  1326. checked_policy.next_payment_at_block,
  1327. checked_policy.payout_interval,
  1328. )
  1329. .expect("Failed to create reward relationship!");
  1330. Some(relationship_id)
  1331. } else {
  1332. None
  1333. };
  1334. // Get possible stake for role
  1335. let application =
  1336. hiring::ApplicationById::<T>::get(successful_application.hiring_application_id);
  1337. // Staking profile for worker
  1338. let stake_profile = application.active_role_staking_id.as_ref().map(|stake_id| {
  1339. RoleStakeProfile::new(
  1340. stake_id,
  1341. &opening
  1342. .policy_commitment
  1343. .terminate_role_stake_unstaking_period,
  1344. &opening.policy_commitment.exit_role_stake_unstaking_period,
  1345. )
  1346. });
  1347. // Get worker id
  1348. let new_worker_id = <NextWorkerId<T, I>>::get();
  1349. // Construct worker
  1350. let worker = Worker::new(
  1351. &successful_application.member_id,
  1352. &successful_application.role_account_id,
  1353. &reward_relationship,
  1354. &stake_profile,
  1355. );
  1356. // Store a worker
  1357. <WorkerById<T, I>>::insert(new_worker_id, worker);
  1358. Self::increase_active_worker_counter();
  1359. // Update next worker id
  1360. <NextWorkerId<T, I>>::mutate(|id| *id += <WorkerId<T> as One>::one());
  1361. application_id_to_worker_id.insert(*id, new_worker_id);
  1362. // Sets a leader on successful opening when opening is for leader.
  1363. if matches!(opening.opening_type, OpeningType::Leader) {
  1364. Self::set_lead(new_worker_id);
  1365. }
  1366. });
  1367. application_id_to_worker_id
  1368. }
  1369. // Increases active worker counter (saturating).
  1370. fn increase_active_worker_counter() {
  1371. let next_active_worker_count_value = Self::active_worker_count().saturating_add(1);
  1372. <ActiveWorkerCount<I>>::put(next_active_worker_count_value);
  1373. }
  1374. // Decreases active worker counter (saturating).
  1375. fn decrease_active_worker_counter() {
  1376. let next_active_worker_count_value = Self::active_worker_count().saturating_sub(1);
  1377. <ActiveWorkerCount<I>>::put(next_active_worker_count_value);
  1378. }
  1379. }