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