Skip to content

Commit

Permalink
🗝️ Fix opening:create on the liberated chain (#4383)
Browse files Browse the repository at this point in the history
* Enable ts aliases in scripts

* Make `council:elect` callable from other scripts

* Create proposals with new staking accounts
Because all wellknown accounts are locked once the council is elected.

* Add a `approveProposal` script

* Fix `opening:create` on the liberated chain

* Document `-d BLOCK_TIME`

* Fix `useProposalConstants`
  • Loading branch information
thesan committed Jun 5, 2023
1 parent e78a75a commit d46fce3
Show file tree
Hide file tree
Showing 12 changed files with 206 additions and 132 deletions.
6 changes: 4 additions & 2 deletions docs/mocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,20 @@ To test most of the extrinsics requires existing on-chain data. To create some o

Available commands:

- `yarn workspace @joystream/pioneer node-mocks council:elect [-d BLOCK_TIME] [--to ELECTION_STAGE]` - Run an election until the specified stage: VOTE, REVEAL, or IDLE (default)
- `yarn workspace @joystream/pioneer node-mocks council:elect [-d BLOCK_TIME¹] [--to ELECTION_STAGE]` - Run an election until the specified stage: VOTE, REVEAL, or IDLE (default)
- `yarn workspace @joystream/pioneer node-mocks council:announce` - Announce enough candidacies to start the voting stage when the announcing stage ends
- `yarn workspace @joystream/pioneer node-mocks council:vote` - Vote for the announced by the previous command candidate to start the revealing stage next
- `yarn workspace @joystream/pioneer node-mocks council:reveal` - Reveal the votes casted by the previous command to start elect a new council and start the idle stage next
- `yarn workspace @joystream/pioneer node-mocks members:create` - generate memberships using query-node mocks data
- `yarn workspace @joystream/pioneer node-mocks set-budget` - Set membership Working Group budget
- `yarn workspace @joystream/pioneer node-mocks opening:create` - Create an opening
- `yarn workspace @joystream/pioneer node-mocks opening:create [-d BLOCK_TIME¹]` - Create an opening
- `yarn workspace @joystream/pioneer node-mocks opening:fill` - Fill existing opening
- `yarn workspace @joystream/pioneer node-mocks upcoming-opening:create` - Create an upcoming opening
- `yarn workspace @joystream/pioneer node-mocks forumCategory:create` - Create a forum category
- `yarn workspace @joystream/pioneer node-mocks transfer` - Transfer tokens between accounts

**(¹)** `BLOCK_TIME` is the time between each block. It is 6000ms by default but on testing chain it is 1000ms. Therefore when running some of the scripts on these testing chain `-d 1000` should be added for the command to succeed.

To show help:

```shell
Expand Down
16 changes: 7 additions & 9 deletions packages/ui/dev/node-mocks/commands/council/announce.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { ApiPromise } from '@polkadot/api'
import { uniq } from 'lodash'

import { lockLookup } from '../../../../src/accounts/model/lockTypes'
import { flatMapP, mapP } from '../../../../src/common/utils'
import memberData from '../../../../src/mocks/data/raw/members.json'
import { lockLookup } from '@/accounts/model/lockTypes'
import { flatMapP, mapP } from '@/common/utils'
import memberData from '@/mocks/data/raw/members.json'

import { accountsMap } from '../../data/addresses'
import { signAndSend, withApi } from '../../lib/api'
import { createMembersCommand } from '../members/create'

export const announceCandidaciesCommand = async (api: ApiPromise) => {
await createMembersCommand(api)

const candidateCount = api.consts.council.councilSize.toNumber() + 1
const announceStake = api.consts.council.minCandidateStake

Expand Down Expand Up @@ -60,13 +63,8 @@ export const announceCandidaciesCommand = async (api: ApiPromise) => {
})
}

const handler = async () => {
await createMembersCommand()
await withApi(announceCandidaciesCommand)
}

export const announceCandidaciesModule = {
command: 'council:announce',
describe: 'Announce council candidates',
handler: handler,
handler: () => withApi(announceCandidaciesCommand),
}
48 changes: 30 additions & 18 deletions packages/ui/dev/node-mocks/commands/council/elect.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { ApiPromise } from '@polkadot/api'
import yargs from 'yargs'

import { MILLISECONDS_PER_BLOCK } from '../../../../src/common/model/formatters'
import { MILLISECONDS_PER_BLOCK } from '@/common/model/formatters'

import { nextCouncilStageCommand } from '../../../helpers/nextCouncilStage'
import { withApi } from '../../lib/api'
import { createMembersCommand } from '../members/create'

import { announceCandidaciesCommand } from './announce'
import { revealVotesCommand } from './reveal'
import { castVotesCommand } from './vote'

const options = {
export const electCouncilOptions = {
blockTime: {
number: true,
default: MILLISECONDS_PER_BLOCK,
Expand All @@ -22,22 +23,33 @@ const options = {
},
}

type Args = yargs.InferredOptionTypes<typeof electCouncilOptions>
type Props = Partial<Args> & { replaceCurrent?: boolean }

export const electCouncilCommand = async (
api: ApiPromise,
{ blockTime = MILLISECONDS_PER_BLOCK, to = 'IDLE', replaceCurrent = true }: Props
) => {
if (!replaceCurrent) {
const councilors = await api.query.council.councilMembers()
if (councilors.length > 0) return
}

await announceCandidaciesCommand(api)
await nextCouncilStageCommand(api, blockTime)
if (to === 'VOTE') return

await castVotesCommand(api)
await nextCouncilStageCommand(api, blockTime)
if (to === 'REVEAL') return

await revealVotesCommand(api)
await nextCouncilStageCommand(api, blockTime)
}

export const electCouncilModule = {
command: 'council:elect',
describe: 'Elect a full council',
handler: async ({ blockTime, to }: yargs.InferredOptionTypes<typeof options>) => {
await createMembersCommand()
await withApi(async (api) => {
await announceCandidaciesCommand(api)
if (to === 'VOTE') return
await nextCouncilStageCommand(api, blockTime)

await castVotesCommand(api)
if (to === 'REVEAL') return
await nextCouncilStageCommand(api, blockTime)

await revealVotesCommand(api)
})
},
builder: (argv: yargs.Argv<unknown>) => argv.options(options),
handler: ({ blockTime, to }: Args) => withApi((api) => electCouncilCommand(api, { blockTime, to })),
builder: (argv: yargs.Argv<unknown>) => argv.options(electCouncilOptions),
}
70 changes: 35 additions & 35 deletions packages/ui/dev/node-mocks/commands/members/create.ts
Original file line number Diff line number Diff line change
@@ -1,52 +1,52 @@
/* eslint-disable no-console */
import { MembershipMetadata } from '@joystream/metadata-protobuf'
import { ApiPromise } from '@polkadot/api'

import { metadataToBytes } from '@/common/model/JoystreamNode'
import members from '@/mocks/data/raw/members.json'

import { metadataToBytes } from '../../../../src/common/model/JoystreamNode'
import members from '../../../../src/mocks/data/raw/members.json'
import { getSudoAccount } from '../../data/addresses'
import { signAndSend, withApi } from '../../lib/api'

export const createMembersCommand = async () => {
await withApi(async (api) => {
const nextId = await api.query.members.nextMemberId()

if (Number(nextId) > 0) {
console.log('Some members were already added')
return
}

const createMembers = members.map((member) => {
return api.tx.members.buyMembership({
handle: member.handle,
metadata: metadataToBytes(MembershipMetadata, {
name: member.metadata.name,
about: member.metadata.about,
}),
rootAccount: member.rootAccount,
controllerAccount: member.controllerAccount,
})
export const createMembersCommand = async (api: ApiPromise) => {
const nextId = await api.query.members.nextMemberId()

if (Number(nextId) > 0) {
console.log('Some members were already added')
return
}

const createMembers = members.map((member) => {
return api.tx.members.buyMembership({
handle: member.handle,
metadata: metadataToBytes(MembershipMetadata, {
name: member.metadata.name,
about: member.metadata.about,
}),
rootAccount: member.rootAccount,
controllerAccount: member.controllerAccount,
})
})

const tx = api.tx.utility.batch(createMembers)
const tx = api.tx.utility.batch(createMembers)

await signAndSend(tx, getSudoAccount())
await signAndSend(tx, getSudoAccount())

await Promise.all(
members.map(async ({ id, boundAccounts, controllerAccount }) => {
for (const boundAccount of boundAccounts) {
// Bind staking account
await signAndSend(api.tx.members.addStakingAccountCandidate(id), boundAccount)
await Promise.all(
members.map(async ({ id, boundAccounts, controllerAccount }) => {
for (const boundAccount of boundAccounts) {
// Bind staking account
await signAndSend(api.tx.members.addStakingAccountCandidate(id), boundAccount)

// Confirm staking account
await signAndSend(api.tx.members.confirmStakingAccount(id, boundAccount), controllerAccount)
}
})
)
})
// Confirm staking account
await signAndSend(api.tx.members.confirmStakingAccount(id, boundAccount), controllerAccount)
}
})
)
}

export const createMembersModule = {
command: 'members:create',
describe: 'Create member accounts from mocks',
handler: createMembersCommand,
handler: () => withApi(createMembersCommand),
}
51 changes: 36 additions & 15 deletions packages/ui/dev/node-mocks/commands/opening/create.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
import { OpeningMetadata } from '@joystream/metadata-protobuf'
import { pick } from 'lodash'
import yargs from 'yargs'

import { createType } from '@/common/model/createType'
import { MILLISECONDS_PER_BLOCK } from '@/common/model/formatters'
import { metadataToBytes } from '@/common/model/JoystreamNode'
import { isDefined } from '@/common/utils'

import { getDataFromEvent, metadataToBytes } from '../../../../src/common/model/JoystreamNode'
import { GROUP, GroupIdName } from '../../consts'
import { getSudoAccount } from '../../data/addresses'
import { signAndSend, withApi } from '../../lib/api'
import { withApi } from '../../lib/api'
import { electCouncilCommand, electCouncilOptions } from '../council/elect'
import { approveProposal } from '../proposals/approve'
import { createProposal } from '../proposals/create'

const addOpeningOptions = pick(electCouncilOptions, 'blockTime')

export const addOpeningCommand = async ({ group = GROUP }: { group?: GroupIdName } = {}) => {
type Args = yargs.InferredOptionTypes<typeof addOpeningOptions>
type Props = Partial<Args> & { group?: GroupIdName }

export const addOpeningCommand = async ({ group = GROUP, blockTime = MILLISECONDS_PER_BLOCK }: Props) => {
const title = `Test ${group} opening`

const openingMetadata = {
Expand All @@ -22,23 +35,31 @@ export const addOpeningCommand = async ({ group = GROUP }: { group?: GroupIdName
}

return await withApi(async (api) => {
const tx = api.tx[group].addOpening(
metadataToBytes(OpeningMetadata, openingMetadata),
'Leader',
{ stakeAmount: api.consts[group].minimumApplicationStake, leavingUnstakingPeriod: 360_000 },
'1337'
)
const { minimumApplicationStake, minUnstakingPeriodLimit } = api.consts[group]
const proposalDetails = createType('PalletProposalsCodexProposalDetails', {
CreateWorkingGroupLeadOpening: {
description: metadataToBytes(OpeningMetadata, openingMetadata),
stakePolicy: { stakeAmount: minimumApplicationStake, leavingUnstakingPeriod: minUnstakingPeriodLimit },
rewardPerBlock: '1337',
group: 'membership',
},
})

// 1. Elect a council
await electCouncilCommand(api, { blockTime, replaceCurrent: false })

const events = await signAndSend(api.tx.sudo.sudo(tx), getSudoAccount())
// 2. Create a working lead opening proposal
const [proposalId] = await createProposal(api, proposalDetails)
if (!isDefined(proposalId)) throw 'Failed to create the proposal'

return String(getDataFromEvent(events, group, 'OpeningAdded'))
// 3. Approve the proposal
await approveProposal(api, proposalId)
})
}

export const createOpeningModule = {
command: 'opening:create',
describe: 'Create new opening',
handler: async () => {
await addOpeningCommand()
},
handler: ({ blockTime }: Args) => addOpeningCommand({ blockTime }),
builder: (argv: yargs.Argv<unknown>) => argv.options(addOpeningOptions),
}
21 changes: 21 additions & 0 deletions packages/ui/dev/node-mocks/commands/proposals/approve.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { ApiPromise } from '@polkadot/api'

import memberData from '@/mocks/data/raw/members.json'

import { signAndSend } from '../../lib/api'

export const approveProposal = async (api: ApiPromise, proposalId: number) => {
const councilors = await api.query.council.councilMembers()
await Promise.all(
councilors.map((councilor) => {
const memberId = councilor.membershipId.toString()
const member = memberData.find(({ id }) => id === memberId)
if (!member) throw `Couldn't find the councilor ${memberId} controller account`

const tx = api.tx.proposalsEngine.vote(councilor.membershipId, proposalId, 'Approve', 'LGTM')

// Assume councilors stacking account are their controller accounts too
return signAndSend(tx, member.controllerAccount)
})
)
}
Loading

2 comments on commit d46fce3

@vercel
Copy link

@vercel vercel bot commented on d46fce3 Jun 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

pioneer-2 – ./

pioneer-2-git-dev-joystream.vercel.app
pioneer-2.vercel.app
pioneer-2-joystream.vercel.app

@vercel
Copy link

@vercel vercel bot commented on d46fce3 Jun 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.