lib.rs 58 KB

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