lib.rs 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790
  1. // Ensure we're `no_std` when compiling for Wasm.
  2. #![cfg_attr(not(feature = "std"), no_std)]
  3. mod constraints;
  4. #[cfg(test)]
  5. mod tests;
  6. mod types;
  7. #[macro_use]
  8. mod errors;
  9. use rstd::collections::btree_map::BTreeMap;
  10. use rstd::collections::btree_set::BTreeSet;
  11. use rstd::prelude::*;
  12. use rstd::vec::Vec;
  13. use sr_primitives::traits::{EnsureOrigin, One, Zero};
  14. use srml_support::traits::{Currency, ExistenceRequirement, WithdrawReasons};
  15. use srml_support::{decl_event, decl_module, decl_storage, dispatch, ensure};
  16. use system::{ensure_root, ensure_signed, RawOrigin};
  17. use constraints::InputValidationLengthConstraint;
  18. use errors::bureaucracy_errors::*;
  19. use errors::WrappedError;
  20. use types::{
  21. Lead, OpeningPolicyCommitment, RewardPolicy, Worker, WorkerApplication, WorkerOpening,
  22. WorkerRoleStage, WorkerRoleStakeProfile,
  23. };
  24. //TODO: docs
  25. //TODO: migrate to decl_error
  26. //TODO: 'roles' extrinsics
  27. //TODO: initialize a mint!
  28. /// Alias for the _Lead_ type
  29. pub type LeadOf<T> =
  30. Lead<<T as membership::members::Trait>::MemberId, <T as system::Trait>::AccountId>;
  31. /*
  32. + add_worker_opening
  33. + accept_worker_applications
  34. + begin_worker_applicant_review
  35. + fill_worker_opening
  36. + withdraw_worker_application
  37. + terminate_worker_application
  38. + apply_on_worker_opening
  39. */
  40. /// Workaround for BTreeSet type
  41. pub type WorkerApplicationIdSet<T> = BTreeSet<WorkerApplicationId<T>>;
  42. /// Type for the identifier for an opening for a worker.
  43. pub type WorkerOpeningId<T> = <T as hiring::Trait>::OpeningId;
  44. /// Type for the identifier for an application as a worker.
  45. pub type WorkerApplicationId<T> = <T as hiring::Trait>::ApplicationId;
  46. /// Balance type of runtime
  47. pub type BalanceOf<T> =
  48. <<T as stake::Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::Balance;
  49. /// Balance type of runtime
  50. pub type CurrencyOf<T> = <T as stake::Trait>::Currency;
  51. /// Negative imbalance of runtime.
  52. pub type NegativeImbalance<T> =
  53. <<T as stake::Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::NegativeImbalance;
  54. /// Alias for the worker application id to the worker id dictionary
  55. pub type WorkerApplicationIdToWorkerIdMap<T> = BTreeMap<WorkerApplicationId<T>, WorkerId<T>>;
  56. /// Type identifier for worker role, which must be same as membership actor identifier
  57. pub type WorkerId<T> = <T as membership::members::Trait>::ActorId;
  58. // Type simplification
  59. type WorkerOpeningInfo<T> = (
  60. WorkerOpening<
  61. <T as hiring::Trait>::OpeningId,
  62. <T as system::Trait>::BlockNumber,
  63. BalanceOf<T>,
  64. WorkerApplicationId<T>,
  65. >,
  66. hiring::Opening<
  67. BalanceOf<T>,
  68. <T as system::Trait>::BlockNumber,
  69. <T as hiring::Trait>::ApplicationId,
  70. >,
  71. );
  72. // Type simplification
  73. type WorkerApplicationInfo<T> = (
  74. WorkerApplication<
  75. <T as system::Trait>::AccountId,
  76. WorkerOpeningId<T>,
  77. <T as membership::members::Trait>::MemberId,
  78. <T as hiring::Trait>::ApplicationId,
  79. >,
  80. WorkerApplicationId<T>,
  81. WorkerOpening<
  82. <T as hiring::Trait>::OpeningId,
  83. <T as system::Trait>::BlockNumber,
  84. BalanceOf<T>,
  85. WorkerApplicationId<T>,
  86. >,
  87. );
  88. // Type simplification
  89. type WorkerOf<T> = Worker<
  90. <T as system::Trait>::AccountId,
  91. <T as recurringrewards::Trait>::RewardRelationshipId,
  92. <T as stake::Trait>::StakeId,
  93. <T as system::Trait>::BlockNumber,
  94. >;
  95. /// The bureaucracy main _Trait_
  96. pub trait Trait<I: Instance>:
  97. system::Trait
  98. + membership::members::Trait
  99. + hiring::Trait
  100. + minting::Trait
  101. + stake::Trait
  102. + recurringrewards::Trait
  103. {
  104. /// Engine event type.
  105. type Event: From<Event<Self, I>> + Into<<Self as system::Trait>::Event>;
  106. }
  107. decl_event!(
  108. /// Proposals engine events
  109. pub enum Event<T, I>
  110. where
  111. <T as membership::members::Trait>::MemberId,
  112. <T as system::Trait>::AccountId,
  113. WorkerOpeningId = WorkerOpeningId<T>,
  114. WorkerApplicationId = WorkerApplicationId<T>,
  115. WorkerApplicationIdToWorkerIdMap = WorkerApplicationIdToWorkerIdMap<T>,
  116. {
  117. /// Emits on setting the leader.
  118. /// Params:
  119. /// - Member id of the leader.
  120. /// - Role account id of the leader.
  121. LeaderSet(MemberId, AccountId),
  122. /// Emits on adding new worker opening.
  123. /// Params:
  124. /// - Worker opening id
  125. WorkerOpeningAdded(WorkerOpeningId),
  126. /// Emits on accepting application for the worker opening.
  127. /// Params:
  128. /// - Worker opening id
  129. AcceptedWorkerApplications(WorkerOpeningId),
  130. /// Emits on adding the application for the worker opening.
  131. /// Params:
  132. /// - Worker opening id
  133. /// - Worker application id
  134. AppliedOnWorkerOpening(WorkerOpeningId, WorkerApplicationId),
  135. /// Emits on withdrawing the application for the worker opening.
  136. /// Params:
  137. /// - Worker application id
  138. WorkerApplicationWithdrawn(WorkerApplicationId),
  139. /// Emits on terminating the application for the worker opening.
  140. /// Params:
  141. /// - Worker application id
  142. WorkerApplicationTerminated(WorkerApplicationId),
  143. /// Emits on beginning the application review for the worker opening.
  144. /// Params:
  145. /// - Worker opening id
  146. BeganWorkerApplicationReview(WorkerOpeningId),
  147. /// Emits on filling the worker opening.
  148. /// Params:
  149. /// - Worker opening id
  150. /// - Worker application id to the worker id dictionary
  151. WorkerOpeningFilled(WorkerOpeningId, WorkerApplicationIdToWorkerIdMap),
  152. }
  153. );
  154. decl_storage! {
  155. trait Store for Module<T: Trait<I>, I: Instance> as Bureaucracy {
  156. /// The mint currently funding the rewards for this module.
  157. pub Mint get(mint) : <T as minting::Trait>::MintId;
  158. /// The current lead.
  159. pub CurrentLead get(current_lead) : Option<LeadOf<T>>;
  160. /// Next identifier value for new worker opening.
  161. pub NextWorkerOpeningId get(next_worker_opening_id): WorkerOpeningId<T>;
  162. /// Maps identifier to worker opening.
  163. pub WorkerOpeningById get(worker_opening_by_id): linked_map WorkerOpeningId<T> => WorkerOpening<T::OpeningId, T::BlockNumber, BalanceOf<T>, WorkerApplicationId<T>>;
  164. /// Opening human readable text length limits
  165. pub OpeningHumanReadableText get(opening_human_readable_text): InputValidationLengthConstraint;
  166. /// Maps identifier to worker application on opening.
  167. pub WorkerApplicationById get(worker_application_by_id) : linked_map WorkerApplicationId<T> => WorkerApplication<T::AccountId, WorkerOpeningId<T>, T::MemberId, T::ApplicationId>;
  168. /// Next identifier value for new worker application.
  169. pub NextWorkerApplicationId get(next_worker_application_id) : WorkerApplicationId<T>;
  170. /// Worker application human readable text length limits
  171. pub WorkerApplicationHumanReadableText get(worker_application_human_readable_text) : InputValidationLengthConstraint;
  172. /// Maps identifier to corresponding worker.
  173. pub WorkerById get(worker_by_id) : linked_map WorkerId<T> => WorkerOf<T>;
  174. /// Next identifier for new worker.
  175. pub NextWorkerId get(next_worker_id) : WorkerId<T>;
  176. }
  177. }
  178. decl_module! {
  179. pub struct Module<T: Trait<I>, I: Instance> for enum Call where origin: T::Origin {
  180. /// Default deposit_event() handler
  181. fn deposit_event() = default;
  182. /// Introduce a lead when one is not currently set.
  183. pub fn set_lead(origin, member_id: T::MemberId, role_account_id: T::AccountId) -> dispatch::Result {
  184. ensure_root(origin)?;
  185. // Construct lead
  186. let new_lead = Lead {
  187. member_id,
  188. role_account_id: role_account_id.clone(),
  189. };
  190. // mutation
  191. // Update current lead
  192. <CurrentLead<T, I>>::put(new_lead);
  193. // Trigger an event
  194. Self::deposit_event(RawEvent::LeaderSet(member_id, role_account_id));
  195. Ok(())
  196. }
  197. /// Add an opening for a worker role.
  198. pub fn add_worker_opening(
  199. origin,
  200. activate_at: hiring::ActivateOpeningAt<T::BlockNumber>,
  201. commitment: OpeningPolicyCommitment<T::BlockNumber,
  202. BalanceOf<T>>,
  203. human_readable_text: Vec<u8>
  204. ){
  205. // Ensure lead is set and is origin signer
  206. Self::ensure_origin_is_set_lead(origin)?;
  207. Self::ensure_opening_human_readable_text_is_valid(&human_readable_text)?;
  208. // Add opening
  209. // NB: This call can in principle fail, because the staking policies
  210. // may not respect the minimum currency requirement.
  211. let policy_commitment = commitment.clone();
  212. // mutation
  213. let opening_id = ensure_on_wrapped_error!(
  214. hiring::Module::<T>::add_opening(
  215. activate_at,
  216. commitment.max_review_period_length,
  217. commitment.application_rationing_policy,
  218. commitment.application_staking_policy,
  219. commitment.role_staking_policy,
  220. human_readable_text,
  221. ))?;
  222. let new_worker_opening_id = NextWorkerOpeningId::<T, I>::get();
  223. // Create and add worker opening.
  224. let new_opening_by_id = WorkerOpening::<WorkerOpeningId<T>, T::BlockNumber, BalanceOf<T>, WorkerApplicationId<T>> {
  225. opening_id : opening_id,
  226. worker_applications: BTreeSet::new(),
  227. policy_commitment: policy_commitment
  228. };
  229. WorkerOpeningById::<T, I>::insert(new_worker_opening_id, new_opening_by_id);
  230. // Update NextWorkerOpeningId
  231. NextWorkerOpeningId::<T, I>::mutate(|id| *id += <WorkerOpeningId<T> as One>::one());
  232. // Trigger event
  233. Self::deposit_event(RawEvent::WorkerOpeningAdded(new_worker_opening_id));
  234. }
  235. /// Begin accepting worker applications to an opening that is active.
  236. pub fn accept_worker_applications(origin, worker_opening_id: WorkerOpeningId<T>) {
  237. // Ensure lead is set and is origin signer
  238. Self::ensure_origin_is_set_lead(origin)?;
  239. // Ensure opening exists in this working group
  240. // NB: Even though call to hiring module will have implicit check for
  241. // existence of opening as well, this check is to make sure that the opening is for
  242. // this working group, not something else.
  243. let (worker_opening, _opening) = Self::ensure_worker_opening_exists(&worker_opening_id)?;
  244. // Attempt to begin accepting applications
  245. // NB: Combined ensure check and mutation in hiring module
  246. // mutation
  247. ensure_on_wrapped_error!(
  248. hiring::Module::<T>::begin_accepting_applications(worker_opening.opening_id)
  249. )?;
  250. // Trigger event
  251. Self::deposit_event(RawEvent::AcceptedWorkerApplications(worker_opening_id));
  252. }
  253. /// Apply on a worker opening.
  254. pub fn apply_on_worker_opening(
  255. origin,
  256. member_id: T::MemberId,
  257. worker_opening_id: WorkerOpeningId<T>,
  258. role_account: T::AccountId,
  259. opt_role_stake_balance: Option<BalanceOf<T>>,
  260. opt_application_stake_balance: Option<BalanceOf<T>>,
  261. human_readable_text: Vec<u8>
  262. ) {
  263. // Ensure origin which will server as the source account for staked funds is signed
  264. let source_account = ensure_signed(origin)?;
  265. // In absence of a more general key delegation system which allows an account with some funds to
  266. // grant another account permission to stake from its funds, the origin of this call must have the funds
  267. // and cannot specify another arbitrary account as the source account.
  268. // Ensure the source_account is either the controller or root account of member with given id
  269. ensure!(
  270. membership::members::Module::<T>::ensure_member_controller_account(&source_account, &member_id).is_ok() ||
  271. membership::members::Module::<T>::ensure_member_root_account(&source_account, &member_id).is_ok(),
  272. MSG_ORIGIN_IS_NEITHER_MEMBER_CONTROLLER_OR_ROOT
  273. );
  274. // Ensure worker opening exists
  275. let (worker_opening, _opening) = Self::ensure_worker_opening_exists(&worker_opening_id)?;
  276. // Ensure that there is sufficient balance to cover stake proposed
  277. Self::ensure_can_make_stake_imbalance(
  278. vec![&opt_role_stake_balance, &opt_application_stake_balance],
  279. &source_account)
  280. .map_err(|_err| MSG_INSUFFICIENT_BALANCE_TO_APPLY)?;
  281. // Ensure application text is valid
  282. Self::ensure_worker_application_text_is_valid(&human_readable_text)?;
  283. // Ensure application can actually be added
  284. ensure_on_wrapped_error!(
  285. hiring::Module::<T>::ensure_can_add_application(worker_opening.opening_id, opt_role_stake_balance, opt_application_stake_balance)
  286. )?;
  287. // Ensure member does not have an active application to this opening
  288. Self::ensure_member_has_no_active_application_on_opening(
  289. worker_opening.worker_applications,
  290. member_id
  291. )?;
  292. // mutation
  293. // Make imbalances for staking
  294. let opt_role_stake_imbalance = Self::make_stake_opt_imbalance(&opt_role_stake_balance, &source_account);
  295. let opt_application_stake_imbalance = Self::make_stake_opt_imbalance(&opt_application_stake_balance, &source_account);
  296. // Call hiring module to add application
  297. let add_application_result = hiring::Module::<T>::add_application(
  298. worker_opening.opening_id,
  299. opt_role_stake_imbalance,
  300. opt_application_stake_imbalance,
  301. human_readable_text
  302. );
  303. // Has to hold
  304. assert!(add_application_result.is_ok());
  305. let application_id = add_application_result.unwrap().application_id_added;
  306. // Get id of new worker application
  307. let new_worker_application_id = NextWorkerApplicationId::<T, I>::get();
  308. // Make worker application
  309. let worker_application = WorkerApplication::new(&role_account, &worker_opening_id, &member_id, &application_id);
  310. // Store application
  311. WorkerApplicationById::<T, I>::insert(new_worker_application_id, worker_application);
  312. // Update next worker application identifier value
  313. NextWorkerApplicationId::<T, I>::mutate(|id| *id += <WorkerApplicationId<T> as One>::one());
  314. // Add application to set of application in worker opening
  315. WorkerOpeningById::<T, I>::mutate(worker_opening_id, |worker_opening| {
  316. worker_opening.worker_applications.insert(new_worker_application_id);
  317. });
  318. // Trigger event
  319. Self::deposit_event(RawEvent::AppliedOnWorkerOpening(worker_opening_id, new_worker_application_id));
  320. }
  321. pub fn withdraw_worker_application(
  322. origin,
  323. worker_application_id: WorkerApplicationId<T>
  324. ) {
  325. // Ensuring worker application actually exists
  326. let (worker_application, _, worker_opening) = Self::ensure_worker_application_exists(&worker_application_id)?;
  327. // Ensure that it is signed
  328. let signer_account = ensure_signed(origin)?;
  329. // Ensure that signer is applicant role account
  330. ensure!(
  331. signer_account == worker_application.role_account,
  332. MSG_ORIGIN_IS_NOT_APPLICANT
  333. );
  334. // Attempt to deactivate application
  335. // NB: Combined ensure check and mutation in hiring module
  336. ensure_on_wrapped_error!(
  337. hiring::Module::<T>::deactive_application(
  338. worker_application.application_id,
  339. worker_opening.policy_commitment.exit_worker_role_application_stake_unstaking_period,
  340. worker_opening.policy_commitment.exit_worker_role_stake_unstaking_period
  341. )
  342. )?;
  343. // mutation
  344. // Trigger event
  345. Self::deposit_event(RawEvent::WorkerApplicationWithdrawn(worker_application_id));
  346. }
  347. pub fn terminate_worker_application(
  348. origin,
  349. worker_application_id: WorkerApplicationId<T>
  350. ) {
  351. // Ensure lead is set and is origin signer
  352. Self::ensure_origin_is_set_lead(origin)?;
  353. // Ensuring worker application actually exists
  354. let (worker_application, _, worker_opening) = Self::ensure_worker_application_exists(&worker_application_id)?;
  355. // Attempt to deactivate application
  356. // NB: Combined ensure check and mutation in hiring module
  357. ensure_on_wrapped_error!(
  358. hiring::Module::<T>::deactive_application(
  359. worker_application.application_id,
  360. worker_opening.policy_commitment.terminate_worker_application_stake_unstaking_period,
  361. worker_opening.policy_commitment.terminate_worker_role_stake_unstaking_period
  362. )
  363. )?;
  364. // mutation
  365. // Trigger event
  366. Self::deposit_event(RawEvent::WorkerApplicationTerminated(worker_application_id));
  367. }
  368. /// Begin reviewing, and therefore not accepting new applications.
  369. pub fn begin_worker_applicant_review(origin, worker_opening_id: WorkerOpeningId<T>) {
  370. // Ensure lead is set and is origin signer
  371. Self::ensure_origin_is_set_lead(origin)?;
  372. // Ensure opening exists
  373. // NB: Even though call to hiring modul will have implicit check for
  374. // existence of opening as well, this check is to make sure that the opening is for
  375. // this working group, not something else.
  376. let (worker_opening, _opening) = Self::ensure_worker_opening_exists(&worker_opening_id)?;
  377. // Attempt to begin review of applications
  378. // NB: Combined ensure check and mutation in hiring module
  379. ensure_on_wrapped_error!(
  380. hiring::Module::<T>::begin_review(worker_opening.opening_id)
  381. )?;
  382. // mutation
  383. // Trigger event
  384. Self::deposit_event(RawEvent::BeganWorkerApplicationReview(worker_opening_id));
  385. }
  386. /// Fill opening for worker
  387. pub fn fill_worker_opening(
  388. origin,
  389. worker_opening_id: WorkerOpeningId<T>,
  390. successful_worker_application_ids: WorkerApplicationIdSet<T>,
  391. reward_policy: Option<RewardPolicy<minting::BalanceOf<T>, T::BlockNumber>>
  392. ) {
  393. // Ensure lead is set and is origin signer
  394. Self::ensure_origin_is_set_lead(origin)?;
  395. // Ensure worker opening exists
  396. let (worker_opening, _) = Self::ensure_worker_opening_exists(&worker_opening_id)?;
  397. // Make iterator over successful worker application
  398. let successful_iter = successful_worker_application_ids
  399. .iter()
  400. // recover worker application from id
  401. .map(|worker_application_id| { Self::ensure_worker_application_exists(worker_application_id)})
  402. // remove Err cases, i.e. non-existing applications
  403. .filter_map(|result| result.ok());
  404. // Count number of successful workers provided
  405. let num_provided_successful_worker_application_ids = successful_worker_application_ids.len();
  406. // Ensure all worker applications exist
  407. let number_of_successful_applications = successful_iter
  408. .clone()
  409. .count();
  410. ensure!(
  411. number_of_successful_applications == num_provided_successful_worker_application_ids,
  412. MSG_SUCCESSFUL_WORKER_APPLICATION_DOES_NOT_EXIST
  413. );
  414. // Attempt to fill opening
  415. let successful_application_ids = successful_iter
  416. .clone()
  417. .map(|(successful_worker_application, _, _)| successful_worker_application.application_id)
  418. .collect::<BTreeSet<_>>();
  419. // NB: Combined ensure check and mutation in hiring module
  420. ensure_on_wrapped_error!(
  421. hiring::Module::<T>::fill_opening(
  422. worker_opening.opening_id,
  423. successful_application_ids,
  424. worker_opening.policy_commitment.fill_opening_successful_applicant_application_stake_unstaking_period,
  425. worker_opening.policy_commitment.fill_opening_failed_applicant_application_stake_unstaking_period,
  426. worker_opening.policy_commitment.fill_opening_failed_applicant_role_stake_unstaking_period
  427. )
  428. )?;
  429. let create_reward_settings = if let Some(policy) = reward_policy {
  430. // A reward will need to be created so ensure our configured mint exists
  431. let mint_id = Self::mint();
  432. ensure!(<minting::Mints<T>>::exists(mint_id), MSG_FILL_WORKER_OPENING_MINT_DOES_NOT_EXIST);
  433. // Make sure valid parameters are selected for next payment at block number
  434. ensure!(policy.next_payment_at_block > <system::Module<T>>::block_number(), MSG_FILL_WORKER_OPENING_INVALID_NEXT_PAYMENT_BLOCK);
  435. // The verified reward settings to use
  436. Some((mint_id, policy))
  437. } else {
  438. None
  439. };
  440. // mutation
  441. let mut worker_application_id_to_worker_id = BTreeMap::new();
  442. successful_iter
  443. .clone()
  444. .for_each(|(successful_worker_application, id, _)| {
  445. // Create a reward relationship
  446. let reward_relationship = if let Some((mint_id, checked_policy)) = create_reward_settings.clone() {
  447. // Create a new recipient for the new relationship
  448. let recipient = <recurringrewards::Module<T>>::add_recipient();
  449. // member must exist, since it was checked that it can enter the role
  450. let member_profile = <membership::members::Module<T>>::member_profile(successful_worker_application.member_id).unwrap();
  451. // rewards are deposited in the member's root account
  452. let reward_destination_account = member_profile.root_account;
  453. // values have been checked so this should not fail!
  454. let relationship_id = <recurringrewards::Module<T>>::add_reward_relationship(
  455. mint_id,
  456. recipient,
  457. reward_destination_account,
  458. checked_policy.amount_per_payout,
  459. checked_policy.next_payment_at_block,
  460. checked_policy.payout_interval,
  461. ).expect("Failed to create reward relationship!");
  462. Some(relationship_id)
  463. } else {
  464. None
  465. };
  466. // Get possible stake for role
  467. let application = hiring::ApplicationById::<T>::get(successful_worker_application.application_id);
  468. // Staking profile for worker
  469. let stake_profile =
  470. if let Some(ref stake_id) = application.active_role_staking_id {
  471. Some(
  472. WorkerRoleStakeProfile::new(
  473. stake_id,
  474. &worker_opening.policy_commitment.terminate_worker_role_stake_unstaking_period,
  475. &worker_opening.policy_commitment.exit_worker_role_stake_unstaking_period
  476. )
  477. )
  478. } else {
  479. None
  480. };
  481. // Get worker id
  482. let new_worker_id = <NextWorkerId<T, I>>::get();
  483. // Construct worker
  484. let worker = Worker::new(
  485. &(successful_worker_application.role_account),
  486. &reward_relationship,
  487. &stake_profile,
  488. &WorkerRoleStage::Active,
  489. );
  490. // Store worker
  491. <WorkerById<T, I>>::insert(new_worker_id, worker);
  492. // Update next worker id
  493. <NextWorkerId<T, I>>::mutate(|id| *id += <WorkerId<T> as One>::one());
  494. worker_application_id_to_worker_id.insert(id, new_worker_id);
  495. });
  496. // Trigger event
  497. Self::deposit_event(RawEvent::WorkerOpeningFilled(worker_opening_id, worker_application_id_to_worker_id));
  498. }
  499. }
  500. }
  501. impl<Origin, T, I> EnsureOrigin<Origin> for Module<T, I>
  502. where
  503. Origin: Into<Result<RawOrigin<T::AccountId>, Origin>> + From<RawOrigin<T::AccountId>>,
  504. T: Trait<I>,
  505. I: Instance,
  506. {
  507. type Success = ();
  508. fn try_origin(o: Origin) -> Result<Self::Success, Origin> {
  509. o.into().and_then(|o| match o {
  510. RawOrigin::Signed(account_id) => {
  511. Self::ensure_is_lead_account(account_id).map_err(|_| RawOrigin::None.into())
  512. }
  513. _ => Err(RawOrigin::None.into()),
  514. })
  515. }
  516. }
  517. impl<T: Trait<I>, I: Instance> Module<T, I> {
  518. /// Checks that provided lead account id belongs to the current bureaucracy leader
  519. pub fn ensure_is_lead_account(lead_account_id: T::AccountId) -> Result<(), &'static str> {
  520. let lead = <CurrentLead<T, I>>::get();
  521. if let Some(lead) = lead {
  522. if lead.role_account_id != lead_account_id {
  523. return Err(MSG_IS_NOT_LEAD_ACCOUNT);
  524. }
  525. } else {
  526. return Err(MSG_CURRENT_LEAD_NOT_SET);
  527. }
  528. Ok(())
  529. }
  530. fn ensure_opening_human_readable_text_is_valid(text: &[u8]) -> dispatch::Result {
  531. <OpeningHumanReadableText<I>>::get().ensure_valid(
  532. text.len(),
  533. MSG_OPENING_TEXT_TOO_SHORT,
  534. MSG_OPENING_TEXT_TOO_LONG,
  535. )
  536. }
  537. fn ensure_origin_is_set_lead(origin: T::Origin) -> Result<(), &'static str> {
  538. // Ensure is signed
  539. let signer = ensure_signed(origin)?;
  540. Self::ensure_is_lead_account(signer)
  541. }
  542. fn ensure_worker_opening_exists(
  543. worker_opening_id: &WorkerOpeningId<T>,
  544. ) -> Result<WorkerOpeningInfo<T>, &'static str> {
  545. ensure!(
  546. WorkerOpeningById::<T, I>::exists(worker_opening_id),
  547. MSG_WORKER_OPENING_DOES_NOT_EXIST
  548. );
  549. let worker_opening = WorkerOpeningById::<T, I>::get(worker_opening_id);
  550. let opening = hiring::OpeningById::<T>::get(worker_opening.opening_id);
  551. Ok((worker_opening, opening))
  552. }
  553. fn make_stake_opt_imbalance(
  554. opt_balance: &Option<BalanceOf<T>>,
  555. source_account: &T::AccountId,
  556. ) -> Option<NegativeImbalance<T>> {
  557. if let Some(balance) = opt_balance {
  558. let withdraw_result = CurrencyOf::<T>::withdraw(
  559. source_account,
  560. *balance,
  561. WithdrawReasons::all(),
  562. ExistenceRequirement::AllowDeath,
  563. );
  564. assert!(withdraw_result.is_ok());
  565. withdraw_result.ok()
  566. } else {
  567. None
  568. }
  569. }
  570. fn ensure_member_has_no_active_application_on_opening(
  571. worker_applications: WorkerApplicationIdSet<T>,
  572. member_id: T::MemberId,
  573. ) -> Result<(), &'static str> {
  574. for worker_application_id in worker_applications {
  575. let worker_application = WorkerApplicationById::<T, I>::get(worker_application_id);
  576. // Look for application by the member for the opening
  577. if worker_application.member_id != member_id {
  578. continue;
  579. }
  580. // Get application details
  581. let application = <hiring::ApplicationById<T>>::get(worker_application.application_id);
  582. // Return error if application is in active stage
  583. if application.stage == hiring::ApplicationStage::Active {
  584. return Err(MSG_MEMBER_HAS_ACTIVE_APPLICATION_ON_OPENING);
  585. }
  586. }
  587. // Member does not have any active applications to the opening
  588. Ok(())
  589. }
  590. fn ensure_worker_application_text_is_valid(text: &[u8]) -> dispatch::Result {
  591. <WorkerApplicationHumanReadableText<I>>::get().ensure_valid(
  592. text.len(),
  593. MSG_WORKER_APPLICATION_TEXT_TOO_SHORT,
  594. MSG_WORKER_APPLICATION_TEXT_TOO_LONG,
  595. )
  596. }
  597. // CRITICAL:
  598. // https://github.com/Joystream/substrate-runtime-joystream/issues/92
  599. // This assumes that ensure_can_withdraw can be don
  600. // for a sum of balance that later will be actually withdrawn
  601. // using individual terms in that sum.
  602. // This needs to be fully checked across all possibly scenarios
  603. // of actual balance, minimum balance limit, reservation, vesting and locking.
  604. fn ensure_can_make_stake_imbalance(
  605. opt_balances: Vec<&Option<BalanceOf<T>>>,
  606. source_account: &T::AccountId,
  607. ) -> Result<(), &'static str> {
  608. let zero_balance = <BalanceOf<T> as Zero>::zero();
  609. // Total amount to be staked
  610. let total_amount = opt_balances.iter().fold(zero_balance, |sum, opt_balance| {
  611. sum + if let Some(balance) = opt_balance {
  612. *balance
  613. } else {
  614. zero_balance
  615. }
  616. });
  617. if total_amount > zero_balance {
  618. // Ensure that
  619. if CurrencyOf::<T>::free_balance(source_account) < total_amount {
  620. Err(MSG_INSUFFICIENT_BALANCE_TO_COVER_STAKE)
  621. } else {
  622. let new_balance = CurrencyOf::<T>::free_balance(source_account) - total_amount;
  623. CurrencyOf::<T>::ensure_can_withdraw(
  624. source_account,
  625. total_amount,
  626. WithdrawReasons::all(),
  627. new_balance,
  628. )
  629. }
  630. } else {
  631. Ok(())
  632. }
  633. }
  634. fn ensure_worker_application_exists(
  635. worker_application_id: &WorkerApplicationId<T>,
  636. ) -> Result<WorkerApplicationInfo<T>, &'static str> {
  637. ensure!(
  638. WorkerApplicationById::<T, I>::exists(worker_application_id),
  639. MSG_WORKER_APPLICATION_DOES_NOT_EXIST
  640. );
  641. let worker_application = WorkerApplicationById::<T, I>::get(worker_application_id);
  642. let worker_opening = WorkerOpeningById::<T, I>::get(worker_application.worker_opening_id);
  643. Ok((worker_application, *worker_application_id, worker_opening))
  644. }
  645. }