From aaee365f64849825f77b705151d9ff7a732df119 Mon Sep 17 00:00:00 2001 From: librelois <elois@ifee.fr> Date: Mon, 11 May 2020 23:33:35 +0200 Subject: [PATCH] [feat] oxyde-pow: refactor duniter prover to use rust cluster --- app/lib/constants.ts | 1 - app/lib/dto/ConfDTO.ts | 9 +- app/lib/dto/TransactionDTO.ts | 1 - app/lib/wizard.ts | 6 +- app/modules/prover/index.ts | 16 +- app/modules/prover/lib/blockGenerator.ts | 57 ++- app/modules/prover/lib/blockProver.ts | 202 +++------ app/modules/prover/lib/constants.ts | 5 +- app/modules/prover/lib/engine.ts | 68 --- app/modules/prover/lib/permanentProver.ts | 374 +++++++---------- app/modules/prover/lib/powCluster.ts | 288 +++++-------- app/modules/prover/lib/proof.ts | 397 ------------------ app/modules/prover/lib/prover.ts | 2 +- neon/lib/pow.ts | 6 +- server.ts | 1 - test/fast/prover/prover-pow-1-cluster.ts | 69 +-- test/fast/prover/prover-pow-2-engine.ts | 70 ++- test/fast/prover/prover-pow-3-prover.ts | 73 ++-- .../block-generation/start-generate-blocks.ts | 4 - test/integration/branches/branches_revert2.ts | 6 +- .../coming-back-with-less-than-sigqty.ts | 2 +- .../fork-resolution/register-fork-blocks.ts | 28 +- test/integration/membership_chainability.ts | 5 +- .../proof-of-work/continuous-proof.ts | 112 +++-- .../proof-of-work/proof-of-work.ts | 15 +- test/integration/tools/shutdown-engine.ts | 3 +- test/integration/tools/toolbox.ts | 8 +- .../transactions/transactions-test.ts | 6 +- test/integration/wot/wotb.ts | 116 ++--- 29 files changed, 624 insertions(+), 1326 deletions(-) delete mode 100644 app/modules/prover/lib/engine.ts delete mode 100644 app/modules/prover/lib/proof.ts diff --git a/app/lib/constants.ts b/app/lib/constants.ts index fc3d9a200..c18efccf7 100644 --- a/app/lib/constants.ts +++ b/app/lib/constants.ts @@ -268,7 +268,6 @@ module.exports = { X_PERCENT: 0.9, PERCENTROT: 2 / 3, BLOCKSROT: 20, - POWDELAY: 0, AVGGENTIME: 16 * 60, DTDIFFEVAL: 10, MEDIANTIMEBLOCKS: 20, diff --git a/app/lib/dto/ConfDTO.ts b/app/lib/dto/ConfDTO.ts index 7d5d82474..13bf245d2 100644 --- a/app/lib/dto/ConfDTO.ts +++ b/app/lib/dto/ConfDTO.ts @@ -129,8 +129,9 @@ export class ConfDTO public upInterval: number, public cpu: number, public nbCores: number, + public ecoPow: boolean, public prefix: number, - public powSecurityRetryDelay: number, + public powMaxDuration: number, public powMaxHandicap: number, public c: number, public dt: number, @@ -149,7 +150,6 @@ export class ConfDTO public sigStock: number, public xpercent: number, public percentRot: number, - public powDelay: number, public avgGenTime: number, public medianTimeBlocks: number, public httplogs: boolean, @@ -215,6 +215,7 @@ export class ConfDTO 3600 * 1000, constants.PROOF_OF_WORK.DEFAULT.CPU, 1, + false, constants.PROOF_OF_WORK.DEFAULT.PREFIX, 0, 0, @@ -235,7 +236,6 @@ export class ConfDTO constants.CONTRACT.DEFAULT.SIGSTOCK, constants.CONTRACT.DEFAULT.X_PERCENT, constants.CONTRACT.DEFAULT.PERCENTROT, - constants.CONTRACT.DEFAULT.POWDELAY, constants.CONTRACT.DEFAULT.AVGGENTIME, constants.CONTRACT.DEFAULT.MEDIANTIMEBLOCKS, false, @@ -271,7 +271,7 @@ export class ConfDTO } static defaultConf() { - /*return new ConfDTO("", "", [], [], 0, 3600 * 1000, constants.PROOF_OF_WORK.DEFAULT.CPU, 1, constants.PROOF_OF_WORK.DEFAULT.PREFIX, 0, 0, constants.CONTRACT.DEFAULT.C, constants.CONTRACT.DEFAULT.DT, constants.CONTRACT.DEFAULT.DT_REEVAL, 0, constants.CONTRACT.DEFAULT.UD0, 0, 0, constants.CONTRACT.DEFAULT.STEPMAX, constants.CONTRACT.DEFAULT.SIGPERIOD, 0, constants.CONTRACT.DEFAULT.SIGVALIDITY, constants.CONTRACT.DEFAULT.MSVALIDITY, constants.CONTRACT.DEFAULT.SIGQTY, constants.CONTRACT.DEFAULT.SIGSTOCK, constants.CONTRACT.DEFAULT.X_PERCENT, constants.CONTRACT.DEFAULT.PERCENTROT, constants.CONTRACT.DEFAULT.POWDELAY, constants.CONTRACT.DEFAULT.AVGGENTIME, constants.CONTRACT.DEFAULT.MEDIANTIMEBLOCKS, false, 3000, false, constants.BRANCHES.DEFAULT_WINDOW_SIZE, constants.CONTRACT.DEFAULT.IDTYWINDOW, constants.CONTRACT.DEFAULT.MSWINDOW, constants.CONTRACT.DEFAULT.SIGWINDOW, 0, { pub:'', sec:'' }, null, "", "", 0, "", "", "", "", 0, "", "", null, false, "", true, true)*/ + /*return new ConfDTO("", "", [], [], 0, 3600 * 1000, constants.PROOF_OF_WORK.DEFAULT.CPU, 1, constants.PROOF_OF_WORK.DEFAULT.PREFIX, 0, 0, constants.CONTRACT.DEFAULT.C, constants.CONTRACT.DEFAULT.DT, constants.CONTRACT.DEFAULT.DT_REEVAL, 0, constants.CONTRACT.DEFAULT.UD0, 0, 0, constants.CONTRACT.DEFAULT.STEPMAX, constants.CONTRACT.DEFAULT.SIGPERIOD, 0, constants.CONTRACT.DEFAULT.SIGVALIDITY, constants.CONTRACT.DEFAULT.MSVALIDITY, constants.CONTRACT.DEFAULT.SIGQTY, constants.CONTRACT.DEFAULT.SIGSTOCK, constants.CONTRACT.DEFAULT.X_PERCENT, constants.CONTRACT.DEFAULT.PERCENTROT, constants.CONTRACT.DEFAULT.AVGGENTIME, constants.CONTRACT.DEFAULT.MEDIANTIMEBLOCKS, false, 3000, false, constants.BRANCHES.DEFAULT_WINDOW_SIZE, constants.CONTRACT.DEFAULT.IDTYWINDOW, constants.CONTRACT.DEFAULT.MSWINDOW, constants.CONTRACT.DEFAULT.SIGWINDOW, 0, { pub:'', sec:'' }, null, "", "", 0, "", "", "", "", 0, "", "", null, false, "", true, true)*/ return { currency: null, endpoints: [], @@ -289,7 +289,6 @@ export class ConfDTO sigQty: constants.CONTRACT.DEFAULT.SIGQTY, xpercent: constants.CONTRACT.DEFAULT.X_PERCENT, percentRot: constants.CONTRACT.DEFAULT.PERCENTROT, - powDelay: constants.CONTRACT.DEFAULT.POWDELAY, avgGenTime: constants.CONTRACT.DEFAULT.AVGGENTIME, dtDiffEval: constants.CONTRACT.DEFAULT.DTDIFFEVAL, medianTimeBlocks: constants.CONTRACT.DEFAULT.MEDIANTIMEBLOCKS, diff --git a/app/lib/dto/TransactionDTO.ts b/app/lib/dto/TransactionDTO.ts index 71dfa660f..5cae0d764 100644 --- a/app/lib/dto/TransactionDTO.ts +++ b/app/lib/dto/TransactionDTO.ts @@ -263,7 +263,6 @@ export class TransactionDTO implements Cloneable { if (dubp_version >= 12) { sigResult.sigs[i].ok = verify(raw, sig, pub); } else { - // TODO ESZ list all invalid transactions sigResult.sigs[i].ok = verify(raw, sig, pub); } matching = sigResult.sigs[i].ok; diff --git a/app/lib/wizard.ts b/app/lib/wizard.ts index 1c38b02e1..e7863adc1 100644 --- a/app/lib/wizard.ts +++ b/app/lib/wizard.ts @@ -167,9 +167,9 @@ const tasks: any = { async.waterfall( [ function (next: any) { - simpleInteger( - "Start computation of a new block if none received since (seconds)", - "powDelay", + simpleFloat( + "Proportion of the cpu dedicated to the pow (between 0 and 1)", + "cpu", conf, next ); diff --git a/app/modules/prover/index.ts b/app/modules/prover/index.ts index 8bdca8b55..d190659e7 100644 --- a/app/modules/prover/index.ts +++ b/app/modules/prover/index.ts @@ -44,14 +44,17 @@ export const ProverDependency = { } else if (conf.nbCores <= 0) { conf.nbCores = 1; } + if (conf.ecoPow === null || conf.ecoPow === undefined) { + conf.ecoPow = ProverConstants.DEFAULT_ECO_MODE; + } if (conf.prefix === null || conf.prefix === undefined) { conf.prefix = ProverConstants.DEFAULT_PEER_ID; } - conf.powSecurityRetryDelay = ProverConstants.POW_SECURITY_RETRY_DELAY; + conf.powMaxDuration = ProverConstants.POW_MAX_DURATION; conf.powMaxHandicap = ProverConstants.POW_MAXIMUM_ACCEPTABLE_HANDICAP; }, beforeSave: async (conf: ConfDTO) => { - delete conf.powSecurityRetryDelay; + delete conf.powMaxDuration; delete conf.powMaxHandicap; }, }, @@ -88,14 +91,9 @@ export const ProverDependency = { prover: (server: Server) => new Prover(server), blockGenerator: (server: Server, prover: any) => new BlockGeneratorWhichProves(server, prover), - generateTheNextBlock: async (server: Server, manualValues: any) => { - const prover = new BlockProver(server); - const generator = new BlockGeneratorWhichProves(server, prover); - return generator.nextBlock(manualValues); - }, generateAndProveTheNext: async ( server: Server, - block: any, + block: BlockDTO | null, trial: any, manualValues: any ) => { @@ -322,7 +320,7 @@ function generateAndSend( function proveAndSend( program: any, server: Server, - block: any, + block: BlockDTO, issuer: any, difficulty: any, done: any diff --git a/app/modules/prover/lib/blockGenerator.ts b/app/modules/prover/lib/blockGenerator.ts index 66c1aa751..4463e6fe6 100644 --- a/app/modules/prover/lib/blockGenerator.ts +++ b/app/modules/prover/lib/blockGenerator.ts @@ -33,6 +33,7 @@ import { DataErrors } from "../../../lib/common-libs/errors"; import { Underscore } from "../../../lib/common-libs/underscore"; import { DBCert } from "../../../lib/dal/sqliteDAL/CertDAL"; import { Map } from "../../../lib/common-libs/crypto/map"; +import { BlockProver } from "./blockProver"; const inquirer = require("inquirer"); @@ -82,7 +83,10 @@ export class BlockGenerator { return this.server.dal; } - nextBlock(manualValues: any = {}, simulationValues: any = {}) { + nextBlock( + manualValues: any = {}, + simulationValues: any = {} + ): Promise<BlockDTO> { return this.generateNextBlock( new NextBlockGenerator(this.mainContext, this.server, this.logger), manualValues, @@ -670,7 +674,11 @@ export class BlockGenerator { block.number = current ? current.number + 1 : 0; // Compute the new MedianTime if (block.number == 0) { - block.medianTime = moment.utc().unix() - this.conf.rootoffset; + if (manualValues && manualValues.time) { + block.medianTime = manualValues.time; + } else { + block.medianTime = moment.utc().unix() - this.conf.rootoffset; + } } else { block.medianTime = vHEAD.medianTime; } @@ -888,6 +896,12 @@ export class BlockGenerator { block.issuersCount = vHEAD.issuersCount; block.issuersFrame = vHEAD.issuersFrame; block.issuersFrameVar = vHEAD.issuersFrameVar; + // Block time + if (manualValues && manualValues.time && block.number > 0) { + block.time = manualValues.time; + } else { + block.time = this.generateBlockTime(block); + } // Manual values before hashing if (manualValues) { Underscore.extend( @@ -896,31 +910,49 @@ export class BlockGenerator { ); } // InnerHash - block.time = block.medianTime; block.inner_hash = hashf(rawer.getBlockInnerPart(block)).toUpperCase(); return block; } + + private generateBlockTime(block: BlockDTO) { + if (block.number === 0) { + return block.medianTime; + } else { + const now = moment.utc().unix(); + const maxAcceleration = LOCAL_RULES_HELPERS.maxAcceleration(this.conf); + const timeoffset = + block.number >= this.conf.medianTimeBlocks + ? 0 + : this.conf.rootoffset || 0; + const medianTime = block.medianTime; + const upperBound = Math.min( + medianTime + maxAcceleration, + now - timeoffset + ); + return Math.max(medianTime, upperBound); + } + } } export class BlockGeneratorWhichProves extends BlockGenerator { - constructor(server: Server, private prover: any) { + prover: BlockProver; + + constructor(server: Server, private prover_: BlockProver | null) { super(server); + + this.prover = prover_ || new BlockProver(server); } async makeNextBlock( - block: DBBlock | null, + block: BlockDTO | null, trial?: number | null, manualValues: any = null - ) { + ): Promise<BlockDTO> { const unsignedBlock = block || (await this.nextBlock(manualValues)); const trialLevel = trial || (await this.mainContext.getIssuerPersonalizedDifficulty(this.selfPubkey)); - return this.prover.prove( - unsignedBlock, - trialLevel, - (manualValues && manualValues.time) || null - ); + return this.prover.prove(unsignedBlock, trialLevel); } } @@ -1060,6 +1092,9 @@ class NextBlockGenerator implements BlockGeneratorInterface { * @constructor */ class ManualRootGenerator implements BlockGeneratorInterface { + conf() { + return null; + } findNewCertsFromWoT() { return Promise.resolve({}); } diff --git a/app/modules/prover/lib/blockProver.ts b/app/modules/prover/lib/blockProver.ts index 8a9733dc6..1cbb47c7b 100644 --- a/app/modules/prover/lib/blockProver.ts +++ b/app/modules/prover/lib/blockProver.ts @@ -11,155 +11,97 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. -import { ProverConstants } from "./constants"; import { Server } from "../../../../server"; -import { PowEngine } from "./engine"; -import { DBBlock } from "../../../lib/db/DBBlock"; import { CommonConstants } from "../../../lib/common-libs/constants"; import { BlockDTO } from "../../../lib/dto/BlockDTO"; import { ConfDTO, Keypair } from "../../../lib/dto/ConfDTO"; +import { ValidProof } from "../../../../neon/lib"; +import { hashf } from "../../../lib/common-libs"; +import { Master as PowCluster } from "./powCluster"; -const os = require("os"); const querablep = require("querablep"); -const POW_FOUND = true; -const POW_NOT_FOUND_YET = false; - export class WorkerFarm { - private theEngine: PowEngine; - private onAlmostPoW: any = null; + private powCluster: PowCluster; private powPromise: any = null; - private stopPromise: any = null; - private checkPoWandNotify: any = null; constructor(private server: Server, private logger: any) { - this.theEngine = new PowEngine(server.conf, server.logger, server.dal); - - // An utility method to filter the pow notifications - this.checkPoWandNotify = (hash: string, block: DBBlock, found: boolean) => { - const matches = hash.match(/^(0{2,})[^0]/); - if (matches && this.onAlmostPoW) { - this.onAlmostPoW(hash, matches, block, found); - } - }; - - // Keep track of PoW advancement - this.theEngine.setOnInfoMessage((message: any) => { - if (message.error) { - this.logger.error( - "Error in engine#%s:", - this.theEngine.id, - message.error - ); - } else if (message.pow) { - // A message about the PoW - const msg = message.pow; - this.checkPoWandNotify(msg.pow, msg.block, POW_NOT_FOUND_YET); - } - }); + this.powCluster = new PowCluster(server.conf.nbCores, server.logger); } get nbWorkers() { - return this.theEngine.getNbWorkers(); + return this.powCluster.nbWorkers; } - changeCPU(cpu: any) { - return this.theEngine.setConf({ cpu }); + changeCPU(cpu: number) { + return this.powCluster.setConf({ cpu }); } - changePoWPrefix(prefix: any) { - return this.theEngine.setConf({ prefix }); + changePoWPrefix(prefix: number) { + return this.powCluster.setConf({ prefix }); } isComputing() { return this.powPromise !== null && !this.powPromise.isResolved(); } - isStopping() { - return this.stopPromise !== null && !this.stopPromise.isResolved(); - } - /** * Eventually stops the engine PoW if one was computing */ - stopPoW() { - this.stopPromise = querablep(this.theEngine.cancel()); - return this.stopPromise; + stopPoW(notify: boolean): void { + this.powCluster.cancel(notify); } shutDownEngine() { - return this.theEngine.shutDown(); + return this.powCluster.shutDown(); } /** * Starts a new computation of PoW - * @param stuff The necessary data for computing the PoW + * @param proofAsk The necessary data for computing the PoW */ - async askNewProof(stuff: ProofAsk) { + async askNewProof(proofAsk: ProofAsk): Promise<ValidProof | null> { // Starts the PoW - this.powPromise = querablep(this.theEngine.prove(stuff)); - const res = await this.powPromise; - if (res) { - this.checkPoWandNotify(res.pow.pow, res.pow.block, POW_FOUND); - } - return res && res.pow; - } - - setOnAlmostPoW(onPoW: any) { - this.onAlmostPoW = onPoW; + this.powPromise = querablep(this.powCluster.prove(proofAsk)); + return await this.powPromise; } } export class BlockProver { logger: any; waitResolve: any; - workerFarmPromise: any; + workerFarm: WorkerFarm; constructor(private server: Server) { this.logger = server.logger; - - const debug = process.execArgv.toString().indexOf("--debug") !== -1; - if (debug) { - //Set an unused port number. - process.execArgv = []; - } + this.workerFarm = new WorkerFarm(this.server, this.logger); } get conf(): ConfDTO { return this.server.conf; } - get pair(): Keypair | null { + get pair(): Keypair { return this.conf.pair; } - getWorker(): Promise<WorkerFarm> { - if (!this.workerFarmPromise) { - this.workerFarmPromise = (async () => { - return new WorkerFarm(this.server, this.logger); - })(); - } - return this.workerFarmPromise; - } - - async cancel() { + async cancel(notify: boolean) { // If no farm was instanciated, there is nothing to do yet - if (this.workerFarmPromise) { - let farm = await this.getWorker(); - await farm.stopPoW(); - if (this.waitResolve) { - this.waitResolve(); - this.waitResolve = null; - } + this.workerFarm.stopPoW(notify); + if (this.waitResolve) { + this.waitResolve(); + this.waitResolve = null; } } - prove(block: any, difficulty: any, forcedTime: any = null) { + prove(block: BlockDTO, difficulty: number) { if (this.waitResolve) { this.waitResolve(); this.waitResolve = null; } + const ecoMode = this.conf.ecoPow && difficulty > block.powMin; + const remainder = difficulty % 16; const nbZeros = (difficulty - remainder) / 16; const highMark = CommonConstants.PROOF_OF_WORK.UPPER_BOUND[remainder]; @@ -167,64 +109,41 @@ export class BlockProver { block.version + 1 === CommonConstants.DUBP_NEXT_VERSION; return (async () => { - let powFarm = await this.getWorker(); - if (block.number == 0) { // On initial block, difficulty is the one given manually block.powMin = difficulty; } - // Start - powFarm.setOnAlmostPoW( - (pow: any, matches: any, aBlock: any, found: boolean) => { - this.powEvent(found, pow); - if ( - matches && - matches[1].length >= ProverConstants.MINIMAL_ZEROS_TO_SHOW_IN_LOGS - ) { - this.logger.info( - "Matched %s zeros %s with Nonce = %s for block#%s by %s", - matches[1].length, - pow, - aBlock.nonce, - aBlock.number, - aBlock.issuer.slice(0, 6) - ); - } - } - ); - block.nonce = 0; this.logger.info( "Generating proof-of-work with %s leading zeros followed by [0-" + highMark + - "]... (CPU usage set to %s%) for block#%s", + "]... (CPU usage set to %s%, use %s cores) for block#%s", nbZeros, (this.conf.cpu * 100).toFixed(0), + ecoMode ? 1 : this.workerFarm.nbWorkers, block.number, block.issuer.slice(0, 6) ); const start = Date.now(); - let result = await powFarm.askNewProof({ + let proofOrNull = await this.workerFarm.askNewProof({ + maxDuration: this.conf.powMaxDuration, newPoW: { conf: { - powNoSecurity: this.conf.powNoSecurity, - cpu: this.conf.cpu, - prefix: this.conf.prefix ? String(this.conf.prefix) : "", avgGenTime: this.conf.avgGenTime, + cpu: this.conf.cpu, medianTimeBlocks: this.conf.medianTimeBlocks, + nbCores: ecoMode ? 1 : undefined, + powNoSecurity: this.conf.powNoSecurity, + prefix: this.conf.prefix ? String(this.conf.prefix) : "", }, block: block, - zeros: nbZeros, - highMark: highMark, - forcedTime: forcedTime, + difficulty, pair: this.pair, }, - specialNonce: notifyVersionJumpReady - ? 999 * (ProverConstants.NONCE_RANGE / 1000) - : 0, + specialNonce: notifyVersionJumpReady ? 999 : 0, }); - if (!result) { + if (proofOrNull === null) { this.logger.info( "GIVEN proof-of-work for block#%s with %s leading zeros followed by [0-" + highMark + @@ -235,18 +154,27 @@ export class BlockProver { ); throw "Proof-of-work computation canceled because block received"; } else { - const proof = result.block; - const testsCount = result.testsCount * powFarm.nbWorkers; + let proof: ValidProof = proofOrNull; + + // Apply proof to block + block.hash = proof.hash; + block.nonce = proof.nonce; + block.signature = proof.sig; + if (!block.inner_hash || block.inner_hash === "") { + block.inner_hash = hashf(block.getRawInnerPart()).toUpperCase(); + } + + const testsCount = proofOrNull.itersCount * this.workerFarm.nbWorkers; const duration = Date.now() - start; const testsPerSecond = testsCount / (duration / 1000); this.logger.info( "Done: #%s, %s in %ss (~%s tests, ~%s tests/s, using %s cores, CPU %s%)", block.number, - proof.hash, + block.hash, (duration / 1000).toFixed(2), testsCount, testsPerSecond.toFixed(2), - powFarm.nbWorkers, + ecoMode ? 1 : this.workerFarm.nbWorkers, Math.floor(100 * this.conf.cpu) ); this.logger.info( @@ -255,31 +183,26 @@ export class BlockProver { "]!", nbZeros ); - return BlockDTO.fromJSONObject(proof); + + return BlockDTO.fromJSONObject(block); } })(); } async changeCPU(cpu: number) { this.conf.cpu = cpu; - const farm = await this.getWorker(); - return farm.changeCPU(cpu); + return this.workerFarm.changeCPU(cpu); } - async changePoWPrefix(prefix: any) { + async changePoWPrefix(prefix: number) { this.conf.prefix = prefix; - const farm = await this.getWorker(); - return farm.changePoWPrefix(prefix); - } - - private powEvent(found: boolean, hash: string) { - this.server && this.server.push({ pow: { found, hash } }); + return this.workerFarm.changePoWPrefix(prefix); } } export interface ProofAsk { initialTestsPerRound?: number; - maxDuration?: number; + maxDuration: number; specialNonce?: number; newPoW: { conf: { @@ -290,11 +213,8 @@ export interface ProofAsk { avgGenTime: number; medianTimeBlocks: number; }; - block: any; - zeros: number; - highMark: string; - forcedTime?: number; - pair: Keypair | null; - turnDuration?: number; + block: BlockDTO; + difficulty: number; + pair: Keypair; }; } diff --git a/app/modules/prover/lib/constants.ts b/app/modules/prover/lib/constants.ts index 299722a4b..412ee7f83 100644 --- a/app/modules/prover/lib/constants.ts +++ b/app/modules/prover/lib/constants.ts @@ -18,6 +18,7 @@ export const ProverConstants = { POW_MINIMAL_TO_SHOW: 2, DEFAULT_CPU: 0.6, + DEFAULT_ECO_MODE: true, DEFAULT_PEER_ID: 1, MIN_PEER_ID: 1, MAX_PEER_ID: 899, // Due to MAX_SAFE_INTEGER = 9007199254740991 (16 digits, and we use 11 digits for the nonce + 2 digits for core number => 3 digits for the peer, must be below 900) @@ -27,6 +28,6 @@ export const ProverConstants = { POW_MAXIMUM_ACCEPTABLE_HANDICAP: 64, POW_NB_PAUSES_PER_ROUND: 10, - // When to trigger the PoW process again if no PoW is triggered for a while. In milliseconds. - POW_SECURITY_RETRY_DELAY: 10 * 60 * 1000, + // If the pow has still not been solved after this delay in milliseconds, the block is regenerated to update its content and time field. + POW_MAX_DURATION: 10 * 60 * 1000, }; diff --git a/app/modules/prover/lib/engine.ts b/app/modules/prover/lib/engine.ts deleted file mode 100644 index 93afb37ba..000000000 --- a/app/modules/prover/lib/engine.ts +++ /dev/null @@ -1,68 +0,0 @@ -// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1 -// Copyright (C) 2018 Cedric Moreau <cem.moreau@gmail.com> -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -import { Master as PowCluster } from "./powCluster"; -import { ConfDTO } from "../../../lib/dto/ConfDTO"; -import { FileDAL } from "../../../lib/dal/fileDAL"; -import { ProofAsk } from "./blockProver"; - -const os = require("os"); - -// Super important for Node.js debugging -const debug = process.execArgv.toString().indexOf("--debug") !== -1; -if (debug) { - //Set an unused port number. - process.execArgv = []; -} - -export class PowEngine { - private nbWorkers: number; - private cluster: PowCluster; - readonly id: number; - - constructor(private conf: ConfDTO, logger: any, private dal?: FileDAL) { - // We use as much cores as available, but not more than CORES_MAXIMUM_USE_IN_PARALLEL - this.nbWorkers = conf.nbCores; - this.cluster = new PowCluster(this.nbWorkers, logger, dal); - this.id = this.cluster.clusterId; - } - - getNbWorkers() { - return this.cluster.nbWorkers; - } - - forceInit() { - return this.cluster.initCluster(); - } - - async prove(stuff: ProofAsk) { - await this.cluster.cancelWork(); - return await this.cluster.proveByWorkers(stuff); - } - - cancel() { - return this.cluster.cancelWork(); - } - - setConf(value: any) { - return this.cluster.changeConf(value); - } - - setOnInfoMessage(callback: any) { - return (this.cluster.onInfoMessage = callback); - } - - async shutDown() { - return this.cluster.shutDownWorkers(); - } -} diff --git a/app/modules/prover/lib/permanentProver.ts b/app/modules/prover/lib/permanentProver.ts index bf52ee7d7..3dc0768e1 100644 --- a/app/modules/prover/lib/permanentProver.ts +++ b/app/modules/prover/lib/permanentProver.ts @@ -17,24 +17,22 @@ import { BlockProver } from "./blockProver"; import { DBBlock } from "../../../lib/db/DBBlock"; import { dos2unix } from "../../../lib/common-libs/dos2unix"; import { parsers } from "../../../lib/common-libs/parsers/index"; - import { Server } from "../../../../server"; -import { Querable, querablep } from "../../../lib/common-libs/querable"; +import { BlockDTO } from "../../../lib/dto/BlockDTO"; + +const NOT_COMPUTABLE_DELAY: number = 30000; export class PermanentProver { - logger: any; conf: ConfDTO; - prover: BlockProver; generator: BlockGeneratorWhichProves; + logger: any; loops: number; + prover: BlockProver; + stop: boolean; - private permanencePromise: Querable<void> | null = null; - - private blockchainChangedResolver: any = null; - private promiseOfWaitingBetween2BlocksOfOurs: any = null; - private lastComputedBlock: any = null; - private resolveContinuePromise: any = null; - private continuePromise: any = null; + private currentBlockstamp: string | null = null; + private lastComputedBlock: BlockDTO | null = null; + private rejectProofPromise: any; constructor(private server: Server) { this.logger = server.logger; @@ -42,234 +40,61 @@ export class PermanentProver { this.prover = new BlockProver(server); this.generator = new BlockGeneratorWhichProves(server, this.prover); - // Promises triggering the prooving lopp - this.resolveContinuePromise = null; - this.continuePromise = new Promise( - (resolve) => (this.resolveContinuePromise = resolve) - ); - this.loops = 0; + this.stop = true; } allowedToStart() { - if (!this.permanencePromise || this.permanencePromise.isFulfilled()) { - this.startPermanence(); - } - this.resolveContinuePromise(true); - } - - async startPermanence() { - let permanenceResolve = () => {}; - this.permanencePromise = querablep( - new Promise((res) => { - permanenceResolve = res; - }) - ); - - /****************** - * Main proof loop - *****************/ - - while (await this.continuePromise) { - try { - const waitingRaces = []; - - // By default, we do not make a new proof - let doProof = false; - - try { - const selfPubkey = this.server.keyPair.publicKey; - const dal = this.server.dal; - const theConf = this.server.conf; - if (!selfPubkey) { - throw "No self pubkey found."; - } - let current; - const isMember = await dal.isMember(selfPubkey); - if (!isMember) { - throw "Local node is not a member. Waiting to be a member before computing a block."; - } - current = await dal.getCurrentBlockOrNull(); - if (!current) { - throw "Waiting for a root block before computing new blocks"; - } - const trial = await this.server - .getBcContext() - .getIssuerPersonalizedDifficulty(selfPubkey); - this.checkTrialIsNotTooHigh(trial, current, selfPubkey); - const lastIssuedByUs = current.issuer == selfPubkey; - if (lastIssuedByUs && !this.promiseOfWaitingBetween2BlocksOfOurs) { - this.promiseOfWaitingBetween2BlocksOfOurs = new Promise((resolve) => - setTimeout(resolve, theConf.powDelay) - ); - this.logger.warn( - "Waiting " + - theConf.powDelay + - "ms before starting to compute next block..." - ); - } else { - // We have waited enough - this.promiseOfWaitingBetween2BlocksOfOurs = null; - // But under some conditions, we can make one - doProof = true; - } - } catch (e) { - this.logger.warn(e); + if (this.stop) { + const loop = async () => { + this.logger.trace("PermanentProver: start loop #" + this.loops); + if (this.stop) { + return; } - if (doProof) { - /******************* - * COMPUTING A BLOCK - ******************/ + await this.tryToComputeNextblock(); - try { - let cancelAlreadyTriggered = false; - - // The canceller - (async () => { - // If the blockchain changes - await new Promise( - (resolve) => (this.blockchainChangedResolver = resolve) - ); - cancelAlreadyTriggered = true; - // Then cancel the generation - await this.prover.cancel(); - })(); - - let unsignedBlock = null, - trial2 = 0; - if (!cancelAlreadyTriggered) { - // The pushFIFO is here to get the difficulty level while excluding any new block to be resolved. - // Without it, a new block could be added meanwhile and would make the difficulty wrongly computed. - await this.server.BlockchainService.pushFIFO( - "generatingNextBlock", - async () => { - const current = (await this.server.dal.getCurrentBlockOrNull()) as DBBlock; - const selfPubkey = this.server.keyPair.publicKey; - if (!cancelAlreadyTriggered) { - trial2 = await this.server - .getBcContext() - .getIssuerPersonalizedDifficulty(selfPubkey); - } - this.checkTrialIsNotTooHigh(trial2, current, selfPubkey); - if (!cancelAlreadyTriggered) { - unsignedBlock = await this.generator.nextBlock(); - } - } - ); - if (!cancelAlreadyTriggered) { - this.lastComputedBlock = await this.prover.prove( - unsignedBlock, - trial2, - null - ); - } - try { - const obj = parsers.parseBlock.syncWrite( - dos2unix(this.lastComputedBlock.getRawSigned()) - ); - await this.server.writeBlock(obj); - await new Promise((res) => { - this.server.once("bcEvent", () => res()); - }); - } catch (err) { - this.logger.warn( - "Proof-of-work self-submission: %s", - err.message || err - ); - } - } - } catch (e) { - this.logger.warn( - "The proof-of-work generation was canceled: %s", - (e && e.message) || - (e && e.uerr && e.uerr.message) || - e || - "unkonwn reason" - ); - } - } else { - /******************* - * OR WAITING PHASE - ******************/ - if (this.promiseOfWaitingBetween2BlocksOfOurs) { - waitingRaces.push(this.promiseOfWaitingBetween2BlocksOfOurs); - } - - let raceDone = false; - - await Promise.race( - waitingRaces.concat([ - // The blockchain has changed! We or someone else found a proof, we must make a gnu one - new Promise( - (resolve) => - (this.blockchainChangedResolver = () => { - this.logger.warn("Blockchain changed!"); - resolve(); - }) - ), - - // Security: if nothing happens for a while, trigger the whole process again - new Promise((resolve) => - setTimeout(() => { - if (!raceDone) { - this.logger.warn( - "Security trigger: proof-of-work process seems stuck" - ); - resolve(); - } - }, this.conf.powSecurityRetryDelay) - ), - ]) - ); - - raceDone = true; + if (!this.stop) { + this.loops++; + setImmediate(loop); } - } catch (e) { - this.logger.warn(e); - } + }; - this.loops++; - // Informative variable - this.logger.trace("PoW loops = %s", this.loops); + // Start main loop + this.stop = false; + setImmediate(loop); } - - permanenceResolve(); } - async blockchainChanged(gottenBlock?: any) { - if ( - this.server && - (!gottenBlock || - !this.lastComputedBlock || - gottenBlock.hash !== this.lastComputedBlock.hash) - ) { - // Cancel any processing proof - await this.prover.cancel(); - // If we were waiting, stop it and process the continuous generation - this.blockchainChangedResolver && this.blockchainChangedResolver(); + async blockchainChanged() { + if (this.server && this.currentBlockstamp) { + let newCurrentBlock = await this.server.dal.getCurrentBlockOrNull(); + if (newCurrentBlock) { + let newCurrentBlockstamp = + newCurrentBlock.number + "-" + newCurrentBlock.hash; + if (newCurrentBlockstamp != this.currentBlockstamp) { + // HEAD change, cancel pow + this.logger.warn("prover: blockchain HEAD change, cancel pow."); + await this.prover.cancel(false); + if (this.rejectProofPromise) { + this.rejectProofPromise({ message: "blockchain change" }); + } + } + } else { + // No blockchain, probably genesis revert, cancel pow + this.logger.warn( + "prover: no blockchain, probably genesis revert, cancel pow." + ); + await this.prover.cancel(true); + } } } - async stopEveryting() { - // First: avoid continuing the main loop - this.resolveContinuePromise(true); - this.continuePromise = new Promise( - (resolve) => (this.resolveContinuePromise = resolve) - ); - // Second: stop any started proof - await this.prover.cancel(); - // If we were waiting, stop it and process the continuous generation - this.blockchainChangedResolver && this.blockchainChangedResolver(); - const farm = await this.prover.getWorker(); - await farm.shutDownEngine(); - } - - private checkTrialIsNotTooHigh( + private checkTrialIsTooHigh( trial: number, current: DBBlock, selfPubkey: string - ) { + ): boolean { if (trial > current.powMin + this.conf.powMaxHandicap) { this.logger.debug( "Trial = %s, powMin = %s, pubkey = %s", @@ -277,7 +102,112 @@ export class PermanentProver { current.powMin, selfPubkey.slice(0, 6) ); - throw "Too high difficulty: waiting for other members to write next block"; + this.logger.warn( + "Too high difficulty: waiting for other members to write next block" + ); + return true; + } else { + return false; + } + } + + // Is the next block computable ? + private async isNextBlockComputable(): Promise<number | null> { + const selfPubkey = this.server.keyPair.publicKey; + const dal = this.server.dal; + if (!selfPubkey) { + this.logger.warn("No self pubkey found."); + return null; + } else if (!(await dal.isMember(selfPubkey))) { + this.logger.warn( + "Local node is not a member. Waiting to be a member before computing a block." + ); + return null; + } else { + let currentBlock = await dal.getCurrentBlockOrNull(); + if (!currentBlock) { + this.logger.warn( + "Waiting for a root block before computing new blocks" + ); + return null; + } else { + this.currentBlockstamp = currentBlock.number + "-" + currentBlock.hash; + const selfDifficulty: number = await this.server + .getBcContext() + .getIssuerPersonalizedDifficulty(selfPubkey); + // If self difficulty is too high, we must not make the proof. + if ( + this.checkTrialIsTooHigh(selfDifficulty, currentBlock, selfPubkey) + ) { + return null; + } else { + return selfDifficulty; + } + } } } + + private async prove(unsignedBlock: BlockDTO, selfDifficulty: number) { + try { + if (this.rejectProofPromise) { + this.rejectProofPromise({ message: "new proof ask" }); + } + this.lastComputedBlock = await new Promise( + async (resolve, reject) => { + this.rejectProofPromise = reject; + try { + let proof = await this.prover.prove( + unsignedBlock, + selfDifficulty + ); + resolve(proof); + } catch (err) { + reject(err); + } + } + ); + this.logger.trace("prover: pow finished."); + // Try to share founded block + try { + if (this.lastComputedBlock) { + const obj = parsers.parseBlock.syncWrite( + dos2unix(this.lastComputedBlock.getRawSigned()) + ); + await this.server.writeBlock(obj); + await new Promise((res) => { + this.server.once("bcEvent", () => res()); + }); + } else { + throw "prover return null proof"; + } + } catch (err) { + this.logger.warn( + "Proof-of-work self-submission: %s", + err.message || err + ); + } + } catch (err) { + // pow cancelled + this.logger.trace("prover: pow cancelled. reason: " + err.message); + } + } + + private async tryToComputeNextblock() { + const selfDifficulty = await this.isNextBlockComputable(); + if (selfDifficulty !== null) { + // Generate next block + let unsignedBlock = await this.generator.nextBlock(); + // Launch proof of work computation + // If it throws that the pow was canceled + await this.prove(unsignedBlock, selfDifficulty); + } else { + await new Promise((resolve) => setTimeout(resolve, NOT_COMPUTABLE_DELAY)); + } + } + + async stopEveryting() { + this.logger.warn("prover: stop service."); + this.stop = true; + this.prover.workerFarm.shutDownEngine(); + } } diff --git a/app/modules/prover/lib/powCluster.ts b/app/modules/prover/lib/powCluster.ts index f8d99a629..e87ba5a89 100644 --- a/app/modules/prover/lib/powCluster.ts +++ b/app/modules/prover/lib/powCluster.ts @@ -11,14 +11,12 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. -import { ConfDTO } from "../../../lib/dto/ConfDTO"; -import { ProverConstants } from "./constants"; -import { createPowWorker } from "./proof"; -import { PowWorker } from "./PowWorker"; -import { FileDAL } from "../../../lib/dal/fileDAL"; +import * as pEvent from "p-event"; import { Underscore } from "../../../lib/common-libs/underscore"; import { ProofAsk } from "./blockProver"; -import { ExitCodes } from "../../../lib/common-libs/exit-codes"; +import { PowCluster, PowConf, ValidProof } from "../../../../neon/lib"; +import { ProverConstants } from "./constants"; +import { hashf } from "../../../lib/common-libs"; const nuuid = require("node-uuid"); const cluster = require("cluster"); @@ -27,203 +25,138 @@ const querablep = require("querablep"); let clusterId = 0; cluster.setMaxListeners(3); -export interface SlaveWorker { - worker: PowWorker; - index: number; - online: Promise<void>; - nonceBeginning: number; +export class NewPowConf { + cpu?: number; + prefix?: number; } /** * Cluster controller, handles the messages between the main program and the PoW cluster. */ export class Master { - nbCancels = 0; - clusterId: number; - currentPromise: any | null = null; - slaves: SlaveWorker[] = []; - slavesMap: { - [k: number]: SlaveWorker | null; - } = {}; - conf: any = {}; + clusterIsUp: boolean; + powPromise: any | null = null; + rustCluster: PowCluster; + nbWorkers: number = 0; + conf: PowConf; logger: any; onInfoCallback: any; workersOnline: Promise<any>[]; - constructor( - private nbCores: number | null | undefined, - logger: any, - private dal?: FileDAL - ) { + constructor(nbCores: number | null, logger: any) { this.clusterId = clusterId++; this.logger = logger || Master.defaultLogger(); - this.onInfoMessage = (message: any) => { - this.logger.info(`${message.pow.pow} nonce = ${message.pow.block.nonce}`); - }; - } - - get nbWorkers() { - return this.slaves.length; - } - set onInfoMessage(callback: any) { - this.onInfoCallback = callback; - } - - onWorkerMessage(workerIndex: number, message: any) { - // this.logger.info(`worker#${this.slavesMap[worker.id].index} sent message:${message}`) - if (message && message.pow) { - this.onInfoCallback && this.onInfoCallback(message); - } - 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++; - } - // this.logger.debug(`ENGINE c#${this.clusterId}#${this.slavesMap[worker.id].index}:`, message) + // Init cluster + this.rustCluster = new PowCluster(this.logger, nbCores || 1); + this.clusterIsUp = true; + this.nbWorkers = this.rustCluster.getWorkersCount(); } /***************** * CLUSTER METHODS ****************/ - initCluster() { - // Setup master - cluster.setupMaster({ - exec: __filename, - execArgv: [], // Do not try to debug forks - }); - - const nbCores = - this.nbCores !== undefined && this.nbCores !== null ? this.nbCores : 1; - this.slaves = Array.from({ length: nbCores }).map((value, index) => { - 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({ - rootPath: this.dal ? this.dal.rootPath : "", - command: "conf", - value: this.conf, - }); - }, - (code: any, signal: any) => { - this.logger.info( - `worker ${worker.pid} died with code ${code} and signal ${signal}` - ); - } - ); - + setConf(newPowConf: NewPowConf) { + if (this.conf) { this.logger.info( - `Creating worker c#${this.clusterId}#w#${nodejsWorker.id}` + `Changing conf to: ${JSON.stringify(newPowConf)} on PoW cluster` + ); + if (newPowConf.cpu) { + this.conf.cpu = Math.round(newPowConf.cpu * 100); + } + if (newPowConf.prefix) { + this.conf.prefix = newPowConf.prefix; + } + + this.rustCluster.updateConf( + this.conf.cpu, + this.conf.secretKey, + this.conf.prefix ); - const slave = { - // The Node.js worker - worker, - - // Inner identifier - index, - - // Worker ready - online: worker.online, - - // Each worker has his own chunk of possible nonces - nonceBeginning: - this.nbCores === 1 ? 0 : (index + 1) * ProverConstants.NONCE_RANGE, - }; - this.slavesMap[nodejsWorker.id] = slave; - return slave; - }); - - this.workersOnline = this.slaves.map((s) => s.online); - return Promise.all(this.workersOnline); - } - - changeConf(conf: ConfDTO) { - this.logger.info( - `Changing conf to: ${JSON.stringify(conf)} on PoW cluster` - ); - this.conf.cpu = conf.cpu || this.conf.cpu; - this.conf.prefix = this.conf.prefix || conf.prefix; - this.slaves.forEach((s) => { - s.worker.sendConf({ - rootPath: "", - command: "conf", - value: this.conf, - }); - }); - return Promise.resolve(Underscore.clone(conf)); - } - - private cancelWorkersWork() { - this.slaves.forEach((s) => { - s.worker.sendCancel(); - }); - if (this.dal) { - this.dal.powDAL.writeCurrent(""); } + return Promise.resolve(Underscore.clone(newPowConf)); } - async cancelWork() { - const workEnded = this.currentPromise; - // Don't await the cancellation! - this.cancelWorkersWork(); - // Current promise is done - this.currentPromise = null; - return await workEnded; + cancel(notify: boolean) { + this.rustCluster.stopPow(notify); } - async shutDownWorkers() { - if (this.workersOnline) { - await Promise.all(this.workersOnline); - await Promise.all( - this.slaves.map(async (s) => { - s.worker.kill(); - }) - ); + shutDown() { + if (this.clusterIsUp) { + this.rustCluster.quit(); + this.clusterIsUp = false; + this.nbWorkers = 0; } - this.slaves = []; } - async proveByWorkers(stuff: ProofAsk) { - // Eventually spawn the workers - if (this.slaves.length === 0) { - this.initCluster(); + async prove(proofAsk: ProofAsk): Promise<ValidProof | null> { + // Define prefix and blockInnerHash + let prefix = ProverConstants.DEFAULT_PEER_ID; + if (proofAsk.newPoW.conf.prefix) { + prefix = parseInt(proofAsk.newPoW.conf.prefix, 10); } - - if (this.dal) { - await this.dal.powDAL.writeCurrent( - [stuff.newPoW.block.number - 1, stuff.newPoW.block.previousHash].join( - "-" - ) - ); + let blockInnerHash = proofAsk.newPoW.block.inner_hash; + if (!blockInnerHash || blockInnerHash === "") { + blockInnerHash = hashf( + proofAsk.newPoW.block.getRawInnerPart() + ).toUpperCase(); } - // Register the new proof uuid - const uuid = nuuid.v4(); - this.currentPromise = querablep( - (async () => { - await Promise.all(this.workersOnline); + // Save pow conf + this.conf = { + cpu: Math.round( + (proofAsk.newPoW.conf.cpu || ProverConstants.DEFAULT_CPU) * 100 + ), + secretKey: proofAsk.newPoW.pair.sec, + prefix, + }; - if (!this.currentPromise) { + /// Launch pow promise + this.powPromise = querablep( + (async () => { + /*if (!this.powPromise) { this.logger.info(`Proof canceled during workers' initialization`); return null; + }*/ + + this.rustCluster.startPow( + { + blockInnerHash, + nbCores: proofAsk.newPoW.conf.nbCores, + cpu: this.conf.cpu, + diff: proofAsk.newPoW.difficulty, + secretKey: this.conf.secretKey, + marker: proofAsk.specialNonce || 0, + prefix: this.conf.prefix, + }, + proofAsk.maxDuration || ProverConstants.POW_MAX_DURATION + ); + + let proofOrNull: ValidProof | null = null; + try { + // `emitter` emitted a `proof` event + proofOrNull = await pEvent(this.rustCluster, "proof"); + } catch (error) { + // `emitter` emitted an `error` event + console.error(error); + } + + if (proofOrNull === null) { + this.logger.info( + "No engine found the proof. It was probably cancelled." + ); + } else { + this.logger.info( + `ENGINE c#${this.clusterId}#${proofOrNull.workerId} HAS FOUND A PROOF #${proofOrNull.hash}` + ); } + return proofOrNull; + // Start the salves' job - const asks = this.slaves.map(async (s, index) => { + /*const asks = this.slaves.map(async (s, index) => { const nonceBeginning = stuff.specialNonce || s.nonceBeginning; const proof = await s.worker.askProof({ uuid, @@ -252,30 +185,11 @@ export class Master { workerID: index, proof, }; - }); - - // Find a proof - const result = await Promise.race(asks); - // Don't await the cancellation! - 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; + return this.powPromise; } static defaultLogger() { @@ -285,7 +199,7 @@ export class Master { } } -if (cluster.isMaster) { +/*if (cluster.isMaster) { // Super important for Node.js debugging const debug = process.execArgv.toString().indexOf("--debug") !== -1; if (debug) { @@ -298,4 +212,4 @@ if (cluster.isMaster) { }); createPowWorker(); -} +}*/ diff --git a/app/modules/prover/lib/proof.ts b/app/modules/prover/lib/proof.ts deleted file mode 100644 index f24d30571..000000000 --- a/app/modules/prover/lib/proof.ts +++ /dev/null @@ -1,397 +0,0 @@ -// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1 -// Copyright (C) 2018 Cedric Moreau <cem.moreau@gmail.com> -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -import * as moment from "moment"; -import { LOCAL_RULES_HELPERS } from "../../../lib/rules/local_rules"; -import { hashf } from "../../../lib/common"; -import { DBBlock } from "../../../lib/db/DBBlock"; -import { ConfDTO } from "../../../lib/dto/ConfDTO"; -import { ProverConstants } from "./constants"; -import { Ed25519Signator, KeyPairBuilder } from "../../../../neon/lib"; -import { dos2unix } from "../../../lib/common-libs/dos2unix"; -import { rawer } from "../../../lib/common-libs/index"; -import { ProcessCpuProfiler } from "../../../ProcessCpuProfiler"; -import { PowDAL } from "../../../lib/dal/fileDALs/PowDAL"; -import { Directory } from "../../../lib/system/directory"; -import { ExitCodes } from "../../../lib/common-libs/exit-codes"; - -const querablep = require("querablep"); - -export function createPowWorker() { - let powDAL: PowDAL | null = null; - let computing = querablep(Promise.resolve(null)); - let askedStop = false; - - // By default, we do not prefix the PoW by any number - let prefix = 0; - - let sigFuncSaved: (msg: string) => string; - let lastSecret: any, - lastVersion: number, - currentCPU: number = 1; - - 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("unhandledRejection", () => { - process.exit(ExitCodes.OK); - }); - - process.on("message", async (message) => { - switch (message.command) { - case "newPoW": - (async () => { - askedStop = true; - - // Very important: do not await if the computation is already done, to keep the lock on JS engine - if (!computing.isFulfilled()) { - await computing; - } - - if (message.value.rootPath) { - const params = await Directory.getHomeFS( - false, - message.value.rootPath, - false - ); - powDAL = new PowDAL(message.value.rootPath, params.fs); - } - - const res = await beginNewProofOfWork(message.value); - answer(message, res); - })(); - break; - - case "cancel": - if (!computing.isFulfilled()) { - askedStop = true; - } - 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; - } - }); - - 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; - - // Define sigFunc - const signator = KeyPairBuilder.fromSecretKey(pair.sec); - let sigFunc = null; - if (sigFuncSaved && lastSecret === pair.sec) { - sigFunc = sigFuncSaved; - } else { - lastSecret = pair.sec; - sigFunc = (msg: string) => signator.sign(msg); - } - - /***************** - * GO! - ****************/ - - let pow = "", - sig = "", - raw = ""; - 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) - - while (!found && !askedStop) { - /***************** - * A TURN ~ 100ms - ****************/ - - await Promise.race([ - // I. Stop the turn if it exceeds `turnDuration` ms - countDown(turnDuration), - - // II. Process the turn's PoW - (async () => { - // Prove - let i = 0; - const thisTurn = turn; - - // 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); - - /***************** - * Iterations of a turn - ****************/ - - while ( - !found && - i < testsPerRound && - thisTurn === turn && - !askedStop - ) { - // Nonce change (what makes the PoW change if the time field remains the same) - nonce++; - - /***************** - * A PROOF OF WORK - ****************/ - - // 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(); - - /***************** - * 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 } }); - } - - /***************** - * - Update local vars - * - Allow to receive stop signal - ****************/ - - if (!found && !askedStop) { - i++; - testsCount++; - if (i % pausePeriod === 0) { - await countDown(1); // Very low pause, just the time to process eventual end of the turn - } - } - } - - /***************** - * 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) - ); - } - pausePeriod = Math.floor( - testsPerRound / ProverConstants.POW_NB_PAUSES_PER_ROUND - ); - } - } - - /***************** - * 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); - } - })(), - ]); - - // console.log('W#%s.powDAL = ', process.pid, powDAL) - - if (powDAL && !conf.powNoSecurity) { - const currentProofCheck = await powDAL.getCurrent(); - if (currentProofCheck !== null) { - if (currentProofCheck === "") { - askedStop = true; - } else { - const [currentNumber, currentHash] = currentProofCheck.split( - "-" - ); - if ( - block.number !== parseInt(currentNumber) + 1 || - block.previousHash !== currentHash - ) { - askedStop = true; - } - } - } - } - - // Next turn - turn++; - - turnDuration += 1; - turnDuration = Math.min(turnDuration, maxDuration); // Max 1 second per turn - } - - /***************** - * POW IS OVER - * ----------- - * - * We either have found a valid POW or a stop event has been detected. - ****************/ - - if (askedStop) { - // PoW stopped - askedStop = false; - pSend({ canceled: true }); - return null; - } else { - // PoW success - block.hash = pow; - block.signature = sig; - return { - pow: { - block: block, - testsCount: testsCount, - pow: pow, - }, - }; - } - })() - ); - - return computing; - } - - function countDown(duration: number) { - return new Promise((resolve) => setTimeout(resolve, duration)); - } - - 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; - } - 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 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"); - } - }); - } -} diff --git a/app/modules/prover/lib/prover.ts b/app/modules/prover/lib/prover.ts index c1adcbdea..d01ce627a 100644 --- a/app/modules/prover/lib/prover.ts +++ b/app/modules/prover/lib/prover.ts @@ -32,7 +32,7 @@ export class Prover extends stream.Transform { (obj.bcEvent && obj.bcEvent === OtherConstants.BC_EVENT.HEAD_CHANGED) || obj.bcEvent === OtherConstants.BC_EVENT.SWITCHED ) { - this.permaProver.blockchainChanged(obj.block); + this.permaProver.blockchainChanged(); } else if (obj.cpu !== undefined) { this.permaProver.prover.changeCPU(obj.cpu); // We multiply by 10 to give room to computers with < 100 cores } diff --git a/neon/lib/pow.ts b/neon/lib/pow.ts index fed7a3a69..57088accd 100644 --- a/neon/lib/pow.ts +++ b/neon/lib/pow.ts @@ -82,7 +82,11 @@ export class PowCluster extends EventEmitter { } stopPow(notify: boolean): void { - this.rustCluster.stopPow(notify); + try { + this.rustCluster.stopPow(notify); + } catch (err) { + this.logger.warn(err.message | err); + } this.isComputing = false; } diff --git a/server.ts b/server.ts index d20a8290a..8c648af61 100644 --- a/server.ts +++ b/server.ts @@ -200,7 +200,6 @@ export class Server extends stream.Duplex implements HookableServer { this.conf.msWindow = this.conf.msWindow === undefined ? constants.CONTRACT.DEFAULT.MSWINDOW : this.conf.msWindow this.conf.xpercent = this.conf.xpercent === undefined ? constants.CONTRACT.DEFAULT.X_PERCENT : this.conf.xpercent this.conf.percentRot = this.conf.percentRot === undefined ? constants.CONTRACT.DEFAULT.PERCENTROT : this.conf.percentRot - this.conf.powDelay = this.conf.powDelay === undefined ? constants.CONTRACT.DEFAULT.POWDELAY : this.conf.powDelay this.conf.avgGenTime = this.conf.avgGenTime === undefined ? constants.CONTRACT.DEFAULT.AVGGENTIME : this.conf.avgGenTime this.conf.dtDiffEval = this.conf.dtDiffEval === undefined ? constants.CONTRACT.DEFAULT.DTDIFFEVAL : this.conf.dtDiffEval this.conf.medianTimeBlocks = this.conf.medianTimeBlocks === undefined ? constants.CONTRACT.DEFAULT.MEDIANTIMEBLOCKS : this.conf.medianTimeBlocks diff --git a/test/fast/prover/prover-pow-1-cluster.ts b/test/fast/prover/prover-pow-1-cluster.ts index ea00618c0..3f772d6a8 100644 --- a/test/fast/prover/prover-pow-1-cluster.ts +++ b/test/fast/prover/prover-pow-1-cluster.ts @@ -12,6 +12,7 @@ // GNU Affero General Public License for more details. import {Master} from "../../../app/modules/prover/lib/powCluster" +import { BlockDTO } from "../../../app/lib/dto/BlockDTO"; require('should') const logger = require('../../../app/lib/logger').NewLogger() @@ -25,52 +26,21 @@ describe('PoW Cluster', () => { }) after(() => { - return master.shutDownWorkers() + return master.shutDown() }) - it('should have an empty cluster if no PoW was asked', () => { - master.nbWorkers.should.equal(0) + it('should have a cluster initialized with the number of workers requested.', () => { + master.nbWorkers.should.equal(1) }) - it('should answer for a basic PoW in more than 50ms (cold)', async () => { + it('should answer within 50ms for a basic PoW', async () => { const start = Date.now() - await master.proveByWorkers({ - newPoW: { - block: { - number: 0 - }, - zeros: 0, - highMark: 'F', - conf: { - medianTimeBlocks: 1, - avgGenTime: 100, - cpu: 0.8, - prefix: '8' - }, - pair: { - pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', - sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP' - }, - turnDuration: 10 - } - }) - const delay = Date.now() - start - delay.should.be.above(50) - }) - - it('should have an non-empty cluster after a PoW was asked', () => { - master.nbWorkers.should.above(0) - }) - - it('should answer within 100ms for a basic PoW (warm)', async () => { - const start = Date.now() - await master.proveByWorkers({ + const block = BlockDTO.fromJSONObject({ number: 0 }); + await master.prove({ + maxDuration: 1000, newPoW: { - block: { - number: 0 - }, - zeros: 0, - highMark: 'F', + block, + difficulty: 0, conf: { medianTimeBlocks: 1, avgGenTime: 100, @@ -81,23 +51,20 @@ describe('PoW Cluster', () => { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP' }, - turnDuration: 100 } }) const delay = Date.now() - start - delay.should.be.below(100) + delay.should.be.below(50) }) it('should be able to stop all the cores on cancel', async () => { - master.proveByWorkers({ + const block = BlockDTO.fromJSONObject({ number: 0 }); + master.prove({ initialTestsPerRound: 100, maxDuration: 1000, newPoW: { - block: { - number: 0 - }, - zeros: 10, - highMark: 'F', + block, + difficulty: 161, conf: { medianTimeBlocks: 1, avgGenTime: 100, @@ -111,12 +78,8 @@ describe('PoW Cluster', () => { } } }) - await new Promise(res => { - master.onInfoMessage = () => res() - }) - await master.cancelWork() + master.cancel(true) await new Promise(res => setTimeout(res, 100)) - master.nbCancels.should.equal(1) }) }); diff --git a/test/fast/prover/prover-pow-2-engine.ts b/test/fast/prover/prover-pow-2-engine.ts index 403f822ef..479e4b431 100644 --- a/test/fast/prover/prover-pow-2-engine.ts +++ b/test/fast/prover/prover-pow-2-engine.ts @@ -11,39 +11,37 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. -import {PowEngine} from "../../../app/modules/prover/lib/engine" -import {NewLogger} from "../../../app/lib/logger" +import { NewLogger } from "../../../app/lib/logger" +import { assertNotNull } from "../../integration/tools/test-framework" +import { BlockDTO } from "../../../app/lib/dto/BlockDTO" +import { Master as PowCluster } from "../../../app/modules/prover/lib/powCluster" -const should = require('should'); const logger = NewLogger() describe('PoW Engine', () => { it('should be configurable', async () => { - const e1 = new PowEngine({ nbCores: 1 } as any, logger); - (await e1.setConf({ cpu: 0.2, prefix: '34' })).should.deepEqual({ cpu: 0.2, prefix: '34' }); - await e1.shutDown() + const e1 = new PowCluster(1, logger); + (await e1.setConf({ cpu: 0.2, prefix: 34 })).should.deepEqual({ cpu: 0.2, prefix: 34 }); + e1.shutDown() }) it('should be able to make a proof', async () => { - const e1 = new PowEngine({ nbCores: 1 } as any, logger); - const block = { number: 35 }; - const zeros = 2; - const highMark = 'A'; + const e1 = new PowCluster(1, logger); + const block = BlockDTO.fromJSONObject({ number: 35, time: 1 }); const pair = { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP' }; - const forcedTime = 1; + const medianTimeBlocks = 20; const avgGenTime = 5 * 60; const proof = await e1.prove({ + maxDuration: 1000, newPoW: { block, - zeros, - highMark, + difficulty: 38, pair, - forcedTime, conf: { medianTimeBlocks, avgGenTime @@ -51,43 +49,35 @@ describe('PoW Engine', () => { } } ) - proof.should.deepEqual({ - pow: { - block: { - number: 35, - time: 1, - inner_hash: '51937F1192447A96537D10968689F4F48859E2DD6F8F9E8DE1006C9697C6C940', - nonce: 212, - hash: '009A52E6E2E4EA7DE950A2DA673114FA55B070EBE350D75FF0C62C6AAE9A37E5', - signature: 'bkmLGX7LNVkuOUMc+/HT6fXJajQtR5uk87fetIntMbGRZjychzu0whl5+AOOGlf+ilp/ara5UK6ppxyPcJIJAg==' - }, - testsCount: 211, - pow: '009A52E6E2E4EA7DE950A2DA673114FA55B070EBE350D75FF0C62C6AAE9A37E5' - } - }); - await e1.shutDown() + assertNotNull(proof); + if (proof) { + proof.should.deepEqual({ + itersCount: 387, + nonce: 10000000000386, + sig: 'PWd3l8U6mFlTNbBqL7MC65jXysFocmWHc0tYG+ggV/JGN0HgdykXKdV1YF8ZR++ZODGfmkxcHz8qoSn653cvAw==', + hash: '0038399DB06BC113FD6832B1D333D3947505115F1AD41846FEE9BAB7E3EDAB0F', + workerId: 0, + }); + } + e1.shutDown() }) it('should be able to stop a proof', async () => { - const e1 = new PowEngine({ nbCores: 1 } as any, logger); - await e1.forceInit() - const block = { number: 26 }; - const zeros = 10; // Requires hundreds of thousands of tries probably - const highMark = 'A'; + const e1 = new PowCluster(1, logger); + + const block = BlockDTO.fromJSONObject({ number: 26 }); const pair = { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP' }; - const forcedTime = 1; const medianTimeBlocks = 20; const avgGenTime = 5 * 60; const proofPromise = e1.prove({ + maxDuration: 1000, newPoW: { block, - zeros, - highMark, + difficulty: 166, pair, - forcedTime, conf: { medianTimeBlocks, avgGenTime @@ -96,9 +86,9 @@ describe('PoW Engine', () => { } ) await new Promise((res) => setTimeout(res, 10)) - await e1.cancel() + e1.cancel(true) // const proof = await proofPromise; // should.not.exist(proof); - await e1.shutDown() + e1.shutDown() }) }) diff --git a/test/fast/prover/prover-pow-3-prover.ts b/test/fast/prover/prover-pow-3-prover.ts index bd9197170..0418f57a7 100644 --- a/test/fast/prover/prover-pow-3-prover.ts +++ b/test/fast/prover/prover-pow-3-prover.ts @@ -11,7 +11,8 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. -import {BlockProver} from "../../../app/modules/prover/lib/blockProver" +import { BlockDTO } from "../../../app/lib/dto/BlockDTO"; +import { BlockProver } from "../../../app/modules/prover/lib/blockProver"; const should = require('should') const winston = require('winston') @@ -19,18 +20,13 @@ const winston = require('winston') describe('PoW block prover', () => { - let prover:BlockProver - - before(() => { - - // Mute logger - winston.remove(winston.transports.Console) - - prover = new BlockProver({ + let proverBuilder = (nbCores: number): BlockProver => { + return new BlockProver({ conf: { - nbCores: 1, - medianTimeBlocks: 20, avgGenTime: 5 * 60, + ecoPow: true, + medianTimeBlocks: 20, + nbCores, pair: { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP' @@ -38,32 +34,41 @@ describe('PoW block prover', () => { }, push: () => {}, logger: winston - } as any) + } as any); + }; + + before(() => { + + // Mute logger + winston.remove(winston.transports.Console) }) it('should be configurable', async () => { + let prover = proverBuilder(1); const res1 = await prover.changeCPU(0.2) res1.should.deepEqual({ cpu: 0.2 }) - const res2 = await prover.changePoWPrefix('34') - res2.should.deepEqual({ prefix: '34' }) + const res2 = await prover.changePoWPrefix(34) + res2.should.deepEqual({ prefix: 34 }) }) it('should be able to make a proof', async () => { + let prover = proverBuilder(1); const block = { number: 35, + time: 1, issuer: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd' } - const forcedTime = 1; - const proof = await prover.prove(block, 24, forcedTime) + + const proof = await prover.prove(BlockDTO.fromJSONObject(block), 24) proof.should.containEql({ version: 10, - nonce: 340000000000034, + nonce: 10000000000006, number: 35, time: 1, currency: '', issuer: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', - signature: 'E2sL6bFC9yOZSqBlSGYF158gsAXfJlWsHRHy1oVn3e7ZR6e6SXQ5Sq2fm1ex6Wv4BqO3n9qq0OHsxajUxshICg==', - hash: '03B176DE082DC451235763D4087305BBD01FD6B6C3248C74EF93B0839DFE9A05', + signature: 'fsEH9xkpuRBkVpCB12ze6G6ibKsa5iAiO1h54wh4Q5ByNepEQjv71af/rDn5Oquql2L+ZmpFcvItT6F6edN1Dw==', + hash: '046CC6CF632EEA14B500EB0E23ED6C734D176E3A214744EB1BED48D7F3396B4E', parameters: '', previousHash: undefined, previousIssuer: undefined, @@ -81,27 +86,41 @@ describe('PoW block prover', () => { }) it('should be able to use a prefix maxed at 899', async () => { + let prover = proverBuilder(1); const block = { number: 1, issuer: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd' } - const params = await prover.changePoWPrefix('899') - params.should.deepEqual({ prefix: '899' }) - const forcedTime = 1; - const proof = await prover.prove(block, 1, forcedTime) - proof.nonce.should.equal(8990000000000001) + const params = await prover.changePoWPrefix(899) + params.should.deepEqual({ prefix: 899 }) + + const proof = await prover.prove(BlockDTO.fromJSONObject(block), 1) + proof.nonce.should.equal(8990000000000000) String(proof.nonce).should.have.length(16) }) + it('should use a single worker on eco mode', async () => { + let prover = proverBuilder(4); + const block = { + number: 1, + issuer: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', + powMin: 1 + } + const proof = await prover.prove(BlockDTO.fromJSONObject(block), 2) + proof.nonce.should.equal(10000000000000) + }) + it('should be able to stop a proof', async () => { + let prover = proverBuilder(1); const block = { number: 35, + time: 1, issuer: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd' } - const forcedTime = 1; - const proofPromise = prover.prove(block, 70, forcedTime) + + const proofPromise = prover.prove(BlockDTO.fromJSONObject(block), 150) await new Promise((res) => setTimeout(res, 20)) - await prover.cancel() + prover.cancel(true) let err = '' try { await proofPromise diff --git a/test/integration/block-generation/start-generate-blocks.ts b/test/integration/block-generation/start-generate-blocks.ts index 04c52d83b..46c9174a6 100644 --- a/test/integration/block-generation/start-generate-blocks.ts +++ b/test/integration/block-generation/start-generate-blocks.ts @@ -55,7 +55,6 @@ describe("Generation", function() { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP' }, - powDelay: 1 }, commonConf)); s2 = NewTestingServer( @@ -67,7 +66,6 @@ describe("Generation", function() { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F' }, - powDelay: 1 }, commonConf)); cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 }); @@ -107,8 +105,6 @@ describe("Generation", function() { s1.startBlockComputation(); await until(s2, 'block', 1); s2.startBlockComputation(); - s1.conf.powDelay = 2000; - s2.conf.powDelay = 2000; await Promise.all([ serverWaitBlock(s1._server, 3), serverWaitBlock(s2._server, 3) diff --git a/test/integration/branches/branches_revert2.ts b/test/integration/branches/branches_revert2.ts index e1f1010b8..ea075fb34 100644 --- a/test/integration/branches/branches_revert2.ts +++ b/test/integration/branches/branches_revert2.ts @@ -120,7 +120,7 @@ describe("Revert two blocks", function() { res.sources[0].should.have.property('type').equal('D'); res.sources[0].should.have.property('noffset').equal(2); res.sources[0].should.have.property('amount').equal(120); - res.sources[1].should.have.property('identifier').equal('5F91D05DD1B1C9CAFDBCF5538C63DAF770A20790D08C9A88E0625A8D5599825D'); + res.sources[1].should.have.property('identifier').equal('CA7B64FEC4D7EA06B3DAC90E3B7F187D80F7A027AC33EAE15464B23FE382F1C2'); res.sources[1].should.have.property('type').equal('T'); res.sources[1].should.have.property('noffset').equal(0); res.sources[1].should.have.property('amount').equal(51); @@ -260,7 +260,7 @@ describe("Revert two blocks", function() { return expectAnswer(rp('http://127.0.0.1:7712/tx/sources/HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd'), (body:string) => { let res = JSON.parse(body); res.sources.should.have.length(1); - res.sources[0].should.have.property('identifier').equal('EE74E456FC16888FF24C3A9749B9E3A8D5005A9CCE988B2CFF4619AFEA50F890'); + res.sources[0].should.have.property('identifier').equal('45EE7F358AF0D87CD4286E1E0ED48A6E97E16BACD98D52BF114F9B6E5A40EDAC'); res.sources[0].should.have.property('type').equal('T'); res.sources[0].should.have.property('noffset').equal(1); res.sources[0].should.have.property('amount').equal(101); @@ -275,7 +275,7 @@ describe("Revert two blocks", function() { res.sources[0].should.have.property('type').equal('D'); res.sources[0].should.have.property('noffset').equal(2); res.sources[0].should.have.property('amount').equal(120); - res.sources[1].should.have.property('identifier').equal('EE74E456FC16888FF24C3A9749B9E3A8D5005A9CCE988B2CFF4619AFEA50F890'); + res.sources[1].should.have.property('identifier').equal('45EE7F358AF0D87CD4286E1E0ED48A6E97E16BACD98D52BF114F9B6E5A40EDAC'); res.sources[1].should.have.property('type').equal('T'); res.sources[1].should.have.property('noffset').equal(0); res.sources[1].should.have.property('amount').equal(19); diff --git a/test/integration/fork-resolution/coming-back-with-less-than-sigqty.ts b/test/integration/fork-resolution/coming-back-with-less-than-sigqty.ts index e89ba3637..19ac5f83c 100644 --- a/test/integration/fork-resolution/coming-back-with-less-than-sigqty.ts +++ b/test/integration/fork-resolution/coming-back-with-less-than-sigqty.ts @@ -50,7 +50,7 @@ describe('A member coming back with less than `sigQty` valid certs total', () => test('(t = 5) cat & tac certify each other', async (s1, cat, tac, toc) => { await s1.commit({ time: now + 5 }) - await s1.commit({ time: now + 5 }) + const b3 = await s1.commit({ time: now + 5 }) await new Promise(resolve => setTimeout(resolve, 500)) // cat and tac certify each other to stay in the WoT await tac.cert(cat) diff --git a/test/integration/fork-resolution/register-fork-blocks.ts b/test/integration/fork-resolution/register-fork-blocks.ts index d448373d5..51601f79a 100644 --- a/test/integration/fork-resolution/register-fork-blocks.ts +++ b/test/integration/fork-resolution/register-fork-blocks.ts @@ -135,11 +135,11 @@ describe("Fork blocks", function() { it('should exist a different third block on each node', async () => { await s1.expectJSON('/blockchain/current', { number: 3, - hash: "2C0451EA29CA759AE8296D0751989067AEEC35050BC8CD5623B05C0665C24471" + hash: "456168AF8EDC77DCE3314B5DECE31807E3CF8DE398821F9009A21E7503E3C13B" }) await s2.expectJSON('/blockchain/current', { number: 3, - hash: "33038E3E9C1BFB8328234CDD42D1F47B8D362A78161B03E43732CA7432D10A76" + hash: "B960BB85E8E280778DA175708E1BB0CFF29743997B036D9915F39D6B0A80450F" }) }) @@ -147,16 +147,16 @@ describe("Fork blocks", function() { await s1.expect('/blockchain/branches', (res:HttpBranches) => { assert.equal(res.blocks.length, 2) assert.equal(res.blocks[0].number, 3) - assert.equal(res.blocks[0].hash, '33038E3E9C1BFB8328234CDD42D1F47B8D362A78161B03E43732CA7432D10A76') + assert.equal(res.blocks[0].hash, 'B960BB85E8E280778DA175708E1BB0CFF29743997B036D9915F39D6B0A80450F') assert.equal(res.blocks[1].number, 3) - assert.equal(res.blocks[1].hash, '2C0451EA29CA759AE8296D0751989067AEEC35050BC8CD5623B05C0665C24471') + assert.equal(res.blocks[1].hash, '456168AF8EDC77DCE3314B5DECE31807E3CF8DE398821F9009A21E7503E3C13B') }) await s2.expect('/blockchain/branches', (res:HttpBranches) => { assert.equal(res.blocks.length, 2) assert.equal(res.blocks[0].number, 3) - assert.equal(res.blocks[0].hash, '2C0451EA29CA759AE8296D0751989067AEEC35050BC8CD5623B05C0665C24471') + assert.equal(res.blocks[0].hash, '456168AF8EDC77DCE3314B5DECE31807E3CF8DE398821F9009A21E7503E3C13B') assert.equal(res.blocks[1].number, 3) - assert.equal(res.blocks[1].hash, '33038E3E9C1BFB8328234CDD42D1F47B8D362A78161B03E43732CA7432D10A76') + assert.equal(res.blocks[1].hash, 'B960BB85E8E280778DA175708E1BB0CFF29743997B036D9915F39D6B0A80450F') }) }) @@ -205,11 +205,11 @@ describe("Fork blocks", function() { it('should exist a same current block on each node', async () => { await s1.expectJSON('/blockchain/current', { number: 8, - hash: "C41F10519A24950C051F3ABBBF71775D9EF836374EF538897DFFF08E7A3F5E50" + hash: "77BDFF0C817C0BA831A01C36BD4A68393DF1B2FAC9C146663F442104B2694E7B" }) await s2.expectJSON('/blockchain/current', { number: 8, - hash: "C41F10519A24950C051F3ABBBF71775D9EF836374EF538897DFFF08E7A3F5E50" + hash: "77BDFF0C817C0BA831A01C36BD4A68393DF1B2FAC9C146663F442104B2694E7B" }) }) @@ -217,20 +217,20 @@ describe("Fork blocks", function() { await s1.expect('/blockchain/branches', (res:HttpBranches) => { assert.equal(res.blocks.length, 3) assert.equal(res.blocks[0].number, 3) - assert.equal(res.blocks[0].hash, '33038E3E9C1BFB8328234CDD42D1F47B8D362A78161B03E43732CA7432D10A76') // This is s2 fork! + assert.equal(res.blocks[0].hash, '5BE17620F8C91240CE1D4BB6657B22EC2C58D138CD130038B937BE6988380F92') // This is s2 fork! assert.equal(res.blocks[1].number, 3) - assert.equal(res.blocks[1].hash, '7A1982E7746DE1993F8900C2D453A1E7C010B2BDF304DB83BCBF84932CE8A630') + assert.equal(res.blocks[1].hash, 'B960BB85E8E280778DA175708E1BB0CFF29743997B036D9915F39D6B0A80450F') assert.equal(res.blocks[2].number, 8) - assert.equal(res.blocks[2].hash, 'C41F10519A24950C051F3ABBBF71775D9EF836374EF538897DFFF08E7A3F5E50') + assert.equal(res.blocks[2].hash, '77BDFF0C817C0BA831A01C36BD4A68393DF1B2FAC9C146663F442104B2694E7B') }) await s2.expect('/blockchain/branches', (res:HttpBranches) => { assert.equal(res.blocks.length, 3) assert.equal(res.blocks[0].number, 3) - assert.equal(res.blocks[0].hash, '33038E3E9C1BFB8328234CDD42D1F47B8D362A78161B03E43732CA7432D10A76') // This is s2 fork! + assert.equal(res.blocks[0].hash, '5BE17620F8C91240CE1D4BB6657B22EC2C58D138CD130038B937BE6988380F92') // This is s2 fork! assert.equal(res.blocks[1].number, 3) - assert.equal(res.blocks[1].hash, '7A1982E7746DE1993F8900C2D453A1E7C010B2BDF304DB83BCBF84932CE8A630') + assert.equal(res.blocks[1].hash, 'B960BB85E8E280778DA175708E1BB0CFF29743997B036D9915F39D6B0A80450F') assert.equal(res.blocks[2].number, 8) - assert.equal(res.blocks[2].hash, 'C41F10519A24950C051F3ABBBF71775D9EF836374EF538897DFFF08E7A3F5E50') + assert.equal(res.blocks[2].hash, '77BDFF0C817C0BA831A01C36BD4A68393DF1B2FAC9C146663F442104B2694E7B') }) }) diff --git a/test/integration/membership_chainability.ts b/test/integration/membership_chainability.ts index bf9367230..fababd6dc 100644 --- a/test/integration/membership_chainability.ts +++ b/test/integration/membership_chainability.ts @@ -40,7 +40,7 @@ describe("Membership chainability", function() { await s1.commit({ time: now }) await s1.commit({ time: now }) await s1.commit({ time: now, actives: [ - 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd:rppB5NEwmdMUCxw3N/QPMk+V1h2Jpn0yxTzdO2xxcNN3MACv6x8vNTChWwM6DOq+kXiQHTczFzoux+82WkMfDQ==:1-12D7B9BEBE941F6929A4A61CDC06DEEEFCB00FD1DA72E42FFF7B19A338D421E1:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:cat' + 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd:dCg82/KH+9z31EPMye0NUf5XyXzfDyT97jV9U+okw2nDZRjMuhVvcOBmXFlqMMGRvyvdT9p8H0FVkmt+kUHeDA==:1-138EEDBDD80C5AD620D3F7376AD4EE26A9B7D886647CF629B363C16695087307:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:cat' ]}) CommonConstants.DUBP_NEXT_VERSION = nowVersion }) @@ -87,9 +87,10 @@ describe("Membership chainability", function() { }) it('should refuse a block with a too early membership in it', async () => { + let ms = await cat.makeMembership('IN'); await s1.commitWaitError({ time: now + 20, - actives: ['HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd:SiCD1MSyDiZKWLp/SP/2Vj5T3JMgjNnIIKMI//yvKRdWMzKjEn6/ZT+TCjyjnl85qRfmEuWv1jLmQSoe8GXSDg==:1-0DEE2A8EA05322FCC4355D5F0E7A2830F4A22ACEBDC4B62399484E091A5CCF27:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:cat'] + actives: ['HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd:T98oZvPVX299GN7mj8ekAjf9JOOnvPFuXunzTmYVEEL0pM3bjeYoMTDOHoxUPkXZgPrnh354HTHcTpUrJg8BDg==:1-3D4277FF4E3F3B24ECC7B541EA36296BECE21CE89FF4915EBC7A66AFA10D5251:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:cat'] }, 'ruleMembershipPeriod') }) diff --git a/test/integration/proof-of-work/continuous-proof.ts b/test/integration/proof-of-work/continuous-proof.ts index 1db5b4f28..bb9d1edc6 100644 --- a/test/integration/proof-of-work/continuous-proof.ts +++ b/test/integration/proof-of-work/continuous-proof.ts @@ -20,91 +20,89 @@ const should = require('should'); const NB_CORES_FOR_COMPUTATION = 1 // For simple tests. Can be changed to test multiple cores. -let s1:TestingServer, s2:TestingServer, s3:TestingServer, i1:TestUser, i2:TestUser +async function serverBuilder(): Promise<TestingServer> { + let s =NewTestingServer({ + cpu: 1, + nbCores: NB_CORES_FOR_COMPUTATION, + powMin: 1, + pair: { + pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', + sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP' + } + }); + + let i1 = new TestUser('i1', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s }); + let i2 = new TestUser('i2', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s }); + + + await s.prepareForNetwork(); + await i1.createIdentity(); + await i2.createIdentity(); + await i1.cert(i2); + await i2.cert(i1); + await i1.join(); + await i2.join(); + await s.commit(); + await s.closeCluster(); + return s; +} describe("Continous proof-of-work", function() { - before(async () => { - - s1 = NewTestingServer({ - cpu: 1, - nbCores: NB_CORES_FOR_COMPUTATION, - powDelay: 100, - powMin: 1, - pair: { - pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', - sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP' - } - }) - - i1 = new TestUser('i1', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 }); - i2 = new TestUser('i2', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 }); - - await s1.prepareForNetwork(); - await i1.createIdentity(); - await i2.createIdentity(); - await i1.cert(i2); - await i2.cert(i1); - await i1.join(); - await i2.join(); - await s1.commit(); - await s1.closeCluster(); - }) - it('should automatically stop waiting if nothing happens', async () => { - s1.conf.powSecurityRetryDelay = 10; + let s = await serverBuilder(); let start = Date.now(); - s1.startBlockComputation(); - // s1.permaProver.should.have.property('loops').equal(0); - await s1.until('block', 1); - // s1.permaProver.should.have.property('loops').equal(1); + s.startBlockComputation(); + //s1.permaProver.should.have.property('loops').equal(0); + await s.until('block', 1); + //s1.permaProver.should.have.property('loops').equal(0); (start - Date.now()).should.be.belowOrEqual(1000); - await s1.stopBlockComputation(); + await s.stopBlockComputation(); + //s1.permaProver.should.have.property('loops').equal(0); await new Promise((resolve) => setTimeout(resolve, 100)); - // s1.permaProver.should.have.property('loops').equal(2); - s1.conf.powSecurityRetryDelay = 10 * 60 * 1000; - await s1.revert(); - s1.permaProver.loops = 0; - await s1.stopBlockComputation(); + //s1.permaProver.should.have.property('loops').equal(0); + await s.revert(); + s.permaProver.loops = 0; + await s.stopBlockComputation(); }) it('should be able to start generation and find a block', async () => { - s1.permaProver.should.have.property('loops').equal(0); + let s = await serverBuilder(); + //s1.permaProver.should.have.property('loops').equal(0); await [ - s1.startBlockComputation(), - s1.until('block', 2) + s.startBlockComputation(), + s.until('block', 2) ]; // Should have made: - // * 1 loop between block 0 and 1 by waiting // * 1 loop for making b#1 - // * 1 loop by waiting between b#1 and b#2 // * 1 loop for making b#2 await new Promise((resolve) => setTimeout(resolve, 100)); - // s1.permaProver.should.have.property('loops').equal(4); - await s1.stopBlockComputation(); + //s1.permaProver.should.have.property('loops').equal(1); + await s.stopBlockComputation(); // If we wait a bit, the loop should be ended await new Promise((resolve) => setTimeout(resolve, 100)); - // s1.permaProver.should.have.property('loops').equal(5); - await s1.stopBlockComputation(); + //s1.permaProver.should.have.property('loops').equal(1); + await s.stopBlockComputation(); }) it('should be able to cancel generation because of a blockchain switch', async () => { - // s1.permaProver.should.have.property('loops').equal(5); - s1.startBlockComputation(); - await s1.until('block', 1); + let s = await serverBuilder(); + //s1.permaProver.should.have.property('loops').equal(1); + s.startBlockComputation(); + await s.until('block', 1); // * 1 loop for making b#3 await new Promise((resolve) => setTimeout(resolve, 100)); - // s1.permaProver.should.have.property('loops').equal(6); - await s1.permaProver.blockchainChanged(); + await s.permaProver.blockchainChanged(); await new Promise((resolve) => setTimeout(resolve, 100)); // * 1 loop for waiting for b#4 but being interrupted - s1.permaProver.should.have.property('loops').greaterThanOrEqual(3); - await s1.stopBlockComputation(); + s.permaProver.should.have.property('loops').greaterThanOrEqual(2); + await s.stopBlockComputation(); // If we wait a bit, the loop should be ended await new Promise((resolve) => setTimeout(resolve, 200)); - s1.permaProver.should.have.property('loops').greaterThanOrEqual(4); + s.permaProver.should.have.property('loops').greaterThanOrEqual(3); + await s.stopBlockComputation(); }) it('testing proof-of-work during a block pulling', async () => { @@ -133,6 +131,6 @@ describe("Continous proof-of-work", function() { const current = await s3.get('/blockchain/current') await s3.stopBlockComputation(); current.number.should.be.aboveOrEqual(14) - await s1.closeCluster() + await s3.closeCluster() }) }); diff --git a/test/integration/proof-of-work/proof-of-work.ts b/test/integration/proof-of-work/proof-of-work.ts index 29b3a7306..112862c62 100644 --- a/test/integration/proof-of-work/proof-of-work.ts +++ b/test/integration/proof-of-work/proof-of-work.ts @@ -13,6 +13,7 @@ import {NewLogger} from "../../../app/lib/logger" import {BlockProver} from "../../../app/modules/prover/lib/blockProver" +import { BlockDTO } from "../../../app/lib/dto/BlockDTO"; const should = require('should'); const logger = NewLogger(); @@ -25,11 +26,9 @@ conf.cpu keyring from Key ***/ -const intermediateProofs:any[] = []; const NB_CORES_FOR_COMPUTATION = 1 // For simple tests. Can be changed to test multiple cores. const prover = new BlockProver({ - push: (data:any) => intermediateProofs.push(data), conf: { nbCores: NB_CORES_FOR_COMPUTATION, cpu: 1.0, // 80%, @@ -48,14 +47,12 @@ const MUST_START_WITH_TWO_ZEROS = 32; describe("Proof-of-work", function() { it('should be able to find an easy PoW', async () => { - let block = await prover.prove({ + let block = await prover.prove(BlockDTO.fromJSONObject({ issuer: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', + time: now, number: 2 - }, MUST_START_WITH_TWO_ZEROS, now); - block.hash.should.match(/^0/); - intermediateProofs.length.should.be.greaterThan(0); - intermediateProofs[intermediateProofs.length - 1].pow.should.have.property('found').equal(true); - intermediateProofs[intermediateProofs.length - 1].pow.should.have.property('hash').equal(block.hash); + }), MUST_START_WITH_TWO_ZEROS); + block.hash.should.match(/^00/); }) // Too randomly successing test @@ -117,7 +114,7 @@ describe("Proof-of-work", function() { // it('should be able to cancel a waiting on other PoW receival', () => co(function*() { // const now = 1474464481; // const res = await toolbox.simpleNetworkOf2NodesAnd2Users({ - // powSecurityRetryDelay: 10 * 60 * 1000, + // powMaxDuration: 10 * 60 * 1000, // powMaxHandicap: 8, // percentRot: 1, // powMin: 35 diff --git a/test/integration/tools/shutdown-engine.ts b/test/integration/tools/shutdown-engine.ts index b58c37666..e0f305b4b 100644 --- a/test/integration/tools/shutdown-engine.ts +++ b/test/integration/tools/shutdown-engine.ts @@ -15,7 +15,6 @@ import {TestingServer} from "./toolbox" export async function shutDownEngine(server:TestingServer) { if ((server as any)._utProver) { - const farm = await (server as any)._utProver.getWorker() - return farm.shutDownEngine() + await (server as any)._utProver.workerFarm.shutDownEngine() } } diff --git a/test/integration/tools/toolbox.ts b/test/integration/tools/toolbox.ts index be44dd60d..fe17319d8 100644 --- a/test/integration/tools/toolbox.ts +++ b/test/integration/tools/toolbox.ts @@ -229,7 +229,7 @@ export const fakeSyncServer = async (currency: string, readBlocksMethod:any, rea export const server = (conf:any) => NewTestingServer(conf) export const simpleTestingServer = (conf:any) => NewTestingServer(conf) -export const NewTestingServer = (conf:any) => { +export const NewTestingServer = (conf:any): TestingServer => { const host = conf.host ||Â HOST const port = conf.port ||Â PORT++ const commonConf = { @@ -333,6 +333,9 @@ export class TestingServer { private port:number, private server:Server) { + // Disable eco mode on tests + server.conf.ecoPow = false; + ProverDependency.duniter.methods.hookServer(server) server.addEndpointsDefinitions(async () => { @@ -683,8 +686,7 @@ export class TestingServer { async closeCluster() { const server:Server = this.server if ((server as any)._utProver) { - const farm = await (server as any)._utProver.getWorker() - await farm.shutDownEngine() + await (server as any)._utProver.workerFarm.shutDownEngine(); } } diff --git a/test/integration/transactions/transactions-test.ts b/test/integration/transactions/transactions-test.ts index c485b1922..e7b602ea3 100644 --- a/test/integration/transactions/transactions-test.ts +++ b/test/integration/transactions/transactions-test.ts @@ -117,12 +117,12 @@ describe("Testing transactions", function() { const txSrc = (Underscore.findWhere(res.sources, { type: 'T' }) as any) assert.equal(txSrc.amount, 690); }) - const tx = await s1.get('/tx/hash/B6DCADFB841AC05A902741A8772A70B4086D5AEAB147AD48987DDC3887DD55C8') + const tx = await s1.get('/tx/hash/D6AD1C6175A052C521DA41BEA3206949397BDEA04230AD3D4A236121702FBC28') assert.notEqual(tx, null) assert.deepEqual(tx, { "comment": "", "currency": "duniter_unit_test_currency", - "hash": "B6DCADFB841AC05A902741A8772A70B4086D5AEAB147AD48987DDC3887DD55C8", + "hash": "D6AD1C6175A052C521DA41BEA3206949397BDEA04230AD3D4A236121702FBC28", "inputs": [ "1200:0:D:DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV:2" ], @@ -136,7 +136,7 @@ describe("Testing transactions", function() { ], "raw": "", "signatures": [ - "Wy2tAKp/aFH2hqZJ5qnUFUNEukFbHwaR4v9gZ/aGoySPfXovDwld9W15w8C0ojVYbma9nlU3eLkVqzVBYz3lAw==" + "1SynH9V2C2vDizC6wiri82RDwKy35pVSTOcgRqaVAni0/opWVD19bTgktLUJMOscOcdx8xtcs2z/BOC/923ICA==" ], "unlocks": [ "0:SIG(0)" diff --git a/test/integration/wot/wotb.ts b/test/integration/wot/wotb.ts index 64ac82aec..980338536 100644 --- a/test/integration/wot/wotb.ts +++ b/test/integration/wot/wotb.ts @@ -74,46 +74,10 @@ describe("WOTB module", () => { sigQty: 1, dt: 1, ud0: 120 }, commonConf)); - s2 = NewTestingServer( - Underscore.extend({ - name: 'bb41', - memory: MEMORY_MODE, - port: '9338', - pair: { - pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', - sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP' - }, - rootoffset: 10, - sigQty: 1, dt: 1, ud0: 120, - msValidity: 400 // Memberships expire after 400 second delay - }, commonConf)); - - s3 = NewTestingServer( - Underscore.extend({ - name: 'bb11', - memory: MEMORY_MODE, - port: '9339', - pair: { - pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', - sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7' - }, - rootoffset: 10, - sigQty: 1, dt: 1, ud0: 120, - sigValidity: 1400, sigPeriod: 0 - }, commonConf)); - cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 }); toc = new TestUser('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 }); tic = new TestUser('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 }); - cat2 = new TestUser('cat2', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s2 }); - toc2 = new TestUser('toc2', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s2 }); - tic2 = new TestUser('tic2', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s2 }); - - cat3 = new TestUser('cat3', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s3 }); - toc3 = new TestUser('toc3', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s3 }); - tic3 = new TestUser('tic3', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s3 }); - /** * cat <==> toc */ @@ -190,9 +154,27 @@ describe("WOTB module", () => { let wotb:Wot before(async () => { - /** - * tic <==> cat <==> toc - */ + s2 = NewTestingServer( + Underscore.extend({ + name: 'bb41', + memory: MEMORY_MODE, + port: '9338', + pair: { + pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', + sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP' + }, + rootoffset: 10, + sigQty: 1, dt: 1, ud0: 120, + msValidity: 400 // Memberships expire after 400 second delay + }, commonConf)); + + cat2 = new TestUser('cat2', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s2 }); + toc2 = new TestUser('toc2', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s2 }); + tic2 = new TestUser('tic2', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s2 }); + + // + // tic <==> cat <==> toc + // await s2.initWithDAL().then(BmaDependency.duniter.methods.bma).then((bmapi) => bmapi.openConnections()); wotb = s2.dal.wotb; await cat2.createIdentity(); @@ -270,11 +252,29 @@ describe("WOTB module", () => { }); }); - describe("Server 3", () => { + describe.skip("Server 3", () => { let wotb:Wot before(async () => { + s3 = NewTestingServer( + Underscore.extend({ + name: 'bb11', + memory: MEMORY_MODE, + port: '9339', + pair: { + pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', + sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7' + }, + rootoffset: 10, + sigQty: 1, dt: 1, ud0: 120, + sigValidity: 1400, sigPeriod: 0 + }, commonConf)); + + cat3 = new TestUser('cat3', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s3 }); + toc3 = new TestUser('toc3', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s3 }); + tic3 = new TestUser('tic3', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s3 }); + await s3.initWithDAL().then(BmaDependency.duniter.methods.bma).then((bmapi) => bmapi.openConnections()); wotb = s3.dal.wotb; await cat3.createIdentity(); @@ -297,9 +297,9 @@ describe("WOTB module", () => { await s3.commit({ time: now + 1200 }); - /** - * cat <==> tic - */ + // + // cat <==> tic + // wotb.isEnabled(0).should.equal(true); wotb.isEnabled(1).should.equal(true); // cat3 <==> tic3 @@ -330,9 +330,9 @@ describe("WOTB module", () => { time: now + 4000 }); // MedianTime is now +1000 for next certs - /** - * cat <==> tic --> toc - */ + // + // cat <==> tic --> toc + // wotb.isEnabled(0).should.equal(true); wotb.isEnabled(1).should.equal(true); wotb.isEnabled(2).should.equal(true); @@ -349,9 +349,9 @@ describe("WOTB module", () => { await s3.commit({ time: now + 4000 }); - /** - * cat tic <==> toc - */ + // + // cat tic <==> toc + // wotb.isEnabled(0).should.equal(true); // But marked as to kick: cannot issue new links wotb.isEnabled(1).should.equal(true); wotb.isEnabled(2).should.equal(true); @@ -367,9 +367,9 @@ describe("WOTB module", () => { await s3.commit({ time: now + 2500 }); - /** - * tic <-- toc - */ + // + // tic <-- toc + // wotb.isEnabled(0).should.equal(false); wotb.isEnabled(1).should.equal(true); wotb.isEnabled(2).should.equal(true); @@ -387,9 +387,9 @@ describe("WOTB module", () => { await s3.commit({ time: now + 5000 }); - /** - * cat <-- tic <-- [toc] - */ + // + // cat <-- tic <-- [toc] + // wotb.isEnabled(0).should.equal(true); wotb.isEnabled(1).should.equal(true); wotb.isEnabled(2).should.equal(false); @@ -467,8 +467,8 @@ describe("WOTB module", () => { should.equal(wotb.getWoTSize(), 0) }); - after(() => { - CommonConstants.DUBP_NEXT_VERSION = 10 - }) }); + after(() => { + CommonConstants.DUBP_NEXT_VERSION = 10 + }) }); -- GitLab