difftreelog
Fix transfer.nload script
in: master
1 file changed
tests/src/transfer.nload.tsdiffbeforeafterboth1// 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}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 os from 'os';18import {IKeyringPair} from '@polkadot/types/types';19import {usingPlaygrounds} from './util';20import {UniqueHelper} from './util/playgrounds/unique';21import * as notReallyCluster from 'cluster'; // https://github.com/nodejs/node/issues/42271#issuecomment-106341534622const cluster = notReallyCluster as unknown as notReallyCluster.Cluster;2324async function findUnusedAddress(helper: UniqueHelper, privateKey: (account: string) => Promise<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 = await privateKey(`//${randomSeed}`);30 bal = await helper.balance.getSubstrate(unused.address);31 } while (bal !== 0n);32 return unused;33}3435function findUnusedAddresses(helper: UniqueHelper, privateKey: (account: string) => Promise<IKeyringPair>, amount: number): Promise<IKeyringPair[]> {36 return Promise.all(new Array(amount).fill(null).map(() => findUnusedAddress(helper, privateKey, '_' + 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, helper: UniqueHelper, privateKey: (account: string) => Promise<IKeyringPair>, 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(helper, privateKey, 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: Promise<boolean | void>[] = [];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 = helper.balance.transferToSubstrate(accounts[i], newUser.address, amount);76 txs.push(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 usingPlaygrounds(async (helper) => {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 helper.wait.newBlocks(1);107 }108 });109 const waiting: Promise<void>[] = [];110 console.log(`Starting ${os.cpus().length} workers`);111 usingPlaygrounds(async (helper, privateKey) => {112 const alice = await privateKey('//Alice');113 for (const id in os.cpus()) {114 const WORKER_NAME = `//LoadWorker${id}_${Date.now()}`;115 const workerAccount = await privateKey(WORKER_NAME);116 await helper.balance.transferToSubstrate(alice, workerAccount.address, 400n * 10n ** 23n);117118 const worker = cluster.fork({119 WORKER_NAME,120 STAGES: id + 2,121 });122 worker.on('message', msg => {123 for (const key in msg) {124 increaseCounter(key, msg[key]);125 }126 });127 waiting.push(new Promise(res => worker.on('exit', res)));128 }129 await Promise.all(waiting);130 testDone = true;131 });132} else {133 increaseCounter('startedWorkers', 1);134 usingPlaygrounds(async (helper, privateKey) => {135 await distributeBalance(await privateKey(process.env.WORKER_NAME as string), helper, privateKey, 400n * 10n ** 22n, 10);136 });137 const interval = setInterval(() => {138 flushCounterToMaster();139 }, 100);140 interval.unref();141}