Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add staking functions and tests #26

Merged
merged 21 commits into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
c3dbc5f
improve balance and transfer types
marc-aurele-besner Jun 12, 2024
96f0d2e
add staking functions
marc-aurele-besner Jun 12, 2024
700abb6
reduce farmer size and fix operator command following latest subspace…
marc-aurele-besner Jun 12, 2024
6b2d80f
staking test (registerOperator, nominateOperator and deregisterOperator)
marc-aurele-besner Jun 12, 2024
928facb
add some testing helpers functions to reduce code duplication and imp…
marc-aurele-besner Jun 12, 2024
e7b10a0
implement helpers functions
marc-aurele-besner Jun 12, 2024
2d72c88
put all basic test setup in a setup helper
marc-aurele-besner Jun 12, 2024
1f3698a
implement setup helper
marc-aurele-besner Jun 12, 2024
3d99539
add sudo and verifyOperatorRegistrationFinal helpers
marc-aurele-besner Jun 12, 2024
f6e8967
adding events validations
marc-aurele-besner Jun 12, 2024
8fa0241
adding sudo helper
marc-aurele-besner Jun 12, 2024
f85ca8d
fix unlockNominator
marc-aurele-besner Jun 12, 2024
ffe28b1
add/improve staking tests
marc-aurele-besner Jun 12, 2024
614b0f6
remove unnecessary log in wallet
marc-aurele-besner Jun 13, 2024
e997bff
improve tests helper
marc-aurele-besner Jun 13, 2024
f001274
improve consensus tests
marc-aurele-besner Jun 13, 2024
46f50a9
run all tests serially in the current process, rather than creating a…
marc-aurele-besner Jun 13, 2024
322acab
remove unnecessary transfer
marc-aurele-besner Jun 13, 2024
2a72986
only run consensus test on local node
marc-aurele-besner Jun 13, 2024
114ce36
remove commented tests
marc-aurele-besner Jun 19, 2024
cb28707
add consensus localhost tests script
marc-aurele-besner Jun 19, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 4 additions & 30 deletions packages/auto-consensus/__test__/balances.test.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,11 @@
import type { NetworkInput } from '@autonomys/auto-utils'
import {
ActivateWalletInput,
activate,
activateWallet,
disconnect,
networks,
} from '@autonomys/auto-utils'
import { ActivateWalletInput, activateWallet } from '@autonomys/auto-utils'
import { address } from '../src/address'
import { balance, totalIssuance } from '../src/balances'
import { setup } from './helpers'

describe('Verify balances functions', () => {
const isLocalhost = process.env.LOCALHOST === 'true'

// Define the test network and its details
const TEST_NETWORK: NetworkInput = !isLocalhost
? { networkId: networks[0].id }
: { networkId: 'autonomys-localhost' }
const TEST_INVALID_NETWORK = { networkId: 'invalid-network' }

const TEST_MNEMONIC = 'test test test test test test test test test test test junk'
const TEST_ADDRESS = '5GmS1wtCfR4tK5SSgnZbVT4kYw5W8NmxmijcsxCQE6oLW6A8'
const ALICE_URI = '//Alice'
const ALICE_ADDRESS = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'
const BOB_URI = '//Bob'
const BOB_ADDRESS = '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty'

beforeAll(async () => {
await activate(TEST_NETWORK)
})

afterAll(async () => {
await disconnect()
})
const { isLocalhost, TEST_NETWORK, TEST_MNEMONIC, TEST_ADDRESS, ALICE_URI, ALICE_ADDRESS } =
setup()

describe('Test totalIssuance()', () => {
test('Check totalIssuance return a number greater than zero', async () => {
Expand Down
117 changes: 117 additions & 0 deletions packages/auto-consensus/__test__/helpers/events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
export type ActionEvents = string | string[]
export type Events = ActionEvents | ActionEvents[]

// Enum for Event Types
const enum Type {
system = 'system',
balances = 'balances',
transactionPayment = 'transactionPayment',
domains = 'domains',
sudo = 'sudo',
}

// Utility Function for Event Names
const eventName = (type: Type, event: string) => `${type}.${event}`

// System Events
const system: {
[key: string]: string
} = {
failure: eventName(Type.system, 'ExtrinsicFailed'),
newAccount: eventName(Type.system, 'NewAccount'),
success: eventName(Type.system, 'ExtrinsicSuccess'),
}

// Balances Events
const balances: {
[key: string]: string
} = {
deposit: eventName(Type.balances, 'Deposit'),
endowed: eventName(Type.balances, 'Endowed'),
transfer: eventName(Type.balances, 'Transfer'),
withdraw: eventName(Type.balances, 'Withdraw'),
}

// Transaction Payment Events
const transactionPayment: {
[key: string]: string
} = {
feePaid: eventName(Type.transactionPayment, 'TransactionFeePaid'),
}

// Domains Events
const domains: {
[key: string]: string
} = {
forceDomainEpochTransition: eventName(Type.domains, 'ForceDomainEpochTransition'),
fundsUnlocked: eventName(Type.domains, 'FundsUnlocked'),
operatorDeregistered: eventName(Type.domains, 'OperatorDeregistered'),
operatorNominated: eventName(Type.domains, 'OperatorNominated'),
operatorRegistered: eventName(Type.domains, 'OperatorRegistered'),
operatorUnlocked: eventName(Type.domains, 'OperatorUnlocked'),
storageFeeDeposited: eventName(Type.domains, 'StorageFeeDeposited'),
withdrawStake: eventName(Type.domains, 'WithdrewStake'),
}

// Sudo Events
const sudo: {
[key: string]: string
} = {
sudid: eventName(Type.sudo, 'Sudid'),
}

// Define specific extrinsic keys for events
type EventKeys =
| 'transfer'
| 'operatorRegistered'
| 'operatorNominated'
| 'operatorDeRegistered'
| 'withdrawStake'
| 'unlockFunds'
| 'forceDomainEpochTransition'

// Events Mappings
export const events: { [key in EventKeys]: ActionEvents } = {
transfer: [balances.withdraw, balances.transfer, transactionPayment.feePaid, system.success],
operatorRegistered: [
balances.withdraw,
domains.storageFeeDeposited,
domains.operatorRegistered,
transactionPayment.feePaid,
system.success,
],
operatorNominated: [
balances.withdraw,
balances.transfer,
domains.storageFeeDeposited,
domains.operatorNominated,
transactionPayment.feePaid,
system.success,
],
operatorDeRegistered: [
balances.withdraw,
domains.operatorDeregistered,
transactionPayment.feePaid,
system.success,
],
withdrawStake: [
balances.withdraw,
domains.withdrawStake,
transactionPayment.feePaid,
system.success,
],
unlockFunds: [
balances.withdraw,
domains.fundsUnlocked,
transactionPayment.feePaid,
system.success,
],
forceDomainEpochTransition: [
balances.withdraw,
domains.forceDomainEpochTransition,
sudo.sudid,
balances.deposit,
transactionPayment.feePaid,
system.success,
],
}
5 changes: 5 additions & 0 deletions packages/auto-consensus/__test__/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from './events'
export * from './setup'
export * from './staking'
export * from './sudo'
export * from './tx'
39 changes: 39 additions & 0 deletions packages/auto-consensus/__test__/helpers/setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import type { NetworkInput } from '@autonomys/auto-utils'
import { activate, disconnect, networks } from '@autonomys/auto-utils'

export const setup = () => {
const isLocalhost = process.env.LOCALHOST === 'true'

// Define the test network and its details
const TEST_NETWORK: NetworkInput = !isLocalhost
? { networkId: networks[0].id }
: { networkId: 'autonomys-localhost' }
const TEST_INVALID_NETWORK = { networkId: 'invalid-network' }

const TEST_MNEMONIC = 'test test test test test test test test test test test junk'
const TEST_ADDRESS = '5GmS1wtCfR4tK5SSgnZbVT4kYw5W8NmxmijcsxCQE6oLW6A8'
const ALICE_URI = '//Alice'
const ALICE_ADDRESS = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'
const BOB_URI = '//Bob'
const BOB_ADDRESS = '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty'

beforeAll(async () => {
await activate(TEST_NETWORK)
})

afterAll(async () => {
await disconnect()
})

return {
isLocalhost,
TEST_NETWORK,
TEST_INVALID_NETWORK,
TEST_MNEMONIC,
TEST_ADDRESS,
ALICE_URI,
ALICE_ADDRESS,
BOB_URI,
BOB_ADDRESS,
}
}
79 changes: 79 additions & 0 deletions packages/auto-consensus/__test__/helpers/staking.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import type { ApiPromise } from '@polkadot/api'
import { u8aToHex } from '@polkadot/util'
import { operator, operators, RegisterOperatorInput } from '../../src/staking'

const STORAGE_FEE_DEPOSIT_PERCENTAGE = 20 // 20%

export const parseBigInt = (operatorId: string | number | bigint): bigint =>
typeof operatorId === 'bigint' ? operatorId : BigInt(operatorId)

export const calculateStake = (input: RegisterOperatorInput) => {
const { amountToStake, nominationTax } = input

return (parseBigInt(amountToStake) * BigInt(100 - STORAGE_FEE_DEPOSIT_PERCENTAGE)) / BigInt(100)
// To-Do: Add the nomination tax
}

export const calculateStorageFee = (input: RegisterOperatorInput) => {
const { amountToStake } = input

return (parseBigInt(amountToStake) * BigInt(STORAGE_FEE_DEPOSIT_PERCENTAGE)) / BigInt(100)
}

export const verifyOperatorRegistration = async (input: RegisterOperatorInput) => {
const { api, Operator, domainId, minimumNominatorStake, nominationTax } = input

const operatorsList = await operators(api)
const findOperator = operatorsList.find(
(o) => o.operatorDetails.signingKey === u8aToHex(Operator.publicKey),
)
expect(findOperator).toBeDefined()
if (findOperator) {
expect(findOperator.operatorDetails.currentDomainId).toEqual(BigInt(domainId))
expect(findOperator.operatorDetails.currentTotalStake).toEqual(BigInt(0))
expect(findOperator.operatorDetails.minimumNominatorStake).toEqual(
BigInt(minimumNominatorStake),
)
expect(findOperator.operatorDetails.nominationTax).toEqual(Number(nominationTax))
expect(findOperator.operatorDetails.status).toEqual({ registered: null })
const thisOperator = await operator(api, findOperator.operatorId)
expect(thisOperator.currentDomainId).toEqual(BigInt(domainId))
expect(thisOperator.currentTotalStake).toEqual(BigInt(0))
expect(thisOperator.minimumNominatorStake).toEqual(BigInt(minimumNominatorStake))
expect(thisOperator.nominationTax).toEqual(Number(nominationTax))
expect(thisOperator.status).toEqual({ registered: null })
}

return findOperator
}

export const verifyOperatorRegistrationFinal = async (input: RegisterOperatorInput) => {
const { api, Operator, domainId, amountToStake, minimumNominatorStake, nominationTax } = input

const operatorsList = await operators(api)
const findOperator = operatorsList.find(
(o) => o.operatorDetails.signingKey === u8aToHex(Operator.publicKey),
)
expect(findOperator).toBeDefined()
if (findOperator) {
expect(findOperator.operatorDetails.currentDomainId).toEqual(BigInt(domainId))
expect(findOperator.operatorDetails.currentTotalStake).toEqual(
(BigInt(amountToStake) / BigInt(100)) * BigInt(80),
)
expect(findOperator.operatorDetails.minimumNominatorStake).toEqual(
BigInt(minimumNominatorStake),
)
expect(findOperator.operatorDetails.nominationTax).toEqual(Number(nominationTax))
expect(findOperator.operatorDetails.totalStorageFeeDeposit).toEqual(
(BigInt(amountToStake) / BigInt(100)) * BigInt(20),
)
const thisOperator = await operator(api, findOperator.operatorId)
expect(thisOperator.currentDomainId).toEqual(BigInt(domainId))
expect(thisOperator.currentTotalStake).toEqual(calculateStake(input))
expect(thisOperator.minimumNominatorStake).toEqual(BigInt(minimumNominatorStake))
expect(thisOperator.nominationTax).toEqual(Number(nominationTax))
expect(thisOperator.totalStorageFeeDeposit).toEqual(calculateStorageFee(input))
}

return findOperator
}
13 changes: 13 additions & 0 deletions packages/auto-consensus/__test__/helpers/sudo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ApiPromise } from '@polkadot/api'
import type { AddressOrPair, SubmittableExtrinsic } from '@polkadot/api/types'
import type { ISubmittableResult } from '@polkadot/types/types'
import type { Events } from './events'
import { signAndSendTx } from './tx'

export const sudo = async (
api: ApiPromise,
sender: AddressOrPair,
tx: SubmittableExtrinsic<'promise', ISubmittableResult>,
eventsExpected: Events = [],
log: boolean = true,
) => await signAndSendTx(sender, api.tx.sudo.sudo(tx), eventsExpected, log)
68 changes: 68 additions & 0 deletions packages/auto-consensus/__test__/helpers/tx.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import type { AddressOrPair, SubmittableExtrinsic } from '@polkadot/api/types'
import type { EventRecord } from '@polkadot/types/interfaces'
import type { ISubmittableResult } from '@polkadot/types/types'
import type { Events } from './events'

const validateEvents = (
events: EventRecord[],
eventsExpected: Events,
tx: string,
block: string,
log: boolean = true,
) => {
const _eventsExpected =
typeof eventsExpected === 'string'
? [eventsExpected]
: eventsExpected.map((e: string | string[]) => (typeof e === 'string' ? [e] : e)).flat()

events.forEach(({ event: { data, method, section } }) => {
// if (log) console.log(`${section}.${method}`, data.toString()) // Uncomment this line to log every events with their data
const index = _eventsExpected.indexOf(`${section}.${method}`)
if (index > -1) _eventsExpected.splice(index, 1)
else if (log)
console.log('Event not expected', `${section}.${method}`, 'tx', tx, 'block', block)
})
if (_eventsExpected.length > 0)
console.log('Events not found', _eventsExpected, 'tx', tx, 'block', block)

expect(_eventsExpected).toHaveLength(0)

return _eventsExpected
}

export const signAndSendTx = async (
sender: AddressOrPair,
tx: SubmittableExtrinsic<'promise', ISubmittableResult>,
eventsExpected: Events = [],
log: boolean = true,
) => {
let txHashHex: string | undefined = undefined
let blockHash: string | undefined = undefined
await new Promise<void>((resolve, reject) => {
tx.signAndSend(sender, ({ events, status, txHash }) => {
if (status.isInBlock) {
txHashHex = txHash.toHex()
blockHash = status.asInBlock.toHex()
if (log) console.log('Successful tx', txHashHex, 'in block', blockHash)

if (eventsExpected.length > 0) {
eventsExpected = validateEvents(events, eventsExpected, txHashHex, blockHash, log)
if (eventsExpected.length === 0) resolve()
else reject(new Error('Events not found'))
} else resolve()
} else if (
status.isRetracted ||
status.isFinalityTimeout ||
status.isDropped ||
status.isInvalid
) {
if (log) console.error('Transaction failed')
reject(new Error('Transaction failed'))
}
})
})
expect(txHashHex).toBeDefined()
expect(blockHash).toBeDefined()

return { txHash: txHashHex, blockHash }
}
10 changes: 2 additions & 8 deletions packages/auto-consensus/__test__/info.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
import { activate, disconnect } from '@autonomys/auto-utils'
import { networkTimestamp } from '../src/info'
import { setup } from './helpers'

describe('Verify info functions', () => {
beforeAll(async () => {
await activate()
})

afterAll(async () => {
await disconnect()
})
setup()

test('Check network timestamp return a number greater than zero', async () => {
// totalIssuance is an async function that returns a hex number as a string
Expand Down
Loading
Loading