1234567891011121314151617import {ApiPromise} from '@polkadot/api';18import {IKeyringPair} from '@polkadot/types/types';19import privateKey from './substrate/privateKey';20import usingApi, {submitTransactionAsync} from './substrate/substrate-api';21import waitNewBlocks from './substrate/wait-new-blocks';22import {findUnusedAddresses} from './util/helpers';23import * as cluster from 'cluster';24import os from 'os';252627const FEE = 10n ** 8n;2829let counters: { [key: string]: number } = {};30function increaseCounter(name: string, amount: number) {31 if (!counters[name]) {32 counters[name] = 0;33 }34 counters[name] += amount;35}36function flushCounterToMaster() {37 if (Object.keys(counters).length === 0) {38 return;39 }40 process.send!(counters);41 counters = {};42}4344async function distributeBalance(source: IKeyringPair, api: ApiPromise, totalAmount: bigint, stages: number) {45 const accounts = [source];46 47 const failedAccounts = [0];4849 const finalUserAmount = 2 ** stages - 1;50 accounts.push(...await findUnusedAddresses(api, finalUserAmount));51 52 increaseCounter('requests', finalUserAmount);5354 for (let stage = 0; stage < stages; stage++) {55 const usersWithBalance = 2 ** stage;56 const amount = totalAmount / (2n ** BigInt(stage)) - FEE * BigInt(stage);57 58 const txs = [];59 for (let i = 0; i < usersWithBalance; i++) {60 const newUser = accounts[i + usersWithBalance];61 62 const tx = api.tx.balances.transfer(newUser.address, amount);63 txs.push(submitTransactionAsync(accounts[i], tx).catch(() => {64 failedAccounts.push(i + usersWithBalance);65 increaseCounter('txFailed', 1);66 }));67 increaseCounter('tx', 1);68 }69 await Promise.all(txs);70 }7172 for (const account of failedAccounts.reverse()) {73 accounts.splice(account, 1);74 }75 return accounts;76}7778if (cluster.isMaster) {79 let testDone = false;80 usingApi(async (api) => {81 const prevCounters: { [key: string]: number } = {};82 while (!testDone) {83 for (const name in counters) {84 if (!(name in prevCounters)) {85 prevCounters[name] = 0;86 }87 if(counters[name] === prevCounters[name]) {88 continue;89 }90 console.log(`${name.padEnd(15)} = ${counters[name] - prevCounters[name]}`);91 prevCounters[name] = counters[name];92 }93 await waitNewBlocks(api, 1);94 }95 });96 const waiting: Promise<void>[] = [];97 console.log(`Starting ${os.cpus().length} workers`);98 usingApi(async (api) => {99 const alice = privateKey('//Alice');100 for (const id in os.cpus()) {101 const WORKER_NAME = `//LoadWorker${id}_${Date.now()}`;102 const workerAccount = privateKey(WORKER_NAME);103 const tx = api.tx.balances.transfer(workerAccount.address, 400n * 10n ** 23n);104 await submitTransactionAsync(alice, tx);105106 const worker = cluster.fork({107 WORKER_NAME,108 STAGES: id + 2,109 });110 worker.on('message', msg => {111 for (const key in msg) {112 increaseCounter(key, msg[key]);113 }114 });115 waiting.push(new Promise(res => worker.on('exit', res)));116 }117 await Promise.all(waiting);118 testDone = true;119 });120} else {121 increaseCounter('startedWorkers', 1);122 usingApi(async (api) => {123 await distributeBalance(privateKey(process.env.WORKER_NAME as string), api, 400n * 10n ** 22n, 10);124 });125 const interval = setInterval(() => {126 flushCounterToMaster();127 }, 100);128 interval.unref();129}