123456789101112131415161718import os from 'os';19import type {IKeyringPair} from '@polkadot/types/types';20import {usingPlaygrounds} from '@unique/tests/util/index.js';21import {UniqueHelper} from '@unique/playgrounds/unique.js';22import * as notReallyCluster from 'cluster'; 23const cluster = notReallyCluster as unknown as notReallyCluster.Cluster;2425async function findUnusedAddress(helper: UniqueHelper, privateKey: (account: string) => Promise<IKeyringPair>, seedAddition = ''): Promise<IKeyringPair> {26 let bal = 0n;27 let unused;28 do {29 const randomSeed = 'seed' + Math.floor(Math.random() * Math.floor(10000)) + seedAddition;30 unused = await privateKey(`//${randomSeed}`);31 bal = await helper.balance.getSubstrate(unused.address);32 } while(bal !== 0n);33 return unused;34}3536function findUnusedAddresses(helper: UniqueHelper, privateKey: (account: string) => Promise<IKeyringPair>, amount: number): Promise<IKeyringPair[]> {37 return Promise.all(new Array(amount).fill(null).map(() => findUnusedAddress(helper, privateKey, '_' + Date.now())));38}394041const FEE = 10n ** 8n;4243let counters: { [key: string]: number } = {};44function increaseCounter(name: string, amount: number) {45 if(!counters[name]) {46 counters[name] = 0;47 }48 counters[name] += amount;49}50function flushCounterToMaster() {51 if(Object.keys(counters).length === 0) {52 return;53 }54 process.send!(counters);55 counters = {};56}5758async function distributeBalance(source: IKeyringPair, helper: UniqueHelper, privateKey: (account: string) => Promise<IKeyringPair>, totalAmount: bigint, stages: number) {59 const accounts = [source];60 61 const failedAccounts = [0];6263 const finalUserAmount = 2 ** stages - 1;64 accounts.push(...await findUnusedAddresses(helper, privateKey, finalUserAmount));65 66 increaseCounter('requests', finalUserAmount);6768 for(let stage = 0; stage < stages; stage++) {69 const usersWithBalance = 2 ** stage;70 const amount = totalAmount / (2n ** BigInt(stage)) - FEE * BigInt(stage);71 72 const txs: Promise<boolean | void>[] = [];73 for(let i = 0; i < usersWithBalance; i++) {74 const newUser = accounts[i + usersWithBalance];75 76 const tx = helper.balance.transferToSubstrate(accounts[i], newUser.address, amount);77 txs.push(tx.catch(() => {78 failedAccounts.push(i + usersWithBalance);79 increaseCounter('txFailed', 1);80 }));81 increaseCounter('tx', 1);82 }83 await Promise.all(txs);84 }8586 for(const account of failedAccounts.reverse()) {87 accounts.splice(account, 1);88 }89 return accounts;90}9192if(cluster.isMaster) {93 let testDone = false;94 usingPlaygrounds(async (helper) => {95 const prevCounters: { [key: string]: number } = {};96 while(!testDone) {97 for(const name in counters) {98 if(!(name in prevCounters)) {99 prevCounters[name] = 0;100 }101 if(counters[name] === prevCounters[name]) {102 continue;103 }104 console.log(`${name.padEnd(15)} = ${counters[name] - prevCounters[name]}`);105 prevCounters[name] = counters[name];106 }107 await helper.wait.newBlocks(1);108 }109 });110 const waiting: Promise<void>[] = [];111 console.log(`Starting ${os.cpus().length} workers`);112 usingPlaygrounds(async (helper, privateKey) => {113 const alice = await privateKey('//Alice');114 for(const id in os.cpus()) {115 const WORKER_NAME = `//LoadWorker${id}_${Date.now()}`;116 const workerAccount = await privateKey(WORKER_NAME);117 await helper.balance.transferToSubstrate(alice, workerAccount.address, 400n * 10n ** 23n);118119 const worker = cluster.fork({120 WORKER_NAME,121 STAGES: id + 2,122 });123 worker.on('message', msg => {124 for(const key in msg) {125 increaseCounter(key, msg[key]);126 }127 });128 waiting.push(new Promise(res => worker.on('exit', res)));129 }130 await Promise.all(waiting);131 testDone = true;132 });133} else {134 increaseCounter('startedWorkers', 1);135 usingPlaygrounds(async (helper, privateKey) => {136 await distributeBalance(await privateKey(process.env.WORKER_NAME as string), helper, privateKey, 400n * 10n ** 22n, 10);137 });138 const interval = setInterval(() => {139 flushCounterToMaster();140 }, 100);141 interval.unref();142}