git.delta.rocks / unique-network / refs/commits / 074bf74bed72

difftreelog

source

tests/src/transfer.nload.ts5.0 KiBsourcehistory
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {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 * as cluster from 'cluster';22import os from 'os';2324async function findUnusedAddress(api: ApiPromise, privateKeyWrapper: (account: string) => IKeyringPair, seedAddition = ''): Promise<IKeyringPair> {25  let bal = 0n;26  let unused;27  do {28    const randomSeed = 'seed' + Math.floor(Math.random() * Math.floor(10000)) + seedAddition;29    unused = privateKeyWrapper(`//${randomSeed}`);30    bal = (await api.query.system.account(unused.address)).data.free.toBigInt();31  } while (bal !== 0n);32  return unused;33}3435function findUnusedAddresses(api: ApiPromise, privateKeyWrapper: (account: string) => IKeyringPair, amount: number): Promise<IKeyringPair[]> {36  return Promise.all(new Array(amount).fill(null).map(() => findUnusedAddress(api, privateKeyWrapper, '_' + Date.now())));37}3839// Innacurate transfer fee40const FEE = 10n ** 8n;4142let counters: { [key: string]: number } = {};43function increaseCounter(name: string, amount: number) {44  if (!counters[name]) {45    counters[name] = 0;46  }47  counters[name] += amount;48}49function flushCounterToMaster() {50  if (Object.keys(counters).length === 0) {51    return;52  }53    process.send!(counters);54    counters = {};55}5657async function distributeBalance(source: IKeyringPair, api: ApiPromise, totalAmount: bigint, stages: number) {58  const accounts = [source];59  // we don't need source in output array60  const failedAccounts = [0];6162  const finalUserAmount = 2 ** stages - 1;63  accounts.push(...await findUnusedAddresses(api, finalUserAmount));64  // findUnusedAddresses produces at least 1 request per user65  increaseCounter('requests', finalUserAmount);6667  for (let stage = 0; stage < stages; stage++) {68    const usersWithBalance = 2 ** stage;69    const amount = totalAmount / (2n ** BigInt(stage)) - FEE * BigInt(stage);70    // console.log(`Stage ${stage}/${stages}, ${usersWithBalance} => ${usersWithBalance * 2} = ${amount}`);71    const txs = [];72    for (let i = 0; i < usersWithBalance; i++) {73      const newUser = accounts[i + usersWithBalance];74      // console.log(`${accounts[i].address} => ${newUser.address} = ${amountToSplit}`);75      const tx = api.tx.balances.transfer(newUser.address, amount);76      txs.push(submitTransactionAsync(accounts[i], tx).catch(() => {77        failedAccounts.push(i + usersWithBalance);78        increaseCounter('txFailed', 1);79      }));80      increaseCounter('tx', 1);81    }82    await Promise.all(txs);83  }8485  for (const account of failedAccounts.reverse()) {86    accounts.splice(account, 1);87  }88  return accounts;89}9091if (cluster.isMaster) {92  let testDone = false;93  usingApi(async (api) => {94    const prevCounters: { [key: string]: number } = {};95    while (!testDone) {96      for (const name in counters) {97        if (!(name in prevCounters)) {98          prevCounters[name] = 0;99        }100        if(counters[name] === prevCounters[name]) {101          continue;102        }103        console.log(`${name.padEnd(15)} = ${counters[name] - prevCounters[name]}`);104        prevCounters[name] = counters[name];105      }106      await waitNewBlocks(api, 1);107    }108  });109  const waiting: Promise<void>[] = [];110  console.log(`Starting ${os.cpus().length} workers`);111  usingApi(async (api, privateKeyWrapper) => {112    const alice = privateKeyWrapper('//Alice');113    for (const id in os.cpus()) {114      const WORKER_NAME = `//LoadWorker${id}_${Date.now()}`;115      const workerAccount = privateKeyWrapper(WORKER_NAME);116      const tx = api.tx.balances.transfer(workerAccount.address, 400n * 10n ** 23n);117      await submitTransactionAsync(alice, tx);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  usingApi(async (api, privateKeyWrapper) => {136    await distributeBalance(privateKeyWrapper(process.env.WORKER_NAME as string), api, 400n * 10n ** 22n, 10);137  });138  const interval = setInterval(() => {139    flushCounterToMaster();140  }, 100);141  interval.unref();142}