SlashWorkerStakesFixture.ts 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. import BN from 'bn.js'
  2. import { assert } from 'chai'
  3. import { Api } from '../../Api'
  4. import { QueryNodeApi } from '../../QueryNodeApi'
  5. import { EventDetails, WorkingGroupModuleName } from '../../types'
  6. import { BaseWorkingGroupFixture } from './BaseWorkingGroupFixture'
  7. import { Worker, WorkerId } from '@joystream/types/working-group'
  8. import { SubmittableExtrinsic } from '@polkadot/api/types'
  9. import { ISubmittableResult } from '@polkadot/types/types/'
  10. import { Utils } from '../../utils'
  11. import { EventType } from '../../graphql/generated/schema'
  12. import { lockIdByWorkingGroup } from '../../consts'
  13. import { StakeSlashedEventFieldsFragment, WorkerFieldsFragment } from '../../graphql/generated/queries'
  14. export class SlashWorkerStakesFixture extends BaseWorkingGroupFixture {
  15. protected asSudo: boolean
  16. protected workerIds: WorkerId[]
  17. protected penalties: BN[]
  18. protected workers: Worker[] = []
  19. protected workerStakes: BN[] = []
  20. public constructor(
  21. api: Api,
  22. query: QueryNodeApi,
  23. group: WorkingGroupModuleName,
  24. workerIds: WorkerId[],
  25. penalties: BN[],
  26. asSudo = false
  27. ) {
  28. super(api, query, group)
  29. this.workerIds = workerIds
  30. this.penalties = penalties
  31. this.asSudo = asSudo
  32. }
  33. protected async loadWorkersData(): Promise<void> {
  34. this.workers = await this.api.query[this.group].workerById.multi<Worker>(this.workerIds)
  35. this.workerStakes = await Promise.all(
  36. this.workers.map((w) => this.api.getStakedBalance(w.staking_account_id, lockIdByWorkingGroup[this.group]))
  37. )
  38. }
  39. protected async getSignerAccountOrAccounts(): Promise<string> {
  40. return this.asSudo ? (await this.api.query.sudo.key()).toString() : await this.api.getLeadRoleKey(this.group)
  41. }
  42. protected async getExtrinsics(): Promise<SubmittableExtrinsic<'promise'>[]> {
  43. const extrinsics = this.workerIds.map((workerId, i) =>
  44. this.api.tx[this.group].slashStake(workerId, this.penalties[i], this.getRationale(workerId))
  45. )
  46. return this.asSudo ? extrinsics.map((tx) => this.api.tx.sudo.sudo(tx)) : extrinsics
  47. }
  48. protected getEventFromResult(result: ISubmittableResult): Promise<EventDetails> {
  49. return this.api.retrieveWorkingGroupsEventDetails(result, this.group, 'StakeSlashed')
  50. }
  51. public async execute(): Promise<void> {
  52. await this.loadWorkersData()
  53. await super.execute()
  54. }
  55. protected getRationale(workerId: WorkerId): string {
  56. return `Worker ${workerId.toString()} slashing rationale`
  57. }
  58. protected assertQueryNodeEventIsValid(qEvent: StakeSlashedEventFieldsFragment, i: number): void {
  59. assert.equal(qEvent.event.type, EventType.StakeSlashed)
  60. assert.equal(qEvent.worker.runtimeId, this.workerIds[i].toNumber())
  61. assert.equal(qEvent.group.name, this.group)
  62. assert.equal(qEvent.rationale, this.getRationale(this.workerIds[i]))
  63. assert.equal(qEvent.requestedAmount, this.penalties[i].toString())
  64. assert.equal(qEvent.slashedAmount, BN.min(this.penalties[i], this.workerStakes[i]))
  65. }
  66. protected assertQueriedWorkersAreValid(
  67. qEvents: StakeSlashedEventFieldsFragment[],
  68. qWorkers: WorkerFieldsFragment[]
  69. ): void {
  70. this.events.map((e, i) => {
  71. const qEvent = this.findMatchingQueryNodeEvent(e, qEvents)
  72. const workerId = this.workerIds[i]
  73. const worker = qWorkers.find((w) => w.runtimeId === workerId.toNumber())
  74. Utils.assert(worker, 'Query node: Worker not found!')
  75. assert.equal(worker.stake, BN.max(this.workerStakes[i].sub(this.penalties[i]), new BN(0)).toString())
  76. assert.include(
  77. worker.slashes.map((e) => e.id),
  78. qEvent.id
  79. )
  80. })
  81. }
  82. async runQueryNodeChecks(): Promise<void> {
  83. await super.runQueryNodeChecks()
  84. // Query and check the events
  85. const qEvents = await this.query.tryQueryWithTimeout(
  86. () => this.query.getStakeSlashedEvents(this.events),
  87. (qEvents) => this.assertQueryNodeEventsAreValid(qEvents)
  88. )
  89. // Check workers
  90. const qWorkers = await this.query.getWorkersByIds(this.workerIds, this.group)
  91. this.assertQueriedWorkersAreValid(qEvents, qWorkers)
  92. }
  93. }