Skip to content

Commit

Permalink
Merge pull request #4976 from eshark9312/verify-validator-membership
Browse files Browse the repository at this point in the history
Verify Validator Membership
  • Loading branch information
thesan committed Dec 6, 2023
2 parents 5d911ce + f9b9bfd commit dfaffcf
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 0 deletions.
6 changes: 6 additions & 0 deletions metadata-protobuf/proto/WorkingGroups.proto
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,11 @@ message ModeratePost {
message RemarkMetadataAction {
oneof action {
ModeratePost moderate_post = 1;
VerifyValidator verify_validator = 2;
}
}

message VerifyValidator {
required uint64 member_id = 1;
required bool is_verified = 2;
}
27 changes: 27 additions & 0 deletions query-node/mappings/src/workingGroups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
InvalidActionMetadata,
LeaderSetEvent,
LeaderUnsetEvent,
MemberMetadata,
Membership,
NewMissedRewardLevelReachedEvent,
OpeningAddedEvent,
Expand Down Expand Up @@ -725,6 +726,19 @@ export async function workingGroups_LeadRemarked({ store, event }: EventContext
return invalidMetadata(`Forum post not found by id: ${postId}`)
}
await moderatePost(store, event, 'leadRemark', post, actor, rationale)
} else if (metadata?.verifyValidator) {
if (group.name !== 'membershipWorkingGroup') {
return invalidMetadata(`The ${group.name} can't verify the validator's membership`)
}
const { memberId, isVerified } = metadata.verifyValidator

const member = await getById(store, Membership, memberId)
if (!member) {
return invalidMetadata(`Membership not found by id: ${memberId}`)
}
member.metadata.isVerifiedValidator = isVerified
await store.save<MemberMetadata>(member.metadata)
await store.save<Membership>(member)
} else {
return invalidMetadata('Unrecognized remarked action')
}
Expand All @@ -747,6 +761,19 @@ export async function workingGroups_WorkerRemarked({ store, event }: EventContex
return invalidMetadata(`Forum post not found by id: ${postId}`)
}
await moderatePost(store, event, 'workerRemark', post, actor, rationale)
} else if (metadata?.verifyValidator) {
if (group.name !== 'membershipWorkingGroup') {
return invalidMetadata(`The ${group.name} can't verify the validator's membership`)
}
const { memberId, isVerified } = metadata.verifyValidator

const member = await getById(store, Membership, memberId)
if (!member) {
return invalidMetadata(`Membership not found by id: ${memberId}`)
}
member.metadata.isVerifiedValidator = isVerified
await store.save<MemberMetadata>(member.metadata)
await store.save<Membership>(member)
} else {
return invalidMetadata('Unrecognized remarked action')
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import Long from 'long'
import { SubmittableExtrinsic } from '@polkadot/api/types'
import { ISubmittableResult } from '@polkadot/types/types/'
import { WorkerId, MemberId } from '@joystream/types/primitives'
import { RemarkMetadataAction } from '@joystream/metadata-protobuf'
import { Api } from '../../Api'
import { QueryNodeApi } from '../../QueryNodeApi'
import { AnyQueryNodeEvent, EventDetails } from '../../types'
import { Utils } from '../../utils'
import { MembershipFieldsFragment } from '../../graphql/generated/queries'
import { WithMembershipWorkersFixture } from './WithMembershipWorkersFixture'

export type VerifyValidatorInput = {
memberId: MemberId
isVerified: boolean
asWorker?: WorkerId
expectFailure?: boolean
}

export class VerifyValidatorMembershipFixture extends WithMembershipWorkersFixture {
protected verifications: VerifyValidatorInput[]

public constructor(api: Api, query: QueryNodeApi, verifications: VerifyValidatorInput[]) {
super(api, query)
this.verifications = verifications
}

protected async getSignerAccountOrAccounts(): Promise<string[]> {
return this.getSignersFromInput(this.verifications)
}

protected async getExtrinsics(): Promise<SubmittableExtrinsic<'promise'>[]> {
return this.verifications.map((u) => {
const metadata = Utils.metadataToBytes(RemarkMetadataAction, {
verifyValidator: { memberId: Long.fromString(u.memberId.toString()), isVerified: u.isVerified },
})
return u.memberId
? this.api.tx.membershipWorkingGroup.workerRemark(u.memberId, metadata)
: this.api.tx.membershipWorkingGroup.leadRemark(metadata)
})
}

protected async getEventFromResult(result: ISubmittableResult): Promise<EventDetails> {
if (this.api.findEvent(result, 'membershipWorkingGroup', 'WorkerRemarked')) {
return this.api.getEventDetails(result, 'membershipWorkingGroup', 'WorkerRemarked')
} else {
return this.api.getEventDetails(result, 'membershipWorkingGroup', 'LeadRemarked')
}
}

private assertQueriedMembershipsAreValid(qMembers: MembershipFieldsFragment[]): void {
this.events.map((e, i) => {
const verification = this.verifications[i]
if (verification.expectFailure) return

const qMembership = qMembers.find((p) => p.id === verification.memberId.toString())
Utils.assert(qMembership, 'Query node: Membership not found')
})
}

protected assertQueryNodeEventIsValid(qEvent: AnyQueryNodeEvent, i: number): void {
// TODO: implement QN checks
}

async runQueryNodeChecks(): Promise<void> {
await super.runQueryNodeChecks()
const qMembers = await this.query.getMembersByIds(this.verifications.map((m) => m.memberId))
this.assertQueriedMembershipsAreValid(qMembers)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { WorkerId } from '@joystream/types/primitives'
import { StandardizedFixture } from '../../Fixture'

export abstract class WithMembershipWorkersFixture extends StandardizedFixture {
protected membershipLeadId?: WorkerId

protected async getMembershipLeadId(): Promise<WorkerId> {
if (!this.membershipLeadId) {
const optMembershipLeadId = await this.api.query.membershipWorkingGroup.currentLead()
if (optMembershipLeadId.isNone) {
throw new Error('Membership working group lead not set!')
}

this.membershipLeadId = optMembershipLeadId.unwrap()
}

return this.membershipLeadId
}

protected async getSignersFromInput(input: { asWorker?: WorkerId }[]): Promise<string[]> {
return Promise.all(
input.map(async (r) => {
const workerId = r.asWorker || (await this.getMembershipLeadId())
return (await this.api.query.membershipWorkingGroup.workerById(workerId)).unwrap().roleAccountId.toString()
})
)
}
}
1 change: 1 addition & 0 deletions tests/network-tests/src/fixtures/membership/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export { RemoveStakingAccountsHappyCaseFixture } from './RemoveStakingAccountsHa
export { TransferInvitesHappyCaseFixture } from './TransferInvitesHappyCaseFixture'
export { UpdateAccountsHappyCaseFixture } from './UpdateAccountsHappyCaseFixture'
export { UpdateProfileHappyCaseFixture, MemberProfileData } from './UpdateProfileHappyCaseFixture'
export { VerifyValidatorMembershipFixture, VerifyValidatorInput } from './VerifyValidatorMembershipFixture'
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { FlowProps } from '../../Flow'
import {
BuyMembershipHappyCaseFixture,
VerifyValidatorInput,
VerifyValidatorMembershipFixture,
} from '../../fixtures/membership'

import { extendDebug } from '../../Debugger'
import { FixtureRunner } from '../../Fixture'

export default async function updateValidatorVerificationStatus({ api, query }: FlowProps): Promise<void> {
const debug = extendDebug('flow:updating-validator-verification-status')
debug('Started')
api.enableDebugTxLogs()

const accounts = (await api.createKeyPairs(2)).map(({ key }) => key.address)
const buyMembershipsFixture = new BuyMembershipHappyCaseFixture(api, query, accounts)

const updates1: VerifyValidatorInput[] = buyMembershipsFixture
.getCreatedMembers()
.map((memberId) => ({ memberId, isVerified: true }))

const verifyFixture = new VerifyValidatorMembershipFixture(api, query, updates1)
await new FixtureRunner(verifyFixture).runWithQueryNodeChecks()

const updates2 = updates1.map(({ memberId }) => ({ memberId, isVerified: false }))

const unverifyFixture = new VerifyValidatorMembershipFixture(api, query, updates2)
await new FixtureRunner(unverifyFixture).runWithQueryNodeChecks()

debug('Done')
}
2 changes: 2 additions & 0 deletions tests/network-tests/src/scenarios/full.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import invitingMembers from '../flows/membership/invitingMembers'
import { createAppActions } from '../flows/content/createAppActions'
import { createApp } from '../flows/content/createApp'
import { updateApp } from '../flows/content/updateApp'
import updateValidatorVerificationStatus from '../flows/membership/updateValidatorVerifications'

// eslint-disable-next-line @typescript-eslint/no-floating-promises
scenario('Full', async ({ job, env }) => {
Expand Down Expand Up @@ -89,6 +90,7 @@ scenario('Full', async ({ job, env }) => {

// Memberships (depending on hired leads)
job('updating member verification status', updatingVerificationStatus).after(hireLeads)
job('updating validator verification status', updateValidatorVerificationStatus).after(hireLeads)

// Forum:
job('forum categories', categories).requires(hireLeads)
Expand Down
2 changes: 2 additions & 0 deletions tests/network-tests/src/scenarios/memberships.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import invitingMembers from '../flows/membership/invitingMembers'
import transferringInvites from '../flows/membership/transferringInvites'
import managingStakingAccounts from '../flows/membership/managingStakingAccounts'
import { scenario } from '../Scenario'
import updateValidatorVerificationStatus from '../flows/membership/updateValidatorVerifications'
import updatingVerificationStatus from '../flows/membership/updateVerificationStatus'
import leadOpening from '../flows/working-groups/leadOpening'
import electCouncil from '../flows/council/elect'
Expand All @@ -24,4 +25,5 @@ scenario('Memberships', async ({ job }) => {
job('transferring invites', transferringInvites)
job('managing staking accounts', managingStakingAccounts)
job('updating member verification status', updatingVerificationStatus).after(hireLeads)
job('updating validator verification status', updateValidatorVerificationStatus).after(hireLeads)
})

0 comments on commit dfaffcf

Please sign in to comment.