tests.rs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979
  1. #![cfg(test)]
  2. use super::*;
  3. use crate::mock::*;
  4. use frame_support::traits::OnFinalize;
  5. use frame_support::{assert_err, assert_ok};
  6. #[test]
  7. fn stake_pool_works() {
  8. build_test_externalities().execute_with(|| {
  9. // using deposit_creating
  10. assert_eq!(Balances::total_issuance(), 0);
  11. assert_eq!(StakePool::stake_pool_balance(), 0);
  12. // minimum balance (existential deposit) feature applies to stake pool
  13. if Balances::minimum_balance() > 0 {
  14. let pos_imbalance = Balances::deposit_creating(
  15. &StakePool::stake_pool_account_id(),
  16. Balances::minimum_balance() - 1,
  17. );
  18. assert_eq!(pos_imbalance.peek(), 0);
  19. assert_eq!(Balances::total_issuance(), 0);
  20. assert_eq!(StakePool::stake_pool_balance(), 0);
  21. }
  22. let starting_pool_balance = Balances::minimum_balance() + 1000;
  23. let _ =
  24. Balances::deposit_creating(&StakePool::stake_pool_account_id(), starting_pool_balance);
  25. assert_eq!(Balances::total_issuance(), starting_pool_balance);
  26. assert_eq!(StakePool::stake_pool_balance(), starting_pool_balance);
  27. let staker_starting_balance = Balances::minimum_balance() + 1000;
  28. // using transfer_funds_from_account_into_pool()
  29. let _ = Balances::deposit_creating(&1, staker_starting_balance);
  30. assert_eq!(
  31. Balances::total_issuance(),
  32. starting_pool_balance + staker_starting_balance
  33. );
  34. let funds = 100;
  35. assert_ok!(StakePool::transfer_funds_from_account_into_stake_pool(
  36. &1, funds
  37. ));
  38. // total issuance unchanged after movement of funds
  39. assert_eq!(
  40. Balances::total_issuance(),
  41. starting_pool_balance + staker_starting_balance
  42. );
  43. // funds moved into stake pool
  44. assert_eq!(
  45. StakePool::stake_pool_balance(),
  46. starting_pool_balance + funds
  47. );
  48. // no fees were deducted
  49. assert_eq!(Balances::free_balance(&1), staker_starting_balance - funds);
  50. StakePool::transfer_funds_from_pool_into_account(&1, funds);
  51. assert_eq!(Balances::free_balance(&1), staker_starting_balance);
  52. assert_eq!(StakePool::stake_pool_balance(), starting_pool_balance);
  53. });
  54. }
  55. #[test]
  56. fn create_stake() {
  57. build_test_externalities().execute_with(|| {
  58. let stake_id = StakePool::create_stake();
  59. assert_eq!(stake_id, 0);
  60. assert!(<Stakes<Test>>::contains_key(&stake_id));
  61. assert_eq!(StakePool::stakes_created(), stake_id + 1);
  62. // Should be NotStaked
  63. let stake = StakePool::stakes(&stake_id);
  64. assert_eq!(stake.staking_status, StakingStatus::NotStaked);
  65. });
  66. }
  67. #[test]
  68. fn remove_stake_in_not_staked_state() {
  69. build_test_externalities().execute_with(|| {
  70. <Stakes<Test>>::insert(
  71. &100,
  72. Stake {
  73. created: 0,
  74. staking_status: StakingStatus::NotStaked,
  75. },
  76. );
  77. assert_ok!(StakePool::remove_stake(&100));
  78. assert!(!<Stakes<Test>>::contains_key(&100));
  79. // when status is Staked, removing should fail
  80. <Stakes<Test>>::insert(
  81. &200,
  82. Stake {
  83. created: 0,
  84. staking_status: StakingStatus::Staked(Default::default()),
  85. },
  86. );
  87. assert_err!(
  88. StakePool::remove_stake(&200),
  89. StakeActionError::Error(StakingError::AlreadyStaked)
  90. );
  91. assert!(<Stakes<Test>>::contains_key(&200));
  92. });
  93. }
  94. #[test]
  95. fn enter_staked_state() {
  96. build_test_externalities().execute_with(|| {
  97. <Stakes<Test>>::insert(
  98. &100,
  99. Stake {
  100. created: 0,
  101. staking_status: StakingStatus::NotStaked,
  102. },
  103. );
  104. let starting_balance: u64 = Balances::minimum_balance();
  105. let staker_account: u64 = 1;
  106. let stake_value: u64 = Balances::minimum_balance() + 100;
  107. let _ = Balances::deposit_creating(&staker_account, starting_balance);
  108. // can't stake zero
  109. assert_err!(
  110. StakePool::stake_from_account(&100, &staker_account, 0),
  111. StakeActionError::Error(StakingFromAccountError::StakingError(
  112. StakingError::CannotStakeZero
  113. ))
  114. );
  115. // must stake at least the minimum balance
  116. if Balances::minimum_balance() > 0 {
  117. assert_err!(
  118. StakePool::stake_from_account(
  119. &100,
  120. &staker_account,
  121. Balances::minimum_balance() - 1
  122. ),
  123. StakeActionError::Error(StakingFromAccountError::StakingError(
  124. StakingError::CannotStakeLessThanMinimumBalance
  125. ))
  126. );
  127. }
  128. // cannot stake with insufficient funds
  129. assert_err!(
  130. StakePool::stake_from_account(&100, &staker_account, stake_value),
  131. StakeActionError::Error(StakingFromAccountError::InsufficientBalanceInSourceAccount)
  132. );
  133. // deposit exact amount to stake
  134. let _ = Balances::deposit_creating(&staker_account, stake_value);
  135. assert_ok!(StakePool::stake_from_account(
  136. &100,
  137. &staker_account,
  138. stake_value
  139. ));
  140. assert_eq!(Balances::free_balance(&staker_account), starting_balance);
  141. assert_eq!(StakePool::stake_pool_balance(), stake_value);
  142. });
  143. }
  144. #[test]
  145. fn increasing_stake() {
  146. build_test_externalities().execute_with(|| {
  147. let starting_pool_stake = Balances::minimum_balance() + 5000;
  148. let _ =
  149. Balances::deposit_creating(&StakePool::stake_pool_account_id(), starting_pool_stake);
  150. let starting_stake = Balances::minimum_balance() + 100;
  151. <Stakes<Test>>::insert(
  152. &100,
  153. Stake {
  154. created: 0,
  155. staking_status: StakingStatus::Staked(StakedState {
  156. staked_amount: starting_stake,
  157. ongoing_slashes: BTreeMap::new(),
  158. next_slash_id: 0,
  159. staked_status: StakedStatus::Normal,
  160. }),
  161. },
  162. );
  163. let additional_stake: u64 = 500;
  164. let starting_balance: u64 = Balances::minimum_balance() + additional_stake;
  165. let staker_account: u64 = 1;
  166. let _ = Balances::deposit_creating(&staker_account, starting_balance);
  167. assert_err!(
  168. StakePool::increase_stake_from_account(&100, &staker_account, 0),
  169. StakeActionError::Error(IncreasingStakeFromAccountError::IncreasingStakeError(
  170. IncreasingStakeError::CannotChangeStakeByZero
  171. ))
  172. );
  173. let total_staked =
  174. StakePool::increase_stake_from_account(&100, &staker_account, additional_stake)
  175. .ok()
  176. .unwrap();
  177. assert_eq!(total_staked, starting_stake + additional_stake);
  178. assert_eq!(
  179. Balances::free_balance(&staker_account),
  180. starting_balance - additional_stake
  181. );
  182. assert_eq!(
  183. StakePool::stake_pool_balance(),
  184. starting_pool_stake + additional_stake
  185. );
  186. assert_eq!(
  187. StakePool::stakes(&100),
  188. Stake {
  189. created: 0,
  190. staking_status: StakingStatus::Staked(StakedState {
  191. staked_amount: starting_stake + additional_stake,
  192. ongoing_slashes: BTreeMap::new(),
  193. next_slash_id: 0,
  194. staked_status: StakedStatus::Normal,
  195. })
  196. }
  197. );
  198. // cannot increase stake if insufficent balance
  199. assert!(StakePool::increase_stake_from_account(
  200. &100,
  201. &staker_account,
  202. Balances::free_balance(&staker_account) + 1
  203. )
  204. .is_err());
  205. });
  206. }
  207. #[test]
  208. fn decreasing_stake() {
  209. build_test_externalities().execute_with(|| {
  210. let starting_pool_stake = 5000;
  211. let _ =
  212. Balances::deposit_creating(&StakePool::stake_pool_account_id(), starting_pool_stake);
  213. let starting_stake = Balances::minimum_balance() + 2000;
  214. <Stakes<Test>>::insert(
  215. &100,
  216. Stake {
  217. created: 0,
  218. staking_status: StakingStatus::Staked(StakedState {
  219. staked_amount: starting_stake,
  220. ongoing_slashes: BTreeMap::new(),
  221. next_slash_id: 0,
  222. staked_status: StakedStatus::Normal,
  223. }),
  224. },
  225. );
  226. let starting_balance: u64 = Balances::minimum_balance();
  227. let staker_account: u64 = 1;
  228. let decrease_stake_by: u64 = 200;
  229. let _ = Balances::deposit_creating(&staker_account, starting_balance);
  230. assert_err!(
  231. StakePool::decrease_stake_to_account(&100, &staker_account, 0),
  232. StakeActionError::Error(DecreasingStakeError::CannotChangeStakeByZero)
  233. );
  234. let total_staked =
  235. StakePool::decrease_stake_to_account(&100, &staker_account, decrease_stake_by)
  236. .ok()
  237. .unwrap();
  238. assert_eq!(total_staked, starting_stake - decrease_stake_by);
  239. assert_eq!(
  240. Balances::free_balance(&staker_account),
  241. starting_balance + decrease_stake_by
  242. );
  243. assert_eq!(
  244. StakePool::stake_pool_balance(),
  245. starting_pool_stake - decrease_stake_by
  246. );
  247. assert_eq!(
  248. StakePool::stakes(&100),
  249. Stake {
  250. created: 0,
  251. staking_status: StakingStatus::Staked(StakedState {
  252. staked_amount: starting_stake - decrease_stake_by,
  253. ongoing_slashes: BTreeMap::new(),
  254. next_slash_id: 0,
  255. staked_status: StakedStatus::Normal,
  256. })
  257. }
  258. );
  259. // cannot unstake more than total at stake
  260. assert_err!(
  261. StakePool::decrease_stake_to_account(&100, &staker_account, total_staked + 1),
  262. StakeActionError::Error(DecreasingStakeError::InsufficientStake)
  263. );
  264. // decreasing stake to value less than minimum_balance should reduce entire stake
  265. if Balances::minimum_balance() > 0 {
  266. let over_minimum = 50;
  267. let staked_amount = Balances::minimum_balance() + over_minimum;
  268. let _ = Balances::deposit_creating(&StakePool::stake_pool_account_id(), staked_amount);
  269. <Stakes<Test>>::insert(
  270. &200,
  271. Stake {
  272. created: 0,
  273. staking_status: StakingStatus::Staked(StakedState {
  274. staked_amount,
  275. ongoing_slashes: BTreeMap::new(),
  276. next_slash_id: 0,
  277. staked_status: StakedStatus::Normal,
  278. }),
  279. },
  280. );
  281. assert_eq!(Balances::free_balance(&2), 0);
  282. let starting_pool_balance = StakePool::stake_pool_balance();
  283. let remaining_stake = StakePool::decrease_stake_to_account(&200, &2, over_minimum + 1)
  284. .ok()
  285. .unwrap();
  286. assert_eq!(remaining_stake, 0);
  287. assert_eq!(Balances::free_balance(&2), staked_amount);
  288. assert_eq!(
  289. StakePool::stake_pool_balance(),
  290. starting_pool_balance - staked_amount
  291. );
  292. }
  293. });
  294. }
  295. #[test]
  296. fn initiating_pausing_resuming_cancelling_slashes() {
  297. build_test_externalities().execute_with(|| {
  298. let staked_amount = Balances::minimum_balance() + 10000;
  299. let _ = Balances::deposit_creating(&StakePool::stake_pool_account_id(), staked_amount);
  300. assert_err!(
  301. StakePool::initiate_slashing(&100, 5000, 0),
  302. StakeActionError::StakeNotFound
  303. );
  304. let stake_id = StakePool::create_stake();
  305. <Stakes<Test>>::insert(
  306. &stake_id,
  307. Stake {
  308. created: System::block_number(),
  309. staking_status: StakingStatus::NotStaked,
  310. },
  311. );
  312. assert_err!(
  313. StakePool::initiate_slashing(&stake_id, 5000, 0),
  314. StakeActionError::Error(InitiateSlashingError::SlashPeriodShouldBeGreaterThanZero)
  315. );
  316. assert_err!(
  317. StakePool::initiate_slashing(&stake_id, 5000, 1),
  318. StakeActionError::Error(InitiateSlashingError::NotStaked)
  319. );
  320. <Stakes<Test>>::insert(
  321. &stake_id,
  322. Stake {
  323. created: System::block_number(),
  324. staking_status: StakingStatus::Staked(StakedState {
  325. staked_amount,
  326. ongoing_slashes: BTreeMap::new(),
  327. next_slash_id: 0,
  328. staked_status: StakedStatus::Unstaking(UnstakingState {
  329. started_at_block: 0,
  330. blocks_remaining_in_active_period_for_unstaking: 100,
  331. is_active: true,
  332. }),
  333. }),
  334. },
  335. );
  336. // assert_err!(StakePool::initiate_slashing(&stake_id, 0, 0), StakingError::ZeroSlashing);
  337. let mut slash_id = 0;
  338. assert!(StakePool::initiate_slashing(&stake_id, 5000, 10).is_ok());
  339. let mut expected_ongoing_slashes: fixtures::OngoingSlashes = BTreeMap::new();
  340. expected_ongoing_slashes.insert(
  341. slash_id,
  342. Slash {
  343. started_at_block: System::block_number(),
  344. is_active: true,
  345. blocks_remaining_in_active_period_for_slashing: 10,
  346. slash_amount: 5000,
  347. },
  348. );
  349. assert_eq!(
  350. StakePool::stakes(&stake_id),
  351. Stake {
  352. created: System::block_number(),
  353. staking_status: StakingStatus::Staked(StakedState {
  354. staked_amount,
  355. ongoing_slashes: expected_ongoing_slashes.clone(),
  356. next_slash_id: slash_id + 1,
  357. staked_status: StakedStatus::Unstaking(UnstakingState {
  358. started_at_block: 0,
  359. blocks_remaining_in_active_period_for_unstaking: 100,
  360. is_active: false,
  361. }),
  362. })
  363. }
  364. );
  365. assert_err!(
  366. StakePool::pause_slashing(&stake_id, &999),
  367. StakeActionError::Error(PauseSlashingError::SlashNotFound)
  368. );
  369. assert_err!(
  370. StakePool::pause_slashing(&999, &slash_id),
  371. StakeActionError::StakeNotFound
  372. );
  373. assert_ok!(StakePool::pause_slashing(&stake_id, &slash_id));
  374. expected_ongoing_slashes.insert(
  375. slash_id,
  376. Slash {
  377. started_at_block: System::block_number(),
  378. is_active: false,
  379. blocks_remaining_in_active_period_for_slashing: 10,
  380. slash_amount: 5000,
  381. },
  382. );
  383. assert_eq!(
  384. StakePool::stakes(&stake_id),
  385. Stake {
  386. created: System::block_number(),
  387. staking_status: StakingStatus::Staked(StakedState {
  388. staked_amount,
  389. ongoing_slashes: expected_ongoing_slashes.clone(),
  390. next_slash_id: slash_id + 1,
  391. staked_status: StakedStatus::Unstaking(UnstakingState {
  392. started_at_block: 0,
  393. blocks_remaining_in_active_period_for_unstaking: 100,
  394. is_active: false,
  395. }),
  396. })
  397. }
  398. );
  399. assert_err!(
  400. StakePool::resume_slashing(&stake_id, &999),
  401. StakeActionError::Error(ResumeSlashingError::SlashNotFound)
  402. );
  403. assert_err!(
  404. StakePool::resume_slashing(&999, &slash_id),
  405. StakeActionError::StakeNotFound
  406. );
  407. assert_ok!(StakePool::resume_slashing(&stake_id, &slash_id));
  408. expected_ongoing_slashes.insert(
  409. slash_id,
  410. Slash {
  411. started_at_block: System::block_number(),
  412. is_active: true,
  413. blocks_remaining_in_active_period_for_slashing: 10,
  414. slash_amount: 5000,
  415. },
  416. );
  417. assert_eq!(
  418. StakePool::stakes(&stake_id),
  419. Stake {
  420. created: System::block_number(),
  421. staking_status: StakingStatus::Staked(StakedState {
  422. staked_amount,
  423. ongoing_slashes: expected_ongoing_slashes.clone(),
  424. next_slash_id: slash_id + 1,
  425. staked_status: StakedStatus::Unstaking(UnstakingState {
  426. started_at_block: 0,
  427. blocks_remaining_in_active_period_for_unstaking: 100,
  428. is_active: false,
  429. }),
  430. })
  431. }
  432. );
  433. assert_err!(
  434. StakePool::cancel_slashing(&stake_id, &999),
  435. StakeActionError::Error(CancelSlashingError::SlashNotFound)
  436. );
  437. assert_err!(
  438. StakePool::cancel_slashing(&999, &slash_id),
  439. StakeActionError::StakeNotFound
  440. );
  441. assert_ok!(StakePool::cancel_slashing(&stake_id, &slash_id));
  442. assert_eq!(
  443. StakePool::stakes(&stake_id),
  444. Stake {
  445. created: System::block_number(),
  446. staking_status: StakingStatus::Staked(StakedState {
  447. staked_amount,
  448. ongoing_slashes: BTreeMap::new(),
  449. next_slash_id: slash_id + 1,
  450. staked_status: StakedStatus::Unstaking(UnstakingState {
  451. started_at_block: 0,
  452. blocks_remaining_in_active_period_for_unstaking: 100,
  453. is_active: true,
  454. }),
  455. })
  456. }
  457. );
  458. expected_ongoing_slashes = BTreeMap::new();
  459. let slashing_amount = 5000;
  460. slash_id += 1;
  461. assert!(StakePool::initiate_slashing(&stake_id, slashing_amount, 2).is_ok());
  462. StakePool::on_finalize(System::block_number());
  463. expected_ongoing_slashes.insert(
  464. slash_id,
  465. Slash {
  466. started_at_block: System::block_number(),
  467. is_active: true,
  468. blocks_remaining_in_active_period_for_slashing: 1,
  469. slash_amount: slashing_amount,
  470. },
  471. );
  472. assert_eq!(
  473. StakePool::stakes(&stake_id),
  474. Stake {
  475. created: System::block_number(),
  476. staking_status: StakingStatus::Staked(StakedState {
  477. staked_amount,
  478. ongoing_slashes: expected_ongoing_slashes.clone(),
  479. next_slash_id: slash_id + 1,
  480. staked_status: StakedStatus::Unstaking(UnstakingState {
  481. started_at_block: 0,
  482. blocks_remaining_in_active_period_for_unstaking: 100,
  483. is_active: false,
  484. }),
  485. })
  486. }
  487. );
  488. StakePool::on_finalize(System::block_number());
  489. assert_eq!(
  490. StakePool::stakes(&stake_id),
  491. Stake {
  492. created: System::block_number(),
  493. staking_status: StakingStatus::Staked(StakedState {
  494. staked_amount: staked_amount - slashing_amount,
  495. ongoing_slashes: BTreeMap::new(),
  496. next_slash_id: slash_id + 1,
  497. staked_status: StakedStatus::Unstaking(UnstakingState {
  498. started_at_block: 0,
  499. blocks_remaining_in_active_period_for_unstaking: 99,
  500. is_active: true
  501. })
  502. })
  503. }
  504. );
  505. assert_eq!(
  506. StakePool::stake_pool_balance(),
  507. staked_amount - slashing_amount
  508. );
  509. });
  510. }
  511. #[test]
  512. fn initiating_pausing_resuming_unstaking() {
  513. build_test_externalities().execute_with(|| {
  514. let staked_amount = Balances::minimum_balance() + 10000;
  515. let starting_stake_fund_balance = Balances::minimum_balance() + 3333;
  516. let _ = Balances::deposit_creating(
  517. &StakePool::stake_pool_account_id(),
  518. starting_stake_fund_balance + staked_amount,
  519. );
  520. assert_err!(
  521. StakePool::initiate_unstaking(&100, Some(1)),
  522. StakeActionError::StakeNotFound
  523. );
  524. let stake_id = StakePool::create_stake();
  525. <Stakes<Test>>::insert(
  526. &stake_id,
  527. Stake {
  528. created: System::block_number(),
  529. staking_status: StakingStatus::NotStaked,
  530. },
  531. );
  532. assert_err!(
  533. StakePool::initiate_unstaking(&stake_id, Some(0)),
  534. StakeActionError::Error(InitiateUnstakingError::UnstakingPeriodShouldBeGreaterThanZero)
  535. );
  536. assert_err!(
  537. StakePool::initiate_unstaking(&stake_id, Some(1)),
  538. StakeActionError::Error(InitiateUnstakingError::UnstakingError(
  539. UnstakingError::NotStaked
  540. ))
  541. );
  542. let mut ongoing_slashes = BTreeMap::new();
  543. ongoing_slashes.insert(
  544. 1,
  545. Slash {
  546. started_at_block: System::block_number(),
  547. is_active: true,
  548. blocks_remaining_in_active_period_for_slashing: 100,
  549. slash_amount: 100,
  550. },
  551. );
  552. <Stakes<Test>>::insert(
  553. &stake_id,
  554. Stake {
  555. created: System::block_number(),
  556. staking_status: StakingStatus::Staked(StakedState {
  557. staked_amount,
  558. ongoing_slashes,
  559. next_slash_id: 2,
  560. staked_status: StakedStatus::Normal,
  561. }),
  562. },
  563. );
  564. assert_err!(
  565. StakePool::initiate_unstaking(&stake_id, Some(1)),
  566. StakeActionError::Error(InitiateUnstakingError::UnstakingError(
  567. UnstakingError::CannotUnstakeWhileSlashesOngoing
  568. ))
  569. );
  570. assert_ok!(StakePool::cancel_slashing(&stake_id, &1));
  571. assert_ok!(StakePool::initiate_unstaking(&stake_id, Some(2)));
  572. assert_eq!(
  573. StakePool::stakes(&stake_id),
  574. Stake {
  575. created: System::block_number(),
  576. staking_status: StakingStatus::Staked(StakedState {
  577. staked_amount,
  578. ongoing_slashes: BTreeMap::new(),
  579. next_slash_id: 2,
  580. staked_status: StakedStatus::Unstaking(UnstakingState {
  581. started_at_block: System::block_number(),
  582. blocks_remaining_in_active_period_for_unstaking: 2,
  583. is_active: true
  584. })
  585. })
  586. }
  587. );
  588. StakePool::on_finalize(System::block_number());
  589. assert_eq!(
  590. StakePool::stakes(&stake_id),
  591. Stake {
  592. created: System::block_number(),
  593. staking_status: StakingStatus::Staked(StakedState {
  594. staked_amount,
  595. ongoing_slashes: BTreeMap::new(),
  596. next_slash_id: 2,
  597. staked_status: StakedStatus::Unstaking(UnstakingState {
  598. started_at_block: System::block_number(),
  599. blocks_remaining_in_active_period_for_unstaking: 1,
  600. is_active: true
  601. })
  602. })
  603. }
  604. );
  605. StakePool::finalize_slashing_and_unstaking();
  606. assert_eq!(
  607. StakePool::stakes(&stake_id),
  608. Stake {
  609. created: System::block_number(),
  610. staking_status: StakingStatus::NotStaked
  611. }
  612. );
  613. assert_eq!(StakePool::stake_pool_balance(), starting_stake_fund_balance);
  614. // unstaked amount is destroyed by StakingEventsHandler
  615. assert_eq!(Balances::total_issuance(), starting_stake_fund_balance);
  616. });
  617. }
  618. #[test]
  619. fn unstake() {
  620. build_test_externalities().execute_with(|| {
  621. assert_err!(
  622. StakePool::initiate_unstaking(&0, None),
  623. StakeActionError::StakeNotFound
  624. );
  625. let staked_amount = Balances::minimum_balance() + 10000;
  626. let starting_stake_fund_balance = Balances::minimum_balance() + 3333;
  627. let _ = Balances::deposit_creating(
  628. &StakePool::stake_pool_account_id(),
  629. starting_stake_fund_balance + staked_amount,
  630. );
  631. let stake_id = StakePool::create_stake();
  632. assert_err!(
  633. StakePool::initiate_unstaking(&stake_id, None),
  634. StakeActionError::Error(InitiateUnstakingError::UnstakingError(
  635. UnstakingError::NotStaked
  636. ))
  637. );
  638. let mut ongoing_slashes = BTreeMap::new();
  639. ongoing_slashes.insert(
  640. 1,
  641. Slash {
  642. started_at_block: System::block_number(),
  643. is_active: true,
  644. blocks_remaining_in_active_period_for_slashing: 100,
  645. slash_amount: 100,
  646. },
  647. );
  648. <Stakes<Test>>::insert(
  649. &stake_id,
  650. Stake {
  651. created: System::block_number(),
  652. staking_status: StakingStatus::Staked(StakedState {
  653. staked_amount,
  654. ongoing_slashes,
  655. next_slash_id: 0,
  656. staked_status: StakedStatus::Normal,
  657. }),
  658. },
  659. );
  660. assert_err!(
  661. StakePool::initiate_unstaking(&stake_id, None),
  662. StakeActionError::Error(InitiateUnstakingError::UnstakingError(
  663. UnstakingError::CannotUnstakeWhileSlashesOngoing
  664. ))
  665. );
  666. <Stakes<Test>>::insert(
  667. &stake_id,
  668. Stake {
  669. created: System::block_number(),
  670. staking_status: StakingStatus::Staked(StakedState {
  671. staked_amount,
  672. ongoing_slashes: BTreeMap::new(),
  673. next_slash_id: 0,
  674. staked_status: StakedStatus::Unstaking(UnstakingState {
  675. started_at_block: 0,
  676. blocks_remaining_in_active_period_for_unstaking: 100,
  677. is_active: true,
  678. }),
  679. }),
  680. },
  681. );
  682. assert_err!(
  683. StakePool::initiate_unstaking(&stake_id, None),
  684. StakeActionError::Error(InitiateUnstakingError::UnstakingError(
  685. UnstakingError::AlreadyUnstaking
  686. ))
  687. );
  688. <Stakes<Test>>::insert(
  689. &stake_id,
  690. Stake {
  691. created: System::block_number(),
  692. staking_status: StakingStatus::Staked(StakedState {
  693. staked_amount,
  694. ongoing_slashes: BTreeMap::new(),
  695. next_slash_id: 0,
  696. staked_status: StakedStatus::Normal,
  697. }),
  698. },
  699. );
  700. assert_ok!(StakePool::initiate_unstaking(&stake_id, None));
  701. assert_eq!(StakePool::stake_pool_balance(), starting_stake_fund_balance);
  702. });
  703. }
  704. #[test]
  705. fn immediate_slashing_cannot_slash_non_existent_stake() {
  706. build_test_externalities().execute_with(|| {
  707. let outcome = StakePool::slash_immediate(&100, 5000, false);
  708. assert!(outcome.is_err());
  709. let error = outcome.err().unwrap();
  710. assert_eq!(error, StakeActionError::StakeNotFound);
  711. });
  712. }
  713. #[test]
  714. fn immediate_slashing_without_unstaking() {
  715. build_test_externalities().execute_with(|| {
  716. const UNSTAKE_POLICY: bool = false;
  717. let staked_amount = Balances::minimum_balance() + 10000;
  718. let _ = Balances::deposit_creating(&StakePool::stake_pool_account_id(), staked_amount);
  719. let stake_id = StakePool::create_stake();
  720. let created_at = System::block_number();
  721. let initial_stake_state = Stake {
  722. created: created_at,
  723. staking_status: StakingStatus::Staked(StakedState {
  724. staked_amount,
  725. staked_status: StakedStatus::Normal,
  726. next_slash_id: 0,
  727. ongoing_slashes: BTreeMap::new(),
  728. }),
  729. };
  730. <Stakes<Test>>::insert(&stake_id, initial_stake_state);
  731. let slash_amount = 5000;
  732. let outcome = StakePool::slash_immediate(&stake_id, slash_amount, UNSTAKE_POLICY);
  733. assert!(outcome.is_ok());
  734. let outcome = outcome.ok().unwrap();
  735. assert_eq!(outcome.caused_unstake, false);
  736. assert_eq!(outcome.actually_slashed, slash_amount);
  737. assert_eq!(outcome.remaining_stake, staked_amount - slash_amount);
  738. // Default handler destroys imbalance
  739. assert_eq!(outcome.remaining_imbalance.peek(), 0);
  740. assert_eq!(
  741. <Stakes<Test>>::get(stake_id),
  742. Stake {
  743. created: created_at,
  744. staking_status: StakingStatus::Staked(StakedState {
  745. staked_amount: outcome.remaining_stake,
  746. staked_status: StakedStatus::Normal,
  747. next_slash_id: 0,
  748. ongoing_slashes: BTreeMap::new()
  749. }),
  750. }
  751. );
  752. // slash to zero but without asking to unstake
  753. // Slash and unstake by making slash go to zero
  754. let slash_amount = outcome.remaining_stake;
  755. let outcome = StakePool::slash_immediate(&stake_id, slash_amount, UNSTAKE_POLICY)
  756. .ok()
  757. .unwrap();
  758. assert_eq!(outcome.caused_unstake, false);
  759. assert_eq!(outcome.actually_slashed, slash_amount);
  760. assert_eq!(outcome.remaining_stake, 0);
  761. // Default handler destroys imbalance
  762. assert_eq!(outcome.remaining_imbalance.peek(), 0);
  763. // Should still be staked, even if staked amount = 0
  764. assert_eq!(
  765. <Stakes<Test>>::get(stake_id),
  766. Stake {
  767. created: created_at,
  768. staking_status: StakingStatus::Staked(StakedState {
  769. staked_amount: 0,
  770. staked_status: StakedStatus::Normal,
  771. next_slash_id: 0,
  772. ongoing_slashes: BTreeMap::new()
  773. }),
  774. }
  775. );
  776. });
  777. }
  778. #[test]
  779. fn immediate_slashing_with_unstaking() {
  780. build_test_externalities().execute_with(|| {
  781. const UNSTAKE_POLICY: bool = true;
  782. let staked_amount = Balances::minimum_balance() + 10000;
  783. let _ = Balances::deposit_creating(&StakePool::stake_pool_account_id(), staked_amount);
  784. let stake_id = StakePool::create_stake();
  785. let created_at = System::block_number();
  786. let initial_stake_state = Stake {
  787. created: created_at,
  788. staking_status: StakingStatus::Staked(StakedState {
  789. staked_amount,
  790. staked_status: StakedStatus::Normal,
  791. next_slash_id: 0,
  792. ongoing_slashes: BTreeMap::new(),
  793. }),
  794. };
  795. <Stakes<Test>>::insert(&stake_id, initial_stake_state);
  796. // Slash whole amount unstake by making slash go to zero
  797. let slash_amount = staked_amount;
  798. let outcome = StakePool::slash_immediate(&stake_id, slash_amount, UNSTAKE_POLICY)
  799. .ok()
  800. .unwrap();
  801. assert_eq!(outcome.caused_unstake, true);
  802. assert_eq!(outcome.actually_slashed, slash_amount);
  803. assert_eq!(outcome.remaining_stake, 0);
  804. // Default handler destroys imbalance
  805. assert_eq!(outcome.remaining_imbalance.peek(), 0);
  806. // Should now be unstaked
  807. assert_eq!(
  808. <Stakes<Test>>::get(stake_id),
  809. Stake {
  810. created: created_at,
  811. staking_status: StakingStatus::NotStaked
  812. }
  813. );
  814. });
  815. }
  816. #[test]
  817. fn immediate_slashing_cannot_slash_if_not_staked() {
  818. build_test_externalities().execute_with(|| {
  819. let stake_id = StakePool::create_stake();
  820. let created_at = System::block_number();
  821. let initial_stake_state = Stake {
  822. created: created_at,
  823. staking_status: StakingStatus::NotStaked,
  824. };
  825. <Stakes<Test>>::insert(&stake_id, initial_stake_state);
  826. let outcome = StakePool::slash_immediate(&stake_id, 1, false);
  827. let outcome_err = outcome.err().unwrap();
  828. assert_eq!(
  829. outcome_err,
  830. StakeActionError::Error(ImmediateSlashingError::NotStaked)
  831. );
  832. });
  833. }
  834. #[test]
  835. fn immediate_slashing_cannot_slash_zero() {
  836. build_test_externalities().execute_with(|| {
  837. let staked_amount = Balances::minimum_balance() + 10000;
  838. let _ = Balances::deposit_creating(&StakePool::stake_pool_account_id(), staked_amount);
  839. let stake_id = StakePool::create_stake();
  840. let created_at = System::block_number();
  841. let initial_stake_state = Stake {
  842. created: created_at,
  843. staking_status: StakingStatus::Staked(StakedState {
  844. staked_amount,
  845. staked_status: StakedStatus::Normal,
  846. next_slash_id: 0,
  847. ongoing_slashes: BTreeMap::new(),
  848. }),
  849. };
  850. <Stakes<Test>>::insert(&stake_id, initial_stake_state);
  851. const ZERO_SLASH_AMOUNT: u64 = 0;
  852. let outcome_err = StakePool::slash_immediate(&stake_id, ZERO_SLASH_AMOUNT, true)
  853. .err()
  854. .unwrap();
  855. assert_eq!(
  856. outcome_err,
  857. StakeActionError::Error(ImmediateSlashingError::SlashAmountShouldBeGreaterThanZero)
  858. );
  859. });
  860. }