From 0eecc2e54b8bd81cd6cbf9cd9015679cb065388c Mon Sep 17 00:00:00 2001
From: cgeek <cem.moreau@gmail.com>
Date: Mon, 17 Jul 2017 15:03:12 +0200
Subject: [PATCH] [enh] #1037 Migrate Services

---
 .eslintignore                                 |   1 +
 .gitignore                                    |   1 +
 app/lib/blockchain/DuniterBlockchain.ts       |   4 +-
 app/lib/computation/BlockchainContext.ts      |   2 +-
 app/lib/dal/fileDAL.ts                        |   6 +-
 app/lib/dal/sqliteDAL/IdentityDAL.ts          |  10 +-
 app/lib/dto/ConfDTO.ts                        |  13 +-
 app/service/BlockchainService.js              | 395 ----------------
 app/service/BlockchainService.ts              | 428 ++++++++++++++++++
 ...bstractService.js => GlobalFifoPromise.ts} |  21 +-
 ...{IdentityService.js => IdentityService.ts} | 173 +++----
 app/service/MembershipService.js              |  65 ---
 app/service/MembershipService.ts              |  66 +++
 .../{PeeringService.js => PeeringService.ts}  | 152 ++++---
 app/service/TransactionsService.js            |  59 ---
 app/service/TransactionsService.ts            |  58 +++
 server.js                                     |  24 +-
 test/eslint.js                                |  32 +-
 18 files changed, 796 insertions(+), 714 deletions(-)
 delete mode 100644 app/service/BlockchainService.js
 create mode 100644 app/service/BlockchainService.ts
 rename app/service/{AbstractService.js => GlobalFifoPromise.ts} (75%)
 rename app/service/{IdentityService.js => IdentityService.ts} (53%)
 delete mode 100644 app/service/MembershipService.js
 create mode 100644 app/service/MembershipService.ts
 rename app/service/{PeeringService.js => PeeringService.ts} (68%)
 delete mode 100644 app/service/TransactionsService.js
 create mode 100644 app/service/TransactionsService.ts

diff --git a/.eslintignore b/.eslintignore
index f8cb13760..eddb5e16e 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -10,5 +10,6 @@ app/lib/dal/sqliteDAL/*.js
 app/lib/dal/sqliteDAL/index/*.js
 app/lib/dal/fileDALs/*.js
 app/lib/dal/fileDAL.js
+app/service/*.js
 test/*.js
 test/**/*.js
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index f386f2a39..f19d2281f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -47,4 +47,5 @@ app/lib/dal/sqliteDAL/*.js*
 app/lib/dal/sqliteDAL/index/*.js*
 app/lib/dal/fileDALs/*.js*
 app/lib/dal/fileDAL.js*
+app/service/*.js*
 app/lib/wot.js*
\ No newline at end of file
diff --git a/app/lib/blockchain/DuniterBlockchain.ts b/app/lib/blockchain/DuniterBlockchain.ts
index 2de2ea33e..b9615249d 100644
--- a/app/lib/blockchain/DuniterBlockchain.ts
+++ b/app/lib/blockchain/DuniterBlockchain.ts
@@ -161,7 +161,7 @@ export class DuniterBlockchain extends MiscIndexedBlockchain {
     return { index, HEAD }
   }
 
-  async pushTheBlock(obj:BlockDTO, index:IndexEntry[], HEAD:DBHead, conf:ConfDTO, dal:any, logger:any) {
+  async pushTheBlock(obj:BlockDTO, index:IndexEntry[], HEAD:DBHead | null, conf:ConfDTO, dal:any, logger:any) {
     const start = Date.now();
     const block = new Block(obj);
     try {
@@ -188,7 +188,7 @@ export class DuniterBlockchain extends MiscIndexedBlockchain {
     // await supra.recordIndex(index)
   }
 
-  async saveBlockData(current:DBBlock, block:BlockDTO, conf:ConfDTO, dal:any, logger:any, index:IndexEntry[], HEAD:DBHead) {
+  async saveBlockData(current:DBBlock, block:BlockDTO, conf:ConfDTO, dal:any, logger:any, index:IndexEntry[], HEAD:DBHead | null) {
     if (block.number == 0) {
       await this.saveParametersForRoot(block, conf, dal);
     }
diff --git a/app/lib/computation/BlockchainContext.ts b/app/lib/computation/BlockchainContext.ts
index 88ab213cc..2b267ece2 100644
--- a/app/lib/computation/BlockchainContext.ts
+++ b/app/lib/computation/BlockchainContext.ts
@@ -105,7 +105,7 @@ export class BlockchainContext {
     return this.blockchain.checkBlock(block, withPoWAndSignature, this.conf, this.dal)
   }
 
-  async addBlock(obj: BlockDTO, index: any, HEAD: DBHead): Promise<any> {
+  async addBlock(obj: BlockDTO, index: any = null, HEAD: DBHead | null = null): Promise<any> {
     const block = await this.blockchain.pushTheBlock(obj, index, HEAD, this.conf, this.dal, this.logger)
     this.vHEAD_1 = this.vHEAD = this.HEADrefreshed = null
     return block
diff --git a/app/lib/dal/fileDAL.ts b/app/lib/dal/fileDAL.ts
index 3258a8a89..b2f5f0ca3 100644
--- a/app/lib/dal/fileDAL.ts
+++ b/app/lib/dal/fileDAL.ts
@@ -361,7 +361,11 @@ export class FileDAL {
     const nonPendings = _.filter(writtens, (w:IindexEntry) => {
       return _.where(pendings, { pubkey: w.pub }).length == 0;
     });
-    const found = pendings.concat(nonPendings);
+    const found = pendings.concat(nonPendings.map((i:any) => {
+      // Use the correct field
+      i.pubkey = i.pub
+      return i
+    }));
     return await Promise.all(found.map(async (f:any) => {
       const ms = await this.mindexDAL.getReducedMS(f.pub);
       if (ms) {
diff --git a/app/lib/dal/sqliteDAL/IdentityDAL.ts b/app/lib/dal/sqliteDAL/IdentityDAL.ts
index 173d737f2..1f1261d22 100644
--- a/app/lib/dal/sqliteDAL/IdentityDAL.ts
+++ b/app/lib/dal/sqliteDAL/IdentityDAL.ts
@@ -19,8 +19,12 @@ export interface DBIdentity {
   hash: string
   written: boolean
   wotb_id: number | null
-  expires_on: number,
-  certsCount: number,
+  revoked_on: number | null
+  expires_on: number
+}
+
+export interface DBSandboxIdentity extends DBIdentity {
+  certsCount: number
   ref_block: number
 }
 
@@ -164,7 +168,7 @@ export class IdentityDAL extends AbstractSQLite<DBIdentity> {
     return this.query('SELECT * FROM sandbox_idty LIMIT ' + (this.sandbox.maxSize), [])
   }
 
-  sandbox = new SandBox(constants.SANDBOX_SIZE_IDENTITIES, this.getSandboxIdentities.bind(this), (compared:DBIdentity, reference:DBIdentity) => {
+  sandbox = new SandBox(constants.SANDBOX_SIZE_IDENTITIES, this.getSandboxIdentities.bind(this), (compared:DBSandboxIdentity, reference:DBSandboxIdentity) => {
     if (compared.certsCount < reference.certsCount) {
       return -1;
     }
diff --git a/app/lib/dto/ConfDTO.ts b/app/lib/dto/ConfDTO.ts
index 6b135ca6a..7143a0634 100644
--- a/app/lib/dto/ConfDTO.ts
+++ b/app/lib/dto/ConfDTO.ts
@@ -1,3 +1,8 @@
+export interface Keypair {
+  pub: string
+  sec: string
+}
+
 export class ConfDTO {
 
   constructor(
@@ -31,9 +36,15 @@ export class ConfDTO {
     public idtyWindow: number,
     public msWindow: number,
     public sigWindow: number,
+    public swichOnTimeAheadBy: number,
+    public pair: Keypair | null,
+    public remoteport: number,
+    public remotehost: string,
+    public remoteipv4: string,
+    public remoteipv6: string,
 ) {}
 
   static mock() {
-    return new ConfDTO("", [], [], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, false, 0, false, 0, 0, 0, 0)
+    return new ConfDTO("", [], [], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, false, 0, false, 0, 0, 0, 0, 0, null, 0, "", "", "")
   }
 }
\ No newline at end of file
diff --git a/app/service/BlockchainService.js b/app/service/BlockchainService.js
deleted file mode 100644
index 1e1ace7ab..000000000
--- a/app/service/BlockchainService.js
+++ /dev/null
@@ -1,395 +0,0 @@
-"use strict";
-
-const _               = require('underscore');
-const co              = require('co');
-const parsers         = require('duniter-common').parsers;
-const rules           = require('../lib/rules')
-const constants       = require('../lib/constants');
-const blockchainCtx   = require('../lib/computation/BlockchainContext').BlockchainContext
-const Block           = require('../lib/entity/block');
-const BlockDTO        = require('../lib/dto/BlockDTO').BlockDTO
-const Identity        = require('../lib/entity/identity');
-const Transaction     = require('../lib/entity/transaction');
-const AbstractService = require('./AbstractService');
-const QuickSynchronizer = require('../lib/computation/QuickSync').QuickSynchronizer
-
-const CHECK_ALL_RULES = true;
-
-module.exports = (server) => {
-  return new BlockchainService(server);
-};
-
-function BlockchainService (server) {
-
-  AbstractService.call(this);
-
-  let that = this;
-  const mainContext = new blockchainCtx();
-  let conf, dal, logger, selfPubkey, quickSynchronizer
-
-  this.getContext = () => mainContext;
-
-  this.setConfDAL = (newConf, newDAL, newKeyPair) => {
-    dal = newDAL;
-    conf = newConf;
-    logger = require('../lib/logger')(dal.profile)
-    quickSynchronizer = new QuickSynchronizer(server.blockchain, conf, dal, logger)
-    mainContext.setConfDAL(conf, dal, server.blockchain, quickSynchronizer)
-    selfPubkey = newKeyPair.publicKey;
-  };
-
-  this.current = () => dal.getCurrentBlockOrNull();
-
-  this.promoted = (number) => co(function *() {
-    const bb = yield dal.getPromoted(number);
-    if (!bb) throw constants.ERRORS.BLOCK_NOT_FOUND;
-    return bb;
-  });
-
-  this.checkBlock = function(block) {
-    const dto = BlockDTO.fromJSONObject(block)
-    return mainContext.checkBlock(dto);
-  };
-
-  this.branches = () => co(function *() {
-    let forkBlocks = yield dal.blockDAL.getForkBlocks();
-    forkBlocks = _.sortBy(forkBlocks, 'number');
-    // Get the blocks refering current blockchain
-    const forkables = [];
-    for (const block of forkBlocks) {
-      const refered = yield dal.getBlockByNumberAndHashOrNull(block.number - 1, block.previousHash);
-      if (refered) {
-        forkables.push(block);
-      }
-    }
-    const branches = getBranches(forkables, _.difference(forkBlocks, forkables));
-    const current = yield mainContext.current();
-    const forks = branches.map((branch) => branch[branch.length - 1]);
-    return forks.concat([current]);
-  });
-
-  function getBranches(forkables, others) {
-    // All starting branches
-    let branches = forkables.map((fork) => [fork]);
-    // For each "pending" block, we try to add it to all branches
-    for (const other of others) {
-      for (let j = 0, len2 = branches.length; j < len2; j++) {
-        const branch = branches[j];
-        const last = branch[branch.length - 1];
-        if (other.number == last.number + 1 && other.previousHash == last.hash) {
-          branch.push(other);
-        } else if (branch[1]) {
-          // We try to find out if another fork block can be forked
-          const diff = other.number - branch[0].number;
-          if (diff > 0 && branch[diff - 1] && branch[diff - 1].hash == other.previousHash) {
-            // We duplicate the branch, and we add the block to this second branch
-            branches.push(branch.slice());
-            // First we remove the blocks that are not part of the fork
-            branch.splice(diff, branch.length - diff);
-            branch.push(other);
-            j++;
-          }
-        }
-      }
-    }
-    branches = _.sortBy(branches, (branch) => -branch.length);
-    if (branches.length) {
-      const maxSize = branches[0].length;
-      const longestsBranches = [];
-      for (const branch of branches) {
-        if (branch.length == maxSize) {
-          longestsBranches.push(branch);
-        }
-      }
-      return longestsBranches;
-    }
-    return [];
-  }
-
-  this.submitBlock = (obj, doCheck, forkAllowed) => this.pushFIFO(() => checkAndAddBlock(obj, doCheck, forkAllowed));
-
-  const checkAndAddBlock = (blockToAdd, doCheck, forkAllowed) => co(function *() {
-    // Check global format, notably version number
-    const obj = parsers.parseBlock.syncWrite(Block.statics.fromJSON(blockToAdd).getRawSigned());
-    // Force usage of local currency name, do not accept other currencies documents
-    if (conf.currency) {
-      obj.currency = conf.currency || obj.currency;
-    } else {
-      conf.currency = obj.currency;
-    }
-    try {
-      Transaction.statics.cleanSignatories(obj.transactions);
-    }
-    catch (e) {
-      throw e;
-    }
-    let existing = yield dal.getBlockByNumberAndHashOrNull(obj.number, obj.hash);
-    if (existing) {
-      throw constants.ERRORS.BLOCK_ALREADY_PROCESSED;
-    }
-    let current = yield mainContext.current();
-    let followsCurrent = !current || (obj.number == current.number + 1 && obj.previousHash == current.hash);
-    if (followsCurrent) {
-      // try to add it on main blockchain
-      const dto = BlockDTO.fromJSONObject(obj)
-      if (doCheck) {
-        const { index, HEAD } = yield mainContext.checkBlock(dto, constants.WITH_SIGNATURES_AND_POW);
-        return yield mainContext.addBlock(dto, index, HEAD)
-      } else {
-        return yield mainContext.addBlock(dto)
-      }
-    } else if (forkAllowed) {
-      // add it as side chain
-      if (current.number - obj.number + 1 >= conf.forksize) {
-        throw 'Block out of fork window';
-      }
-      let absolute = yield dal.getAbsoluteBlockByNumberAndHash(obj.number, obj.hash);
-      let res = null;
-      if (!absolute) {
-        res = yield mainContext.addSideBlock(obj, doCheck);
-      }
-      yield that.tryToFork(current);
-      return res;
-    } else {
-      throw "Fork block rejected by " + selfPubkey;
-    }
-  });
-
-
-  that.tryToFork = (current) => eventuallySwitchOnSideChain(current);
-
-  const eventuallySwitchOnSideChain = (current) => co(function *() {
-    const branches = yield that.branches();
-    const blocksAdvance = conf.swichOnTimeAheadBy / (conf.avgGenTime / 60);
-    const timeAdvance = conf.swichOnTimeAheadBy * 60;
-    let potentials = _.without(branches, current);
-    // We switch only to blockchain with X_MIN advance considering both theoretical time by block + written time
-    potentials = _.filter(potentials, (p) => p.number - current.number >= blocksAdvance
-                                  && p.medianTime - current.medianTime >= timeAdvance);
-    logger.trace('SWITCH: %s branches...', branches.length);
-    logger.trace('SWITCH: %s potential side chains...', potentials.length);
-    for (const potential of potentials) {
-      logger.info('SWITCH: get side chain #%s-%s...', potential.number, potential.hash);
-      const sideChain = yield getWholeForkBranch(potential);
-      logger.info('SWITCH: revert main chain to block #%s...', sideChain[0].number - 1);
-      yield revertToBlock(sideChain[0].number - 1);
-      try {
-        logger.info('SWITCH: apply side chain #%s-%s...', potential.number, potential.hash);
-        yield applySideChain(sideChain);
-      } catch (e) {
-        logger.warn('SWITCH: error %s', e.stack || e);
-        // Revert the revert (so we go back to original chain)
-        const revertedChain = yield getWholeForkBranch(current);
-        yield revertToBlock(revertedChain[0].number - 1);
-        yield applySideChain(revertedChain);
-        yield markSideChainAsWrong(sideChain);
-      }
-    }
-  });
-
-  const getWholeForkBranch = (topForkBlock) => co(function *() {
-    const fullBranch = [];
-    let isForkBlock = true;
-    let next = topForkBlock;
-    while (isForkBlock) {
-      fullBranch.push(next);
-      logger.trace('SWITCH: get absolute #%s-%s...', next.number - 1, next.previousHash);
-      next = yield dal.getAbsoluteBlockByNumberAndHash(next.number - 1, next.previousHash);
-      isForkBlock = next.fork;
-    }
-    //fullBranch.push(next);
-    // Revert order so we have a crescending branch
-    return fullBranch.reverse();
-  });
-
-  const revertToBlock = (number) => co(function *() {
-    let nowCurrent = yield that.current();
-    logger.trace('SWITCH: main chain current = #%s-%s...', nowCurrent.number, nowCurrent.hash);
-    while (nowCurrent.number > number) {
-      logger.trace('SWITCH: main chain revert #%s-%s...', nowCurrent.number, nowCurrent.hash);
-      yield mainContext.revertCurrentBlock();
-      nowCurrent = yield that.current();
-    }
-  });
-
-  const applySideChain = (chain) => co(function *() {
-    for (const block of chain) {
-      logger.trace('SWITCH: apply side block #%s-%s -> #%s-%s...', block.number, block.hash, block.number - 1, block.previousHash);
-      yield checkAndAddBlock(block, CHECK_ALL_RULES);
-    }
-  });
-
-  const markSideChainAsWrong = (chain) => co(function *() {
-    for (const block of chain) {
-      block.wrong = true;
-      // Saves the block (DAL)
-      yield dal.saveSideBlockInFile(block);
-    }
-  });
-
-  this.revertCurrentBlock = () => this.pushFIFO(() => mainContext.revertCurrentBlock());
-
-  this.applyNextAvailableFork = () => this.pushFIFO(() => mainContext.applyNextAvailableFork());
-
-  this.requirementsOfIdentities = (identities) => co(function *() {
-    let all = [];
-    let current = yield dal.getCurrentBlockOrNull();
-    for (const obj of identities) {
-      let idty = new Identity(obj);
-      try {
-        let reqs = yield that.requirementsOfIdentity(idty, current);
-        all.push(reqs);
-      } catch (e) {
-        logger.warn(e);
-      }
-    }
-    return all;
-  });
-
-  this.requirementsOfIdentity = (idty, current) => co(function *() {
-    // TODO: this is not clear
-    let expired = false;
-    let outdistanced = false;
-    let isSentry = false;
-    let wasMember = false;
-    let expiresMS = 0;
-    let expiresPending = 0;
-    let certs = [];
-    let certsPending = [];
-    let mssPending = [];
-    try {
-      const join = yield server.generatorGetJoinData(current, idty.hash, 'a');
-      const pubkey = join.identity.pubkey;
-      // Check WoT stability
-      const someNewcomers = join.identity.wasMember ? [] : [join.identity.pubkey];
-      const nextBlockNumber = current ? current.number + 1 : 0;
-      const joinData = {};
-      joinData[join.identity.pubkey] = join;
-      const updates = {};
-      certsPending = yield dal.certDAL.getToTarget(idty.hash);
-      certsPending = certsPending.map((c) => {
-        c.blockstamp = [c.block_number, c.block_hash].join('-')
-        return c
-      });
-      mssPending = yield dal.msDAL.getPendingINOfTarget(idty.hash)
-      mssPending = mssPending.map((ms) => {
-        ms.blockstamp = ms.block
-        ms.sig = ms.signature
-        ms.type = ms.membership
-        return ms
-      });
-      const newCerts = yield server.generatorComputeNewCerts(nextBlockNumber, [join.identity.pubkey], joinData, updates);
-      const newLinks = yield server.generatorNewCertsToLinks(newCerts, updates);
-      const currentTime = current ? current.medianTime : 0;
-      certs = yield that.getValidCerts(pubkey, newCerts);
-      outdistanced = yield rules.HELPERS.isOver3Hops(pubkey, newLinks, someNewcomers, current, conf, dal);
-      // Expiration of current membershship
-      const currentMembership = yield dal.mindexDAL.getReducedMS(pubkey);
-      const currentMSN = currentMembership ? parseInt(currentMembership.created_on) : -1;
-      if (currentMSN >= 0) {
-        if (join.identity.member) {
-          const msBlock = yield dal.getBlock(currentMSN);
-          if (msBlock && msBlock.medianTime) { // special case for block #0
-            expiresMS = Math.max(0, (msBlock.medianTime + conf.msValidity - currentTime));
-          }
-          else {
-            expiresMS = conf.msValidity;
-          }
-        } else {
-          expiresMS = 0;
-        }
-      }
-      // Expiration of pending membership
-      const lastJoin = yield dal.lastJoinOfIdentity(idty.hash);
-      if (lastJoin) {
-        const msBlock = yield dal.getBlock(lastJoin.blockNumber);
-        if (msBlock && msBlock.medianTime) { // Special case for block#0
-          expiresPending = Math.max(0, (msBlock.medianTime + conf.msValidity - currentTime));
-        }
-        else {
-          expiresPending = conf.msValidity;
-        }
-      }
-      wasMember = idty.wasMember;
-      isSentry = idty.member && (yield dal.isSentry(idty.pub, conf));
-      // Expiration of certifications
-      for (const cert of certs) {
-        cert.expiresIn = Math.max(0, cert.timestamp + conf.sigValidity - currentTime);
-      }
-    } catch (e) {
-      // We throw whatever isn't "Too old identity" error
-      if (!(e && e.uerr && e.uerr.ucode == constants.ERRORS.TOO_OLD_IDENTITY.uerr.ucode)) {
-        throw e;
-      } else {
-        expired = true;
-      }
-    }
-    return {
-      pubkey: idty.pubkey,
-      uid: idty.uid,
-      sig: idty.sig,
-      meta: {
-        timestamp: idty.buid
-      },
-      revocation_sig: idty.revocation_sig,
-      revoked: idty.revoked,
-      revoked_on: idty.revoked_on,
-      expired: expired,
-      outdistanced: outdistanced,
-      isSentry: isSentry,
-      wasMember: wasMember,
-      certifications: certs,
-      pendingCerts: certsPending,
-      pendingMemberships: mssPending,
-      membershipPendingExpiresIn: expiresPending,
-      membershipExpiresIn: expiresMS
-    };
-  });
-
-  this.getValidCerts = (newcomer, newCerts) => co(function *() {
-    const links = yield dal.getValidLinksTo(newcomer);
-    const certsFromLinks = links.map((lnk) => { return { from: lnk.issuer, to: lnk.receiver, timestamp: lnk.expires_on - conf.sigValidity }; });
-    const certsFromCerts = [];
-    const certs = newCerts[newcomer] || [];
-    for (const cert of certs) {
-      const block = yield dal.getBlock(cert.block_number);
-      certsFromCerts.push({
-        from: cert.from,
-        to: cert.to,
-        sig: cert.sig,
-        timestamp: block.medianTime
-      });
-    }
-    return certsFromLinks.concat(certsFromCerts);
-  });
-
-  this.isMember = () => dal.isMember(selfPubkey);
-  this.getCountOfSelfMadePoW = () => dal.getCountOfPoW(selfPubkey);
-
-  // This method is called by duniter-crawler 1.3.x
-  this.saveParametersForRootBlock = (block) => server.blockchain.saveParametersForRoot(block, conf, dal)
-
-  this.blocksBetween = (from, count) => co(function *() {
-    if (count > 5000) {
-      throw 'Count is too high';
-    }
-    const current = yield that.current();
-    count = Math.min(current.number - from + 1, count);
-    if (!current || current.number < from) {
-      return [];
-    }
-    return dal.getBlocksBetween(from, from + count - 1);
-  });
-
-  /**
-   * Allows to quickly insert a bunch of blocks. To reach such speed, this method skips global rules and buffers changes.
-   *
-   * **This method should be used ONLY when a node is really far away from current blockchain HEAD (i.e several hundreds of blocks late).
-   *
-   * This method is called by duniter-crawler 1.3.x.
-   *
-   * @param blocks An array of blocks to insert.
-   * @param to The final block number of the fast insertion.
-   */
-  this.fastBlockInsertions = (blocks, to) => mainContext.quickApplyBlocks(blocks, to)
-}
diff --git a/app/service/BlockchainService.ts b/app/service/BlockchainService.ts
new file mode 100644
index 000000000..2c874f7d7
--- /dev/null
+++ b/app/service/BlockchainService.ts
@@ -0,0 +1,428 @@
+"use strict";
+import {GlobalFifoPromise} from "./GlobalFifoPromise"
+import {BlockchainContext} from "../lib/computation/BlockchainContext"
+import {ConfDTO} from "../lib/dto/ConfDTO"
+import {FileDAL} from "../lib/dal/fileDAL"
+import {QuickSynchronizer} from "../lib/computation/QuickSync"
+import {BlockDTO} from "../lib/dto/BlockDTO"
+import {DBIdentity} from "../lib/dal/sqliteDAL/IdentityDAL"
+import {DBBlock} from "../lib/db/DBBlock"
+
+const _               = require('underscore');
+const co              = require('co');
+const parsers         = require('duniter-common').parsers;
+const rules           = require('../lib/rules')
+const constants       = require('../lib/constants');
+const Block           = require('../lib/entity/block');
+const Identity        = require('../lib/entity/identity');
+const Transaction     = require('../lib/entity/transaction');
+
+const CHECK_ALL_RULES = true;
+
+export class BlockchainService {
+
+  mainContext:BlockchainContext
+  conf:ConfDTO
+  dal:FileDAL
+  logger:any
+  selfPubkey:string
+  quickSynchronizer:QuickSynchronizer
+
+  constructor(private server:any) {
+    this.mainContext = new BlockchainContext()
+  }
+
+  getContext() {
+    return this.mainContext
+  }
+  
+
+  setConfDAL(newConf:ConfDTO, newDAL:FileDAL, newKeyPair:any) {
+    this.dal = newDAL;
+    this.conf = newConf;
+    this.logger = require('../lib/logger')(this.dal.profile)
+    this.quickSynchronizer = new QuickSynchronizer(this.server.blockchain, this.conf, this.dal, this.logger)
+    this.mainContext.setConfDAL(this.conf, this.dal, this.server.blockchain, this.quickSynchronizer)
+    this.selfPubkey = newKeyPair.publicKey;
+  }
+
+  current() {
+    return this.dal.getCurrentBlockOrNull()
+  }
+  
+
+  async promoted(number:number) {
+    const bb = await this.dal.getPromoted(number);
+    if (!bb) throw constants.ERRORS.BLOCK_NOT_FOUND;
+    return bb;
+  }
+
+  checkBlock(block:any) {
+    const dto = BlockDTO.fromJSONObject(block)
+    return this.mainContext.checkBlock(dto);
+  }
+
+  async branches() {
+    let forkBlocks = await this.dal.blockDAL.getForkBlocks();
+    forkBlocks = _.sortBy(forkBlocks, 'number');
+    // Get the blocks refering current blockchain
+    const forkables = [];
+    for (const block of forkBlocks) {
+      const refered = await this.dal.getBlockByNumberAndHashOrNull(block.number - 1, block.previousHash);
+      if (refered) {
+        forkables.push(block);
+      }
+    }
+    const branches = this.getBranches(forkables, _.difference(forkBlocks, forkables));
+    const current = await this.mainContext.current();
+    const forks = branches.map((branch) => branch[branch.length - 1]);
+    return forks.concat([current]);
+  }
+
+  private getBranches(forkables:any[], others:any[]) {
+    // All starting branches
+    let branches = forkables.map((fork) => [fork]);
+    // For each "pending" block, we try to add it to all branches
+    for (const other of others) {
+      for (let j = 0, len2 = branches.length; j < len2; j++) {
+        const branch = branches[j];
+        const last = branch[branch.length - 1];
+        if (other.number == last.number + 1 && other.previousHash == last.hash) {
+          branch.push(other);
+        } else if (branch[1]) {
+          // We try to find out if another fork block can be forked
+          const diff = other.number - branch[0].number;
+          if (diff > 0 && branch[diff - 1] && branch[diff - 1].hash == other.previousHash) {
+            // We duplicate the branch, and we add the block to this second branch
+            branches.push(branch.slice());
+            // First we remove the blocks this are not part of the fork
+            branch.splice(diff, branch.length - diff);
+            branch.push(other);
+            j++;
+          }
+        }
+      }
+    }
+    branches = _.sortBy(branches, (branch:any) => -branch.length);
+    if (branches.length) {
+      const maxSize = branches[0].length;
+      const longestsBranches = [];
+      for (const branch of branches) {
+        if (branch.length == maxSize) {
+          longestsBranches.push(branch);
+        }
+      }
+      return longestsBranches;
+    }
+    return [];
+  }
+
+  submitBlock(obj:any, doCheck:boolean, forkAllowed:boolean) {
+    return GlobalFifoPromise.pushFIFO(() => {
+      return this.checkAndAddBlock(obj, doCheck, forkAllowed)
+    })
+  }
+
+  private async checkAndAddBlock(blockToAdd:any, doCheck:boolean, forkAllowed:boolean = false) {
+    // Check global format, notably version number
+    const obj = parsers.parseBlock.syncWrite(Block.statics.fromJSON(blockToAdd).getRawSigned());
+    // Force usage of local currency name, do not accept other currencies documents
+    if (this.conf.currency) {
+      obj.currency = this.conf.currency || obj.currency;
+    } else {
+      this.conf.currency = obj.currency;
+    }
+    try {
+      Transaction.statics.cleanSignatories(obj.transactions);
+    }
+    catch (e) {
+      throw e;
+    }
+    let existing = await this.dal.getBlockByNumberAndHashOrNull(obj.number, obj.hash);
+    if (existing) {
+      throw constants.ERRORS.BLOCK_ALREADY_PROCESSED;
+    }
+    let current = await this.mainContext.current();
+    let followsCurrent = !current || (obj.number == current.number + 1 && obj.previousHash == current.hash);
+    if (followsCurrent) {
+      // try to add it on main blockchain
+      const dto = BlockDTO.fromJSONObject(obj)
+      if (doCheck) {
+        const { index, HEAD } = await this.mainContext.checkBlock(dto, constants.WITH_SIGNATURES_AND_POW);
+        return await this.mainContext.addBlock(dto, index, HEAD)
+      } else {
+        return await this.mainContext.addBlock(dto)
+      }
+    } else if (forkAllowed) {
+      // add it as side chain
+      if (current.number - obj.number + 1 >= this.conf.forksize) {
+        throw 'Block out of fork window';
+      }
+      let absolute = await this.dal.getAbsoluteBlockByNumberAndHash(obj.number, obj.hash)
+      let res = null;
+      if (!absolute) {
+        res = await this.mainContext.addSideBlock(obj)
+      }
+      await this.tryToFork(current);
+      return res;
+    } else {
+      throw "Fork block rejected by " + this.selfPubkey;
+    }
+  }
+
+
+  tryToFork(current:DBBlock) {
+    return this.eventuallySwitchOnSideChain(current)
+  }
+
+  private async eventuallySwitchOnSideChain(current:DBBlock) {
+    const branches = await this.branches()
+    const blocksAdvance = this.conf.swichOnTimeAheadBy / (this.conf.avgGenTime / 60);
+    const timeAdvance = this.conf.swichOnTimeAheadBy * 60;
+    let potentials = _.without(branches, current);
+    // We switch only to blockchain with X_MIN advance considering both theoretical time by block + written time
+    potentials = _.filter(potentials, (p:DBBlock) => p.number - current.number >= blocksAdvance
+                                  && p.medianTime - current.medianTime >= timeAdvance);
+    this.logger.trace('SWITCH: %s branches...', branches.length);
+    this.logger.trace('SWITCH: %s potential side chains...', potentials.length);
+    for (const potential of potentials) {
+      this.logger.info('SWITCH: get side chain #%s-%s...', potential.number, potential.hash);
+      const sideChain = await this.getWholeForkBranch(potential)
+      this.logger.info('SWITCH: revert main chain to block #%s...', sideChain[0].number - 1);
+      await this.revertToBlock(sideChain[0].number - 1)
+      try {
+        this.logger.info('SWITCH: apply side chain #%s-%s...', potential.number, potential.hash);
+        await this.applySideChain(sideChain)
+      } catch (e) {
+        this.logger.warn('SWITCH: error %s', e.stack || e);
+        // Revert the revert (so we go back to original chain)
+        const revertedChain = await this.getWholeForkBranch(current)
+        await this.revertToBlock(revertedChain[0].number - 1)
+        await this.applySideChain(revertedChain)
+        await this.markSideChainAsWrong(sideChain)
+      }
+    }
+  }
+
+  private async getWholeForkBranch(topForkBlock:DBBlock) {
+    const fullBranch = [];
+    let isForkBlock = true;
+    let next = topForkBlock;
+    while (isForkBlock) {
+      fullBranch.push(next);
+      this.logger.trace('SWITCH: get absolute #%s-%s...', next.number - 1, next.previousHash);
+      next = await this.dal.getAbsoluteBlockByNumberAndHash(next.number - 1, next.previousHash);
+      isForkBlock = next.fork;
+    }
+    //fullBranch.push(next);
+    // Revert order so we have a crescending branch
+    return fullBranch.reverse();
+  }
+
+  private async revertToBlock(number:number) {
+    let nowCurrent = await this.current();
+    this.logger.trace('SWITCH: main chain current = #%s-%s...', nowCurrent.number, nowCurrent.hash);
+    while (nowCurrent.number > number) {
+      this.logger.trace('SWITCH: main chain revert #%s-%s...', nowCurrent.number, nowCurrent.hash);
+      await this.mainContext.revertCurrentBlock();
+      nowCurrent = await this.current();
+    }
+  }
+
+  private async applySideChain(chain:DBBlock[]) {
+    for (const block of chain) {
+      this.logger.trace('SWITCH: apply side block #%s-%s -> #%s-%s...', block.number, block.hash, block.number - 1, block.previousHash);
+      await this.checkAndAddBlock(block, CHECK_ALL_RULES);
+    }
+  }
+
+  private async markSideChainAsWrong(chain:DBBlock[]) {
+    for (const block of chain) {
+      block.wrong = true;
+      // Saves the block (DAL)
+      await this.dal.saveSideBlockInFile(block);
+    }
+  }
+
+  revertCurrentBlock() {
+    return GlobalFifoPromise.pushFIFO(() => this.mainContext.revertCurrentBlock())
+  }
+  
+
+  applyNextAvailableFork() {
+    return GlobalFifoPromise.pushFIFO(() => this.mainContext.applyNextAvailableFork())
+  }
+  
+
+  async requirementsOfIdentities(identities:DBIdentity[]) {
+    let all = [];
+    let current = await this.dal.getCurrentBlockOrNull();
+    for (const obj of identities) {
+      let idty = new Identity(obj);
+      try {
+        let reqs = await this.requirementsOfIdentity(idty, current);
+        all.push(reqs);
+      } catch (e) {
+        this.logger.warn(e);
+      }
+    }
+    return all;
+  }
+
+  async requirementsOfIdentity(idty:DBIdentity, current:DBBlock) {
+    // TODO: this is not clear
+    let expired = false;
+    let outdistanced = false;
+    let isSentry = false;
+    let wasMember = false;
+    let expiresMS = 0;
+    let expiresPending = 0;
+    let certs = [];
+    let certsPending = [];
+    let mssPending = [];
+    try {
+      const join = await this.server.generatorGetJoinData(current, idty.hash, 'a');
+      const pubkey = join.identity.pubkey;
+      // Check WoT stability
+      const someNewcomers = join.identity.wasMember ? [] : [join.identity.pubkey];
+      const nextBlockNumber = current ? current.number + 1 : 0;
+      const joinData:any = {};
+      joinData[join.identity.pubkey] = join;
+      const updates = {};
+      certsPending = await this.dal.certDAL.getToTarget(idty.hash);
+      certsPending = certsPending.map((c:any) => {
+        c.blockstamp = [c.block_number, c.block_hash].join('-')
+        return c
+      });
+      mssPending = await this.dal.msDAL.getPendingINOfTarget(idty.hash)
+      mssPending = mssPending.map((ms:any) => {
+        ms.blockstamp = ms.block
+        ms.sig = ms.signature
+        ms.type = ms.membership
+        return ms
+      });
+      const newCerts = await this.server.generatorComputeNewCerts(nextBlockNumber, [join.identity.pubkey], joinData, updates);
+      const newLinks = await this.server.generatorNewCertsToLinks(newCerts, updates);
+      const currentTime = current ? current.medianTime : 0;
+      certs = await this.getValidCerts(pubkey, newCerts);
+      outdistanced = await rules.HELPERS.isOver3Hops(pubkey, newLinks, someNewcomers, current, this.conf, this.dal);
+      // Expiration of current membershship
+      const currentMembership = await this.dal.mindexDAL.getReducedMS(pubkey);
+      const currentMSN = currentMembership ? parseInt(currentMembership.created_on) : -1;
+      if (currentMSN >= 0) {
+        if (join.identity.member) {
+          const msBlock = await this.dal.getBlock(currentMSN);
+          if (msBlock && msBlock.medianTime) { // special case for block #0
+            expiresMS = Math.max(0, (msBlock.medianTime + this.conf.msValidity - currentTime));
+          }
+          else {
+            expiresMS = this.conf.msValidity;
+          }
+        } else {
+          expiresMS = 0;
+        }
+      }
+      // Expiration of pending membership
+      const lastJoin = await this.dal.lastJoinOfIdentity(idty.hash);
+      if (lastJoin) {
+        const msBlock = await this.dal.getBlock(lastJoin.blockNumber);
+        if (msBlock && msBlock.medianTime) { // Special case for block#0
+          expiresPending = Math.max(0, (msBlock.medianTime + this.conf.msValidity - currentTime));
+        }
+        else {
+          expiresPending = this.conf.msValidity;
+        }
+      }
+      wasMember = idty.wasMember;
+      isSentry = idty.member && (await this.dal.isSentry(idty.pubkey, this.conf));
+      // Expiration of certifications
+      for (const cert of certs) {
+        cert.expiresIn = Math.max(0, cert.timestamp + this.conf.sigValidity - currentTime);
+      }
+    } catch (e) {
+      // We throw whatever isn't "Too old identity" error
+      if (!(e && e.uerr && e.uerr.ucode == constants.ERRORS.TOO_OLD_IDENTITY.uerr.ucode)) {
+        throw e;
+      } else {
+        expired = true;
+      }
+    }
+    return {
+      pubkey: idty.pubkey,
+      uid: idty.uid,
+      sig: idty.sig,
+      meta: {
+        timestamp: idty.buid
+      },
+      revocation_sig: idty.revocation_sig,
+      revoked: idty.revoked,
+      revoked_on: idty.revoked_on,
+      expired: expired,
+      outdistanced: outdistanced,
+      isSentry: isSentry,
+      wasMember: wasMember,
+      certifications: certs,
+      pendingCerts: certsPending,
+      pendingMemberships: mssPending,
+      membershipPendingExpiresIn: expiresPending,
+      membershipExpiresIn: expiresMS
+    };
+  }
+
+  async getValidCerts(newcomer:string, newCerts:any) {
+    const links = await this.dal.getValidLinksTo(newcomer);
+    const certsFromLinks = links.map((lnk:any) => { return { from: lnk.issuer, to: lnk.receiver, timestamp: lnk.expires_on - this.conf.sigValidity }; });
+    const certsFromCerts = [];
+    const certs = newCerts[newcomer] || [];
+    for (const cert of certs) {
+      const block = await this.dal.getBlock(cert.block_number);
+      certsFromCerts.push({
+        from: cert.from,
+        to: cert.to,
+        sig: cert.sig,
+        timestamp: block.medianTime
+      });
+    }
+    return certsFromLinks.concat(certsFromCerts);
+  }
+
+  isMember() {
+    return this.dal.isMember(this.selfPubkey)
+  }
+  
+  getCountOfSelfMadePoW() {
+    return this.dal.getCountOfPoW(this.selfPubkey)
+  }
+  
+
+  // This method is called by duniter-crawler 1.3.x
+  saveParametersForRootBlock(block:BlockDTO) {
+    return this.server.blockchain.saveParametersForRoot(block, this.conf, this.dal)
+  }
+
+  async blocksBetween(from:number, count:number) {
+    if (count > 5000) {
+      throw 'Count is too high';
+    }
+    const current = await this.current()
+    count = Math.min(current.number - from + 1, count);
+    if (!current || current.number < from) {
+      return [];
+    }
+    return this.dal.getBlocksBetween(from, from + count - 1);
+  }
+
+  /**
+   * Allows to quickly insert a bunch of blocks. To reach such speed, this method skips global rules and buffers changes.
+   *
+   * **This method should be used ONLY when a node is really far away from current blockchain HEAD (i.e several hundreds of blocks late).
+   *
+   * This method is called by duniter-crawler 1.3.x.
+   *
+   * @param blocks An array of blocks to insert.
+   * @param to The final block number of the fast insertion.
+   */
+  fastBlockInsertions(blocks:BlockDTO[], to:number | null) {
+    return this.mainContext.quickApplyBlocks(blocks, to)
+  }
+}
diff --git a/app/service/AbstractService.js b/app/service/GlobalFifoPromise.ts
similarity index 75%
rename from app/service/AbstractService.js
rename to app/service/GlobalFifoPromise.ts
index 28f7e1e79..85b7c2959 100644
--- a/app/service/AbstractService.js
+++ b/app/service/GlobalFifoPromise.ts
@@ -3,27 +3,26 @@ const async = require('async');
 const Q     = require('q');
 const co    = require('co');
 
-const fifo = async.queue(function (task, callback) {
+const fifo = async.queue(function (task:any, callback:any) {
   task(callback);
 }, 1);
 
-module.exports = function AbstractService () {
+export class GlobalFifoPromise {
 
-  /**
-   * Gets the queue object for advanced flow control.
-   */
-  this.getFIFO = () => fifo;
+  static getLen() {
+    return fifo.length()
+  }
 
   /**
    * Adds a promise to a FIFO stack of promises, so the given promise will be executed against a shared FIFO stack.
    * @param p
    * @returns {Q.Promise<T>} A promise wrapping the promise given in the parameter.
    */
-  this.pushFIFO = (p) => {
+  static pushFIFO(p: () => Promise<any>) {
     // Return a promise that will be done after the fifo has executed the given promise
-    return Q.Promise((resolve, reject) => {
+    return Q.Promise((resolve:any, reject:any) => {
       // Push the promise on the stack
-      fifo.push(function (cb) {
+      fifo.push(function (cb:any) {
         co(function*(){
           // OK its the turn of given promise, execute it
           try {
@@ -35,7 +34,7 @@ module.exports = function AbstractService () {
             cb(e);
           }
         });
-      }, (err, res) => {
+      }, (err:any, res:any) => {
         // An error occured => reject promise
         if (err) return reject(err);
         // Success => we resolve with given promise result
@@ -43,4 +42,4 @@ module.exports = function AbstractService () {
       });
     });
   };
-};
+}
diff --git a/app/service/IdentityService.js b/app/service/IdentityService.ts
similarity index 53%
rename from app/service/IdentityService.js
rename to app/service/IdentityService.ts
index dcb7ffe08..97a7d9657 100644
--- a/app/service/IdentityService.js
+++ b/app/service/IdentityService.ts
@@ -1,4 +1,9 @@
+import {GlobalFifoPromise} from "./GlobalFifoPromise";
+
 "use strict";
+import {FileDAL} from "../lib/dal/fileDAL"
+import {ConfDTO} from "../lib/dto/ConfDTO"
+import {DBIdentity} from "../lib/dal/sqliteDAL/IdentityDAL"
 const Q               = require('q');
 const rules           = require('../lib/rules')
 const keyring          = require('duniter-common').keyring;
@@ -7,152 +12,164 @@ const Block           = require('../../app/lib/entity/block');
 const Identity        = require('../../app/lib/entity/identity');
 const Certification   = require('../../app/lib/entity/certification');
 const Revocation      = require('../../app/lib/entity/revocation');
-const AbstractService = require('./AbstractService');
-const co              = require('co');
 
 const BY_ABSORPTION = true;
 
-module.exports = () => {
-  return new IdentityService();
-};
-
-function IdentityService () {
+export class IdentityService {
 
-  AbstractService.call(this);
+  dal:FileDAL
+  conf:ConfDTO
+  logger:any
 
-  const that = this;
-  let dal, conf, logger;
+  constructor() {}
 
-  this.setConfDAL = (newConf, newDAL) => {
-    dal = newDAL;
-    conf = newConf;
-    logger = require('../lib/logger')(dal.profile);
-  };
+  setConfDAL(newConf:ConfDTO, newDAL:FileDAL) {
+    this.dal = newDAL;
+    this.conf = newConf;
+    this.logger = require('../lib/logger')(this.dal.profile);
+  }
 
-  this.searchIdentities = (search) => dal.searchJustIdentities(search);
+  searchIdentities(search:string) {
+    return this.dal.searchJustIdentities(search)
+  }
 
-  this.findMember = (search) => co(function *() {
+  async findMember(search:string) {
     let idty = null;
     if (search.match(constants.PUBLIC_KEY)) {
-      idty = yield dal.getWrittenIdtyByPubkey(search);
+      idty = await this.dal.getWrittenIdtyByPubkey(search);
     }
     else {
-      idty = yield dal.getWrittenIdtyByUID(search);
+      idty = await this.dal.getWrittenIdtyByUID(search);
     }
     if (!idty) {
       throw constants.ERRORS.NO_MEMBER_MATCHING_PUB_OR_UID;
     }
-    yield dal.fillInMembershipsOfIdentity(Q(idty));
+    await this.dal.fillInMembershipsOfIdentity(Q(idty));
     return new Identity(idty);
-  });
+  }
 
-  this.findMemberWithoutMemberships = (search) => co(function *() {
+  async findMemberWithoutMemberships(search:string) {
     let idty = null;
     if (search.match(constants.PUBLIC_KEY)) {
-      idty = yield dal.getWrittenIdtyByPubkey(search);
+      idty = await this.dal.getWrittenIdtyByPubkey(search)
     }
     else {
-      idty = yield dal.getWrittenIdtyByUID(search);
+      idty = await this.dal.getWrittenIdtyByUID(search)
     }
     if (!idty) {
       throw constants.ERRORS.NO_MEMBER_MATCHING_PUB_OR_UID;
     }
     return new Identity(idty);
-  });
+  }
 
-  this.getWrittenByPubkey = (pubkey) => dal.getWrittenIdtyByPubkey(pubkey);
+  getWrittenByPubkey(pubkey:string) {
+    return this.dal.getWrittenIdtyByPubkey(pubkey)
+  }
 
-  this.getPendingFromPubkey = (pubkey) => dal.getNonWritten(pubkey);
+  getPendingFromPubkey(pubkey:string) {
+    return this.dal.getNonWritten(pubkey)
+  }
 
-  this.submitIdentity = (obj, byAbsorption) => {
+  submitIdentity(obj:DBIdentity, byAbsorption = false) {
     let idty = new Identity(obj);
     // Force usage of local currency name, do not accept other currencies documents
-    idty.currency = conf.currency || idty.currency;
+    idty.currency = this.conf.currency;
     const createIdentity = idty.rawWithoutSig();
-    return that.pushFIFO(() => co(function *() {
-      logger.info('⬇ IDTY %s %s', idty.pubkey, idty.uid);
+    return GlobalFifoPromise.pushFIFO(async () => {
+      this.logger.info('⬇ IDTY %s %s', idty.pubkey, idty.uid);
       // Check signature's validity
       let verified = keyring.verify(createIdentity, idty.sig, idty.pubkey);
       if (!verified) {
         throw constants.ERRORS.SIGNATURE_DOES_NOT_MATCH;
       }
-      let existing = yield dal.getIdentityByHashOrNull(idty.hash);
+      let existing = await this.dal.getIdentityByHashOrNull(idty.hash);
       if (existing) {
         throw constants.ERRORS.ALREADY_UP_TO_DATE;
       }
       else if (!existing) {
         // Create if not already written uid/pubkey
-        let used = yield dal.getWrittenIdtyByPubkey(idty.pubkey);
+        let used = await this.dal.getWrittenIdtyByPubkey(idty.pubkey);
         if (used) {
           throw constants.ERRORS.PUBKEY_ALREADY_USED;
         }
-        used = yield dal.getWrittenIdtyByUID(idty.uid);
+        used = await this.dal.getWrittenIdtyByUID(idty.uid);
         if (used) {
           throw constants.ERRORS.UID_ALREADY_USED;
         }
-        const current = yield dal.getCurrentBlockOrNull();
+        const current = await this.dal.getCurrentBlockOrNull();
         if (idty.buid == '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855' && current) {
           throw constants.ERRORS.BLOCKSTAMP_DOES_NOT_MATCH_A_BLOCK;
         } else if (current) {
-          let basedBlock = yield dal.getBlockByBlockstamp(idty.buid);
+          let basedBlock = await this.dal.getBlockByBlockstamp(idty.buid);
           if (!basedBlock) {
             throw constants.ERRORS.BLOCKSTAMP_DOES_NOT_MATCH_A_BLOCK;
           }
-          idty.expires_on = basedBlock.medianTime + conf.idtyWindow;
+          idty.expires_on = basedBlock.medianTime + this.conf.idtyWindow;
         }
-        yield rules.GLOBAL.checkIdentitiesAreWritable({ identities: [idty.inline()], version: (current && current.version) || constants.BLOCK_GENERATED_VERSION }, conf, dal);
+        await rules.GLOBAL.checkIdentitiesAreWritable({ identities: [idty.inline()], version: (current && current.version) || constants.BLOCK_GENERATED_VERSION }, this.conf, this.dal);
         idty = new Identity(idty);
         if (byAbsorption !== BY_ABSORPTION) {
           idty.ref_block = parseInt(idty.buid.split('-')[0]);
-          if (!(yield dal.idtyDAL.sandbox.acceptNewSandBoxEntry(idty, conf.pair && conf.pair.pub))) {
+          if (!(await this.dal.idtyDAL.sandbox.acceptNewSandBoxEntry(idty, this.conf.pair && this.conf.pair.pub))) {
             throw constants.ERRORS.SANDBOX_FOR_IDENTITY_IS_FULL;
           }
         }
-        yield dal.savePendingIdentity(idty);
-        logger.info('✔ IDTY %s %s', idty.pubkey, idty.uid);
+        await this.dal.savePendingIdentity(idty);
+        this.logger.info('✔ IDTY %s %s', idty.pubkey, idty.uid);
         return idty;
       }
-    }));
-  };
+    })
+  }
 
-  this.submitCertification = (obj) => co(function *() {
-    const current = yield dal.getCurrentBlockOrNull();
+  async submitCertification(obj:any) {
+    const current = await this.dal.getCurrentBlockOrNull();
     // Prepare validator for certifications
-    const potentialNext = new Block({ currency: conf.currency, identities: [], number: current ? current.number + 1 : 0 });
+    const potentialNext = new Block({ currency: this.conf.currency, identities: [], number: current ? current.number + 1 : 0 });
     // Force usage of local currency name, do not accept other currencies documents
-    obj.currency = conf.currency || obj.currency;
+    obj.currency = this.conf.currency || obj.currency;
     const cert = Certification.statics.fromJSON(obj);
     const targetHash = cert.getTargetHash();
-    let idty = yield dal.getIdentityByHashOrNull(targetHash);
+    let idty = await this.dal.getIdentityByHashOrNull(targetHash);
     let idtyAbsorbed = false
     if (!idty) {
       idtyAbsorbed = true
-      idty = yield that.submitIdentity({
-        currency: cert.currency,
-        issuer: cert.idty_issuer,
+      idty = await this.submitIdentity({
         pubkey: cert.idty_issuer,
         uid: cert.idty_uid,
         buid: cert.idty_buid,
-        sig: cert.idty_sig
+        sig: cert.idty_sig,
+        written: false,
+        revoked: false,
+        member: false,
+        wasMember: false,
+        kick: false,
+        leaving: false,
+        hash: '',
+        wotb_id: null,
+        expires_on: 0,
+        revoked_on: null,
+        revocation_sig: null,
+        currentMSN: null,
+        currentINN: null
       }, BY_ABSORPTION);
     }
-    return that.pushFIFO(() => co(function *() {
-      logger.info('⬇ CERT %s block#%s -> %s', cert.from, cert.block_number, idty.uid);
+    return GlobalFifoPromise.pushFIFO(async () => {
+      this.logger.info('⬇ CERT %s block#%s -> %s', cert.from, cert.block_number, idty.uid);
       try {
-        yield rules.HELPERS.checkCertificationIsValid(cert, potentialNext, () => Q(idty), conf, dal);
+        await rules.HELPERS.checkCertificationIsValid(cert, potentialNext, () => Q(idty), this.conf, this.dal);
       } catch (e) {
         cert.err = e;
       }
       if (!cert.err) {
         try {
-          let basedBlock = yield dal.getBlock(cert.block_number);
+          let basedBlock = await this.dal.getBlock(cert.block_number);
           if (cert.block_number == 0 && !basedBlock) {
             basedBlock = {
               number: 0,
               hash: 'E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855'
             };
           } else {
-            cert.expires_on = basedBlock.medianTime + conf.sigWindow;
+            cert.expires_on = basedBlock.medianTime + this.conf.sigWindow;
           }
           cert.block_hash = basedBlock.hash;
           const mCert = new Certification({
@@ -164,13 +181,13 @@ function IdentityService () {
             to: idty.pubkey,
             expires_on: cert.expires_on
           });
-          let existingCert = yield dal.existsCert(mCert);
+          let existingCert = await this.dal.existsCert(mCert);
           if (!existingCert) {
-            if (!(yield dal.certDAL.getSandboxForKey(cert.from).acceptNewSandBoxEntry(mCert, conf.pair && conf.pair.pub))) {
+            if (!(await this.dal.certDAL.getSandboxForKey(cert.from).acceptNewSandBoxEntry(mCert, this.conf.pair && this.conf.pair.pub))) {
               throw constants.ERRORS.SANDBOX_FOR_CERT_IS_FULL;
             }
-            yield dal.registerNewCertification(new Certification(mCert));
-            logger.info('✔ CERT %s', mCert.from);
+            await this.dal.registerNewCertification(new Certification(mCert));
+            this.logger.info('✔ CERT %s', mCert.from);
           } else {
             throw constants.ERRORS.ALREADY_UP_TO_DATE;
           }
@@ -180,30 +197,30 @@ function IdentityService () {
       }
       if (cert.err) {
         if (idtyAbsorbed) {
-          yield dal.idtyDAL.deleteByHash(targetHash)
+          await this.dal.idtyDAL.deleteByHash(targetHash)
         }
         const err = cert.err
         const errMessage = (err.uerr && err.uerr.message) || err.message || err
-        logger.info('✘ CERT %s %s', cert.from, errMessage);
+        this.logger.info('✘ CERT %s %s', cert.from, errMessage);
         throw cert.err;
       }
       return cert;
-    }));
-  });
+    })
+  }
 
-  this.submitRevocation = (obj) => {
+  submitRevocation(obj:any) {
     // Force usage of local currency name, do not accept other currencies documents
-    obj.currency = conf.currency || obj.currency;
+    obj.currency = this.conf.currency || obj.currency;
     const revoc = new Revocation(obj);
     const raw = revoc.rawWithoutSig();
-    return that.pushFIFO(() => co(function *() {
+    return GlobalFifoPromise.pushFIFO(async () => {
       try {
-        logger.info('⬇ REVOCATION %s %s', revoc.pubkey, revoc.uid);
+        this.logger.info('⬇ REVOCATION %s %s', revoc.pubkey, revoc.uid);
         let verified = keyring.verify(raw, revoc.revocation, revoc.pubkey);
         if (!verified) {
           throw 'Wrong signature for revocation';
         }
-        const existing = yield dal.getIdentityByHashOrNull(obj.hash);
+        const existing = await this.dal.getIdentityByHashOrNull(obj.hash);
         if (existing) {
           // Modify
           if (existing.revoked) {
@@ -212,8 +229,8 @@ function IdentityService () {
           else if (existing.revocation_sig) {
             throw 'Revocation already registered';
           } else {
-            yield dal.setRevocating(existing, revoc.revocation);
-            logger.info('✔ REVOCATION %s %s', revoc.pubkey, revoc.uid);
+            await this.dal.setRevocating(existing, revoc.revocation);
+            this.logger.info('✔ REVOCATION %s %s', revoc.pubkey, revoc.uid);
             revoc.json = function() {
               return {
                 result: true
@@ -228,11 +245,11 @@ function IdentityService () {
           idty.revocation_sig = revoc.signature;
           idty.certsCount = 0;
           idty.ref_block = parseInt(idty.buid.split('-')[0]);
-          if (!(yield dal.idtyDAL.sandbox.acceptNewSandBoxEntry(idty, conf.pair && conf.pair.pub))) {
+          if (!(await this.dal.idtyDAL.sandbox.acceptNewSandBoxEntry(idty, this.conf.pair && this.conf.pair.pub))) {
             throw constants.ERRORS.SANDBOX_FOR_IDENTITY_IS_FULL;
           }
-          yield dal.savePendingIdentity(idty);
-          logger.info('✔ REVOCATION %s %s', revoc.pubkey, revoc.uid);
+          await this.dal.savePendingIdentity(idty);
+          this.logger.info('✔ REVOCATION %s %s', revoc.pubkey, revoc.uid);
           revoc.json = function() {
             return {
               result: true
@@ -241,9 +258,9 @@ function IdentityService () {
           return revoc;
         }
       } catch (e) {
-        logger.info('✘ REVOCATION %s %s', revoc.pubkey, revoc.uid);
+        this.logger.info('✘ REVOCATION %s %s', revoc.pubkey, revoc.uid);
         throw e;
       }
-    }));
-  };
+    })
+  }
 }
diff --git a/app/service/MembershipService.js b/app/service/MembershipService.js
deleted file mode 100644
index ffce63db7..000000000
--- a/app/service/MembershipService.js
+++ /dev/null
@@ -1,65 +0,0 @@
-"use strict";
-
-const co              = require('co');
-const rules           = require('../lib/rules')
-const hashf           = require('duniter-common').hashf;
-const constants       = require('../lib/constants');
-const Membership      = require('../lib/entity/membership');
-const AbstractService = require('./AbstractService');
-
-module.exports = () => {
-  return new MembershipService();
-};
-
-function MembershipService () {
-
-  AbstractService.call(this);
-
-  let conf, dal, logger;
-
-  this.setConfDAL = (newConf, newDAL) => {
-    dal = newDAL;
-    conf = newConf;
-    logger = require('../lib/logger')(dal.profile);
-  };
-
-  this.current = () => dal.getCurrentBlockOrNull();
-
-  this.submitMembership = (ms) => this.pushFIFO(() => co(function *() {
-    const entry = new Membership(ms);
-    // Force usage of local currency name, do not accept other currencies documents
-    entry.currency = conf.currency || entry.currency;
-    entry.idtyHash = (hashf(entry.userid + entry.certts + entry.issuer) + "").toUpperCase();
-    logger.info('⬇ %s %s', entry.issuer, entry.membership);
-    if (!rules.HELPERS.checkSingleMembershipSignature(entry)) {
-      throw constants.ERRORS.WRONG_SIGNATURE_MEMBERSHIP;
-    }
-    // Get already existing Membership with same parameters
-    const mostRecentNumber = yield dal.getMostRecentMembershipNumberForIssuer(entry.issuer);
-    const thisNumber = parseInt(entry.block);
-    if (mostRecentNumber == thisNumber) {
-      throw constants.ERRORS.ALREADY_RECEIVED_MEMBERSHIP;
-    } else if (mostRecentNumber > thisNumber) {
-      throw constants.ERRORS.A_MORE_RECENT_MEMBERSHIP_EXISTS;
-    }
-    const isMember = yield dal.isMember(entry.issuer);
-    const isJoin = entry.membership == 'IN';
-    if (!isMember && !isJoin) {
-      // LEAVE
-      throw constants.ERRORS.MEMBERSHIP_A_NON_MEMBER_CANNOT_LEAVE;
-    }
-    const current = yield dal.getCurrentBlockOrNull();
-    const basedBlock = yield rules.HELPERS.checkMembershipBlock(entry, current, conf, dal);
-    if (basedBlock) {
-      entry.expires_on = basedBlock.medianTime + conf.msWindow;
-    }
-    entry.pubkey = entry.issuer;
-    if (!(yield dal.msDAL.sandbox.acceptNewSandBoxEntry(entry, conf.pair && conf.pair.pub))) {
-      throw constants.ERRORS.SANDBOX_FOR_MEMERSHIP_IS_FULL;
-    }
-    // Saves entry
-    yield dal.savePendingMembership(entry);
-    logger.info('✔ %s %s', entry.issuer, entry.membership);
-    return entry;
-  }));
-}
diff --git a/app/service/MembershipService.ts b/app/service/MembershipService.ts
new file mode 100644
index 000000000..5fa1defa1
--- /dev/null
+++ b/app/service/MembershipService.ts
@@ -0,0 +1,66 @@
+"use strict";
+import {GlobalFifoPromise} from "./GlobalFifoPromise"
+import {ConfDTO} from "../lib/dto/ConfDTO"
+import {FileDAL} from "../lib/dal/fileDAL"
+
+const rules           = require('../lib/rules')
+const hashf           = require('duniter-common').hashf;
+const constants       = require('../lib/constants');
+const Membership      = require('../lib/entity/membership');
+
+export class MembershipService {
+
+  conf:ConfDTO
+  dal:FileDAL
+  logger:any
+
+  setConfDAL(newConf:ConfDTO, newDAL:FileDAL) {
+    this.dal = newDAL;
+    this.conf = newConf;
+    this.logger = require('../lib/logger')(this.dal.profile);
+  }
+
+  current() {
+    return this.dal.getCurrentBlockOrNull()
+  }
+
+  submitMembership(ms:any) {
+    return GlobalFifoPromise.pushFIFO(async () => {
+      const entry = new Membership(ms);
+      // Force usage of local currency name, do not accept other currencies documents
+      entry.currency = this.conf.currency || entry.currency;
+      entry.idtyHash = (hashf(entry.userid + entry.certts + entry.issuer) + "").toUpperCase();
+      this.logger.info('⬇ %s %s', entry.issuer, entry.membership);
+      if (!rules.HELPERS.checkSingleMembershipSignature(entry)) {
+        throw constants.ERRORS.WRONG_SIGNATURE_MEMBERSHIP;
+      }
+      // Get already existing Membership with same parameters
+      const mostRecentNumber = await this.dal.getMostRecentMembershipNumberForIssuer(entry.issuer);
+      const thisNumber = parseInt(entry.block);
+      if (mostRecentNumber == thisNumber) {
+        throw constants.ERRORS.ALREADY_RECEIVED_MEMBERSHIP;
+      } else if (mostRecentNumber > thisNumber) {
+        throw constants.ERRORS.A_MORE_RECENT_MEMBERSHIP_EXISTS;
+      }
+      const isMember = await this.dal.isMember(entry.issuer);
+      const isJoin = entry.membership == 'IN';
+      if (!isMember && !isJoin) {
+        // LEAVE
+        throw constants.ERRORS.MEMBERSHIP_A_NON_MEMBER_CANNOT_LEAVE;
+      }
+      const current = await this.dal.getCurrentBlockOrNull();
+      const basedBlock = await rules.HELPERS.checkMembershipBlock(entry, current, this.conf, this.dal);
+      if (basedBlock) {
+        entry.expires_on = basedBlock.medianTime + this.conf.msWindow;
+      }
+      entry.pubkey = entry.issuer;
+      if (!(await this.dal.msDAL.sandbox.acceptNewSandBoxEntry(entry, this.conf.pair && this.conf.pair.pub))) {
+        throw constants.ERRORS.SANDBOX_FOR_MEMERSHIP_IS_FULL;
+      }
+      // Saves entry
+      await this.dal.savePendingMembership(entry);
+      this.logger.info('✔ %s %s', entry.issuer, entry.membership);
+      return entry;
+    })
+  }
+}
diff --git a/app/service/PeeringService.js b/app/service/PeeringService.ts
similarity index 68%
rename from app/service/PeeringService.js
rename to app/service/PeeringService.ts
index 525ade941..31de19ac0 100644
--- a/app/service/PeeringService.js
+++ b/app/service/PeeringService.ts
@@ -1,5 +1,9 @@
-"use strict";
-const co             = require('co');
+import {GlobalFifoPromise} from "./GlobalFifoPromise"
+import {ConfDTO, Keypair} from "../lib/dto/ConfDTO"
+import {FileDAL} from "../lib/dal/fileDAL"
+import {DBPeer} from "../lib/dal/sqliteDAL/PeerDAL"
+import {DBBlock} from "../lib/db/DBBlock"
+
 const util           = require('util');
 const _              = require('underscore');
 const Q              = require('q');
@@ -13,41 +17,49 @@ const hashf          = require('duniter-common').hashf;
 const rawer          = require('duniter-common').rawer;
 const constants      = require('../lib/constants');
 const Peer           = require('../lib/entity/peer');
-const AbstractService = require('./AbstractService');
 
-function PeeringService(server) {
+export interface Keyring {
+  publicKey:string
+  secretKey:string
+}
 
-  AbstractService.call(this);
-  let conf, dal, pair, selfPubkey;
+export class PeeringService {
 
-  this.setConfDAL = (newConf, newDAL, newPair) => {
-    dal = newDAL;
-    conf = newConf;
-    pair = newPair;
-    this.pubkey = pair.publicKey;
-    selfPubkey = this.pubkey;
-  };
+  conf:ConfDTO
+  dal:FileDAL
+  selfPubkey:string
+  pair:Keyring
+  pubkey:string
+  peerInstance:DBPeer | null
 
-  let peer = null;
-  const that = this;
+  constructor(private server:any) {
+  }
 
-  this.peer = (newPeer) => co(function *() {
+  setConfDAL(newConf:ConfDTO, newDAL:FileDAL, newPair:Keyring) {
+    this.dal = newDAL;
+    this.conf = newConf;
+    this.pair = newPair;
+    this.pubkey = this.pair.publicKey;
+    this.selfPubkey = this.pubkey;
+  }
+
+  async peer(newPeer:DBPeer | null = null) {
     if (newPeer) {
-      peer = newPeer;
+      this.peerInstance = newPeer;
     }
-    let thePeer = peer;
+    let thePeer = this.peerInstance;
     if (!thePeer) {
-      thePeer = yield that.generateSelfPeer(conf, 0);
+      thePeer = await this.generateSelfPeer(this.conf, 0)
     }
     return Peer.statics.peerize(thePeer);
-  });
+  }
 
-  this.mirrorEndpoints = () => co(function *() {
-    let localPeer = yield that.peer();
-    return getOtherEndpoints(localPeer.endpoints, conf);
-  });
+  async mirrorEndpoints() {
+    let localPeer = await this.peer();
+    return this.getOtherEndpoints(localPeer.endpoints, this.conf);
+  }
 
-  this.checkPeerSignature = function (p) {
+  checkPeerSignature(p:DBPeer) {
     const raw = rawer.getPeerWithoutSignature(p);
     const sig = p.signature;
     const pub = p.pubkey;
@@ -55,19 +67,19 @@ function PeeringService(server) {
     return !!signaturesMatching;
   };
 
-  this.submitP = function(peering, eraseIfAlreadyRecorded, cautious){
+  submitP(peering:DBPeer, eraseIfAlreadyRecorded = false, cautious = true) {
     // Force usage of local currency name, do not accept other currencies documents
-    peering.currency = conf.currency || peering.currency;
+    peering.currency = this.conf.currency || peering.currency;
     let thePeer = new Peer(peering);
     let sp = thePeer.block.split('-');
     const blockNumber = parseInt(sp[0]);
     let blockHash = sp[1];
     let sigTime = 0;
-    let block;
+    let block:DBBlock | null;
     let makeCheckings = cautious || cautious === undefined;
-    return that.pushFIFO(() => co(function *() {
+    return GlobalFifoPromise.pushFIFO(async () => {
       if (makeCheckings) {
-        let goodSignature = that.checkPeerSignature(thePeer);
+        let goodSignature = this.checkPeerSignature(thePeer);
         if (!goodSignature) {
           throw 'Signature from a peer must match';
         }
@@ -76,7 +88,7 @@ function PeeringService(server) {
         thePeer.statusTS = 0;
         thePeer.status = 'UP';
       } else {
-        block = yield dal.getBlockByNumberAndHashOrNull(blockNumber, blockHash);
+        block = await this.dal.getBlockByNumberAndHashOrNull(blockNumber, blockHash);
         if (!block && makeCheckings) {
           throw constants.ERROR.PEER.UNKNOWN_REFERENCE_BLOCK;
         } else if (!block) {
@@ -87,7 +99,7 @@ function PeeringService(server) {
       }
       sigTime = block ? block.medianTime : 0;
       thePeer.statusTS = sigTime;
-      let found = yield dal.getPeerOrNull(thePeer.pubkey);
+      let found = await this.dal.getPeerOrNull(thePeer.pubkey);
       let peerEntity = Peer.statics.peerize(found || thePeer);
       if(found){
         // Already existing peer
@@ -118,16 +130,16 @@ function PeeringService(server) {
       peerEntity.last_try = null;
       peerEntity.hash = String(hashf(peerEntity.getRawSigned())).toUpperCase();
       peerEntity.raw = peerEntity.getRaw();
-      yield dal.savePeer(peerEntity);
+      await this.dal.savePeer(peerEntity);
       let savedPeer = Peer.statics.peerize(peerEntity);
-      if (peerEntity.pubkey == selfPubkey) {
-        const localEndpoint = yield server.getMainEndpoint(conf);
+      if (peerEntity.pubkey == this.selfPubkey) {
+        const localEndpoint = await this.server.getMainEndpoint(this.conf);
         const localNodeNotListed = !peerEntity.containsEndpoint(localEndpoint);
-        const current = localNodeNotListed && (yield dal.getCurrentBlockOrNull());
+        const current = localNodeNotListed && (await this.dal.getCurrentBlockOrNull());
         if (!localNodeNotListed) {
           const indexOfThisNode = peerEntity.endpoints.indexOf(localEndpoint);
           if (indexOfThisNode !== -1) {
-            server.push({
+            this.server.push({
               nodeIndexInPeers: indexOfThisNode
             });
           } else {
@@ -136,24 +148,24 @@ function PeeringService(server) {
         }
         if (localNodeNotListed && (!current || current.number > blockNumber)) {
           // Document with pubkey of local peer, but doesn't contain local interface: we must add it
-          that.generateSelfPeer(conf, 0);
+          this.generateSelfPeer(this.conf, 0);
         } else {
-          peer = peerEntity;
+          this.peerInstance = peerEntity;
         }
       }
       return savedPeer;
-    }));
-  };
+    })
+  }
 
-  this.handleNewerPeer = (pretendedNewer) => {
+  handleNewerPeer(pretendedNewer:DBPeer) {
     logger.debug('Applying pretended newer peer document %s/%s', pretendedNewer.block);
-    return server.singleWritePromise(_.extend({ documentType: 'peer' }, pretendedNewer));
-  };
+    return this.server.singleWritePromise(_.extend({ documentType: 'peer' }, pretendedNewer));
+  }
 
-  this.generateSelfPeer = (theConf, signalTimeInterval) => co(function*() {
-    const current = yield server.dal.getCurrentBlockOrNull();
+  async generateSelfPeer(theConf:ConfDTO, signalTimeInterval:number) {
+    const current = await this.server.dal.getCurrentBlockOrNull();
     const currency = theConf.currency || constants.DEFAULT_CURRENCY_NAME;
-    const peers = yield dal.findPeers(selfPubkey);
+    const peers = await this.dal.findPeers(this.selfPubkey);
     let p1 = {
       version: constants.DOCUMENTS_VERSION,
       currency: currency,
@@ -163,22 +175,22 @@ function PeeringService(server) {
     if (peers.length != 0 && peers[0]) {
       p1 = _(peers[0]).extend({version: constants.DOCUMENTS_VERSION, currency: currency});
     }
-    let endpoint = yield server.getMainEndpoint(theConf);
-    let otherPotentialEndpoints = getOtherEndpoints(p1.endpoints, theConf);
+    let endpoint = await this.server.getMainEndpoint(theConf);
+    let otherPotentialEndpoints = this.getOtherEndpoints(p1.endpoints, theConf);
     logger.info('Sibling endpoints:', otherPotentialEndpoints);
-    let reals = yield otherPotentialEndpoints.map((theEndpoint) => co(function*() {
+    let reals = await otherPotentialEndpoints.map(async (theEndpoint:string) => {
       let real = true;
       let remote = Peer.statics.endpoint2host(theEndpoint);
       try {
         // We test only BMA APIs, because other may exist and we cannot judge against them yet
         if (theEndpoint.startsWith('BASIC_MERKLED_API')) {
-          let answer = yield rp('http://' + remote + '/network/peering', { json: true });
-          if (!answer || answer.pubkey != selfPubkey) {
+          let answer = await rp('http://' + remote + '/network/peering', { json: true });
+          if (!answer || answer.pubkey != this.selfPubkey) {
             throw Error("Not same pubkey as local instance");
           }
         }
-        // We also remove endpoints that are *asked* to be removed in the conf file
-        if ((conf.rmEndpoints || []).indexOf(theEndpoint) !== -1) {
+        // We also remove endpoints this are *asked* to be removed in the conf file
+        if ((this.conf.rmEndpoints || []).indexOf(theEndpoint) !== -1) {
           real = false;
         }
       } catch (e) {
@@ -186,7 +198,7 @@ function PeeringService(server) {
         real = false;
       }
       return real;
-    }));
+    })
     let toConserve = otherPotentialEndpoints.filter((ep, i) => reals[i]);
     if (!currency || endpoint == 'BASIC_MERKLED_API') {
       logger.error('It seems there is an issue with your configuration.');
@@ -202,34 +214,34 @@ function PeeringService(server) {
     }
     // The number cannot be superior to current block
     minBlock = Math.min(minBlock, current ? current.number : minBlock);
-    let targetBlock = yield server.dal.getBlock(minBlock);
-    const p2 = {
+    let targetBlock = await this.server.dal.getBlock(minBlock);
+    const p2:any = {
       version: constants.DOCUMENTS_VERSION,
       currency: currency,
-      pubkey: selfPubkey,
+      pubkey: this.selfPubkey,
       block: targetBlock ? [targetBlock.number, targetBlock.hash].join('-') : constants.PEER.SPECIAL_BLOCK,
-      endpoints: _.uniq([endpoint].concat(toConserve).concat(conf.endpoints || []))
+      endpoints: _.uniq([endpoint].concat(toConserve).concat(this.conf.endpoints || []))
     };
     const raw2 = dos2unix(new Peer(p2).getRaw());
     logger.info('External access:', new Peer(p2).getURL());
     logger.debug('Generating server\'s peering entry based on block#%s...', p2.block.split('-')[0]);
-    p2.signature = yield server.sign(raw2);
-    p2.pubkey = selfPubkey;
+    p2.signature = await this.server.sign(raw2);
+    p2.pubkey = this.selfPubkey;
     p2.documentType = 'peer';
     // Remember this is now local peer value
-    peer = p2;
+    this.peerInstance = p2;
     // Submit & share with the network
-    yield server.submitP(p2, false);
-    const selfPeer = yield dal.getPeer(selfPubkey);
+    await this.server.submitP(p2, false);
+    const selfPeer = await this.dal.getPeer(this.selfPubkey);
     // Set peer's statut to UP
     selfPeer.documentType = 'selfPeer';
-    yield that.peer(selfPeer);
-    server.streamPush(selfPeer);
+    await this.peer(selfPeer);
+    this.server.streamPush(selfPeer);
     logger.info("Next peering signal in %s min", signalTimeInterval / 1000 / 60);
     return selfPeer;
-  });
+  }
 
-  function getOtherEndpoints(endpoints, theConf) {
+  private getOtherEndpoints(endpoints:string[], theConf:ConfDTO) {
     return endpoints.filter((ep) => {
       return !ep.match(constants.BMA_REGEXP) || (
           !(ep.includes(' ' + theConf.remoteport) && (
@@ -239,7 +251,3 @@ function PeeringService(server) {
 }
 
 util.inherits(PeeringService, events.EventEmitter);
-
-module.exports = function (server, pair, dal) {
-  return new PeeringService(server, pair, dal);
-};
diff --git a/app/service/TransactionsService.js b/app/service/TransactionsService.js
deleted file mode 100644
index bb5f89a20..000000000
--- a/app/service/TransactionsService.js
+++ /dev/null
@@ -1,59 +0,0 @@
-"use strict";
-
-const co              = require('co');
-const Q               = require('q');
-const constants       = require('../lib/constants');
-const rules           = require('../lib/rules')
-const Transaction     = require('../lib/entity/transaction');
-const AbstractService = require('./AbstractService');
-const TransactionDTO  = require('../lib/dto/TransactionDTO').TransactionDTO
-
-module.exports = () => {
-  return new TransactionService();
-};
-
-function TransactionService () {
-
-  AbstractService.call(this);
-
-  const CHECK_PENDING_TRANSACTIONS = true;
-
-  let conf, dal, logger;
-
-  this.setConfDAL = (newConf, newDAL) => {
-    dal = newDAL;
-    conf = newConf;
-    logger = require('../lib/logger')(dal.profile);
-  };
-
-  this.processTx = (txObj) => this.pushFIFO(() => co(function *() {
-    const tx = new Transaction(txObj, conf.currency);
-    try {
-      logger.info('⬇ TX %s:%s from %s', tx.output_amount, tx.output_base, tx.issuers);
-      const existing = yield dal.getTxByHash(tx.hash);
-      const current = yield dal.getCurrentBlockOrNull();
-      if (existing) {
-        throw constants.ERRORS.TX_ALREADY_PROCESSED;
-      }
-      // Start checks...
-      const transaction = tx.getTransaction();
-      const nextBlockWithFakeTimeVariation = { medianTime: current.medianTime + 1 };
-      const dto = TransactionDTO.fromJSONObject(tx)
-      yield Q.nbind(rules.HELPERS.checkSingleTransactionLocally, rules.HELPERS)(dto);
-      yield rules.HELPERS.checkTxBlockStamp(transaction, dal);
-      yield rules.HELPERS.checkSingleTransaction(dto, nextBlockWithFakeTimeVariation, conf, dal, CHECK_PENDING_TRANSACTIONS);
-      const server_pubkey = conf.pair && conf.pair.pub;
-      transaction.pubkey = transaction.issuers.indexOf(server_pubkey) !== -1 ? server_pubkey : '';
-      if (!(yield dal.txsDAL.sandbox.acceptNewSandBoxEntry(transaction, server_pubkey))) {
-        throw constants.ERRORS.SANDBOX_FOR_TRANSACTION_IS_FULL;
-      }
-      tx.blockstampTime = transaction.blockstampTime;
-      yield dal.saveTransaction(tx);
-      logger.info('✔ TX %s:%s from %s', tx.output_amount, tx.output_base, tx.issuers);
-      return tx;
-    } catch (e) {
-      logger.info('✘ TX %s:%s from %s', tx.output_amount, tx.output_base, tx.issuers);
-      throw e;
-    }
-  }));
-}
diff --git a/app/service/TransactionsService.ts b/app/service/TransactionsService.ts
new file mode 100644
index 000000000..d987d811f
--- /dev/null
+++ b/app/service/TransactionsService.ts
@@ -0,0 +1,58 @@
+"use strict";
+import {GlobalFifoPromise} from "./GlobalFifoPromise"
+import {ConfDTO} from "../lib/dto/ConfDTO"
+import {FileDAL} from "../lib/dal/fileDAL"
+import {TransactionDTO} from "../lib/dto/TransactionDTO"
+
+const co              = require('co');
+const Q               = require('q');
+const constants       = require('../lib/constants');
+const rules           = require('../lib/rules')
+const Transaction     = require('../lib/entity/transaction');
+const CHECK_PENDING_TRANSACTIONS = true
+
+export class TransactionService {
+
+  conf:ConfDTO
+  dal:FileDAL
+  logger:any
+
+  setConfDAL(newConf:ConfDTO, newDAL:FileDAL) {
+    this.dal = newDAL;
+    this.conf = newConf;
+    this.logger = require('../lib/logger')(this.dal.profile);
+  }
+
+  processTx(txObj:any) {
+    return GlobalFifoPromise.pushFIFO(async () => {
+      const tx = new Transaction(txObj, this.conf.currency);
+      try {
+        this.logger.info('⬇ TX %s:%s from %s', tx.output_amount, tx.output_base, tx.issuers);
+        const existing = await this.dal.getTxByHash(tx.hash);
+        const current = await this.dal.getCurrentBlockOrNull();
+        if (existing) {
+          throw constants.ERRORS.TX_ALREADY_PROCESSED;
+        }
+        // Start checks...
+        const transaction = tx.getTransaction();
+        const nextBlockWithFakeTimeVariation = { medianTime: current.medianTime + 1 };
+        const dto = TransactionDTO.fromJSONObject(tx)
+        await Q.nbind(rules.HELPERS.checkSingleTransactionLocally, rules.HELPERS)(dto);
+        await rules.HELPERS.checkTxBlockStamp(transaction, this.dal);
+        await rules.HELPERS.checkSingleTransaction(dto, nextBlockWithFakeTimeVariation, this.conf, this.dal, CHECK_PENDING_TRANSACTIONS);
+        const server_pubkey = this.conf.pair && this.conf.pair.pub;
+        transaction.pubkey = transaction.issuers.indexOf(server_pubkey) !== -1 ? server_pubkey : '';
+        if (!(await this.dal.txsDAL.sandbox.acceptNewSandBoxEntry(transaction, server_pubkey))) {
+          throw constants.ERRORS.SANDBOX_FOR_TRANSACTION_IS_FULL;
+        }
+        tx.blockstampTime = transaction.blockstampTime;
+        await this.dal.saveTransaction(tx);
+        this.logger.info('✔ TX %s:%s from %s', tx.output_amount, tx.output_base, tx.issuers);
+        return tx;
+      } catch (e) {
+        this.logger.info('✘ TX %s:%s from %s', tx.output_amount, tx.output_base, tx.issuers);
+        throw e;
+      }
+    })
+  }
+}
diff --git a/server.js b/server.js
index 3d8a77bb8..d07d87c2f 100644
--- a/server.js
+++ b/server.js
@@ -33,21 +33,21 @@ function Server (home, memoryOnly, overrideConf) {
   that.logger = logger;
 
   that.MerkleService       = require("./app/lib/helpers/merkle");
-  that.IdentityService     = require('./app/service/IdentityService')();
-  that.MembershipService   = require('./app/service/MembershipService')();
-  that.PeeringService      = require('./app/service/PeeringService')(that);
-  that.BlockchainService   = require('./app/service/BlockchainService')(that);
-  that.TransactionsService = require('./app/service/TransactionsService')();
+  that.IdentityService     = new (require('./app/service/IdentityService').IdentityService)()
+  that.MembershipService   = new (require('./app/service/MembershipService').MembershipService)()
+  that.PeeringService      = new (require('./app/service/PeeringService').PeeringService)(that)
+  that.BlockchainService   = new (require('./app/service/BlockchainService').BlockchainService)(that)
+  that.TransactionsService = new (require('./app/service/TransactionsService').TransactionService)()
 
   // Create document mapping
   const documentsMapping = {
-    'identity':      { action: that.IdentityService.submitIdentity,                                               parser: parsers.parseIdentity },
-    'certification': { action: that.IdentityService.submitCertification,                                          parser: parsers.parseCertification},
-    'revocation':    { action: that.IdentityService.submitRevocation,                                             parser: parsers.parseRevocation },
-    'membership':    { action: that.MembershipService.submitMembership,                                           parser: parsers.parseMembership },
-    'peer':          { action: that.PeeringService.submitP,                                                       parser: parsers.parsePeer },
-    'transaction':   { action: that.TransactionsService.processTx,                                                parser: parsers.parseTransaction },
-    'block':         { action: _.partial(that.BlockchainService.submitBlock, _, true, constants.NO_FORK_ALLOWED), parser: parsers.parseBlock }
+    'identity':      { action: (obj) => that.IdentityService.submitIdentity(obj),                                 parser: parsers.parseIdentity },
+    'certification': { action: (obj) => that.IdentityService.submitCertification(obj),                            parser: parsers.parseCertification},
+    'revocation':    { action: (obj) => that.IdentityService.submitRevocation(obj),                               parser: parsers.parseRevocation },
+    'membership':    { action: (obj) => that.MembershipService.submitMembership(obj),                             parser: parsers.parseMembership },
+    'peer':          { action: (obj) => that.PeeringService.submitP(obj),                                         parser: parsers.parsePeer },
+    'transaction':   { action: (obj) => that.TransactionsService.processTx(obj),                                  parser: parsers.parseTransaction },
+    'block':         { action: (obj) => that.BlockchainService.submitBlock(obj, true, constants.NO_FORK_ALLOWED), parser: parsers.parseBlock }
   };
 
   // Unused, but made mandatory by Duplex interface
diff --git a/test/eslint.js b/test/eslint.js
index 24994f994..e66ef6503 100644
--- a/test/eslint.js
+++ b/test/eslint.js
@@ -1,17 +1,21 @@
-const lint = require('mocha-eslint');
+describe('Linting', () => {
 
-// Array of paths to lint
-// Note: a seperate Mocha test will be run for each path and each file which
-// matches a glob pattern
-const paths = [
-  'app',
-  'bin/duniter',
-  'test'
-];
+  const lint = require('mocha-eslint');
 
-// Specify style of output
-const options = {};
-options.formatter = 'stylish';
+  // Array of paths to lint
+  // Note: a seperate Mocha test will be run for each path and each file which
+  // matches a glob pattern
+    const paths = [
+      'app',
+      'bin/duniter',
+      'test'
+    ];
 
-// Run the tests
-lint(paths, options);
+  // Specify style of output
+    const options = {};
+    options.formatter = 'stylish';
+
+  // Run the tests
+  lint(paths, options);
+
+})
\ No newline at end of file
-- 
GitLab