From 3bb0aa73b2db5e96d5c2417c2e2466a374025bfe Mon Sep 17 00:00:00 2001 From: cgeek <cem.moreau@gmail.com> Date: Sat, 23 Dec 2017 19:31:49 +0100 Subject: [PATCH] [fix] #1234 Synchronize workers on each PoW task --- app/modules/prover/lib/PowWorker.ts | 95 +++++ app/modules/prover/lib/engine.ts | 4 +- app/modules/prover/lib/permanentProver.ts | 2 +- app/modules/prover/lib/powCluster.ts | 159 ++++---- app/modules/prover/lib/proof.ts | 467 +++++++++++----------- 5 files changed, 404 insertions(+), 323 deletions(-) create mode 100644 app/modules/prover/lib/PowWorker.ts diff --git a/app/modules/prover/lib/PowWorker.ts b/app/modules/prover/lib/PowWorker.ts new file mode 100644 index 000000000..fd225941a --- /dev/null +++ b/app/modules/prover/lib/PowWorker.ts @@ -0,0 +1,95 @@ +import {Querable} from "./permanentProver" + +const querablep = require('querablep') + +/********* + * + * PoW worker + * ---------- + * + * Its model is super simple: we ask him to find a proof, and we can wait for it. + * Eventually, we can tell him to cancel his proof, which makes it answer `null` as proof value. + * + * The worker also provides two properties: + * + * - `worker.online`: a promise which is resolved when the worker gets « online » for the first time + * - `worker.exit`: a promise which is resolved when the worker exits (which occurs when the worker is being closed or killed) + * + ********/ + +export class PowWorker { + + private onlinePromise:Promise<void> + private onlineResolver:()=>void + + private exitPromise:Promise<void> + private exitResolver:()=>void + + private proofPromise:Querable<{ message: { answer:any }}|null> + private proofResolver:(proof:{ message: { answer:any }}|null)=>void + + private messageHandler:((worker:any, msg:any)=>void) + + constructor( + private nodejsWorker:any, + private onPowMessage:(message:any)=>void, + private onlineHandler:()=>void, + private exitHandler:(code:any, signal:any)=>void) { + + // Handle "online" promise + this.onlinePromise = new Promise(res => this.onlineResolver = res) + nodejsWorker.on('online', () => { + this.onlineHandler() + this.onlineResolver() + }) + + // Handle "exit" promise + this.exitPromise = new Promise(res => this.exitResolver = res) + nodejsWorker.on('exit', (code:any, signal:any) => { + this.exitHandler(code, signal) + this.exitResolver() + }) + + nodejsWorker.on('message', (message:any) => { + if (message) { + this.onPowMessage(message) + } + if (this.proofPromise && message.uuid && !this.proofPromise.isResolved() && this.proofResolver) { + const result:{ message: { answer:any }}|null = message ? { message } : null + this.proofResolver(result) + } + }) + } + + get online() { + return this.onlinePromise + } + + get exited() { + return this.exitPromise + } + + get pid() { + return this.nodejsWorker.process.pid + } + + askProof(commandMessage:{ uuid:string, command:string, value:any }) { + this.proofPromise = querablep(new Promise<{ message: { answer:any }}|null>(res => this.proofResolver = res)) + this.nodejsWorker.send(commandMessage) + return this.proofPromise + } + + sendConf(confMessage:{ command:string, value:any }) { + this.nodejsWorker.send(confMessage) + } + + sendCancel() { + this.nodejsWorker.send({ + command: 'cancel' + }) + } + + kill() { + this.nodejsWorker.kill() + } +} \ No newline at end of file diff --git a/app/modules/prover/lib/engine.ts b/app/modules/prover/lib/engine.ts index 2537c5921..6ab1ca645 100644 --- a/app/modules/prover/lib/engine.ts +++ b/app/modules/prover/lib/engine.ts @@ -33,9 +33,7 @@ export class PowEngine { } async prove(stuff:any) { - if (this.cluster.hasProofPending) { - await this.cluster.cancelWork() - } + await this.cluster.cancelWork() return await this.cluster.proveByWorkers(stuff) } diff --git a/app/modules/prover/lib/permanentProver.ts b/app/modules/prover/lib/permanentProver.ts index 7ef75a358..d5c4fefed 100644 --- a/app/modules/prover/lib/permanentProver.ts +++ b/app/modules/prover/lib/permanentProver.ts @@ -9,7 +9,7 @@ import {Server} from "../../../../server" const querablep = require('querablep'); -interface Querable<T> extends Promise<T> { +export interface Querable<T> extends Promise<T> { isFulfilled(): boolean isResolved(): boolean isRejected(): boolean diff --git a/app/modules/prover/lib/powCluster.ts b/app/modules/prover/lib/powCluster.ts index cd9c7c239..d0c64c4e4 100644 --- a/app/modules/prover/lib/powCluster.ts +++ b/app/modules/prover/lib/powCluster.ts @@ -1,5 +1,7 @@ import {ConfDTO} from "../../../lib/dto/ConfDTO" import {ProverConstants} from "./constants" +import {createPowWorker} from "./proof" +import {PowWorker} from "./PowWorker" const _ = require('underscore') const nuuid = require('node-uuid'); @@ -9,6 +11,13 @@ const querablep = require('querablep') let clusterId = 0 cluster.setMaxListeners(3) +export interface SlaveWorker { + worker:PowWorker, + index:number, + online:Promise<void>, + nonceBeginning:number +} + /** * Cluster controller, handles the messages between the main program and the PoW cluster. */ @@ -18,15 +27,14 @@ export class Master { clusterId:number currentPromise:any|null = null - slaves:any[] = [] - slavesMap:any = {} + slaves:SlaveWorker[] = [] + slavesMap:{ + [k:number]: SlaveWorker|null + } = {} conf:any = {} logger:any onInfoCallback:any workersOnline:Promise<any>[] - private exitHandler: (worker: any, code: any, signal: any) => void - private onlineHandler: (worker: any) => void - private messageHandler: (worker: any, msg: any) => void constructor(private nbCores:number, logger:any) { this.clusterId = clusterId++ @@ -34,53 +42,23 @@ export class Master { this.onInfoMessage = (message:any) => { this.logger.info(`${message.pow.pow} nonce = ${message.pow.block.nonce}`) } - - this.exitHandler = (worker:any, code:any, signal:any) => { - this.logger.info(`worker ${worker.process.pid} died with code ${code} and signal ${signal}`) - } - - this.onlineHandler = (worker:any) => { - // We just listen to the workers of this Master - if (this.slavesMap[worker.id]) { - this.logger.info(`[online] worker c#${this.clusterId}#w#${worker.id}`) - this.slavesMap[worker.id].online.extras.resolve() - worker.send({ - command: 'conf', - value: this.conf - }) - } - } - - this.messageHandler = (worker:any, msg:any) => { - // Message for this cluster - if (this.slavesMap[worker.id]) { - this.onWorkerMessage(worker, msg) - } - } } get nbWorkers() { return this.slaves.length } - get hasProofPending() { - return !!this.currentPromise - } - set onInfoMessage(callback:any) { this.onInfoCallback = callback } - onWorkerMessage(worker:any, message:any) { + onWorkerMessage(workerIndex:number, message:any) { // this.logger.info(`worker#${this.slavesMap[worker.id].index} sent message:${message}`) - if (message.pow && message.pow.pow) { + if (message && message.pow) { this.onInfoCallback && this.onInfoCallback(message) } - if (this.currentPromise && message.uuid === this.currentPromise.extras.uuid && !this.currentPromise.isResolved() && message.answer) { - this.logger.info(`ENGINE c#${this.clusterId}#${this.slavesMap[worker.id].index} HAS FOUND A PROOF #${message.answer.pow.pow}`) - this.currentPromise.extras.resolve(message.answer) - // Stop the slaves' current work - this.cancelWork() + if (this.currentPromise && message.uuid && !this.currentPromise.isResolved() && message.answer) { + this.logger.info(`ENGINE c#${this.clusterId}#${workerIndex} HAS FOUND A PROOF #${message.answer.pow.pow}`) } else if (message.canceled) { this.nbCancels++ } @@ -94,13 +72,26 @@ export class Master { initCluster() { // Setup master cluster.setupMaster({ - exec: __filename + exec: __filename, + execArgv: [] // Do not try to debug forks }) this.slaves = Array.from({ length: this.nbCores }).map((value, index) => { - const worker = cluster.fork() - this.logger.info(`Creating worker c#${this.clusterId}#w#${worker.id}`) - this.slavesMap[worker.id] = { + const nodejsWorker = cluster.fork() + const worker = new PowWorker(nodejsWorker, message => { + this.onWorkerMessage(index, message) + }, () => { + this.logger.info(`[online] worker c#${this.clusterId}#w#${index}`) + worker.sendConf({ + command: 'conf', + value: this.conf + }) + }, (code:any, signal:any) => { + this.logger.info(`worker ${worker.pid} died with code ${code} and signal ${signal}`) + }) + + this.logger.info(`Creating worker c#${this.clusterId}#w#${nodejsWorker.id}`) + const slave = { // The Node.js worker worker, @@ -109,24 +100,16 @@ export class Master { index, // Worker ready - online: (function onlinePromise() { - let resolve - const p = querablep(new Promise(res => resolve = res)) - p.extras = { resolve } - return p - })(), + online: worker.online, // Each worker has his own chunk of possible nonces nonceBeginning: this.nbCores === 1 ? 0 : (index + 1) * ProverConstants.NONCE_RANGE } - return this.slavesMap[worker.id] + this.slavesMap[nodejsWorker.id] = slave + return slave }) - cluster.on('exit', this.exitHandler) - cluster.on('online', this.onlineHandler) - cluster.on('message', this.messageHandler) - - this.workersOnline = this.slaves.map((s:any) => s.online) + this.workersOnline = this.slaves.map((s) => s.online) return Promise.all(this.workersOnline) } @@ -135,7 +118,7 @@ export class Master { this.conf.cpu = conf.cpu || this.conf.cpu this.conf.prefix = this.conf.prefix || conf.prefix this.slaves.forEach(s => { - s.worker.send({ + s.worker.sendConf({ command: 'conf', value: this.conf }) @@ -143,41 +126,26 @@ export class Master { return Promise.resolve(_.clone(conf)) } - cancelWork() { - this.logger.info(`Cancelling the work on PoW cluster of %s slaves`, this.slaves.length) + private cancelWorkersWork() { this.slaves.forEach(s => { - s.worker.send({ - command: 'cancel' - }) + s.worker.sendCancel() }) + } - // Eventually force the end of current promise - if (this.currentPromise && !this.currentPromise.isFulfilled()) { - this.currentPromise.extras.resolve(null) - } - + async cancelWork() { + this.cancelWorkersWork() + const workEnded = this.currentPromise // Current promise is done this.currentPromise = null - - return Promise.resolve() - } - - newPromise(uuid:string) { - let resolve - const p = querablep(new Promise(res => resolve = res)) - p.extras = { resolve, uuid } - return p + return await workEnded } async shutDownWorkers() { if (this.workersOnline) { await Promise.all(this.workersOnline) - await Promise.all(this.slaves.map(async (s:any) => { + await Promise.all(this.slaves.map(async (s) => { s.worker.kill() })) - cluster.removeListener('exit', this.exitHandler) - cluster.removeListener('online', this.onlineHandler) - cluster.removeListener('message', this.messageHandler) } this.slaves = [] } @@ -191,9 +159,7 @@ export class Master { // Register the new proof uuid const uuid = nuuid.v4() - this.currentPromise = this.newPromise(uuid) - - return (async () => { + this.currentPromise = querablep((async () => { await Promise.all(this.workersOnline) if (!this.currentPromise) { @@ -202,8 +168,8 @@ export class Master { } // Start the salves' job - this.slaves.forEach((s:any, index) => { - s.worker.send({ + const asks = this.slaves.map(async (s, index) => { + const proof = await s.worker.askProof({ uuid, command: 'newPoW', value: { @@ -222,10 +188,29 @@ export class Master { } } }) + this.logger.info(`[done] worker c#${this.clusterId}#w#${index}`) + return { + workerID: index, + proof + } }) - return await this.currentPromise - })() + // Find a proof + const result = await Promise.race(asks) + this.cancelWorkersWork() + // Wait for all workers to have stopped looking for a proof + await Promise.all(asks) + + if (!result.proof || !result.proof.message.answer) { + this.logger.info('No engine found the proof. It was probably cancelled.') + return null + } else { + this.logger.info(`ENGINE c#${this.clusterId}#${result.workerID} HAS FOUND A PROOF #${result.proof.message.answer.pow.pow}`) + return result.proof.message.answer + } + })()) + + return this.currentPromise } static defaultLogger() { @@ -250,5 +235,5 @@ if (cluster.isMaster) { process.exit(0) }); - require('./proof') + createPowWorker() } diff --git a/app/modules/prover/lib/proof.ts b/app/modules/prover/lib/proof.ts index 407c7a965..546bf676a 100644 --- a/app/modules/prover/lib/proof.ts +++ b/app/modules/prover/lib/proof.ts @@ -11,295 +11,298 @@ import {ProcessCpuProfiler} from "../../../ProcessCpuProfiler" const moment = require('moment'); const querablep = require('querablep'); -let computing = querablep(Promise.resolve(null)); -let askedStop = false; +export function createPowWorker() { -// By default, we do not prefix the PoW by any number -let prefix = 0; + let computing = querablep(Promise.resolve(null)); + let askedStop = false; -let signatureFunc:any, lastSecret:any, currentCPU = 1; +// By default, we do not prefix the PoW by any number + let prefix = 0; -process.on('uncaughtException', (err:any) => { - console.error(err.stack || Error(err)) - if (process.send) { - process.send({error: err}); - } else { - throw Error('process.send() is not defined') - } -}); + let signatureFunc:any, lastSecret:any, currentCPU = 1; -process.on('unhandledRejection', () => { - process.exit() -}) + process.on('uncaughtException', (err:any) => { + console.error(err.stack || Error(err)) + if (process.send) { + process.send({error: err}); + } else { + throw Error('process.send() is not defined') + } + }); -process.on('message', async (message) => { + process.on('unhandledRejection', () => { + process.exit() + }) - switch (message.command) { + process.on('message', async (message) => { - case 'newPoW': - (async () => { - askedStop = true + switch (message.command) { - // Very important: do not await if the computation is already done, to keep the lock on JS engine - if (!computing.isFulfilled()) { - await computing; - } + case 'newPoW': + (async () => { + askedStop = true - const res = await beginNewProofOfWork(message.value); - answer(message, res); - })() - break; + // Very important: do not await if the computation is already done, to keep the lock on JS engine + if (!computing.isFulfilled()) { + await computing; + } - case 'cancel': - if (!computing.isFulfilled()) { - askedStop = true; - } - break; + const res = await beginNewProofOfWork(message.value); + answer(message, res); + })() + break; - case 'conf': - if (message.value.cpu !== undefined) { - currentCPU = message.value.cpu - } - if (message.value.prefix !== undefined) { - prefix = message.value.prefix - } - answer(message, { currentCPU, prefix }); - break; - } + case 'cancel': + if (!computing.isFulfilled()) { + askedStop = true; + } + break; -}) - -function beginNewProofOfWork(stuff:any) { - askedStop = false; - computing = querablep((async () => { - - /***************** - * PREPARE POW STUFF - ****************/ - - let nonce = 0; - const maxDuration = stuff.maxDuration || 1000 - const conf = stuff.conf; - const block = stuff.block; - const nonceBeginning = stuff.nonceBeginning; - const nbZeros = stuff.zeros; - const pair = stuff.pair; - const forcedTime = stuff.forcedTime; - currentCPU = conf.cpu || ProverConstants.DEFAULT_CPU; - prefix = parseInt(conf.prefix || prefix) - if (prefix && prefix < ProverConstants.NONCE_RANGE) { - prefix *= 100 * ProverConstants.NONCE_RANGE - } - const highMark = stuff.highMark; - let sigFunc = null; - if (signatureFunc && lastSecret === pair.sec) { - sigFunc = signatureFunc; - } - else { - lastSecret = pair.sec; - sigFunc = (msg:string) => KeyGen(pair.pub, pair.sec).signSync(msg) + case 'conf': + if (message.value.cpu !== undefined) { + currentCPU = message.value.cpu + } + if (message.value.prefix !== undefined) { + prefix = message.value.prefix + } + answer(message, { currentCPU, prefix }); + break; } - signatureFunc = sigFunc; - let pow = "", sig = "", raw = ""; - /***************** - * GO! - ****************/ + }) + + function beginNewProofOfWork(stuff:any) { + askedStop = false; + computing = querablep((async () => { - let pausePeriod = 1; - let testsCount = 0; - let found = false; - let turn = 0; - const profiler = new ProcessCpuProfiler(100) - let cpuUsage = profiler.cpuUsageOverLastMilliseconds(1) - // We limit the number of tests according to CPU usage - let testsPerRound = stuff.initialTestsPerRound || 1 - let turnDuration = 20 // We initially goes quickly to the max speed = 50 reevaluations per second (1000 / 20) + /***************** + * PREPARE POW STUFF + ****************/ - while (!found && !askedStop) { + let nonce = 0; + const maxDuration = stuff.maxDuration || 1000 + const conf = stuff.conf; + const block = stuff.block; + const nonceBeginning = stuff.nonceBeginning; + const nbZeros = stuff.zeros; + const pair = stuff.pair; + const forcedTime = stuff.forcedTime; + currentCPU = conf.cpu || ProverConstants.DEFAULT_CPU; + prefix = parseInt(conf.prefix || prefix) + if (prefix && prefix < ProverConstants.NONCE_RANGE) { + prefix *= 100 * ProverConstants.NONCE_RANGE + } + const highMark = stuff.highMark; + let sigFunc = null; + if (signatureFunc && lastSecret === pair.sec) { + sigFunc = signatureFunc; + } + else { + lastSecret = pair.sec; + sigFunc = (msg:string) => KeyGen(pair.pub, pair.sec).signSync(msg) + } + signatureFunc = sigFunc; + let pow = "", sig = "", raw = ""; /***************** - * A TURN ~ 100ms + * GO! ****************/ - await Promise.race([ + let pausePeriod = 1; + let testsCount = 0; + let found = false; + let turn = 0; + const profiler = new ProcessCpuProfiler(100) + let cpuUsage = profiler.cpuUsageOverLastMilliseconds(1) + // We limit the number of tests according to CPU usage + let testsPerRound = stuff.initialTestsPerRound || 1 + let turnDuration = 20 // We initially goes quickly to the max speed = 50 reevaluations per second (1000 / 20) - // I. Stop the turn if it exceeds `turnDuration` ms - countDown(turnDuration), + while (!found && !askedStop) { - // II. Process the turn's PoW - (async () => { + /***************** + * A TURN ~ 100ms + ****************/ - // Prove - let i = 0; - const thisTurn = turn; + await Promise.race([ - // Time is updated regularly during the proof - block.time = getBlockTime(block, conf, forcedTime) - if (block.number === 0) { - block.medianTime = block.time - } - block.inner_hash = getBlockInnerHash(block); + // I. Stop the turn if it exceeds `turnDuration` ms + countDown(turnDuration), - /***************** - * Iterations of a turn - ****************/ + // II. Process the turn's PoW + (async () => { - while(!found && i < testsPerRound && thisTurn === turn && !askedStop) { + // Prove + let i = 0; + const thisTurn = turn; - // Nonce change (what makes the PoW change if the time field remains the same) - nonce++ + // Time is updated regularly during the proof + block.time = getBlockTime(block, conf, forcedTime) + if (block.number === 0) { + block.medianTime = block.time + } + block.inner_hash = getBlockInnerHash(block); /***************** - * A PROOF OF WORK + * Iterations of a turn ****************/ - // The final nonce is composed of 3 parts - block.nonce = prefix + nonceBeginning + nonce - raw = dos2unix("InnerHash: " + block.inner_hash + "\nNonce: " + block.nonce + "\n") - sig = dos2unix(sigFunc(raw)) - pow = hashf("InnerHash: " + block.inner_hash + "\nNonce: " + block.nonce + "\n" + sig + "\n").toUpperCase() + while(!found && i < testsPerRound && thisTurn === turn && !askedStop) { - /***************** - * Check the POW result - ****************/ + // Nonce change (what makes the PoW change if the time field remains the same) + nonce++ - let j = 0, charOK = true; - while (j < nbZeros && charOK) { - charOK = pow[j] === '0'; - j++; - } - if (charOK) { - found = !!(pow[nbZeros].match(new RegExp('[0-' + highMark + ']'))) - } - if (!found && nbZeros > 0 && j - 1 >= ProverConstants.POW_MINIMAL_TO_SHOW) { - pSend({ pow: { pow: pow, block: block, nbZeros: nbZeros }}); - } + /***************** + * A PROOF OF WORK + ****************/ - /***************** - * - Update local vars - * - Allow to receive stop signal - ****************/ + // The final nonce is composed of 3 parts + block.nonce = prefix + nonceBeginning + nonce + raw = dos2unix("InnerHash: " + block.inner_hash + "\nNonce: " + block.nonce + "\n") + sig = dos2unix(sigFunc(raw)) + pow = hashf("InnerHash: " + block.inner_hash + "\nNonce: " + block.nonce + "\n" + sig + "\n").toUpperCase() - if (!found && !askedStop) { - i++; - testsCount++; - if (i % pausePeriod === 0) { - await countDown(0); // Very low pause, just the time to process eventual end of the turn + /***************** + * Check the POW result + ****************/ + + let j = 0, charOK = true; + while (j < nbZeros && charOK) { + charOK = pow[j] === '0'; + j++; + } + if (charOK) { + found = !!(pow[nbZeros].match(new RegExp('[0-' + highMark + ']'))) + } + if (!found && nbZeros > 0 && j - 1 >= ProverConstants.POW_MINIMAL_TO_SHOW) { + pSend({ pow: { pow: pow, block: block, nbZeros: nbZeros }}); } - } - } - /***************** - * Check the POW result - ****************/ - if (!found) { - - // CPU speed recording - if (turn > 0) { - cpuUsage = profiler.cpuUsageOverLastMilliseconds(turnDuration) - if (cpuUsage > currentCPU + 0.005 || cpuUsage < currentCPU - 0.005) { - let powVariationFactor - // powVariationFactor = currentCPU / (cpuUsage || 0.01) / 5 // divide by 2 to avoid extreme responses - if (currentCPU > cpuUsage) { - powVariationFactor = 1.01 - testsPerRound = Math.max(1, Math.ceil(testsPerRound * powVariationFactor)) - } else { - powVariationFactor = 0.99 - testsPerRound = Math.max(1, Math.floor(testsPerRound * powVariationFactor)) + /***************** + * - Update local vars + * - Allow to receive stop signal + ****************/ + + if (!found && !askedStop) { + i++; + testsCount++; + if (i % pausePeriod === 0) { + await countDown(0); // Very low pause, just the time to process eventual end of the turn } - pausePeriod = Math.floor(testsPerRound / ProverConstants.POW_NB_PAUSES_PER_ROUND) } } /***************** - * UNLOAD CPU CHARGE FOR THIS TURN + * Check the POW result ****************/ - // We wait for a maximum time of `turnDuration`. - // This will trigger the end of the turn by the concurrent race I. During that time, the proof.js script - // just does nothing: this gives of a bit of breath to the CPU. Tthe amount of "breath" depends on the "cpu" - // parameter. - await countDown(turnDuration); - } - })() - ]); + if (!found) { + + // CPU speed recording + if (turn > 0) { + cpuUsage = profiler.cpuUsageOverLastMilliseconds(turnDuration) + if (cpuUsage > currentCPU + 0.005 || cpuUsage < currentCPU - 0.005) { + let powVariationFactor + // powVariationFactor = currentCPU / (cpuUsage || 0.01) / 5 // divide by 2 to avoid extreme responses + if (currentCPU > cpuUsage) { + powVariationFactor = 1.01 + testsPerRound = Math.max(1, Math.ceil(testsPerRound * powVariationFactor)) + } else { + powVariationFactor = 0.99 + testsPerRound = Math.max(1, Math.floor(testsPerRound * powVariationFactor)) + } + pausePeriod = Math.floor(testsPerRound / ProverConstants.POW_NB_PAUSES_PER_ROUND) + } + } - // Next turn - turn++ + /***************** + * UNLOAD CPU CHARGE FOR THIS TURN + ****************/ + // We wait for a maximum time of `turnDuration`. + // This will trigger the end of the turn by the concurrent race I. During that time, the proof.js script + // just does nothing: this gives of a bit of breath to the CPU. Tthe amount of "breath" depends on the "cpu" + // parameter. + await countDown(turnDuration); + } + })() + ]); - turnDuration += 1 - turnDuration = Math.min(turnDuration, maxDuration) // Max 1 second per turn - } + // Next turn + turn++ - /***************** - * POW IS OVER - * ----------- - * - * We either have found a valid POW or a stop event has been detected. - ****************/ + turnDuration += 1 + turnDuration = Math.min(turnDuration, maxDuration) // Max 1 second per turn + } - if (askedStop) { + /***************** + * POW IS OVER + * ----------- + * + * We either have found a valid POW or a stop event has been detected. + ****************/ - // PoW stopped - askedStop = false; - pSend({ canceled: true }) - return null + if (askedStop) { - } else { + // PoW stopped + askedStop = false; + pSend({ canceled: true }) + return null - // PoW success - block.hash = pow - block.signature = sig - return { - pow: { - block: block, - testsCount: testsCount, - pow: pow + } else { + + // PoW success + block.hash = pow + block.signature = sig + return { + pow: { + block: block, + testsCount: testsCount, + pow: pow + } } } - } - })()) + })()) - return computing; -} + return computing; + } -function countDown(duration:number) { - return new Promise((resolve) => setTimeout(resolve, duration)); -} + function countDown(duration:number) { + return new Promise((resolve) => setTimeout(resolve, duration)); + } -function getBlockInnerHash(block:DBBlock) { - const raw = rawer.getBlockInnerPart(block); - return hashf(raw) -} + function getBlockInnerHash(block:DBBlock) { + const raw = rawer.getBlockInnerPart(block); + return hashf(raw) + } -function getBlockTime (block:DBBlock, conf:ConfDTO, forcedTime:number|null) { - if (forcedTime) { - return forcedTime; + function getBlockTime (block:DBBlock, conf:ConfDTO, forcedTime:number|null) { + if (forcedTime) { + return forcedTime; + } + const now = moment.utc().unix(); + const maxAcceleration = LOCAL_RULES_HELPERS.maxAcceleration(conf); + const timeoffset = block.number >= conf.medianTimeBlocks ? 0 : conf.rootoffset || 0; + const medianTime = block.medianTime; + const upperBound = block.number === 0 ? medianTime : Math.min(medianTime + maxAcceleration, now - timeoffset); + return Math.max(medianTime, upperBound); } - const now = moment.utc().unix(); - const maxAcceleration = LOCAL_RULES_HELPERS.maxAcceleration(conf); - const timeoffset = block.number >= conf.medianTimeBlocks ? 0 : conf.rootoffset || 0; - const medianTime = block.medianTime; - const upperBound = block.number === 0 ? medianTime : Math.min(medianTime + maxAcceleration, now - timeoffset); - return Math.max(medianTime, upperBound); -} -function answer(message:any, theAnswer:any) { - return pSend({ - uuid: message.uuid, - answer: theAnswer - }) -} + function answer(message:any, theAnswer:any) { + return pSend({ + uuid: message.uuid, + answer: theAnswer + }) + } -function pSend(stuff:any) { - return new Promise(function (resolve, reject) { - if (process.send) { - process.send(stuff, function (error:any) { - !error && resolve(); - error && reject(); - }) - } else { - reject('process.send() is not defined') - } - }); + function pSend(stuff:any) { + return new Promise(function (resolve, reject) { + if (process.send) { + process.send(stuff, function (error:any) { + !error && resolve(); + error && reject(); + }) + } else { + reject('process.send() is not defined') + } + }); + } } -- GitLab