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