From c3dbc5f6c545cb2e399e37e46410357fc13a5cbb Mon Sep 17 00:00:00 2001 From: Marc-Aurele Besner <82244926+marc-aurele-besner@users.noreply.github.com> Date: Wed, 12 Jun 2024 11:13:50 -0400 Subject: [PATCH 01/21] improve balance and transfer types --- packages/auto-consensus/__test__/transfer.test.ts | 8 ++++---- packages/auto-consensus/src/balances.ts | 8 ++++---- packages/auto-consensus/src/transfer.ts | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/auto-consensus/__test__/transfer.test.ts b/packages/auto-consensus/__test__/transfer.test.ts index 0b5454e9..c778ab83 100644 --- a/packages/auto-consensus/__test__/transfer.test.ts +++ b/packages/auto-consensus/__test__/transfer.test.ts @@ -45,7 +45,7 @@ describe('Verify transfer functions', () => { expect(accounts[0].address).toEqual(ALICE_ADDRESS) const sender = accounts[0] - let txHash: string | undefined + let blockHash: string | undefined const _balanceSenderStart = await balance(api, address(sender.address)) const _balanceReceiverStart = await balance(api, address(BOB_ADDRESS)) @@ -56,8 +56,8 @@ describe('Verify transfer functions', () => { await new Promise((resolve, reject) => { tx.signAndSend(sender, ({ status }) => { if (status.isInBlock) { - txHash = status.asInBlock.toHex() - console.log('Successful transfer of 1 with hash ' + txHash) + blockHash = status.asInBlock.toHex() + console.log('Successful transfer of 1 with block hash ' + blockHash) resolve() } else if ( status.isRetracted || @@ -72,7 +72,7 @@ describe('Verify transfer functions', () => { }) }) - expect(txHash).toBeDefined() + expect(blockHash).toBeDefined() const _balanceSenderEnd = await balance(api, address(sender.address)) const _balanceReceiverEnd = await balance(api, address(BOB_ADDRESS)) diff --git a/packages/auto-consensus/src/balances.ts b/packages/auto-consensus/src/balances.ts index bdf50821..b1426117 100644 --- a/packages/auto-consensus/src/balances.ts +++ b/packages/auto-consensus/src/balances.ts @@ -1,5 +1,5 @@ import { activate } from '@autonomys/auto-utils' -import { ApiPromise } from '@polkadot/api' +import type { ApiPromise } from '@polkadot/api' import type { BN } from '@polkadot/util' type RawBalanceData = { @@ -9,9 +9,9 @@ type RawBalanceData = { flags: BN } type BalanceData = { - free: any - reserved: any - frozen: any + free: bigint + reserved: bigint + frozen: bigint } export const totalIssuance = async (networkId?: string) => { diff --git a/packages/auto-consensus/src/transfer.ts b/packages/auto-consensus/src/transfer.ts index f322b96f..b884f108 100644 --- a/packages/auto-consensus/src/transfer.ts +++ b/packages/auto-consensus/src/transfer.ts @@ -1,4 +1,4 @@ -import { ApiPromise } from '@polkadot/api' +import type { ApiPromise } from '@polkadot/api' export const transfer = async ( api: ApiPromise, From 96f0d2e78574867d78f7c5f90a37088c86625eeb Mon Sep 17 00:00:00 2001 From: Marc-Aurele Besner <82244926+marc-aurele-besner@users.noreply.github.com> Date: Wed, 12 Jun 2024 11:15:38 -0400 Subject: [PATCH 02/21] add staking functions --- packages/auto-consensus/package.json | 1 + packages/auto-consensus/src/staking.ts | 203 +++++++++++++++++++++++++ yarn.lock | 3 +- 3 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 packages/auto-consensus/src/staking.ts diff --git a/packages/auto-consensus/package.json b/packages/auto-consensus/package.json index b9ee477f..2908ae66 100644 --- a/packages/auto-consensus/package.json +++ b/packages/auto-consensus/package.json @@ -24,6 +24,7 @@ "@autonomys/auto-utils": "workspace:*" }, "devDependencies": { + "@polkadot/types-codec": "^11.2.1", "@types/jest": "^29.5.12", "eslint": "^8.57.0", "jest": "^29.7.0", diff --git a/packages/auto-consensus/src/staking.ts b/packages/auto-consensus/src/staking.ts new file mode 100644 index 00000000..0e398ecd --- /dev/null +++ b/packages/auto-consensus/src/staking.ts @@ -0,0 +1,203 @@ +import type { ApiPromise } from '@polkadot/api' +import type { KeyringPair } from '@polkadot/keyring/types' +import type { StorageKey } from '@polkadot/types' +import { createType } from '@polkadot/types' +import type { AnyTuple, Codec } from '@polkadot/types-codec/types' +import { u8aToHex } from '@polkadot/util' + +type RawOperatorId = string[] +type RawOperatorDetails = { + signingKey: string + currentDomainId: number + nextDomainId: number + minimumNominatorStake: string + nominationTax: number + currentTotalStake: number + currentEpochRewards: number + currentTotalShares: number + status: object[] + depositsInEpoch: string + withdrawalsInEpoch: number + totalStorageFeeDeposit: string +} +export type OperatorDetails = { + signingKey: string + currentDomainId: bigint + nextDomainId: bigint + minimumNominatorStake: bigint + nominationTax: number + currentTotalStake: bigint + currentEpochRewards: bigint + currentTotalShares: bigint + status: object[] + depositsInEpoch: bigint + withdrawalsInEpoch: bigint + totalStorageFeeDeposit: bigint +} +export type Operator = { + operatorId: bigint + operatorDetails: OperatorDetails +} + +type StringNumberOrBigInt = string | number | bigint + +export type RegisterOperatorInput = { + api: ApiPromise + senderAddress: string + Operator: KeyringPair + domainId: StringNumberOrBigInt + amountToStake: StringNumberOrBigInt + minimumNominatorStake: StringNumberOrBigInt + nominationTax: StringNumberOrBigInt +} + +export type StakingInput = { + api: ApiPromise + operatorId: StringNumberOrBigInt +} + +export interface WithdrawStakeInput extends StakingInput { + shares: StringNumberOrBigInt +} + +export interface NominateOperatorInput extends StakingInput { + amountToStake: StringNumberOrBigInt +} + +const parseOperatorDetails = (operatorDetails: Codec): OperatorDetails => { + const rawOD = operatorDetails.toJSON() as RawOperatorDetails + return { + signingKey: rawOD.signingKey, + currentDomainId: BigInt(rawOD.currentDomainId), + nextDomainId: BigInt(rawOD.nextDomainId), + minimumNominatorStake: BigInt(rawOD.minimumNominatorStake), + nominationTax: rawOD.nominationTax, + currentTotalStake: BigInt(rawOD.currentTotalStake), + currentEpochRewards: BigInt(rawOD.currentEpochRewards), + currentTotalShares: BigInt(rawOD.currentTotalShares), + status: rawOD.status, + depositsInEpoch: BigInt(rawOD.depositsInEpoch), + withdrawalsInEpoch: BigInt(rawOD.withdrawalsInEpoch), + totalStorageFeeDeposit: BigInt(rawOD.totalStorageFeeDeposit), + } +} + +const parseOperator = (operator: [StorageKey, Codec]): Operator => { + return { + operatorId: BigInt((operator[0].toHuman() as RawOperatorId)[0]), + operatorDetails: parseOperatorDetails(operator[1]), + } +} + +const parseString = (operatorId: StringNumberOrBigInt): string => + typeof operatorId === 'string' ? operatorId : operatorId.toString() + +export const operators = async (api: ApiPromise) => { + try { + const _operators = await api.query.domains.operators.entries() + return _operators.map((o) => parseOperator(o)) + } catch (error) { + console.error('error', error) + throw new Error('Error querying operators list.' + error) + } +} + +export const operator = async (api: ApiPromise, operatorId: StringNumberOrBigInt) => { + try { + const _operator = await api.query.domains.operators(parseString(operatorId)) + return parseOperatorDetails(_operator) + } catch (error) { + console.error('error', error) + throw new Error(`Error querying operatorId: ${operatorId} with error: ${error}`) + } +} + +export const registerOperator = async (input: RegisterOperatorInput) => { + try { + const { + api, + senderAddress, + Operator, + domainId, + amountToStake, + minimumNominatorStake, + nominationTax, + } = input + + const message = createType(api.registry, 'AccountId', senderAddress).toU8a() + const signingKey = u8aToHex(Operator.publicKey) + const signature = Operator.sign(message) + + return await api.tx.domains.registerOperator( + parseString(domainId), + parseString(amountToStake), + { + signingKey, + minimumNominatorStake: parseString(minimumNominatorStake), + nominationTax: parseString(nominationTax), + }, + signature, + ) + } catch (error) { + console.error('error', error) + throw new Error('Error creating register operator tx.' + error) + } +} + +export const nominateOperator = async (input: NominateOperatorInput) => { + try { + const { api, operatorId, amountToStake } = input + + return await api.tx.domains.nominateOperator( + parseString(operatorId), + parseString(amountToStake), + ) + } catch (error) { + console.error('error', error) + throw new Error('Error creating nominate operator tx.' + error) + } +} + +export const withdrawStake = async (input: WithdrawStakeInput) => { + try { + const { api, operatorId, shares } = input + + return await api.tx.domains.withdrawStake(parseString(operatorId), parseString(shares)) + } catch (error) { + console.error('error', error) + throw new Error('Error creating withdraw stake tx.' + error) + } +} + +export const deregisterOperator = async (input: StakingInput) => { + try { + const { api, operatorId } = input + + return await api.tx.domains.deregisterOperator(parseString(operatorId)) + } catch (error) { + console.error('error', error) + throw new Error('Error creating de-register operator tx.' + error) + } +} + +export const unlockFunds = async (input: StakingInput) => { + try { + const { api, operatorId } = input + + return await api.tx.domains.unlockFunds(parseString(operatorId)) + } catch (error) { + console.error('error', error) + throw new Error('Error creating unlock funds tx.' + error) + } +} + +export const unlockOperator = async (input: StakingInput) => { + try { + const { api, operatorId } = input + + return await api.tx.domains.unlockOperator(parseString(operatorId)) + } catch (error) { + console.error('error', error) + throw new Error('Error creating unlock operator tx.' + error) + } +} diff --git a/yarn.lock b/yarn.lock index d0d265f0..cf6008ef 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20,6 +20,7 @@ __metadata: resolution: "@autonomys/auto-consensus@workspace:packages/auto-consensus" dependencies: "@autonomys/auto-utils": "workspace:*" + "@polkadot/types-codec": "npm:^11.2.1" "@types/jest": "npm:^29.5.12" eslint: "npm:^8.57.0" jest: "npm:^29.7.0" @@ -1213,7 +1214,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/types-codec@npm:11.2.1": +"@polkadot/types-codec@npm:11.2.1, @polkadot/types-codec@npm:^11.2.1": version: 11.2.1 resolution: "@polkadot/types-codec@npm:11.2.1" dependencies: From 700abb670f55763111aafb2c8c5a10fbfdcbdd14 Mon Sep 17 00:00:00 2001 From: Marc-Aurele Besner <82244926+marc-aurele-besner@users.noreply.github.com> Date: Wed, 12 Jun 2024 11:16:30 -0400 Subject: [PATCH 03/21] reduce farmer size and fix operator command following latest subspace change --- scripts/run-farmer.sh | 2 +- scripts/run-operator.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/run-farmer.sh b/scripts/run-farmer.sh index 0798e27a..83c2933a 100644 --- a/scripts/run-farmer.sh +++ b/scripts/run-farmer.sh @@ -2,6 +2,6 @@ # Run farmer echo "Running farmer..." -./executables/farmer farm path=executables/farmer-temp,size=2GB --reward-address 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY --node-rpc-url ws://127.0.0.1:9944 +./executables/farmer farm path=executables/farmer-temp,size=1GiB --reward-address 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY --node-rpc-url ws://127.0.0.1:9944 echo "Both node and farmer are running in parallel." \ No newline at end of file diff --git a/scripts/run-operator.sh b/scripts/run-operator.sh index d90d3196..37780314 100644 --- a/scripts/run-operator.sh +++ b/scripts/run-operator.sh @@ -17,4 +17,4 @@ echo -e "${YELLOW}You can change these variables at the top of the script.${NC}" # Run an operator echo "Running an operator..." -./executables/node run --dev --farmer --timekeeper --base-path "$BASE_PATH" --name "localhost-operator" --rpc-rate-limit 1000 --rpc-max-connections 10000 --state-pruning archive-canonical --blocks-pruning 512 --rpc-cors all --force-synced --force-authoring -- --domain-id 0 --operator-id 2 --state-pruning archive-canonical --blocks-pruning 512 --rpc-cors all \ No newline at end of file +./executables/node run --dev --farmer --timekeeper --base-path "$BASE_PATH" --name "localhost-operator" --rpc-rate-limit 1000 --rpc-max-connections 10000 --state-pruning archive-canonical --blocks-pruning archive-canonical --rpc-cors all --force-synced --force-authoring -- --domain-id 0 --operator-id 1 --state-pruning archive-canonical --blocks-pruning 512 --rpc-cors all \ No newline at end of file From 6b2d80f9e4d53803f9873845a61040eb11554543 Mon Sep 17 00:00:00 2001 From: Marc-Aurele Besner <82244926+marc-aurele-besner@users.noreply.github.com> Date: Wed, 12 Jun 2024 11:17:13 -0400 Subject: [PATCH 04/21] staking test (registerOperator, nominateOperator and deregisterOperator) --- .../auto-consensus/__test__/staking.test.ts | 494 ++++++++++++++++++ 1 file changed, 494 insertions(+) create mode 100644 packages/auto-consensus/__test__/staking.test.ts diff --git a/packages/auto-consensus/__test__/staking.test.ts b/packages/auto-consensus/__test__/staking.test.ts new file mode 100644 index 00000000..1e0dc53f --- /dev/null +++ b/packages/auto-consensus/__test__/staking.test.ts @@ -0,0 +1,494 @@ +import type { NetworkInput } from '@autonomys/auto-utils' +import { + ActivateWalletInput, + activate, + activateWallet, + disconnect, + networks, +} from '@autonomys/auto-utils' +import { u8aToHex } from '@polkadot/util' +import { mnemonicGenerate } from '@polkadot/util-crypto' +import { address } from '../src/address' +import { balance } from '../src/balances' +import { + deregisterOperator, + nominateOperator, + operator, + operators, + registerOperator, +} from '../src/staking' +import { transfer } from '../src/transfer' + +describe('Verify staking 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() + }) + + if (isLocalhost) { + describe('Test registerOperator()', () => { + test('Check Alice can register random wallet as an operator', async () => { + const mnemonicOperator = mnemonicGenerate() + const { api, accounts } = await activateWallet({ + ...TEST_NETWORK, + uri: ALICE_URI, + } as ActivateWalletInput) + const { accounts: operatorAccounts } = await activateWallet({ + ...TEST_NETWORK, + uri: mnemonicOperator, + } as ActivateWalletInput) + expect(accounts.length).toBeGreaterThan(0) + expect(accounts[0].address).toEqual(ALICE_ADDRESS) + + const sender = accounts[0] + let blockHash: string | undefined + + const _balanceSenderStart = await balance(api, address(sender.address)) + expect(_balanceSenderStart.free).toBeGreaterThan(BigInt(0)) + + const domainId = '0' + const amountToStake = '100000000000000000000' + const minimumNominatorStake = '1000000000000000000' + const nominationTax = '5' + const tx = await registerOperator({ + api, + senderAddress: ALICE_ADDRESS, + Operator: operatorAccounts[0], + domainId, + amountToStake, + minimumNominatorStake, + nominationTax, + }) + + await new Promise((resolve, reject) => { + tx.signAndSend(sender, ({ status }) => { + if (status.isInBlock) { + blockHash = status.asInBlock.toHex() + console.log('Successful of Bob as operator with block hash ' + blockHash) + resolve() + } else if ( + status.isRetracted || + status.isFinalityTimeout || + status.isDropped || + status.isInvalid + ) { + reject(new Error('Transaction failed')) + } else { + console.log('Status of registration: ' + status.type) + } + }) + }) + + expect(blockHash).toBeDefined() + + const _balanceSenderEnd = await balance(api, address(sender.address)) + expect(_balanceSenderEnd.free).toBeLessThan( + _balanceSenderStart.free - BigInt(amountToStake), + ) + + const operatorsList = await operators(api) + const findOperator = operatorsList.find( + (o) => o.operatorDetails.signingKey === u8aToHex(operatorAccounts[0].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 }) + + const forceStakingEpochTransition = + await api.tx.domains.forceStakingEpochTransition(domainId) + const sudoTx = await api.tx.sudo.sudo(forceStakingEpochTransition) + + await new Promise((resolve, reject) => { + sudoTx.signAndSend(sender, ({ status }) => { + if (status.isInBlock) { + blockHash = status.asInBlock.toHex() + console.log('Successful forceStakingEpochTransition with block hash ' + blockHash) + resolve() + } else if ( + status.isRetracted || + status.isFinalityTimeout || + status.isDropped || + status.isInvalid + ) { + reject(new Error('Transaction failed')) + } else { + console.log('Status of forceStakingEpochTransition: ' + status.type) + } + }) + }) + + const operatorsListFinal = await operators(api) + const findOperatorFinal = operatorsListFinal.find( + (o) => o.operatorDetails.signingKey === u8aToHex(operatorAccounts[0].publicKey), + ) + expect(findOperatorFinal).toBeDefined() + if (findOperatorFinal) { + expect(findOperatorFinal.operatorDetails.currentDomainId).toEqual(BigInt(domainId)) + expect(findOperatorFinal.operatorDetails.currentTotalStake).toEqual( + (BigInt(amountToStake) / BigInt(100)) * BigInt(80), + ) + expect(findOperatorFinal.operatorDetails.minimumNominatorStake).toEqual( + BigInt(minimumNominatorStake), + ) + expect(findOperatorFinal.operatorDetails.nominationTax).toEqual(Number(nominationTax)) + expect(findOperatorFinal.operatorDetails.totalStorageFeeDeposit).toEqual( + (BigInt(amountToStake) / BigInt(100)) * BigInt(20), + ) + const thisOperatorFinal = await operator(api, findOperator.operatorId) + expect(thisOperatorFinal.currentDomainId).toEqual(BigInt(domainId)) + expect(thisOperatorFinal.currentTotalStake).toEqual( + (BigInt(amountToStake) / BigInt(100)) * BigInt(80), + ) + expect(thisOperatorFinal.minimumNominatorStake).toEqual(BigInt(minimumNominatorStake)) + expect(thisOperatorFinal.nominationTax).toEqual(Number(nominationTax)) + expect(thisOperatorFinal.totalStorageFeeDeposit).toEqual( + (BigInt(amountToStake) / BigInt(100)) * BigInt(20), + ) + } + } + }, 30000) + }) + + describe('Test nominateOperator()', () => { + test('Check Alice can nominate OperatorId 1', async () => { + const { api, accounts } = await activateWallet({ + ...TEST_NETWORK, + uri: ALICE_URI, + } as ActivateWalletInput) + expect(accounts.length).toBeGreaterThan(0) + expect(accounts[0].address).toEqual(ALICE_ADDRESS) + + const sender = accounts[0] + let blockHash: string | undefined + + const _balanceSenderStart = await balance(api, address(sender.address)) + const _balanceReceiverStart = await balance(api, address(TEST_ADDRESS)) + expect(_balanceSenderStart.free).toBeGreaterThan(BigInt(0)) + + const amountToStake = '50000000000000000000' + const tx = await nominateOperator({ + api, + operatorId: '1', + amountToStake, + }) + + await new Promise((resolve, reject) => { + tx.signAndSend(sender, ({ status }) => { + if (status.isInBlock) { + blockHash = status.asInBlock.toHex() + console.log('Successful of Alice nomination with block hash ' + blockHash) + resolve() + } else if ( + status.isRetracted || + status.isFinalityTimeout || + status.isDropped || + status.isInvalid + ) { + reject(new Error('Transaction failed')) + } else { + console.log('Status of nomination: ' + status.type) + } + }) + }) + }, 10000) + + test('Check Operator can addFunds after registration', async () => { + const mnemonicOperator = mnemonicGenerate() + const { api, accounts } = await activateWallet({ + ...TEST_NETWORK, + uri: ALICE_URI, + } as ActivateWalletInput) + const { accounts: operatorAccounts } = await activateWallet({ + ...TEST_NETWORK, + uri: mnemonicOperator, + } as ActivateWalletInput) + expect(accounts.length).toBeGreaterThan(0) + expect(accounts[0].address).toEqual(ALICE_ADDRESS) + + const sender = accounts[0] + let blockHash: string | undefined + + const _balanceSenderStart = await balance(api, address(sender.address)) + expect(_balanceSenderStart.free).toBeGreaterThan(BigInt(0)) + + // Transfer some funds to the operator + const amountToTransfer = '10000000000000000000' + const transferTx = await transfer(api, operatorAccounts[0].address, amountToTransfer) + + await new Promise((resolve, reject) => { + transferTx.signAndSend(sender, ({ status }) => { + if (status.isInBlock) { + blockHash = status.asInBlock.toHex() + console.log('Successful transfer with block hash ' + blockHash) + resolve() + } else if ( + status.isRetracted || + status.isFinalityTimeout || + status.isDropped || + status.isInvalid + ) { + reject(new Error('Transaction failed')) + } else { + console.log('Status of transfer: ' + status.type) + } + }) + }) + + expect(blockHash).toBeDefined() + + const domainId = '0' + const amountToStake = '100000000000000000000' + const minimumNominatorStake = '1000000000000000000' + const nominationTax = '5' + const tx = await registerOperator({ + api, + senderAddress: ALICE_ADDRESS, + Operator: operatorAccounts[0], + domainId, + amountToStake, + minimumNominatorStake, + nominationTax, + }) + + await new Promise((resolve, reject) => { + tx.signAndSend(sender, ({ status }) => { + if (status.isInBlock) { + blockHash = status.asInBlock.toHex() + console.log('Successful of Bob as operator with block hash ' + blockHash) + resolve() + } else if ( + status.isRetracted || + status.isFinalityTimeout || + status.isDropped || + status.isInvalid + ) { + reject(new Error('Transaction failed')) + } else { + console.log('Status of registration: ' + status.type) + } + }) + }) + + expect(blockHash).toBeDefined() + + const _balanceSenderEnd = await balance(api, address(sender.address)) + expect(_balanceSenderEnd.free).toBeLessThan( + _balanceSenderStart.free - BigInt(amountToStake), + ) + + const operatorsList = await operators(api) + const findOperator = operatorsList.find( + (o) => o.operatorDetails.signingKey === u8aToHex(operatorAccounts[0].publicKey), + ) + expect(findOperator).toBeDefined() + if (findOperator) { + expect(findOperator.operatorDetails.currentDomainId).toEqual(BigInt(domainId)) + // To-Do: Either remove this check or figure why it's not working (guessing there is a delay for the stake to be active) + // expect(operator.operatorDetails.currentTotalStake).toEqual(BigInt(amountToStake)) + expect(findOperator.operatorDetails.minimumNominatorStake).toEqual( + BigInt(minimumNominatorStake), + ) + expect(findOperator.operatorDetails.nominationTax).toEqual(Number(nominationTax)) + const thisOperator = await operator(api, findOperator.operatorId) + expect(thisOperator.currentDomainId).toEqual(BigInt(domainId)) + // To-Do: Either remove this check or figure why it's not working (guessing there is a delay for the stake to be active) + // expect(thisOperator.currentTotalStake).toEqual(BigInt(amountToStake)) + expect(thisOperator.minimumNominatorStake).toEqual(BigInt(minimumNominatorStake)) + expect(thisOperator.nominationTax).toEqual(Number(nominationTax)) + + const amountToStake2 = BigInt(amountToTransfer) / BigInt(2) + const tx3 = await nominateOperator({ + api, + operatorId: findOperator.operatorId, + amountToStake: amountToStake2.toString(), + }) + + await new Promise((resolve, reject) => { + tx3.signAndSend(operatorAccounts[0], ({ status }) => { + if (status.isInBlock) { + blockHash = status.asInBlock.toHex() + console.log('Successful of Operator nomination with block hash ' + blockHash) + resolve() + } else if ( + status.isRetracted || + status.isFinalityTimeout || + status.isDropped || + status.isInvalid + ) { + reject(new Error('Transaction failed')) + } else { + console.log('Status of nomination: ' + status.type) + } + }) + }) + } + }, 30000) + }) + + describe('Test deregisterOperator()', () => { + test('Check Operator can deregisterOperator after registration', async () => { + const mnemonicOperator = mnemonicGenerate() + const { api, accounts } = await activateWallet({ + ...TEST_NETWORK, + uri: ALICE_URI, + } as ActivateWalletInput) + const { accounts: operatorAccounts } = await activateWallet({ + ...TEST_NETWORK, + uri: mnemonicOperator, + } as ActivateWalletInput) + expect(accounts.length).toBeGreaterThan(0) + expect(accounts[0].address).toEqual(ALICE_ADDRESS) + + const sender = accounts[0] + let blockHash: string | undefined + + const _balanceSenderStart = await balance(api, address(sender.address)) + expect(_balanceSenderStart.free).toBeGreaterThan(BigInt(0)) + + // Transfer some funds to the operator + const amountToTransfer = '10000000000000000000' + const transferTx = await transfer(api, operatorAccounts[0].address, amountToTransfer) + + await new Promise((resolve, reject) => { + transferTx.signAndSend(sender, ({ status }) => { + if (status.isInBlock) { + blockHash = status.asInBlock.toHex() + console.log('Successful transfer with block hash ' + blockHash) + resolve() + } else if ( + status.isRetracted || + status.isFinalityTimeout || + status.isDropped || + status.isInvalid + ) { + reject(new Error('Transaction failed')) + } else { + console.log('Status of transfer: ' + status.type) + } + }) + }) + + expect(blockHash).toBeDefined() + + const domainId = '0' + const amountToStake = '100000000000000000000' + const minimumNominatorStake = '1000000000000000000' + const nominationTax = '5' + const tx = await registerOperator({ + api, + senderAddress: ALICE_ADDRESS, + Operator: operatorAccounts[0], + domainId, + amountToStake, + minimumNominatorStake, + nominationTax, + }) + + await new Promise((resolve, reject) => { + tx.signAndSend(sender, ({ status }) => { + if (status.isInBlock) { + blockHash = status.asInBlock.toHex() + console.log('Successful of Bob as operator with block hash ' + blockHash) + resolve() + } else if ( + status.isRetracted || + status.isFinalityTimeout || + status.isDropped || + status.isInvalid + ) { + reject(new Error('Transaction failed')) + } else { + console.log('Status of registration: ' + status.type) + } + }) + }) + + expect(blockHash).toBeDefined() + + const _balanceSenderEnd = await balance(api, address(sender.address)) + expect(_balanceSenderEnd.free).toBeLessThan( + _balanceSenderStart.free - BigInt(amountToStake), + ) + + const operatorsList = await operators(api) + const findOperator = operatorsList.find( + (o) => o.operatorDetails.signingKey === u8aToHex(operatorAccounts[0].publicKey), + ) + expect(findOperator).toBeDefined() + if (findOperator) { + expect(findOperator.operatorDetails.currentDomainId).toEqual(BigInt(domainId)) + // To-Do: Either remove this check or figure why it's not working (guessing there is a delay for the stake to be active) + // expect(operator.operatorDetails.currentTotalStake).toEqual(BigInt(amountToStake)) + expect(findOperator.operatorDetails.minimumNominatorStake).toEqual( + BigInt(minimumNominatorStake), + ) + expect(findOperator.operatorDetails.nominationTax).toEqual(Number(nominationTax)) + const thisOperator = await operator(api, findOperator.operatorId) + expect(thisOperator.currentDomainId).toEqual(BigInt(domainId)) + // To-Do: Either remove this check or figure why it's not working (guessing there is a delay for the stake to be active) + // expect(thisOperator.currentTotalStake).toEqual(BigInt(amountToStake)) + expect(thisOperator.minimumNominatorStake).toEqual(BigInt(minimumNominatorStake)) + expect(thisOperator.nominationTax).toEqual(Number(nominationTax)) + + const tx3 = await deregisterOperator({ + api, + operatorId: findOperator.operatorId, + }) + + await new Promise((resolve, reject) => { + tx3.signAndSend(operatorAccounts[0], ({ status }) => { + if (status.isInBlock) { + blockHash = status.asInBlock.toHex() + console.log('Successful of Operator de-registering with block hash ' + blockHash) + resolve() + } else if ( + status.isRetracted || + status.isFinalityTimeout || + status.isDropped || + status.isInvalid + ) { + reject(new Error('Transaction failed')) + } else { + console.log('Status of de-registering: ' + status.type) + } + }) + }) + } + }, 30000) + }) + } else { + test('Staking test only run on localhost', async () => { + expect(true).toBeTruthy() + }) + } +}) From 928facbd8ec4fb6ca0112f5d4238b4ca9ee7aba6 Mon Sep 17 00:00:00 2001 From: Marc-Aurele Besner <82244926+marc-aurele-besner@users.noreply.github.com> Date: Wed, 12 Jun 2024 11:43:04 -0400 Subject: [PATCH 05/21] add some testing helpers functions to reduce code duplication and improve readability --- .../auto-consensus/__test__/helpers/index.ts | 2 ++ .../__test__/helpers/staking.ts | 27 +++++++++++++++++ .../auto-consensus/__test__/helpers/tx.ts | 30 +++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 packages/auto-consensus/__test__/helpers/index.ts create mode 100644 packages/auto-consensus/__test__/helpers/staking.ts create mode 100644 packages/auto-consensus/__test__/helpers/tx.ts diff --git a/packages/auto-consensus/__test__/helpers/index.ts b/packages/auto-consensus/__test__/helpers/index.ts new file mode 100644 index 00000000..3cfbfbd4 --- /dev/null +++ b/packages/auto-consensus/__test__/helpers/index.ts @@ -0,0 +1,2 @@ +export * from './staking' +export * from './tx' diff --git a/packages/auto-consensus/__test__/helpers/staking.ts b/packages/auto-consensus/__test__/helpers/staking.ts new file mode 100644 index 00000000..f84a3118 --- /dev/null +++ b/packages/auto-consensus/__test__/helpers/staking.ts @@ -0,0 +1,27 @@ +import { u8aToHex } from '@polkadot/util' +import { operator, operators, RegisterOperatorInput } from '../../src/staking' + +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 }) + } +} diff --git a/packages/auto-consensus/__test__/helpers/tx.ts b/packages/auto-consensus/__test__/helpers/tx.ts new file mode 100644 index 00000000..61d60997 --- /dev/null +++ b/packages/auto-consensus/__test__/helpers/tx.ts @@ -0,0 +1,30 @@ +import type { AddressOrPair, SubmittableExtrinsic } from '@polkadot/api/types' +import type { ISubmittableResult } from '@polkadot/types/types' + +export const signAndSendTx = async ( + sender: AddressOrPair, + tx: SubmittableExtrinsic<'promise', ISubmittableResult>, + log: boolean = true, +) => { + let blockHash: string | undefined = undefined + await new Promise((resolve, reject) => { + tx.signAndSend(sender, ({ status }) => { + if (status.isInBlock) { + blockHash = status.asInBlock.toHex() + if (log) console.log('Successful transfer of 1 with block hash ' + blockHash) + resolve() + } else if ( + status.isRetracted || + status.isFinalityTimeout || + status.isDropped || + status.isInvalid + ) { + if (log) console.error('Transaction failed') + reject(new Error('Transaction failed')) + } else if (log) console.log('Status of transfer: ' + status.type) + }) + }) + expect(blockHash).toBeDefined() + + return blockHash +} From e7b10a0825099a48459b83a0c89c07d783d7cb33 Mon Sep 17 00:00:00 2001 From: Marc-Aurele Besner <82244926+marc-aurele-besner@users.noreply.github.com> Date: Wed, 12 Jun 2024 11:43:16 -0400 Subject: [PATCH 06/21] implement helpers functions --- .../auto-consensus/__test__/staking.test.ts | 256 +++--------------- .../auto-consensus/__test__/transfer.test.ts | 24 +- 2 files changed, 47 insertions(+), 233 deletions(-) diff --git a/packages/auto-consensus/__test__/staking.test.ts b/packages/auto-consensus/__test__/staking.test.ts index 1e0dc53f..fee5c4a2 100644 --- a/packages/auto-consensus/__test__/staking.test.ts +++ b/packages/auto-consensus/__test__/staking.test.ts @@ -18,6 +18,7 @@ import { registerOperator, } from '../src/staking' import { transfer } from '../src/transfer' +import { signAndSendTx, verifyOperatorRegistration } from './helpers' describe('Verify staking functions', () => { const isLocalhost = process.env.LOCALHOST === 'true' @@ -59,8 +60,6 @@ describe('Verify staking functions', () => { expect(accounts[0].address).toEqual(ALICE_ADDRESS) const sender = accounts[0] - let blockHash: string | undefined - const _balanceSenderStart = await balance(api, address(sender.address)) expect(_balanceSenderStart.free).toBeGreaterThan(BigInt(0)) @@ -68,7 +67,7 @@ describe('Verify staking functions', () => { const amountToStake = '100000000000000000000' const minimumNominatorStake = '1000000000000000000' const nominationTax = '5' - const tx = await registerOperator({ + const txInput = { api, senderAddress: ALICE_ADDRESS, Operator: operatorAccounts[0], @@ -76,28 +75,9 @@ describe('Verify staking functions', () => { amountToStake, minimumNominatorStake, nominationTax, - }) - - await new Promise((resolve, reject) => { - tx.signAndSend(sender, ({ status }) => { - if (status.isInBlock) { - blockHash = status.asInBlock.toHex() - console.log('Successful of Bob as operator with block hash ' + blockHash) - resolve() - } else if ( - status.isRetracted || - status.isFinalityTimeout || - status.isDropped || - status.isInvalid - ) { - reject(new Error('Transaction failed')) - } else { - console.log('Status of registration: ' + status.type) - } - }) - }) - - expect(blockHash).toBeDefined() + } + await signAndSendTx(sender, await registerOperator(txInput)) + await verifyOperatorRegistration(txInput) const _balanceSenderEnd = await balance(api, address(sender.address)) expect(_balanceSenderEnd.free).toBeLessThan( @@ -126,26 +106,7 @@ describe('Verify staking functions', () => { const forceStakingEpochTransition = await api.tx.domains.forceStakingEpochTransition(domainId) - const sudoTx = await api.tx.sudo.sudo(forceStakingEpochTransition) - - await new Promise((resolve, reject) => { - sudoTx.signAndSend(sender, ({ status }) => { - if (status.isInBlock) { - blockHash = status.asInBlock.toHex() - console.log('Successful forceStakingEpochTransition with block hash ' + blockHash) - resolve() - } else if ( - status.isRetracted || - status.isFinalityTimeout || - status.isDropped || - status.isInvalid - ) { - reject(new Error('Transaction failed')) - } else { - console.log('Status of forceStakingEpochTransition: ' + status.type) - } - }) - }) + await signAndSendTx(sender, await api.tx.sudo.sudo(forceStakingEpochTransition)) const operatorsListFinal = await operators(api) const findOperatorFinal = operatorsListFinal.find( @@ -189,37 +150,18 @@ describe('Verify staking functions', () => { expect(accounts[0].address).toEqual(ALICE_ADDRESS) const sender = accounts[0] - let blockHash: string | undefined - const _balanceSenderStart = await balance(api, address(sender.address)) - const _balanceReceiverStart = await balance(api, address(TEST_ADDRESS)) expect(_balanceSenderStart.free).toBeGreaterThan(BigInt(0)) const amountToStake = '50000000000000000000' - const tx = await nominateOperator({ - api, - operatorId: '1', - amountToStake, - }) - - await new Promise((resolve, reject) => { - tx.signAndSend(sender, ({ status }) => { - if (status.isInBlock) { - blockHash = status.asInBlock.toHex() - console.log('Successful of Alice nomination with block hash ' + blockHash) - resolve() - } else if ( - status.isRetracted || - status.isFinalityTimeout || - status.isDropped || - status.isInvalid - ) { - reject(new Error('Transaction failed')) - } else { - console.log('Status of nomination: ' + status.type) - } - }) - }) + await signAndSendTx( + sender, + await nominateOperator({ + api, + operatorId: '1', + amountToStake, + }), + ) }, 10000) test('Check Operator can addFunds after registration', async () => { @@ -236,41 +178,22 @@ describe('Verify staking functions', () => { expect(accounts[0].address).toEqual(ALICE_ADDRESS) const sender = accounts[0] - let blockHash: string | undefined const _balanceSenderStart = await balance(api, address(sender.address)) expect(_balanceSenderStart.free).toBeGreaterThan(BigInt(0)) // Transfer some funds to the operator const amountToTransfer = '10000000000000000000' - const transferTx = await transfer(api, operatorAccounts[0].address, amountToTransfer) - - await new Promise((resolve, reject) => { - transferTx.signAndSend(sender, ({ status }) => { - if (status.isInBlock) { - blockHash = status.asInBlock.toHex() - console.log('Successful transfer with block hash ' + blockHash) - resolve() - } else if ( - status.isRetracted || - status.isFinalityTimeout || - status.isDropped || - status.isInvalid - ) { - reject(new Error('Transaction failed')) - } else { - console.log('Status of transfer: ' + status.type) - } - }) - }) - - expect(blockHash).toBeDefined() + await signAndSendTx( + sender, + await transfer(api, operatorAccounts[0].address, amountToTransfer), + ) const domainId = '0' const amountToStake = '100000000000000000000' const minimumNominatorStake = '1000000000000000000' const nominationTax = '5' - const tx = await registerOperator({ + const txInput = { api, senderAddress: ALICE_ADDRESS, Operator: operatorAccounts[0], @@ -278,28 +201,9 @@ describe('Verify staking functions', () => { amountToStake, minimumNominatorStake, nominationTax, - }) - - await new Promise((resolve, reject) => { - tx.signAndSend(sender, ({ status }) => { - if (status.isInBlock) { - blockHash = status.asInBlock.toHex() - console.log('Successful of Bob as operator with block hash ' + blockHash) - resolve() - } else if ( - status.isRetracted || - status.isFinalityTimeout || - status.isDropped || - status.isInvalid - ) { - reject(new Error('Transaction failed')) - } else { - console.log('Status of registration: ' + status.type) - } - }) - }) - - expect(blockHash).toBeDefined() + } + await signAndSendTx(sender, await registerOperator(txInput)) + await verifyOperatorRegistration(txInput) const _balanceSenderEnd = await balance(api, address(sender.address)) expect(_balanceSenderEnd.free).toBeLessThan( @@ -327,30 +231,14 @@ describe('Verify staking functions', () => { expect(thisOperator.nominationTax).toEqual(Number(nominationTax)) const amountToStake2 = BigInt(amountToTransfer) / BigInt(2) - const tx3 = await nominateOperator({ - api, - operatorId: findOperator.operatorId, - amountToStake: amountToStake2.toString(), - }) - - await new Promise((resolve, reject) => { - tx3.signAndSend(operatorAccounts[0], ({ status }) => { - if (status.isInBlock) { - blockHash = status.asInBlock.toHex() - console.log('Successful of Operator nomination with block hash ' + blockHash) - resolve() - } else if ( - status.isRetracted || - status.isFinalityTimeout || - status.isDropped || - status.isInvalid - ) { - reject(new Error('Transaction failed')) - } else { - console.log('Status of nomination: ' + status.type) - } - }) - }) + await signAndSendTx( + operatorAccounts[0], + await nominateOperator({ + api, + operatorId: findOperator.operatorId, + amountToStake: amountToStake2.toString(), + }), + ) } }, 30000) }) @@ -370,41 +258,22 @@ describe('Verify staking functions', () => { expect(accounts[0].address).toEqual(ALICE_ADDRESS) const sender = accounts[0] - let blockHash: string | undefined const _balanceSenderStart = await balance(api, address(sender.address)) expect(_balanceSenderStart.free).toBeGreaterThan(BigInt(0)) // Transfer some funds to the operator const amountToTransfer = '10000000000000000000' - const transferTx = await transfer(api, operatorAccounts[0].address, amountToTransfer) - - await new Promise((resolve, reject) => { - transferTx.signAndSend(sender, ({ status }) => { - if (status.isInBlock) { - blockHash = status.asInBlock.toHex() - console.log('Successful transfer with block hash ' + blockHash) - resolve() - } else if ( - status.isRetracted || - status.isFinalityTimeout || - status.isDropped || - status.isInvalid - ) { - reject(new Error('Transaction failed')) - } else { - console.log('Status of transfer: ' + status.type) - } - }) - }) - - expect(blockHash).toBeDefined() + await signAndSendTx( + sender, + await transfer(api, operatorAccounts[0].address, amountToTransfer), + ) const domainId = '0' const amountToStake = '100000000000000000000' const minimumNominatorStake = '1000000000000000000' const nominationTax = '5' - const tx = await registerOperator({ + const txInput = { api, senderAddress: ALICE_ADDRESS, Operator: operatorAccounts[0], @@ -412,28 +281,9 @@ describe('Verify staking functions', () => { amountToStake, minimumNominatorStake, nominationTax, - }) - - await new Promise((resolve, reject) => { - tx.signAndSend(sender, ({ status }) => { - if (status.isInBlock) { - blockHash = status.asInBlock.toHex() - console.log('Successful of Bob as operator with block hash ' + blockHash) - resolve() - } else if ( - status.isRetracted || - status.isFinalityTimeout || - status.isDropped || - status.isInvalid - ) { - reject(new Error('Transaction failed')) - } else { - console.log('Status of registration: ' + status.type) - } - }) - }) - - expect(blockHash).toBeDefined() + } + await signAndSendTx(sender, await registerOperator(txInput)) + await verifyOperatorRegistration(txInput) const _balanceSenderEnd = await balance(api, address(sender.address)) expect(_balanceSenderEnd.free).toBeLessThan( @@ -460,29 +310,13 @@ describe('Verify staking functions', () => { expect(thisOperator.minimumNominatorStake).toEqual(BigInt(minimumNominatorStake)) expect(thisOperator.nominationTax).toEqual(Number(nominationTax)) - const tx3 = await deregisterOperator({ - api, - operatorId: findOperator.operatorId, - }) - - await new Promise((resolve, reject) => { - tx3.signAndSend(operatorAccounts[0], ({ status }) => { - if (status.isInBlock) { - blockHash = status.asInBlock.toHex() - console.log('Successful of Operator de-registering with block hash ' + blockHash) - resolve() - } else if ( - status.isRetracted || - status.isFinalityTimeout || - status.isDropped || - status.isInvalid - ) { - reject(new Error('Transaction failed')) - } else { - console.log('Status of de-registering: ' + status.type) - } - }) - }) + await signAndSendTx( + operatorAccounts[0], + await deregisterOperator({ + api, + operatorId: findOperator.operatorId, + }), + ) } }, 30000) }) diff --git a/packages/auto-consensus/__test__/transfer.test.ts b/packages/auto-consensus/__test__/transfer.test.ts index c778ab83..7a6a760c 100644 --- a/packages/auto-consensus/__test__/transfer.test.ts +++ b/packages/auto-consensus/__test__/transfer.test.ts @@ -9,6 +9,7 @@ import { import { address } from '../src/address' import { balance } from '../src/balances' import { transfer } from '../src/transfer' +import { signAndSendTx } from './helpers' describe('Verify transfer functions', () => { const isLocalhost = process.env.LOCALHOST === 'true' @@ -51,28 +52,7 @@ describe('Verify transfer functions', () => { const _balanceReceiverStart = await balance(api, address(BOB_ADDRESS)) expect(_balanceSenderStart.free).toBeGreaterThan(BigInt(0)) - const tx = await transfer(api, BOB_ADDRESS, 1) - - await new Promise((resolve, reject) => { - tx.signAndSend(sender, ({ status }) => { - if (status.isInBlock) { - blockHash = status.asInBlock.toHex() - console.log('Successful transfer of 1 with block hash ' + blockHash) - resolve() - } else if ( - status.isRetracted || - status.isFinalityTimeout || - status.isDropped || - status.isInvalid - ) { - reject(new Error('Transaction failed')) - } else { - console.log('Status of transfer: ' + status.type) - } - }) - }) - - expect(blockHash).toBeDefined() + await signAndSendTx(sender, await transfer(api, BOB_ADDRESS, 1)) const _balanceSenderEnd = await balance(api, address(sender.address)) const _balanceReceiverEnd = await balance(api, address(BOB_ADDRESS)) From 2d72c889b9a1e9139b21f3ab98c99f6b34db5522 Mon Sep 17 00:00:00 2001 From: Marc-Aurele Besner <82244926+marc-aurele-besner@users.noreply.github.com> Date: Wed, 12 Jun 2024 13:22:29 -0400 Subject: [PATCH 07/21] put all basic test setup in a setup helper --- .../auto-consensus/__test__/helpers/index.ts | 1 + .../auto-consensus/__test__/helpers/setup.ts | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 packages/auto-consensus/__test__/helpers/setup.ts diff --git a/packages/auto-consensus/__test__/helpers/index.ts b/packages/auto-consensus/__test__/helpers/index.ts index 3cfbfbd4..dba73dcd 100644 --- a/packages/auto-consensus/__test__/helpers/index.ts +++ b/packages/auto-consensus/__test__/helpers/index.ts @@ -1,2 +1,3 @@ +export * from './setup' export * from './staking' export * from './tx' diff --git a/packages/auto-consensus/__test__/helpers/setup.ts b/packages/auto-consensus/__test__/helpers/setup.ts new file mode 100644 index 00000000..88759645 --- /dev/null +++ b/packages/auto-consensus/__test__/helpers/setup.ts @@ -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, + } +} From 1f3698a89f4d74eca68039808188a5227f3176b0 Mon Sep 17 00:00:00 2001 From: Marc-Aurele Besner <82244926+marc-aurele-besner@users.noreply.github.com> Date: Wed, 12 Jun 2024 13:22:46 -0400 Subject: [PATCH 08/21] implement setup helper --- .../auto-consensus/__test__/balances.test.ts | 34 +++---------------- packages/auto-consensus/__test__/info.test.ts | 10 ++---- .../auto-consensus/__test__/staking.test.ts | 34 ++----------------- .../auto-consensus/__test__/transfer.test.ts | 34 ++----------------- 4 files changed, 12 insertions(+), 100 deletions(-) diff --git a/packages/auto-consensus/__test__/balances.test.ts b/packages/auto-consensus/__test__/balances.test.ts index 440903e9..56fb491c 100644 --- a/packages/auto-consensus/__test__/balances.test.ts +++ b/packages/auto-consensus/__test__/balances.test.ts @@ -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 () => { diff --git a/packages/auto-consensus/__test__/info.test.ts b/packages/auto-consensus/__test__/info.test.ts index 016e907b..3a4779e0 100644 --- a/packages/auto-consensus/__test__/info.test.ts +++ b/packages/auto-consensus/__test__/info.test.ts @@ -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 diff --git a/packages/auto-consensus/__test__/staking.test.ts b/packages/auto-consensus/__test__/staking.test.ts index fee5c4a2..7df8ffd0 100644 --- a/packages/auto-consensus/__test__/staking.test.ts +++ b/packages/auto-consensus/__test__/staking.test.ts @@ -1,11 +1,4 @@ -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 { u8aToHex } from '@polkadot/util' import { mnemonicGenerate } from '@polkadot/util-crypto' import { address } from '../src/address' @@ -18,31 +11,10 @@ import { registerOperator, } from '../src/staking' import { transfer } from '../src/transfer' -import { signAndSendTx, verifyOperatorRegistration } from './helpers' +import { setup, signAndSendTx, verifyOperatorRegistration } from './helpers' describe('Verify staking 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, ALICE_URI, ALICE_ADDRESS } = setup() if (isLocalhost) { describe('Test registerOperator()', () => { diff --git a/packages/auto-consensus/__test__/transfer.test.ts b/packages/auto-consensus/__test__/transfer.test.ts index 7a6a760c..a7db6923 100644 --- a/packages/auto-consensus/__test__/transfer.test.ts +++ b/packages/auto-consensus/__test__/transfer.test.ts @@ -1,39 +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 } from '../src/balances' import { transfer } from '../src/transfer' -import { signAndSendTx } from './helpers' +import { setup, signAndSendTx } from './helpers' describe('Verify transfer 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, ALICE_URI, ALICE_ADDRESS, BOB_ADDRESS } = setup() if (isLocalhost) { describe('Test transfer()', () => { From 3d9953983f4237c9405531b34832fef89efdb848 Mon Sep 17 00:00:00 2001 From: Marc-Aurele Besner <82244926+marc-aurele-besner@users.noreply.github.com> Date: Wed, 12 Jun 2024 13:48:18 -0400 Subject: [PATCH 09/21] add sudo and verifyOperatorRegistrationFinal helpers --- .../auto-consensus/__test__/helpers/index.ts | 1 + .../__test__/helpers/staking.ts | 51 +++++++ .../auto-consensus/__test__/helpers/sudo.ts | 11 ++ .../auto-consensus/__test__/staking.test.ts | 127 +++--------------- 4 files changed, 79 insertions(+), 111 deletions(-) create mode 100644 packages/auto-consensus/__test__/helpers/sudo.ts diff --git a/packages/auto-consensus/__test__/helpers/index.ts b/packages/auto-consensus/__test__/helpers/index.ts index dba73dcd..f3e71396 100644 --- a/packages/auto-consensus/__test__/helpers/index.ts +++ b/packages/auto-consensus/__test__/helpers/index.ts @@ -1,3 +1,4 @@ export * from './setup' export * from './staking' +export * from './sudo' export * from './tx' diff --git a/packages/auto-consensus/__test__/helpers/staking.ts b/packages/auto-consensus/__test__/helpers/staking.ts index f84a3118..4b50d368 100644 --- a/packages/auto-consensus/__test__/helpers/staking.ts +++ b/packages/auto-consensus/__test__/helpers/staking.ts @@ -1,6 +1,24 @@ 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 @@ -24,4 +42,37 @@ export const verifyOperatorRegistration = async (input: RegisterOperatorInput) = 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 } diff --git a/packages/auto-consensus/__test__/helpers/sudo.ts b/packages/auto-consensus/__test__/helpers/sudo.ts new file mode 100644 index 00000000..bc63afa2 --- /dev/null +++ b/packages/auto-consensus/__test__/helpers/sudo.ts @@ -0,0 +1,11 @@ +import { ApiPromise } from '@polkadot/api' +import type { AddressOrPair, SubmittableExtrinsic } from '@polkadot/api/types' +import type { ISubmittableResult } from '@polkadot/types/types' +import { signAndSendTx } from './tx' + +export const sudo = async ( + api: ApiPromise, + sender: AddressOrPair, + tx: SubmittableExtrinsic<'promise', ISubmittableResult>, + log: boolean = true, +) => await signAndSendTx(sender, api.tx.sudo.sudo(tx), log) diff --git a/packages/auto-consensus/__test__/staking.test.ts b/packages/auto-consensus/__test__/staking.test.ts index 7df8ffd0..9bb24902 100644 --- a/packages/auto-consensus/__test__/staking.test.ts +++ b/packages/auto-consensus/__test__/staking.test.ts @@ -1,17 +1,16 @@ import { ActivateWalletInput, activateWallet } from '@autonomys/auto-utils' -import { u8aToHex } from '@polkadot/util' import { mnemonicGenerate } from '@polkadot/util-crypto' import { address } from '../src/address' import { balance } from '../src/balances' -import { - deregisterOperator, - nominateOperator, - operator, - operators, - registerOperator, -} from '../src/staking' +import { deregisterOperator, nominateOperator, registerOperator } from '../src/staking' import { transfer } from '../src/transfer' -import { setup, signAndSendTx, verifyOperatorRegistration } from './helpers' +import { + setup, + signAndSendTx, + sudo, + verifyOperatorRegistration, + verifyOperatorRegistrationFinal, +} from './helpers' describe('Verify staking functions', () => { const { isLocalhost, TEST_NETWORK, ALICE_URI, ALICE_ADDRESS } = setup() @@ -49,65 +48,15 @@ describe('Verify staking functions', () => { nominationTax, } await signAndSendTx(sender, await registerOperator(txInput)) - await verifyOperatorRegistration(txInput) + const findOperator = await verifyOperatorRegistration(txInput) const _balanceSenderEnd = await balance(api, address(sender.address)) expect(_balanceSenderEnd.free).toBeLessThan( _balanceSenderStart.free - BigInt(amountToStake), ) - - const operatorsList = await operators(api) - const findOperator = operatorsList.find( - (o) => o.operatorDetails.signingKey === u8aToHex(operatorAccounts[0].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 }) - - const forceStakingEpochTransition = - await api.tx.domains.forceStakingEpochTransition(domainId) - await signAndSendTx(sender, await api.tx.sudo.sudo(forceStakingEpochTransition)) - - const operatorsListFinal = await operators(api) - const findOperatorFinal = operatorsListFinal.find( - (o) => o.operatorDetails.signingKey === u8aToHex(operatorAccounts[0].publicKey), - ) - expect(findOperatorFinal).toBeDefined() - if (findOperatorFinal) { - expect(findOperatorFinal.operatorDetails.currentDomainId).toEqual(BigInt(domainId)) - expect(findOperatorFinal.operatorDetails.currentTotalStake).toEqual( - (BigInt(amountToStake) / BigInt(100)) * BigInt(80), - ) - expect(findOperatorFinal.operatorDetails.minimumNominatorStake).toEqual( - BigInt(minimumNominatorStake), - ) - expect(findOperatorFinal.operatorDetails.nominationTax).toEqual(Number(nominationTax)) - expect(findOperatorFinal.operatorDetails.totalStorageFeeDeposit).toEqual( - (BigInt(amountToStake) / BigInt(100)) * BigInt(20), - ) - const thisOperatorFinal = await operator(api, findOperator.operatorId) - expect(thisOperatorFinal.currentDomainId).toEqual(BigInt(domainId)) - expect(thisOperatorFinal.currentTotalStake).toEqual( - (BigInt(amountToStake) / BigInt(100)) * BigInt(80), - ) - expect(thisOperatorFinal.minimumNominatorStake).toEqual(BigInt(minimumNominatorStake)) - expect(thisOperatorFinal.nominationTax).toEqual(Number(nominationTax)) - expect(thisOperatorFinal.totalStorageFeeDeposit).toEqual( - (BigInt(amountToStake) / BigInt(100)) * BigInt(20), - ) - } + await sudo(api, sender, await api.tx.domains.forceStakingEpochTransition(domainId)) + await verifyOperatorRegistrationFinal(txInput) } }, 30000) }) @@ -182,36 +131,8 @@ describe('Verify staking functions', () => { _balanceSenderStart.free - BigInt(amountToStake), ) - const operatorsList = await operators(api) - const findOperator = operatorsList.find( - (o) => o.operatorDetails.signingKey === u8aToHex(operatorAccounts[0].publicKey), - ) - expect(findOperator).toBeDefined() - if (findOperator) { - expect(findOperator.operatorDetails.currentDomainId).toEqual(BigInt(domainId)) - // To-Do: Either remove this check or figure why it's not working (guessing there is a delay for the stake to be active) - // expect(operator.operatorDetails.currentTotalStake).toEqual(BigInt(amountToStake)) - expect(findOperator.operatorDetails.minimumNominatorStake).toEqual( - BigInt(minimumNominatorStake), - ) - expect(findOperator.operatorDetails.nominationTax).toEqual(Number(nominationTax)) - const thisOperator = await operator(api, findOperator.operatorId) - expect(thisOperator.currentDomainId).toEqual(BigInt(domainId)) - // To-Do: Either remove this check or figure why it's not working (guessing there is a delay for the stake to be active) - // expect(thisOperator.currentTotalStake).toEqual(BigInt(amountToStake)) - expect(thisOperator.minimumNominatorStake).toEqual(BigInt(minimumNominatorStake)) - expect(thisOperator.nominationTax).toEqual(Number(nominationTax)) - - const amountToStake2 = BigInt(amountToTransfer) / BigInt(2) - await signAndSendTx( - operatorAccounts[0], - await nominateOperator({ - api, - operatorId: findOperator.operatorId, - amountToStake: amountToStake2.toString(), - }), - ) - } + await sudo(api, sender, await api.tx.domains.forceStakingEpochTransition(domainId)) + await verifyOperatorRegistrationFinal(txInput) }, 30000) }) @@ -262,26 +183,10 @@ describe('Verify staking functions', () => { _balanceSenderStart.free - BigInt(amountToStake), ) - const operatorsList = await operators(api) - const findOperator = operatorsList.find( - (o) => o.operatorDetails.signingKey === u8aToHex(operatorAccounts[0].publicKey), - ) - expect(findOperator).toBeDefined() - if (findOperator) { - expect(findOperator.operatorDetails.currentDomainId).toEqual(BigInt(domainId)) - // To-Do: Either remove this check or figure why it's not working (guessing there is a delay for the stake to be active) - // expect(operator.operatorDetails.currentTotalStake).toEqual(BigInt(amountToStake)) - expect(findOperator.operatorDetails.minimumNominatorStake).toEqual( - BigInt(minimumNominatorStake), - ) - expect(findOperator.operatorDetails.nominationTax).toEqual(Number(nominationTax)) - const thisOperator = await operator(api, findOperator.operatorId) - expect(thisOperator.currentDomainId).toEqual(BigInt(domainId)) - // To-Do: Either remove this check or figure why it's not working (guessing there is a delay for the stake to be active) - // expect(thisOperator.currentTotalStake).toEqual(BigInt(amountToStake)) - expect(thisOperator.minimumNominatorStake).toEqual(BigInt(minimumNominatorStake)) - expect(thisOperator.nominationTax).toEqual(Number(nominationTax)) + await sudo(api, sender, await api.tx.domains.forceStakingEpochTransition(domainId)) + const findOperator = await verifyOperatorRegistrationFinal(txInput) + if (findOperator) { await signAndSendTx( operatorAccounts[0], await deregisterOperator({ From f6e8967893e140ac9250e017601d21cb499644f6 Mon Sep 17 00:00:00 2001 From: Marc-Aurele Besner <82244926+marc-aurele-besner@users.noreply.github.com> Date: Wed, 12 Jun 2024 16:48:45 -0400 Subject: [PATCH 10/21] adding events validations --- .../auto-consensus/__test__/helpers/events.ts | 47 +++++++++++++++++++ .../auto-consensus/__test__/helpers/index.ts | 1 + .../auto-consensus/__test__/helpers/tx.ts | 35 ++++++++++++-- 3 files changed, 79 insertions(+), 4 deletions(-) create mode 100644 packages/auto-consensus/__test__/helpers/events.ts diff --git a/packages/auto-consensus/__test__/helpers/events.ts b/packages/auto-consensus/__test__/helpers/events.ts new file mode 100644 index 00000000..e039cd34 --- /dev/null +++ b/packages/auto-consensus/__test__/helpers/events.ts @@ -0,0 +1,47 @@ +export type ActionEvents = string | string[] +export type Events = ActionEvents | ActionEvents[] + +export const system: { + [key: string]: string +} = { + success: 'system.ExtrinsicSuccess', + failure: 'system.ExtrinsicFailed', +} + +export const balances: { + [key: string]: string +} = { + withdraw: 'balances.Withdraw', +} + +export const transactionPayment: { + [key: string]: string +} = { + feePaid: 'transactionPayment.TransactionFeePaid', +} + +export const domains: { + [key: string]: string +} = { + storageFeeDeposited: 'system.StorageFeeDeposited', + operatorRegistered: 'system.OperatorRegistered', +} + +export const events: { + [key: string]: ActionEvents +} = { + transfer: [balances.withdraw, 'balances.Transfer'], + operatorRegistered: [ + balances.withdraw, + transactionPayment.feePaid, + domains.storageFeeDeposited, + domains.operatorRegistered, + system.success, + ], + withdrawStake: [balances.withdraw, transactionPayment.feePaid, system.success], + unlockFunds: [balances.withdraw, transactionPayment.feePaid, system.success], + operatorDeRegistered: [balances.withdraw, transactionPayment.feePaid, system.success], + ...system, + ...balances, + ...transactionPayment, +} diff --git a/packages/auto-consensus/__test__/helpers/index.ts b/packages/auto-consensus/__test__/helpers/index.ts index f3e71396..9443bec6 100644 --- a/packages/auto-consensus/__test__/helpers/index.ts +++ b/packages/auto-consensus/__test__/helpers/index.ts @@ -1,3 +1,4 @@ +export * from './events' export * from './setup' export * from './staking' export * from './sudo' diff --git a/packages/auto-consensus/__test__/helpers/tx.ts b/packages/auto-consensus/__test__/helpers/tx.ts index 61d60997..c63b97a7 100644 --- a/packages/auto-consensus/__test__/helpers/tx.ts +++ b/packages/auto-consensus/__test__/helpers/tx.ts @@ -1,18 +1,45 @@ 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, 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()) + const index = _eventsExpected.indexOf(`${section}.${method}`) + if (index > -1) _eventsExpected.splice(index, 1) + else if (log) console.log('Event not expected', `${section}.${method}`) + }) + if (_eventsExpected.length > 0) console.log('Events not found', _eventsExpected) + + expect(_eventsExpected).toHaveLength(0) + + return _eventsExpected +} export const signAndSendTx = async ( sender: AddressOrPair, tx: SubmittableExtrinsic<'promise', ISubmittableResult>, + eventsExpected: Events = [], log: boolean = true, ) => { let blockHash: string | undefined = undefined await new Promise((resolve, reject) => { - tx.signAndSend(sender, ({ status }) => { + tx.signAndSend(sender, ({ events, status }) => { if (status.isInBlock) { blockHash = status.asInBlock.toHex() - if (log) console.log('Successful transfer of 1 with block hash ' + blockHash) - resolve() + if (log) console.log('Successful tx with block hash ' + blockHash) + + if (eventsExpected.length > 0) { + eventsExpected = validateEvents(events, eventsExpected, log) + if (eventsExpected.length === 0) resolve() + else reject(new Error('Events not found')) + } else resolve() } else if ( status.isRetracted || status.isFinalityTimeout || @@ -21,7 +48,7 @@ export const signAndSendTx = async ( ) { if (log) console.error('Transaction failed') reject(new Error('Transaction failed')) - } else if (log) console.log('Status of transfer: ' + status.type) + } else if (log) console.log('Status of tx: ' + status.type) }) }) expect(blockHash).toBeDefined() From 8fa02415596cdf413c80d8efa8631b07d5d824b0 Mon Sep 17 00:00:00 2001 From: Marc-Aurele Besner <82244926+marc-aurele-besner@users.noreply.github.com> Date: Wed, 12 Jun 2024 16:48:59 -0400 Subject: [PATCH 11/21] adding sudo helper --- packages/auto-consensus/__test__/helpers/sudo.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/auto-consensus/__test__/helpers/sudo.ts b/packages/auto-consensus/__test__/helpers/sudo.ts index bc63afa2..7ba853d3 100644 --- a/packages/auto-consensus/__test__/helpers/sudo.ts +++ b/packages/auto-consensus/__test__/helpers/sudo.ts @@ -7,5 +7,6 @@ export const sudo = async ( api: ApiPromise, sender: AddressOrPair, tx: SubmittableExtrinsic<'promise', ISubmittableResult>, + eventsExpected: string[] = [], log: boolean = true, -) => await signAndSendTx(sender, api.tx.sudo.sudo(tx), log) +) => await signAndSendTx(sender, api.tx.sudo.sudo(tx), eventsExpected, log) From f85ca8de8f157f160813821d83ec3b79cfd499c7 Mon Sep 17 00:00:00 2001 From: Marc-Aurele Besner <82244926+marc-aurele-besner@users.noreply.github.com> Date: Wed, 12 Jun 2024 16:49:18 -0400 Subject: [PATCH 12/21] fix unlockNominator --- packages/auto-consensus/src/staking.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/auto-consensus/src/staking.ts b/packages/auto-consensus/src/staking.ts index 0e398ecd..f6137dbb 100644 --- a/packages/auto-consensus/src/staking.ts +++ b/packages/auto-consensus/src/staking.ts @@ -191,13 +191,13 @@ export const unlockFunds = async (input: StakingInput) => { } } -export const unlockOperator = async (input: StakingInput) => { +export const unlockNominator = async (input: StakingInput) => { try { const { api, operatorId } = input - return await api.tx.domains.unlockOperator(parseString(operatorId)) + return await api.tx.domains.unlockNominator(parseString(operatorId)) } catch (error) { console.error('error', error) - throw new Error('Error creating unlock operator tx.' + error) + throw new Error('Error creating unlock nominator tx.' + error) } } From ffe28b17440349f9de46aac9853f30d0a61bd060 Mon Sep 17 00:00:00 2001 From: Marc-Aurele Besner <82244926+marc-aurele-besner@users.noreply.github.com> Date: Wed, 12 Jun 2024 16:49:54 -0400 Subject: [PATCH 13/21] add/improve staking tests --- .../auto-consensus/__test__/staking.test.ts | 82 ++++++++++++++++++- 1 file changed, 78 insertions(+), 4 deletions(-) diff --git a/packages/auto-consensus/__test__/staking.test.ts b/packages/auto-consensus/__test__/staking.test.ts index 9bb24902..1a4fab58 100644 --- a/packages/auto-consensus/__test__/staking.test.ts +++ b/packages/auto-consensus/__test__/staking.test.ts @@ -2,9 +2,16 @@ import { ActivateWalletInput, activateWallet } from '@autonomys/auto-utils' import { mnemonicGenerate } from '@polkadot/util-crypto' import { address } from '../src/address' import { balance } from '../src/balances' -import { deregisterOperator, nominateOperator, registerOperator } from '../src/staking' +import { + deregisterOperator, + nominateOperator, + registerOperator, + unlockFunds, + withdrawStake, +} from '../src/staking' import { transfer } from '../src/transfer' import { + events, setup, signAndSendTx, sudo, @@ -47,7 +54,7 @@ describe('Verify staking functions', () => { minimumNominatorStake, nominationTax, } - await signAndSendTx(sender, await registerOperator(txInput)) + await signAndSendTx(sender, await registerOperator(txInput), [events.operatorRegistered]) const findOperator = await verifyOperatorRegistration(txInput) const _balanceSenderEnd = await balance(api, address(sender.address)) @@ -123,7 +130,7 @@ describe('Verify staking functions', () => { minimumNominatorStake, nominationTax, } - await signAndSendTx(sender, await registerOperator(txInput)) + await signAndSendTx(sender, await registerOperator(txInput), [events.operatorRegistered]) await verifyOperatorRegistration(txInput) const _balanceSenderEnd = await balance(api, address(sender.address)) @@ -136,6 +143,72 @@ describe('Verify staking functions', () => { }, 30000) }) + describe('Test withdrawStake()', () => { + test('Check Operator can withdrawStake after registration', async () => { + const mnemonicOperator = mnemonicGenerate() + const { api, accounts } = await activateWallet({ + ...TEST_NETWORK, + uri: ALICE_URI, + } as ActivateWalletInput) + const { accounts: operatorAccounts } = await activateWallet({ + ...TEST_NETWORK, + uri: mnemonicOperator, + } as ActivateWalletInput) + expect(accounts.length).toBeGreaterThan(0) + expect(accounts[0].address).toEqual(ALICE_ADDRESS) + + const sender = accounts[0] + + const _balanceSenderStart = await balance(api, address(sender.address)) + expect(_balanceSenderStart.free).toBeGreaterThan(BigInt(0)) + + // Transfer some funds to the operator + const amountToTransfer = '10000000000000000000' + await signAndSendTx( + sender, + await transfer(api, operatorAccounts[0].address, amountToTransfer), + ) + + const domainId = '0' + const amountToStake = '100000000000000000000' + const minimumNominatorStake = '1000000000000000000' + const nominationTax = '5' + const txInput = { + api, + senderAddress: ALICE_ADDRESS, + Operator: operatorAccounts[0], + domainId, + amountToStake, + minimumNominatorStake, + nominationTax, + } + await signAndSendTx(sender, await registerOperator(txInput), [events.operatorRegistered]) + await verifyOperatorRegistration(txInput) + + const _balanceSenderEnd = await balance(api, address(sender.address)) + expect(_balanceSenderEnd.free).toBeLessThan( + _balanceSenderStart.free - BigInt(amountToStake), + ) + + await sudo(api, sender, await api.tx.domains.forceStakingEpochTransition(domainId)) + const findOperator = await verifyOperatorRegistrationFinal(txInput) + + await sudo(api, sender, await api.tx.domains.forceStakingEpochTransition(domainId)) + + if (findOperator) { + await signAndSendTx( + operatorAccounts[0], + await withdrawStake({ + api, + operatorId: findOperator.operatorId, + shares: findOperator.operatorDetails.currentTotalShares, + }), + [events.withdrawStake], + ) + } + }, 60000) + }) + describe('Test deregisterOperator()', () => { test('Check Operator can deregisterOperator after registration', async () => { const mnemonicOperator = mnemonicGenerate() @@ -175,7 +248,7 @@ describe('Verify staking functions', () => { minimumNominatorStake, nominationTax, } - await signAndSendTx(sender, await registerOperator(txInput)) + await signAndSendTx(sender, await registerOperator(txInput), [events.operatorRegistered]) await verifyOperatorRegistration(txInput) const _balanceSenderEnd = await balance(api, address(sender.address)) @@ -193,6 +266,7 @@ describe('Verify staking functions', () => { api, operatorId: findOperator.operatorId, }), + [events.operatorDeRegistered], ) } }, 30000) From 614b0f60e4f8de39488f6e6b3ff1ce541289040b Mon Sep 17 00:00:00 2001 From: Marc-Aurele Besner <82244926+marc-aurele-besner@users.noreply.github.com> Date: Thu, 13 Jun 2024 10:31:19 -0400 Subject: [PATCH 14/21] remove unnecessary log in wallet --- packages/auto-utils/src/wallet.ts | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/packages/auto-utils/src/wallet.ts b/packages/auto-utils/src/wallet.ts index b5a14875..aa81e237 100644 --- a/packages/auto-utils/src/wallet.ts +++ b/packages/auto-utils/src/wallet.ts @@ -41,20 +41,11 @@ export const activateWallet = async (input: ActivateWalletInput) => { // Get the list of accounts from the extension const allAccounts = await web3Accounts() accounts.push(...allAccounts) - - // Attach the first account (or handle multiple accounts as needed) - if (allAccounts.length > 0) { - const selectedAccount = allAccounts[0] - console.log('Connected to account:', selectedAccount.address) - // You can now use selectedAccount for transactions - } else { - console.warn('No accounts found in the Polkadot.js extension') - } + if (allAccounts.length === 0) console.warn('No accounts found in the Polkadot.js extension') } else if ((input as Mnemonic).mnemonic || (input as URI).uri) { // Attach the wallet in a node environment const account = await setupWallet(input) accounts.push(account) - if (account) console.log('Wallet attached:', account.address) } else throw new Error('No wallet provided') return { api, accounts } From e997bff1806b387e83ee369ef5f3a43ace3078c6 Mon Sep 17 00:00:00 2001 From: Marc-Aurele Besner <82244926+marc-aurele-besner@users.noreply.github.com> Date: Thu, 13 Jun 2024 10:31:41 -0400 Subject: [PATCH 15/21] improve tests helper --- .../auto-consensus/__test__/helpers/events.ts | 110 ++++++++++++++---- .../__test__/helpers/staking.ts | 1 + .../auto-consensus/__test__/helpers/sudo.ts | 3 +- .../auto-consensus/__test__/helpers/tx.ts | 29 +++-- 4 files changed, 113 insertions(+), 30 deletions(-) diff --git a/packages/auto-consensus/__test__/helpers/events.ts b/packages/auto-consensus/__test__/helpers/events.ts index e039cd34..548afce7 100644 --- a/packages/auto-consensus/__test__/helpers/events.ts +++ b/packages/auto-consensus/__test__/helpers/events.ts @@ -1,47 +1,117 @@ export type ActionEvents = string | string[] export type Events = ActionEvents | ActionEvents[] -export const system: { +// 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 } = { - success: 'system.ExtrinsicSuccess', - failure: 'system.ExtrinsicFailed', + failure: eventName(Type.system, 'ExtrinsicFailed'), + newAccount: eventName(Type.system, 'NewAccount'), + success: eventName(Type.system, 'ExtrinsicSuccess'), } -export const balances: { +// Balances Events +const balances: { [key: string]: string } = { - withdraw: 'balances.Withdraw', + deposit: eventName(Type.balances, 'Deposit'), + endowed: eventName(Type.balances, 'Endowed'), + transfer: eventName(Type.balances, 'Transfer'), + withdraw: eventName(Type.balances, 'Withdraw'), } -export const transactionPayment: { +// Transaction Payment Events +const transactionPayment: { [key: string]: string } = { - feePaid: 'transactionPayment.TransactionFeePaid', + feePaid: eventName(Type.transactionPayment, 'TransactionFeePaid'), } -export const domains: { +// Domains Events +const domains: { [key: string]: string } = { - storageFeeDeposited: 'system.StorageFeeDeposited', - operatorRegistered: 'system.OperatorRegistered', + 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'), } -export const events: { - [key: string]: ActionEvents +// Sudo Events +const sudo: { + [key: string]: string } = { - transfer: [balances.withdraw, 'balances.Transfer'], + 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, - transactionPayment.feePaid, 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, ], - withdrawStake: [balances.withdraw, transactionPayment.feePaid, system.success], - unlockFunds: [balances.withdraw, transactionPayment.feePaid, system.success], - operatorDeRegistered: [balances.withdraw, transactionPayment.feePaid, system.success], - ...system, - ...balances, - ...transactionPayment, } diff --git a/packages/auto-consensus/__test__/helpers/staking.ts b/packages/auto-consensus/__test__/helpers/staking.ts index 4b50d368..6dd2d6e2 100644 --- a/packages/auto-consensus/__test__/helpers/staking.ts +++ b/packages/auto-consensus/__test__/helpers/staking.ts @@ -1,3 +1,4 @@ +import type { ApiPromise } from '@polkadot/api' import { u8aToHex } from '@polkadot/util' import { operator, operators, RegisterOperatorInput } from '../../src/staking' diff --git a/packages/auto-consensus/__test__/helpers/sudo.ts b/packages/auto-consensus/__test__/helpers/sudo.ts index 7ba853d3..8a48f660 100644 --- a/packages/auto-consensus/__test__/helpers/sudo.ts +++ b/packages/auto-consensus/__test__/helpers/sudo.ts @@ -1,12 +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: string[] = [], + eventsExpected: Events = [], log: boolean = true, ) => await signAndSendTx(sender, api.tx.sudo.sudo(tx), eventsExpected, log) diff --git a/packages/auto-consensus/__test__/helpers/tx.ts b/packages/auto-consensus/__test__/helpers/tx.ts index c63b97a7..1fe8bdfd 100644 --- a/packages/auto-consensus/__test__/helpers/tx.ts +++ b/packages/auto-consensus/__test__/helpers/tx.ts @@ -3,19 +3,27 @@ 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, log: boolean = true) => { +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()) + // 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}`) + else if (log) + console.log('Event not expected', `${section}.${method}`, 'tx', tx, 'block', block) }) - if (_eventsExpected.length > 0) console.log('Events not found', _eventsExpected) + if (_eventsExpected.length > 0) + console.log('Events not found', _eventsExpected, 'tx', tx, 'block', block) expect(_eventsExpected).toHaveLength(0) @@ -28,15 +36,17 @@ export const signAndSendTx = async ( eventsExpected: Events = [], log: boolean = true, ) => { + let txHashHex: string | undefined = undefined let blockHash: string | undefined = undefined await new Promise((resolve, reject) => { - tx.signAndSend(sender, ({ events, status }) => { + tx.signAndSend(sender, ({ events, status, txHash }) => { if (status.isInBlock) { + txHashHex = txHash.toHex() blockHash = status.asInBlock.toHex() - if (log) console.log('Successful tx with block hash ' + blockHash) + if (log) console.log('Successful tx', txHashHex, 'in block', blockHash) if (eventsExpected.length > 0) { - eventsExpected = validateEvents(events, eventsExpected, log) + eventsExpected = validateEvents(events, eventsExpected, txHashHex, blockHash, log) if (eventsExpected.length === 0) resolve() else reject(new Error('Events not found')) } else resolve() @@ -48,10 +58,11 @@ export const signAndSendTx = async ( ) { if (log) console.error('Transaction failed') reject(new Error('Transaction failed')) - } else if (log) console.log('Status of tx: ' + status.type) + } }) }) + expect(txHashHex).toBeDefined() expect(blockHash).toBeDefined() - return blockHash + return { txHash: txHashHex, blockHash } } From f001274c2ea04d70457a84f48f391a3e467f1a1d Mon Sep 17 00:00:00 2001 From: Marc-Aurele Besner <82244926+marc-aurele-besner@users.noreply.github.com> Date: Thu, 13 Jun 2024 10:32:09 -0400 Subject: [PATCH 16/21] improve consensus tests --- .../auto-consensus/__test__/staking.test.ts | 189 +++++++++++------- .../auto-consensus/__test__/transfer.test.ts | 6 +- 2 files changed, 125 insertions(+), 70 deletions(-) diff --git a/packages/auto-consensus/__test__/staking.test.ts b/packages/auto-consensus/__test__/staking.test.ts index 1a4fab58..2d36c310 100644 --- a/packages/auto-consensus/__test__/staking.test.ts +++ b/packages/auto-consensus/__test__/staking.test.ts @@ -5,6 +5,7 @@ import { balance } from '../src/balances' import { deregisterOperator, nominateOperator, + operator, registerOperator, unlockFunds, withdrawStake, @@ -62,7 +63,9 @@ describe('Verify staking functions', () => { _balanceSenderStart.free - BigInt(amountToStake), ) if (findOperator) { - await sudo(api, sender, await api.tx.domains.forceStakingEpochTransition(domainId)) + await sudo(api, sender, await api.tx.domains.forceStakingEpochTransition(domainId), [ + events.forceDomainEpochTransition, + ]) await verifyOperatorRegistrationFinal(txInput) } }, 30000) @@ -89,6 +92,7 @@ describe('Verify staking functions', () => { operatorId: '1', amountToStake, }), + [events.operatorNominated], ) }, 10000) @@ -115,6 +119,7 @@ describe('Verify staking functions', () => { await signAndSendTx( sender, await transfer(api, operatorAccounts[0].address, amountToTransfer), + [events.transfer], ) const domainId = '0' @@ -138,13 +143,28 @@ describe('Verify staking functions', () => { _balanceSenderStart.free - BigInt(amountToStake), ) - await sudo(api, sender, await api.tx.domains.forceStakingEpochTransition(domainId)) - await verifyOperatorRegistrationFinal(txInput) - }, 30000) + await sudo(api, sender, await api.tx.domains.forceStakingEpochTransition(domainId), [ + events.forceDomainEpochTransition, + ]) + const operator = await verifyOperatorRegistrationFinal(txInput) + + if (operator) { + const amountToAdd = '50000000000000000000' + await signAndSendTx( + operatorAccounts[0], + await nominateOperator({ + api, + operatorId: operator?.operatorId, + amountToStake: amountToAdd, + }), + [events.operatorNominated], + ) + } else throw new Error('Operator not found') + }, 120000) }) - describe('Test withdrawStake()', () => { - test('Check Operator can withdrawStake after registration', async () => { + describe('Test deregisterOperator()', () => { + test('Check Operator can deregisterOperator after registration', async () => { const mnemonicOperator = mnemonicGenerate() const { api, accounts } = await activateWallet({ ...TEST_NETWORK, @@ -162,13 +182,6 @@ describe('Verify staking functions', () => { const _balanceSenderStart = await balance(api, address(sender.address)) expect(_balanceSenderStart.free).toBeGreaterThan(BigInt(0)) - // Transfer some funds to the operator - const amountToTransfer = '10000000000000000000' - await signAndSendTx( - sender, - await transfer(api, operatorAccounts[0].address, amountToTransfer), - ) - const domainId = '0' const amountToStake = '100000000000000000000' const minimumNominatorStake = '1000000000000000000' @@ -185,92 +198,134 @@ describe('Verify staking functions', () => { await signAndSendTx(sender, await registerOperator(txInput), [events.operatorRegistered]) await verifyOperatorRegistration(txInput) - const _balanceSenderEnd = await balance(api, address(sender.address)) - expect(_balanceSenderEnd.free).toBeLessThan( - _balanceSenderStart.free - BigInt(amountToStake), - ) - - await sudo(api, sender, await api.tx.domains.forceStakingEpochTransition(domainId)) + await sudo(api, sender, await api.tx.domains.forceStakingEpochTransition(domainId), [ + events.forceDomainEpochTransition, + ]) const findOperator = await verifyOperatorRegistrationFinal(txInput) - await sudo(api, sender, await api.tx.domains.forceStakingEpochTransition(domainId)) - if (findOperator) { await signAndSendTx( - operatorAccounts[0], - await withdrawStake({ + sender, + await deregisterOperator({ api, operatorId: findOperator.operatorId, - shares: findOperator.operatorDetails.currentTotalShares, }), - [events.withdrawStake], + [events.operatorDeRegistered], ) } }, 60000) }) - describe('Test deregisterOperator()', () => { - test('Check Operator can deregisterOperator after registration', async () => { - const mnemonicOperator = mnemonicGenerate() + describe('Test withdrawStake()', () => { + test('Check Alice can nominate OperatorId 1 and then withdrawStake', async () => { const { api, accounts } = await activateWallet({ ...TEST_NETWORK, uri: ALICE_URI, } as ActivateWalletInput) - const { accounts: operatorAccounts } = await activateWallet({ - ...TEST_NETWORK, - uri: mnemonicOperator, - } as ActivateWalletInput) expect(accounts.length).toBeGreaterThan(0) expect(accounts[0].address).toEqual(ALICE_ADDRESS) const sender = accounts[0] - const _balanceSenderStart = await balance(api, address(sender.address)) expect(_balanceSenderStart.free).toBeGreaterThan(BigInt(0)) - // Transfer some funds to the operator - const amountToTransfer = '10000000000000000000' + const operatorId = '1' + const operatorDetails = await operator(api, operatorId) + + const amountToStake = '50000000000000000000' await signAndSendTx( sender, - await transfer(api, operatorAccounts[0].address, amountToTransfer), + await nominateOperator({ + api, + operatorId, + amountToStake, + }), + [events.operatorNominated], ) - const domainId = '0' - const amountToStake = '100000000000000000000' - const minimumNominatorStake = '1000000000000000000' - const nominationTax = '5' - const txInput = { + await sudo( api, - senderAddress: ALICE_ADDRESS, - Operator: operatorAccounts[0], - domainId, - amountToStake, - minimumNominatorStake, - nominationTax, - } - await signAndSendTx(sender, await registerOperator(txInput), [events.operatorRegistered]) - await verifyOperatorRegistration(txInput) - - const _balanceSenderEnd = await balance(api, address(sender.address)) - expect(_balanceSenderEnd.free).toBeLessThan( - _balanceSenderStart.free - BigInt(amountToStake), + sender, + await api.tx.domains.forceStakingEpochTransition(operatorDetails.currentDomainId), + [events.forceDomainEpochTransition], ) - await sudo(api, sender, await api.tx.domains.forceStakingEpochTransition(domainId)) - const findOperator = await verifyOperatorRegistrationFinal(txInput) - - if (findOperator) { - await signAndSendTx( - operatorAccounts[0], - await deregisterOperator({ - api, - operatorId: findOperator.operatorId, - }), - [events.operatorDeRegistered], - ) - } + await signAndSendTx( + sender, + await withdrawStake({ + api, + operatorId, + shares: operatorDetails.currentTotalShares / BigInt(1000), + }), + [events.withdrawStake], + ) }, 30000) }) + + // Still unsure if this is testable due to the lock period of 2 days + // describe('Test unlockFunds()', () => { + // test('Check Operator can unlockFunds after registration', async () => { + // const mnemonicOperator = mnemonicGenerate() + // const { api, accounts } = await activateWallet({ + // ...TEST_NETWORK, + // uri: ALICE_URI, + // } as ActivateWalletInput) + // const { accounts: operatorAccounts } = await activateWallet({ + // ...TEST_NETWORK, + // uri: mnemonicOperator, + // } as ActivateWalletInput) + // expect(accounts.length).toBeGreaterThan(0) + // expect(accounts[0].address).toEqual(ALICE_ADDRESS) + + // const sender = accounts[0] + + // const _balanceSenderStart = await balance(api, address(sender.address)) + // expect(_balanceSenderStart.free).toBeGreaterThan(BigInt(0)) + + // // Transfer some funds to the operator + // const amountToTransfer = '10000000000000000000' + // await signAndSendTx( + // sender, + // await transfer(api, operatorAccounts[0].address, amountToTransfer), + // [events.transfer] + // ) + + // const domainId = '0' + // const amountToStake = '100000000000000000000' + // const minimumNominatorStake = '1000000000000000000' + // const nominationTax = '5' + // const txInput = { + // api, + // senderAddress: ALICE_ADDRESS, + // Operator: operatorAccounts[0], + // domainId, + // amountToStake, + // minimumNominatorStake, + // nominationTax, + // } + // await signAndSendTx(sender, await registerOperator(txInput), [events.operatorRegistered]) + // await verifyOperatorRegistration(txInput) + + // const _balanceSenderEnd = await balance(api, address(sender.address)) + // expect(_balanceSenderEnd.free).toBeLessThan( + // _balanceSenderStart.free - BigInt(amountToStake), + // ) + + // await sudo(api, sender, await api.tx.domains.forceStakingEpochTransition(domainId), [events.forceDomainEpochTransition]) + // const findOperator = await verifyOperatorRegistrationFinal(txInput) + + // if (findOperator) { + // await signAndSendTx( + // sender, + // await unlockFunds({ + // api, + // operatorId: findOperator.operatorId, + // }), + // [events.operatorDeRegistered], + // ) + // } + // }, 30000) + // }) } else { test('Staking test only run on localhost', async () => { expect(true).toBeTruthy() diff --git a/packages/auto-consensus/__test__/transfer.test.ts b/packages/auto-consensus/__test__/transfer.test.ts index a7db6923..762df324 100644 --- a/packages/auto-consensus/__test__/transfer.test.ts +++ b/packages/auto-consensus/__test__/transfer.test.ts @@ -2,7 +2,7 @@ import { ActivateWalletInput, activateWallet } from '@autonomys/auto-utils' import { address } from '../src/address' import { balance } from '../src/balances' import { transfer } from '../src/transfer' -import { setup, signAndSendTx } from './helpers' +import { events, setup, signAndSendTx } from './helpers' describe('Verify transfer functions', () => { const { isLocalhost, TEST_NETWORK, ALICE_URI, ALICE_ADDRESS, BOB_ADDRESS } = setup() @@ -24,13 +24,13 @@ describe('Verify transfer functions', () => { const _balanceReceiverStart = await balance(api, address(BOB_ADDRESS)) expect(_balanceSenderStart.free).toBeGreaterThan(BigInt(0)) - await signAndSendTx(sender, await transfer(api, BOB_ADDRESS, 1)) + await signAndSendTx(sender, await transfer(api, BOB_ADDRESS, 1), [events.transfer]) const _balanceSenderEnd = await balance(api, address(sender.address)) const _balanceReceiverEnd = await balance(api, address(BOB_ADDRESS)) expect(_balanceSenderEnd.free).toBeLessThan(_balanceSenderStart.free) expect(_balanceReceiverEnd.free).toBeGreaterThan(_balanceReceiverStart.free) - }) + }, 60000) }) } else { test('Transfer test only run on localhost', async () => { From 46f50a9752f24fec15ad610227cb8089c3e2605c Mon Sep 17 00:00:00 2001 From: Marc-Aurele Besner <82244926+marc-aurele-besner@users.noreply.github.com> Date: Thu, 13 Jun 2024 10:32:33 -0400 Subject: [PATCH 17/21] run all tests serially in the current process, rather than creating a worker pool of child processes --- packages/auto-consensus/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/auto-consensus/package.json b/packages/auto-consensus/package.json index 2908ae66..72bd59ec 100644 --- a/packages/auto-consensus/package.json +++ b/packages/auto-consensus/package.json @@ -6,7 +6,7 @@ "build": "tsc", "clean": "rm -rf dist", "format": "prettier --write \"src/**/*.ts\"", - "test": "jest" + "test": "jest -i" }, "files": [ "dist", From 322acaba2e27b8b65c3b6067057c8e7aa0410b6c Mon Sep 17 00:00:00 2001 From: Marc-Aurele Besner <82244926+marc-aurele-besner@users.noreply.github.com> Date: Thu, 13 Jun 2024 11:01:57 -0400 Subject: [PATCH 18/21] remove unnecessary transfer --- .../auto-consensus/__test__/staking.test.ts | 32 ++----------------- 1 file changed, 3 insertions(+), 29 deletions(-) diff --git a/packages/auto-consensus/__test__/staking.test.ts b/packages/auto-consensus/__test__/staking.test.ts index 2d36c310..460d6541 100644 --- a/packages/auto-consensus/__test__/staking.test.ts +++ b/packages/auto-consensus/__test__/staking.test.ts @@ -114,14 +114,6 @@ describe('Verify staking functions', () => { const _balanceSenderStart = await balance(api, address(sender.address)) expect(_balanceSenderStart.free).toBeGreaterThan(BigInt(0)) - // Transfer some funds to the operator - const amountToTransfer = '10000000000000000000' - await signAndSendTx( - sender, - await transfer(api, operatorAccounts[0].address, amountToTransfer), - [events.transfer], - ) - const domainId = '0' const amountToStake = '100000000000000000000' const minimumNominatorStake = '1000000000000000000' @@ -138,11 +130,6 @@ describe('Verify staking functions', () => { await signAndSendTx(sender, await registerOperator(txInput), [events.operatorRegistered]) await verifyOperatorRegistration(txInput) - const _balanceSenderEnd = await balance(api, address(sender.address)) - expect(_balanceSenderEnd.free).toBeLessThan( - _balanceSenderStart.free - BigInt(amountToStake), - ) - await sudo(api, sender, await api.tx.domains.forceStakingEpochTransition(domainId), [ events.forceDomainEpochTransition, ]) @@ -151,16 +138,16 @@ describe('Verify staking functions', () => { if (operator) { const amountToAdd = '50000000000000000000' await signAndSendTx( - operatorAccounts[0], + sender, await nominateOperator({ api, - operatorId: operator?.operatorId, + operatorId: operator.operatorId, amountToStake: amountToAdd, }), [events.operatorNominated], ) } else throw new Error('Operator not found') - }, 120000) + }, 180000) }) describe('Test deregisterOperator()', () => { @@ -282,14 +269,6 @@ describe('Verify staking functions', () => { // const _balanceSenderStart = await balance(api, address(sender.address)) // expect(_balanceSenderStart.free).toBeGreaterThan(BigInt(0)) - // // Transfer some funds to the operator - // const amountToTransfer = '10000000000000000000' - // await signAndSendTx( - // sender, - // await transfer(api, operatorAccounts[0].address, amountToTransfer), - // [events.transfer] - // ) - // const domainId = '0' // const amountToStake = '100000000000000000000' // const minimumNominatorStake = '1000000000000000000' @@ -306,11 +285,6 @@ describe('Verify staking functions', () => { // await signAndSendTx(sender, await registerOperator(txInput), [events.operatorRegistered]) // await verifyOperatorRegistration(txInput) - // const _balanceSenderEnd = await balance(api, address(sender.address)) - // expect(_balanceSenderEnd.free).toBeLessThan( - // _balanceSenderStart.free - BigInt(amountToStake), - // ) - // await sudo(api, sender, await api.tx.domains.forceStakingEpochTransition(domainId), [events.forceDomainEpochTransition]) // const findOperator = await verifyOperatorRegistrationFinal(txInput) From 2a72986d7fa5071f7eea8516df734ace783fc7a6 Mon Sep 17 00:00:00 2001 From: Marc-Aurele Besner <82244926+marc-aurele-besner@users.noreply.github.com> Date: Thu, 13 Jun 2024 19:11:22 -0400 Subject: [PATCH 19/21] only run consensus test on local node --- packages/auto-consensus/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/auto-consensus/package.json b/packages/auto-consensus/package.json index 72bd59ec..e0f77cfb 100644 --- a/packages/auto-consensus/package.json +++ b/packages/auto-consensus/package.json @@ -6,7 +6,7 @@ "build": "tsc", "clean": "rm -rf dist", "format": "prettier --write \"src/**/*.ts\"", - "test": "jest -i" + "test-local": "jest -i" }, "files": [ "dist", From 114ce362069f978300f474a40bf2699e183f0759 Mon Sep 17 00:00:00 2001 From: Marc-Aurele Besner <82244926+marc-aurele-besner@users.noreply.github.com> Date: Wed, 19 Jun 2024 09:42:12 -0400 Subject: [PATCH 20/21] remove commented tests --- .../auto-consensus/__test__/staking.test.ts | 52 ------------------- 1 file changed, 52 deletions(-) diff --git a/packages/auto-consensus/__test__/staking.test.ts b/packages/auto-consensus/__test__/staking.test.ts index 460d6541..65bd4dcd 100644 --- a/packages/auto-consensus/__test__/staking.test.ts +++ b/packages/auto-consensus/__test__/staking.test.ts @@ -248,58 +248,6 @@ describe('Verify staking functions', () => { ) }, 30000) }) - - // Still unsure if this is testable due to the lock period of 2 days - // describe('Test unlockFunds()', () => { - // test('Check Operator can unlockFunds after registration', async () => { - // const mnemonicOperator = mnemonicGenerate() - // const { api, accounts } = await activateWallet({ - // ...TEST_NETWORK, - // uri: ALICE_URI, - // } as ActivateWalletInput) - // const { accounts: operatorAccounts } = await activateWallet({ - // ...TEST_NETWORK, - // uri: mnemonicOperator, - // } as ActivateWalletInput) - // expect(accounts.length).toBeGreaterThan(0) - // expect(accounts[0].address).toEqual(ALICE_ADDRESS) - - // const sender = accounts[0] - - // const _balanceSenderStart = await balance(api, address(sender.address)) - // expect(_balanceSenderStart.free).toBeGreaterThan(BigInt(0)) - - // const domainId = '0' - // const amountToStake = '100000000000000000000' - // const minimumNominatorStake = '1000000000000000000' - // const nominationTax = '5' - // const txInput = { - // api, - // senderAddress: ALICE_ADDRESS, - // Operator: operatorAccounts[0], - // domainId, - // amountToStake, - // minimumNominatorStake, - // nominationTax, - // } - // await signAndSendTx(sender, await registerOperator(txInput), [events.operatorRegistered]) - // await verifyOperatorRegistration(txInput) - - // await sudo(api, sender, await api.tx.domains.forceStakingEpochTransition(domainId), [events.forceDomainEpochTransition]) - // const findOperator = await verifyOperatorRegistrationFinal(txInput) - - // if (findOperator) { - // await signAndSendTx( - // sender, - // await unlockFunds({ - // api, - // operatorId: findOperator.operatorId, - // }), - // [events.operatorDeRegistered], - // ) - // } - // }, 30000) - // }) } else { test('Staking test only run on localhost', async () => { expect(true).toBeTruthy() From cb28707b22b2c2274449901a994df7ab51f2efa1 Mon Sep 17 00:00:00 2001 From: Marc-Aurele Besner <82244926+marc-aurele-besner@users.noreply.github.com> Date: Wed, 19 Jun 2024 10:51:55 -0400 Subject: [PATCH 21/21] add consensus localhost tests script --- scripts/localhost-run-test.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/localhost-run-test.sh b/scripts/localhost-run-test.sh index 80308a46..66d4d205 100644 --- a/scripts/localhost-run-test.sh +++ b/scripts/localhost-run-test.sh @@ -1,4 +1,5 @@ LOCALHOST="true" export LOCALHOST -yarn run test \ No newline at end of file +yarn run test +yarn workspace @autonomys/auto-consensus run test-local \ No newline at end of file