benchmarking.rs 36 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127
  1. #![cfg(feature = "runtime-benchmarks")]
  2. use super::*;
  3. use core::convert::TryInto;
  4. use frame_benchmarking::{account, benchmarks_instance, Zero};
  5. use frame_support::traits::OnInitialize;
  6. use frame_system::EventRecord;
  7. use frame_system::Module as System;
  8. use frame_system::RawOrigin;
  9. use sp_runtime::traits::Bounded;
  10. use sp_std::prelude::*;
  11. use crate::types::StakeParameters;
  12. use crate::Module as WorkingGroup;
  13. use balances::Module as Balances;
  14. use membership::Module as Membership;
  15. const SEED: u32 = 0;
  16. const MAX_BYTES: u32 = 16384;
  17. fn assert_last_event<T: Trait<I>, I: Instance>(generic_event: <T as Trait<I>>::Event) {
  18. let events = System::<T>::events();
  19. let system_event: <T as frame_system::Trait>::Event = generic_event.into();
  20. // compare to the last event record
  21. let EventRecord { event, .. } = &events[events.len() - 1];
  22. assert_eq!(event, &system_event);
  23. }
  24. fn get_byte(num: u32, byte_number: u8) -> u8 {
  25. ((num & (0xff << (8 * byte_number))) >> 8 * byte_number) as u8
  26. }
  27. fn add_opening_helper<T: Trait<I>, I: Instance>(
  28. id: u32,
  29. add_opening_origin: &T::Origin,
  30. job_opening_type: &OpeningType,
  31. ) -> OpeningId {
  32. let staking_policy = StakePolicy {
  33. stake_amount: T::MinimumApplicationStake::get(),
  34. leaving_unstaking_period: T::MinUnstakingPeriodLimit::get() + One::one(),
  35. };
  36. WorkingGroup::<T, _>::add_opening(
  37. add_opening_origin.clone(),
  38. vec![],
  39. *job_opening_type,
  40. staking_policy,
  41. Some(One::one()),
  42. )
  43. .unwrap();
  44. let opening_id = id.into();
  45. assert!(
  46. OpeningById::<T, I>::contains_key(opening_id),
  47. "Opening not added"
  48. );
  49. opening_id
  50. }
  51. fn apply_on_opening_helper<T: Trait<I>, I: Instance>(
  52. id: u32,
  53. applicant_id: &T::AccountId,
  54. member_id: &T::MemberId,
  55. opening_id: &OpeningId,
  56. ) -> ApplicationId {
  57. let stake_parameters = StakeParameters {
  58. // Due to mock implementation of StakingHandler we can't go over 1000
  59. stake: T::MinimumApplicationStake::get(),
  60. staking_account_id: applicant_id.clone(),
  61. };
  62. WorkingGroup::<T, _>::apply_on_opening(
  63. RawOrigin::Signed(applicant_id.clone()).into(),
  64. ApplyOnOpeningParameters::<T> {
  65. member_id: *member_id,
  66. opening_id: *opening_id,
  67. role_account_id: applicant_id.clone(),
  68. reward_account_id: applicant_id.clone(),
  69. description: vec![],
  70. stake_parameters,
  71. },
  72. )
  73. .unwrap();
  74. let application_id = id.into();
  75. assert!(
  76. ApplicationById::<T, I>::contains_key(application_id),
  77. "Application not added"
  78. );
  79. application_id
  80. }
  81. fn add_opening_and_apply_with_multiple_ids<T: Trait<I> + membership::Trait, I: Instance>(
  82. ids: &Vec<u32>,
  83. add_opening_origin: &T::Origin,
  84. job_opening_type: &OpeningType,
  85. ) -> (OpeningId, BTreeSet<ApplicationId>, Vec<T::AccountId>) {
  86. let opening_id = add_opening_helper::<T, I>(1, add_opening_origin, job_opening_type);
  87. let mut successful_application_ids = BTreeSet::new();
  88. let mut account_ids = Vec::new();
  89. for id in ids.iter() {
  90. let (applicant_account_id, applicant_member_id) =
  91. member_funded_account::<T, I>("member", *id);
  92. let application_id = apply_on_opening_helper::<T, I>(
  93. *id,
  94. &applicant_account_id,
  95. &applicant_member_id,
  96. &opening_id,
  97. );
  98. successful_application_ids.insert(application_id);
  99. account_ids.push(applicant_account_id);
  100. }
  101. (opening_id, successful_application_ids, account_ids)
  102. }
  103. fn add_and_apply_opening<T: Trait<I>, I: Instance>(
  104. id: u32,
  105. add_opening_origin: &T::Origin,
  106. applicant_id: &T::AccountId,
  107. member_id: &T::MemberId,
  108. job_opening_type: &OpeningType,
  109. ) -> (OpeningId, ApplicationId) {
  110. let opening_id = add_opening_helper::<T, I>(id, add_opening_origin, job_opening_type);
  111. let application_id = apply_on_opening_helper::<T, I>(id, applicant_id, member_id, &opening_id);
  112. (opening_id, application_id)
  113. }
  114. // Method to generate a distintic valid handle
  115. // for a membership. For each index.
  116. fn handle_from_id<T: membership::Trait>(id: u32) -> Vec<u8> {
  117. let min_handle_length = 1;
  118. let mut handle = vec![];
  119. for i in 0..4 {
  120. handle.push(get_byte(id, i));
  121. }
  122. while handle.len() < (min_handle_length as usize) {
  123. handle.push(0u8);
  124. }
  125. handle
  126. }
  127. fn member_funded_account<T: Trait<I> + membership::Trait, I: Instance>(
  128. name: &'static str,
  129. id: u32,
  130. ) -> (T::AccountId, T::MemberId) {
  131. let account_id = account::<T::AccountId>(name, id, SEED);
  132. let handle = handle_from_id::<T>(id);
  133. let _ = Balances::<T>::make_free_balance_be(&account_id, BalanceOf::<T>::max_value());
  134. let params = membership::BuyMembershipParameters {
  135. root_account: account_id.clone(),
  136. controller_account: account_id.clone(),
  137. handle: Some(handle),
  138. metadata: Vec::new(),
  139. referrer_id: None,
  140. };
  141. Membership::<T>::buy_membership(RawOrigin::Signed(account_id.clone()).into(), params).unwrap();
  142. let _ = Balances::<T>::make_free_balance_be(&account_id, BalanceOf::<T>::max_value());
  143. let member_id = T::MemberId::from(id.try_into().unwrap());
  144. Membership::<T>::add_staking_account_candidate(
  145. RawOrigin::Signed(account_id.clone()).into(),
  146. member_id.clone(),
  147. )
  148. .unwrap();
  149. Membership::<T>::confirm_staking_account(
  150. RawOrigin::Signed(account_id.clone()).into(),
  151. member_id.clone(),
  152. account_id.clone(),
  153. )
  154. .unwrap();
  155. (account_id, member_id)
  156. }
  157. fn force_missed_reward<T: Trait<I>, I: Instance>() {
  158. let curr_block_number =
  159. System::<T>::block_number().saturating_add(T::RewardPeriod::get().into());
  160. System::<T>::set_block_number(curr_block_number);
  161. WorkingGroup::<T, _>::set_budget(RawOrigin::Root.into(), Zero::zero()).unwrap();
  162. WorkingGroup::<T, _>::on_initialize(curr_block_number);
  163. }
  164. pub fn insert_a_worker<T: Trait<I> + membership::Trait, I: Instance>(
  165. job_opening_type: OpeningType,
  166. id: u32,
  167. lead_id: Option<T::AccountId>,
  168. ) -> (T::AccountId, WorkerId<T>)
  169. where
  170. WorkingGroup<T, I>: OnInitialize<T::BlockNumber>,
  171. {
  172. let (caller_id, member_id) = member_funded_account::<T, I>("member", id);
  173. let worker_id = complete_opening::<T, I>(job_opening_type, id, lead_id, &caller_id, member_id);
  174. (caller_id, worker_id)
  175. }
  176. pub fn complete_opening<T: Trait<I> + membership::Trait, I: Instance>(
  177. job_opening_type: OpeningType,
  178. id: u32,
  179. lead_id: Option<T::AccountId>,
  180. caller_id: &T::AccountId,
  181. member_id: T::MemberId,
  182. ) -> WorkerId<T> {
  183. let add_worker_origin = match job_opening_type {
  184. OpeningType::Leader => RawOrigin::Root,
  185. OpeningType::Regular => RawOrigin::Signed(lead_id.clone().unwrap()),
  186. };
  187. let (opening_id, application_id) = add_and_apply_opening::<T, I>(
  188. id,
  189. &T::Origin::from(add_worker_origin.clone()),
  190. caller_id,
  191. &member_id,
  192. &job_opening_type,
  193. );
  194. let mut successful_application_ids = BTreeSet::<ApplicationId>::new();
  195. successful_application_ids.insert(application_id);
  196. WorkingGroup::<T, _>::fill_opening(
  197. add_worker_origin.clone().into(),
  198. opening_id,
  199. successful_application_ids,
  200. )
  201. .unwrap();
  202. // Every worst case either include or doesn't mind having a non-zero
  203. // remaining reward
  204. force_missed_reward::<T, I>();
  205. let worker_id = WorkerId::<T>::from(id.try_into().unwrap());
  206. assert!(WorkerById::<T, I>::contains_key(worker_id));
  207. worker_id
  208. }
  209. benchmarks_instance! {
  210. where_clause {
  211. where T: membership::Trait
  212. }
  213. _ { }
  214. on_initialize_leaving {
  215. let i in 2 .. T::MaxWorkerNumberLimit::get();
  216. let (lead_id, lead_worker_id) = insert_a_worker::<T, I>(
  217. OpeningType::Leader,
  218. 0,
  219. None
  220. );
  221. let (opening_id, successful_application_ids, application_account_id) =
  222. add_opening_and_apply_with_multiple_ids::<T, I>(
  223. &(1..i).collect(),
  224. &T::Origin::from(RawOrigin::Signed(lead_id.clone())),
  225. &OpeningType::Regular
  226. );
  227. WorkingGroup::<T, I>::fill_opening(
  228. RawOrigin::Signed(lead_id.clone()).into(),
  229. opening_id,
  230. successful_application_ids.clone()
  231. ).unwrap();
  232. force_missed_reward::<T,I>();
  233. // Force all workers to leave (Including the lead)
  234. // We should have every WorkerId from 0 to i-1
  235. // Corresponding to each account id
  236. let mut worker_id = Zero::zero();
  237. for id in application_account_id {
  238. worker_id += One::one();
  239. WorkingGroup::<T, _>::leave_role(
  240. RawOrigin::Signed(id).into(),
  241. worker_id,
  242. Some(vec![0u8]),
  243. ).unwrap();
  244. }
  245. // Worst case scenario one of the leaving workers is the lead
  246. WorkingGroup::<T, _>::leave_role(
  247. RawOrigin::Signed(lead_id).into(),
  248. lead_worker_id,
  249. Some(vec![0u8]),
  250. ).unwrap();
  251. for i in 1..successful_application_ids.len() {
  252. let worker = WorkerId::<T>::from(i.try_into().unwrap());
  253. assert!(WorkerById::<T, I>::contains_key(worker), "Not all workers added");
  254. assert_eq!(
  255. WorkingGroup::<T, _>::worker_by_id(worker).started_leaving_at,
  256. Some(System::<T>::block_number()),
  257. "Worker hasn't started leaving"
  258. );
  259. }
  260. // Maintain consistency with add_opening_helper
  261. let leaving_unstaking_period = T::MinUnstakingPeriodLimit::get() + One::one();
  262. // Force unstaking period to have passed
  263. let curr_block_number =
  264. System::<T>::block_number().saturating_add(leaving_unstaking_period.into());
  265. System::<T>::set_block_number(curr_block_number);
  266. WorkingGroup::<T, _>::set_budget(
  267. RawOrigin::Root.into(),
  268. BalanceOf::<T>::max_value()
  269. ).unwrap();
  270. assert_eq!(WorkingGroup::<T, _>::budget(), BalanceOf::<T>::max_value());
  271. }: { WorkingGroup::<T, _>::on_initialize(curr_block_number) }
  272. verify {
  273. WorkerById::<T, I>::iter().for_each(|(worker_id, _)| {
  274. assert!(!WorkerById::<T, I>::contains_key(worker_id), "Worker hasn't left");
  275. });
  276. let reward_per_worker = BalanceOf::<T>::from(T::RewardPeriod::get());
  277. assert_eq!(
  278. WorkingGroup::<T, I>::budget(),
  279. BalanceOf::<T>::max_value()
  280. .saturating_sub(BalanceOf::<T>::from(i) * reward_per_worker)
  281. .saturating_sub(reward_per_worker),
  282. "Budget wasn't correctly updated, probably not all workers rewarded"
  283. );
  284. }
  285. on_initialize_rewarding_with_missing_reward {
  286. let i in 2 .. T::MaxWorkerNumberLimit::get();
  287. let (lead_id, _) = insert_a_worker::<T, I>(
  288. OpeningType::Leader,
  289. 0,
  290. None
  291. );
  292. let (opening_id, successful_application_ids, _) =
  293. add_opening_and_apply_with_multiple_ids::<T, I>(
  294. &(1..i).collect(),
  295. &T::Origin::from(RawOrigin::Signed(lead_id.clone())),
  296. &OpeningType::Regular
  297. );
  298. WorkingGroup::<T, _>::fill_opening(RawOrigin::Signed(lead_id.clone()).into(), opening_id,
  299. successful_application_ids.clone()).unwrap();
  300. for i in 1..successful_application_ids.len() {
  301. assert!(
  302. WorkerById::<T, I>::contains_key(WorkerId::<T>::from(i.try_into().unwrap())),
  303. "Not all workers added"
  304. );
  305. }
  306. // Worst case scenario there is a missing reward
  307. force_missed_reward::<T, I>();
  308. // Sets periods so that we can reward
  309. let curr_block_number =
  310. System::<T>::block_number().saturating_add(T::RewardPeriod::get().into());
  311. System::<T>::set_block_number(curr_block_number);
  312. // Sets budget so that we can pay it
  313. WorkingGroup::<T, _>::set_budget(
  314. RawOrigin::Root.into(),
  315. BalanceOf::<T>::max_value()
  316. ).unwrap();
  317. assert_eq!(WorkingGroup::<T, _>::budget(), BalanceOf::<T>::max_value());
  318. }: { WorkingGroup::<T, _>::on_initialize(curr_block_number) }
  319. verify {
  320. let reward_per_worker = BalanceOf::<T>::from(T::RewardPeriod::get());
  321. let reward_for_workers =
  322. BalanceOf::<T>::from(i) * reward_per_worker * BalanceOf::<T>::from(2u32);
  323. assert_eq!(
  324. WorkingGroup::<T, _>::budget(),
  325. // When creating a worker using `insert_a_worker` it gives the lead a number of block
  326. // equating to reward period as missed reward(and the reward value is 1) therefore the
  327. // additional discount of balance
  328. BalanceOf::<T>::max_value()
  329. .saturating_sub(reward_for_workers)
  330. .saturating_sub(reward_per_worker),
  331. "Budget wasn't correctly updated, probably not all workers rewarded"
  332. );
  333. }
  334. on_initialize_rewarding_with_missing_reward_cant_pay {
  335. let i in 2 .. T::MaxWorkerNumberLimit::get();
  336. let (lead_id, _) = insert_a_worker::<T, I>(
  337. OpeningType::Leader,
  338. 0,
  339. None
  340. );
  341. let (opening_id, successful_application_ids, _) =
  342. add_opening_and_apply_with_multiple_ids::<T, I>(
  343. &(1..i).collect(),
  344. &T::Origin::from(RawOrigin::Signed(lead_id.clone())),
  345. &OpeningType::Regular
  346. );
  347. WorkingGroup::<T, _>::fill_opening(RawOrigin::Signed(lead_id.clone()).into(), opening_id,
  348. successful_application_ids.clone()).unwrap();
  349. for i in 1..successful_application_ids.len() {
  350. assert!(
  351. WorkerById::<T, I>::contains_key(WorkerId::<T>::from(i.try_into().unwrap())),
  352. "Not all workers added"
  353. );
  354. }
  355. // Sets periods so that we can reward
  356. let curr_block_number =
  357. System::<T>::block_number().saturating_add(T::RewardPeriod::get().into());
  358. System::<T>::set_block_number(curr_block_number);
  359. // Sets budget so that we can't pay it
  360. WorkingGroup::<T, _>::set_budget(RawOrigin::Root.into(), Zero::zero()).unwrap();
  361. assert_eq!(WorkingGroup::<T, _>::budget(), Zero::zero());
  362. }: { WorkingGroup::<T, _>::on_initialize(curr_block_number) }
  363. verify {
  364. WorkerById::<T, I>::iter().for_each(|(_, worker)| {
  365. let missed_reward = worker.missed_reward.expect("There should be some missed reward");
  366. assert!(
  367. missed_reward >= BalanceOf::<T>::from(T::RewardPeriod::get()),
  368. "At least one worker wasn't rewarded"
  369. );
  370. });
  371. }
  372. on_initialize_rewarding_without_missing_reward {
  373. let i in 2 .. T::MaxWorkerNumberLimit::get();
  374. let (lead_id, _) = insert_a_worker::<T, I>(
  375. OpeningType::Leader,
  376. 0,
  377. None
  378. );
  379. let (opening_id, successful_application_ids, _) =
  380. add_opening_and_apply_with_multiple_ids::<T, I>(
  381. &(1..i).collect(),
  382. &T::Origin::from(RawOrigin::Signed(lead_id.clone())),
  383. &OpeningType::Regular
  384. );
  385. WorkingGroup::<T, _>::fill_opening(RawOrigin::Signed(lead_id.clone()).into(), opening_id,
  386. successful_application_ids.clone()).unwrap();
  387. for i in 1..successful_application_ids.len() {
  388. assert!(
  389. WorkerById::<T, I>::contains_key(WorkerId::<T>::from(i.try_into().unwrap())),
  390. "Not all workers added"
  391. );
  392. }
  393. // Sets periods so that we can reward
  394. let curr_block_number =
  395. System::<T>::block_number().saturating_add(T::RewardPeriod::get().into());
  396. System::<T>::set_block_number(curr_block_number);
  397. // Sets budget so that we can pay it
  398. WorkingGroup::<T, _>::set_budget(
  399. RawOrigin::Root.into(), BalanceOf::<T>::max_value()
  400. ).unwrap();
  401. assert_eq!(WorkingGroup::<T, _>::budget(), BalanceOf::<T>::max_value());
  402. }: { WorkingGroup::<T, _>::on_initialize(curr_block_number) }
  403. verify {
  404. let reward_per_worker = BalanceOf::<T>::from(T::RewardPeriod::get());
  405. let workers_total_reward = BalanceOf::<T>::from(i) * reward_per_worker;
  406. assert_eq!(
  407. WorkingGroup::<T, _>::budget(),
  408. // When creating a worker using `insert_a_worker` it gives the lead a number of block
  409. // equating to reward period as missed reward(and the reward value is 1) therefore the
  410. // additional discount of balance
  411. BalanceOf::<T>::max_value()
  412. .saturating_sub(workers_total_reward)
  413. .saturating_sub(reward_per_worker),
  414. "Budget wasn't correctly updated, probably not all workers rewarded"
  415. );
  416. }
  417. apply_on_opening {
  418. let i in 1 .. MAX_BYTES;
  419. let (lead_account_id, lead_member_id) = member_funded_account::<T, I>("lead", 0);
  420. let opening_id = add_opening_helper::<T, I>(
  421. 0,
  422. &T::Origin::from(RawOrigin::Root),
  423. &OpeningType::Leader
  424. );
  425. let apply_on_opening_params = ApplyOnOpeningParameters::<T> {
  426. member_id: lead_member_id,
  427. opening_id: opening_id.clone(),
  428. role_account_id: lead_account_id.clone(),
  429. reward_account_id: lead_account_id.clone(),
  430. description: vec![0u8; i.try_into().unwrap()],
  431. stake_parameters:
  432. StakeParameters {
  433. stake: T::MinimumApplicationStake::get(),
  434. staking_account_id: lead_account_id.clone(),
  435. }
  436. };
  437. }: _ (RawOrigin::Signed(lead_account_id.clone()), apply_on_opening_params.clone())
  438. verify {
  439. assert!(
  440. ApplicationById::<T, I>::contains_key(0),
  441. "Application not found"
  442. );
  443. assert_last_event::<T, I>(
  444. RawEvent::AppliedOnOpening(apply_on_opening_params, Zero::zero()).into()
  445. );
  446. }
  447. fill_opening_lead {
  448. let (lead_account_id, lead_member_id) = member_funded_account::<T, I>("lead", 0);
  449. let (opening_id, application_id) = add_and_apply_opening::<T, I>(
  450. 0,
  451. &RawOrigin::Root.into(),
  452. &lead_account_id,
  453. &lead_member_id,
  454. &OpeningType::Leader
  455. );
  456. let mut successful_application_ids: BTreeSet<ApplicationId> = BTreeSet::new();
  457. successful_application_ids.insert(application_id);
  458. }: fill_opening(RawOrigin::Root, opening_id, successful_application_ids.clone())
  459. verify {
  460. assert!(!OpeningById::<T, I>::contains_key(opening_id), "Opening still not filled");
  461. let worker_id = Zero::zero();
  462. assert_eq!(
  463. WorkingGroup::<T, I>::current_lead(),
  464. Some(worker_id),
  465. "Opening for lead not filled"
  466. );
  467. let mut application_id_to_worker_id = BTreeMap::new();
  468. application_id_to_worker_id.insert(application_id, worker_id);
  469. assert_last_event::<T, I>(RawEvent::OpeningFilled(
  470. opening_id,
  471. application_id_to_worker_id,
  472. successful_application_ids
  473. ).into()
  474. );
  475. }
  476. fill_opening_worker {
  477. let i in 1 .. T::MaxWorkerNumberLimit::get() - 1;
  478. let (lead_id, lead_worker_id) = insert_a_worker::<T, I>(
  479. OpeningType::Leader,
  480. 0,
  481. None
  482. );
  483. let (opening_id, successful_application_ids, _) =
  484. add_opening_and_apply_with_multiple_ids::<T, I>(
  485. &(1..i+1).collect(),
  486. &T::Origin::from(RawOrigin::Signed(lead_id.clone())),
  487. &OpeningType::Regular
  488. );
  489. }: fill_opening(
  490. RawOrigin::Signed(lead_id.clone()),
  491. opening_id,
  492. successful_application_ids.clone()
  493. )
  494. verify {
  495. assert!(!OpeningById::<T, I>::contains_key(opening_id), "Opening still not filled");
  496. let mut application_id_to_worker_id = BTreeMap::new();
  497. for (i, application_id) in successful_application_ids.iter().enumerate() {
  498. let worker_id = WorkerId::<T>::from((i + 1).try_into().unwrap());
  499. application_id_to_worker_id.insert(*application_id, worker_id);
  500. assert!(
  501. WorkerById::<T, I>::contains_key(WorkerId::<T>::from(i.try_into().unwrap())),
  502. "Not all workers added"
  503. );
  504. }
  505. assert_last_event::<T, I>(RawEvent::OpeningFilled(
  506. opening_id,
  507. application_id_to_worker_id,
  508. successful_application_ids
  509. ).into()
  510. );
  511. }
  512. update_role_account{
  513. let (lead_id, lead_worker_id) =
  514. insert_a_worker::<T, I>(OpeningType::Leader, 0, None);
  515. let new_account_id = account::<T::AccountId>("new_lead_account", 1, SEED);
  516. }: _ (RawOrigin::Signed(lead_id), lead_worker_id, new_account_id.clone())
  517. verify {
  518. assert_eq!(
  519. WorkingGroup::<T, I>::worker_by_id(lead_worker_id).role_account_id,
  520. new_account_id,
  521. "Role account notupdated"
  522. );
  523. assert_last_event::<T, I>(
  524. RawEvent::WorkerRoleAccountUpdated(lead_worker_id, new_account_id).into()
  525. );
  526. }
  527. cancel_opening {
  528. let (lead_id, _) =
  529. insert_a_worker::<T, I>(OpeningType::Leader, 0, None);
  530. let opening_id = add_opening_helper::<T, I>(
  531. 1,
  532. &T::Origin::from(RawOrigin::Signed(lead_id.clone())),
  533. &OpeningType::Regular
  534. );
  535. }: _ (RawOrigin::Signed(lead_id.clone()), opening_id)
  536. verify {
  537. assert!(!OpeningById::<T, I>::contains_key(opening_id), "Opening not removed");
  538. assert_last_event::<T, I>(RawEvent::OpeningCanceled(opening_id).into());
  539. }
  540. withdraw_application {
  541. let (caller_id, member_id) = member_funded_account::<T, I>("lead", 0);
  542. let (_, application_id) = add_and_apply_opening::<T, I>(0,
  543. &RawOrigin::Root.into(),
  544. &caller_id,
  545. &member_id,
  546. &OpeningType::Leader
  547. );
  548. }: _ (RawOrigin::Signed(caller_id.clone()), application_id)
  549. verify {
  550. assert!(!ApplicationById::<T, I>::contains_key(application_id), "Application not removed");
  551. assert_last_event::<T, I>(RawEvent::ApplicationWithdrawn(application_id).into());
  552. }
  553. // Regular worker is the worst case scenario since the checks
  554. // require access to the storage whilist that's not the case with a lead opening
  555. slash_stake {
  556. let i in 0 .. MAX_BYTES;
  557. let (lead_id, lead_worker_id) =
  558. insert_a_worker::<T, I>(OpeningType::Leader, 0, None);
  559. let (caller_id, worker_id) = insert_a_worker::<T, I>(
  560. OpeningType::Regular,
  561. 1,
  562. Some(lead_id.clone())
  563. );
  564. let slashing_amount = One::one();
  565. let rationale = Some(vec![0u8; i.try_into().unwrap()]);
  566. }: _(
  567. RawOrigin::Signed(lead_id.clone()),
  568. worker_id,
  569. slashing_amount,
  570. rationale.clone()
  571. )
  572. verify {
  573. assert_last_event::<T, I>(RawEvent::StakeSlashed(
  574. worker_id,
  575. slashing_amount,
  576. slashing_amount,
  577. rationale
  578. ).into()
  579. );
  580. }
  581. terminate_role_worker {
  582. let i in 0 .. MAX_BYTES;
  583. let (lead_id, _) =
  584. insert_a_worker::<T, I>(OpeningType::Leader, 0, None);
  585. let (caller_id, worker_id) = insert_a_worker::<T, I>(
  586. OpeningType::Regular,
  587. 1,
  588. Some(lead_id.clone())
  589. );
  590. // To be able to pay unpaid reward
  591. let current_budget = BalanceOf::<T>::max_value();
  592. WorkingGroup::<T, _>::set_budget(RawOrigin::Root.into(), current_budget).unwrap();
  593. let penalty = Some(One::one());
  594. let rationale = Some(vec![0u8; i.try_into().unwrap()]);
  595. }: terminate_role(
  596. RawOrigin::Signed(lead_id.clone()),
  597. worker_id,
  598. penalty,
  599. rationale.clone()
  600. )
  601. verify {
  602. assert!(!WorkerById::<T, I>::contains_key(worker_id), "Worker not terminated");
  603. assert_last_event::<T, I>(RawEvent::TerminatedWorker(worker_id, penalty, rationale).into());
  604. }
  605. terminate_role_lead {
  606. let i in 0 .. MAX_BYTES;
  607. let (_, lead_worker_id) =
  608. insert_a_worker::<T, I>(OpeningType::Leader, 0, None);
  609. let current_budget = BalanceOf::<T>::max_value();
  610. // To be able to pay unpaid reward
  611. WorkingGroup::<T, _>::set_budget(RawOrigin::Root.into(), current_budget).unwrap();
  612. let penalty = Some(One::one());
  613. let rationale = Some(vec![0u8; i.try_into().unwrap()]);
  614. }: terminate_role(
  615. RawOrigin::Root,
  616. lead_worker_id,
  617. penalty,
  618. rationale.clone()
  619. )
  620. verify {
  621. assert!(!WorkerById::<T, I>::contains_key(lead_worker_id), "Worker not terminated");
  622. assert_last_event::<T, I>(
  623. RawEvent::TerminatedLeader(lead_worker_id, penalty, rationale).into()
  624. );
  625. }
  626. // Regular worker is the worst case scenario since the checks
  627. // require access to the storage whilist that's not the case with a lead opening
  628. increase_stake {
  629. let (lead_id, _) =
  630. insert_a_worker::<T, I>(OpeningType::Leader, 0, None);
  631. let (caller_id, worker_id) = insert_a_worker::<T, I>(
  632. OpeningType::Regular,
  633. 1,
  634. Some(lead_id.clone())
  635. );
  636. let old_stake = One::one();
  637. WorkingGroup::<T, _>::decrease_stake(
  638. RawOrigin::Signed(lead_id.clone()).into(), worker_id.clone(), old_stake).unwrap();
  639. let new_stake = old_stake + One::one();
  640. }: _ (RawOrigin::Signed(caller_id.clone()), worker_id.clone(), new_stake)
  641. verify {
  642. assert_last_event::<T, I>(RawEvent::StakeIncreased(worker_id, new_stake).into());
  643. }
  644. // Regular worker is the worst case scenario since the checks
  645. // require access to the storage whilist that's not the case with a lead opening
  646. decrease_stake {
  647. let (lead_id, _) =
  648. insert_a_worker::<T, I>(OpeningType::Leader, 0, None);
  649. let (_, worker_id) = insert_a_worker::<T, I>(
  650. OpeningType::Regular,
  651. 1,
  652. Some(lead_id.clone())
  653. );
  654. // I'm assuming that we will usually have MaxBalance > 1
  655. let new_stake = One::one();
  656. }: _ (RawOrigin::Signed(lead_id), worker_id, new_stake)
  657. verify {
  658. assert_last_event::<T, I>(RawEvent::StakeDecreased(worker_id, new_stake).into());
  659. }
  660. spend_from_budget {
  661. let (lead_id, _) = insert_a_worker::<T, I>(
  662. OpeningType::Leader,
  663. 0,
  664. None
  665. );
  666. let current_budget = BalanceOf::<T>::max_value();
  667. WorkingGroup::<T, _>::set_budget(RawOrigin::Root.into(), current_budget).unwrap();
  668. }: _ (RawOrigin::Signed(lead_id.clone()), lead_id.clone(), current_budget, None)
  669. verify {
  670. assert_eq!(WorkingGroup::<T, I>::budget(), Zero::zero(), "Budget not updated");
  671. assert_last_event::<T, I>(RawEvent::BudgetSpending(lead_id, current_budget, None).into());
  672. }
  673. // Regular worker is the worst case scenario since the checks
  674. // require access to the storage whilist that's not the case with a lead opening
  675. update_reward_amount {
  676. let (lead_id, _) =
  677. insert_a_worker::<T, I>(OpeningType::Leader, 0, None);
  678. let (_, worker_id) = insert_a_worker::<T, I>(
  679. OpeningType::Regular,
  680. 1,
  681. Some(lead_id.clone())
  682. );
  683. let new_reward = Some(BalanceOf::<T>::max_value());
  684. }: _ (RawOrigin::Signed(lead_id.clone()), worker_id, new_reward)
  685. verify {
  686. assert_eq!(
  687. WorkingGroup::<T, I>::worker_by_id(worker_id).reward_per_block,
  688. new_reward,
  689. "Reward not updated"
  690. );
  691. assert_last_event::<T, I>(
  692. RawEvent::WorkerRewardAmountUpdated(worker_id, new_reward).into()
  693. );
  694. }
  695. set_status_text {
  696. let i in 0 .. MAX_BYTES;
  697. let (lead_id, _) =
  698. insert_a_worker::<T, I>(OpeningType::Leader, 0, None);
  699. let status_text = Some(vec![0u8; i.try_into().unwrap()]);
  700. }: _ (RawOrigin::Signed(lead_id), status_text.clone())
  701. verify {
  702. let status_text_hash = T::Hashing::hash(&status_text.clone().unwrap()).as_ref().to_vec();
  703. assert_eq!(
  704. WorkingGroup::<T, I>::status_text_hash(),
  705. status_text_hash,
  706. "Status text not updated"
  707. );
  708. assert_last_event::<T, I>(
  709. RawEvent::StatusTextChanged(status_text_hash, status_text).into()
  710. );
  711. }
  712. update_reward_account {
  713. let (caller_id, worker_id) =
  714. insert_a_worker::<T, I>(OpeningType::Leader, 0, None);
  715. let new_id = account::<T::AccountId>("new_id", 1, 0);
  716. }: _ (RawOrigin::Signed(caller_id), worker_id, new_id.clone())
  717. verify {
  718. assert_eq!(
  719. WorkingGroup::<T, I>::worker_by_id(worker_id).reward_account_id,
  720. new_id,
  721. "Reward account not updated"
  722. );
  723. assert_last_event::<T, I>(RawEvent::WorkerRewardAccountUpdated(worker_id, new_id).into());
  724. }
  725. set_budget {
  726. let new_budget = BalanceOf::<T>::max_value();
  727. }: _(RawOrigin::Root, new_budget)
  728. verify {
  729. assert_eq!(WorkingGroup::<T, I>::budget(), new_budget, "Budget isn't updated");
  730. assert_last_event::<T, I>(RawEvent::BudgetSet(new_budget).into());
  731. }
  732. // Regular opening is the worst case scenario since the checks
  733. // require access to the storage whilist that's not the case with a lead opening
  734. add_opening {
  735. let i in 0 .. MAX_BYTES;
  736. let (lead_id, _) =
  737. insert_a_worker::<T, I>(OpeningType::Leader, 0, None);
  738. let stake_policy = StakePolicy {
  739. stake_amount: BalanceOf::<T>::max_value(),
  740. leaving_unstaking_period: T::BlockNumber::max_value(),
  741. };
  742. let description = vec![0u8; i.try_into().unwrap()];
  743. }: _(
  744. RawOrigin::Signed(lead_id),
  745. description.clone(),
  746. OpeningType::Regular,
  747. stake_policy.clone(),
  748. Some(BalanceOf::<T>::max_value())
  749. )
  750. verify {
  751. assert!(OpeningById::<T, I>::contains_key(1));
  752. assert_last_event::<T, I>(RawEvent::OpeningAdded(
  753. 1,
  754. description,
  755. OpeningType::Regular,
  756. stake_policy,
  757. Some(BalanceOf::<T>::max_value())
  758. ).into()
  759. );
  760. }
  761. leave_role {
  762. let i in 0 .. MAX_BYTES;
  763. // Workers with stake can't leave immediatly
  764. let (caller_id, caller_worker_id) = insert_a_worker::<T, I>(
  765. OpeningType::Leader,
  766. 0,
  767. None
  768. );
  769. }: leave_role(
  770. RawOrigin::Signed(caller_id),
  771. caller_worker_id,
  772. Some(vec![0u8; i.try_into().unwrap()])
  773. )
  774. verify {
  775. assert_eq!(
  776. WorkingGroup::<T, _>::worker_by_id(caller_worker_id).started_leaving_at,
  777. Some(System::<T>::block_number()),
  778. "Worker hasn't started leaving"
  779. );
  780. }
  781. lead_remark {
  782. let (caller_id, _) = insert_a_worker::<T, I>(
  783. OpeningType::Leader,
  784. 0,
  785. None
  786. );
  787. let msg = b"test".to_vec();
  788. }: _ (RawOrigin::Signed(caller_id), msg.clone())
  789. verify {
  790. assert_last_event::<T, I>(RawEvent::LeadRemarked(msg).into());
  791. }
  792. worker_remark {
  793. let (lead_id, _) =
  794. insert_a_worker::<T, I>(OpeningType::Leader, 0, None);
  795. let (caller_id, worker_id) = insert_a_worker::<T, I>(
  796. OpeningType::Regular,
  797. 1,
  798. Some(lead_id.clone()));
  799. let msg = b"test".to_vec();
  800. }: _ (RawOrigin::Signed(caller_id), worker_id, msg.clone())
  801. verify {
  802. assert_last_event::<T, I>(RawEvent::WorkerRemarked(worker_id, msg).into());
  803. }
  804. }
  805. #[cfg(test)]
  806. mod tests {
  807. use super::*;
  808. use crate::tests::{build_test_externalities, Test};
  809. use frame_support::assert_ok;
  810. #[test]
  811. fn test_leave_role() {
  812. build_test_externalities().execute_with(|| {
  813. assert_ok!(test_benchmark_leave_role::<Test>());
  814. });
  815. }
  816. #[test]
  817. fn test_add_opening() {
  818. build_test_externalities().execute_with(|| {
  819. assert_ok!(test_benchmark_add_opening::<Test>());
  820. });
  821. }
  822. #[test]
  823. fn test_set_budget() {
  824. build_test_externalities().execute_with(|| {
  825. assert_ok!(test_benchmark_set_budget::<Test>());
  826. });
  827. }
  828. #[test]
  829. fn test_update_reward_account() {
  830. build_test_externalities().execute_with(|| {
  831. assert_ok!(test_benchmark_update_reward_account::<Test>());
  832. });
  833. }
  834. #[test]
  835. fn test_set_status_text() {
  836. build_test_externalities().execute_with(|| {
  837. assert_ok!(test_benchmark_set_status_text::<Test>());
  838. });
  839. }
  840. #[test]
  841. fn test_update_reward_amount() {
  842. build_test_externalities().execute_with(|| {
  843. assert_ok!(test_benchmark_update_reward_amount::<Test>());
  844. });
  845. }
  846. #[test]
  847. fn test_spend_from_budget() {
  848. build_test_externalities().execute_with(|| {
  849. assert_ok!(test_benchmark_spend_from_budget::<Test>());
  850. });
  851. }
  852. #[test]
  853. fn test_decrease_stake() {
  854. build_test_externalities().execute_with(|| {
  855. assert_ok!(test_benchmark_decrease_stake::<Test>());
  856. });
  857. }
  858. #[test]
  859. fn test_increase_stake() {
  860. build_test_externalities().execute_with(|| {
  861. assert_ok!(test_benchmark_increase_stake::<Test>());
  862. });
  863. }
  864. #[test]
  865. fn test_terminate_role_lead() {
  866. build_test_externalities().execute_with(|| {
  867. assert_ok!(test_benchmark_terminate_role_lead::<Test>());
  868. });
  869. }
  870. #[test]
  871. fn test_terminate_role_worker() {
  872. build_test_externalities().execute_with(|| {
  873. assert_ok!(test_benchmark_terminate_role_worker::<Test>());
  874. });
  875. }
  876. #[test]
  877. fn test_slash_stake() {
  878. build_test_externalities().execute_with(|| {
  879. assert_ok!(test_benchmark_slash_stake::<Test>());
  880. });
  881. }
  882. #[test]
  883. fn test_withdraw_application() {
  884. build_test_externalities().execute_with(|| {
  885. assert_ok!(test_benchmark_withdraw_application::<Test>());
  886. });
  887. }
  888. #[test]
  889. fn test_cancel_opening() {
  890. build_test_externalities().execute_with(|| {
  891. assert_ok!(test_benchmark_cancel_opening::<Test>());
  892. });
  893. }
  894. #[test]
  895. fn test_update_role_account() {
  896. build_test_externalities().execute_with(|| {
  897. assert_ok!(test_benchmark_update_role_account::<Test>());
  898. });
  899. }
  900. #[test]
  901. fn test_fill_opening_worker() {
  902. build_test_externalities().execute_with(|| {
  903. assert_ok!(test_benchmark_fill_opening_worker::<Test>());
  904. });
  905. }
  906. #[test]
  907. fn test_fill_opening_lead() {
  908. build_test_externalities().execute_with(|| {
  909. assert_ok!(test_benchmark_fill_opening_lead::<Test>());
  910. });
  911. }
  912. #[test]
  913. fn test_apply_on_opening() {
  914. build_test_externalities().execute_with(|| {
  915. assert_ok!(test_benchmark_apply_on_opening::<Test>());
  916. });
  917. }
  918. #[test]
  919. fn test_on_inintialize_rewarding_without_missing_reward() {
  920. build_test_externalities().execute_with(|| {
  921. assert_ok!(test_benchmark_on_initialize_rewarding_without_missing_reward::<Test>());
  922. });
  923. }
  924. #[test]
  925. fn test_on_inintialize_rewarding_with_missing_reward_cant_pay() {
  926. build_test_externalities().execute_with(|| {
  927. assert_ok!(
  928. test_benchmark_on_initialize_rewarding_with_missing_reward_cant_pay::<Test>()
  929. );
  930. });
  931. }
  932. #[test]
  933. fn test_on_inintialize_rewarding_with_missing_reward() {
  934. build_test_externalities().execute_with(|| {
  935. assert_ok!(test_benchmark_on_initialize_rewarding_with_missing_reward::<Test>());
  936. });
  937. }
  938. #[test]
  939. fn test_on_inintialize_leaving() {
  940. build_test_externalities().execute_with(|| {
  941. assert_ok!(test_benchmark_on_initialize_leaving::<Test>());
  942. });
  943. }
  944. #[test]
  945. fn test_lead_remark() {
  946. build_test_externalities().execute_with(|| {
  947. assert_ok!(test_benchmark_lead_remark::<Test>());
  948. });
  949. }
  950. #[test]
  951. fn test_worker_remark() {
  952. build_test_externalities().execute_with(|| {
  953. assert_ok!(test_benchmark_worker_remark::<Test>());
  954. });
  955. }
  956. }