diff --git a/tests/network-tests/package.json b/tests/network-tests/package.json index b5b77b23ef..acac02ad75 100644 --- a/tests/network-tests/package.json +++ b/tests/network-tests/package.json @@ -7,7 +7,7 @@ "test": "tap --files src/nicaea/tests/proposals/*Test.ts --files src/nicaea/tests/workingGroup/*Test.ts -T", "test-migration-constantinople": "tap --files src/rome/tests/romeRuntimeUpgradeTest.ts --files src/constantinople/tests/electingCouncilTest.ts -T", "test-migration-nicaea": "tap --files src/constantinople/tests/proposals/updateRuntimeTest.ts --files src/nicaea/tests/electingCouncilTest.ts -T", - "debug": "tap --files src/nicaea/tests/workingGroup/*Test.ts -T", + "debug": "tap --files src/iznik/tests/workingGroup/*Test.ts -T", "lint": "eslint . --quiet --ext .ts", "checks": "yarn lint && tsc --noEmit --pretty && prettier ./ --check", "format": "prettier ./ --write " diff --git a/tests/network-tests/src/iznik/dto/fillOpeningParameters.ts b/tests/network-tests/src/iznik/dto/fillOpeningParameters.ts new file mode 100644 index 0000000000..e3fb7bf50f --- /dev/null +++ b/tests/network-tests/src/iznik/dto/fillOpeningParameters.ts @@ -0,0 +1,85 @@ +import BN from 'bn.js' + +export class FillOpeningParameters { + private amountPerPayout!: BN + private nextPaymentAtBlock!: BN + private payoutInterval!: BN + private openingId!: BN + private successfulApplicationId!: BN + private workingGroup!: string + + public getAmountPerPayout(): BN { + return this.amountPerPayout + } + + public getNextPaymentAtBlock(): BN { + return this.nextPaymentAtBlock + } + + public getPayoutInterval(): BN { + return this.payoutInterval + } + + public getOpeningId(): BN { + return this.openingId + } + + public getSuccessfulApplicationId(): BN { + return this.successfulApplicationId + } + + public getWorkingGroup(): string { + return this.workingGroup + } + + public setAmountPerPayout(value: BN): FillOpeningParameters { + this.amountPerPayout = value + return this + } + + public setNextPaymentAtBlock(value: BN): FillOpeningParameters { + this.nextPaymentAtBlock = value + return this + } + + public setPayoutInterval(value: BN): FillOpeningParameters { + this.payoutInterval = value + return this + } + + public setOpeningId(value: BN): FillOpeningParameters { + this.openingId = value + return this + } + + public setSuccessfulApplicationId(value: BN): FillOpeningParameters { + this.successfulApplicationId = value + return this + } + + public setWorkingGroup(value: string): FillOpeningParameters { + this.workingGroup = value + return this + } + + constructor() { + return + } + + public getRewardPolicy() { + return { + amount_per_payout: this.amountPerPayout, + next_payment_at_block: this.nextPaymentAtBlock, + payout_interval: this.payoutInterval, + } + } + + public getFillOpeningParameters() { + return { + opening_id: this.openingId, + successful_application_id: this.successfulApplicationId, + reward_policy: this.getRewardPolicy(), + working_group: this.workingGroup, + } + } +} diff --git a/tests/network-tests/src/iznik/dto/workingGroupOpening.ts b/tests/network-tests/src/iznik/dto/workingGroupOpening.ts new file mode 100644 index 0000000000..b0474e62d4 --- /dev/null +++ b/tests/network-tests/src/iznik/dto/workingGroupOpening.ts @@ -0,0 +1,255 @@ +import BN from 'bn.js' + +export class WorkingGroupOpening { + private activateAtBlock: BN | undefined + private maxActiveApplicants!: BN + private maxReviewPeriodLength!: BN + private applicationStakingPolicyAmount!: BN + private applicationCrowdedOutUnstakingPeriodLength!: BN + private applicationExpiredUnstakingPeriodLength!: BN + private roleStakingPolicyAmount!: BN + private roleCrowdedOutUnstakingPeriodLength!: BN + private roleExpiredUnstakingPeriodLength!: BN + private slashableMaxCount!: BN + private slashableMaxPercentPtsPerTime!: BN + private successfulApplicantApplicationStakeUnstakingPeriod!: BN + private failedApplicantApplicationStakeUnstakingPeriod!: BN + private failedApplicantRoleStakeUnstakingPeriod!: BN + private terminateApplicationStakeUnstakingPeriod!: BN + private terminateRoleStakeUnstakingPeriod!: BN + private exitRoleApplicationStakeUnstakingPeriod!: BN + private exitRoleStakeUnstakingPeriod!: BN + private text!: string + private openingType!: string + + public getActivateAtBlock(): BN | undefined { + return this.activateAtBlock + } + + public getMaxActiveApplicants(): BN { + return this.maxActiveApplicants + } + + public getMaxReviewPeriodLength(): BN { + return this.maxReviewPeriodLength + } + + public getApplicationStakingPolicyAmount(): BN { + return this.applicationStakingPolicyAmount + } + + public getApplicationCrowdedOutUnstakingPeriodLength(): BN { + return this.applicationCrowdedOutUnstakingPeriodLength + } + + public getApplicationExpiredUnstakingPeriodLength(): BN { + return this.applicationExpiredUnstakingPeriodLength + } + + public getRoleStakingPolicyAmount(): BN { + return this.roleStakingPolicyAmount + } + + public getRoleCrowdedOutUnstakingPeriodLength(): BN { + return this.roleCrowdedOutUnstakingPeriodLength + } + + public getRoleExpiredUnstakingPeriodLength(): BN { + return this.roleExpiredUnstakingPeriodLength + } + + public getSlashableMaxCount(): BN { + return this.slashableMaxCount + } + + public getSlashableMaxPercentPtsPerTime(): BN { + return this.slashableMaxPercentPtsPerTime + } + + public getSuccessfulApplicantApplicationStakeUnstakingPeriod(): BN { + return this.successfulApplicantApplicationStakeUnstakingPeriod + } + + public getFailedApplicantApplicationStakeUnstakingPeriod(): BN { + return this.failedApplicantApplicationStakeUnstakingPeriod + } + + public getFailedApplicantRoleStakeUnstakingPeriod(): BN { + return this.failedApplicantRoleStakeUnstakingPeriod + } + + public getTerminateApplicationStakeUnstakingPeriod(): BN { + return this.terminateApplicationStakeUnstakingPeriod + } + + public getTerminateRoleStakeUnstakingPeriod(): BN { + return this.terminateRoleStakeUnstakingPeriod + } + + public getExitRoleApplicationStakeUnstakingPeriod(): BN { + return this.exitRoleApplicationStakeUnstakingPeriod + } + + public getExitRoleStakeUnstakingPeriod(): BN { + return this.exitRoleStakeUnstakingPeriod + } + + public getText(): string { + return this.text + } + + public getOpeningType(): string { + return this.openingType + } + + public setActivateAtBlock(value: BN | undefined): WorkingGroupOpening { + this.activateAtBlock = value + return this + } + + public setMaxActiveApplicants(value: BN): WorkingGroupOpening { + this.maxActiveApplicants = value + return this + } + + public setMaxReviewPeriodLength(value: BN): WorkingGroupOpening { + this.maxReviewPeriodLength = value + return this + } + + public setApplicationStakingPolicyAmount(value: BN): WorkingGroupOpening { + this.applicationStakingPolicyAmount = value + return this + } + + public setApplicationCrowdedOutUnstakingPeriodLength(value: BN): WorkingGroupOpening { + this.applicationCrowdedOutUnstakingPeriodLength = value + return this + } + + public setApplicationExpiredUnstakingPeriodLength(value: BN): WorkingGroupOpening { + this.applicationExpiredUnstakingPeriodLength = value + return this + } + + public setRoleStakingPolicyAmount(value: BN): WorkingGroupOpening { + this.roleStakingPolicyAmount = value + return this + } + + public setRoleCrowdedOutUnstakingPeriodLength(value: BN): WorkingGroupOpening { + this.roleCrowdedOutUnstakingPeriodLength = value + return this + } + + public setRoleExpiredUnstakingPeriodLength(value: BN): WorkingGroupOpening { + this.roleExpiredUnstakingPeriodLength = value + return this + } + + public setSlashableMaxCount(value: BN): WorkingGroupOpening { + this.slashableMaxCount = value + return this + } + + public setSlashableMaxPercentPtsPerTime(value: BN): WorkingGroupOpening { + this.slashableMaxPercentPtsPerTime = value + return this + } + + public setSuccessfulApplicantApplicationStakeUnstakingPeriod(value: BN): WorkingGroupOpening { + this.successfulApplicantApplicationStakeUnstakingPeriod = value + return this + } + + public setFailedApplicantApplicationStakeUnstakingPeriod(value: BN): WorkingGroupOpening { + this.failedApplicantApplicationStakeUnstakingPeriod = value + return this + } + + public setFailedApplicantRoleStakeUnstakingPeriod(value: BN): WorkingGroupOpening { + this.failedApplicantRoleStakeUnstakingPeriod = value + return this + } + + public setTerminateApplicationStakeUnstakingPeriod(value: BN): WorkingGroupOpening { + this.terminateApplicationStakeUnstakingPeriod = value + return this + } + + public setTerminateRoleStakeUnstakingPeriod(value: BN): WorkingGroupOpening { + this.terminateRoleStakeUnstakingPeriod = value + return this + } + + public setExitRoleApplicationStakeUnstakingPeriod(value: BN): WorkingGroupOpening { + this.exitRoleApplicationStakeUnstakingPeriod = value + return this + } + + public setExitRoleStakeUnstakingPeriod(value: BN): WorkingGroupOpening { + this.exitRoleStakeUnstakingPeriod = value + return this + } + + public setText(value: string): WorkingGroupOpening { + this.text = value + return this + } + + public setOpeningType(value: string): WorkingGroupOpening { + this.openingType = value + return this + } + + constructor() { + return + } + + public getActivateAt() { + return this.activateAtBlock === undefined ? 'CurrentBlock' : { ExactBlock: this.activateAtBlock } + } + + public getCommitment() { + return { + 'application_rationing_policy': { 'max_active_applicants': this.maxActiveApplicants }, + 'max_review_period_length': this.maxReviewPeriodLength, + 'application_staking_policy': { + 'amount': this.applicationStakingPolicyAmount, + 'amount_mode': 'AtLeast', + 'crowded_out_unstaking_period_length': this.applicationCrowdedOutUnstakingPeriodLength, + 'review_period_expired_unstaking_period_length': this.applicationExpiredUnstakingPeriodLength, + }, + 'role_staking_policy': { + 'amount': this.roleStakingPolicyAmount, + 'amount_mode': 'AtLeast', + 'crowded_out_unstaking_period_length': this.roleCrowdedOutUnstakingPeriodLength, + 'review_period_expired_unstaking_period_length': this.roleExpiredUnstakingPeriodLength, + }, + 'role_slashing_terms': { + 'Slashable': { + 'max_count': this.slashableMaxCount, + 'max_percent_pts_per_time': this.slashableMaxPercentPtsPerTime, + }, + }, + 'fill_opening_successful_applicant_application_stake_unstaking_period': this + .successfulApplicantApplicationStakeUnstakingPeriod, + 'fill_opening_failed_applicant_application_stake_unstaking_period': this + .failedApplicantApplicationStakeUnstakingPeriod, + 'fill_opening_failed_applicant_role_stake_unstaking_period': this.failedApplicantRoleStakeUnstakingPeriod, + 'terminate_application_stake_unstaking_period': this.terminateApplicationStakeUnstakingPeriod, + 'terminate_role_stake_unstaking_period': this.terminateRoleStakeUnstakingPeriod, + 'exit_role_application_stake_unstaking_period': this.exitRoleApplicationStakeUnstakingPeriod, + 'exit_role_stake_unstaking_period': this.exitRoleStakeUnstakingPeriod, + } + } + + public getAddOpeningParameters(workingGroup: string) { + return { + activate_at: this.getActivateAt(), + commitment: this.getCommitment(), + human_readable_text: this.getText(), + working_group: workingGroup, + } + } +} diff --git a/tests/network-tests/src/iznik/tap-parallel-not-ok b/tests/network-tests/src/iznik/tap-parallel-not-ok new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/network-tests/src/iznik/tests/council/electingCouncilTest.ts b/tests/network-tests/src/iznik/tests/council/electingCouncilTest.ts new file mode 100644 index 0000000000..a888c6cc2f --- /dev/null +++ b/tests/network-tests/src/iznik/tests/council/electingCouncilTest.ts @@ -0,0 +1,65 @@ +import { KeyringPair } from '@polkadot/keyring/types' +import { initConfig } from '../../utils/config' +import { Keyring, WsProvider } from '@polkadot/api' +import { setTestTimeout } from '../../utils/setTestTimeout' +import BN from 'bn.js' +import tap from 'tap' +import { registerJoystreamTypes } from '@nicaea/types' +import { ApiWrapper } from '../../utils/apiWrapper' +import { closeApi } from '../../utils/closeApi' +import { BuyMembershipHappyCaseFixture } from '../fixtures/membershipModule' +import { ElectCouncilFixture } from '../fixtures/councilElectionModule' +import { Utils } from '../../utils/utils' + +tap.mocha.describe('Electing council scenario', async () => { + initConfig() + registerJoystreamTypes() + + const nodeUrl: string = process.env.NODE_URL! + const sudoUri: string = process.env.SUDO_ACCOUNT_URI! + const keyring = new Keyring({ type: 'sr25519' }) + const provider = new WsProvider(nodeUrl) + const apiWrapper: ApiWrapper = await ApiWrapper.create(provider) + const sudo: KeyringPair = keyring.addFromUri(sudoUri) + + const N: number = +process.env.MEMBERSHIP_CREATION_N! + const m1KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N) + const m2KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N) + const paidTerms: number = +process.env.MEMBERSHIP_PAID_TERMS! + const K: number = +process.env.COUNCIL_ELECTION_K! + const greaterStake: BN = new BN(+process.env.COUNCIL_STAKE_GREATER_AMOUNT!) + const lesserStake: BN = new BN(+process.env.COUNCIL_STAKE_LESSER_AMOUNT!) + + const durationInBlocks = 25 + + setTestTimeout(apiWrapper, durationInBlocks) + + const firstMemberSetFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + m1KeyPairs, + paidTerms + ) + tap.test('Creating first set of members', async () => firstMemberSetFixture.runner(false)) + + const secondMemberSetFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + m2KeyPairs, + paidTerms + ) + tap.test('Creating second set of members', async () => secondMemberSetFixture.runner(false)) + + const electCouncilFixture: ElectCouncilFixture = new ElectCouncilFixture( + apiWrapper, + m1KeyPairs, + m2KeyPairs, + K, + sudo, + greaterStake, + lesserStake + ) + tap.test('Elect council', async () => electCouncilFixture.runner(false)) + + closeApi(apiWrapper) +}) diff --git a/tests/network-tests/src/iznik/tests/fixtures/councilElectionModule.ts b/tests/network-tests/src/iznik/tests/fixtures/councilElectionModule.ts new file mode 100644 index 0000000000..067ac6c390 --- /dev/null +++ b/tests/network-tests/src/iznik/tests/fixtures/councilElectionModule.ts @@ -0,0 +1,140 @@ +import { ApiWrapper } from '../../utils/apiWrapper' +import { KeyringPair } from '@polkadot/keyring/types' +import BN from 'bn.js' +import { assert } from 'chai' +import { Seat } from '@nicaea/types/council' +import { v4 as uuid } from 'uuid' +import { Utils } from '../../utils/utils' +import { Fixture } from './interfaces/fixture' + +export class ElectCouncilFixture implements Fixture { + private apiWrapper: ApiWrapper + private m1KeyPairs: KeyringPair[] + private m2KeyPairs: KeyringPair[] + private k: number + private sudo: KeyringPair + private greaterStake: BN + private lesserStake: BN + + public constructor( + apiWrapper: ApiWrapper, + m1KeyPairs: KeyringPair[], + m2KeyPairs: KeyringPair[], + k: number, + sudo: KeyringPair, + greaterStake: BN, + lesserStake: BN + ) { + this.apiWrapper = apiWrapper + this.m1KeyPairs = m1KeyPairs + this.m2KeyPairs = m2KeyPairs + this.k = k + this.sudo = sudo + this.greaterStake = greaterStake + this.lesserStake = lesserStake + } + + public async runner(expectFailure: boolean): Promise { + let now = await this.apiWrapper.getBestBlock() + const applyForCouncilFee: BN = this.apiWrapper.estimateApplyForCouncilFee(this.greaterStake) + const voteForCouncilFee: BN = this.apiWrapper.estimateVoteForCouncilFee( + this.sudo.address, + this.sudo.address, + this.greaterStake + ) + const salt: string[] = [] + this.m1KeyPairs.forEach(() => { + salt.push(''.concat(uuid().replace(/-/g, ''))) + }) + const revealVoteFee: BN = this.apiWrapper.estimateRevealVoteFee(this.sudo.address, salt[0]) + + // Topping the balances + await this.apiWrapper.transferBalanceToAccounts( + this.sudo, + this.m2KeyPairs, + applyForCouncilFee.add(this.greaterStake) + ) + await this.apiWrapper.transferBalanceToAccounts( + this.sudo, + this.m1KeyPairs, + voteForCouncilFee.add(revealVoteFee).add(this.greaterStake) + ) + + // First K members stake more + await this.apiWrapper.sudoStartAnnouncingPerion(this.sudo, now.addn(100)) + await this.apiWrapper.batchApplyForCouncilElection(this.m2KeyPairs.slice(0, this.k), this.greaterStake) + this.m2KeyPairs.slice(0, this.k).forEach((keyPair) => + this.apiWrapper.getCouncilElectionStake(keyPair.address).then((stake) => { + assert( + stake.eq(this.greaterStake), + `${keyPair.address} not applied correctly for council election with stake ${stake} versus expected ${this.greaterStake}` + ) + }) + ) + + // Last members stake less + await this.apiWrapper.batchApplyForCouncilElection(this.m2KeyPairs.slice(this.k), this.lesserStake) + this.m2KeyPairs.slice(this.k).forEach((keyPair) => + this.apiWrapper.getCouncilElectionStake(keyPair.address).then((stake) => { + assert( + stake.eq(this.lesserStake), + `${keyPair.address} not applied correctrly for council election with stake ${stake} versus expected ${this.lesserStake}` + ) + }) + ) + + // Voting + await this.apiWrapper.sudoStartVotingPerion(this.sudo, now.addn(100)) + await this.apiWrapper.batchVoteForCouncilMember( + this.m1KeyPairs.slice(0, this.k), + this.m2KeyPairs.slice(0, this.k), + salt.slice(0, this.k), + this.lesserStake + ) + await this.apiWrapper.batchVoteForCouncilMember( + this.m1KeyPairs.slice(this.k), + this.m2KeyPairs.slice(this.k), + salt.slice(this.k), + this.greaterStake + ) + + // Revealing + await this.apiWrapper.sudoStartRevealingPerion(this.sudo, now.addn(100)) + await this.apiWrapper.batchRevealVote( + this.m1KeyPairs.slice(0, this.k), + this.m2KeyPairs.slice(0, this.k), + salt.slice(0, this.k) + ) + await this.apiWrapper.batchRevealVote( + this.m1KeyPairs.slice(this.k), + this.m2KeyPairs.slice(this.k), + salt.slice(this.k) + ) + now = await this.apiWrapper.getBestBlock() + + // Resolving election + // 3 is to ensure the revealing block is in future + await this.apiWrapper.sudoStartRevealingPerion(this.sudo, now.addn(3)) + await Utils.wait(this.apiWrapper.getBlockDuration().muln(2.5).toNumber()) + const seats: Seat[] = await this.apiWrapper.getCouncil() + + // Preparing collections to increase assertion readability + const m2addresses: string[] = this.m2KeyPairs.map((keyPair) => keyPair.address) + const m1addresses: string[] = this.m1KeyPairs.map((keyPair) => keyPair.address) + const members: string[] = seats.map((seat) => seat.member.toString()) + const bakers: string[] = seats.map((seat) => seat.backers.map((baker) => baker.member.toString())).flat() + + // Assertions + m2addresses.forEach((address) => assert(members.includes(address), `Account ${address} is not in the council`)) + m1addresses.forEach((address) => assert(bakers.includes(address), `Account ${address} is not in the voters`)) + seats.forEach((seat) => + assert( + Utils.getTotalStake(seat).eq(this.greaterStake.add(this.lesserStake)), + `Member ${seat.member} has unexpected stake ${Utils.getTotalStake(seat)}` + ) + ) + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} diff --git a/tests/network-tests/src/iznik/tests/fixtures/interfaces/fixture.ts b/tests/network-tests/src/iznik/tests/fixtures/interfaces/fixture.ts new file mode 100644 index 0000000000..06d94a98ff --- /dev/null +++ b/tests/network-tests/src/iznik/tests/fixtures/interfaces/fixture.ts @@ -0,0 +1,3 @@ +export interface Fixture { + runner(expectFailure: boolean): Promise +} diff --git a/tests/network-tests/src/iznik/tests/fixtures/membershipModule.ts b/tests/network-tests/src/iznik/tests/fixtures/membershipModule.ts new file mode 100644 index 0000000000..4c8a824f92 --- /dev/null +++ b/tests/network-tests/src/iznik/tests/fixtures/membershipModule.ts @@ -0,0 +1,106 @@ +import { ApiWrapper } from '../../utils/apiWrapper' +import { KeyringPair } from '@polkadot/keyring/types' +import BN from 'bn.js' +import { assert } from 'chai' +import { Fixture } from './interfaces/fixture' + +export class BuyMembershipHappyCaseFixture implements Fixture { + private apiWrapper: ApiWrapper + private sudo: KeyringPair + private keyPairs: KeyringPair[] + private paidTerms: number + + public constructor(apiWrapper: ApiWrapper, sudo: KeyringPair, keyPairs: KeyringPair[], paidTerms: number) { + this.apiWrapper = apiWrapper + this.sudo = sudo + this.keyPairs = keyPairs + this.paidTerms = paidTerms + } + + public async runner(expectFailure: boolean): Promise { + // Fee estimation and transfer + const membershipFee: BN = await this.apiWrapper.getMembershipFee(this.paidTerms) + const membershipTransactionFee: BN = this.apiWrapper.estimateBuyMembershipFee( + this.sudo, + this.paidTerms, + 'member_name_which_is_longer_than_expected' + ) + await this.apiWrapper.transferBalanceToAccounts( + this.sudo, + this.keyPairs, + membershipTransactionFee.add(new BN(membershipFee)) + ) + + // Buying membership + await Promise.all( + this.keyPairs.map(async (keyPair, index) => { + await this.apiWrapper.buyMembership( + keyPair, + this.paidTerms, + `new_member_${index}${keyPair.address.substring(0, 8)}` + ) + }) + ) + + // Assertions + this.keyPairs.forEach((keyPair) => + this.apiWrapper + .getMemberIds(keyPair.address) + .then((membership) => assert(membership.length > 0, `Account ${keyPair.address} is not a member`)) + ) + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class BuyMembershipWithInsufficienFundsFixture implements Fixture { + private apiWrapper: ApiWrapper + private sudo: KeyringPair + private aKeyPair: KeyringPair + private paidTerms: number + + public constructor(apiWrapper: ApiWrapper, sudo: KeyringPair, aKeyPair: KeyringPair, paidTerms: number) { + this.apiWrapper = apiWrapper + this.sudo = sudo + this.aKeyPair = aKeyPair + this.paidTerms = paidTerms + } + + public async runner(expectFailure: boolean) { + // Fee estimation and transfer + const membershipFee: BN = await this.apiWrapper.getMembershipFee(this.paidTerms) + const membershipTransactionFee: BN = this.apiWrapper.estimateBuyMembershipFee( + this.sudo, + this.paidTerms, + 'member_name_which_is_longer_than_expected' + ) + await this.apiWrapper.transferBalance(this.sudo, this.aKeyPair.address, membershipTransactionFee) + + // Balance assertion + await this.apiWrapper + .getBalance(this.aKeyPair.address) + .then((balance) => + assert( + balance.toBn() < membershipFee.add(membershipTransactionFee), + 'Account A already have sufficient balance to purchase membership' + ) + ) + + // Buying memebership + await this.apiWrapper.buyMembership( + this.aKeyPair, + this.paidTerms, + `late_member_${this.aKeyPair.address.substring(0, 8)}`, + true + ) + + // Assertions + this.apiWrapper + .getMemberIds(this.aKeyPair.address) + .then((membership) => assert(membership.length === 0, 'Account A is a member')) + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} diff --git a/tests/network-tests/src/iznik/tests/fixtures/proposalsModule.ts b/tests/network-tests/src/iznik/tests/fixtures/proposalsModule.ts new file mode 100644 index 0000000000..7190933465 --- /dev/null +++ b/tests/network-tests/src/iznik/tests/fixtures/proposalsModule.ts @@ -0,0 +1,1006 @@ +import { KeyringPair } from '@polkadot/keyring/types' +import { ApiWrapper, WorkingGroups } from '../../utils/apiWrapper' +import { v4 as uuid } from 'uuid' +import BN from 'bn.js' +import { WorkingGroupOpening } from '../../dto/workingGroupOpening' +import { FillOpeningParameters } from '../../dto/fillOpeningParameters' +import { Fixture } from './interfaces/fixture' +import { Bytes } from '@polkadot/types' +import { assert } from 'chai' + +export class CreateWorkingGroupLeaderOpeningFixture implements Fixture { + private apiWrapper: ApiWrapper + private m1KeyPairs: KeyringPair[] + private sudo: KeyringPair + private applicationStake: BN + private roleStake: BN + private workingGroup: string + + private result: BN | undefined + + constructor( + apiWrapper: ApiWrapper, + m1KeyPairs: KeyringPair[], + sudo: KeyringPair, + applicationStake: BN, + roleStake: BN, + workingGroup: string + ) { + this.apiWrapper = apiWrapper + this.m1KeyPairs = m1KeyPairs + this.sudo = sudo + this.applicationStake = applicationStake + this.roleStake = roleStake + this.workingGroup = workingGroup + } + + public getResult(): BN | undefined { + return this.result + } + + public async runner(expectFailure: boolean): Promise { + // Setup + const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8) + const description: string = 'Testing working group lead opening proposal ' + uuid().substring(0, 8) + + // Proposal stake calculation + const proposalStake: BN = new BN(100000) + const proposalFee: BN = this.apiWrapper.estimateProposeCreateWorkingGroupLeaderOpeningFee() + await this.apiWrapper.transferBalance(this.sudo, this.m1KeyPairs[0].address, proposalFee.add(proposalStake)) + + // Opening construction + const opening = new WorkingGroupOpening() + .setMaxActiveApplicants(new BN(this.m1KeyPairs.length)) + .setMaxReviewPeriodLength(new BN(32)) + .setApplicationStakingPolicyAmount(new BN(this.applicationStake)) + .setApplicationCrowdedOutUnstakingPeriodLength(new BN(1)) + .setApplicationExpiredUnstakingPeriodLength(new BN(1)) + .setRoleStakingPolicyAmount(new BN(this.roleStake)) + .setRoleCrowdedOutUnstakingPeriodLength(new BN(1)) + .setRoleExpiredUnstakingPeriodLength(new BN(1)) + .setSlashableMaxCount(new BN(1)) + .setSlashableMaxPercentPtsPerTime(new BN(100)) + .setSuccessfulApplicantApplicationStakeUnstakingPeriod(new BN(1)) + .setFailedApplicantApplicationStakeUnstakingPeriod(new BN(1)) + .setFailedApplicantRoleStakeUnstakingPeriod(new BN(1)) + .setTerminateApplicationStakeUnstakingPeriod(new BN(1)) + .setTerminateRoleStakeUnstakingPeriod(new BN(1)) + .setExitRoleApplicationStakeUnstakingPeriod(new BN(1)) + .setExitRoleStakeUnstakingPeriod(new BN(1)) + .setText(uuid().substring(0, 8)) + + // Proposal creation + const proposalPromise = this.apiWrapper.expectProposalCreated() + await this.apiWrapper.proposeCreateWorkingGroupLeaderOpening( + this.m1KeyPairs[0], + proposalTitle, + description, + proposalStake, + opening, + this.workingGroup + ) + const proposalNumber: BN = await proposalPromise + this.result = proposalNumber + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class BeginWorkingGroupLeaderApplicationReviewFixture implements Fixture { + private apiWrapper: ApiWrapper + private m1KeyPairs: KeyringPair[] + private sudo: KeyringPair + private openingId: BN + private workingGroup: string + + private result: BN | undefined + + constructor( + apiWrapper: ApiWrapper, + m1KeyPairs: KeyringPair[], + sudo: KeyringPair, + openingId: BN, + workingGroup: string + ) { + this.apiWrapper = apiWrapper + this.m1KeyPairs = m1KeyPairs + this.sudo = sudo + this.openingId = openingId + this.workingGroup = workingGroup + } + + public getResult(): BN | undefined { + return this.result + } + + public async runner(expectFailure: boolean): Promise { + // Setup + const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8) + const description: string = 'Testing begin working group lead application review proposal ' + uuid().substring(0, 8) + + // Proposal stake calculation + const proposalStake: BN = new BN(25000) + const proposalFee: BN = this.apiWrapper.estimateProposeBeginWorkingGroupLeaderApplicationReviewFee() + await this.apiWrapper.transferBalance(this.sudo, this.m1KeyPairs[0].address, proposalFee.add(proposalStake)) + + // Proposal creation + const proposalPromise = this.apiWrapper.expectProposalCreated() + await this.apiWrapper.proposeBeginWorkingGroupLeaderApplicationReview( + this.m1KeyPairs[0], + proposalTitle, + description, + proposalStake, + this.openingId, + this.workingGroup + ) + this.result = await proposalPromise + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class FillLeaderOpeningProposalFixture implements Fixture { + private apiWrapper: ApiWrapper + private m1KeyPairs: KeyringPair[] + private applicantRoleAccountAddress: string + private sudo: KeyringPair + private firstRewardInterval: BN + private rewardInterval: BN + private payoutAmount: BN + private openingId: BN + private workingGroup: WorkingGroups + + private result: BN | undefined + + constructor( + apiWrapper: ApiWrapper, + m1KeyPairs: KeyringPair[], + applicantRoleAccountAddress: string, + sudo: KeyringPair, + firstRewardInterval: BN, + rewardInterval: BN, + payoutAmount: BN, + openingId: BN, + workingGroup: WorkingGroups + ) { + this.apiWrapper = apiWrapper + this.m1KeyPairs = m1KeyPairs + this.applicantRoleAccountAddress = applicantRoleAccountAddress + this.sudo = sudo + this.firstRewardInterval = firstRewardInterval + this.rewardInterval = rewardInterval + this.payoutAmount = payoutAmount + this.openingId = openingId + this.workingGroup = workingGroup + } + + public getResult(): BN | undefined { + return this.result + } + + public async runner(expectFailure: boolean): Promise { + // Setup + const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8) + const description: string = 'Testing fill opening proposal ' + uuid().substring(0, 8) + const workingGroupString: string = this.apiWrapper.getWorkingGroupString(this.workingGroup) + + // Proposal stake calculation + const proposalStake: BN = new BN(50000) + const proposalFee: BN = this.apiWrapper.estimateProposeFillLeaderOpeningFee() + await this.apiWrapper.transferBalance(this.sudo, this.m1KeyPairs[0].address, proposalFee.add(proposalStake)) + + // Proposal creation + const applicationId: BN = ( + await this.apiWrapper.getActiveApplicationsIdsByRoleAccount(this.applicantRoleAccountAddress, this.workingGroup) + )[0] + const now = await this.apiWrapper.getBestBlock() + const fillOpeningParameters: FillOpeningParameters = new FillOpeningParameters() + .setAmountPerPayout(this.payoutAmount) + .setNextPaymentAtBlock(now.add(this.firstRewardInterval)) + .setPayoutInterval(this.rewardInterval) + .setOpeningId(this.openingId) + .setSuccessfulApplicationId(applicationId) + .setWorkingGroup(workingGroupString) + + const proposalPromise = this.apiWrapper.expectProposalCreated() + await this.apiWrapper.proposeFillLeaderOpening( + this.m1KeyPairs[0], + proposalTitle, + description, + proposalStake, + fillOpeningParameters + ) + this.result = await proposalPromise + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class TerminateLeaderRoleProposalFixture implements Fixture { + private apiWrapper: ApiWrapper + private m1KeyPairs: KeyringPair[] + private leaderRoleAccountAddress: string + private sudo: KeyringPair + private slash: boolean + private workingGroup: WorkingGroups + + private result: BN | undefined + + constructor( + apiWrapper: ApiWrapper, + m1KeyPairs: KeyringPair[], + leaderRoleAccountAddress: string, + sudo: KeyringPair, + slash: boolean, + workingGroup: WorkingGroups + ) { + this.apiWrapper = apiWrapper + this.m1KeyPairs = m1KeyPairs + this.leaderRoleAccountAddress = leaderRoleAccountAddress + this.sudo = sudo + this.slash = slash + this.workingGroup = workingGroup + } + + public getResult(): BN | undefined { + return this.result + } + + public async runner(expectFailure: boolean): Promise { + // Setup + const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8) + const description: string = 'Testing begin working group lead application review proposal ' + uuid().substring(0, 8) + const rationale: string = 'Testing leader termination ' + uuid().substring(0, 8) + const workingGroupString: string = this.apiWrapper.getWorkingGroupString(this.workingGroup) + const workerId: BN = await this.apiWrapper.getWorkerIdByRoleAccount( + this.leaderRoleAccountAddress, + this.workingGroup + ) + + // Proposal stake calculation + const proposalStake: BN = new BN(100000) + const proposalFee: BN = this.apiWrapper.estimateProposeTerminateLeaderRoleFee() + await this.apiWrapper.transferBalance(this.sudo, this.m1KeyPairs[0].address, proposalFee.add(proposalStake)) + + // Proposal creation + const proposalPromise = this.apiWrapper.expectProposalCreated() + await this.apiWrapper.proposeTerminateLeaderRole( + this.m1KeyPairs[0], + proposalTitle, + description, + proposalStake, + workerId, + rationale, + this.slash, + workingGroupString + ) + this.result = await proposalPromise + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class SetLeaderRewardProposalFixture implements Fixture { + private apiWrapper: ApiWrapper + private m1KeyPairs: KeyringPair[] + private sudo: KeyringPair + private payoutAmount: BN + private workingGroup: WorkingGroups + + private result: BN | undefined + + constructor( + apiWrapper: ApiWrapper, + m1KeyPairs: KeyringPair[], + sudo: KeyringPair, + payoutAmount: BN, + workingGroup: WorkingGroups + ) { + this.apiWrapper = apiWrapper + this.m1KeyPairs = m1KeyPairs + this.sudo = sudo + this.payoutAmount = payoutAmount + this.workingGroup = workingGroup + } + + public getResult(): BN | undefined { + return this.result + } + + public async runner(expectFailure: boolean): Promise { + // Setup + const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8) + const description: string = 'Testing set leader reward proposal ' + uuid().substring(0, 8) + const workingGroupString: string = this.apiWrapper.getWorkingGroupString(this.workingGroup) + const workerId: BN = (await this.apiWrapper.getLeadWorkerId(this.workingGroup))! + + // Proposal stake calculation + const proposalStake: BN = new BN(50000) + const proposalFee: BN = this.apiWrapper.estimateProposeLeaderRewardFee() + await this.apiWrapper.transferBalance(this.sudo, this.m1KeyPairs[0].address, proposalFee.add(proposalStake)) + + // Proposal creation + const proposalPromise = this.apiWrapper.expectProposalCreated() + await this.apiWrapper.proposeLeaderReward( + this.m1KeyPairs[0], + proposalTitle, + description, + proposalStake, + workerId, + this.payoutAmount, + workingGroupString + ) + this.result = await proposalPromise + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class DecreaseLeaderStakeProposalFixture implements Fixture { + private apiWrapper: ApiWrapper + private m1KeyPairs: KeyringPair[] + private sudo: KeyringPair + private stakeDecrement: BN + private workingGroup: WorkingGroups + + private result: BN | undefined + + constructor( + apiWrapper: ApiWrapper, + m1KeyPairs: KeyringPair[], + sudo: KeyringPair, + stakeDecrement: BN, + workingGroup: WorkingGroups + ) { + this.apiWrapper = apiWrapper + this.m1KeyPairs = m1KeyPairs + this.sudo = sudo + this.stakeDecrement = stakeDecrement + this.workingGroup = workingGroup + } + + public getResult(): BN | undefined { + return this.result + } + + public async runner(expectFailure: boolean): Promise { + // Setup + const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8) + const description: string = 'Testing decrease leader stake proposal ' + uuid().substring(0, 8) + const workingGroupString: string = this.apiWrapper.getWorkingGroupString(this.workingGroup) + const workerId: BN = (await this.apiWrapper.getLeadWorkerId(this.workingGroup))! + + // Proposal stake calculation + const proposalStake: BN = new BN(50000) + const proposalFee: BN = this.apiWrapper.estimateProposeDecreaseLeaderStakeFee() + await this.apiWrapper.transferBalance(this.sudo, this.m1KeyPairs[0].address, proposalFee.add(proposalStake)) + + // Proposal creation + const proposalPromise = this.apiWrapper.expectProposalCreated() + await this.apiWrapper.proposeDecreaseLeaderStake( + this.m1KeyPairs[0], + proposalTitle, + description, + proposalStake, + workerId, + this.stakeDecrement, + workingGroupString + ) + this.result = await proposalPromise + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class SlashLeaderProposalFixture implements Fixture { + private apiWrapper: ApiWrapper + private m1KeyPairs: KeyringPair[] + private sudo: KeyringPair + private slashAmount: BN + private workingGroup: WorkingGroups + + private result: BN | undefined + + constructor( + apiWrapper: ApiWrapper, + m1KeyPairs: KeyringPair[], + sudo: KeyringPair, + slashAmount: BN, + workingGroup: WorkingGroups + ) { + this.apiWrapper = apiWrapper + this.m1KeyPairs = m1KeyPairs + this.sudo = sudo + this.slashAmount = slashAmount + this.workingGroup = workingGroup + } + + public getResult(): BN | undefined { + return this.result + } + + public async runner(expectFailure: boolean): Promise { + // Setup + const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8) + const description: string = 'Testing slash leader stake proposal ' + uuid().substring(0, 8) + const workingGroupString: string = this.apiWrapper.getWorkingGroupString(this.workingGroup) + const workerId: BN = (await this.apiWrapper.getLeadWorkerId(this.workingGroup))! + + // Proposal stake calculation + const proposalStake: BN = new BN(50000) + const proposalFee: BN = this.apiWrapper.estimateProposeSlashLeaderStakeFee() + await this.apiWrapper.transferBalance(this.sudo, this.m1KeyPairs[0].address, proposalFee.add(proposalStake)) + + // Proposal creation + const proposalPromise = this.apiWrapper.expectProposalCreated() + await this.apiWrapper.proposeSlashLeaderStake( + this.m1KeyPairs[0], + proposalTitle, + description, + proposalStake, + workerId, + this.slashAmount, + workingGroupString + ) + this.result = await proposalPromise + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class WorkingGroupMintCapacityProposalFixture implements Fixture { + private apiWrapper: ApiWrapper + private m1KeyPairs: KeyringPair[] + private sudo: KeyringPair + private mintCapacity: BN + private workingGroup: WorkingGroups + + private result: BN | undefined + + constructor( + apiWrapper: ApiWrapper, + m1KeyPairs: KeyringPair[], + sudo: KeyringPair, + mintCapacity: BN, + workingGroup: WorkingGroups + ) { + this.apiWrapper = apiWrapper + this.m1KeyPairs = m1KeyPairs + this.sudo = sudo + this.mintCapacity = mintCapacity + this.workingGroup = workingGroup + } + + public getResult(): BN | undefined { + return this.result + } + + public async runner(expectFailure: boolean): Promise { + // Setup + const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8) + const description: string = 'Testing working group mint capacity proposal ' + uuid().substring(0, 8) + const workingGroupString: string = this.apiWrapper.getWorkingGroupString(this.workingGroup) + + // Proposal stake calculation + const proposalStake: BN = new BN(50000) + const proposalFee: BN = this.apiWrapper.estimateProposeWorkingGroupMintCapacityFee() + await this.apiWrapper.transferBalance(this.sudo, this.m1KeyPairs[0].address, proposalFee.add(proposalStake)) + + // Proposal creation + const proposalPromise = this.apiWrapper.expectProposalCreated() + await this.apiWrapper.proposeWorkingGroupMintCapacity( + this.m1KeyPairs[0], + proposalTitle, + description, + proposalStake, + this.mintCapacity, + workingGroupString + ) + this.result = await proposalPromise + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class ElectionParametersProposalFixture implements Fixture { + private apiWrapper: ApiWrapper + private m1KeyPairs: KeyringPair[] + private m2KeyPairs: KeyringPair[] + private sudo: KeyringPair + + constructor(apiWrapper: ApiWrapper, m1KeyPairs: KeyringPair[], m2KeyPairs: KeyringPair[], sudo: KeyringPair) { + this.apiWrapper = apiWrapper + this.m1KeyPairs = m1KeyPairs + this.m2KeyPairs = m2KeyPairs + this.sudo = sudo + } + + public async runner(expectFailure: boolean): Promise { + // Setup + const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8) + const description: string = 'Testing validator count proposal ' + uuid().substring(0, 8) + const runtimeVoteFee: BN = this.apiWrapper.estimateVoteForProposalFee() + await this.apiWrapper.transferBalanceToAccounts(this.sudo, this.m2KeyPairs, runtimeVoteFee) + const announcingPeriod: BN = await this.apiWrapper.getAnnouncingPeriod() + const votingPeriod: BN = await this.apiWrapper.getVotingPeriod() + const revealingPeriod: BN = await this.apiWrapper.getRevealingPeriod() + const councilSize: BN = await this.apiWrapper.getCouncilSize() + const candidacyLimit: BN = await this.apiWrapper.getCandidacyLimit() + const newTermDuration: BN = await this.apiWrapper.getNewTermDuration() + const minCouncilStake: BN = await this.apiWrapper.getMinCouncilStake() + const minVotingStake: BN = await this.apiWrapper.getMinVotingStake() + + // Proposal stake calculation + const proposalStake: BN = new BN(200000) + const proposalFee: BN = this.apiWrapper.estimateProposeElectionParametersFee( + description, + description, + proposalStake, + announcingPeriod, + votingPeriod, + revealingPeriod, + councilSize, + candidacyLimit, + newTermDuration, + minCouncilStake, + minVotingStake + ) + await this.apiWrapper.transferBalance(this.sudo, this.m1KeyPairs[0].address, proposalFee.add(proposalStake)) + + // Proposal creation + const proposedAnnouncingPeriod: BN = announcingPeriod.subn(1) + const proposedVotingPeriod: BN = votingPeriod.addn(1) + const proposedRevealingPeriod: BN = revealingPeriod.addn(1) + const proposedCouncilSize: BN = councilSize.addn(1) + const proposedCandidacyLimit: BN = candidacyLimit.addn(1) + const proposedNewTermDuration: BN = newTermDuration.addn(1) + const proposedMinCouncilStake: BN = minCouncilStake.addn(1) + const proposedMinVotingStake: BN = minVotingStake.addn(1) + const proposalPromise = this.apiWrapper.expectProposalCreated() + await this.apiWrapper.proposeElectionParameters( + this.m1KeyPairs[0], + proposalTitle, + description, + proposalStake, + proposedAnnouncingPeriod, + proposedVotingPeriod, + proposedRevealingPeriod, + proposedCouncilSize, + proposedCandidacyLimit, + proposedNewTermDuration, + proposedMinCouncilStake, + proposedMinVotingStake + ) + const proposalNumber = await proposalPromise + + // Approving the proposal + const proposalExecutionPromise = this.apiWrapper.expectProposalFinalized() + await this.apiWrapper.batchApproveProposal(this.m2KeyPairs, proposalNumber) + await proposalExecutionPromise + + // Assertions + const newAnnouncingPeriod: BN = await this.apiWrapper.getAnnouncingPeriod() + const newVotingPeriod: BN = await this.apiWrapper.getVotingPeriod() + const newRevealingPeriod: BN = await this.apiWrapper.getRevealingPeriod() + const newCouncilSize: BN = await this.apiWrapper.getCouncilSize() + const newCandidacyLimit: BN = await this.apiWrapper.getCandidacyLimit() + const newNewTermDuration: BN = await this.apiWrapper.getNewTermDuration() + const newMinCouncilStake: BN = await this.apiWrapper.getMinCouncilStake() + const newMinVotingStake: BN = await this.apiWrapper.getMinVotingStake() + assert( + proposedAnnouncingPeriod.eq(newAnnouncingPeriod), + `Announcing period has unexpected value ${newAnnouncingPeriod}, expected ${proposedAnnouncingPeriod}` + ) + assert( + proposedVotingPeriod.eq(newVotingPeriod), + `Voting period has unexpected value ${newVotingPeriod}, expected ${proposedVotingPeriod}` + ) + assert( + proposedRevealingPeriod.eq(newRevealingPeriod), + `Revealing has unexpected value ${newRevealingPeriod}, expected ${proposedRevealingPeriod}` + ) + assert( + proposedCouncilSize.eq(newCouncilSize), + `Council size has unexpected value ${newCouncilSize}, expected ${proposedCouncilSize}` + ) + assert( + proposedCandidacyLimit.eq(newCandidacyLimit), + `Candidacy limit has unexpected value ${newCandidacyLimit}, expected ${proposedCandidacyLimit}` + ) + assert( + proposedNewTermDuration.eq(newNewTermDuration), + `New term duration has unexpected value ${newNewTermDuration}, expected ${proposedNewTermDuration}` + ) + assert( + proposedMinCouncilStake.eq(newMinCouncilStake), + `Min council stake has unexpected value ${newMinCouncilStake}, expected ${proposedMinCouncilStake}` + ) + assert( + proposedMinVotingStake.eq(newMinVotingStake), + `Min voting stake has unexpected value ${newMinVotingStake}, expected ${proposedMinVotingStake}` + ) + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class SetLeadProposalFixture implements Fixture { + private apiWrapper: ApiWrapper + private m1KeyPairs: KeyringPair[] + private m2KeyPairs: KeyringPair[] + private sudo: KeyringPair + + constructor(apiWrapper: ApiWrapper, m1KeyPairs: KeyringPair[], m2KeyPairs: KeyringPair[], sudo: KeyringPair) { + this.apiWrapper = apiWrapper + this.m1KeyPairs = m1KeyPairs + this.m2KeyPairs = m2KeyPairs + this.sudo = sudo + } + + public async runner(expectFailure: boolean): Promise { + // Setup + const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8) + const description: string = 'Testing validator count proposal ' + uuid().substring(0, 8) + const runtimeVoteFee: BN = this.apiWrapper.estimateVoteForProposalFee() + await this.apiWrapper.transferBalanceToAccounts(this.sudo, this.m2KeyPairs, runtimeVoteFee) + + // Proposal stake calculation + const proposalStake: BN = new BN(50000) + const proposalFee: BN = this.apiWrapper.estimateProposeLeadFee( + description, + description, + proposalStake, + this.sudo.address + ) + await this.apiWrapper.transferBalance(this.sudo, this.m1KeyPairs[0].address, proposalFee.add(proposalStake)) + + // Proposal creation + const proposalPromise = this.apiWrapper.expectProposalCreated() + await this.apiWrapper.proposeLead(this.m1KeyPairs[0], proposalTitle, description, proposalStake, this.m1KeyPairs[1]) + const proposalNumber = await proposalPromise + + // Approving the proposal + const proposalExecutionPromise = this.apiWrapper.expectProposalFinalized() + await this.apiWrapper.batchApproveProposal(this.m2KeyPairs, proposalNumber) + await proposalExecutionPromise + const newLead: string = await this.apiWrapper.getCurrentLeadAddress() + assert( + newLead === this.m1KeyPairs[1].address, + `New lead has unexpected value ${newLead}, expected ${this.m1KeyPairs[1].address}` + ) + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class SpendingProposalFixture implements Fixture { + private apiWrapper: ApiWrapper + private m1KeyPairs: KeyringPair[] + private m2KeyPairs: KeyringPair[] + private sudo: KeyringPair + private spendingBalance: BN + private mintCapacity: BN + + constructor( + apiWrapper: ApiWrapper, + m1KeyPairs: KeyringPair[], + m2KeyPairs: KeyringPair[], + sudo: KeyringPair, + spendingBalance: BN, + mintCapacity: BN + ) { + this.apiWrapper = apiWrapper + this.m1KeyPairs = m1KeyPairs + this.m2KeyPairs = m2KeyPairs + this.sudo = sudo + this.spendingBalance = spendingBalance + this.mintCapacity = mintCapacity + } + + public async runner(expectFailure: boolean): Promise { + // Setup + const description = 'spending proposal which is used for API network testing with some mock data' + const runtimeVoteFee: BN = this.apiWrapper.estimateVoteForProposalFee() + + // Topping the balances + const proposalStake: BN = new BN(25000) + const runtimeProposalFee: BN = this.apiWrapper.estimateProposeSpendingFee( + description, + description, + proposalStake, + this.spendingBalance, + this.sudo.address + ) + await this.apiWrapper.transferBalance(this.sudo, this.m1KeyPairs[0].address, runtimeProposalFee.add(proposalStake)) + await this.apiWrapper.transferBalanceToAccounts(this.sudo, this.m2KeyPairs, runtimeVoteFee) + await this.apiWrapper.sudoSetCouncilMintCapacity(this.sudo, this.mintCapacity) + + // Proposal creation + const proposalPromise = this.apiWrapper.expectProposalCreated() + await this.apiWrapper.proposeSpending( + this.m1KeyPairs[0], + 'testing spending' + uuid().substring(0, 8), + 'spending to test proposal functionality' + uuid().substring(0, 8), + proposalStake, + this.spendingBalance, + this.sudo.address + ) + const proposalNumber = await proposalPromise + + // Approving spending proposal + const balanceBeforeMinting: BN = await this.apiWrapper.getBalance(this.sudo.address) + const spendingPromise = this.apiWrapper.expectProposalFinalized() + await this.apiWrapper.batchApproveProposal(this.m2KeyPairs, proposalNumber) + await spendingPromise + const balanceAfterMinting: BN = await this.apiWrapper.getBalance(this.sudo.address) + assert( + balanceAfterMinting.sub(balanceBeforeMinting).eq(this.spendingBalance), + `member ${ + this.m1KeyPairs[0].address + } has unexpected balance ${balanceAfterMinting}, expected ${balanceBeforeMinting.add(this.spendingBalance)}` + ) + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class TextProposalFixture implements Fixture { + private apiWrapper: ApiWrapper + private m1KeyPairs: KeyringPair[] + private m2KeyPairs: KeyringPair[] + private sudo: KeyringPair + + constructor(apiWrapper: ApiWrapper, m1KeyPairs: KeyringPair[], m2KeyPairs: KeyringPair[], sudo: KeyringPair) { + this.apiWrapper = apiWrapper + this.m1KeyPairs = m1KeyPairs + this.m2KeyPairs = m2KeyPairs + this.sudo = sudo + } + + public async runner(expectFailure: boolean): Promise { + // Setup + const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8) + const description: string = 'Testing text proposal ' + uuid().substring(0, 8) + const proposalText: string = 'Text of the testing proposal ' + uuid().substring(0, 8) + const runtimeVoteFee: BN = this.apiWrapper.estimateVoteForProposalFee() + await this.apiWrapper.transferBalanceToAccounts(this.sudo, this.m2KeyPairs, runtimeVoteFee) + + // Proposal stake calculation + const proposalStake: BN = new BN(25000) + const runtimeProposalFee: BN = this.apiWrapper.estimateProposeTextFee( + proposalStake, + description, + description, + proposalText + ) + await this.apiWrapper.transferBalance(this.sudo, this.m1KeyPairs[0].address, runtimeProposalFee.add(proposalStake)) + + // Proposal creation + const proposalPromise = this.apiWrapper.expectProposalCreated() + await this.apiWrapper.proposeText(this.m1KeyPairs[0], proposalStake, proposalTitle, description, proposalText) + const proposalNumber = await proposalPromise + + // Approving text proposal + const textProposalPromise = this.apiWrapper.expectProposalFinalized() + await this.apiWrapper.batchApproveProposal(this.m2KeyPairs, proposalNumber) + await textProposalPromise + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class ValidatorCountProposalFixture implements Fixture { + private apiWrapper: ApiWrapper + private m1KeyPairs: KeyringPair[] + private m2KeyPairs: KeyringPair[] + private sudo: KeyringPair + private validatorCountIncrement: BN + + constructor( + apiWrapper: ApiWrapper, + m1KeyPairs: KeyringPair[], + m2KeyPairs: KeyringPair[], + sudo: KeyringPair, + validatorCountIncrement: BN + ) { + this.apiWrapper = apiWrapper + this.m1KeyPairs = m1KeyPairs + this.m2KeyPairs = m2KeyPairs + this.sudo = sudo + this.validatorCountIncrement = validatorCountIncrement + } + + public async runner(expectFailure: boolean): Promise { + // Setup + const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8) + const description: string = 'Testing validator count proposal ' + uuid().substring(0, 8) + const runtimeVoteFee: BN = this.apiWrapper.estimateVoteForProposalFee() + await this.apiWrapper.transferBalanceToAccounts(this.sudo, this.m2KeyPairs, runtimeVoteFee) + + // Proposal stake calculation + const proposalStake: BN = new BN(100000) + const proposalFee: BN = this.apiWrapper.estimateProposeValidatorCountFee(description, description, proposalStake) + await this.apiWrapper.transferBalance(this.sudo, this.m1KeyPairs[0].address, proposalFee.add(proposalStake)) + const validatorCount: BN = await this.apiWrapper.getValidatorCount() + + // Proposal creation + const proposedValidatorCount: BN = validatorCount.add(this.validatorCountIncrement) + const proposalPromise = this.apiWrapper.expectProposalCreated() + await this.apiWrapper.proposeValidatorCount( + this.m1KeyPairs[0], + proposalTitle, + description, + proposalStake, + proposedValidatorCount + ) + const proposalNumber = await proposalPromise + + // Approving the proposal + const proposalExecutionPromise = this.apiWrapper.expectProposalFinalized() + await this.apiWrapper.batchApproveProposal(this.m2KeyPairs, proposalNumber) + await proposalExecutionPromise + const newValidatorCount: BN = await this.apiWrapper.getValidatorCount() + assert( + proposedValidatorCount.eq(newValidatorCount), + `Validator count has unexpeccted value ${newValidatorCount}, expected ${proposedValidatorCount}` + ) + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class ContentWorkingGroupMintCapacityProposalFixture implements Fixture { + private apiWrapper: ApiWrapper + private m1KeyPairs: KeyringPair[] + private m2KeyPairs: KeyringPair[] + private sudo: KeyringPair + private mintingCapacityIncrement: BN + + constructor( + apiWrapper: ApiWrapper, + m1KeyPairs: KeyringPair[], + m2KeyPairs: KeyringPair[], + sudo: KeyringPair, + mintingCapacityIncrement: BN + ) { + this.apiWrapper = apiWrapper + this.m1KeyPairs = m1KeyPairs + this.m2KeyPairs = m2KeyPairs + this.sudo = sudo + this.mintingCapacityIncrement = mintingCapacityIncrement + } + + public async runner(expectFailure: boolean): Promise { + // Setup + const description = 'Mint capacity proposal which is used for API network testing' + const runtimeVoteFee: BN = this.apiWrapper.estimateVoteForProposalFee() + const initialMintingCapacity: BN = await this.apiWrapper.getContentWorkingGroupMintCapacity() + + // Topping the balances + const proposalStake: BN = new BN(50000) + const runtimeProposalFee: BN = this.apiWrapper.estimateProposeContentWorkingGroupMintCapacityFee( + description, + description, + proposalStake, + initialMintingCapacity.add(this.mintingCapacityIncrement) + ) + await this.apiWrapper.transferBalance(this.sudo, this.m1KeyPairs[0].address, runtimeProposalFee.add(proposalStake)) + await this.apiWrapper.transferBalanceToAccounts(this.sudo, this.m2KeyPairs, runtimeVoteFee) + + // Proposal creation + const proposedMintingCapacity: BN = initialMintingCapacity.add(this.mintingCapacityIncrement) + const proposalPromise = this.apiWrapper.expectProposalCreated() + await this.apiWrapper.proposeContentWorkingGroupMintCapacity( + this.m1KeyPairs[0], + 'testing mint capacity' + uuid().substring(0, 8), + 'mint capacity to test proposal functionality' + uuid().substring(0, 8), + proposalStake, + proposedMintingCapacity + ) + const proposalNumber = await proposalPromise + + // Approving mint capacity proposal + const mintCapacityPromise = this.apiWrapper.expectProposalFinalized() + await this.apiWrapper.batchApproveProposal(this.m2KeyPairs, proposalNumber) + await mintCapacityPromise + const newMintingCapacity: BN = await this.apiWrapper.getContentWorkingGroupMintCapacity() + assert( + proposedMintingCapacity.eq(newMintingCapacity), + `Content working group has unexpected minting capacity ${newMintingCapacity}, expected ${proposedMintingCapacity}` + ) + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class UpdateRuntimeFixture implements Fixture { + private apiWrapper: ApiWrapper + private m1KeyPairs: KeyringPair[] + private m2KeyPairs: KeyringPair[] + private sudo: KeyringPair + + constructor(apiWrapper: ApiWrapper, m1KeyPairs: KeyringPair[], m2KeyPairs: KeyringPair[], sudo: KeyringPair) { + this.apiWrapper = apiWrapper + this.m1KeyPairs = m1KeyPairs + this.m2KeyPairs = m2KeyPairs + this.sudo = sudo + } + + public async runner(expectFailure: boolean): Promise { + // Setup + const runtime: Bytes = await this.apiWrapper.getRuntime() + const description = 'runtime upgrade proposal which is used for API network testing' + const runtimeVoteFee: BN = this.apiWrapper.estimateVoteForProposalFee() + + // Topping the balances + const proposalStake: BN = new BN(1000000) + const runtimeProposalFee: BN = this.apiWrapper.estimateProposeRuntimeUpgradeFee( + proposalStake, + description, + description, + runtime + ) + await this.apiWrapper.transferBalance(this.sudo, this.m1KeyPairs[0].address, runtimeProposalFee.add(proposalStake)) + await this.apiWrapper.transferBalanceToAccounts(this.sudo, this.m2KeyPairs, runtimeVoteFee) + + // Proposal creation + const proposalPromise = this.apiWrapper.expectProposalCreated() + await this.apiWrapper.proposeRuntime( + this.m1KeyPairs[0], + proposalStake, + 'testing runtime' + uuid().substring(0, 8), + 'runtime to test proposal functionality' + uuid().substring(0, 8), + runtime + ) + const proposalNumber = await proposalPromise + + // Approving runtime update proposal + const runtimePromise = this.apiWrapper.expectProposalFinalized() + await this.apiWrapper.batchApproveProposal(this.m2KeyPairs, proposalNumber) + await runtimePromise + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class VoteForProposalFixture implements Fixture { + private apiWrapper: ApiWrapper + private m2KeyPairs: KeyringPair[] + private sudo: KeyringPair + private proposalNumber: BN + + constructor(apiWrapper: ApiWrapper, m2KeyPairs: KeyringPair[], sudo: KeyringPair, proposalNumber: BN) { + this.apiWrapper = apiWrapper + this.m2KeyPairs = m2KeyPairs + this.sudo = sudo + this.proposalNumber = proposalNumber + } + + public async runner(expectFailure: boolean): Promise { + const proposalVoteFee: BN = this.apiWrapper.estimateVoteForProposalFee() + await this.apiWrapper.transferBalanceToAccounts(this.sudo, this.m2KeyPairs, proposalVoteFee) + + // Approving the proposal + const proposalExecutionPromise = this.apiWrapper.expectProposalFinalized() + await this.apiWrapper.batchApproveProposal(this.m2KeyPairs, this.proposalNumber) + await proposalExecutionPromise + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} diff --git a/tests/network-tests/src/iznik/tests/fixtures/workingGroupModule.ts b/tests/network-tests/src/iznik/tests/fixtures/workingGroupModule.ts new file mode 100644 index 0000000000..97328e4d7a --- /dev/null +++ b/tests/network-tests/src/iznik/tests/fixtures/workingGroupModule.ts @@ -0,0 +1,1139 @@ +import BN from 'bn.js' +import { assert } from 'chai' +import { ApiWrapper, WorkingGroups } from '../../utils/apiWrapper' +import { KeyringPair } from '@polkadot/keyring/types' +import { Balance, Event } from '@polkadot/types/interfaces' +import { Keyring } from '@polkadot/api' +import { v4 as uuid } from 'uuid' +import { RewardRelationship } from '@nicaea/types/recurring-rewards' +import { Worker, ApplicationIdToWorkerIdMap, Application } from '@nicaea/types/working-group' +import { Utils } from '../../utils/utils' +import { Opening as HiringOpening } from '@nicaea/types/hiring' +import { WorkingGroupOpening } from '../../dto/workingGroupOpening' +import { Fixture } from './interfaces/fixture' + +export class AddWorkerOpeningFixture implements Fixture { + private apiWrapper: ApiWrapper + private membersKeyPairs: KeyringPair[] + private lead: KeyringPair + private sudo: KeyringPair + private applicationStake: BN + private roleStake: BN + private activationDelay: BN + private unstakingPeriod: BN + private module: WorkingGroups + + private result: BN | undefined + + public getResult(): BN | undefined { + return this.result + } + + public constructor( + apiWrapper: ApiWrapper, + membersKeyPairs: KeyringPair[], + lead: KeyringPair, + sudo: KeyringPair, + applicationStake: BN, + roleStake: BN, + activationDelay: BN, + unstakingPeriod: BN, + module: WorkingGroups + ) { + this.apiWrapper = apiWrapper + this.membersKeyPairs = membersKeyPairs + this.lead = lead + this.sudo = sudo + this.applicationStake = applicationStake + this.roleStake = roleStake + this.activationDelay = activationDelay + this.unstakingPeriod = unstakingPeriod + this.module = module + } + + public async runner(expectFailure: boolean): Promise { + // Worker opening construction + const activateAtBlock: BN | undefined = this.activationDelay.eqn(0) + ? undefined + : (await this.apiWrapper.getBestBlock()).add(this.activationDelay) + const opening = new WorkingGroupOpening() + .setActivateAtBlock(activateAtBlock) + .setMaxActiveApplicants(new BN(this.membersKeyPairs.length)) + .setMaxReviewPeriodLength(new BN(32)) + .setApplicationStakingPolicyAmount(new BN(this.applicationStake)) + .setApplicationCrowdedOutUnstakingPeriodLength(new BN(1)) + .setApplicationExpiredUnstakingPeriodLength(new BN(1)) + .setRoleStakingPolicyAmount(new BN(this.roleStake)) + .setRoleCrowdedOutUnstakingPeriodLength(new BN(1)) + .setRoleExpiredUnstakingPeriodLength(new BN(1)) + .setSlashableMaxCount(new BN(1)) + .setSlashableMaxPercentPtsPerTime(new BN(100)) + .setSuccessfulApplicantApplicationStakeUnstakingPeriod(this.unstakingPeriod) + .setFailedApplicantApplicationStakeUnstakingPeriod(this.unstakingPeriod) + .setFailedApplicantRoleStakeUnstakingPeriod(this.unstakingPeriod) + .setTerminateApplicationStakeUnstakingPeriod(this.unstakingPeriod) + .setTerminateRoleStakeUnstakingPeriod(this.unstakingPeriod) + .setExitRoleApplicationStakeUnstakingPeriod(this.unstakingPeriod) + .setExitRoleStakeUnstakingPeriod(this.unstakingPeriod) + .setText(uuid().substring(0, 8)) + .setOpeningType('Worker') + + // Fee estimation and transfer + const addOpeningFee: BN = this.apiWrapper.estimateAddOpeningFee(opening, this.module) + await this.apiWrapper.transferBalance(this.sudo, this.lead.address, addOpeningFee) + + // Worker opening creation + const addOpeningPromise: Promise = this.apiWrapper.expectEvent('OpeningAdded') + await this.apiWrapper.addOpening(this.lead, opening, this.module, expectFailure) + if (!expectFailure) { + const openingId: BN = ((await addOpeningPromise).data[0] as unknown) as BN + console.log('received opening id ' + openingId) + this.result = openingId + } + } +} + +export class AddLeaderOpeningFixture implements Fixture { + private apiWrapper: ApiWrapper + private membersKeyPairs: KeyringPair[] + private sudo: KeyringPair + private applicationStake: BN + private roleStake: BN + private activationDelay: BN + private module: WorkingGroups + + private result: BN | undefined + + public getResult(): BN | undefined { + return this.result + } + + public constructor( + apiWrapper: ApiWrapper, + membersKeyPairs: KeyringPair[], + sudo: KeyringPair, + applicationStake: BN, + roleStake: BN, + activationDelay: BN, + module: WorkingGroups + ) { + this.apiWrapper = apiWrapper + this.membersKeyPairs = membersKeyPairs + this.sudo = sudo + this.applicationStake = applicationStake + this.roleStake = roleStake + this.activationDelay = activationDelay + this.module = module + } + + public async runner(expectFailure: boolean): Promise { + // Leader opening creation + const activateAtBlock: BN | undefined = this.activationDelay.eqn(0) + ? undefined + : (await this.apiWrapper.getBestBlock()).add(this.activationDelay) + const opening = new WorkingGroupOpening() + .setActivateAtBlock(activateAtBlock) + .setMaxActiveApplicants(new BN(this.membersKeyPairs.length)) + .setMaxReviewPeriodLength(new BN(32)) + .setApplicationStakingPolicyAmount(new BN(this.applicationStake)) + .setApplicationCrowdedOutUnstakingPeriodLength(new BN(1)) + .setApplicationExpiredUnstakingPeriodLength(new BN(1)) + .setRoleStakingPolicyAmount(new BN(this.roleStake)) + .setRoleCrowdedOutUnstakingPeriodLength(new BN(1)) + .setRoleExpiredUnstakingPeriodLength(new BN(1)) + .setSlashableMaxCount(new BN(1)) + .setSlashableMaxPercentPtsPerTime(new BN(100)) + .setSuccessfulApplicantApplicationStakeUnstakingPeriod(new BN(1)) + .setFailedApplicantApplicationStakeUnstakingPeriod(new BN(1)) + .setFailedApplicantRoleStakeUnstakingPeriod(new BN(1)) + .setTerminateApplicationStakeUnstakingPeriod(new BN(1)) + .setTerminateRoleStakeUnstakingPeriod(new BN(1)) + .setExitRoleApplicationStakeUnstakingPeriod(new BN(1)) + .setExitRoleStakeUnstakingPeriod(new BN(1)) + .setText(uuid().substring(0, 8)) + .setOpeningType('leader') + + const addOpeningPromise: Promise = this.apiWrapper.expectEvent('OpeningAdded') + await this.apiWrapper.sudoAddOpening(this.sudo, opening, this.module) + const openingId: BN = ((await addOpeningPromise).data[0] as unknown) as BN + this.result = openingId + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class AcceptApplicationsFixture implements Fixture { + private apiWrapper: ApiWrapper + private lead: KeyringPair + private sudo: KeyringPair + private openingId: BN + private module: WorkingGroups + + public constructor( + apiWrapper: ApiWrapper, + lead: KeyringPair, + sudo: KeyringPair, + openingId: BN, + module: WorkingGroups + ) { + this.apiWrapper = apiWrapper + this.lead = lead + this.sudo = sudo + this.openingId = openingId + this.module = module + } + + public async runner(expectFailure: boolean): Promise { + // Fee estimation and transfer + const acceptApplicationsFee = this.apiWrapper.estimateAcceptApplicationsFee(this.module) + await this.apiWrapper.transferBalance(this.sudo, this.lead.address, acceptApplicationsFee) + + // Begin accepting applications + await this.apiWrapper.acceptApplications(this.lead, this.openingId, this.module) + + const opening: HiringOpening = await this.apiWrapper.getHiringOpening(this.openingId) + assert(opening.is_active, `Opening ${this.openingId} is not active`) + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class ApplyForOpeningFixture implements Fixture { + private apiWrapper: ApiWrapper + private membersKeyPairs: KeyringPair[] + private sudo: KeyringPair + private applicationStake: BN + private roleStake: BN + private openingId: BN + private module: WorkingGroups + + public constructor( + apiWrapper: ApiWrapper, + membersKeyPairs: KeyringPair[], + sudo: KeyringPair, + applicationStake: BN, + roleStake: BN, + openingId: BN, + module: WorkingGroups + ) { + this.apiWrapper = apiWrapper + this.membersKeyPairs = membersKeyPairs + this.sudo = sudo + this.applicationStake = applicationStake + this.roleStake = roleStake + this.openingId = openingId + this.module = module + } + + public async runner(expectFailure: boolean): Promise { + // Fee estimation and transfer + const applyOnOpeningFee: BN = this.apiWrapper + .estimateApplyOnOpeningFee(this.sudo, this.module) + .add(this.applicationStake) + .add(this.roleStake) + await this.apiWrapper.transferBalanceToAccounts(this.sudo, this.membersKeyPairs, applyOnOpeningFee) + + // Applying for created worker opening + await this.apiWrapper.batchApplyOnOpening( + this.membersKeyPairs, + this.openingId, + this.roleStake, + this.applicationStake, + uuid().substring(0, 8), + this.module, + expectFailure + ) + } +} + +export class WithdrawApplicationFixture implements Fixture { + private apiWrapper: ApiWrapper + private membersKeyPairs: KeyringPair[] + private sudo: KeyringPair + private module: WorkingGroups + + constructor(apiWrapper: ApiWrapper, membersKeyPairs: KeyringPair[], sudo: KeyringPair, module: WorkingGroups) { + this.apiWrapper = apiWrapper + this.membersKeyPairs = membersKeyPairs + this.sudo = sudo + this.module = module + } + + public async runner(expectFailure: boolean): Promise { + // Fee estimation and transfer + const withdrawApplicaitonFee: BN = this.apiWrapper.estimateWithdrawApplicationFee(this.module) + await this.apiWrapper.transferBalanceToAccounts(this.sudo, this.membersKeyPairs, withdrawApplicaitonFee) + + // Application withdrawal + await this.apiWrapper.batchWithdrawApplication(this.membersKeyPairs, this.module) + + // Assertions + this.membersKeyPairs.forEach(async (keyPair) => { + const activeApplications: BN[] = await this.apiWrapper.getActiveApplicationsIdsByRoleAccount( + keyPair.address, + this.module + ) + assert(activeApplications.length === 0, `Unexpected active application found for ${keyPair.address}`) + }) + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class BeginApplicationReviewFixture implements Fixture { + private apiWrapper: ApiWrapper + private lead: KeyringPair + private sudo: KeyringPair + private openingId: BN + private module: WorkingGroups + + constructor(apiWrapper: ApiWrapper, lead: KeyringPair, sudo: KeyringPair, openingId: BN, module: WorkingGroups) { + this.apiWrapper = apiWrapper + this.lead = lead + this.sudo = sudo + this.openingId = openingId + this.module = module + } + + public async runner(expectFailure: boolean): Promise { + // Fee estimation and transfer + const beginReviewFee: BN = this.apiWrapper.estimateBeginApplicantReviewFee(this.module) + await this.apiWrapper.transferBalance(this.sudo, this.lead.address, beginReviewFee) + + // Begin application review + const beginApplicantReviewPromise: Promise = this.apiWrapper.expectApplicationReviewBegan() + await this.apiWrapper.beginApplicantReview(this.lead, this.openingId, this.module) + await beginApplicantReviewPromise + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class BeginLeaderApplicationReviewFixture implements Fixture { + private apiWrapper: ApiWrapper + private sudo: KeyringPair + private openingId: BN + private module: WorkingGroups + + constructor(apiWrapper: ApiWrapper, sudo: KeyringPair, openingId: BN, module: WorkingGroups) { + this.apiWrapper = apiWrapper + this.sudo = sudo + this.openingId = openingId + this.module = module + } + + public async runner(expectFailure: boolean): Promise { + // Begin application review + await this.apiWrapper.sudoBeginApplicantReview(this.sudo, this.openingId, this.module) + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class FillOpeningFixture implements Fixture { + private apiWrapper: ApiWrapper + private membersKeyPairs: KeyringPair[] + private lead: KeyringPair + private sudo: KeyringPair + private openingId: BN + private firstPayoutInterval: BN + private payoutInterval: BN + private amountPerPayout: BN + private module: WorkingGroups + + constructor( + apiWrapper: ApiWrapper, + membersKeyPairs: KeyringPair[], + lead: KeyringPair, + sudo: KeyringPair, + openingId: BN, + firstPayoutInterval: BN, + payoutInterval: BN, + amountPerPayout: BN, + module: WorkingGroups + ) { + this.apiWrapper = apiWrapper + this.membersKeyPairs = membersKeyPairs + this.lead = lead + this.sudo = sudo + this.openingId = openingId + this.firstPayoutInterval = firstPayoutInterval + this.payoutInterval = payoutInterval + this.amountPerPayout = amountPerPayout + this.module = module + } + + public async runner(expectFailure: boolean): Promise { + // Fee estimation and transfer + const beginReviewFee: BN = this.apiWrapper.estimateBeginApplicantReviewFee(this.module) + await this.apiWrapper.transferBalance(this.sudo, this.lead.address, beginReviewFee) + const applicationIds: BN[] = ( + await Promise.all( + this.membersKeyPairs.map(async (keypair) => + this.apiWrapper.getActiveApplicationsIdsByRoleAccount(keypair.address, this.module) + ) + ) + ).flat() + + // Fill worker opening + const now: BN = await this.apiWrapper.getBestBlock() + const fillOpeningPromise: Promise = this.apiWrapper.expectOpeningFilled() + await this.apiWrapper.fillOpening( + this.lead, + this.openingId, + applicationIds, + this.amountPerPayout, + now.add(this.firstPayoutInterval), + this.payoutInterval, + this.module + ) + const applicationIdToWorkerIdMap: ApplicationIdToWorkerIdMap = await fillOpeningPromise + + // Assertions + applicationIdToWorkerIdMap.forEach(async (workerId, applicationId) => { + const worker: Worker = await this.apiWrapper.getWorkerById(workerId, this.module) + const application: Application = await this.apiWrapper.getApplicationById(applicationId, this.module) + assert( + worker.role_account_id.toString() === application.role_account_id.toString(), + `Role account ids does not match, worker account: ${worker.role_account_id}, application account ${application.role_account_id}` + ) + }) + const openingWorkersAccounts: string[] = (await this.apiWrapper.getWorkers(this.module)).map((worker) => + worker.role_account_id.toString() + ) + this.membersKeyPairs.forEach((keyPair) => + assert(openingWorkersAccounts.includes(keyPair.address), `Account ${keyPair.address} is not worker`) + ) + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class FillLeaderOpeningFixture implements Fixture { + private apiWrapper: ApiWrapper + private membersKeyPairs: KeyringPair[] + private sudo: KeyringPair + private openingId: BN + private firstPayoutInterval: BN + private payoutInterval: BN + private amountPerPayout: BN + private module: WorkingGroups + + constructor( + apiWrapper: ApiWrapper, + membersKeyPairs: KeyringPair[], + sudo: KeyringPair, + openingId: BN, + firstPayoutInterval: BN, + payoutInterval: BN, + amountPerPayout: BN, + module: WorkingGroups + ) { + this.apiWrapper = apiWrapper + this.membersKeyPairs = membersKeyPairs + this.sudo = sudo + this.openingId = openingId + this.firstPayoutInterval = firstPayoutInterval + this.payoutInterval = payoutInterval + this.amountPerPayout = amountPerPayout + this.module = module + } + + public async runner(expectFailure: boolean): Promise { + const applicationIds: BN[] = ( + await Promise.all( + this.membersKeyPairs.map(async (keypair) => + this.apiWrapper.getActiveApplicationsIdsByRoleAccount(keypair.address, this.module) + ) + ) + ).flat() + + // Fill leader opening + const now: BN = await this.apiWrapper.getBestBlock() + const fillOpeningPromise: Promise = this.apiWrapper.expectOpeningFilled() + await this.apiWrapper.sudoFillOpening( + this.sudo, + this.openingId, + applicationIds, + this.amountPerPayout, + now.add(this.firstPayoutInterval), + this.payoutInterval, + this.module + ) + + // Assertions + const applicationIdToWorkerIdMap: ApplicationIdToWorkerIdMap = await fillOpeningPromise + applicationIdToWorkerIdMap.forEach(async (workerId, applicationId) => { + const worker: Worker = await this.apiWrapper.getWorkerById(workerId, this.module) + const application: Application = await this.apiWrapper.getApplicationById(applicationId, this.module) + assert( + worker.role_account_id.toString() === application.role_account_id.toString(), + `Role account ids does not match, leader account: ${worker.role_account_id}, application account ${application.role_account_id}` + ) + }) + const leadWorkerId: BN = (await this.apiWrapper.getLeadWorkerId(this.module))! + const openingLeaderAccount: string = ( + await this.apiWrapper.getWorkerById(leadWorkerId, this.module) + ).role_account_id.toString() + assert( + openingLeaderAccount === this.membersKeyPairs[0].address, + `Unexpected leader account ${openingLeaderAccount}, expected ${this.membersKeyPairs[0].address}` + ) + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class IncreaseStakeFixture implements Fixture { + private apiWrapper: ApiWrapper + private membersKeyPairs: KeyringPair[] + private sudo: KeyringPair + private module: WorkingGroups + + constructor(apiWrapper: ApiWrapper, membersKeyPairs: KeyringPair[], sudo: KeyringPair, module: WorkingGroups) { + this.apiWrapper = apiWrapper + this.membersKeyPairs = membersKeyPairs + this.sudo = sudo + this.module = module + } + + public async runner(expectFailure: boolean): Promise { + // Fee estimation and transfer + const increaseStakeFee: BN = this.apiWrapper.estimateIncreaseStakeFee(this.module) + const stakeIncrement: BN = new BN(1) + await this.apiWrapper.transferBalance( + this.sudo, + this.membersKeyPairs[0].address, + increaseStakeFee.add(stakeIncrement) + ) + const workerId: BN = await this.apiWrapper.getWorkerIdByRoleAccount(this.membersKeyPairs[0].address, this.module) + + // Increase worker stake + const increasedWorkerStake: BN = (await this.apiWrapper.getWorkerStakeAmount(workerId, this.module)).add( + stakeIncrement + ) + await this.apiWrapper.increaseStake(this.membersKeyPairs[0], workerId, stakeIncrement, this.module) + const newWorkerStake: BN = await this.apiWrapper.getWorkerStakeAmount(workerId, this.module) + assert( + increasedWorkerStake.eq(newWorkerStake), + `Unexpected worker stake ${newWorkerStake}, expected ${increasedWorkerStake}` + ) + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class UpdateRewardAccountFixture implements Fixture { + public apiWrapper: ApiWrapper + public membersKeyPairs: KeyringPair[] + public keyring: Keyring + public sudo: KeyringPair + public module: WorkingGroups + + constructor( + apiWrapper: ApiWrapper, + membersKeyPairs: KeyringPair[], + keyring: Keyring, + sudo: KeyringPair, + module: WorkingGroups + ) { + this.apiWrapper = apiWrapper + this.membersKeyPairs = membersKeyPairs + this.keyring = keyring + this.sudo = sudo + this.module = module + } + + public async runner(expectFailure: boolean): Promise { + // Fee estimation and transfer + const updateRewardAccountFee: BN = this.apiWrapper.estimateUpdateRewardAccountFee(this.sudo.address, this.module) + await this.apiWrapper.transferBalance(this.sudo, this.membersKeyPairs[0].address, updateRewardAccountFee) + const workerId: BN = await this.apiWrapper.getWorkerIdByRoleAccount(this.membersKeyPairs[0].address, this.module) + + // Update reward account + const createdAccount: KeyringPair = this.keyring.addFromUri(uuid().substring(0, 8)) + await this.apiWrapper.updateRewardAccount(this.membersKeyPairs[0], workerId, createdAccount.address, this.module) + const newRewardAccount: string = await this.apiWrapper.getWorkerRewardAccount(workerId, this.module) + assert( + newRewardAccount === createdAccount.address, + `Unexpected role account ${newRewardAccount}, expected ${createdAccount.address}` + ) + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class UpdateRoleAccountFixture implements Fixture { + private apiWrapper: ApiWrapper + private membersKeyPairs: KeyringPair[] + private keyring: Keyring + private sudo: KeyringPair + private module: WorkingGroups + + constructor( + apiWrapper: ApiWrapper, + membersKeyPairs: KeyringPair[], + keyring: Keyring, + sudo: KeyringPair, + module: WorkingGroups + ) { + this.apiWrapper = apiWrapper + this.membersKeyPairs = membersKeyPairs + this.keyring = keyring + this.sudo = sudo + this.module = module + } + + public async runner(expectFailure: boolean): Promise { + // Fee estimation and transfer + const updateRoleAccountFee: BN = this.apiWrapper.estimateUpdateRoleAccountFee(this.sudo.address, this.module) + await this.apiWrapper.transferBalance(this.sudo, this.membersKeyPairs[0].address, updateRoleAccountFee) + const workerId: BN = await this.apiWrapper.getWorkerIdByRoleAccount(this.membersKeyPairs[0].address, this.module) + + // Update role account + const createdAccount: KeyringPair = this.keyring.addFromUri(uuid().substring(0, 8)) + await this.apiWrapper.updateRoleAccount(this.membersKeyPairs[0], workerId, createdAccount.address, this.module) + const newRoleAccount: string = ( + await this.apiWrapper.getWorkerById(workerId, this.module) + ).role_account_id.toString() + assert( + newRoleAccount === createdAccount.address, + `Unexpected role account ${newRoleAccount}, expected ${createdAccount.address}` + ) + + this.membersKeyPairs[0] = createdAccount + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class TerminateApplicationsFixture implements Fixture { + private apiWrapper: ApiWrapper + private membersKeyPairs: KeyringPair[] + private lead: KeyringPair + private sudo: KeyringPair + private module: WorkingGroups + + constructor( + apiWrapper: ApiWrapper, + membersKeyPairs: KeyringPair[], + lead: KeyringPair, + sudo: KeyringPair, + module: WorkingGroups + ) { + this.apiWrapper = apiWrapper + this.membersKeyPairs = membersKeyPairs + this.lead = lead + this.sudo = sudo + this.module = module + } + + public async runner(expectFailure: boolean): Promise { + // Fee estimation and transfer + const terminateApplicationFee = this.apiWrapper.estimateTerminateApplicationFee(this.module) + await this.apiWrapper.transferBalance( + this.sudo, + this.lead.address, + terminateApplicationFee.muln(this.membersKeyPairs.length) + ) + + // Terminate worker applications + await this.apiWrapper.batchTerminateApplication(this.lead, this.membersKeyPairs, this.module) + this.membersKeyPairs.forEach(async (keyPair) => { + const activeApplications = await this.apiWrapper.getActiveApplicationsIdsByRoleAccount( + keyPair.address, + this.module + ) + assert(activeApplications.length === 0, `Account ${keyPair.address} has unexpected active applications`) + }) + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class DecreaseStakeFixture implements Fixture { + private apiWrapper: ApiWrapper + private membersKeyPairs: KeyringPair[] + private lead: KeyringPair + private sudo: KeyringPair + private module: WorkingGroups + + constructor( + apiWrapper: ApiWrapper, + membersKeyPairs: KeyringPair[], + lead: KeyringPair, + sudo: KeyringPair, + module: WorkingGroups + ) { + this.apiWrapper = apiWrapper + this.membersKeyPairs = membersKeyPairs + this.lead = lead + this.sudo = sudo + this.module = module + } + + public async runner(expectFailure: boolean): Promise { + // Fee estimation and transfer + const decreaseStakeFee = this.apiWrapper.estimateDecreaseStakeFee(this.module) + await this.apiWrapper.transferBalance(this.sudo, this.lead.address, decreaseStakeFee) + const workerStakeDecrement = new BN(1) + const workerId: BN = await this.apiWrapper.getWorkerIdByRoleAccount(this.membersKeyPairs[0].address, this.module) + + // Worker stake decrement + const decreasedWorkerStake: BN = (await this.apiWrapper.getWorkerStakeAmount(workerId, this.module)).sub( + workerStakeDecrement + ) + await this.apiWrapper.decreaseStake(this.lead, workerId, workerStakeDecrement, this.module, expectFailure) + const newWorkerStake: BN = await this.apiWrapper.getWorkerStakeAmount(workerId, this.module) + + // Assertions + if (!expectFailure) { + assert( + decreasedWorkerStake.eq(newWorkerStake), + `Unexpected worker stake ${newWorkerStake}, expected ${decreasedWorkerStake}` + ) + } + } +} + +export class SlashFixture implements Fixture { + private apiWrapper: ApiWrapper + private membersKeyPairs: KeyringPair[] + private lead: KeyringPair + private sudo: KeyringPair + private module: WorkingGroups + + constructor( + apiWrapper: ApiWrapper, + membersKeyPairs: KeyringPair[], + lead: KeyringPair, + sudo: KeyringPair, + module: WorkingGroups + ) { + this.apiWrapper = apiWrapper + this.membersKeyPairs = membersKeyPairs + this.lead = lead + this.sudo = sudo + this.module = module + } + + public async runner(expectFailure: boolean): Promise { + // Fee estimation and transfer + const slashStakeFee = this.apiWrapper.estimateSlashStakeFee(this.module) + await this.apiWrapper.transferBalance(this.sudo, this.lead.address, slashStakeFee) + const slashAmount = new BN(1) + const workerId: BN = await this.apiWrapper.getWorkerIdByRoleAccount(this.membersKeyPairs[0].address, this.module) + + // Slash worker + const slashedStake: BN = (await this.apiWrapper.getWorkerStakeAmount(workerId, this.module)).sub(slashAmount) + await this.apiWrapper.slashStake(this.lead, workerId, slashAmount, this.module, expectFailure) + const newStake: BN = await this.apiWrapper.getWorkerStakeAmount(workerId, this.module) + + // Assertions + assert(slashedStake.eq(newStake), `Unexpected worker stake ${newStake}, expected ${slashedStake}`) + } +} + +export class TerminateRoleFixture implements Fixture { + private apiWrapper: ApiWrapper + private membersKeyPairs: KeyringPair[] + private lead: KeyringPair + private sudo: KeyringPair + private module: WorkingGroups + + constructor( + apiWrapper: ApiWrapper, + membersKeyPairs: KeyringPair[], + lead: KeyringPair, + sudo: KeyringPair, + module: WorkingGroups + ) { + this.apiWrapper = apiWrapper + this.membersKeyPairs = membersKeyPairs + this.lead = lead + this.sudo = sudo + this.module = module + } + + public async runner(expectFailure: boolean): Promise { + // Fee estimation and transfer + const terminateRoleFee = this.apiWrapper.estimateTerminateRoleFee(this.module) + await this.apiWrapper.transferBalance(this.sudo, this.lead.address, terminateRoleFee) + const workerId: BN = await this.apiWrapper.getWorkerIdByRoleAccount(this.membersKeyPairs[0].address, this.module) + + // Slash worker + await this.apiWrapper.terminateRole(this.lead, workerId, uuid().substring(0, 8), this.module, expectFailure) + + // Assertions + this.apiWrapper.getWorkerIdByRoleAccount(this.membersKeyPairs[0].address, this.module) + const newWorkerId = await this.apiWrapper.getWorkerIdByRoleAccount(this.membersKeyPairs[0].address, this.module) + assert(newWorkerId === undefined, `Worker with account ${this.membersKeyPairs[0].address} is not terminated`) + } +} + +export class LeaveRoleFixture implements Fixture { + private apiWrapper: ApiWrapper + private membersKeyPairs: KeyringPair[] + private sudo: KeyringPair + private module: WorkingGroups + + constructor(apiWrapper: ApiWrapper, membersKeyPairs: KeyringPair[], sudo: KeyringPair, module: WorkingGroups) { + this.apiWrapper = apiWrapper + this.membersKeyPairs = membersKeyPairs + this.sudo = sudo + this.module = module + } + + public async runner(expectFailure: boolean): Promise { + // Fee estimation and transfer + const leaveRoleFee = this.apiWrapper.estimateLeaveRoleFee(this.module) + await this.apiWrapper.transferBalanceToAccounts(this.sudo, this.membersKeyPairs, leaveRoleFee) + + await this.apiWrapper.batchLeaveRole(this.membersKeyPairs, uuid().substring(0, 8), expectFailure, this.module) + + // Assertions + this.membersKeyPairs.forEach(async (keyPair) => { + this.apiWrapper.getWorkerIdByRoleAccount(keyPair.address, this.module) + const newWorkerId = await this.apiWrapper.getWorkerIdByRoleAccount(keyPair.address, this.module) + assert(newWorkerId === undefined, `Worker with account ${keyPair.address} is not terminated`) + }) + } +} + +export class AwaitPayoutFixture implements Fixture { + private apiWrapper: ApiWrapper + private membersKeyPairs: KeyringPair[] + private module: WorkingGroups + + constructor(apiWrapper: ApiWrapper, membersKeyPairs: KeyringPair[], module: WorkingGroups) { + this.apiWrapper = apiWrapper + this.membersKeyPairs = membersKeyPairs + this.module = module + } + + public async runner(expectFailure: boolean): Promise { + const workerId: BN = await this.apiWrapper.getWorkerIdByRoleAccount(this.membersKeyPairs[0].address, this.module) + const worker: Worker = await this.apiWrapper.getWorkerById(workerId, this.module) + const reward: RewardRelationship = await this.apiWrapper.getRewardRelationship(worker.reward_relationship.unwrap()) + const now: BN = await this.apiWrapper.getBestBlock() + const nextPaymentBlock: BN = new BN(reward.getField('next_payment_at_block').toString()) + const payoutInterval: BN = new BN(reward.getField('payout_interval').toString()) + const amountPerPayout: BN = new BN(reward.getField('amount_per_payout').toString()) + + assert(now.lt(nextPaymentBlock), `Payout already happened in block ${nextPaymentBlock} now ${now}`) + const balance = await this.apiWrapper.getBalance(this.membersKeyPairs[0].address) + + const firstPayoutWaitingPeriod = nextPaymentBlock.sub(now).addn(1) + await Utils.wait(this.apiWrapper.getBlockDuration().mul(firstPayoutWaitingPeriod).toNumber()) + + const balanceAfterFirstPayout = await this.apiWrapper.getBalance(this.membersKeyPairs[0].address) + const expectedBalanceFirst = balance.add(amountPerPayout) + assert( + balanceAfterFirstPayout.eq(expectedBalanceFirst), + `Unexpected balance, expected ${expectedBalanceFirst} got ${balanceAfterFirstPayout}` + ) + + const secondPayoutWaitingPeriod = payoutInterval.addn(1) + await Utils.wait(this.apiWrapper.getBlockDuration().mul(secondPayoutWaitingPeriod).toNumber()) + + const balanceAfterSecondPayout = await this.apiWrapper.getBalance(this.membersKeyPairs[0].address) + const expectedBalanceSecond = expectedBalanceFirst.add(amountPerPayout) + assert( + balanceAfterSecondPayout.eq(expectedBalanceSecond), + `Unexpected balance, expected ${expectedBalanceSecond} got ${balanceAfterSecondPayout}` + ) + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class ExpectLeadOpeningAddedFixture implements Fixture { + private apiWrapper: ApiWrapper + + // Opening id + private result: BN | undefined + private events: Event[] = [] + + constructor(apiWrapper: ApiWrapper) { + this.apiWrapper = apiWrapper + } + + public getResult(): BN | undefined { + return this.result + } + + public getEvents(): Event[] { + return this.events + } + + public async runner(expectFailure: boolean): Promise { + const event: Event = await this.apiWrapper.expectEvent('OpeningAdded') + this.events.push(event) + this.result = (event.data as unknown) as BN + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class ExpectLeaderSetFixture implements Fixture { + private apiWrapper: ApiWrapper + private leaderAddress: string + private module: WorkingGroups + + // Lead worker id + private result: BN | undefined + private events: Event[] = [] + + constructor(apiWrapper: ApiWrapper, leaderAddress: string, module: WorkingGroups) { + this.apiWrapper = apiWrapper + this.leaderAddress = leaderAddress + this.module = module + } + + public getResult(): BN | undefined { + return this.result + } + + public getEvents(): Event[] { + return this.events + } + + public async runner(expectFailure: boolean): Promise { + const event: Event = await this.apiWrapper.expectEvent('LeaderSet') + this.events.push(event) + const leadWorkerId: BN = (event.data as unknown) as BN + const worker: Worker = await this.apiWrapper.getWorkerById(leadWorkerId, this.module) + const leaderApplicationId = ( + await this.apiWrapper.getApplicationsIdsByRoleAccount(this.leaderAddress, this.module) + )[0] + const application: Application = await this.apiWrapper.getApplicationById(leaderApplicationId, this.module) + assert( + worker.role_account_id.eq(application.role_account_id), + `Role account ids does not match, leader account: ${worker.role_account_id}, application account ${application.role_account_id}` + ) + this.result = leadWorkerId + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class ExpectBeganApplicationReviewFixture implements Fixture { + private apiWrapper: ApiWrapper + + private result: BN | undefined + private events: Event[] = [] + + constructor(apiWrapper: ApiWrapper) { + this.apiWrapper = apiWrapper + } + + public getResult(): BN | undefined { + return this.result + } + + public getEvents(): Event[] { + return this.events + } + + public async runner(expectFailure: boolean): Promise { + const event: Event = await this.apiWrapper.expectEvent('BeganApplicationReview') + this.events.push(event) + this.result = (event.data as unknown) as BN + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class ExpectLeaderRoleTerminatedFixture implements Fixture { + private apiWrapper: ApiWrapper + private module: WorkingGroups + + private result: BN | undefined + private events: Event[] = [] + + constructor(apiWrapper: ApiWrapper, module: WorkingGroups) { + this.apiWrapper = apiWrapper + this.module = module + } + + public getResult(): BN | undefined { + return this.result + } + + public getEvents(): Event[] { + return this.events + } + + public async runner(expectFailure: boolean): Promise { + const event: Event = await this.apiWrapper.expectEvent('TerminatedLeader') + this.events.push(event) + const leadWorkerId: BN | undefined = await this.apiWrapper.getLeadWorkerId(this.module) + assert(leadWorkerId === undefined, `Unexpected lead worker id: ${leadWorkerId}, expected none`) + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class ExpectLeaderRewardAmountUpdatedFixture implements Fixture { + private apiWrapper: ApiWrapper + private expectedReward: BN + private module: WorkingGroups + + private result: BN | undefined + private events: Event[] = [] + + constructor(apiWrapper: ApiWrapper, expectedReward: BN, module: WorkingGroups) { + this.apiWrapper = apiWrapper + this.expectedReward = expectedReward + this.module = module + } + + public getResult(): BN | undefined { + return this.result + } + + public getEvents(): Event[] { + return this.events + } + + public async runner(expectFailure: boolean): Promise { + const event: Event = await this.apiWrapper.expectEvent('WorkerRewardAmountUpdated') + this.events.push(event) + const leadWorkerId: BN = (await this.apiWrapper.getLeadWorkerId(this.module))! + const receivedReward: BN = (await this.apiWrapper.getRewardRelationship(leadWorkerId)).getField( + 'amount_per_payout' + ) + assert( + receivedReward.eq(this.expectedReward), + `Unexpected reward amount for worker with id ${leadWorkerId}: ${receivedReward}, expected ${this.expectedReward}` + ) + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class ExpectLeaderStakeDecreasedFixture implements Fixture { + private apiWrapper: ApiWrapper + private expectedStake: BN + private module: WorkingGroups + + private result: BN | undefined + private events: Event[] = [] + + constructor(apiWrapper: ApiWrapper, expectedStake: BN, module: WorkingGroups) { + this.apiWrapper = apiWrapper + this.expectedStake = expectedStake + this.module = module + } + + public getResult(): BN | undefined { + return this.result + } + + public getEvents(): Event[] { + return this.events + } + + public async runner(expectFailure: boolean): Promise { + const event: Event = await this.apiWrapper.expectEvent('StakeDecreased') + this.events.push(event) + const leadWorkerId: BN = (await this.apiWrapper.getLeadWorkerId(this.module))! + const receivedStake: BN = await this.apiWrapper.getWorkerStakeAmount(leadWorkerId, this.module) + assert( + receivedStake.eq(this.expectedStake), + `Unexpected stake amount for worker with id ${leadWorkerId}: ${receivedStake}, expected ${this.expectedStake}` + ) + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class ExpectLeaderSlashedFixture implements Fixture { + private apiWrapper: ApiWrapper + private expectedStake: BN + private module: WorkingGroups + + private result: BN | undefined + private events: Event[] = [] + + constructor(apiWrapper: ApiWrapper, expectedStake: BN, module: WorkingGroups) { + this.apiWrapper = apiWrapper + this.expectedStake = expectedStake + this.module = module + } + + public getResult(): BN | undefined { + return this.result + } + + public getEvents(): Event[] { + return this.events + } + + public async runner(expectFailure: boolean): Promise { + const event: Event = await this.apiWrapper.expectEvent('StakeSlashed') + this.events.push(event) + const leadWorkerId: BN = (await this.apiWrapper.getLeadWorkerId(this.module))! + const receivedStake: BN = await this.apiWrapper.getWorkerStakeAmount(leadWorkerId, this.module) + assert( + receivedStake.eq(this.expectedStake), + `Unexpected stake amount for worker with id after slash ${leadWorkerId}: ${receivedStake}, expected ${this.expectedStake}` + ) + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} + +export class ExpectMintCapacityChangedFixture implements Fixture { + private apiWrapper: ApiWrapper + private expectedMintCapacity: BN + + private result: BN | undefined + private events: Event[] = [] + + constructor(apiWrapper: ApiWrapper, expectedMintCapacity: BN) { + this.apiWrapper = apiWrapper + this.expectedMintCapacity = expectedMintCapacity + } + + public getResult(): BN | undefined { + return this.result + } + + public getEvents(): Event[] { + return this.events + } + + public async runner(expectFailure: boolean): Promise { + const event: Event = await this.apiWrapper.expectEvent('MintCapacityChanged') + this.events.push(event) + const receivedMintCapacity = (event.data[1] as unknown) as BN + assert( + receivedMintCapacity.eq(this.expectedMintCapacity), + `Unexpected mint capacity: ${receivedMintCapacity}, expected ${this.expectedMintCapacity}` + ) + this.result = receivedMintCapacity + if (expectFailure) { + throw new Error('Successful fixture run while expecting failure') + } + } +} diff --git a/tests/network-tests/src/iznik/tests/membership/membershipCreationTest.ts b/tests/network-tests/src/iznik/tests/membership/membershipCreationTest.ts new file mode 100644 index 0000000000..1592f58655 --- /dev/null +++ b/tests/network-tests/src/iznik/tests/membership/membershipCreationTest.ts @@ -0,0 +1,56 @@ +import { KeyringPair } from '@polkadot/keyring/types' +import { Keyring, WsProvider } from '@polkadot/api' +import { initConfig } from '../../utils/config' +import { setTestTimeout } from '../../utils/setTestTimeout' +import tap from 'tap' +import { registerJoystreamTypes } from '@nicaea/types' +import { ApiWrapper } from '../../utils/apiWrapper' +import { closeApi } from '../../utils/closeApi' +import { BuyMembershipHappyCaseFixture, BuyMembershipWithInsufficienFundsFixture } from '../fixtures/membershipModule' +import { Utils } from '../../utils/utils' + +tap.mocha.describe('Membership creation scenario', async () => { + initConfig() + registerJoystreamTypes() + + const nodeUrl: string = process.env.NODE_URL! + const sudoUri: string = process.env.SUDO_ACCOUNT_URI! + const keyring = new Keyring({ type: 'sr25519' }) + const provider = new WsProvider(nodeUrl) + const apiWrapper: ApiWrapper = await ApiWrapper.create(provider) + const sudo: KeyringPair = keyring.addFromUri(sudoUri) + + const N: number = +process.env.MEMBERSHIP_CREATION_N! + const nKeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N) + const aKeyPair: KeyringPair[] = Utils.createKeyPairs(keyring, 1) + const paidTerms: number = +process.env.MEMBERSHIP_PAID_TERMS! + + const durationInBlocks = 7 + + setTestTimeout(apiWrapper, durationInBlocks) + + const happyCaseFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + nKeyPairs, + paidTerms + ) + tap.test('Buy membeship is accepted with sufficient funds', async () => happyCaseFixture.runner(false)) + + const insufficientFundsFixture: BuyMembershipWithInsufficienFundsFixture = new BuyMembershipWithInsufficienFundsFixture( + apiWrapper, + sudo, + aKeyPair[0], + paidTerms + ) + tap.test('Account A can not buy the membership with insufficient funds', async () => + insufficientFundsFixture.runner(false) + ) + + const buyMembershipAfterAccountTopUp = new BuyMembershipHappyCaseFixture(apiWrapper, sudo, aKeyPair, paidTerms) + tap.test('Account A was able to buy the membership with sufficient funds', async () => + buyMembershipAfterAccountTopUp.runner(false) + ) + + closeApi(apiWrapper) +}) diff --git a/tests/network-tests/src/iznik/tests/proposals/contentWorkingGroupMintCapacityProposalTest.ts b/tests/network-tests/src/iznik/tests/proposals/contentWorkingGroupMintCapacityProposalTest.ts new file mode 100644 index 0000000000..d7d7ba3b90 --- /dev/null +++ b/tests/network-tests/src/iznik/tests/proposals/contentWorkingGroupMintCapacityProposalTest.ts @@ -0,0 +1,79 @@ +import { KeyringPair } from '@polkadot/keyring/types' +import { initConfig } from '../../utils/config' +import { Keyring, WsProvider } from '@polkadot/api' +import BN from 'bn.js' +import { setTestTimeout } from '../../utils/setTestTimeout' +import tap from 'tap' +import { registerJoystreamTypes } from '@nicaea/types' +import { closeApi } from '../../utils/closeApi' +import { ApiWrapper } from '../../utils/apiWrapper' +import { ContentWorkingGroupMintCapacityProposalFixture } from '../fixtures/proposalsModule' +import { BuyMembershipHappyCaseFixture } from '../fixtures/membershipModule' +import { ElectCouncilFixture } from '../fixtures/councilElectionModule' +import { Utils } from '../../utils/utils' + +tap.mocha.describe('Validator count proposal scenario', async () => { + initConfig() + registerJoystreamTypes() + + const nodeUrl: string = process.env.NODE_URL! + const sudoUri: string = process.env.SUDO_ACCOUNT_URI! + const keyring = new Keyring({ type: 'sr25519' }) + const provider = new WsProvider(nodeUrl) + const apiWrapper: ApiWrapper = await ApiWrapper.create(provider) + const sudo: KeyringPair = keyring.addFromUri(sudoUri) + + const N: number = +process.env.MEMBERSHIP_CREATION_N! + const m1KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N) + const m2KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N) + + const paidTerms: number = +process.env.MEMBERSHIP_PAID_TERMS! + const K: number = +process.env.COUNCIL_ELECTION_K! + const greaterStake: BN = new BN(+process.env.COUNCIL_STAKE_GREATER_AMOUNT!) + const lesserStake: BN = new BN(+process.env.COUNCIL_STAKE_LESSER_AMOUNT!) + const mintingCapacityIncrement: BN = new BN(+process.env.MINTING_CAPACITY_INCREMENT!) + + const durationInBlocks = 29 + + setTestTimeout(apiWrapper, durationInBlocks) + + const firstMemberSetFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + m1KeyPairs, + paidTerms + ) + tap.test('Creating first set of members', async () => firstMemberSetFixture.runner(false)) + + const secondMemberSetFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + m2KeyPairs, + paidTerms + ) + tap.test('Creating second set of members', async () => secondMemberSetFixture.runner(false)) + + const electCouncilFixture: ElectCouncilFixture = new ElectCouncilFixture( + apiWrapper, + m1KeyPairs, + m2KeyPairs, + K, + sudo, + greaterStake, + lesserStake + ) + tap.test('Elect council', async () => electCouncilFixture.runner(false)) + + const contentWorkingGroupMintCapacityProposalFixture: ContentWorkingGroupMintCapacityProposalFixture = new ContentWorkingGroupMintCapacityProposalFixture( + apiWrapper, + m1KeyPairs, + m2KeyPairs, + sudo, + mintingCapacityIncrement + ) + tap.test('Content working group mint capacity test', async () => + contentWorkingGroupMintCapacityProposalFixture.runner(false) + ) + + closeApi(apiWrapper) +}) diff --git a/tests/network-tests/src/iznik/tests/proposals/electionParametersProposalTest.ts b/tests/network-tests/src/iznik/tests/proposals/electionParametersProposalTest.ts new file mode 100644 index 0000000000..208810c924 --- /dev/null +++ b/tests/network-tests/src/iznik/tests/proposals/electionParametersProposalTest.ts @@ -0,0 +1,75 @@ +import { KeyringPair } from '@polkadot/keyring/types' +import { initConfig } from '../../utils/config' +import { Keyring, WsProvider } from '@polkadot/api' +import BN from 'bn.js' +import { setTestTimeout } from '../../utils/setTestTimeout' +import tap from 'tap' +import { registerJoystreamTypes } from '@nicaea/types' +import { closeApi } from '../../utils/closeApi' +import { ApiWrapper } from '../../utils/apiWrapper' +import { BuyMembershipHappyCaseFixture } from '../fixtures/membershipModule' +import { ElectCouncilFixture } from '../fixtures/councilElectionModule' +import { Utils } from '../../utils/utils' +import { ElectionParametersProposalFixture } from '../fixtures/proposalsModule' + +tap.mocha.describe('Election parameters proposal scenario', async () => { + initConfig() + registerJoystreamTypes() + + const nodeUrl: string = process.env.NODE_URL! + const sudoUri: string = process.env.SUDO_ACCOUNT_URI! + const keyring = new Keyring({ type: 'sr25519' }) + const provider = new WsProvider(nodeUrl) + const apiWrapper: ApiWrapper = await ApiWrapper.create(provider) + const sudo: KeyringPair = keyring.addFromUri(sudoUri) + + const N: number = +process.env.MEMBERSHIP_CREATION_N! + const m1KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N) + const m2KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N) + + const paidTerms: number = +process.env.MEMBERSHIP_PAID_TERMS! + const K: number = +process.env.COUNCIL_ELECTION_K! + const greaterStake: BN = new BN(+process.env.COUNCIL_STAKE_GREATER_AMOUNT!) + const lesserStake: BN = new BN(+process.env.COUNCIL_STAKE_LESSER_AMOUNT!) + + const durationInBlocks = 29 + + setTestTimeout(apiWrapper, durationInBlocks) + + const firstMemberSetFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + m1KeyPairs, + paidTerms + ) + tap.test('Creating first set of members', async () => firstMemberSetFixture.runner(false)) + + const secondMemberSetFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + m2KeyPairs, + paidTerms + ) + tap.test('Creating second set of members', async () => secondMemberSetFixture.runner(false)) + + const electCouncilFixture: ElectCouncilFixture = new ElectCouncilFixture( + apiWrapper, + m1KeyPairs, + m2KeyPairs, + K, + sudo, + greaterStake, + lesserStake + ) + tap.test('Elect council', async () => electCouncilFixture.runner(false)) + + const electionParametersProposalFixture: ElectionParametersProposalFixture = new ElectionParametersProposalFixture( + apiWrapper, + m1KeyPairs, + m2KeyPairs, + sudo + ) + tap.test('Election parameters proposal test', async () => electionParametersProposalFixture.runner(false)) + + closeApi(apiWrapper) +}) diff --git a/tests/network-tests/src/iznik/tests/proposals/manageLeaderRole.ts b/tests/network-tests/src/iznik/tests/proposals/manageLeaderRole.ts new file mode 100644 index 0000000000..0c698d172d --- /dev/null +++ b/tests/network-tests/src/iznik/tests/proposals/manageLeaderRole.ts @@ -0,0 +1,304 @@ +import { KeyringPair } from '@polkadot/keyring/types' +import { initConfig } from '../../utils/config' +import { Keyring, WsProvider } from '@polkadot/api' +import BN from 'bn.js' +import { setTestTimeout } from '../../utils/setTestTimeout' +import tap from 'tap' +import { registerJoystreamTypes } from '@nicaea/types' +import { closeApi } from '../../utils/closeApi' +import { ApiWrapper, WorkingGroups } from '../../utils/apiWrapper' +import { BuyMembershipHappyCaseFixture } from '../fixtures/membershipModule' +import { ElectCouncilFixture } from '../fixtures/councilElectionModule' +import { + BeginWorkingGroupLeaderApplicationReviewFixture, + CreateWorkingGroupLeaderOpeningFixture, + DecreaseLeaderStakeProposalFixture, + FillLeaderOpeningProposalFixture, + SetLeaderRewardProposalFixture, + SlashLeaderProposalFixture, + TerminateLeaderRoleProposalFixture, + VoteForProposalFixture, +} from '../fixtures/proposalsModule' +import { + ApplyForOpeningFixture, + ExpectBeganApplicationReviewFixture, + ExpectLeaderRewardAmountUpdatedFixture, + ExpectLeaderRoleTerminatedFixture, + ExpectLeaderSetFixture, + ExpectLeaderSlashedFixture, + ExpectLeaderStakeDecreasedFixture, + ExpectLeadOpeningAddedFixture, +} from '../fixtures/workingGroupModule' +import { Utils } from '../../utils/utils' + +tap.mocha.describe('Set lead proposal scenario', async () => { + initConfig() + registerJoystreamTypes() + + const nodeUrl: string = process.env.NODE_URL! + const sudoUri: string = process.env.SUDO_ACCOUNT_URI! + const keyring = new Keyring({ type: 'sr25519' }) + const provider = new WsProvider(nodeUrl) + const apiWrapper: ApiWrapper = await ApiWrapper.create(provider) + const sudo: KeyringPair = keyring.addFromUri(sudoUri) + + const N: number = +process.env.MEMBERSHIP_CREATION_N! + const m1KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N) + const m2KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N) + const leadKeyPair: KeyringPair[] = Utils.createKeyPairs(keyring, 1) + + const paidTerms: number = +process.env.MEMBERSHIP_PAID_TERMS! + const K: number = +process.env.COUNCIL_ELECTION_K! + const greaterStake: BN = new BN(+process.env.COUNCIL_STAKE_GREATER_AMOUNT!) + const lesserStake: BN = new BN(+process.env.COUNCIL_STAKE_LESSER_AMOUNT!) + const applicationStake: BN = new BN(process.env.WORKING_GROUP_APPLICATION_STAKE!) + const roleStake: BN = new BN(process.env.WORKING_GROUP_ROLE_STAKE!) + const firstRewardInterval: BN = new BN(process.env.LONG_REWARD_INTERVAL!) + const rewardInterval: BN = new BN(process.env.LONG_REWARD_INTERVAL!) + const payoutAmount: BN = new BN(process.env.PAYOUT_AMOUNT!) + const alteredPayoutAmount: BN = new BN(process.env.ALTERED_PAYOUT_AMOUNT!) + const stakeDecrement: BN = new BN(process.env.STAKE_DECREMENT!) + const slashAmount: BN = new BN(process.env.SLASH_AMOUNT!) + const durationInBlocks = 70 + + setTestTimeout(apiWrapper, durationInBlocks) + + const firstMemberSetFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + m1KeyPairs, + paidTerms + ) + tap.test('Creating first set of members', async () => firstMemberSetFixture.runner(false)) + + const secondMemberSetFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + m2KeyPairs, + paidTerms + ) + tap.test('Creating second set of members', async () => secondMemberSetFixture.runner(false)) + + const leaderMembershipFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + leadKeyPair, + paidTerms + ) + tap.test('Buy membership for lead', async () => leaderMembershipFixture.runner(false)) + + const electCouncilFixture: ElectCouncilFixture = new ElectCouncilFixture( + apiWrapper, + m1KeyPairs, + m2KeyPairs, + K, + sudo, + greaterStake, + lesserStake + ) + tap.test('Elect council', async () => electCouncilFixture.runner(false)) + + const createWorkingGroupLeaderOpeningFixture: CreateWorkingGroupLeaderOpeningFixture = new CreateWorkingGroupLeaderOpeningFixture( + apiWrapper, + m1KeyPairs, + sudo, + applicationStake, + roleStake, + 'Storage' + ) + tap.test('Propose create leader opening', async () => createWorkingGroupLeaderOpeningFixture.runner(false)) + + let voteForCreateOpeningProposalFixture: VoteForProposalFixture + const expectLeadOpeningAddedFixture: ExpectLeadOpeningAddedFixture = new ExpectLeadOpeningAddedFixture(apiWrapper) + tap.test('Approve add opening proposal', async () => { + voteForCreateOpeningProposalFixture = new VoteForProposalFixture( + apiWrapper, + m2KeyPairs, + sudo, + createWorkingGroupLeaderOpeningFixture.getResult()! + ) + voteForCreateOpeningProposalFixture.runner(false) + await expectLeadOpeningAddedFixture.runner(false) + }) + + let applyForLeaderOpeningFixture: ApplyForOpeningFixture + tap.test('Apply for lead opening', async () => { + applyForLeaderOpeningFixture = new ApplyForOpeningFixture( + apiWrapper, + leadKeyPair, + sudo, + applicationStake, + roleStake, + new BN(expectLeadOpeningAddedFixture.getResult()!), + WorkingGroups.StorageWorkingGroup + ) + await applyForLeaderOpeningFixture.runner(false) + }) + + let beginWorkingGroupLeaderApplicationReviewFixture: BeginWorkingGroupLeaderApplicationReviewFixture + tap.test('Propose begin leader application review', async () => { + beginWorkingGroupLeaderApplicationReviewFixture = new BeginWorkingGroupLeaderApplicationReviewFixture( + apiWrapper, + m1KeyPairs, + sudo, + new BN(expectLeadOpeningAddedFixture.getResult()!), + 'Storage' + ) + await beginWorkingGroupLeaderApplicationReviewFixture.runner(false) + }) + + let voteForBeginReviewProposal: VoteForProposalFixture + const expectBeganApplicationReviewFixture: ExpectBeganApplicationReviewFixture = new ExpectBeganApplicationReviewFixture( + apiWrapper + ) + tap.test('Approve begin application review', async () => { + voteForBeginReviewProposal = new VoteForProposalFixture( + apiWrapper, + m2KeyPairs, + sudo, + beginWorkingGroupLeaderApplicationReviewFixture.getResult()! + ) + voteForBeginReviewProposal.runner(false) + await expectBeganApplicationReviewFixture.runner(false) + }) + + let fillLeaderOpeningProposalFixture: FillLeaderOpeningProposalFixture + tap.test('Propose fill leader opening', async () => { + fillLeaderOpeningProposalFixture = new FillLeaderOpeningProposalFixture( + apiWrapper, + m1KeyPairs, + leadKeyPair[0].address, + sudo, + firstRewardInterval, + rewardInterval, + payoutAmount, + new BN(expectLeadOpeningAddedFixture.getResult()!), + WorkingGroups.StorageWorkingGroup + ) + await fillLeaderOpeningProposalFixture.runner(false) + }) + + let voteForFillLeaderProposalFixture: VoteForProposalFixture + const expectLeaderSetFixture: ExpectLeaderSetFixture = new ExpectLeaderSetFixture( + apiWrapper, + leadKeyPair[0].address, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Approve fill leader opening', async () => { + voteForFillLeaderProposalFixture = new VoteForProposalFixture( + apiWrapper, + m2KeyPairs, + sudo, + fillLeaderOpeningProposalFixture.getResult()! + ) + voteForFillLeaderProposalFixture.runner(false) + await expectLeaderSetFixture.runner(false) + }) + + const setLeaderRewardProposalFixture: SetLeaderRewardProposalFixture = new SetLeaderRewardProposalFixture( + apiWrapper, + m1KeyPairs, + sudo, + alteredPayoutAmount, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Propose leader reward', async () => setLeaderRewardProposalFixture.runner(false)) + + let voteForeLeaderRewardFixture: VoteForProposalFixture + const expectLeaderRewardAmountUpdatedFixture: ExpectLeaderRewardAmountUpdatedFixture = new ExpectLeaderRewardAmountUpdatedFixture( + apiWrapper, + alteredPayoutAmount, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Approve new leader reward', async () => { + voteForeLeaderRewardFixture = new VoteForProposalFixture( + apiWrapper, + m2KeyPairs, + sudo, + setLeaderRewardProposalFixture.getResult()! + ) + voteForeLeaderRewardFixture.runner(false) + await expectLeaderRewardAmountUpdatedFixture.runner(false) + }) + + const decreaseLeaderStakeProposalFixture: DecreaseLeaderStakeProposalFixture = new DecreaseLeaderStakeProposalFixture( + apiWrapper, + m1KeyPairs, + sudo, + stakeDecrement, + WorkingGroups.StorageWorkingGroup + ) + let newStake: BN + tap.test('Propose decrease stake', async () => decreaseLeaderStakeProposalFixture.runner(false)) + + let voteForDecreaseStakeProposal: VoteForProposalFixture + let expectLeaderStakeDecreasedFixture: ExpectLeaderStakeDecreasedFixture + tap.test('Approve decreased leader stake', async () => { + newStake = applicationStake.sub(stakeDecrement) + voteForFillLeaderProposalFixture = new VoteForProposalFixture( + apiWrapper, + m2KeyPairs, + sudo, + decreaseLeaderStakeProposalFixture.getResult()! + ) + voteForFillLeaderProposalFixture.runner(false) + expectLeaderStakeDecreasedFixture = new ExpectLeaderStakeDecreasedFixture( + apiWrapper, + newStake, + WorkingGroups.StorageWorkingGroup + ) + await expectLeaderStakeDecreasedFixture.runner(false) + }) + + const slashLeaderProposalFixture: SlashLeaderProposalFixture = new SlashLeaderProposalFixture( + apiWrapper, + m1KeyPairs, + sudo, + slashAmount, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Propose leader slash', async () => slashLeaderProposalFixture.runner(false)) + + let voteForSlashProposalFixture: VoteForProposalFixture + let expectLeaderSlashedFixture: ExpectLeaderSlashedFixture + tap.test('Approve leader slash', async () => { + newStake = newStake.sub(slashAmount) + voteForSlashProposalFixture = new VoteForProposalFixture( + apiWrapper, + m2KeyPairs, + sudo, + slashLeaderProposalFixture.getResult()! + ) + voteForSlashProposalFixture.runner(false) + expectLeaderSlashedFixture = new ExpectLeaderSlashedFixture(apiWrapper, newStake, WorkingGroups.StorageWorkingGroup) + await expectLeaderSlashedFixture.runner(false) + }) + + const terminateLeaderRoleProposalFixture: TerminateLeaderRoleProposalFixture = new TerminateLeaderRoleProposalFixture( + apiWrapper, + m1KeyPairs, + leadKeyPair[0].address, + sudo, + false, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Propose terminate leader role', async () => terminateLeaderRoleProposalFixture.runner(false)) + + let voteForLeaderRoleTerminationFixture: VoteForProposalFixture + const expectLeaderRoleTerminatedFixture: ExpectLeaderRoleTerminatedFixture = new ExpectLeaderRoleTerminatedFixture( + apiWrapper, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Approve leader role termination', async () => { + voteForLeaderRoleTerminationFixture = new VoteForProposalFixture( + apiWrapper, + m2KeyPairs, + sudo, + terminateLeaderRoleProposalFixture.getResult()! + ) + voteForLeaderRoleTerminationFixture.runner(false) + await expectLeaderRoleTerminatedFixture.runner(false) + }) + + closeApi(apiWrapper) +}) diff --git a/tests/network-tests/src/iznik/tests/proposals/setLeadProposalTest.ts b/tests/network-tests/src/iznik/tests/proposals/setLeadProposalTest.ts new file mode 100644 index 0000000000..815f9edbd5 --- /dev/null +++ b/tests/network-tests/src/iznik/tests/proposals/setLeadProposalTest.ts @@ -0,0 +1,75 @@ +import { KeyringPair } from '@polkadot/keyring/types' +import { initConfig } from '../../utils/config' +import { Keyring, WsProvider } from '@polkadot/api' +import BN from 'bn.js' +import { setTestTimeout } from '../../utils/setTestTimeout' +import tap from 'tap' +import { registerJoystreamTypes } from '@nicaea/types' +import { closeApi } from '../../utils/closeApi' +import { ApiWrapper } from '../../utils/apiWrapper' +import { Utils } from '../../utils/utils' +import { BuyMembershipHappyCaseFixture } from '../fixtures/membershipModule' +import { ElectCouncilFixture } from '../fixtures/councilElectionModule' +import { SetLeadProposalFixture } from '../fixtures/proposalsModule' + +tap.mocha.describe('Set lead proposal scenario', async () => { + initConfig() + registerJoystreamTypes() + + const nodeUrl: string = process.env.NODE_URL! + const sudoUri: string = process.env.SUDO_ACCOUNT_URI! + const keyring = new Keyring({ type: 'sr25519' }) + const provider = new WsProvider(nodeUrl) + const apiWrapper: ApiWrapper = await ApiWrapper.create(provider) + const sudo: KeyringPair = keyring.addFromUri(sudoUri) + + const N: number = +process.env.MEMBERSHIP_CREATION_N! + const m1KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N) + const m2KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N) + + const paidTerms: number = +process.env.MEMBERSHIP_PAID_TERMS! + const K: number = +process.env.COUNCIL_ELECTION_K! + const greaterStake: BN = new BN(+process.env.COUNCIL_STAKE_GREATER_AMOUNT!) + const lesserStake: BN = new BN(+process.env.COUNCIL_STAKE_LESSER_AMOUNT!) + + const durationInBlocks = 29 + + setTestTimeout(apiWrapper, durationInBlocks) + + const firstMemberSetFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + m1KeyPairs, + paidTerms + ) + tap.test('Creating first set of members', async () => firstMemberSetFixture.runner(false)) + + const secondMemberSetFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + m2KeyPairs, + paidTerms + ) + tap.test('Creating second set of members', async () => secondMemberSetFixture.runner(false)) + + const electCouncilFixture: ElectCouncilFixture = new ElectCouncilFixture( + apiWrapper, + m1KeyPairs, + m2KeyPairs, + K, + sudo, + greaterStake, + lesserStake + ) + tap.test('Elect council', async () => electCouncilFixture.runner(false)) + + const setLeadProposalFixture: SetLeadProposalFixture = new SetLeadProposalFixture( + apiWrapper, + m1KeyPairs, + m2KeyPairs, + sudo + ) + tap.test('Set lead proposal test', async () => setLeadProposalFixture.runner(false)) + + closeApi(apiWrapper) +}) diff --git a/tests/network-tests/src/iznik/tests/proposals/spendingProposalTest.ts b/tests/network-tests/src/iznik/tests/proposals/spendingProposalTest.ts new file mode 100644 index 0000000000..c821575c5c --- /dev/null +++ b/tests/network-tests/src/iznik/tests/proposals/spendingProposalTest.ts @@ -0,0 +1,78 @@ +import { KeyringPair } from '@polkadot/keyring/types' +import { initConfig } from '../../utils/config' +import { Keyring, WsProvider } from '@polkadot/api' +import BN from 'bn.js' +import { setTestTimeout } from '../../utils/setTestTimeout' +import tap from 'tap' +import { registerJoystreamTypes } from '@nicaea/types' +import { closeApi } from '../../utils/closeApi' +import { ApiWrapper } from '../../utils/apiWrapper' +import { Utils } from '../../utils/utils' +import { BuyMembershipHappyCaseFixture } from '../fixtures/membershipModule' +import { ElectCouncilFixture } from '../fixtures/councilElectionModule' +import { SpendingProposalFixture } from '../fixtures/proposalsModule' + +tap.mocha.describe('Spending proposal scenario', async () => { + initConfig() + registerJoystreamTypes() + + const nodeUrl: string = process.env.NODE_URL! + const sudoUri: string = process.env.SUDO_ACCOUNT_URI! + const keyring = new Keyring({ type: 'sr25519' }) + const provider = new WsProvider(nodeUrl) + const apiWrapper: ApiWrapper = await ApiWrapper.create(provider) + const sudo: KeyringPair = keyring.addFromUri(sudoUri) + + const N: number = +process.env.MEMBERSHIP_CREATION_N! + const m1KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N) + const m2KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N) + + const paidTerms: number = +process.env.MEMBERSHIP_PAID_TERMS! + const K: number = +process.env.COUNCIL_ELECTION_K! + const greaterStake: BN = new BN(+process.env.COUNCIL_STAKE_GREATER_AMOUNT!) + const lesserStake: BN = new BN(+process.env.COUNCIL_STAKE_LESSER_AMOUNT!) + const spendingBalance: BN = new BN(+process.env.SPENDING_BALANCE!) + const mintCapacity: BN = new BN(+process.env.COUNCIL_MINTING_CAPACITY!) + const durationInBlocks = 29 + + setTestTimeout(apiWrapper, durationInBlocks) + + const firstMemberSetFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + m1KeyPairs, + paidTerms + ) + tap.test('Creating first set of members', async () => firstMemberSetFixture.runner(false)) + + const secondMemberSetFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + m2KeyPairs, + paidTerms + ) + tap.test('Creating second set of members', async () => secondMemberSetFixture.runner(false)) + + const electCouncilFixture: ElectCouncilFixture = new ElectCouncilFixture( + apiWrapper, + m1KeyPairs, + m2KeyPairs, + K, + sudo, + greaterStake, + lesserStake + ) + tap.test('Elect council', async () => electCouncilFixture.runner(false)) + + const spendingProposalFixture: SpendingProposalFixture = new SpendingProposalFixture( + apiWrapper, + m1KeyPairs, + m2KeyPairs, + sudo, + spendingBalance, + mintCapacity + ) + tap.test('Spending proposal test', async () => spendingProposalFixture.runner(false)) + + closeApi(apiWrapper) +}) diff --git a/tests/network-tests/src/iznik/tests/proposals/textProposalTest.ts b/tests/network-tests/src/iznik/tests/proposals/textProposalTest.ts new file mode 100644 index 0000000000..f1a3d2b35b --- /dev/null +++ b/tests/network-tests/src/iznik/tests/proposals/textProposalTest.ts @@ -0,0 +1,69 @@ +import { KeyringPair } from '@polkadot/keyring/types' +import { initConfig } from '../../utils/config' +import { Keyring, WsProvider } from '@polkadot/api' +import BN from 'bn.js' +import { setTestTimeout } from '../../utils/setTestTimeout' +import tap from 'tap' +import { registerJoystreamTypes } from '@nicaea/types' +import { closeApi } from '../../utils/closeApi' +import { ApiWrapper } from '../../utils/apiWrapper' +import { Utils } from '../../utils/utils' +import { BuyMembershipHappyCaseFixture } from '../fixtures/membershipModule' +import { ElectCouncilFixture } from '../fixtures/councilElectionModule' +import { TextProposalFixture } from '../fixtures/proposalsModule' + +tap.mocha.describe('Text proposal scenario', async () => { + initConfig() + registerJoystreamTypes() + + const nodeUrl: string = process.env.NODE_URL! + const sudoUri: string = process.env.SUDO_ACCOUNT_URI! + const keyring = new Keyring({ type: 'sr25519' }) + const provider = new WsProvider(nodeUrl) + const apiWrapper: ApiWrapper = await ApiWrapper.create(provider) + const sudo: KeyringPair = keyring.addFromUri(sudoUri) + + const N: number = +process.env.MEMBERSHIP_CREATION_N! + const m1KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N) + const m2KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N) + + const paidTerms: number = +process.env.MEMBERSHIP_PAID_TERMS! + const K: number = +process.env.COUNCIL_ELECTION_K! + const greaterStake: BN = new BN(+process.env.COUNCIL_STAKE_GREATER_AMOUNT!) + const lesserStake: BN = new BN(+process.env.COUNCIL_STAKE_LESSER_AMOUNT!) + const durationInBlocks = 28 + + setTestTimeout(apiWrapper, durationInBlocks) + + const firstMemberSetFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + m1KeyPairs, + paidTerms + ) + tap.test('Creating first set of members', async () => firstMemberSetFixture.runner(false)) + + const secondMemberSetFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + m2KeyPairs, + paidTerms + ) + tap.test('Creating second set of members', async () => secondMemberSetFixture.runner(false)) + + const electCouncilFixture: ElectCouncilFixture = new ElectCouncilFixture( + apiWrapper, + m1KeyPairs, + m2KeyPairs, + K, + sudo, + greaterStake, + lesserStake + ) + tap.test('Elect council', async () => electCouncilFixture.runner(false)) + + const textProposalFixture: TextProposalFixture = new TextProposalFixture(apiWrapper, m1KeyPairs, m2KeyPairs, sudo) + tap.test('Text proposal test', async () => textProposalFixture.runner(false)) + + closeApi(apiWrapper) +}) diff --git a/tests/network-tests/src/iznik/tests/proposals/updateRuntimet.ts b/tests/network-tests/src/iznik/tests/proposals/updateRuntimet.ts new file mode 100644 index 0000000000..772718f6e8 --- /dev/null +++ b/tests/network-tests/src/iznik/tests/proposals/updateRuntimet.ts @@ -0,0 +1,77 @@ +import { KeyringPair } from '@polkadot/keyring/types' +import { initConfig } from '../../utils/config' +import { Keyring, WsProvider } from '@polkadot/api' +import BN from 'bn.js' +import { setTestTimeout } from '../../utils/setTestTimeout' +import tap from 'tap' +import { registerJoystreamTypes } from '@nicaea/types' +import { closeApi } from '../../utils/closeApi' +import { ApiWrapper } from '../../utils/apiWrapper' +import { Utils } from '../../utils/utils' +import { BuyMembershipHappyCaseFixture } from '../fixtures/membershipModule' +import { ElectCouncilFixture } from '../fixtures/councilElectionModule' +import { UpdateRuntimeFixture } from '../fixtures/proposalsModule' + +tap.mocha.describe('Update runtime scenario', async () => { + initConfig() + registerJoystreamTypes() + + const nodeUrl: string = process.env.NODE_URL! + const sudoUri: string = process.env.SUDO_ACCOUNT_URI! + const keyring = new Keyring({ type: 'sr25519' }) + const provider = new WsProvider(nodeUrl) + const apiWrapper: ApiWrapper = await ApiWrapper.create(provider) + const sudo: KeyringPair = keyring.addFromUri(sudoUri) + + const N: number = +process.env.MEMBERSHIP_CREATION_N! + const m1KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N) + const m2KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N) + + const paidTerms: number = +process.env.MEMBERSHIP_PAID_TERMS! + const K: number = +process.env.COUNCIL_ELECTION_K! + const greaterStake: BN = new BN(+process.env.COUNCIL_STAKE_GREATER_AMOUNT!) + const lesserStake: BN = new BN(+process.env.COUNCIL_STAKE_LESSER_AMOUNT!) + const durationInBlocks = 54 + + setTestTimeout(apiWrapper, durationInBlocks) + + const firstMemberSetFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + m1KeyPairs, + paidTerms + ) + tap.test('Creating first set of members', async () => firstMemberSetFixture.runner(false)) + + const secondMemberSetFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + m2KeyPairs, + paidTerms + ) + tap.test('Creating second set of members', async () => secondMemberSetFixture.runner(false)) + + const electCouncilFixture: ElectCouncilFixture = new ElectCouncilFixture( + apiWrapper, + m1KeyPairs, + m2KeyPairs, + K, + sudo, + greaterStake, + lesserStake + ) + tap.test('Elect council', async () => electCouncilFixture.runner(false)) + + const updateRuntimeFixture: UpdateRuntimeFixture = new UpdateRuntimeFixture(apiWrapper, m1KeyPairs, m2KeyPairs, sudo) + tap.test('Upgrade runtime', async () => updateRuntimeFixture.runner(false)) + + const thirdMemberSetFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + Utils.createKeyPairs(keyring, N), + paidTerms + ) + tap.test('Creating third set of members', async () => thirdMemberSetFixture.runner(false)) + + closeApi(apiWrapper) +}) diff --git a/tests/network-tests/src/iznik/tests/proposals/validatorCountProposalTest.ts b/tests/network-tests/src/iznik/tests/proposals/validatorCountProposalTest.ts new file mode 100644 index 0000000000..9564a009cd --- /dev/null +++ b/tests/network-tests/src/iznik/tests/proposals/validatorCountProposalTest.ts @@ -0,0 +1,76 @@ +import { KeyringPair } from '@polkadot/keyring/types' +import { initConfig } from '../../utils/config' +import { Keyring, WsProvider } from '@polkadot/api' +import BN from 'bn.js' +import { setTestTimeout } from '../../utils/setTestTimeout' +import tap from 'tap' +import { registerJoystreamTypes } from '@nicaea/types' +import { closeApi } from '../../utils/closeApi' +import { ApiWrapper } from '../../utils/apiWrapper' +import { Utils } from '../../utils/utils' +import { BuyMembershipHappyCaseFixture } from '../fixtures/membershipModule' +import { ElectCouncilFixture } from '../fixtures/councilElectionModule' +import { ValidatorCountProposalFixture } from '../fixtures/proposalsModule' + +tap.mocha.describe('Validator count proposal scenario', async () => { + initConfig() + registerJoystreamTypes() + + const nodeUrl: string = process.env.NODE_URL! + const sudoUri: string = process.env.SUDO_ACCOUNT_URI! + const keyring = new Keyring({ type: 'sr25519' }) + const provider = new WsProvider(nodeUrl) + const apiWrapper: ApiWrapper = await ApiWrapper.create(provider) + const sudo: KeyringPair = keyring.addFromUri(sudoUri) + + const N: number = +process.env.MEMBERSHIP_CREATION_N! + const m1KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N) + const m2KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N) + + const paidTerms: number = +process.env.MEMBERSHIP_PAID_TERMS! + const K: number = +process.env.COUNCIL_ELECTION_K! + const greaterStake: BN = new BN(+process.env.COUNCIL_STAKE_GREATER_AMOUNT!) + const lesserStake: BN = new BN(+process.env.COUNCIL_STAKE_LESSER_AMOUNT!) + const validatorCountIncrement: BN = new BN(+process.env.VALIDATOR_COUNT_INCREMENT!) + const durationInBlocks = 29 + + setTestTimeout(apiWrapper, durationInBlocks) + + const firstMemberSetFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + m1KeyPairs, + paidTerms + ) + tap.test('Creating first set of members', async () => firstMemberSetFixture.runner(false)) + + const secondMemberSetFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + m2KeyPairs, + paidTerms + ) + tap.test('Creating second set of members', async () => secondMemberSetFixture.runner(false)) + + const electCouncilFixture: ElectCouncilFixture = new ElectCouncilFixture( + apiWrapper, + m1KeyPairs, + m2KeyPairs, + K, + sudo, + greaterStake, + lesserStake + ) + tap.test('Elect council', async () => electCouncilFixture.runner(false)) + + const validatorCountProposalFixture: ValidatorCountProposalFixture = new ValidatorCountProposalFixture( + apiWrapper, + m1KeyPairs, + m2KeyPairs, + sudo, + validatorCountIncrement + ) + tap.test('Validator count proposal', async () => validatorCountProposalFixture.runner(false)) + + closeApi(apiWrapper) +}) diff --git a/tests/network-tests/src/iznik/tests/proposals/workingGroupMintCapacityProposalTest.ts b/tests/network-tests/src/iznik/tests/proposals/workingGroupMintCapacityProposalTest.ts new file mode 100644 index 0000000000..9ec7bba452 --- /dev/null +++ b/tests/network-tests/src/iznik/tests/proposals/workingGroupMintCapacityProposalTest.ts @@ -0,0 +1,96 @@ +import { KeyringPair } from '@polkadot/keyring/types' +import { initConfig } from '../../utils/config' +import { Keyring, WsProvider } from '@polkadot/api' +import BN from 'bn.js' +import { setTestTimeout } from '../../utils/setTestTimeout' +import tap from 'tap' +import { registerJoystreamTypes } from '@nicaea/types' +import { closeApi } from '../../utils/closeApi' +import { ApiWrapper, WorkingGroups } from '../../utils/apiWrapper' +import { Utils } from '../../utils/utils' +import { BuyMembershipHappyCaseFixture } from '../fixtures/membershipModule' +import { ElectCouncilFixture } from '../fixtures/councilElectionModule' +import { VoteForProposalFixture, WorkingGroupMintCapacityProposalFixture } from '../fixtures/proposalsModule' +import { ExpectMintCapacityChangedFixture } from '../fixtures/workingGroupModule' + +tap.mocha.describe('Set storage working group mint capacity scenario', async () => { + initConfig() + registerJoystreamTypes() + + const nodeUrl: string = process.env.NODE_URL! + const sudoUri: string = process.env.SUDO_ACCOUNT_URI! + const keyring = new Keyring({ type: 'sr25519' }) + const provider = new WsProvider(nodeUrl) + const apiWrapper: ApiWrapper = await ApiWrapper.create(provider) + const sudo: KeyringPair = keyring.addFromUri(sudoUri) + + const N: number = +process.env.MEMBERSHIP_CREATION_N! + const m1KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N) + const m2KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N) + + const paidTerms: number = +process.env.MEMBERSHIP_PAID_TERMS! + const K: number = +process.env.COUNCIL_ELECTION_K! + const greaterStake: BN = new BN(+process.env.COUNCIL_STAKE_GREATER_AMOUNT!) + const lesserStake: BN = new BN(+process.env.COUNCIL_STAKE_LESSER_AMOUNT!) + const mintCapacityIncrement: BN = new BN(process.env.MINT_CAPACITY_INCREMENT!) + const durationInBlocks = 30 + + setTestTimeout(apiWrapper, durationInBlocks) + + const firstMemberSetFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + m1KeyPairs, + paidTerms + ) + tap.test('Creating first set of members', async () => firstMemberSetFixture.runner(false)) + + const secondMemberSetFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + m2KeyPairs, + paidTerms + ) + tap.test('Creating second set of members', async () => secondMemberSetFixture.runner(false)) + + const electCouncilFixture: ElectCouncilFixture = new ElectCouncilFixture( + apiWrapper, + m1KeyPairs, + m2KeyPairs, + K, + sudo, + greaterStake, + lesserStake + ) + tap.test('Elect council', async () => electCouncilFixture.runner(false)) + + const newMintCapacity: BN = (await apiWrapper.getWorkingGroupMintCapacity(WorkingGroups.StorageWorkingGroup)).add( + mintCapacityIncrement + ) + const workingGroupMintCapacityProposalFixture: WorkingGroupMintCapacityProposalFixture = new WorkingGroupMintCapacityProposalFixture( + apiWrapper, + m1KeyPairs, + sudo, + newMintCapacity, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Propose mint capacity', async () => workingGroupMintCapacityProposalFixture.runner(false)) + + let voteForProposalFixture: VoteForProposalFixture + const expectMintCapacityChanged: ExpectMintCapacityChangedFixture = new ExpectMintCapacityChangedFixture( + apiWrapper, + newMintCapacity + ) + tap.test('Approve mint capacity', async () => { + voteForProposalFixture = new VoteForProposalFixture( + apiWrapper, + m2KeyPairs, + sudo, + workingGroupMintCapacityProposalFixture.getResult()! + ) + voteForProposalFixture.runner(false) + await expectMintCapacityChanged.runner(false) + }) + + closeApi(apiWrapper) +}) diff --git a/tests/network-tests/src/iznik/tests/workingGroup/atLeastValueBugTest.ts b/tests/network-tests/src/iznik/tests/workingGroup/atLeastValueBugTest.ts new file mode 100644 index 0000000000..e26f4bf9f4 --- /dev/null +++ b/tests/network-tests/src/iznik/tests/workingGroup/atLeastValueBugTest.ts @@ -0,0 +1,155 @@ +import { initConfig } from '../../utils/config' +import { registerJoystreamTypes } from '@nicaea/types' +import { closeApi } from '../../utils/closeApi' +import { ApiWrapper, WorkingGroups } from '../../utils/apiWrapper' +import { WsProvider, Keyring } from '@polkadot/api' +import { KeyringPair } from '@polkadot/keyring/types' +import { setTestTimeout } from '../../utils/setTestTimeout' +import { + AddLeaderOpeningFixture, + AddWorkerOpeningFixture, + ApplyForOpeningFixture, + BeginLeaderApplicationReviewFixture, + FillLeaderOpeningFixture, + LeaveRoleFixture, +} from '../fixtures/workingGroupModule' +import BN from 'bn.js' +import tap from 'tap' +import { BuyMembershipHappyCaseFixture } from '../fixtures/membershipModule' +import { Utils } from '../../utils/utils' + +tap.mocha.describe('Worker application happy case scenario', async () => { + initConfig() + registerJoystreamTypes() + + const nodeUrl: string = process.env.NODE_URL! + const sudoUri: string = process.env.SUDO_ACCOUNT_URI! + const keyring = new Keyring({ type: 'sr25519' }) + const provider = new WsProvider(nodeUrl) + const apiWrapper: ApiWrapper = await ApiWrapper.create(provider) + const sudo: KeyringPair = keyring.addFromUri(sudoUri) + + const N: number = +process.env.WORKING_GROUP_N! + const nKeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N) + const leadKeyPair: KeyringPair[] = Utils.createKeyPairs(keyring, 1) + + const paidTerms: number = +process.env.MEMBERSHIP_PAID_TERMS! + const applicationStake: BN = new BN(process.env.WORKING_GROUP_APPLICATION_STAKE!) + const roleStake: BN = new BN(process.env.WORKING_GROUP_ROLE_STAKE!) + const firstRewardInterval: BN = new BN(process.env.LONG_REWARD_INTERVAL!) + const rewardInterval: BN = new BN(process.env.LONG_REWARD_INTERVAL!) + const payoutAmount: BN = new BN(process.env.PAYOUT_AMOUNT!) + const unstakingPeriod: BN = new BN(process.env.STORAGE_WORKING_GROUP_UNSTAKING_PERIOD!) + const durationInBlocks = 48 + const openingActivationDelay: BN = new BN(0) + + setTestTimeout(apiWrapper, durationInBlocks) + + const happyCaseFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + nKeyPairs, + paidTerms + ) + tap.test('Creating a set of members', async () => happyCaseFixture.runner(false)) + + const leaderHappyCaseFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + leadKeyPair, + paidTerms + ) + tap.test('Buying membership for leader account', async () => leaderHappyCaseFixture.runner(false)) + + const addLeaderOpeningFixture: AddLeaderOpeningFixture = new AddLeaderOpeningFixture( + apiWrapper, + nKeyPairs, + sudo, + applicationStake, + roleStake, + openingActivationDelay, + WorkingGroups.StorageWorkingGroup + ) + // let leadOpenignId: BN + tap.test('Add lead opening', async () => await addLeaderOpeningFixture.runner(false)) + + let applyForLeaderOpeningFixture: ApplyForOpeningFixture + tap.test('Apply for lead opening', async () => { + applyForLeaderOpeningFixture = new ApplyForOpeningFixture( + apiWrapper, + leadKeyPair, + sudo, + applicationStake, + roleStake, + addLeaderOpeningFixture.getResult()!, + WorkingGroups.StorageWorkingGroup + ) + await applyForLeaderOpeningFixture.runner(false) + }) + + let beginLeaderApplicationReviewFixture: BeginLeaderApplicationReviewFixture + tap.test('Begin lead application review', async () => { + beginLeaderApplicationReviewFixture = new BeginLeaderApplicationReviewFixture( + apiWrapper, + sudo, + addLeaderOpeningFixture.getResult()!, + WorkingGroups.StorageWorkingGroup + ) + await beginLeaderApplicationReviewFixture.runner(false) + }) + + let fillLeaderOpeningFixture: FillLeaderOpeningFixture + tap.test('Fill lead opening', async () => { + fillLeaderOpeningFixture = new FillLeaderOpeningFixture( + apiWrapper, + leadKeyPair, + sudo, + addLeaderOpeningFixture.getResult()!, + firstRewardInterval, + rewardInterval, + payoutAmount, + WorkingGroups.StorageWorkingGroup + ) + await fillLeaderOpeningFixture.runner(false) + }) + + const addWorkerOpeningWithoutStakeFixture: AddWorkerOpeningFixture = new AddWorkerOpeningFixture( + apiWrapper, + nKeyPairs, + leadKeyPair[0], + sudo, + new BN(0), + new BN(0), + openingActivationDelay, + unstakingPeriod, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Add worker opening with 0 stake, expect failure', async () => + addWorkerOpeningWithoutStakeFixture.runner(true) + ) + + const addWorkerOpeningWithoutUnstakingPeriodFixture: AddWorkerOpeningFixture = new AddWorkerOpeningFixture( + apiWrapper, + nKeyPairs, + leadKeyPair[0], + sudo, + applicationStake, + roleStake, + openingActivationDelay, + new BN(0), + WorkingGroups.StorageWorkingGroup + ) + tap.test('Add worker opening with 0 unstaking period, expect failure', async () => + addWorkerOpeningWithoutUnstakingPeriodFixture.runner(true) + ) + + const leaveRoleFixture: LeaveRoleFixture = new LeaveRoleFixture( + apiWrapper, + leadKeyPair, + sudo, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Leaving lead role', async () => leaveRoleFixture.runner(false)) + + closeApi(apiWrapper) +}) diff --git a/tests/network-tests/src/iznik/tests/workingGroup/manageWorkerAsLeadTest.ts b/tests/network-tests/src/iznik/tests/workingGroup/manageWorkerAsLeadTest.ts new file mode 100644 index 0000000000..3df2b5d494 --- /dev/null +++ b/tests/network-tests/src/iznik/tests/workingGroup/manageWorkerAsLeadTest.ts @@ -0,0 +1,280 @@ +import { initConfig } from '../../utils/config' +import { registerJoystreamTypes } from '@nicaea/types' +import { closeApi } from '../../utils/closeApi' +import { ApiWrapper, WorkingGroups } from '../../utils/apiWrapper' +import { WsProvider, Keyring } from '@polkadot/api' +import { KeyringPair } from '@polkadot/keyring/types' +import { setTestTimeout } from '../../utils/setTestTimeout' +import { + AddLeaderOpeningFixture, + ApplyForOpeningFixture, + BeginLeaderApplicationReviewFixture, + FillLeaderOpeningFixture, + AddWorkerOpeningFixture, + WithdrawApplicationFixture, + BeginApplicationReviewFixture, + FillOpeningFixture, + LeaveRoleFixture, + DecreaseStakeFixture, + SlashFixture, + TerminateRoleFixture, +} from '../fixtures/workingGroupModule' +import { BuyMembershipHappyCaseFixture } from '../fixtures/membershipModule' +import { Utils } from '../../utils/utils' +import BN from 'bn.js' +import tap from 'tap' + +tap.mocha.describe('Manage worker as worker scenario', async () => { + initConfig() + registerJoystreamTypes() + + const nodeUrl: string = process.env.NODE_URL! + const sudoUri: string = process.env.SUDO_ACCOUNT_URI! + const keyring = new Keyring({ type: 'sr25519' }) + const provider = new WsProvider(nodeUrl) + const apiWrapper: ApiWrapper = await ApiWrapper.create(provider) + const sudo: KeyringPair = keyring.addFromUri(sudoUri) + + const N: number = +process.env.WORKING_GROUP_N! + const nKeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N) + const leadKeyPair: KeyringPair[] = Utils.createKeyPairs(keyring, 1) + + const paidTerms: number = +process.env.MEMBERSHIP_PAID_TERMS! + const applicationStake: BN = new BN(process.env.WORKING_GROUP_APPLICATION_STAKE!) + const roleStake: BN = new BN(process.env.WORKING_GROUP_ROLE_STAKE!) + const firstRewardInterval: BN = new BN(process.env.LONG_REWARD_INTERVAL!) + const rewardInterval: BN = new BN(process.env.LONG_REWARD_INTERVAL!) + const payoutAmount: BN = new BN(process.env.PAYOUT_AMOUNT!) + const unstakingPeriod: BN = new BN(process.env.STORAGE_WORKING_GROUP_UNSTAKING_PERIOD!) + const durationInBlocks = 60 + const openingActivationDelay: BN = new BN(0) + + setTestTimeout(apiWrapper, durationInBlocks) + + const happyCaseFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + nKeyPairs, + paidTerms + ) + tap.test('Creating a set of members', async () => happyCaseFixture.runner(false)) + + const leaderHappyCaseFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + leadKeyPair, + paidTerms + ) + tap.test('Buying membership for leader account', async () => leaderHappyCaseFixture.runner(false)) + + const addLeaderOpeningFixture: AddLeaderOpeningFixture = new AddLeaderOpeningFixture( + apiWrapper, + nKeyPairs, + sudo, + applicationStake, + roleStake, + openingActivationDelay, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Add lead opening', async () => await addLeaderOpeningFixture.runner(false)) + + let applyForLeaderOpeningFixture: ApplyForOpeningFixture + tap.test('Apply for lead opening', async () => { + applyForLeaderOpeningFixture = new ApplyForOpeningFixture( + apiWrapper, + leadKeyPair, + sudo, + applicationStake, + roleStake, + addLeaderOpeningFixture.getResult()!, + WorkingGroups.StorageWorkingGroup + ) + await applyForLeaderOpeningFixture.runner(false) + }) + + let beginLeaderApplicationReviewFixture: BeginLeaderApplicationReviewFixture + tap.test('Begin lead application review', async () => { + beginLeaderApplicationReviewFixture = new BeginLeaderApplicationReviewFixture( + apiWrapper, + sudo, + addLeaderOpeningFixture.getResult()!, + WorkingGroups.StorageWorkingGroup + ) + await beginLeaderApplicationReviewFixture.runner(false) + }) + + let fillLeaderOpeningFixture: FillLeaderOpeningFixture + tap.test('Fill lead opening', async () => { + fillLeaderOpeningFixture = new FillLeaderOpeningFixture( + apiWrapper, + leadKeyPair, + sudo, + addLeaderOpeningFixture.getResult()!, + firstRewardInterval, + rewardInterval, + payoutAmount, + WorkingGroups.StorageWorkingGroup + ) + await fillLeaderOpeningFixture.runner(false) + }) + + const addWorkerOpeningFixture: AddWorkerOpeningFixture = new AddWorkerOpeningFixture( + apiWrapper, + nKeyPairs, + leadKeyPair[0], + sudo, + applicationStake, + roleStake, + openingActivationDelay, + unstakingPeriod, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Add worker opening', async () => addWorkerOpeningFixture.runner(false)) + + let applyForWorkerOpeningFixture: ApplyForOpeningFixture + tap.test('First apply for worker opening', async () => { + applyForWorkerOpeningFixture = new ApplyForOpeningFixture( + apiWrapper, + nKeyPairs, + sudo, + applicationStake, + roleStake, + addWorkerOpeningFixture.getResult()!, + WorkingGroups.StorageWorkingGroup + ) + await applyForWorkerOpeningFixture.runner(false) + }) + + let beginApplicationReviewFixture: BeginApplicationReviewFixture + tap.test('Begin application review', async () => { + beginApplicationReviewFixture = new BeginApplicationReviewFixture( + apiWrapper, + leadKeyPair[0], + sudo, + addWorkerOpeningFixture.getResult()!, + WorkingGroups.StorageWorkingGroup + ) + await beginApplicationReviewFixture.runner(false) + }) + + let fillOpeningFixture: FillOpeningFixture + tap.test('Fill worker opening', async () => { + fillOpeningFixture = new FillOpeningFixture( + apiWrapper, + nKeyPairs, + leadKeyPair[0], + sudo, + addWorkerOpeningFixture.getResult()!, + firstRewardInterval, + rewardInterval, + payoutAmount, + WorkingGroups.StorageWorkingGroup + ) + await fillOpeningFixture.runner(false) + }) + + const leaveRoleFixture: LeaveRoleFixture = new LeaveRoleFixture( + apiWrapper, + leadKeyPair, + sudo, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Leaving lead role', async () => leaveRoleFixture.runner(false)) + + const decreaseStakeFailureFixture: DecreaseStakeFixture = new DecreaseStakeFixture( + apiWrapper, + nKeyPairs, + leadKeyPair[0], + sudo, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Decrease worker stake, expect failure', async () => decreaseStakeFailureFixture.runner(true)) + + const addNewLeaderOpeningFixture: AddLeaderOpeningFixture = new AddLeaderOpeningFixture( + apiWrapper, + nKeyPairs, + sudo, + applicationStake, + roleStake, + openingActivationDelay, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Add lead opening', async () => await addNewLeaderOpeningFixture.runner(false)) + + let applyForNewLeaderOpeningFixture: ApplyForOpeningFixture + tap.test('Apply for lead opening', async () => { + applyForNewLeaderOpeningFixture = new ApplyForOpeningFixture( + apiWrapper, + leadKeyPair, + sudo, + applicationStake, + roleStake, + addNewLeaderOpeningFixture.getResult()!, + WorkingGroups.StorageWorkingGroup + ) + await applyForNewLeaderOpeningFixture.runner(false) + }) + + let beginNewLeaderApplicationReviewFixture: BeginLeaderApplicationReviewFixture + tap.test('Begin lead application review', async () => { + beginNewLeaderApplicationReviewFixture = new BeginLeaderApplicationReviewFixture( + apiWrapper, + sudo, + addNewLeaderOpeningFixture.getResult()!, + WorkingGroups.StorageWorkingGroup + ) + await beginNewLeaderApplicationReviewFixture.runner(false) + }) + + let fillNewLeaderOpeningFixture: FillLeaderOpeningFixture + tap.test('Fill lead opening', async () => { + fillNewLeaderOpeningFixture = new FillLeaderOpeningFixture( + apiWrapper, + leadKeyPair, + sudo, + addNewLeaderOpeningFixture.getResult()!, + firstRewardInterval, + rewardInterval, + payoutAmount, + WorkingGroups.StorageWorkingGroup + ) + await fillNewLeaderOpeningFixture.runner(false) + }) + + const decreaseStakeFixture: DecreaseStakeFixture = new DecreaseStakeFixture( + apiWrapper, + nKeyPairs, + leadKeyPair[0], + sudo, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Decrease worker stake', async () => decreaseStakeFixture.runner(false)) + + const slashFixture: SlashFixture = new SlashFixture( + apiWrapper, + nKeyPairs, + leadKeyPair[0], + sudo, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Slash worker', async () => slashFixture.runner(false)) + + const terminateRoleFixture: TerminateRoleFixture = new TerminateRoleFixture( + apiWrapper, + nKeyPairs, + leadKeyPair[0], + sudo, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Terminate worker role', async () => terminateRoleFixture.runner(false)) + + const newLeaveRoleFixture: LeaveRoleFixture = new LeaveRoleFixture( + apiWrapper, + leadKeyPair, + sudo, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Leaving lead role', async () => newLeaveRoleFixture.runner(false)) + + closeApi(apiWrapper) +}) diff --git a/tests/network-tests/src/iznik/tests/workingGroup/manageWorkerAsWorkerTest.ts b/tests/network-tests/src/iznik/tests/workingGroup/manageWorkerAsWorkerTest.ts new file mode 100644 index 0000000000..ebd0c800b4 --- /dev/null +++ b/tests/network-tests/src/iznik/tests/workingGroup/manageWorkerAsWorkerTest.ts @@ -0,0 +1,210 @@ +import { initConfig } from '../../utils/config' +import { registerJoystreamTypes } from '@nicaea/types' +import { closeApi } from '../../utils/closeApi' +import { ApiWrapper, WorkingGroups } from '../../utils/apiWrapper' +import { WsProvider, Keyring } from '@polkadot/api' +import { KeyringPair } from '@polkadot/keyring/types' +import { setTestTimeout } from '../../utils/setTestTimeout' +import { + AddLeaderOpeningFixture, + AddWorkerOpeningFixture, + ApplyForOpeningFixture, + BeginApplicationReviewFixture, + BeginLeaderApplicationReviewFixture, + FillLeaderOpeningFixture, + FillOpeningFixture, + IncreaseStakeFixture, + LeaveRoleFixture, + UpdateRewardAccountFixture, +} from '../fixtures/workingGroupModule' +import { BuyMembershipHappyCaseFixture } from '../fixtures/membershipModule' +import { Utils } from '../../utils/utils' +import BN from 'bn.js' +import tap from 'tap' + +tap.mocha.describe('Manage worker as worker scenario', async () => { + initConfig() + registerJoystreamTypes() + + const nodeUrl: string = process.env.NODE_URL! + const sudoUri: string = process.env.SUDO_ACCOUNT_URI! + const keyring = new Keyring({ type: 'sr25519' }) + const provider = new WsProvider(nodeUrl) + const apiWrapper: ApiWrapper = await ApiWrapper.create(provider) + const sudo: KeyringPair = keyring.addFromUri(sudoUri) + + const N: number = +process.env.WORKING_GROUP_N! + const nKeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N) + const leadKeyPair: KeyringPair[] = Utils.createKeyPairs(keyring, 1) + + const paidTerms: number = +process.env.MEMBERSHIP_PAID_TERMS! + const applicationStake: BN = new BN(process.env.WORKING_GROUP_APPLICATION_STAKE!) + const roleStake: BN = new BN(process.env.WORKING_GROUP_ROLE_STAKE!) + const firstRewardInterval: BN = new BN(process.env.LONG_REWARD_INTERVAL!) + const rewardInterval: BN = new BN(process.env.LONG_REWARD_INTERVAL!) + const payoutAmount: BN = new BN(process.env.PAYOUT_AMOUNT!) + const unstakingPeriod: BN = new BN(process.env.STORAGE_WORKING_GROUP_UNSTAKING_PERIOD!) + const durationInBlocks = 38 + const openingActivationDelay: BN = new BN(0) + + setTestTimeout(apiWrapper, durationInBlocks) + + const happyCaseFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + nKeyPairs, + paidTerms + ) + tap.test('Creating a set of members', async () => happyCaseFixture.runner(false)) + + const leaderHappyCaseFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + leadKeyPair, + paidTerms + ) + tap.test('Buying membership for leader account', async () => leaderHappyCaseFixture.runner(false)) + + const addLeaderOpeningFixture: AddLeaderOpeningFixture = new AddLeaderOpeningFixture( + apiWrapper, + nKeyPairs, + sudo, + applicationStake, + roleStake, + openingActivationDelay, + WorkingGroups.StorageWorkingGroup + ) + // let leadOpenignId: BN + tap.test('Add lead opening', async () => await addLeaderOpeningFixture.runner(false)) + + let applyForLeaderOpeningFixture: ApplyForOpeningFixture + tap.test('Apply for lead opening', async () => { + applyForLeaderOpeningFixture = new ApplyForOpeningFixture( + apiWrapper, + leadKeyPair, + sudo, + applicationStake, + roleStake, + addLeaderOpeningFixture.getResult()!, + WorkingGroups.StorageWorkingGroup + ) + await applyForLeaderOpeningFixture.runner(false) + }) + + let beginLeaderApplicationReviewFixture: BeginLeaderApplicationReviewFixture + tap.test('Begin lead application review', async () => { + beginLeaderApplicationReviewFixture = new BeginLeaderApplicationReviewFixture( + apiWrapper, + sudo, + addLeaderOpeningFixture.getResult()!, + WorkingGroups.StorageWorkingGroup + ) + await beginLeaderApplicationReviewFixture.runner(false) + }) + + let fillLeaderOpeningFixture: FillLeaderOpeningFixture + tap.test('Fill lead opening', async () => { + fillLeaderOpeningFixture = new FillLeaderOpeningFixture( + apiWrapper, + leadKeyPair, + sudo, + addLeaderOpeningFixture.getResult()!, + firstRewardInterval, + rewardInterval, + payoutAmount, + WorkingGroups.StorageWorkingGroup + ) + await fillLeaderOpeningFixture.runner(false) + }) + + const addWorkerOpeningFixture: AddWorkerOpeningFixture = new AddWorkerOpeningFixture( + apiWrapper, + nKeyPairs, + leadKeyPair[0], + sudo, + applicationStake, + roleStake, + openingActivationDelay, + unstakingPeriod, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Add worker opening', async () => addWorkerOpeningFixture.runner(false)) + + let applyForWorkerOpeningFixture: ApplyForOpeningFixture + tap.test('First apply for worker opening', async () => { + applyForWorkerOpeningFixture = new ApplyForOpeningFixture( + apiWrapper, + nKeyPairs, + sudo, + applicationStake, + roleStake, + addWorkerOpeningFixture.getResult()!, + WorkingGroups.StorageWorkingGroup + ) + await applyForWorkerOpeningFixture.runner(false) + }) + + let beginApplicationReviewFixture: BeginApplicationReviewFixture + tap.test('Begin application review', async () => { + beginApplicationReviewFixture = new BeginApplicationReviewFixture( + apiWrapper, + leadKeyPair[0], + sudo, + addWorkerOpeningFixture.getResult()!, + WorkingGroups.StorageWorkingGroup + ) + await beginApplicationReviewFixture.runner(false) + }) + + let fillOpeningFixture: FillOpeningFixture + tap.test('Fill worker opening', async () => { + fillOpeningFixture = new FillOpeningFixture( + apiWrapper, + nKeyPairs, + leadKeyPair[0], + sudo, + addWorkerOpeningFixture.getResult()!, + firstRewardInterval, + rewardInterval, + payoutAmount, + WorkingGroups.StorageWorkingGroup + ) + await fillOpeningFixture.runner(false) + }) + + const increaseStakeFixture: IncreaseStakeFixture = new IncreaseStakeFixture( + apiWrapper, + nKeyPairs, + sudo, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Increase worker stake', async () => increaseStakeFixture.runner(false)) + + const updateRewardAccountFixture: UpdateRewardAccountFixture = new UpdateRewardAccountFixture( + apiWrapper, + nKeyPairs, + keyring, + sudo, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Update reward account', async () => updateRewardAccountFixture.runner(false)) + + const updateRoleAccountFixture: UpdateRewardAccountFixture = new UpdateRewardAccountFixture( + apiWrapper, + nKeyPairs, + keyring, + sudo, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Update role account', async () => updateRoleAccountFixture.runner(false)) + + const newLeaveRoleFixture: LeaveRoleFixture = new LeaveRoleFixture( + apiWrapper, + leadKeyPair, + sudo, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Leaving lead role', async () => newLeaveRoleFixture.runner(false)) + + closeApi(apiWrapper) +}) diff --git a/tests/network-tests/src/iznik/tests/workingGroup/workerApplicationHappyCaseTest.ts b/tests/network-tests/src/iznik/tests/workingGroup/workerApplicationHappyCaseTest.ts new file mode 100644 index 0000000000..e881af38dd --- /dev/null +++ b/tests/network-tests/src/iznik/tests/workingGroup/workerApplicationHappyCaseTest.ts @@ -0,0 +1,205 @@ +import { initConfig } from '../../utils/config' +import { registerJoystreamTypes } from '@nicaea/types' +import { closeApi } from '../../utils/closeApi' +import { ApiWrapper, WorkingGroups } from '../../utils/apiWrapper' +import { WsProvider, Keyring } from '@polkadot/api' +import { KeyringPair } from '@polkadot/keyring/types' +import { setTestTimeout } from '../../utils/setTestTimeout' +import BN from 'bn.js' +import tap from 'tap' +import { BuyMembershipHappyCaseFixture } from '../fixtures/membershipModule' +import { Utils } from '../../utils/utils' +import { + AddLeaderOpeningFixture, + AddWorkerOpeningFixture, + ApplyForOpeningFixture, + BeginApplicationReviewFixture, + BeginLeaderApplicationReviewFixture, + FillLeaderOpeningFixture, + FillOpeningFixture, + LeaveRoleFixture, + WithdrawApplicationFixture, +} from '../fixtures/workingGroupModule' + +tap.mocha.describe('Worker application happy case scenario', async () => { + initConfig() + registerJoystreamTypes() + + const nodeUrl: string = process.env.NODE_URL! + const sudoUri: string = process.env.SUDO_ACCOUNT_URI! + const keyring = new Keyring({ type: 'sr25519' }) + const provider = new WsProvider(nodeUrl) + const apiWrapper: ApiWrapper = await ApiWrapper.create(provider) + const sudo: KeyringPair = keyring.addFromUri(sudoUri) + + const N: number = +process.env.WORKING_GROUP_N! + const nKeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N) + const leadKeyPair: KeyringPair[] = Utils.createKeyPairs(keyring, 1) + + const paidTerms: number = +process.env.MEMBERSHIP_PAID_TERMS! + const applicationStake: BN = new BN(process.env.WORKING_GROUP_APPLICATION_STAKE!) + const roleStake: BN = new BN(process.env.WORKING_GROUP_ROLE_STAKE!) + const firstRewardInterval: BN = new BN(process.env.LONG_REWARD_INTERVAL!) + const rewardInterval: BN = new BN(process.env.LONG_REWARD_INTERVAL!) + const payoutAmount: BN = new BN(process.env.PAYOUT_AMOUNT!) + const unstakingPeriod: BN = new BN(process.env.STORAGE_WORKING_GROUP_UNSTAKING_PERIOD!) + const durationInBlocks = 48 + const openingActivationDelay: BN = new BN(0) + + setTestTimeout(apiWrapper, durationInBlocks) + + const happyCaseFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + nKeyPairs, + paidTerms + ) + tap.test('Creating a set of members', async () => happyCaseFixture.runner(false)) + + const leaderHappyCaseFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + leadKeyPair, + paidTerms + ) + tap.test('Buying membership for leader account', async () => leaderHappyCaseFixture.runner(false)) + + const addLeaderOpeningFixture: AddLeaderOpeningFixture = new AddLeaderOpeningFixture( + apiWrapper, + nKeyPairs, + sudo, + applicationStake, + roleStake, + openingActivationDelay, + WorkingGroups.StorageWorkingGroup + ) + // let leadOpenignId: BN + tap.test('Add lead opening', async () => await addLeaderOpeningFixture.runner(false)) + + let applyForLeaderOpeningFixture: ApplyForOpeningFixture + tap.test('Apply for lead opening', async () => { + applyForLeaderOpeningFixture = new ApplyForOpeningFixture( + apiWrapper, + leadKeyPair, + sudo, + applicationStake, + roleStake, + addLeaderOpeningFixture.getResult()!, + WorkingGroups.StorageWorkingGroup + ) + await applyForLeaderOpeningFixture.runner(false) + }) + + let beginLeaderApplicationReviewFixture: BeginLeaderApplicationReviewFixture + tap.test('Begin lead application review', async () => { + beginLeaderApplicationReviewFixture = new BeginLeaderApplicationReviewFixture( + apiWrapper, + sudo, + addLeaderOpeningFixture.getResult()!, + WorkingGroups.StorageWorkingGroup + ) + await beginLeaderApplicationReviewFixture.runner(false) + }) + + let fillLeaderOpeningFixture: FillLeaderOpeningFixture + tap.test('Fill lead opening', async () => { + fillLeaderOpeningFixture = new FillLeaderOpeningFixture( + apiWrapper, + leadKeyPair, + sudo, + addLeaderOpeningFixture.getResult()!, + firstRewardInterval, + rewardInterval, + payoutAmount, + WorkingGroups.StorageWorkingGroup + ) + await fillLeaderOpeningFixture.runner(false) + }) + + const addWorkerOpeningFixture: AddWorkerOpeningFixture = new AddWorkerOpeningFixture( + apiWrapper, + nKeyPairs, + leadKeyPair[0], + sudo, + applicationStake, + roleStake, + openingActivationDelay, + unstakingPeriod, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Add worker opening', async () => addWorkerOpeningFixture.runner(false)) + + let firstApplyForWorkerOpeningFixture: ApplyForOpeningFixture + tap.test('First apply for worker opening', async () => { + firstApplyForWorkerOpeningFixture = new ApplyForOpeningFixture( + apiWrapper, + nKeyPairs, + sudo, + applicationStake, + roleStake, + addWorkerOpeningFixture.getResult()!, + WorkingGroups.StorageWorkingGroup + ) + await firstApplyForWorkerOpeningFixture.runner(false) + }) + + const withdrawApplicationFixture: WithdrawApplicationFixture = new WithdrawApplicationFixture( + apiWrapper, + nKeyPairs, + sudo, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Withdraw worker application', async () => withdrawApplicationFixture.runner(false)) + + let secondApplyForWorkerOpeningFixture: ApplyForOpeningFixture + tap.test('Second apply for worker opening', async () => { + secondApplyForWorkerOpeningFixture = new ApplyForOpeningFixture( + apiWrapper, + nKeyPairs, + sudo, + applicationStake, + roleStake, + addWorkerOpeningFixture.getResult()!, + WorkingGroups.StorageWorkingGroup + ) + await secondApplyForWorkerOpeningFixture.runner(false) + }) + + let beginApplicationReviewFixture: BeginApplicationReviewFixture + tap.test('Begin application review', async () => { + beginApplicationReviewFixture = new BeginApplicationReviewFixture( + apiWrapper, + leadKeyPair[0], + sudo, + addWorkerOpeningFixture.getResult()!, + WorkingGroups.StorageWorkingGroup + ) + await beginApplicationReviewFixture.runner(false) + }) + + let fillOpeningFixture: FillOpeningFixture + tap.test('Fill worker opening', async () => { + fillOpeningFixture = new FillOpeningFixture( + apiWrapper, + nKeyPairs, + leadKeyPair[0], + sudo, + addWorkerOpeningFixture.getResult()!, + firstRewardInterval, + rewardInterval, + payoutAmount, + WorkingGroups.StorageWorkingGroup + ) + await fillOpeningFixture.runner(false) + }) + + const leaveRoleFixture: LeaveRoleFixture = new LeaveRoleFixture( + apiWrapper, + leadKeyPair, + sudo, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Leaving lead role', async () => leaveRoleFixture.runner(false)) + + closeApi(apiWrapper) +}) diff --git a/tests/network-tests/src/iznik/tests/workingGroup/workerApplicationRejectionCaseTest.ts b/tests/network-tests/src/iznik/tests/workingGroup/workerApplicationRejectionCaseTest.ts new file mode 100644 index 0000000000..5c90b1df62 --- /dev/null +++ b/tests/network-tests/src/iznik/tests/workingGroup/workerApplicationRejectionCaseTest.ts @@ -0,0 +1,205 @@ +import { initConfig } from '../../utils/config' +import { registerJoystreamTypes } from '@nicaea/types' +import { closeApi } from '../../utils/closeApi' +import { ApiWrapper, WorkingGroups } from '../../utils/apiWrapper' +import { WsProvider, Keyring } from '@polkadot/api' +import { KeyringPair } from '@polkadot/keyring/types' +import { setTestTimeout } from '../../utils/setTestTimeout' +import BN from 'bn.js' +import tap from 'tap' +import { BuyMembershipHappyCaseFixture } from '../fixtures/membershipModule' +import { Utils } from '../../utils/utils' +import { + AcceptApplicationsFixture, + AddLeaderOpeningFixture, + AddWorkerOpeningFixture, + ApplyForOpeningFixture, + BeginLeaderApplicationReviewFixture, + FillLeaderOpeningFixture, + LeaveRoleFixture, + TerminateApplicationsFixture, +} from '../fixtures/workingGroupModule' + +tap.mocha.describe('Worker application happy case scenario', async () => { + initConfig() + registerJoystreamTypes() + + const nodeUrl: string = process.env.NODE_URL! + const sudoUri: string = process.env.SUDO_ACCOUNT_URI! + const keyring = new Keyring({ type: 'sr25519' }) + const provider = new WsProvider(nodeUrl) + const apiWrapper: ApiWrapper = await ApiWrapper.create(provider) + const sudo: KeyringPair = keyring.addFromUri(sudoUri) + + const N: number = +process.env.WORKING_GROUP_N! + const nKeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N) + const leadKeyPair: KeyringPair[] = Utils.createKeyPairs(keyring, 1) + const nonMemberKeyPairs = Utils.createKeyPairs(keyring, N) + + const paidTerms: number = +process.env.MEMBERSHIP_PAID_TERMS! + const applicationStake: BN = new BN(process.env.WORKING_GROUP_APPLICATION_STAKE!) + const roleStake: BN = new BN(process.env.WORKING_GROUP_ROLE_STAKE!) + const firstRewardInterval: BN = new BN(process.env.LONG_REWARD_INTERVAL!) + const rewardInterval: BN = new BN(process.env.LONG_REWARD_INTERVAL!) + const payoutAmount: BN = new BN(process.env.PAYOUT_AMOUNT!) + const unstakingPeriod: BN = new BN(process.env.STORAGE_WORKING_GROUP_UNSTAKING_PERIOD!) + const durationInBlocks = 38 + const openingActivationDelay: BN = new BN(100) + const leadOpeningActivationDelay: BN = new BN(0) + + setTestTimeout(apiWrapper, durationInBlocks) + + const happyCaseFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + nKeyPairs, + paidTerms + ) + tap.test('Creating a set of members', async () => happyCaseFixture.runner(false)) + + const leaderHappyCaseFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + leadKeyPair, + paidTerms + ) + tap.test('Buying membership for leader account', async () => leaderHappyCaseFixture.runner(false)) + + const addLeaderOpeningFixture: AddLeaderOpeningFixture = new AddLeaderOpeningFixture( + apiWrapper, + nKeyPairs, + sudo, + applicationStake, + roleStake, + leadOpeningActivationDelay, + WorkingGroups.StorageWorkingGroup + ) + // let leadOpenignId: BN + tap.test('Add lead opening', async () => await addLeaderOpeningFixture.runner(false)) + + let applyForLeaderOpeningFixture: ApplyForOpeningFixture + tap.test('Apply for lead opening', async () => { + applyForLeaderOpeningFixture = new ApplyForOpeningFixture( + apiWrapper, + leadKeyPair, + sudo, + applicationStake, + roleStake, + addLeaderOpeningFixture.getResult()!, + WorkingGroups.StorageWorkingGroup + ) + await applyForLeaderOpeningFixture.runner(false) + }) + + let beginLeaderApplicationReviewFixture: BeginLeaderApplicationReviewFixture + tap.test('Begin lead application review', async () => { + beginLeaderApplicationReviewFixture = new BeginLeaderApplicationReviewFixture( + apiWrapper, + sudo, + addLeaderOpeningFixture.getResult()!, + WorkingGroups.StorageWorkingGroup + ) + await beginLeaderApplicationReviewFixture.runner(false) + }) + + let fillLeaderOpeningFixture: FillLeaderOpeningFixture + tap.test('Fill lead opening', async () => { + fillLeaderOpeningFixture = new FillLeaderOpeningFixture( + apiWrapper, + leadKeyPair, + sudo, + addLeaderOpeningFixture.getResult()!, + firstRewardInterval, + rewardInterval, + payoutAmount, + WorkingGroups.StorageWorkingGroup + ) + await fillLeaderOpeningFixture.runner(false) + }) + + const addWorkerOpeningFixture: AddWorkerOpeningFixture = new AddWorkerOpeningFixture( + apiWrapper, + nKeyPairs, + leadKeyPair[0], + sudo, + applicationStake, + roleStake, + openingActivationDelay, + unstakingPeriod, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Add worker opening', async () => addWorkerOpeningFixture.runner(false)) + + let applyForWorkerOpeningBeforeAcceptanceFixture: ApplyForOpeningFixture + tap.test('Apply for worker opening, expect failure', async () => { + applyForWorkerOpeningBeforeAcceptanceFixture = new ApplyForOpeningFixture( + apiWrapper, + nKeyPairs, + sudo, + applicationStake, + roleStake, + addWorkerOpeningFixture.getResult()!, + WorkingGroups.StorageWorkingGroup + ) + await applyForWorkerOpeningBeforeAcceptanceFixture.runner(true) + }) + + let acceptApplicationsFixture: AcceptApplicationsFixture + tap.test('Begin accepting worker applications', async () => { + acceptApplicationsFixture = new AcceptApplicationsFixture( + apiWrapper, + leadKeyPair[0], + sudo, + addWorkerOpeningFixture.getResult()!, + WorkingGroups.StorageWorkingGroup + ) + acceptApplicationsFixture.runner(false) + }) + + let applyForWorkerOpeningAsNonMemberFixture: ApplyForOpeningFixture + tap.test('Apply for worker opening as non-member, expect failure', async () => { + applyForWorkerOpeningAsNonMemberFixture = new ApplyForOpeningFixture( + apiWrapper, + nonMemberKeyPairs, + sudo, + applicationStake, + roleStake, + addWorkerOpeningFixture.getResult()!, + WorkingGroups.StorageWorkingGroup + ) + await applyForWorkerOpeningAsNonMemberFixture.runner(true) + }) + + let applyForWorkerOpeningFixture: ApplyForOpeningFixture + tap.test('Apply for worker opening', async () => { + applyForWorkerOpeningFixture = new ApplyForOpeningFixture( + apiWrapper, + nKeyPairs, + sudo, + applicationStake, + roleStake, + addWorkerOpeningFixture.getResult()!, + WorkingGroups.StorageWorkingGroup + ) + await applyForWorkerOpeningFixture.runner(false) + }) + + const terminateApplicationsFixture: TerminateApplicationsFixture = new TerminateApplicationsFixture( + apiWrapper, + nKeyPairs, + leadKeyPair[0], + sudo, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Terminate worker applicaitons', async () => terminateApplicationsFixture.runner(false)) + + const leaveRoleFixture: LeaveRoleFixture = new LeaveRoleFixture( + apiWrapper, + leadKeyPair, + sudo, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Leaving lead role', async () => leaveRoleFixture.runner(false)) + + closeApi(apiWrapper) +}) diff --git a/tests/network-tests/src/iznik/tests/workingGroup/workerPayoutTest.ts b/tests/network-tests/src/iznik/tests/workingGroup/workerPayoutTest.ts new file mode 100644 index 0000000000..1c335afe10 --- /dev/null +++ b/tests/network-tests/src/iznik/tests/workingGroup/workerPayoutTest.ts @@ -0,0 +1,243 @@ +import { initConfig } from '../../utils/config' +import { registerJoystreamTypes } from '@nicaea/types' +import { closeApi } from '../../utils/closeApi' +import { ApiWrapper, WorkingGroups } from '../../utils/apiWrapper' +import { WsProvider, Keyring } from '@polkadot/api' +import { KeyringPair } from '@polkadot/keyring/types' +import { setTestTimeout } from '../../utils/setTestTimeout' +import { + AddLeaderOpeningFixture, + AddWorkerOpeningFixture, + ApplyForOpeningFixture, + AwaitPayoutFixture, + BeginApplicationReviewFixture, + BeginLeaderApplicationReviewFixture, + ExpectMintCapacityChangedFixture, + FillLeaderOpeningFixture, + FillOpeningFixture, + LeaveRoleFixture, +} from '../fixtures/workingGroupModule' +import BN from 'bn.js' +import tap from 'tap' +import { BuyMembershipHappyCaseFixture } from '../fixtures/membershipModule' +import { Utils } from '../../utils/utils' +import { ElectCouncilFixture } from '../fixtures/councilElectionModule' +import { VoteForProposalFixture, WorkingGroupMintCapacityProposalFixture } from '../fixtures/proposalsModule' + +tap.mocha.describe('Worker application happy case scenario', async () => { + initConfig() + registerJoystreamTypes() + + const nodeUrl: string = process.env.NODE_URL! + const sudoUri: string = process.env.SUDO_ACCOUNT_URI! + const keyring = new Keyring({ type: 'sr25519' }) + const provider = new WsProvider(nodeUrl) + const apiWrapper: ApiWrapper = await ApiWrapper.create(provider) + const sudo: KeyringPair = keyring.addFromUri(sudoUri) + + const N: number = +process.env.WORKING_GROUP_N! + const m1KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N) + const m2KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N) + const leadKeyPair: KeyringPair[] = Utils.createKeyPairs(keyring, 1) + + const paidTerms: number = +process.env.MEMBERSHIP_PAID_TERMS! + const K: number = +process.env.COUNCIL_ELECTION_K! + const greaterStake: BN = new BN(+process.env.COUNCIL_STAKE_GREATER_AMOUNT!) + const lesserStake: BN = new BN(+process.env.COUNCIL_STAKE_LESSER_AMOUNT!) + const applicationStake: BN = new BN(process.env.WORKING_GROUP_APPLICATION_STAKE!) + const roleStake: BN = new BN(process.env.WORKING_GROUP_ROLE_STAKE!) + const leaderFirstRewardInterval: BN = new BN(process.env.LONG_REWARD_INTERVAL!) + const leaderRewardInterval: BN = new BN(process.env.LONG_REWARD_INTERVAL!) + const firstRewardInterval: BN = new BN(process.env.SHORT_FIRST_REWARD_INTERVAL!) + const rewardInterval: BN = new BN(process.env.SHORT_REWARD_INTERVAL!) + const payoutAmount: BN = new BN(process.env.PAYOUT_AMOUNT!) + const unstakingPeriod: BN = new BN(process.env.STORAGE_WORKING_GROUP_UNSTAKING_PERIOD!) + const mintCapacity: BN = new BN(process.env.STORAGE_WORKING_GROUP_MINTING_CAPACITY!) + const durationInBlocks = 48 + const openingActivationDelay: BN = new BN(0) + + setTestTimeout(apiWrapper, durationInBlocks) + + const firstMemberSetFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + m1KeyPairs, + paidTerms + ) + tap.test('Creating first set of members', async () => firstMemberSetFixture.runner(false)) + + const secondMemberSetFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + m2KeyPairs, + paidTerms + ) + tap.test('Creating second set of members', async () => secondMemberSetFixture.runner(false)) + + const electCouncilFixture: ElectCouncilFixture = new ElectCouncilFixture( + apiWrapper, + m1KeyPairs, + m2KeyPairs, + K, + sudo, + greaterStake, + lesserStake + ) + tap.test('Elect council', async () => electCouncilFixture.runner(false)) + + const leaderHappyCaseFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture( + apiWrapper, + sudo, + leadKeyPair, + paidTerms + ) + tap.test('Buying membership for leader account', async () => leaderHappyCaseFixture.runner(false)) + + const addLeaderOpeningFixture: AddLeaderOpeningFixture = new AddLeaderOpeningFixture( + apiWrapper, + m1KeyPairs, + sudo, + applicationStake, + roleStake, + openingActivationDelay, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Add lead opening', async () => await addLeaderOpeningFixture.runner(false)) + + let applyForLeaderOpeningFixture: ApplyForOpeningFixture + tap.test('Apply for lead opening', async () => { + applyForLeaderOpeningFixture = new ApplyForOpeningFixture( + apiWrapper, + leadKeyPair, + sudo, + applicationStake, + roleStake, + addLeaderOpeningFixture.getResult()!, + WorkingGroups.StorageWorkingGroup + ) + await applyForLeaderOpeningFixture.runner(false) + }) + + let beginLeaderApplicationReviewFixture: BeginLeaderApplicationReviewFixture + tap.test('Begin lead application review', async () => { + beginLeaderApplicationReviewFixture = new BeginLeaderApplicationReviewFixture( + apiWrapper, + sudo, + addLeaderOpeningFixture.getResult()!, + WorkingGroups.StorageWorkingGroup + ) + await beginLeaderApplicationReviewFixture.runner(false) + }) + + let fillLeaderOpeningFixture: FillLeaderOpeningFixture + tap.test('Fill lead opening', async () => { + fillLeaderOpeningFixture = new FillLeaderOpeningFixture( + apiWrapper, + leadKeyPair, + sudo, + addLeaderOpeningFixture.getResult()!, + leaderFirstRewardInterval, + leaderRewardInterval, + payoutAmount, + WorkingGroups.StorageWorkingGroup + ) + await fillLeaderOpeningFixture.runner(false) + }) + + const workingGroupMintCapacityProposalFixture: WorkingGroupMintCapacityProposalFixture = new WorkingGroupMintCapacityProposalFixture( + apiWrapper, + m1KeyPairs, + sudo, + mintCapacity, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Propose mint capacity', async () => workingGroupMintCapacityProposalFixture.runner(false)) + + let voteForProposalFixture: VoteForProposalFixture + const expectMintCapacityChanged: ExpectMintCapacityChangedFixture = new ExpectMintCapacityChangedFixture( + apiWrapper, + mintCapacity + ) + tap.test('Approve mint capacity', async () => { + voteForProposalFixture = new VoteForProposalFixture( + apiWrapper, + m2KeyPairs, + sudo, + workingGroupMintCapacityProposalFixture.getResult()! + ) + voteForProposalFixture.runner(false) + await expectMintCapacityChanged.runner(false) + }) + + const addWorkerOpeningFixture: AddWorkerOpeningFixture = new AddWorkerOpeningFixture( + apiWrapper, + m1KeyPairs, + leadKeyPair[0], + sudo, + applicationStake, + roleStake, + openingActivationDelay, + unstakingPeriod, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Add worker opening', async () => addWorkerOpeningFixture.runner(false)) + + let applyForWorkerOpeningFixture: ApplyForOpeningFixture + tap.test('First apply for worker opening', async () => { + applyForWorkerOpeningFixture = new ApplyForOpeningFixture( + apiWrapper, + m1KeyPairs, + sudo, + applicationStake, + roleStake, + addWorkerOpeningFixture.getResult()!, + WorkingGroups.StorageWorkingGroup + ) + await applyForWorkerOpeningFixture.runner(false) + }) + + let beginApplicationReviewFixture: BeginApplicationReviewFixture + tap.test('Begin application review', async () => { + beginApplicationReviewFixture = new BeginApplicationReviewFixture( + apiWrapper, + leadKeyPair[0], + sudo, + addWorkerOpeningFixture.getResult()!, + WorkingGroups.StorageWorkingGroup + ) + await beginApplicationReviewFixture.runner(false) + }) + + let fillOpeningFixture: FillOpeningFixture + tap.test('Fill worker opening', async () => { + fillOpeningFixture = new FillOpeningFixture( + apiWrapper, + m1KeyPairs, + leadKeyPair[0], + sudo, + addWorkerOpeningFixture.getResult()!, + firstRewardInterval, + rewardInterval, + payoutAmount, + WorkingGroups.StorageWorkingGroup + ) + await fillOpeningFixture.runner(false) + }) + + const awaitPayoutFixture: AwaitPayoutFixture = new AwaitPayoutFixture( + apiWrapper, + m1KeyPairs, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Await worker payout', async () => awaitPayoutFixture.runner(false)) + + const leaveRoleFixture: LeaveRoleFixture = new LeaveRoleFixture( + apiWrapper, + leadKeyPair, + sudo, + WorkingGroups.StorageWorkingGroup + ) + tap.test('Leaving lead role', async () => leaveRoleFixture.runner(false)) + + closeApi(apiWrapper) +}) diff --git a/tests/network-tests/src/iznik/utils/apiWrapper.ts b/tests/network-tests/src/iznik/utils/apiWrapper.ts new file mode 100644 index 0000000000..52682fb567 --- /dev/null +++ b/tests/network-tests/src/iznik/utils/apiWrapper.ts @@ -0,0 +1,1469 @@ +import { ApiPromise, WsProvider } from '@polkadot/api' +import { Option, Vec, Bytes, u32 } from '@polkadot/types' +import { Codec } from '@polkadot/types/types' +import { KeyringPair } from '@polkadot/keyring/types' +import { UserInfo, PaidMembershipTerms, MemberId } from '@nicaea/types/members' +import { Mint, MintId } from '@nicaea/types/mint' +import { Lead, LeadId } from '@nicaea/types/content-working-group' +import { Application, WorkerId, Worker, ApplicationIdToWorkerIdMap, Opening } from '@nicaea/types/working-group' +import { RoleParameters } from '@nicaea/types/roles' +import { Seat } from '@nicaea/types/council' +import { Balance, Event, EventRecord, AccountId, BlockNumber, BalanceOf } from '@polkadot/types/interfaces' +import BN from 'bn.js' +import { SubmittableExtrinsic } from '@polkadot/api/types' +import { Sender } from './sender' +import { Utils } from './utils' +import { Stake, StakedState } from '@nicaea/types/stake' +import { RewardRelationship } from '@nicaea/types/recurring-rewards' +import { Opening as HiringOpening, Application as HiringApplication, ApplicationId } from '@nicaea/types/hiring' +import { WorkingGroupOpening } from '../dto/workingGroupOpening' +import { FillOpeningParameters } from '../dto/fillOpeningParameters' + +export enum WorkingGroups { + StorageWorkingGroup = 'storageWorkingGroup', +} + +export class ApiWrapper { + private readonly api: ApiPromise + private readonly sender: Sender + + public static async create(provider: WsProvider): Promise { + const api = await ApiPromise.create({ provider }) + return new ApiWrapper(api) + } + + constructor(api: ApiPromise) { + this.api = api + this.sender = new Sender(api) + } + + public close() { + this.api.disconnect() + } + + public getWorkingGroupString(workingGroup: WorkingGroups): string { + switch (workingGroup) { + case WorkingGroups.StorageWorkingGroup: + return 'Storage' + default: + throw new Error(`Invalid working group string representation: ${workingGroup}`) + } + } + + public async buyMembership( + account: KeyringPair, + paidTermsId: number, + name: string, + expectFailure = false + ): Promise { + return this.sender.signAndSend( + this.api.tx.members.buyMembership(paidTermsId, new UserInfo({ 'handle': name, 'avatar_uri': '', 'about': '' })), + account, + expectFailure + ) + } + + public getMemberIds(address: string): Promise { + return this.api.query.members.memberIdsByControllerAccountId>(address) + } + + public getBalance(address: string): Promise { + return this.api.query.balances.freeBalance(address) + } + + public async transferBalance(from: KeyringPair, to: string, amount: BN): Promise { + return this.sender.signAndSend(this.api.tx.balances.transfer(to, amount), from) + } + + public getPaidMembershipTerms(paidTermsId: number): Promise> { + return this.api.query.members.paidMembershipTermsById>(paidTermsId) + } + + public getMembershipFee(paidTermsId: number): Promise { + return this.getPaidMembershipTerms(paidTermsId).then((terms) => terms.unwrap().fee.toBn()) + } + + public async transferBalanceToAccounts(from: KeyringPair, to: KeyringPair[], amount: BN): Promise { + return Promise.all( + to.map(async (keyPair) => { + await this.transferBalance(from, keyPair.address, amount) + }) + ) + } + + private getBaseTxFee(): BN { + return this.api.createType('BalanceOf', this.api.consts.transactionPayment.transactionBaseFee).toBn() + } + + private estimateTxFee(tx: SubmittableExtrinsic<'promise'>): BN { + const baseFee: BN = this.getBaseTxFee() + const byteFee: BN = this.api.createType('BalanceOf', this.api.consts.transactionPayment.transactionByteFee).toBn() + return Utils.calcTxLength(tx).mul(byteFee).add(baseFee) + } + + public estimateBuyMembershipFee(account: KeyringPair, paidTermsId: number, name: string): BN { + return this.estimateTxFee( + this.api.tx.members.buyMembership(paidTermsId, new UserInfo({ 'handle': name, 'avatar_uri': '', 'about': '' })) + ) + } + + public estimateApplyForCouncilFee(amount: BN): BN { + return this.estimateTxFee(this.api.tx.councilElection.apply(amount)) + } + + public estimateVoteForCouncilFee(nominee: string, salt: string, stake: BN): BN { + const hashedVote: string = Utils.hashVote(nominee, salt) + return this.estimateTxFee(this.api.tx.councilElection.vote(hashedVote, stake)) + } + + public estimateRevealVoteFee(nominee: string, salt: string): BN { + const hashedVote: string = Utils.hashVote(nominee, salt) + return this.estimateTxFee(this.api.tx.councilElection.reveal(hashedVote, nominee, salt)) + } + + public estimateProposeRuntimeUpgradeFee(stake: BN, name: string, description: string, runtime: Bytes | string): BN { + return this.estimateTxFee( + this.api.tx.proposalsCodex.createRuntimeUpgradeProposal(stake, name, description, stake, runtime) + ) + } + + public estimateProposeTextFee(stake: BN, name: string, description: string, text: string): BN { + return this.estimateTxFee(this.api.tx.proposalsCodex.createTextProposal(stake, name, description, stake, text)) + } + + public estimateProposeSpendingFee( + title: string, + description: string, + stake: BN, + balance: BN, + destination: string + ): BN { + return this.estimateTxFee( + this.api.tx.proposalsCodex.createSpendingProposal(stake, title, description, stake, balance, destination) + ) + } + + public estimateProposeContentWorkingGroupMintCapacityFee( + title: string, + description: string, + stake: BN, + balance: BN + ): BN { + return this.estimateTxFee( + this.api.tx.proposalsCodex.createSetContentWorkingGroupMintCapacityProposal( + stake, + title, + description, + stake, + balance + ) + ) + } + + public estimateProposeValidatorCountFee(title: string, description: string, stake: BN): BN { + return this.estimateTxFee( + this.api.tx.proposalsCodex.createSetValidatorCountProposal(stake, title, description, stake, stake) + ) + } + + public estimateProposeLeadFee(title: string, description: string, stake: BN, address: string): BN { + return this.estimateTxFee( + this.api.tx.proposalsCodex.createSetLeadProposal(stake, title, description, stake, { stake, address }) + ) + } + + public estimateProposeEvictStorageProviderFee(title: string, description: string, stake: BN, address: string): BN { + return this.estimateTxFee( + this.api.tx.proposalsCodex.createEvictStorageProviderProposal(stake, title, description, stake, address) + ) + } + + public estimateProposeStorageRoleParametersFee( + title: string, + description: string, + stake: BN, + minStake: BN, + minActors: BN, + maxActors: BN, + reward: BN, + rewardPeriod: BN, + bondingPeriod: BN, + unbondingPeriod: BN, + minServicePeriod: BN, + startupGracePeriod: BN, + entryRequestFee: BN + ): BN { + return this.estimateTxFee( + this.api.tx.proposalsCodex.createSetStorageRoleParametersProposal(stake, title, description, stake, [ + minStake, + minActors, + maxActors, + reward, + rewardPeriod, + bondingPeriod, + unbondingPeriod, + minServicePeriod, + startupGracePeriod, + entryRequestFee, + ]) + ) + } + + public estimateProposeElectionParametersFee( + title: string, + description: string, + stake: BN, + announcingPeriod: BN, + votingPeriod: BN, + revealingPeriod: BN, + councilSize: BN, + candidacyLimit: BN, + newTermDuration: BN, + minCouncilStake: BN, + minVotingStake: BN + ): BN { + return this.estimateTxFee( + this.api.tx.proposalsCodex.createSetElectionParametersProposal(stake, title, description, stake, [ + announcingPeriod, + votingPeriod, + revealingPeriod, + councilSize, + candidacyLimit, + newTermDuration, + minCouncilStake, + minVotingStake, + ]) + ) + } + + public estimateVoteForProposalFee(): BN { + return this.estimateTxFee(this.api.tx.proposalsEngine.vote(0, 0, 'Approve')) + } + + public estimateAddOpeningFee(opening: WorkingGroupOpening, module: WorkingGroups): BN { + return this.estimateTxFee( + this.api.tx[module].addOpening( + opening.getActivateAt(), + opening.getCommitment(), + opening.getText(), + opening.getOpeningType() + ) + ) + } + + public estimateAcceptApplicationsFee(module: WorkingGroups): BN { + return this.estimateTxFee(this.api.tx[module].acceptApplications(0)) + } + + public estimateApplyOnOpeningFee(account: KeyringPair, module: WorkingGroups): BN { + return this.estimateTxFee( + this.api.tx[module].applyOnOpening( + 0, + 0, + account.address, + 0, + 0, + 'Some testing text used for estimation purposes which is longer than text expected during the test' + ) + ) + } + + public estimateBeginApplicantReviewFee(module: WorkingGroups): BN { + return this.estimateTxFee(this.api.tx[module].beginApplicantReview(0)) + } + + public estimateFillOpeningFee(module: WorkingGroups): BN { + return this.estimateTxFee( + this.api.tx[module].fillOpening(0, [0], { + 'amount_per_payout': 0, + 'next_payment_at_block': 0, + 'payout_interval': 0, + }) + ) + } + + public estimateIncreaseStakeFee(module: WorkingGroups): BN { + return this.estimateTxFee(this.api.tx[module].increaseStake(0, 0)) + } + + public estimateDecreaseStakeFee(module: WorkingGroups): BN { + return this.estimateTxFee(this.api.tx[module].decreaseStake(0, 0)) + } + + public estimateUpdateRoleAccountFee(address: string, module: WorkingGroups): BN { + return this.estimateTxFee(this.api.tx[module].updateRoleAccount(0, address)) + } + + public estimateUpdateRewardAccountFee(address: string, module: WorkingGroups): BN { + return this.estimateTxFee(this.api.tx[module].updateRewardAccount(0, address)) + } + + public estimateLeaveRoleFee(module: WorkingGroups): BN { + return this.estimateTxFee(this.api.tx[module].leaveRole(0, 'Long justification text')) + } + + public estimateWithdrawApplicationFee(module: WorkingGroups): BN { + return this.estimateTxFee(this.api.tx[module].withdrawApplication(0)) + } + + public estimateTerminateApplicationFee(module: WorkingGroups): BN { + return this.estimateTxFee(this.api.tx[module].terminateApplication(0)) + } + + public estimateSlashStakeFee(module: WorkingGroups): BN { + return this.estimateTxFee(this.api.tx[module].slashStake(0, 0)) + } + + public estimateTerminateRoleFee(module: WorkingGroups): BN { + return this.estimateTxFee( + this.api.tx[module].terminateRole( + 0, + 'Long justification text explaining why the worker role will be terminated', + false + ) + ) + } + + public estimateProposeCreateWorkingGroupLeaderOpeningFee(): BN { + const opening: WorkingGroupOpening = new WorkingGroupOpening() + .setActivateAtBlock(undefined) + .setMaxActiveApplicants(new BN(32)) + .setMaxReviewPeriodLength(new BN(32)) + .setApplicationStakingPolicyAmount(new BN(1)) + .setApplicationCrowdedOutUnstakingPeriodLength(new BN(1)) + .setApplicationExpiredUnstakingPeriodLength(new BN(1)) + .setRoleStakingPolicyAmount(new BN(1)) + .setRoleCrowdedOutUnstakingPeriodLength(new BN(1)) + .setRoleExpiredUnstakingPeriodLength(new BN(1)) + .setSlashableMaxCount(new BN(0)) + .setSlashableMaxPercentPtsPerTime(new BN(0)) + .setSuccessfulApplicantApplicationStakeUnstakingPeriod(new BN(1)) + .setFailedApplicantApplicationStakeUnstakingPeriod(new BN(1)) + .setFailedApplicantRoleStakeUnstakingPeriod(new BN(1)) + .setTerminateApplicationStakeUnstakingPeriod(new BN(1)) + .setTerminateRoleStakeUnstakingPeriod(new BN(1)) + .setExitRoleApplicationStakeUnstakingPeriod(new BN(1)) + .setExitRoleStakeUnstakingPeriod(new BN(1)) + + return this.estimateTxFee( + this.api.tx.proposalsCodex.createAddWorkingGroupLeaderOpeningProposal( + 0, + 'some long title for the purpose of testing', + 'some long description for the purpose of testing', + 0, + { + 'activate_at': opening.getActivateAt(), + 'commitment': opening.getCommitment(), + 'human_readable_text': 'Opening readable text', + 'working_group': 'Storage', + } + ) + ) + } + + public estimateProposeBeginWorkingGroupLeaderApplicationReviewFee(): BN { + return this.estimateTxFee( + this.api.tx.proposalsCodex.createBeginReviewWorkingGroupLeaderApplicationsProposal( + 0, + 'Some testing text used for estimation purposes which is longer than text expected during the test', + 'Some testing text used for estimation purposes which is longer than text expected during the test', + 0, + 0, + 'Storage' + ) + ) + } + + public estimateProposeFillLeaderOpeningFee(): BN { + const fillOpeningParameters: FillOpeningParameters = new FillOpeningParameters() + .setAmountPerPayout(new BN(1)) + .setNextPaymentAtBlock(new BN(99999)) + .setPayoutInterval(new BN(99999)) + .setOpeningId(new BN(0)) + .setSuccessfulApplicationId(new BN(0)) + .setWorkingGroup('Storage') + + return this.estimateTxFee( + this.api.tx.proposalsCodex.createFillWorkingGroupLeaderOpeningProposal( + 0, + 'Some testing text used for estimation purposes which is longer than text expected during the test', + 'Some testing text used for estimation purposes which is longer than text expected during the test', + 0, + fillOpeningParameters.getFillOpeningParameters() + ) + ) + } + + public estimateProposeTerminateLeaderRoleFee(): BN { + return this.estimateTxFee( + this.api.tx.proposalsCodex.createTerminateWorkingGroupLeaderRoleProposal( + 0, + 'Some testing text used for estimation purposes which is longer than text expected during the test', + 'Some testing text used for estimation purposes which is longer than text expected during the test', + 0, + { + 'worker_id': 0, + 'rationale': 'Exceptionaly long and extraordinary descriptive rationale', + 'slash': true, + 'working_group': 'Storage', + } + ) + ) + } + + public estimateProposeLeaderRewardFee(): BN { + return this.estimateTxFee( + this.api.tx.proposalsCodex.createSetWorkingGroupLeaderRewardProposal( + 0, + 'Some testing text used for estimation purposes which is longer than text expected during the test', + 'Some testing text used for estimation purposes which is longer than text expected during the test', + 0, + 0, + 0, + 'Storage' + ) + ) + } + + public estimateProposeDecreaseLeaderStakeFee(): BN { + return this.estimateTxFee( + this.api.tx.proposalsCodex.createDecreaseWorkingGroupLeaderStakeProposal( + 0, + 'Some testing text used for estimation purposes which is longer than text expected during the test', + 'Some testing text used for estimation purposes which is longer than text expected during the test', + 0, + 0, + 0, + 'Storage' + ) + ) + } + + public estimateProposeSlashLeaderStakeFee(): BN { + return this.estimateTxFee( + this.api.tx.proposalsCodex.createSlashWorkingGroupLeaderStakeProposal( + 0, + 'Some testing text used for estimation purposes which is longer than text expected during the test', + 'Some testing text used for estimation purposes which is longer than text expected during the test', + 0, + 0, + 0, + 'Storage' + ) + ) + } + + public estimateProposeWorkingGroupMintCapacityFee(): BN { + return this.estimateTxFee( + this.api.tx.proposalsCodex.createSetWorkingGroupMintCapacityProposal( + 0, + 'Some testing text used for estimation purposes which is longer than text expected during the test', + 'Some testing text used for estimation purposes which is longer than text expected during the test', + 0, + 0, + 'Storage' + ) + ) + } + + private applyForCouncilElection(account: KeyringPair, amount: BN): Promise { + return this.sender.signAndSend(this.api.tx.councilElection.apply(amount), account, false) + } + + public batchApplyForCouncilElection(accounts: KeyringPair[], amount: BN): Promise { + return Promise.all( + accounts.map(async (keyPair) => { + await this.applyForCouncilElection(keyPair, amount) + }) + ) + } + + public async getCouncilElectionStake(address: string): Promise { + // TODO alter then `applicantStake` type will be introduced + return this.api.query.councilElection.applicantStakes(address).then((stake) => { + const parsed = JSON.parse(stake.toString()) + return new BN(parsed.new) + }) + } + + private voteForCouncilMember(account: KeyringPair, nominee: string, salt: string, stake: BN): Promise { + const hashedVote: string = Utils.hashVote(nominee, salt) + return this.sender.signAndSend(this.api.tx.councilElection.vote(hashedVote, stake), account, false) + } + + public batchVoteForCouncilMember( + accounts: KeyringPair[], + nominees: KeyringPair[], + salt: string[], + stake: BN + ): Promise { + return Promise.all( + accounts.map(async (keyPair, index) => { + await this.voteForCouncilMember(keyPair, nominees[index].address, salt[index], stake) + }) + ) + } + + private revealVote(account: KeyringPair, commitment: string, nominee: string, salt: string): Promise { + return this.sender.signAndSend(this.api.tx.councilElection.reveal(commitment, nominee, salt), account, false) + } + + public batchRevealVote(accounts: KeyringPair[], nominees: KeyringPair[], salt: string[]): Promise { + return Promise.all( + accounts.map(async (keyPair, index) => { + const commitment = Utils.hashVote(nominees[index].address, salt[index]) + await this.revealVote(keyPair, commitment, nominees[index].address, salt[index]) + }) + ) + } + + // TODO consider using configurable genesis instead + public sudoStartAnnouncingPerion(sudo: KeyringPair, endsAtBlock: BN): Promise { + return this.sender.signAndSend( + this.api.tx.sudo.sudo(this.api.tx.councilElection.setStageAnnouncing(endsAtBlock)), + sudo, + false + ) + } + + public sudoStartVotingPerion(sudo: KeyringPair, endsAtBlock: BN): Promise { + return this.sender.signAndSend( + this.api.tx.sudo.sudo(this.api.tx.councilElection.setStageVoting(endsAtBlock)), + sudo, + false + ) + } + + public sudoStartRevealingPerion(sudo: KeyringPair, endsAtBlock: BN): Promise { + return this.sender.signAndSend( + this.api.tx.sudo.sudo(this.api.tx.councilElection.setStageRevealing(endsAtBlock)), + sudo, + false + ) + } + + public sudoSetCouncilMintCapacity(sudo: KeyringPair, capacity: BN): Promise { + return this.sender.signAndSend( + this.api.tx.sudo.sudo(this.api.tx.council.setCouncilMintCapacity(capacity)), + sudo, + false + ) + } + + public sudoSetWorkingGroupMintCapacity(sudo: KeyringPair, capacity: BN, module: WorkingGroups): Promise { + return this.sender.signAndSend(this.api.tx.sudo.sudo(this.api.tx[module].setMintCapacity(capacity)), sudo, false) + } + + public getBestBlock(): Promise { + return this.api.derive.chain.bestNumber() + } + + public getCouncil(): Promise { + return this.api.query.council.activeCouncil>().then((seats) => { + return (seats as unknown) as Seat[] + }) + } + + public getRuntime(): Promise { + return this.api.query.substrate.code() + } + + public async proposeRuntime( + account: KeyringPair, + stake: BN, + name: string, + description: string, + runtime: Bytes | string + ): Promise { + const memberId: BN = (await this.getMemberIds(account.address))[0].toBn() + return this.sender.signAndSend( + this.api.tx.proposalsCodex.createRuntimeUpgradeProposal(memberId, name, description, stake, runtime), + account, + false + ) + } + + public async proposeText( + account: KeyringPair, + stake: BN, + name: string, + description: string, + text: string + ): Promise { + const memberId: BN = (await this.getMemberIds(account.address))[0].toBn() + return this.sender.signAndSend( + this.api.tx.proposalsCodex.createTextProposal(memberId, name, description, stake, text), + account, + false + ) + } + + public async proposeSpending( + account: KeyringPair, + title: string, + description: string, + stake: BN, + balance: BN, + destination: string + ): Promise { + const memberId: BN = (await this.getMemberIds(account.address))[0].toBn() + return this.sender.signAndSend( + this.api.tx.proposalsCodex.createSpendingProposal(memberId, title, description, stake, balance, destination), + account, + false + ) + } + + public async proposeContentWorkingGroupMintCapacity( + account: KeyringPair, + title: string, + description: string, + stake: BN, + balance: BN + ): Promise { + const memberId: BN = (await this.getMemberIds(account.address))[0].toBn() + return this.sender.signAndSend( + this.api.tx.proposalsCodex.createSetContentWorkingGroupMintCapacityProposal( + memberId, + title, + description, + stake, + balance + ), + account, + false + ) + } + + public async proposeValidatorCount( + account: KeyringPair, + title: string, + description: string, + stake: BN, + validatorCount: BN + ): Promise { + const memberId: BN = (await this.getMemberIds(account.address))[0].toBn() + return this.sender.signAndSend( + this.api.tx.proposalsCodex.createSetValidatorCountProposal(memberId, title, description, stake, validatorCount), + account, + false + ) + } + + public async proposeLead( + account: KeyringPair, + title: string, + description: string, + stake: BN, + leadAccount: KeyringPair + ): Promise { + const memberId: BN = (await this.getMemberIds(account.address))[0].toBn() + const leadMemberId: BN = (await this.getMemberIds(leadAccount.address))[0].toBn() + const addressString: string = leadAccount.address + return this.sender.signAndSend( + this.api.tx.proposalsCodex.createSetLeadProposal(memberId, title, description, stake, [ + leadMemberId, + addressString, + ]), + account, + false + ) + } + + public async proposeEvictStorageProvider( + account: KeyringPair, + title: string, + description: string, + stake: BN, + storageProvider: string + ): Promise { + const memberId: BN = (await this.getMemberIds(account.address))[0].toBn() + return this.sender.signAndSend( + this.api.tx.proposalsCodex.createEvictStorageProviderProposal( + memberId, + title, + description, + stake, + storageProvider + ), + account, + false + ) + } + + public async proposeStorageRoleParameters( + account: KeyringPair, + title: string, + description: string, + stake: BN, + minStake: BN, + minActors: BN, + maxActors: BN, + reward: BN, + rewardPeriod: BN, + bondingPeriod: BN, + unbondingPeriod: BN, + minServicePeriod: BN, + startupGracePeriod: BN, + entryRequestFee: BN + ): Promise { + const memberId: BN = (await this.getMemberIds(account.address))[0].toBn() + return this.sender.signAndSend( + this.api.tx.proposalsCodex.createSetStorageRoleParametersProposal(memberId, title, description, stake, [ + minStake, + minActors, + maxActors, + reward, + rewardPeriod, + bondingPeriod, + unbondingPeriod, + minServicePeriod, + startupGracePeriod, + entryRequestFee, + ]), + account, + false + ) + } + + public async proposeElectionParameters( + account: KeyringPair, + title: string, + description: string, + stake: BN, + announcingPeriod: BN, + votingPeriod: BN, + revealingPeriod: BN, + councilSize: BN, + candidacyLimit: BN, + newTermDuration: BN, + minCouncilStake: BN, + minVotingStake: BN + ): Promise { + const memberId: BN = (await this.getMemberIds(account.address))[0].toBn() + return this.sender.signAndSend( + this.api.tx.proposalsCodex.createSetElectionParametersProposal(memberId, title, description, stake, [ + announcingPeriod, + votingPeriod, + revealingPeriod, + councilSize, + candidacyLimit, + newTermDuration, + minCouncilStake, + minVotingStake, + ]), + account, + false + ) + } + + public async proposeBeginWorkingGroupLeaderApplicationReview( + account: KeyringPair, + title: string, + description: string, + stake: BN, + openingId: BN, + workingGroup: string + ) { + const memberId: BN = (await this.getMemberIds(account.address))[0].toBn() + return this.sender.signAndSend( + this.api.tx.proposalsCodex.createBeginReviewWorkingGroupLeaderApplicationsProposal( + memberId, + title, + description, + stake, + openingId, + workingGroup + ), + account, + false + ) + } + + public approveProposal(account: KeyringPair, memberId: BN, proposal: BN): Promise { + return this.sender.signAndSend(this.api.tx.proposalsEngine.vote(memberId, proposal, 'Approve'), account, false) + } + + public batchApproveProposal(council: KeyringPair[], proposal: BN): Promise { + return Promise.all( + council.map(async (keyPair) => { + const memberId: BN = (await this.getMemberIds(keyPair.address))[0].toBn() + await this.approveProposal(keyPair, memberId, proposal) + }) + ) + } + + public getBlockDuration(): BN { + return this.api.createType('Moment', this.api.consts.babe.expectedBlockTime).toBn() + } + + public expectProposalCreated(): Promise { + return new Promise(async (resolve) => { + const unsubscribe = await this.api.query.system.events>((events) => { + events.forEach((record) => { + if (record.event.method && record.event.method.toString() === 'ProposalCreated') { + unsubscribe() + resolve(new BN(record.event.data[1].toString())) + } + }) + }) + }) + } + + public expectRuntimeUpgraded(): Promise { + return new Promise(async (resolve) => { + const unsubscribe = await this.api.query.system.events>((events) => { + events.forEach((record) => { + if (record.event.method.toString() === 'RuntimeUpdated') { + unsubscribe() + resolve() + } + }) + }) + }) + } + + public expectProposalFinalized(): Promise { + return new Promise(async (resolve) => { + const unsubscribe = await this.api.query.system.events>((events) => { + events.forEach((record) => { + if ( + record.event.method && + record.event.method.toString() === 'ProposalStatusUpdated' && + record.event.data[1].toString().includes('Executed') + ) { + unsubscribe() + resolve() + } + }) + }) + }) + } + + public expectOpeningFilled(): Promise { + return new Promise(async (resolve) => { + const unsubscribe = await this.api.query.system.events>((events) => { + events.forEach((record) => { + if (record.event.method && record.event.method.toString() === 'OpeningFilled') { + unsubscribe() + resolve((record.event.data[1] as unknown) as ApplicationIdToWorkerIdMap) + } + }) + }) + }) + } + + public expectEvent(eventName: string): Promise { + return new Promise(async (resolve) => { + const unsubscribe = await this.api.query.system.events>((events) => { + events.forEach((record) => { + if (record.event.method && record.event.method.toString() === eventName) { + unsubscribe() + resolve(record.event) + } + }) + }) + }) + } + + public expectApplicationReviewBegan(): Promise { + return new Promise(async (resolve) => { + const unsubscribe = await this.api.query.system.events>((events) => { + events.forEach((record) => { + if (record.event.method && record.event.method.toString() === 'BeganApplicationReview') { + unsubscribe() + resolve((record.event.data as unknown) as BN) + } + }) + }) + }) + } + + public getTotalIssuance(): Promise { + return this.api.query.balances.totalIssuance() + } + + public async getRequiredProposalStake(numerator: number, denominator: number): Promise { + const issuance: number = await (await this.getTotalIssuance()).toNumber() + const stake = (issuance * numerator) / denominator + return new BN(stake.toFixed(0)) + } + + public getProposalCount(): Promise { + return this.api.query.proposalsEngine.proposalCount() + } + + public async getContentWorkingGroupMintCapacity(): Promise { + const mintId: MintId = await this.api.query.contentWorkingGroup.mint() + const mintCodec = await this.api.query.minting.mints(mintId) + const mint: Mint = (mintCodec[0] as unknown) as Mint + return mint.getField('capacity') + } + + public async getWorkingGroupMintCapacity(module: WorkingGroups): Promise { + const mintId: MintId = await this.api.query[module].mint() + const mintCodec = await this.api.query.minting.mints(mintId) + const mint: Mint = (mintCodec[0] as unknown) as Mint + return mint.getField('capacity') + } + + public getValidatorCount(): Promise { + return this.api.query.staking.validatorCount() + } + + public async getCurrentLeadAddress(): Promise { + const leadId: Option = await this.api.query.contentWorkingGroup.currentLeadId>() + const leadCodec = await this.api.query.contentWorkingGroup.leadById(leadId.unwrap()) + const lead = (leadCodec[0] as unknown) as Lead + return lead.role_account.toString() + } + + public async createStorageProvider(account: KeyringPair): Promise { + const memberId: BN = (await this.getMemberIds(account.address))[0].toBn() + await this.sender.signAndSend(this.api.tx.actors.roleEntryRequest('StorageProvider', memberId), account, false) + await this.sender.signAndSend(this.api.tx.actors.stake('StorageProvider', account.address), account, false) + return + } + + public async isStorageProvider(address: string): Promise { + const storageProviders: Vec = await this.api.query.actors.accountIdsByRole>( + 'StorageProvider' + ) + const accountWorkers: BN = await this.getWorkerIdByRoleAccount(address, WorkingGroups.StorageWorkingGroup) + return accountWorkers !== undefined + } + + public async addOpening( + leader: KeyringPair, + opening: WorkingGroupOpening, + module: WorkingGroups, + expectFailure: boolean + ): Promise { + return this.sender.signAndSend(this.createAddOpeningTransaction(opening, module), leader, expectFailure) + } + + public async sudoAddOpening(sudo: KeyringPair, opening: WorkingGroupOpening, module: WorkingGroups): Promise { + return this.sender.signAndSend( + this.api.tx.sudo.sudo(this.createAddOpeningTransaction(opening, module)), + sudo, + false + ) + } + + public async proposeCreateWorkingGroupLeaderOpening( + account: KeyringPair, + title: string, + description: string, + proposalStake: BN, + opening: WorkingGroupOpening, + workingGroup: string + ): Promise { + const memberId: BN = (await this.getMemberIds(account.address))[0] + return this.sender.signAndSend( + this.api.tx.proposalsCodex.createAddWorkingGroupLeaderOpeningProposal( + memberId, + title, + description, + proposalStake, + opening.getAddOpeningParameters(workingGroup) + ), + account, + false + ) + } + + public async proposeFillLeaderOpening( + account: KeyringPair, + title: string, + description: string, + proposalStake: BN, + fillOpeningParameters: FillOpeningParameters + ): Promise { + const memberId: BN = (await this.getMemberIds(account.address))[0] + return this.sender.signAndSend( + this.api.tx.proposalsCodex.createFillWorkingGroupLeaderOpeningProposal( + memberId, + title, + description, + proposalStake, + fillOpeningParameters.getFillOpeningParameters() + ), + account, + false + ) + } + + public async proposeTerminateLeaderRole( + account: KeyringPair, + title: string, + description: string, + proposalStake: BN, + leadWorkerId: BN, + rationale: string, + slash: boolean, + workingGroup: string + ): Promise { + const memberId: BN = (await this.getMemberIds(account.address))[0] + return this.sender.signAndSend( + this.api.tx.proposalsCodex.createTerminateWorkingGroupLeaderRoleProposal( + memberId, + title, + description, + proposalStake, + { + 'worker_id': leadWorkerId, + rationale, + slash, + 'working_group': workingGroup, + } + ), + account, + false + ) + } + + public async proposeLeaderReward( + account: KeyringPair, + title: string, + description: string, + proposalStake: BN, + workerId: BN, + rewardAmount: BN, + workingGroup: string + ): Promise { + const memberId: BN = (await this.getMemberIds(account.address))[0] + return this.sender.signAndSend( + this.api.tx.proposalsCodex.createSetWorkingGroupLeaderRewardProposal( + memberId, + title, + description, + proposalStake, + workerId, + rewardAmount, + workingGroup + ), + account, + false + ) + } + + public async proposeDecreaseLeaderStake( + account: KeyringPair, + title: string, + description: string, + proposalStake: BN, + workerId: BN, + rewardAmount: BN, + workingGroup: string + ): Promise { + const memberId: BN = (await this.getMemberIds(account.address))[0] + return this.sender.signAndSend( + this.api.tx.proposalsCodex.createDecreaseWorkingGroupLeaderStakeProposal( + memberId, + title, + description, + proposalStake, + workerId, + rewardAmount, + workingGroup + ), + account, + false + ) + } + + public async proposeSlashLeaderStake( + account: KeyringPair, + title: string, + description: string, + proposalStake: BN, + workerId: BN, + rewardAmount: BN, + workingGroup: string + ): Promise { + const memberId: BN = (await this.getMemberIds(account.address))[0] + return this.sender.signAndSend( + this.api.tx.proposalsCodex.createSlashWorkingGroupLeaderStakeProposal( + memberId, + title, + description, + proposalStake, + workerId, + rewardAmount, + workingGroup + ), + account, + false + ) + } + + public async proposeWorkingGroupMintCapacity( + account: KeyringPair, + title: string, + description: string, + proposalStake: BN, + mintCapacity: BN, + workingGroup: string + ): Promise { + const memberId: BN = (await this.getMemberIds(account.address))[0] + return this.sender.signAndSend( + this.api.tx.proposalsCodex.createSetWorkingGroupMintCapacityProposal( + memberId, + title, + description, + proposalStake, + mintCapacity, + workingGroup + ), + account, + false + ) + } + + private createAddOpeningTransaction( + opening: WorkingGroupOpening, + module: WorkingGroups + ): SubmittableExtrinsic<'promise'> { + return this.api.tx[module].addOpening( + opening.getActivateAt(), + opening.getCommitment(), + opening.getText(), + opening.getOpeningType() + ) + } + + public async acceptApplications(leader: KeyringPair, openingId: BN, module: WorkingGroups): Promise { + return this.sender.signAndSend(this.api.tx[module].acceptApplications(openingId), leader, false) + } + + public async beginApplicantReview(leader: KeyringPair, openingId: BN, module: WorkingGroups): Promise { + return this.sender.signAndSend(this.api.tx[module].beginApplicantReview(openingId), leader, false) + } + + public async sudoBeginApplicantReview(sudo: KeyringPair, openingId: BN, module: WorkingGroups): Promise { + return this.sender.signAndSend( + this.api.tx.sudo.sudo(this.api.tx[module].beginApplicantReview(openingId)), + sudo, + false + ) + } + + public async applyOnOpening( + account: KeyringPair, + roleAccountAddress: string, + openingId: BN, + roleStake: BN, + applicantStake: BN, + text: string, + expectFailure: boolean, + module: WorkingGroups + ): Promise { + const memberId: BN = (await this.getMemberIds(account.address))[0] + return this.sender.signAndSend( + this.api.tx[module].applyOnOpening(memberId, openingId, roleAccountAddress, roleStake, applicantStake, text), + account, + expectFailure + ) + } + + public async batchApplyOnOpening( + accounts: KeyringPair[], + openingId: BN, + roleStake: BN, + applicantStake: BN, + text: string, + module: WorkingGroups, + expectFailure: boolean + ): Promise { + return Promise.all( + accounts.map(async (keyPair) => { + await this.applyOnOpening( + keyPair, + keyPair.address, + openingId, + roleStake, + applicantStake, + text, + expectFailure, + module + ) + }) + ) + } + + public async fillOpening( + leader: KeyringPair, + openingId: BN, + applicationId: BN[], + amountPerPayout: BN, + nextPaymentBlock: BN, + payoutInterval: BN, + module: WorkingGroups + ): Promise { + return this.sender.signAndSend( + this.api.tx[module].fillOpening(openingId, applicationId, { + 'amount_per_payout': amountPerPayout, + 'next_payment_at_block': nextPaymentBlock, + 'payout_interval': payoutInterval, + }), + leader, + false + ) + } + + public async sudoFillOpening( + sudo: KeyringPair, + openingId: BN, + applicationId: BN[], + amountPerPayout: BN, + nextPaymentBlock: BN, + payoutInterval: BN, + module: WorkingGroups + ): Promise { + return this.sender.signAndSend( + this.api.tx.sudo.sudo( + this.api.tx[module].fillOpening(openingId, applicationId, { + 'amount_per_payout': amountPerPayout, + 'next_payment_at_block': nextPaymentBlock, + 'payout_interval': payoutInterval, + }) + ), + sudo, + false + ) + } + + public async increaseStake(worker: KeyringPair, workerId: BN, stake: BN, module: WorkingGroups): Promise { + return this.sender.signAndSend(this.api.tx[module].increaseStake(workerId, stake), worker, false) + } + + public async decreaseStake( + leader: KeyringPair, + workerId: BN, + stake: BN, + module: WorkingGroups, + expectFailure: boolean + ): Promise { + return this.sender.signAndSend(this.api.tx[module].decreaseStake(workerId, stake), leader, expectFailure) + } + + public async slashStake( + leader: KeyringPair, + workerId: BN, + stake: BN, + module: WorkingGroups, + expectFailure: boolean + ): Promise { + return this.sender.signAndSend(this.api.tx[module].slashStake(workerId, stake), leader, expectFailure) + } + + public async updateRoleAccount( + worker: KeyringPair, + workerId: BN, + newRoleAccount: string, + module: WorkingGroups + ): Promise { + return this.sender.signAndSend(this.api.tx[module].updateRoleAccount(workerId, newRoleAccount), worker, false) + } + + public async updateRewardAccount( + worker: KeyringPair, + workerId: BN, + newRewardAccount: string, + module: WorkingGroups + ): Promise { + return this.sender.signAndSend(this.api.tx[module].updateRewardAccount(workerId, newRewardAccount), worker, false) + } + + public async withdrawApplication(account: KeyringPair, workerId: BN, module: WorkingGroups): Promise { + return this.sender.signAndSend(this.api.tx[module].withdrawApplication(workerId), account, false) + } + + public async batchWithdrawApplication(accounts: KeyringPair[], module: WorkingGroups): Promise { + return Promise.all( + accounts.map(async (keyPair) => { + const applicationIds: BN[] = await this.getApplicationsIdsByRoleAccount(keyPair.address, module) + await this.withdrawApplication(keyPair, applicationIds[0], module) + }) + ) + } + + public async terminateApplication(leader: KeyringPair, applicationId: BN, module: WorkingGroups): Promise { + return this.sender.signAndSend(this.api.tx[module].terminateApplication(applicationId), leader, false) + } + + public async batchTerminateApplication( + leader: KeyringPair, + roleAccounts: KeyringPair[], + module: WorkingGroups + ): Promise { + return Promise.all( + roleAccounts.map(async (keyPair) => { + const applicationIds: BN[] = await this.getActiveApplicationsIdsByRoleAccount(keyPair.address, module) + await this.terminateApplication(leader, applicationIds[0], module) + }) + ) + } + + public async terminateRole( + leader: KeyringPair, + applicationId: BN, + text: string, + module: WorkingGroups, + expectFailure: boolean + ): Promise { + return this.sender.signAndSend(this.api.tx[module].terminateRole(applicationId, text, false), leader, expectFailure) + } + + public async leaveRole( + account: KeyringPair, + text: string, + expectFailure: boolean, + module: WorkingGroups + ): Promise { + const workerId: BN = await this.getWorkerIdByRoleAccount(account.address, module) + return this.sender.signAndSend(this.api.tx[module].leaveRole(workerId, text), account, expectFailure) + } + + public async batchLeaveRole( + roleAccounts: KeyringPair[], + text: string, + expectFailure: boolean, + module: WorkingGroups + ): Promise { + return Promise.all( + roleAccounts.map(async (keyPair) => { + await this.leaveRole(keyPair, text, expectFailure, module) + }) + ) + } + + public async getStorageRoleParameters(): Promise { + return (await this.api.query.actors.parameters>('StorageProvider')).unwrap() + } + + public async getAnnouncingPeriod(): Promise { + return this.api.query.councilElection.announcingPeriod() + } + + public async getVotingPeriod(): Promise { + return this.api.query.councilElection.votingPeriod() + } + + public async getRevealingPeriod(): Promise { + return this.api.query.councilElection.revealingPeriod() + } + + public async getCouncilSize(): Promise { + return this.api.query.councilElection.councilSize() + } + + public async getCandidacyLimit(): Promise { + return this.api.query.councilElection.candidacyLimit() + } + + public async getNewTermDuration(): Promise { + return this.api.query.councilElection.newTermDuration() + } + + public async getMinCouncilStake(): Promise { + return this.api.query.councilElection.minCouncilStake() + } + + public async getMinVotingStake(): Promise { + return this.api.query.councilElection.minVotingStake() + } + + public async getNextOpeningId(module: WorkingGroups): Promise { + return this.api.query[module].nextOpeningId() + } + + public async getNextApplicationId(module: WorkingGroups): Promise { + return this.api.query[module].nextApplicationId() + } + + public async getOpening(id: BN, module: WorkingGroups): Promise { + return ((await this.api.query[module].openingById(id))[0] as unknown) as Opening + } + + public async getHiringOpening(id: BN): Promise { + return ((await this.api.query.hiring.openingById(id))[0] as unknown) as HiringOpening + } + + public async getWorkers(module: WorkingGroups): Promise { + return ((await this.api.query[module].workerById())[1] as unknown) as Worker[] + } + + public async getWorkerById(id: BN, module: WorkingGroups): Promise { + return ((await this.api.query[module].workerById(id))[0] as unknown) as Worker + } + + public async getWorkerIdByRoleAccount(address: string, module: WorkingGroups): Promise { + const workersAndIds = await this.api.query[module].workerById() + const workers: Worker[] = (workersAndIds[1] as unknown) as Worker[] + const ids: WorkerId[] = (workersAndIds[0] as unknown) as WorkerId[] + const index: number = workers.findIndex((worker) => worker.role_account_id.toString() === address) + return ids[index] + } + + public async getApplicationsIdsByRoleAccount(address: string, module: WorkingGroups): Promise { + const applicationsAndIds = await this.api.query[module].applicationById() + const applications: Application[] = (applicationsAndIds[1] as unknown) as Application[] + const ids: ApplicationId[] = (applicationsAndIds[0] as unknown) as ApplicationId[] + return applications + .map((application, index) => (application.role_account_id.toString() === address ? ids[index] : undefined)) + .filter((id) => id !== undefined) as BN[] + } + + public async getHiringApplicationById(id: BN): Promise { + return ((await this.api.query.hiring.applicationById(id))[0] as unknown) as HiringApplication + } + + public async getApplicationById(id: BN, module: WorkingGroups): Promise { + return ((await this.api.query[module].applicationById(id))[0] as unknown) as Application + } + + public async getActiveApplicationsIdsByRoleAccount(address: string, module: WorkingGroups): Promise { + const applicationsAndIds = await this.api.query[module].applicationById() + const applications: Application[] = (applicationsAndIds[1] as unknown) as Application[] + const ids: ApplicationId[] = (applicationsAndIds[0] as unknown) as ApplicationId[] + return ( + await Promise.all( + applications.map(async (application, index) => { + if ( + application.role_account_id.toString() === address && + (await this.getHiringApplicationById(application.application_id)).stage.type === 'Active' + ) { + return ids[index] + } else { + return undefined + } + }) + ) + ).filter((index) => index !== undefined) as BN[] + } + + public async getStake(id: BN): Promise { + return ((await this.api.query.stake.stakes(id))[0] as unknown) as Stake + } + + public async getWorkerStakeAmount(workerId: BN, module: WorkingGroups): Promise { + const stakeId: BN = (await this.getWorkerById(workerId, module)).role_stake_profile.unwrap().stake_id + return (((await this.getStake(stakeId)).staking_status.value as unknown) as StakedState).staked_amount + } + + public async getRewardRelationship(id: BN): Promise { + return (( + await this.api.query.recurringRewards.rewardRelationships(id) + )[0] as unknown) as RewardRelationship + } + + public async getWorkerRewardAccount(workerId: BN, module: WorkingGroups): Promise { + const rewardRelationshipId: BN = (await this.getWorkerById(workerId, module)).reward_relationship.unwrap() + return (await this.getRewardRelationship(rewardRelationshipId)).getField('account').toString() + } + + public async getLeadWorkerId(module: WorkingGroups): Promise { + return (await this.api.query[module].currentLead>()).unwrapOr(undefined) + } +} diff --git a/tests/network-tests/src/iznik/utils/closeApi.ts b/tests/network-tests/src/iznik/utils/closeApi.ts new file mode 100644 index 0000000000..f783623d84 --- /dev/null +++ b/tests/network-tests/src/iznik/utils/closeApi.ts @@ -0,0 +1,8 @@ +import { ApiWrapper } from './apiWrapper' +import tap from 'tap' + +export function closeApi(apiWrapper: ApiWrapper) { + tap.teardown(() => { + apiWrapper.close() + }) +} diff --git a/tests/network-tests/src/iznik/utils/config.ts b/tests/network-tests/src/iznik/utils/config.ts new file mode 100644 index 0000000000..0c431297e1 --- /dev/null +++ b/tests/network-tests/src/iznik/utils/config.ts @@ -0,0 +1,5 @@ +import { config } from 'dotenv' + +export function initConfig() { + config() +} diff --git a/tests/network-tests/src/iznik/utils/sender.ts b/tests/network-tests/src/iznik/utils/sender.ts new file mode 100644 index 0000000000..2fba9b887c --- /dev/null +++ b/tests/network-tests/src/iznik/utils/sender.ts @@ -0,0 +1,66 @@ +import BN from 'bn.js' +import { ApiPromise } from '@polkadot/api' +import { Index } from '@polkadot/types/interfaces' +import { SubmittableExtrinsic } from '@polkadot/api/types' +import { KeyringPair } from '@polkadot/keyring/types' + +export class Sender { + private readonly api: ApiPromise + private static nonceMap: Map = new Map() + + constructor(api: ApiPromise) { + this.api = api + } + + private async getNonce(address: string): Promise { + let oncahinNonce: BN = new BN(0) + if (!Sender.nonceMap.get(address)) { + oncahinNonce = await this.api.query.system.accountNonce(address) + } + let nonce: BN | undefined = Sender.nonceMap.get(address) + if (!nonce) { + nonce = oncahinNonce + } + const nextNonce: BN = nonce.addn(1) + Sender.nonceMap.set(address, nextNonce) + return nonce + } + + private clearNonce(address: string): void { + Sender.nonceMap.delete(address) + } + + public async signAndSend( + tx: SubmittableExtrinsic<'promise'>, + account: KeyringPair, + expectFailure = false + ): Promise { + return new Promise(async (resolve, reject) => { + const nonce: BN = await this.getNonce(account.address) + const signedTx = tx.sign(account, { nonce }) + await signedTx + .send(async (result) => { + if (result.status.isFinalized === true && result.events !== undefined) { + result.events.forEach((event) => { + if (event.event.method === 'ExtrinsicFailed') { + if (expectFailure) { + resolve() + } else { + reject(new Error('Extrinsic failed unexpectedly')) + } + } + }) + resolve() + } + if (result.status.isFuture) { + console.log('nonce ' + nonce + ' for account ' + account.address + ' is in future') + this.clearNonce(account.address) + reject(new Error('Extrinsic nonce is in future')) + } + }) + .catch((error) => { + reject(error) + }) + }) + } +} diff --git a/tests/network-tests/src/iznik/utils/setTestTimeout.ts b/tests/network-tests/src/iznik/utils/setTestTimeout.ts new file mode 100644 index 0000000000..66328bd88a --- /dev/null +++ b/tests/network-tests/src/iznik/utils/setTestTimeout.ts @@ -0,0 +1,7 @@ +import tap from 'tap' +import { ApiWrapper } from './apiWrapper' + +export function setTestTimeout(apiWrapper: ApiWrapper, durationInBlocks: number) { + const durationInMillis = apiWrapper.getBlockDuration().muln(durationInBlocks).toNumber() + tap.setTimeout(durationInMillis) +} diff --git a/tests/network-tests/src/iznik/utils/utils.ts b/tests/network-tests/src/iznik/utils/utils.ts new file mode 100644 index 0000000000..6db1768733 --- /dev/null +++ b/tests/network-tests/src/iznik/utils/utils.ts @@ -0,0 +1,63 @@ +import { IExtrinsic } from '@polkadot/types/types' +import { compactToU8a, stringToU8a } from '@polkadot/util' +import { blake2AsHex } from '@polkadot/util-crypto' +import BN from 'bn.js' +import fs from 'fs' +import Keyring, { decodeAddress } from '@polkadot/keyring' +import { Seat } from '@nicaea/types/council' +import { KeyringPair } from '@polkadot/keyring/types' +import { v4 as uuid } from 'uuid' + +export class Utils { + private static LENGTH_ADDRESS = 32 + 1 // publicKey + prefix + private static LENGTH_ERA = 2 // assuming mortals + private static LENGTH_SIGNATURE = 64 // assuming ed25519 or sr25519 + private static LENGTH_VERSION = 1 // 0x80 & version + + public static calcTxLength = (extrinsic?: IExtrinsic | null, nonce?: BN): BN => { + return new BN( + Utils.LENGTH_VERSION + + Utils.LENGTH_ADDRESS + + Utils.LENGTH_SIGNATURE + + Utils.LENGTH_ERA + + compactToU8a(nonce || 0).length + + (extrinsic ? extrinsic.encodedLength : 0) + ) + } + + /** hash(accountId + salt) */ + public static hashVote(accountId: string, salt: string): string { + const accountU8a = decodeAddress(accountId) + const saltU8a = stringToU8a(salt) + const voteU8a = new Uint8Array(accountU8a.length + saltU8a.length) + voteU8a.set(accountU8a) + voteU8a.set(saltU8a, accountU8a.length) + + const hash = blake2AsHex(voteU8a, 256) + return hash + } + + public static wait(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)) + } + + public static getTotalStake(seat: Seat): BN { + return new BN(+seat.stake.toString() + seat.backers.reduce((a, baker) => a + +baker.stake.toString(), 0)) + } + + public static readRuntimeFromFile(path: string): string { + return '0x' + fs.readFileSync(path).toString('hex') + } + + public static camelToSnakeCase(key: string): string { + return key.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`) + } + + public static createKeyPairs(keyring: Keyring, n: number): KeyringPair[] { + const nKeyPairs: KeyringPair[] = [] + for (let i = 0; i < n; i++) { + nKeyPairs.push(keyring.addFromUri(i + uuid().substring(0, 8))) + } + return nKeyPairs + } +}