diff --git a/app/cli.js b/app/cli.js
index 65c75fb4fd4877f4d25b398c8ff3834fb74b960d..0f31b5fbc833790fe6c8527d2ad32960ce209286 100644
--- a/app/cli.js
+++ b/app/cli.js
@@ -761,9 +761,9 @@ function service(callback, nologs) {
         return callback.apply(that, cbArgs);
       } catch (e) {
         server.disconnect();
-        throw Error(err);
+        throw e;
       }
-      });
+    });
   };
 }
 
diff --git a/app/controllers/blockchain.js b/app/controllers/blockchain.js
index 788a4653684560ca4c8695b38e74e5efb29cf44b..757bad9e2493dcd2d847373d31748a65e52817d3 100644
--- a/app/controllers/blockchain.js
+++ b/app/controllers/blockchain.js
@@ -89,7 +89,7 @@ function BlockchainBinding (server) {
       nextBlockNumber = current ? current.number + 1 : 0;
     }
     const version = current ? current.version : constants.BLOCK_GENERATED_VERSION;
-    const difficulty = yield rules.HELPERS.getTrialLevel(version, idty.pubkey, conf, server.dal);
+    const difficulty = yield server.getBcContext().getIssuerPersonalizedDifficulty(version, idty.pubkey);
     return {
       "block": nextBlockNumber,
       "level": difficulty
@@ -104,7 +104,7 @@ function BlockchainBinding (server) {
     const version = current ? current.version : constants.BLOCK_GENERATED_VERSION;
     for (const issuer of issuers) {
       const member = yield server.dal.getWrittenIdtyByPubkey(issuer);
-      const difficulty = yield rules.HELPERS.getTrialLevel(version, member.pubkey, conf, server.dal);
+      const difficulty = yield server.getBcContext().getIssuerPersonalizedDifficulty(version, member.pubkey);
       difficulties.push({
         uid: member.uid,
         level: difficulty
diff --git a/app/controllers/wot.js b/app/controllers/wot.js
index d984ef9c37a15e23250349f2be8b76dfdffcf2ed..fb8979a7a641e66dadedc14a8d6cc06e14d5c434 100644
--- a/app/controllers/wot.js
+++ b/app/controllers/wot.js
@@ -28,38 +28,33 @@ function WOTBinding (server) {
     // Entitify each result
     identities.forEach((idty, index) => identities[index] = new Identity(idty));
     // Prepare some data to avoid displaying expired certifications
-    const excluding = yield BlockchainService.getCertificationsExludingBlock();
     for (const idty of identities) {
-      const certs = yield server.dal.certsToTarget(idty.getTargetHash());
+      const certs = yield server.dal.certsToTarget(idty.pubkey, idty.getTargetHash());
       const validCerts = [];
       for (const cert of certs) {
-        if (!(excluding && cert.block <= excluding.number)) {
-          const member = yield IdentityService.getWrittenByPubkey(cert.from);
-          if (member) {
-            cert.uids = [member.uid];
-            cert.isMember = member.member;
-            cert.wasMember = member.wasMember;
-          } else {
-            const potentials = yield IdentityService.getPendingFromPubkey(cert.from);
-            cert.uids = _(potentials).pluck('uid');
-            cert.isMember = false;
-            cert.wasMember = false;
-          }
-          validCerts.push(cert);
+        const member = yield IdentityService.getWrittenByPubkey(cert.from);
+        if (member) {
+          cert.uids = [member.uid];
+          cert.isMember = member.member;
+          cert.wasMember = member.wasMember;
+        } else {
+          const potentials = yield IdentityService.getPendingFromPubkey(cert.from);
+          cert.uids = _(potentials).pluck('uid');
+          cert.isMember = false;
+          cert.wasMember = false;
         }
+        validCerts.push(cert);
       }
       idty.certs = validCerts;
       const signed = yield server.dal.certsFrom(idty.pubkey);
       const validSigned = [];
       for (let j = 0; j < signed.length; j++) {
         const cert = _.clone(signed[j]);
-        if (!(excluding && cert.block <= excluding.number)) {
-          cert.idty = yield server.dal.getIdentityByHashOrNull(cert.target);
-          if (cert.idty) {
-            validSigned.push(cert);
-          } else {
-            logger.debug('A certification to an unknown identity was found (%s => %s)', cert.from, cert.to);
-          }
+        cert.idty = yield server.dal.getIdentityByHashOrNull(cert.target);
+        if (cert.idty) {
+          validSigned.push(cert);
+        } else {
+          logger.debug('A certification to an unknown identity was found (%s => %s)', cert.from, cert.to);
         }
       }
       idty.signed = validSigned;
@@ -99,27 +94,24 @@ function WOTBinding (server) {
   this.certifiersOf = (req) => co(function *() {
     const search = yield ParametersService.getSearchP(req);
     const idty = yield IdentityService.findMemberWithoutMemberships(search);
-    const excluding = yield BlockchainService.getCertificationsExludingBlock();
-    const certs = yield server.dal.certsToTarget(idty.getTargetHash());
+    const certs = yield server.dal.certsToTarget(idty.pubkey, idty.getTargetHash());
     idty.certs = [];
     for (const cert of certs) {
-      if (!(excluding && cert.block <= excluding.number)) {
-        const certifier = yield server.dal.getWrittenIdtyByPubkey(cert.from);
-        if (certifier) {
-          cert.uid = certifier.uid;
-          cert.isMember = certifier.member;
-          cert.sigDate = certifier.buid;
-          cert.wasMember = true; // As we checked if(certified)
-          if (!cert.cert_time) {
-            // TODO: would be more efficient to save medianTime on certification reception
-            let certBlock = yield server.dal.getBlock(cert.block_number);
-            cert.cert_time = {
-              block: certBlock.number,
-              medianTime: certBlock.medianTime
-            };
-          }
-          idty.certs.push(cert);
+      const certifier = yield server.dal.getWrittenIdtyByPubkey(cert.from);
+      if (certifier) {
+        cert.uid = certifier.uid;
+        cert.isMember = certifier.member;
+        cert.sigDate = certifier.buid;
+        cert.wasMember = true; // As we checked if(certified)
+        if (!cert.cert_time) {
+          // TODO: would be more efficient to save medianTime on certification reception
+          let certBlock = yield server.dal.getBlock(cert.block_number);
+          cert.cert_time = {
+            block: certBlock.number,
+            medianTime: certBlock.medianTime
+          };
         }
+        idty.certs.push(cert);
       }
     }
     const json = {
@@ -162,27 +154,24 @@ function WOTBinding (server) {
   this.certifiedBy = (req) => co(function *() {
     const search = yield ParametersService.getSearchP(req);
     const idty = yield IdentityService.findMemberWithoutMemberships(search);
-    const excluding = yield BlockchainService.getCertificationsExludingBlock();
     const certs = yield server.dal.certsFrom(idty.pubkey);
     idty.certs = [];
     for (const cert of certs) {
-      if (!(excluding && cert.block <= excluding.number)) {
-        const certified = yield server.dal.getWrittenIdtyByPubkey(cert.to);
-        if (certified) {
-          cert.uid = certified.uid;
-          cert.isMember = certified.member;
-          cert.sigDate = certified.buid;
-          cert.wasMember = true; // As we checked if(certified)
-          if (!cert.cert_time) {
-            // TODO: would be more efficient to save medianTime on certification reception
-            let certBlock = yield server.dal.getBlock(cert.block_number);
-            cert.cert_time = {
-              block: certBlock.number,
-              medianTime: certBlock.medianTime
-            };
-          }
-          idty.certs.push(cert);
+      const certified = yield server.dal.getWrittenIdtyByPubkey(cert.to);
+      if (certified) {
+        cert.uid = certified.uid;
+        cert.isMember = certified.member;
+        cert.sigDate = certified.buid;
+        cert.wasMember = true; // As we checked if(certified)
+        if (!cert.cert_time) {
+          // TODO: would be more efficient to save medianTime on certification reception (note: now partially done with INDEX)
+          let certBlock = yield server.dal.getBlock(cert.block_number);
+          cert.cert_time = {
+            block: certBlock.number,
+            medianTime: certBlock.medianTime
+          };
         }
+        idty.certs.push(cert);
       }
     }
     const json = {
diff --git a/app/lib/computation/blockGenerator.js b/app/lib/computation/blockGenerator.js
index 8411a014a00eecb4b56312b14c8c2c143499ad1c..e087b3cee2f7410bcab7df1f734df2f9d91457e7 100644
--- a/app/lib/computation/blockGenerator.js
+++ b/app/lib/computation/blockGenerator.js
@@ -4,6 +4,7 @@ const co              = require('co');
 const Q               = require('q');
 const moment          = require('moment');
 const inquirer        = require('inquirer');
+const indexer         = require('../dup/indexer');
 const rawer           = require('../ucp/rawer');
 const hashf           = require('../ucp/hashf');
 const constants       = require('../constants');
@@ -33,13 +34,7 @@ function BlockGenerator(mainContext, prover) {
     logger = require('../logger')(dal.profile);
   };
 
-  this.nextBlock = (manualValues) => generateNextBlock(new NextBlockGenerator(conf, dal), manualValues);
-
-  this.nextEmptyBlock = () => co(function *() {
-    const current = yield dal.getCurrentBlockOrNull();
-    const exclusions = yield dal.getToBeKickedPubkeys();
-    return createBlock(current, {}, {}, {}, [], exclusions, []);
-  });
+  this.nextBlock = (manualValues) => generateNextBlock(new NextBlockGenerator(mainContext, conf, dal), manualValues);
 
   this.manualRoot = () => co(function *() {
     let current = yield dal.getCurrentBlockOrNull();
@@ -53,7 +48,7 @@ function BlockGenerator(mainContext, prover) {
     const unsignedBlock = block || (yield that.nextBlock(manualValues));
     const current = yield dal.getCurrentBlockOrNull();
     const version = current ? current.version : constants.BLOCK_GENERATED_VERSION;
-    const trialLevel = trial || (yield rules.HELPERS.getTrialLevel(version, selfPubkey, conf, dal));
+    const trialLevel = trial || (yield mainContext.getIssuerPersonalizedDifficulty(version, selfPubkey));
     return prover.prove(unsignedBlock, trialLevel, (manualValues && manualValues.time) || null);
   });
 
@@ -151,7 +146,9 @@ function BlockGenerator(mainContext, prover) {
         block = {};
       }
       const identity = yield dal.getIdentityByHashOrNull(leave.idHash);
-      if (identity && block && identity.currentMSN < leave.ms.number && identity.member) {
+      const currentMembership = yield dal.mindexDAL.getReducedMS(ms.issuer);
+      const currentMSN = currentMembership ? parseInt(currentMembership.created_on) : -1;
+      if (identity && block && currentMSN < leave.ms.number && identity.member) {
         // MS + matching cert are found
         leave.identity = identity;
         leaveData[identity.pubkey] = leave;
@@ -269,7 +266,9 @@ function BlockGenerator(mainContext, prover) {
         const idtyHash = (hashf(ms.userid + ms.certts + ms.issuer) + "").toUpperCase();
         const join = yield that.getSinglePreJoinData(current, idtyHash, joiners);
         join.ms = ms;
-        if (!join.identity.revoked && join.identity.currentMSN < parseInt(join.ms.number)) {
+        const currentMembership = yield dal.mindexDAL.getReducedMS(ms.issuer);
+        const currentMSN = currentMembership ? parseInt(currentMembership.created_on) : -1;
+        if (!join.identity.revoked && currentMSN < parseInt(join.ms.number)) {
           preJoinData[join.identity.pubkey] = join;
         }
       } catch (err) {
@@ -328,7 +327,7 @@ function BlockGenerator(mainContext, prover) {
   this.getSinglePreJoinData = (current, idHash, joiners) => co(function *() {
     const identity = yield dal.getIdentityByHashOrNull(idHash);
     let foundCerts = [];
-    const blockOfChainability = current ? (yield dal.getChainabilityBlock(current.medianTime, conf.sigPeriod)) : null;
+    const vHEAD_1 = yield mainContext.getvHEAD_1();
     if (!identity) {
       throw 'Identity with hash \'' + idHash + '\' not found';
     }
@@ -349,7 +348,8 @@ function BlockGenerator(mainContext, prover) {
     if (!verified) {
       throw constants.ERRORS.IDENTITY_WRONGLY_SIGNED;
     }
-    if (!identity.leaving) {
+    const isIdentityLeaving = yield dal.isLeaving(idty.pubkey);
+    if (!isIdentityLeaving) {
       if (!current) {
         // Look for certifications from initial joiners
         // TODO: check if this is still working
@@ -375,12 +375,12 @@ function BlockGenerator(mainContext, prover) {
               }
             }
             // Already exists a link not replayable yet?
-            let exists = yield dal.existsLinkFromOrAfterDate(cert.from, cert.to, current.medianTime - conf.sigValidity);
+            let exists = yield dal.existsNonReplayableLink(cert.from, cert.to);
             if (exists) {
               throw 'It already exists a similar certification written, which is not replayable yet';
             }
             // Already exists a link not chainable yet?
-            exists = yield dal.existsNonChainableLink(cert.from, blockOfChainability ? blockOfChainability.number : -1, conf.sigStock);
+            exists = yield dal.existsNonChainableLink(cert.from, vHEAD_1, conf.sigStock);
             if (exists) {
               throw 'It already exists a certification written which is not chainable yet';
             }
@@ -411,7 +411,9 @@ function BlockGenerator(mainContext, prover) {
   const createBlock = (current, joinData, leaveData, updates, revocations, exclusions, transactions, manualValues) => {
     return co(function *() {
 
-      const maxLenOfBlock = yield rules.HELPERS.getMaxBlockSize(dal);
+      const vHEAD = yield mainContext.getvHeadCopy();
+      const vHEAD_1 = yield mainContext.getvHEAD_1();
+      const maxLenOfBlock = indexer.DUP_HELPERS.getMaxBlockSize(vHEAD);
       let blockLen = 0;
       // Revocations have an impact on exclusions
       revocations.forEach((idty) => exclusions.push(idty.pubkey));
@@ -433,7 +435,7 @@ function BlockGenerator(mainContext, prover) {
         block.medianTime = moment.utc().unix() - conf.rootoffset;
       }
       else {
-        block.medianTime = yield rules.HELPERS.getMedianTime(block.number, conf, dal);
+        block.medianTime = vHEAD.medianTime;
       }
       // Choose the version
       block.version = (manualValues && manualValues.version) || (yield rules.HELPERS.getMaxPossibleVersionNumber(current, block));
@@ -552,6 +554,8 @@ function BlockGenerator(mainContext, prover) {
       // Final number of members
       block.membersCount = previousCount + block.joiners.length - block.excluded.length;
 
+      vHEAD.membersCount = block.membersCount;
+
       /*****
        * Priority 4: transactions
        */
@@ -570,20 +574,26 @@ function BlockGenerator(mainContext, prover) {
       /**
        * Finally handle the Universal Dividend
        */
-      block.powMin = block.number == 0 ? conf.powMin || 0 : yield rules.HELPERS.getPoWMin(block.version, block.number, conf, dal);
+      block.powMin = vHEAD.powMin;
+
+      // BR_G13
+      indexer.prepareDividend(vHEAD, vHEAD_1, conf);
+
+      // BR_G14
+      indexer.prepareUnitBase(vHEAD, vHEAD_1, conf);
+
       // Universal Dividend
-      const nextUD = yield rules.HELPERS.getNextUD(dal, conf, block.version, block.medianTime, current, block.membersCount);
-      if (nextUD) {
-        block.dividend = nextUD.dividend;
-        block.unitbase = nextUD.unitbase;
+      if (vHEAD.new_dividend) {
+        block.dividend = vHEAD.dividend;
+        block.unitbase = vHEAD.unitBase;
       } else if (block.version > 2) {
         block.unitbase = block.number == 0 ? 0 : current.unitbase;
       }
-      // V3 Rotation
+      // Rotation
       if (block.version > 2) {
-        block.issuersCount = yield rules.HELPERS.getDifferentIssuers(dal);
-        block.issuersFrame = yield rules.HELPERS.getIssuersFrame(dal);
-        block.issuersFrameVar = yield rules.HELPERS.getIssuersFrameVar(block, dal);
+        block.issuersCount = vHEAD.issuersCount;
+        block.issuersFrame = vHEAD.issuersFrame;
+        block.issuersFrameVar = vHEAD.issuersFrameVar;
       }
       // InnerHash
       block.time = block.medianTime;
@@ -600,7 +610,7 @@ function BlockGenerator(mainContext, prover) {
  * Class to implement strategy of automatic selection of incoming data for next block.
  * @constructor
  */
-function NextBlockGenerator(conf, dal) {
+function NextBlockGenerator(mainContext, conf, dal) {
 
   const logger = require('../logger')(dal.profile);
 
@@ -608,8 +618,7 @@ function NextBlockGenerator(conf, dal) {
     const updates = {};
     const updatesToFrom = {};
     const certs = yield dal.certsFindNew();
-    // The block above which (above from current means blocks with number < current)
-    const blockOfChainability = current ? (yield dal.getChainabilityBlock(current.medianTime, conf.sigPeriod)) : null;
+    const vHEAD_1 = yield mainContext.getvHEAD_1();
     for (const cert of certs) {
       const targetIdty = yield dal.getIdentityByHashOrNull(cert.target);
       // The identity must be known
@@ -634,12 +643,12 @@ function NextBlockGenerator(conf, dal) {
             let exists = false;
             if (current) {
               // Already exists a link not replayable yet?
-              exists = yield dal.existsLinkFromOrAfterDate(cert.from, cert.to, current.medianTime - conf.sigValidity);
+              exists = yield dal.existsNonReplayableLink(cert.from, cert.to);
             }
             if (!exists) {
               // Already exists a link not chainable yet?
               // No chainability block means absolutely nobody can issue certifications yet
-              exists = current && (yield dal.existsNonChainableLink(cert.from, blockOfChainability ? blockOfChainability.number : -1, conf.sigStock));
+              exists = yield dal.existsNonChainableLink(cert.from, vHEAD_1, conf.sigStock);
               if (!exists) {
                 // It does NOT already exists a similar certification written, which is not replayable yet
                 // Signatory must be a member
diff --git a/app/lib/computation/blockchainContext.js b/app/lib/computation/blockchainContext.js
index 2dd0bb208e2898b8ae8f1941820f92519e28316b..a607ee597911d6977bfeaf165f331e6f1abce2a2 100644
--- a/app/lib/computation/blockchainContext.js
+++ b/app/lib/computation/blockchainContext.js
@@ -2,6 +2,7 @@
 const _               = require('underscore');
 const co              = require('co');
 const Q               = require('q');
+const indexer         = require('../dup/indexer');
 const hashf           = require('../ucp/hashf');
 const rawer           = require('../ucp/rawer');
 const constants       = require('../constants');
@@ -10,8 +11,6 @@ const Identity        = require('../entity/identity');
 const Certification   = require('../entity/certification');
 const Membership      = require('../entity/membership');
 const Block           = require('../entity/block');
-const Link            = require('../entity/link');
-const Source          = require('../entity/source');
 const Transaction     = require('../entity/transaction');
 
 module.exports = () => { return new BlockchainContext() };
@@ -21,6 +20,84 @@ function BlockchainContext() {
   const that = this;
   let conf, dal, logger;
 
+  /**
+   * The virtual next HEAD. Computed each time a new block is added, because a lot of HEAD variables are deterministic
+   * and can be computed one, just after a block is added for later controls.
+   */
+  let vHEAD;
+
+  /**
+   * The currently written HEAD, aka. HEAD_1 relatively to incoming HEAD.
+   */
+  let vHEAD_1;
+
+  let HEADrefreshed = Q();
+
+  /**
+   * Refresh the virtual HEAD value for determined variables of the next coming block, avoiding to recompute them
+   * each time a new block arrives to check if the values are correct. We can know and store them early on, in vHEAD.
+   */
+  function refreshHead() {
+    HEADrefreshed = co(function*() {
+      vHEAD_1 = yield dal.head(1);
+      // We suppose next block will have same version #, and no particular data in the block (empty index)
+      let block;
+      // But if no HEAD_1 exist, we must initialize a block with default values
+      if (!vHEAD_1) {
+        block = {
+          version: constants.BLOCK_GENERATED_VERSION,
+          time: Math.round(Date.now() / 1000),
+          powMin: conf.powMin || 0,
+          powZeros: 0,
+          powRemainder: 0,
+          avgBlockSize: 0
+        };
+      } else {
+        block = { version: vHEAD_1.version };
+      }
+      vHEAD = yield indexer.completeGlobalScope(Block.statics.fromJSON(block).json(), conf, [], dal);
+    });
+    return HEADrefreshed;
+  }
+
+  /**
+   * Gets a copy of vHEAD, extended with some extra properties.
+   * @param props The extra properties to add.
+   */
+  this.getvHeadCopy = (props) => co(function*() {
+    if (!vHEAD) {
+      yield refreshHead();
+    }
+    const copy = {};
+    const keys = Object.keys(vHEAD);
+    for (const k of keys) {
+      copy[k] = vHEAD[k];
+    }
+    _.extend(copy, props);
+    return copy;
+  });
+
+  /**
+   * Get currently written HEAD.
+   */
+  this.getvHEAD_1 = () => co(function*() {
+    if (!vHEAD) {
+      yield refreshHead();
+    }
+    return vHEAD_1;
+  });
+
+  /**
+   * Utility method: gives the personalized difficulty level of a given issuer for next block.
+   * @param version The version in which is computed the difficulty.
+   * @param issuer The issuer we want to get the difficulty level.
+   */
+  this.getIssuerPersonalizedDifficulty = (version, issuer) => co(function *() {
+    const vHEAD = yield that.getvHeadCopy({ version, issuer });
+    yield indexer.preparePersonalizedPoW(vHEAD, vHEAD_1, dal.range, conf);
+    return vHEAD.issuerDiff;
+  });
+
   this.setConfDAL = (newConf, newDAL) => {
     dal = newDAL;
     conf = newConf;
@@ -30,11 +107,106 @@ function BlockchainContext() {
   this.checkBlock = (block, withPoWAndSignature) => co(function*(){
     if (withPoWAndSignature) {
       yield Q.nbind(rules.CHECK.ASYNC.ALL_LOCAL, rules, block, conf);
-      yield Q.nbind(rules.CHECK.ASYNC.ALL_GLOBAL, rules, block, conf, dal);
+      const index = indexer.localIndex(block, conf);
+      const mindex = indexer.mindex(index);
+      const iindex = indexer.iindex(index);
+      const sindex = indexer.sindex(index);
+      const cindex = indexer.cindex(index);
+      const HEAD = yield indexer.completeGlobalScope(block, conf, index, dal);
+      const HEAD_1 = yield dal.bindexDAL.head(1);
+      // BR_G49
+      if (indexer.ruleVersion(HEAD, HEAD_1) === false) throw Error('ruleVersion');
+      // BR_G50
+      if (indexer.ruleBlockSize(HEAD) === false) throw Error('ruleBlockSize');
+      // BR_G98
+      if (indexer.ruleCurrency(block, HEAD) === false) throw Error('ruleCurrency');
+      // BR_G51
+      if (indexer.ruleNumber(block, HEAD) === false) throw Error('ruleNumber');
+      // BR_G52
+      if (indexer.rulePreviousHash(block, HEAD) === false) throw Error('rulePreviousHash');
+      // BR_G53
+      if (indexer.rulePreviousIssuer(block, HEAD) === false) throw Error('rulePreviousIssuer');
+      // BR_G101
+      if (indexer.ruleIssuerIsMember(HEAD) === false) throw Error('ruleIssuerIsMember');
+      // BR_G54
+      if (indexer.ruleIssuersCount(block, HEAD) === false) throw Error('ruleIssuersCount');
+      // BR_G55
+      if (indexer.ruleIssuersFrame(block, HEAD) === false) throw Error('ruleIssuersFrame');
+      // BR_G56
+      if (indexer.ruleIssuersFrameVar(block, HEAD) === false) throw Error('ruleIssuersFrameVar');
+      // BR_G57
+      if (indexer.ruleMedianTime(block, HEAD) === false) throw Error('ruleMedianTime');
+      // BR_G58
+      if (indexer.ruleDividend(block, HEAD) === false) throw Error('ruleDividend');
+      // BR_G59
+      if (indexer.ruleUnitBase(block, HEAD) === false) throw Error('ruleUnitBase');
+      // BR_G60
+      if (indexer.ruleMembersCount(block, HEAD) === false) throw Error('ruleMembersCount');
+      // BR_G61
+      if (indexer.rulePowMin(block, HEAD) === false) throw Error('rulePowMin');
+      // BR_G62
+      if (indexer.ruleProofOfWork(HEAD) === false) throw Error('ruleProofOfWork');
+      // BR_G63
+      if (indexer.ruleIdentityWritability(iindex, conf) === false) throw Error('ruleIdentityWritability');
+      // BR_G64
+      if (indexer.ruleMembershipWritability(mindex, conf) === false) throw Error('ruleMembershipWritability');
+      // BR_G65
+      if (indexer.ruleCertificationWritability(cindex, conf) === false) throw Error('ruleCertificationWritability');
+      // BR_G66
+      if (indexer.ruleCertificationStock(cindex, conf) === false) throw Error('ruleCertificationStock');
+      // BR_G67
+      if (indexer.ruleCertificationPeriod(cindex) === false) throw Error('ruleCertificationPeriod');
+      // BR_G68
+      if (indexer.ruleCertificationFromMember(HEAD, cindex) === false) throw Error('ruleCertificationFromMember');
+      // BR_G69
+      if (indexer.ruleCertificationToMemberOrNewcomer(cindex) === false) throw Error('ruleCertificationToMemberOrNewcomer');
+      // BR_G70
+      if (indexer.ruleCertificationToLeaver(cindex) === false) throw Error('ruleCertificationToLeaver');
+      // BR_G71
+      if (indexer.ruleCertificationReplay(cindex) === false) throw Error('ruleCertificationReplay');
+      // BR_G72
+      if (indexer.ruleCertificationSignature(cindex) === false) throw Error('ruleCertificationSignature');
+      // BR_G73
+      if (indexer.ruleIdentityUIDUnicity(iindex) === false) throw Error('ruleIdentityUIDUnicity');
+      // BR_G74
+      if (indexer.ruleIdentityPubkeyUnicity(iindex) === false) throw Error('ruleIdentityPubkeyUnicity');
+      // BR_G75
+      if (indexer.ruleMembershipSuccession(mindex) === false) throw Error('ruleMembershipSuccession');
+      // BR_G76
+      if (indexer.ruleMembershipDistance(mindex) === false) throw Error('ruleMembershipDistance');
+      // BR_G77
+      if (indexer.ruleMembershipOnRevoked(mindex) === false) throw Error('ruleMembershipOnRevoked');
+      // BR_G78
+      if (indexer.ruleMembershipJoinsTwice(mindex) === false) throw Error('ruleMembershipJoinsTwice');
+      // BR_G79
+      if (indexer.ruleMembershipEnoughCerts(mindex) === false) throw Error('ruleMembershipEnoughCerts');
+      // BR_G80
+      if (indexer.ruleMembershipLeaverIsMember(mindex) === false) throw Error('ruleMembershipLeaverIsMember');
+      // BR_G81
+      if (indexer.ruleMembershipActiveIsMember(mindex) === false) throw Error('ruleMembershipActiveIsMember');
+      // BR_G82
+      if (indexer.ruleMembershipRevokedIsMember(mindex) === false) throw Error('ruleMembershipRevokedIsMember');
+      // BR_G83
+      if (indexer.ruleMembershipRevokedSingleton(mindex) === false) throw Error('ruleMembershipRevokedSingleton');
+      // BR_G84
+      if (indexer.ruleMembershipRevocationSignature(mindex) === false) throw Error('ruleMembershipRevocationSignature');
+      // BR_G85
+      if (indexer.ruleMembershipExcludedIsMember(iindex) === false) throw Error('ruleMembershipExcludedIsMember');
+      // BR_G86
+      if (indexer.ruleToBeKickedArePresent(mindex, dal) === false) throw Error('ruleToBeKickedArePresent');
+      // BR_G103
+      if (indexer.ruleTxWritability(sindex) === false) throw Error('ruleToBeKickedArePresent');
+      // BR_G87
+      if (indexer.ruleInputIsAvailable(sindex) === false) throw Error('ruleInputIsAvailable');
+      // BR_G88
+      if (indexer.ruleInputIsUnlocked(sindex) === false) throw Error('ruleInputIsUnlocked');
+      // BR_G89
+      if (indexer.ruleInputIsTimeUnlocked(sindex) === false) throw Error('ruleInputIsTimeUnlocked');
+      // BR_G90
+      if (indexer.ruleOutputBase(sindex, HEAD_1) === false) throw Error('ruleOutputBase');
     }
     else {
       yield Q.nbind(rules.CHECK.ASYNC.ALL_LOCAL_BUT_POW, rules, block, conf);
-      yield Q.nbind(rules.CHECK.ASYNC.ALL_GLOBAL_BUT_POW, rules, block, conf, dal);
     }
     // Check document's coherence
     yield checkIssuer(block);
@@ -48,6 +220,7 @@ function BlockchainContext() {
       block.fork = false;
       yield saveBlockData(currentBlock, block);
       logger.info('Block #' + block.number + ' added to the blockchain in %s ms', (new Date() - start));
+      vHEAD_1 = vHEAD = HEADrefreshed = null;
       return block;
     }
     catch(err) {
@@ -75,6 +248,8 @@ function BlockchainContext() {
     logger.debug('Reverting block #%s...', current.number);
     const res = yield that.revertBlock(current);
     logger.debug('Reverted block #%s', current.number);
+    // Invalidates the head, since it has changed.
+    yield refreshHead();
     return res;
   });
 
@@ -93,18 +268,40 @@ function BlockchainContext() {
   });
 
   this.revertBlock = (block) => co(function *() {
+
+    const blockstamp = [block.number, block.hash].join('-');
+
+    // Revert links
+    const writtenOn = yield dal.cindexDAL.getWrittenOn(blockstamp);
+    for (const entry of writtenOn) {
+      const from = yield dal.getWrittenIdtyByPubkey(entry.issuer);
+      const to = yield dal.getWrittenIdtyByPubkey(entry.receiver);
+      if (entry.op == constants.IDX_CREATE) {
+        // We remove the created link
+        dal.wotb.removeLink(from.wotb_id, to.wotb_id, true);
+      } else {
+        // We add the removed link
+        dal.wotb.addLink(from.wotb_id, to.wotb_id, true);
+      }
+    }
+
+    // Revert nodes
+    yield undoMembersUpdate(block);
+
+    yield dal.bindexDAL.removeBlock(block.number);
+    yield dal.mindexDAL.removeBlock(blockstamp);
+    yield dal.iindexDAL.removeBlock(blockstamp);
+    yield dal.cindexDAL.removeBlock(blockstamp);
+    yield dal.sindexDAL.removeBlock(blockstamp);
+
+    // Then: normal updates
     const previousBlock = yield dal.getBlockByNumberAndHashOrNull(block.number - 1, block.previousHash || '');
     // Set the block as SIDE block (equivalent to removal from main branch)
     yield dal.blockDAL.setSideBlock(block, previousBlock);
-    yield undoCertifications(block);
-    yield undoLinks(block);
-    yield dal.unflagExpiredIdentitiesOf(block.number);
-    yield dal.unflagExpiredCertificationsOf(block.number);
-    if (previousBlock) {
-      yield dal.undoObsoleteLinks(previousBlock.medianTime - conf.sigValidity);
-    }
-    yield undoMembersUpdate(block);
-    yield undoTransactionSources(block);
+
+    // Remove any source created for this block (both Dividend and Transaction).
+    yield dal.removeAllSourcesOfBlock(blockstamp);
+
     yield undoDeleteTransactions(block);
   });
 
@@ -134,30 +331,45 @@ function BlockchainContext() {
   this.current = () => dal.getCurrentBlockOrNull();
 
   const saveBlockData = (current, block) => co(function*() {
+
+    if (block.number == 0) {
+      yield that.saveParametersForRootBlock(block);
+    }
+
+    const indexes = yield dal.generateIndexes(block, conf);
+
+    // Newcomers
+    yield that.createNewcomers(indexes.iindex);
+
+    // Save indexes
+    yield dal.bindexDAL.saveEntity(indexes.HEAD);
+    yield dal.mindexDAL.insertBatch(indexes.mindex);
+    yield dal.iindexDAL.insertBatch(indexes.iindex);
+    yield dal.sindexDAL.insertBatch(indexes.sindex);
+    yield dal.cindexDAL.insertBatch(indexes.cindex);
+
+    // Create/Update nodes in wotb
+    yield that.updateMembers(block);
+
+    yield dal.trimIndexes(block, conf);
     yield updateBlocksComputedVars(current, block);
     // Saves the block (DAL)
     yield dal.saveBlock(block);
-    yield that.saveParametersForRootBlock(block);
-    // Create/Update members (create new identities if do not exist)
-    yield that.updateMembers(block);
+
+    // --> Update links
+    yield dal.updateWotbLinks(indexes.cindex);
+
     // Create/Update certifications
-    yield that.updateCertifications(block);
-    // Save links
-    yield that.updateLinksForBlocks([block], dal.getBlock.bind(dal));
-    // Compute obsolete links
-    yield that.computeObsoleteLinks(block);
-    // Compute obsolete memberships (active, joiner)
-    yield that.computeObsoleteMemberships(block);
-    // Compute obsolete identities
-    yield that.computeExpiredIdentities(block);
-    // Compute obsolete certifications
-    yield that.computeExpiredCertifications(block);
-    // Compute obsolete memberships
-    yield that.computeExpiredMemberships(block);
-    // Update consumed sources & create new ones
-    yield that.updateSources(block);
+    yield that.removeCertificationsFromSandbox(block);
+    // Create/Update memberships
+    yield that.removeMembershipsFromSandbox(block);
+    // Compute to be revoked members
+    yield that.computeToBeRevoked(indexes.mindex);
     // Delete eventually present transactions
     yield that.deleteTransactions(block);
+
+    yield dal.trimSandboxes(block, conf);
+
     return block;
   });
 
@@ -171,19 +383,10 @@ function BlockchainContext() {
     }
     // UD Time update
     if (block.number == 0) {
-      block.UDTime = block.medianTime; // Root = first UD time
       block.dividend = null;
     }
-    else if (block.dividend) {
-      const result = yield {
-        last: dal.lastUDBlock(),
-        root: dal.getBlock(0)
-      };
-      block.UDTime = conf.dt + (result.last ? result.last.UDTime : result.root.medianTime);
-    }
-    else {
+    else if (!block.dividend) {
       block.dividend = null;
-      block.UDTime = current.UDTime;
     }
   });
 
@@ -192,31 +395,28 @@ function BlockchainContext() {
     yield dal.removeUnWrittenWithUID(idty.uid);
   });
 
+  this.createNewcomers = (iindex) => co(function*() {
+    for (const entry of iindex) {
+      if (entry.op == constants.IDX_CREATE) {
+        // Reserves a wotb ID
+        entry.wotb_id = dal.wotb.addNode();
+        logger.trace('%s was affected wotb_id %s', entry.uid, entry.wotb_id);
+        // Remove from the sandbox any other identity with the same pubkey/uid, since it has now been reserved.
+        yield cleanRejectedIdentities({
+          pubkey: entry.pub,
+          uid: entry.uid
+        });
+      }
+    }
+  });
+
   this.updateMembers = (block) => co(function *() {
     return co(function *() {
-      // Newcomers
-      for (const identity of block.identities) {
-        let idty = Identity.statics.fromInline(identity);
-        // Computes the hash if not done yet
-        if (!idty.hash)
-          idty.hash = (hashf(rawer.getOfficialIdentity(idty)) + "").toUpperCase();
-        yield dal.newIdentity(idty);
-        yield cleanRejectedIdentities(idty);
-      }
       // Joiners (come back)
       for (const inlineMS of block.joiners) {
         let ms = Membership.statics.fromInline(inlineMS);
-        yield dal.joinIdentity(ms.issuer, ms.number);
-      }
-      // Actives
-      for (const inlineMS of block.actives) {
-        let ms = Membership.statics.fromInline(inlineMS);
-        yield dal.activeIdentity(ms.issuer, ms.number);
-      }
-      // Leavers
-      for (const inlineMS of block.leavers) {
-        let ms = Membership.statics.fromInline(inlineMS);
-        yield dal.leaveIdentity(ms.issuer, ms.number);
+        const idty = yield dal.getWrittenIdtyByPubkey(ms.issuer);
+        dal.wotb.setEnabled(true, idty.wotb_id);
       }
       // Revoked
       for (const inlineRevocation of block.revoked) {
@@ -225,98 +425,29 @@ function BlockchainContext() {
       }
       // Excluded
       for (const excluded of block.excluded) {
-        dal.excludeIdentity(excluded);
+        const idty = yield dal.getWrittenIdtyByPubkey(excluded);
+        dal.wotb.setEnabled(false, idty.wotb_id);
       }
     });
   });
 
   function undoMembersUpdate (block) {
     return co(function *() {
-      yield dal.unFlagToBeKicked();
       // Undo 'join' which can be either newcomers or comebackers
       for (const msRaw of block.joiners) {
         let ms = Membership.statics.fromInline(msRaw, 'IN', conf.currency);
-        yield dal.unJoinIdentity(ms);
+        const idty = yield dal.getWrittenIdtyByPubkey(ms.issuer);
+        dal.wotb.setEnabled(false, idty.wotb_id);
       }
-      // Undo newcomers (may strengthen the undo 'join')
+      // Undo newcomers
       for (const identity of block.identities) {
-        let idty = Identity.statics.fromInline(identity);
-        yield dal.unacceptIdentity(idty.pubkey);
-      }
-      // Undo renew (only remove last membership IN document)
-      for (const msRaw of block.actives) {
-        let ms = Membership.statics.fromInline(msRaw, 'IN', conf.currency);
-        yield dal.unRenewIdentity(ms);
+        // Does not matter which one it really was, we pop the last X identities
+        dal.wotb.removeNode();
       }
-      // Undo leavers (forget about their last membership OUT document)
-      for (const msRaw of block.leavers) {
-        let ms = Membership.statics.fromInline(msRaw, 'OUT', conf.currency);
-        yield dal.unLeaveIdentity(ms);
-      }
-      // Undo revoked (make them non-revoked)
-      let revokedPubkeys = [];
-      for (const inlineRevocation of block.revoked) {
-        let revocation = Identity.statics.revocationFromInline(inlineRevocation);
-        revokedPubkeys.push(revocation.pubkey);
-        yield dal.unrevokeIdentity(revocation.pubkey);
-      }
-      // Undo excluded (make them become members again, but set them as 'to be kicked')
+      // Undo excluded (make them become members again in wotb)
       for (const pubkey of block.excluded) {
-        yield dal.unExcludeIdentity(pubkey, revokedPubkeys.indexOf(pubkey) !== -1);
-      }
-    });
-  }
-
-  function undoCertifications(block) {
-    return co(function *() {
-      for (const inlineCert of block.certifications) {
-        let cert = Certification.statics.fromInline(inlineCert);
-        let toIdty = yield dal.getWrittenIdtyByPubkey(cert.to);
-        cert.target = new Identity(toIdty).getTargetHash();
-        let existing = yield dal.existsCert(cert);
-        existing.written_block = null;
-        existing.written_hash = null;
-        existing.linked = false;
-        existing.written = false;
-        yield dal.saveCert(new Certification(cert));
-      }
-    });
-  }
-
-  function undoLinks(block) {
-    return co(function *() {
-      for (const inlineCert of block.certifications) {
-        let cert = Certification.statics.fromInline(inlineCert);
-        let fromIdty = yield dal.getWrittenIdtyByPubkey(cert.from);
-        let toIdty = yield dal.getWrittenIdtyByPubkey(cert.to);
-        dal.removeLink(
-          new Link({
-            source: cert.from,
-            target: cert.to,
-            from_wotb_id: fromIdty.wotb_id,
-            to_wotb_id: toIdty.wotb_id,
-            timestamp: block.medianTime,
-            block_number: block.number,
-            block_hash: block.hash,
-            obsolete: false
-          }));
-      }
-    });
-  }
-
-  function undoTransactionSources(block) {
-    return co(function *() {
-      // Remove any source created for this block (both Dividend and Transaction)
-      dal.removeAllSourcesOfBlock(block.number);
-      for (const obj of block.transactions) {
-        obj.version = block.version;
-        obj.currency = block.currency;
-        obj.issuers = obj.signatories;
-        let tx = new Transaction(obj);
-        let txObj = tx.getTransaction();
-        for (const input of txObj.inputs) {
-          yield dal.unConsumeSource(input.identifier, input.noffset);
-        }
+        const idty = yield dal.getWrittenIdtyByPubkey(pubkey);
+        dal.wotb.setEnabled(true, idty.wotb_id);
       }
     });
   }
@@ -324,7 +455,6 @@ function BlockchainContext() {
   function undoDeleteTransactions(block) {
     return co(function *() {
       for (const obj of block.transactions) {
-        obj.version = block.version;
         obj.currency = block.currency;
         obj.issuers = obj.signatories;
         let tx = new Transaction(obj);
@@ -334,57 +464,53 @@ function BlockchainContext() {
   }
 
   /**
-   * Historical method that takes certifications from a block and tries to either:
-   *  * Update the certification found in the DB an set it as written
-   *  * Create it if it does not exist
+   * Delete certifications from the sandbox since it has been written.
    *
-   * Has a sibling method named 'updateCertificationsForBlocks'.
-   * @param block
-   * @param done
+   * @param block Block in which are contained the certifications to remove from sandbox.
    */
-  this.updateCertifications = (block) => co(function*() {
+  this.removeCertificationsFromSandbox = (block) => co(function*() {
     for (let inlineCert of block.certifications) {
       let cert = Certification.statics.fromInline(inlineCert);
       let idty = yield dal.getWritten(cert.to);
       cert.target = new Identity(idty).getTargetHash();
-      const to_uid = idty.uid;
-      idty = yield dal.getWritten(cert.from);
-      const from_uid = idty.uid;
-      const existing = yield dal.existsCert(cert);
-      if (existing) {
-        cert = existing;
-      }
-      cert.written_block = block.number;
-      cert.written_hash = block.hash;
-      cert.from_uid = from_uid;
-      cert.to_uid = to_uid;
-      cert.linked = true;
-      yield dal.officializeCertification(new Certification(cert));
+      yield dal.deleteCert(cert);
+    }
+  });
+
+  /**
+   * Delete memberships from the sandbox since it has been written.
+   *
+   * @param block Block in which are contained the certifications to remove from sandbox.
+   */
+  this.removeMembershipsFromSandbox = (block) => co(function*() {
+    const mss = block.joiners.concat(block.actives).concat(block.leavers);
+    for (const inlineMS of mss) {
+      let ms = Membership.statics.fromInline(inlineMS);
+      yield dal.deleteMS(ms);
     }
   });
 
   that.saveParametersForRootBlock = (block) => co(function*() {
     if (block.parameters) {
-      const sp = block.parameters.split(':');
-
-      conf.c = parseFloat(sp[0]);
-      conf.dt = parseInt(sp[1]);
-      conf.ud0 = parseInt(sp[2]);
-      conf.sigPeriod = parseInt(sp[3]);
-      conf.sigStock = parseInt(sp[4]);
-      conf.sigWindow = parseInt(sp[5]);
-      conf.sigValidity = parseInt(sp[6]);
-      conf.sigQty = parseInt(sp[7]);
-      conf.idtyWindow = parseInt(sp[8]);
-      conf.msWindow = parseInt(sp[9]);
-      conf.xpercent = parseFloat(sp[10]);
-      conf.msValidity = parseInt(sp[11]);
-      conf.stepMax = parseInt(sp[12]);
-      conf.medianTimeBlocks = parseInt(sp[13]);
-      conf.avgGenTime = parseInt(sp[14]);
-      conf.dtDiffEval = parseInt(sp[15]);
-      conf.blocksRot = parseInt(sp[16]);
-      conf.percentRot = parseFloat(sp[17]);
+      const bconf = Block.statics.getConf(block);
+      conf.c = bconf.c;
+      conf.dt = bconf.dt;
+      conf.ud0 = bconf.ud0;
+      conf.sigPeriod = bconf.sigPeriod;
+      conf.sigStock = bconf.sigStock;
+      conf.sigWindow = bconf.sigWindow;
+      conf.sigValidity = bconf.sigValidity;
+      conf.sigQty = bconf.sigQty;
+      conf.idtyWindow = bconf.idtyWindow;
+      conf.msWindow = bconf.msWindow;
+      conf.xpercent = bconf.xpercent;
+      conf.msValidity = bconf.msValidity;
+      conf.stepMax = bconf.stepMax;
+      conf.medianTimeBlocks = bconf.medianTimeBlocks;
+      conf.avgGenTime = bconf.avgGenTime;
+      conf.dtDiffEval = bconf.dtDiffEval;
+      conf.blocksRot = bconf.blocksRot;
+      conf.percentRot = bconf.percentRot;
       conf.currency = block.currency;
       // Super important: adapt wotb module to handle the correct stock
       dal.wotb.setMaxCert(conf.sigStock);
@@ -392,11 +518,10 @@ function BlockchainContext() {
     }
   });
 
-  this.computeObsoleteLinks = (block) => co(function*() {
-    yield dal.obsoletesLinks(block.medianTime - conf.sigValidity);
-    const members = yield dal.getMembersWithoutEnoughValidLinks(conf.sigQty);
-    for (const idty of members) {
-      yield dal.setKicked(idty.pubkey, new Identity(idty).getTargetHash(), true);
+  this.computeToBeRevoked = (mindex) => co(function*() {
+    const revocations = _.filter(mindex, (entry) => entry.revoked_on);
+    for (const revoked of revocations) {
+      yield dal.setRevoked(revoked.pub, true);
     }
   });
 
@@ -409,155 +534,11 @@ function BlockchainContext() {
       throw 'Key ' + target + ' does not have enough links (' + count + '/' + conf.sigQty + ')';
   });
 
-  this.computeObsoleteMemberships = (block) => co(function *() {
-    let lastForKick = yield dal.getMembershipExcludingBlock(block, conf.msValidity);
-    let lastForRevoke = yield dal.getMembershipRevocatingBlock(block, conf.msValidity * constants.REVOCATION_FACTOR);
-    if (lastForKick) {
-      yield dal.kickWithOutdatedMemberships(lastForKick.number);
-    }
-    if (lastForRevoke) {
-      yield dal.revokeWithOutdatedMemberships(lastForRevoke.number);
-    }
-  });
-
-  this.computeExpiredIdentities = (block) => co(function *() {
-    let lastForExpiry = yield dal.getIdentityExpiringBlock(block, conf.idtyWindow);
-    if (lastForExpiry) {
-      yield dal.flagExpiredIdentities(lastForExpiry.number, block.number);
-    }
-  });
-
-  this.computeExpiredCertifications = (block) => co(function *() {
-    let lastForExpiry = yield dal.getCertificationExpiringBlock(block, conf.certWindow);
-    if (lastForExpiry) {
-      yield dal.flagExpiredCertifications(lastForExpiry.number, block.number);
-    }
-  });
-
-  this.computeExpiredMemberships = (block) => co(function *() {
-    let lastForExpiry = yield dal.getMembershipExpiringBlock(block, conf.certWindow);
-    if (lastForExpiry) {
-      yield dal.flagExpiredMemberships(lastForExpiry.number, block.number);
-    }
-  });
-
-  this.updateSources = (block) => co(function*() {
-    if (block.dividend) {
-      const idties = yield dal.getMembers();
-      for (const idty of idties) {
-        yield dal.saveSource(new Source({
-          'type': 'D',
-          'number': block.number,
-          'time': block.medianTime,
-          'identifier': idty.pubkey,
-          'noffset': block.number,
-          'block_hash': block.hash,
-          'amount': block.dividend,
-          'base': block.unitbase,
-          'conditions': 'SIG(' + idty.pubkey + ')', // Only this pubkey can unlock its UD
-          'consumed': 0
-        }));
-      }
-    }
-
-    for (const obj of block.transactions) {
-      obj.currency = block.currency;
-      obj.issuers = obj.signatories;
-      const tx = new Transaction(obj);
-      const txObj = tx.getTransaction();
-      const txHash = tx.getHash(true);
-      for (const input of txObj.inputs) {
-        yield dal.setConsumedSource(input.identifier, input.noffset);
-      }
-
-      let index = 0;
-      for (const output of txObj.outputs) {
-        yield dal.saveSource(new Source({
-          'type': 'T',
-          'number': block.number,
-          'time': block.medianTime,
-          'identifier': txHash,
-          'noffset': index++,
-          'block_hash': block.hash,
-          'amount': output.amount,
-          'base': output.base,
-          'conditions': output.conditions,
-          'consumed': 0
-        }));
-      }
-    }
-  });
-
-  /**
-   * New method for CREATING memberships found in blocks.
-   * Made for performance reasons, this method will batch insert all memberships at once.
-   * @param blocks
-   * @returns {*}
-   */
-  this.updateMembershipsForBlocks = (blocks) => co(function *() {
-    const memberships = [];
-    const types = {
-      'join': 'joiners',
-      'active': 'actives',
-      'leave': 'leavers'
-    };
-    for (const block of blocks) {
-      _.keys(types).forEach(function(type){
-        const msType = type == 'leave' ? 'out' : 'in';
-        const field = types[type];
-        const mss = block[field];
-        for (const msRaw of mss) {
-          const ms = Membership.statics.fromInline(msRaw, type == 'leave' ? 'OUT' : 'IN', block.currency);
-          ms.membership = msType.toUpperCase();
-          ms.written = true;
-          ms.written_number = block.number;
-          ms.type = type;
-          ms.hash = String(hashf(ms.getRawSigned())).toUpperCase();
-          ms.idtyHash = (hashf(ms.userid + ms.certts + ms.issuer) + "").toUpperCase();
-          memberships.push(ms);
-        }
-      });
-    }
-    return dal.updateMemberships(memberships);
-  });
-
-  /**
-   * New method for CREATING links found in blocks.
-   * Made for performance reasons, this method will batch insert all links at once.
-   * @param blocks
-   * @param getBlock
-   * @returns {*}
-   */
-  this.updateLinksForBlocks = (blocks, getBlock) => co(function *() {
-    let links = [];
-    for (const block of blocks) {
-      for (const inlineCert of block.certifications) {
-        let cert = Certification.statics.fromInline(inlineCert);
-        let tagBlock = block;
-        if (block.number > 0) {
-          tagBlock = yield getBlock(cert.block_number);
-        }
-        let fromIdty = yield dal.getWrittenIdtyByPubkey(cert.from);
-        let toIdty = yield dal.getWrittenIdtyByPubkey(cert.to);
-        links.push({
-          source: cert.from,
-          target: cert.to,
-          from_wotb_id: fromIdty.wotb_id,
-          to_wotb_id: toIdty.wotb_id,
-          timestamp: tagBlock.medianTime,
-          block_number: block.number,
-          block_hash: block.hash,
-          obsolete: false
-        });
-      }
-    }
-    return dal.updateLinks(links);
-  });
-
   /**
    * New method for CREATING transactions found in blocks.
    * Made for performance reasons, this method will batch insert all transactions at once.
    * @param blocks
+   * @param getBlockByNumberAndHash
    * @returns {*}
    */
   this.updateTransactionsForBlocks = (blocks, getBlockByNumberAndHash) => co(function *() {
@@ -585,80 +566,6 @@ function BlockchainContext() {
     return dal.updateTransactions(txs);
   });
 
-  /**
-   * New method for CREATING certifications found in blocks.
-   * Made for performance reasons, this method will batch insert all certifications at once.
-   * @param blocks
-   * @returns {*}
-   */
-  this.updateCertificationsForBlocks = (blocks) => co(function *() {
-    const certs = [];
-    for (const block of blocks) {
-      for (const inlineCert of block.certifications) {
-        let cert = Certification.statics.fromInline(inlineCert);
-        const to = yield dal.getWrittenIdtyByPubkey(cert.to);
-        const to_uid = to.uid;
-        cert.target = new Identity(to).getTargetHash();
-        const from = yield dal.getWrittenIdtyByPubkey(cert.from);
-        const from_uid = from.uid;
-        const existing = yield dal.existsCert(cert);
-        if (existing) {
-          cert = existing;
-        }
-        cert.written_block = block.number;
-        cert.written_hash = block.hash;
-        cert.from_uid = from_uid;
-        cert.to_uid = to_uid;
-        cert.linked = true;
-        certs.push(cert);
-      }
-    }
-    return dal.updateCertifications(certs);
-  });
-
-  /**
-   * New method for CREATING sources found in transactions of blocks.
-   * Made for performance reasons, this method will batch insert all sources at once.
-   * @param blocks
-   * @returns {*}
-   */
-  this.updateTransactionSourcesForBlocks = (blocks, dividends) => co(function *() {
-    let sources = dividends;
-    for (const block of blocks) {
-      // Transactions
-      for (const json of block.transactions) {
-        let obj = json;
-        obj.version = block.version;
-        obj.currency = block.currency;
-        obj.issuers = json.signatories;
-        let tx = new Transaction(obj);
-        let txObj = tx.getTransaction();
-        let txHash = tx.getHash(true);
-        sources = sources.concat(txObj.inputs.map((input) => _.extend({ toConsume: true }, input)));
-        sources = sources.concat(txObj.outputs.map((output, index) => _.extend({
-          toConsume: false
-        }, {
-          'type': 'T',
-          'number': block.number,
-          'block_hash': block.hash,
-          'fingerprint': txHash,
-          'amount': output.amount,
-          'base': output.base,
-          'consumed': false,
-          'identifier': txHash,
-          'noffset': index,
-          'conditions': output.conditions
-        })));
-      }
-    }
-    try {
-      let res = yield dal.updateSources(sources);
-      return res;
-    } catch (e) {
-      throw e;
-    }
-  });
-
   this.deleteTransactions = (block) => co(function*() {
     for (const obj of block.transactions) {
       obj.currency = block.currency;
diff --git a/app/lib/computation/permanentProver.js b/app/lib/computation/permanentProver.js
index 197522945f122f33b6961d8acaba85b6e3518d2d..fc65d7a96cbddb85722819b4c4a3454023298258 100644
--- a/app/lib/computation/permanentProver.js
+++ b/app/lib/computation/permanentProver.js
@@ -59,7 +59,7 @@ function PermanentProver(server) {
             throw 'Waiting for a root block before computing new blocks';
           }
           const version = current ? current.version : constants.BLOCK_GENERATED_VERSION;
-          const trial = yield rules.HELPERS.getTrialLevel(version, selfPubkey, conf, dal);
+          const trial = yield server.getBcContext().getIssuerPersonalizedDifficulty(version, selfPubkey);
           if (trial > (current.powMin + constants.POW_MAXIMUM_ACCEPTABLE_HANDICAP)) {
             logger.debug('Trial = %s, powMin = %s, pubkey = %s', trial, current.powMin, selfPubkey.slice(0, 6));
             throw 'Too high difficulty: waiting for other members to write next block';
@@ -107,7 +107,7 @@ function PermanentProver(server) {
             co(function*() {
               try {
                 const block2 = yield server.BlockchainService.generateNext();
-                const trial2 = yield rules.HELPERS.getTrialLevel(block2.version, server.keyPair.publicKey, server.conf, server.dal);
+                const trial2 = yield server.getBcContext().getIssuerPersonalizedDifficulty(block2.version, server.keyPair.publicKey);
                 lastComputedBlock = yield server.BlockchainService.makeNextBlock(block2, trial2);
                 yield onBlockCallback(lastComputedBlock); 
               } catch (e) {
diff --git a/app/lib/constants.js b/app/lib/constants.js
index 5f3c2971e5c2c7b3afae72cc61be8a671e04e383..62dcd5d8f0a0a3f98a364fb26cd6096e23c0605e 100644
--- a/app/lib/constants.js
+++ b/app/lib/constants.js
@@ -109,6 +109,7 @@ module.exports = {
     BLOCK_ALREADY_PROCESSED:              { httpCode: 400, uerr: { ucode: 2028, message: 'Already processed' }},
     TOO_OLD_MEMBERSHIP:                   { httpCode: 400, uerr: { ucode: 2029, message: "Too old membership." }},
     TX_ALREADY_PROCESSED:                 { httpCode: 400, uerr: { ucode: 2030, message: "Transaction already processed" }},
+    A_MORE_RECENT_MEMBERSHIP_EXISTS:      { httpCode: 400, uerr: { ucode: 2031, message: "A more recent membership already exists" }}
   },
 
   DEBUG: {
@@ -374,7 +375,15 @@ module.exports = {
 
   TRANSACTION_MAX_TRIES: 10,
   NONCE_RANGE: 1000 * 1000 * 1000 * 100,
-  POW_MAXIMUM_ACCEPTABLE_HANDICAP: 64
+  POW_MAXIMUM_ACCEPTABLE_HANDICAP: 64,
+
+  // INDEXES
+  M_INDEX: 'MINDEX',
+  I_INDEX: 'IINDEX',
+  S_INDEX: 'SINDEX',
+  C_INDEX: 'CINDEX',
+  IDX_CREATE: 'CREATE',
+  IDX_UPDATE: 'UPDATE'
 };
 
 function exact (regexpContent) {
diff --git a/app/lib/dal/drivers/sqlite.js b/app/lib/dal/drivers/sqlite.js
index 4a38c4723d3088e4b81b4db02ff29d0939ad4591..8389827759e55865ca88860f3e76f221514f4324 100644
--- a/app/lib/dal/drivers/sqlite.js
+++ b/app/lib/dal/drivers/sqlite.js
@@ -31,7 +31,7 @@ function SQLiteDriver(path) {
     const db = yield getDB();
     return new Promise((resolve, reject) => db.all(sql, params, (err, rows) => {
       if (err) {
-        return reject(err);
+        return reject(Error('SQL error "' + err.message + '" on query "' + sql + '"'));
       } else {
         return resolve(rows);
       }
@@ -42,7 +42,7 @@ function SQLiteDriver(path) {
     const db = yield getDB();
     return new Promise((resolve, reject) => db.exec(sql, (err) => {
       if (err) {
-        return reject(err);
+        return reject(Error('SQL error "' + err.message + '" on query "' + sql + '"'));
       } else {
         return resolve();
       }
diff --git a/app/lib/dal/fileDAL.js b/app/lib/dal/fileDAL.js
index 78b49c26f8e70c4968b47d7cfb78dee27e587c34..4a2bfaaa53ca0baac98ee3189a636b3527f1f3e7 100644
--- a/app/lib/dal/fileDAL.js
+++ b/app/lib/dal/fileDAL.js
@@ -2,18 +2,17 @@
 const Q       = require('q');
 const co      = require('co');
 const _       = require('underscore');
+const indexer = require('../dup/indexer');
 const hashf   = require('../ucp/hashf');
 const wotb    = require('../wot');
 const logger = require('../logger')('filedal');
 const directory = require('../system/directory');
 const Configuration = require('../entity/configuration');
-const Membership = require('../entity/membership');
 const Merkle = require('../entity/merkle');
 const Transaction = require('../entity/transaction');
 const constants = require('../constants');
 const ConfDAL = require('./fileDALs/confDAL');
 const StatDAL = require('./fileDALs/statDAL');
-const IndicatorsDAL = require('./fileDALs/IndicatorsDAL');
 const CFSStorage = require('./fileDALs/AbstractCFS');
 
 module.exports = (params) => {
@@ -25,25 +24,26 @@ function FileDAL(params) {
   const rootPath = params.home;
   const myFS = params.fs;
   const sqliteDriver = params.dbf();
-  const wotbInstance = params.wotb;
   const that = this;
 
   this.profile = 'DAL';
-  this.wotb = wotbInstance;
+  this.wotb = params.wotb;
 
   // DALs
   this.confDAL = new ConfDAL(rootPath, myFS, null, that, CFSStorage);
   this.metaDAL = new (require('./sqliteDAL/MetaDAL'))(sqliteDriver);
   this.peerDAL = new (require('./sqliteDAL/PeerDAL'))(sqliteDriver);
   this.blockDAL = new (require('./sqliteDAL/BlockDAL'))(sqliteDriver);
-  this.sourcesDAL = new (require('./sqliteDAL/SourcesDAL'))(sqliteDriver);
   this.txsDAL = new (require('./sqliteDAL/TxsDAL'))(sqliteDriver);
-  this.indicatorsDAL = new IndicatorsDAL(rootPath, myFS, null, that, CFSStorage);
   this.statDAL = new StatDAL(rootPath, myFS, null, that, CFSStorage);
-  this.linksDAL = new (require('./sqliteDAL/LinksDAL'))(sqliteDriver, wotbInstance);
-  this.idtyDAL = new (require('./sqliteDAL/IdentityDAL'))(sqliteDriver, wotbInstance);
+  this.idtyDAL = new (require('./sqliteDAL/IdentityDAL'))(sqliteDriver);
   this.certDAL = new (require('./sqliteDAL/CertDAL'))(sqliteDriver);
   this.msDAL = new (require('./sqliteDAL/MembershipDAL'))(sqliteDriver);
+  this.bindexDAL = new (require('./sqliteDAL/index/BIndexDAL'))(sqliteDriver);
+  this.mindexDAL = new (require('./sqliteDAL/index/MIndexDAL'))(sqliteDriver);
+  this.iindexDAL = new (require('./sqliteDAL/index/IIndexDAL'))(sqliteDriver);
+  this.sindexDAL = new (require('./sqliteDAL/index/SIndexDAL'))(sqliteDriver);
+  this.cindexDAL = new (require('./sqliteDAL/index/CIndexDAL'))(sqliteDriver);
 
   this.newDals = {
     'metaDAL': that.metaDAL,
@@ -51,11 +51,8 @@ function FileDAL(params) {
     'certDAL': that.certDAL,
     'msDAL': that.msDAL,
     'idtyDAL': that.idtyDAL,
-    'sourcesDAL': that.sourcesDAL,
-    'linksDAL': that.linksDAL,
     'txsDAL': that.txsDAL,
     'peerDAL': that.peerDAL,
-    'indicatorsDAL': that.indicatorsDAL,
     'confDAL': that.confDAL,
     'statDAL': that.statDAL,
     'ghostDAL': {
@@ -72,7 +69,12 @@ function FileDAL(params) {
             'CREATE VIEW IF NOT EXISTS network AS select i.uid, (last_try - first_down) / 1000 as down_delay_in_sec, p.* from peer p LEFT JOIN idty i on i.pubkey = p.pubkey ORDER by down_delay_in_sec;' +
             'COMMIT;');
       })
-    }
+    },
+    'bindexDAL': that.bindexDAL,
+    'mindexDAL': that.mindexDAL,
+    'iindexDAL': that.iindexDAL,
+    'sindexDAL': that.sindexDAL,
+    'cindexDAL': that.cindexDAL
   };
 
   let currency = '';
@@ -85,7 +87,8 @@ function FileDAL(params) {
     }
     logger.debug("Upgrade database...");
     yield that.metaDAL.upgradeDatabase();
-    const latestMember = yield that.idtyDAL.getLatestMember();
+    // TODO: remove as of v1.0
+    const latestMember = yield that.iindexDAL.getLatestMember();
     if (latestMember && that.wotb.getWoTSize() > latestMember.wotb_id + 1) {
       logger.warn('Maintenance: cleaning wotb...');
       while (that.wotb.getWoTSize() > latestMember.wotb_id + 1) {
@@ -101,8 +104,6 @@ function FileDAL(params) {
 
   this.getDBVersion = () => that.metaDAL.getVersion();
 
-  this.getCurrency = () => currency;
-
   that.writeFileOfBlock = (block) => that.blockDAL.saveBlock(block);
 
   this.writeSideFileOfBlock = (block) =>
@@ -110,37 +111,6 @@ function FileDAL(params) {
 
   this.listAllPeers = () => that.peerDAL.listAll();
 
-  function nullIfError(promise, done) {
-    return promise
-        .then(function (p) {
-          done && done(null, p);
-          return p;
-        })
-        .catch(function () {
-          done && done(null, null);
-          return null;
-        });
-  }
-
-  function nullIfErrorIs(promise, expectedError, done) {
-    return promise
-        .then(function (p) {
-          done && done(null, p);
-          return p;
-        })
-        .catch(function (err) {
-          if (err == expectedError) {
-            done && done(null, null);
-            return null;
-          }
-          if (done) {
-            done(err);
-            return null;
-          }
-          throw err;
-        });
-  }
-
   this.getPeer = (pubkey) => co(function*() {
     try {
       return that.peerDAL.getPeer(pubkey)
@@ -186,28 +156,35 @@ function FileDAL(params) {
   });
 
   this.getBlockByNumberAndHashOrNull = (number, hash) => co(function*() {
-    return yield nullIfError(that.getBlockByNumberAndHash(number, hash));
-  });
-
-  this.getChainabilityBlock = (currentTime, sigPeriod) => co(function *() {
-    // AGE = current_time - block_time
-    // CHAINABLE = AGE >= sigPeriod
-    // CHAINABLE = block_time =< current_time - sigPeriod
-    return that.blockDAL.getMoreRecentBlockWithTimeEqualBelow(currentTime - sigPeriod);
+    try {
+      return yield that.getBlockByNumberAndHash(number, hash);
+    } catch (e) {
+      return null;
+    }
   });
 
-  this.existsNonChainableLink = (from, chainabilityBlockNumber, sigStock) => co(function *() {
+  this.existsNonChainableLink = (from, vHEAD_1, sigStock) => co(function *() {
     // Cert period rule
-    let links = yield that.linksDAL.getLinksOfIssuerAbove(from, chainabilityBlockNumber);
-    if (links.length > 0) return true;
+    const medianTime = vHEAD_1 ? vHEAD_1.medianTime : 0;
+    const linksFrom = yield that.cindexDAL.reducablesFrom(from);
+    const unchainables = _.filter(linksFrom, (link) => link.chainable_on > medianTime);
+    if (unchainables.length > 0) return true;
     // Max stock rule
-    let activeLinks = yield that.linksDAL.getValidLinksFrom(from);
+    let activeLinks = _.filter(linksFrom, (link) => !link.expired_on);
     return activeLinks.length >= sigStock;
   });
 
 
   this.getCurrentBlockOrNull = () => co(function*() {
-    return nullIfErrorIs(that.getBlockCurrent(), constants.ERROR.BLOCK.NO_CURRENT_BLOCK);
+    let current = null;
+    try {
+      current = yield that.getBlockCurrent();
+    } catch (e) {
+      if (e != constants.ERROR.BLOCK.NO_CURRENT_BLOCK) {
+        throw e;
+      }
+    }
+    return current;
   });
 
   this.getPromoted = (number) => that.getBlock(number);
@@ -223,8 +200,6 @@ function FileDAL(params) {
   
   this.getCountOfPoW = (issuer) => that.blockDAL.getCountOfBlocksIssuedBy(issuer);
 
-  this.getNbIssuedInFrame = (issuer, from) => that.blockDAL.getNbIssuedFrom(issuer, from);
-
   this.getBlocksBetween = (start, end) => Q(this.blockDAL.getBlocks(Math.max(0, start), end));
 
   this.getForkBlocksFollowing = (current) => this.blockDAL.getNextForkBlocks(current.number, current.hash);
@@ -236,66 +211,32 @@ function FileDAL(params) {
     return current;
   });
 
-  this.getBlockFrom = (number) => co(function*() {
-    const current = yield that.getCurrentBlockOrNull();
-    return that.getBlocksBetween(number, current.number);
-  });
-
-  this.getValidLinksFrom = (from) => that.linksDAL.getValidLinksFrom(from);
-
-  this.getValidLinksTo = (to) => that.linksDAL.getValidLinksTo(to);
-
-  this.getMembersWithoutEnoughValidLinks = (sigQty) => that.idtyDAL.query('' +
-    'SELECT * ' +
-    'FROM idty i ' +
-    'WHERE member ' +
-    'AND (' +
-    ' SELECT count(*) ' +
-    ' FROM link lnk ' +
-    ' WHERE NOT lnk.obsolete ' +
-    ' AND lnk.target = i.pubkey' +
-    ') < ?', [sigQty]);
+  this.getValidLinksTo = (to) => that.cindexDAL.getValidLinksTo(to);
 
-  this.getPreviousLinks = (from, to) => co(function *() {
-    let links = yield that.linksDAL.getLinksWithPath(from, to);
-    links = _.sortBy(links, 'timestamp');
-    return links[links.length - 1];
-  });
-
-  this.getValidFromTo = (from, to) => co(function*() {
-    const links = that.getValidLinksFrom(from);
-    return _.chain(links).where({target: to}).value();
-  });
+  this.getAvailableSourcesByPubkey = (pubkey) => this.sindexDAL.getAvailableForPubkey(pubkey);
 
-  this.getLastValidFrom = (from) => co(function *() {
-    let links = yield that.linksDAL.getLinksFrom(from);
-    links = _.sortBy(links, 'timestamp');
-    return links[links.length - 1];
+  this.getIdentityByHashOrNull = (hash) => co(function*() {
+    const pending = yield that.idtyDAL.getByHash(hash);
+    if (!pending) {
+      return that.iindexDAL.getFromHash(hash);
+    }
+    return pending;
   });
 
-  this.getAvailableSourcesByPubkey = function (pubkey) {
-    return that.sourcesDAL.getAvailableForPubkey(pubkey);
-  };
-
-  this.getIdentityByHashOrNull = (hash) => that.idtyDAL.getByHash(hash);
-
-  this.getMembers = () => co(function*() {
-    const idties = yield that.idtyDAL.getWhoIsOrWasMember()
-    return _.chain(idties).where({member: true}).value();
-  });
+  this.getMembers = () => that.iindexDAL.getMembers();
 
   // TODO: this should definitely be reduced by removing fillInMembershipsOfIdentity
   this.getWritten = (pubkey) => co(function*() {
     try {
-      return yield that.fillInMembershipsOfIdentity(that.idtyDAL.getFromPubkey(pubkey));
+      return yield that.fillInMembershipsOfIdentity(that.iindexDAL.getFromPubkey(pubkey));
     } catch (err) {
       logger.error(err);
       return null;
     }
   });
 
-  this.getWrittenIdtyByPubkey = (pubkey) => this.idtyDAL.getFromPubkey(pubkey);
-  this.getWrittenIdtyByUID = (pubkey) => this.idtyDAL.getFromUID(pubkey);
+  this.getWrittenIdtyByPubkey = (pubkey) => this.iindexDAL.getFromPubkey(pubkey);
+  this.getWrittenIdtyByUID = (pubkey) => this.iindexDAL.getFromUID(pubkey);
 
   this.fillInMembershipsOfIdentity = (queryPromise) => co(function*() {
     try {
@@ -327,32 +268,81 @@ function FileDAL(params) {
     return _.chain(pending).where({pubkey: pubkey}).value();
   });
 
-  this.getToBeKicked = () => co(function*() {
-    const membersOnce = yield that.idtyDAL.getWhoIsOrWasMember();
-    return _.chain(membersOnce).where({member: true, kick: true}).value();
-  });
-
   this.getRevocatingMembers = () => co(function *() {
-    return that.idtyDAL.getToRevoke();
+    const revoking = yield that.idtyDAL.getToRevoke();
+    const toRevoke = [];
+    for (const pending of revoking) {
+      const idty = yield that.getWrittenIdtyByPubkey(pending.pubkey);
+      if (!idty.revoked_on) {
+        toRevoke.push(pending);
+      }
+    }
+    return toRevoke;
   });
 
-  this.getToBeKickedPubkeys = () => co(function *() {
-    const exclusions = yield that.getToBeKicked();
-    return _.pluck(exclusions, 'pubkey');
-  });
+  this.getToBeKickedPubkeys = () => that.iindexDAL.getToBeKickedPubkeys();
 
-  this.searchJustIdentities = (search) => this.idtyDAL.searchThoseMatching(search);
+  this.searchJustIdentities = (search) => co(function*() {
+    const pendings = yield that.idtyDAL.searchThoseMatching(search);
+    const writtens = yield that.iindexDAL.searchThoseMatching(search);
+    const nonPendings = _.filter(writtens, (w) => {
+      return _.where(pendings, { pubkey: w.pub }).length == 0;
+    });
+    const found = pendings.concat(nonPendings);
+    return yield found.map(f => co(function*() {
+      const ms = yield that.mindexDAL.getReducedMS(f.pub);
+      if (ms) {
+        f.revoked_on = ms.revoked_on ? parseInt(ms.revoked_on) : null;
+        f.revoked = !!f.revoked_on;
+        f.revocation_sig = ms.revocation || null;
+      }
+      return f;
+    }))
+  });
 
-  this.certsToTarget = (hash) => co(function*() {
+  this.certsToTarget = (pub, hash) => co(function*() {
     const certs = yield that.certDAL.getToTarget(hash);
-    const matching = _.chain(certs).sortBy((c) => -c.block).value();
+    const links = yield that.cindexDAL.getValidLinksTo(pub);
+    let matching = certs;
+    links.map((entry) => {
+      entry.from = entry.issuer;
+      const co = entry.created_on.split('-');
+      const wo = entry.written_on.split('-');
+      entry.block = parseInt(co[0]);
+      entry.block_number = parseInt(co[0]);
+      entry.block_hash = co[1];
+      entry.linked = true;
+      entry.written_block = parseInt(wo[0]);
+      entry.written_hash = wo[1];
+      matching.push(entry);
+    });
+    matching  = _.sortBy(matching, (c) => -c.block);
     matching.reverse();
     return matching;
   });
 
   this.certsFrom = (pubkey) => co(function*() {
-    const certs = yield that.certDAL.getFromPubkey(pubkey);
-    return _.chain(certs).where({from: pubkey}).sortBy((c) => c.block).value();
+    const certs = yield that.certDAL.getFromPubkeyCerts(pubkey);
+    const links = yield that.cindexDAL.getValidLinksFrom(pubkey);
+    let matching = certs;
+    yield links.map((entry) => co(function*() {
+      const idty = yield that.getWrittenIdtyByPubkey(entry.receiver);
+      entry.from = entry.issuer;
+      entry.to = entry.receiver;
+      const co = entry.created_on.split('-');
+      const wo = entry.written_on.split('-');
+      entry.block = parseInt(co[0]);
+      entry.block_number = parseInt(co[0]);
+      entry.block_hash = co[1];
+      entry.target = idty.hash;
+      entry.linked = true;
+      entry.written_block = parseInt(wo[0]);
+      entry.written_hash = wo[1];
+      matching.push(entry);
+    }));
+    matching  = _.sortBy(matching, (c) => -c.block);
+    matching.reverse();
+    return matching;
   });
 
   this.certsFindNew = () => co(function*() {
@@ -365,12 +355,14 @@ function FileDAL(params) {
     return _.chain(certs).sortBy((c) => -c.block).value();
   });
 
-  this.getMembershipForHashAndIssuer = (ms) => co(function*() {
-    try {
-      return that.msDAL.getMembershipOfIssuer(ms);
-    } catch (err) {
-      return null;
+  this.getMostRecentMembershipNumberForIssuer = (issuer) => co(function*() {
+    const mss = yield that.msDAL.getMembershipsOfIssuer(issuer);
+    const reduced = yield that.mindexDAL.getReducedMS(issuer);
+    let max = reduced ? parseInt(reduced.created_on) : -1;
+    for (const ms of mss) {
+      max = Math.max(ms.number, max);
     }
+    return max;
   });
 
   this.lastJoinOfIdentity = (target) => co(function *() {
@@ -388,13 +380,11 @@ function FileDAL(params) {
     return _.chain(mss).sortBy((ms) => -ms.sigDate).value();
   });
 
-  this.existsLinkFromOrAfterDate = (from, to, minDate) => co(function *() {
-    const links = yield that.linksDAL.getSimilarLinksFromDate(from, to, minDate);
-    return links.length ? true : false;
-  });
+  this.existsNonReplayableLink = (from, to) => this.cindexDAL.existsNonReplayableLink(from, to);
 
-  this.getSource = (identifier, noffset) => co(function*() {
-    let src = yield that.sourcesDAL.getSource(identifier, noffset);
+  this.getSource = (identifier, pos) => co(function*() {
+    // TODO: remove for version 1.0
+    let src = yield that.sindexDAL.getSource(identifier, pos);
     // If the source does not exist, we try to check if it exists under another form (issue #735)
     if (!src) {
       let txs = yield that.txsDAL.getTransactionByExtendedHash(identifier);
@@ -403,14 +393,14 @@ function FileDAL(params) {
       }
       if (txs.length && txs[0].version == 3) {
         // Other try: V4
-        src = yield that.sourcesDAL.getSource(txs[0].v4_hash, noffset);
+        src = yield that.sindexDAL.getSource(txs[0].v4_hash, pos);
         if (!src) {
           // Another try: V5
-          src = yield that.sourcesDAL.getSource(txs[0].v5_hash, noffset);
+          src = yield that.sindexDAL.getSource(txs[0].v5_hash, pos);
         }
         if (!src) {
           // Final try: V3 (because identifier maybe be one of [hash, v4_hash, v5_hash]
-          src = yield that.sourcesDAL.getSource(txs[0].hash, noffset);
+          src = yield that.sindexDAL.getSource(txs[0].hash, pos);
         }
       }
     }
@@ -419,185 +409,64 @@ function FileDAL(params) {
 
   this.isMember = (pubkey) => co(function*() {
     try {
-      const idty = yield that.idtyDAL.getFromPubkey(pubkey);
+      const idty = yield that.iindexDAL.getFromPubkey(pubkey);
       return idty.member;
     } catch (err) {
       return false;
     }
   });
 
-  this.isLeaving = (pubkey) => co(function *() {
-    let idty = yield that.idtyDAL.getFromPubkey(pubkey);
-    return idty && idty.leaving || false;
-  });
-
   this.isMemberAndNonLeaver = (pubkey) => co(function*() {
     try {
-      const idty = yield that.idtyDAL.getFromPubkey(pubkey);
-      return (idty && idty.member && !idty.leaving || false);
+      const idty = yield that.iindexDAL.getFromPubkey(pubkey);
+      if (idty && idty.member) {
+        return !(yield that.isLeaving(pubkey));
+      }
+      return false;
     } catch (err) {
       return false;
     }
   });
 
-  this.existsCert = (cert) => that.certDAL.existsGivenCert(cert);
+  this.isLeaving = (pubkey) => co(function*() {
+    const ms = yield that.mindexDAL.getReducedMS(pubkey);
+    return (ms && ms.leaving) || false;
+  });
 
-  this.obsoletesLinks = (minTimestamp) => that.linksDAL.obsoletesLinks(minTimestamp);
+  this.existsCert = (cert) => co(function*() {
+    const existing = yield that.certDAL.existsGivenCert(cert);
+    if (existing) return existing;
+    const existsLink = yield that.cindexDAL.existsNonReplayableLink(cert.from, cert.to);
+    return !!existsLink;
+  });
 
-  this.undoObsoleteLinks = (minTimestamp) => that.linksDAL.unObsoletesLinks(minTimestamp);
+  this.deleteCert = (cert) => that.certDAL.deleteCert(cert);
 
-  this.setConsumedSource = (identifier, noffset) => that.sourcesDAL.consumeSource(identifier, noffset);
+  this.deleteMS = (ms) => that.msDAL.deleteMS(ms);
 
-  this.setKicked = (pubkey, hash, notEnoughLinks) => co(function*() {
-    const kick = notEnoughLinks ? true : false;
-    const idty = yield that.idtyDAL.getFromPubkey(pubkey);
-    if (idty.kick != kick) {
-      idty.kick = kick;
-      return yield that.idtyDAL.saveIdentity(idty);
-    }
+  this.setRevoked = (pubkey) => co(function*() {
+    const idty = yield that.getWrittenIdtyByPubkey(pubkey);
+    idty.revoked = true;
+    return yield that.idtyDAL.saveIdentity(idty);
   });
 
-  this.setRevocating = (hash, revocation_sig) => co(function *() {
-    let idty = yield that.idtyDAL.getByHash(hash);
-    idty.revocation_sig = revocation_sig;
-    return that.idtyDAL.saveIdentity(idty);
-  });
-
-  this.getMembershipExcludingBlock = (current, msValidtyTime) => getCurrentExcludingOrExpiring(
-    current,
-    msValidtyTime,
-    that.indicatorsDAL.getCurrentMembershipExcludingBlock.bind(that.indicatorsDAL),
-    that.indicatorsDAL.writeCurrentExcluding.bind(that.indicatorsDAL)
-  );
-
-  this.getMembershipRevocatingBlock = (current, msValidtyTime) => getCurrentExcludingOrExpiring(
-    current,
-    msValidtyTime,
-    that.indicatorsDAL.getCurrentMembershipRevocatingBlock.bind(that.indicatorsDAL),
-    that.indicatorsDAL.writeCurrentRevocating.bind(that.indicatorsDAL)
-  );
-
-  this.getCertificationExcludingBlock = (current, certValidtyTime) => getCurrentExcludingOrExpiring(
-    current,
-    certValidtyTime,
-    that.indicatorsDAL.getCurrentCertificationExcludingBlock.bind(that.indicatorsDAL),
-    that.indicatorsDAL.writeCurrentExcludingForCert.bind(that.indicatorsDAL)
-  );
-
-  this.getIdentityExpiringBlock = (current, idtyValidtyTime) => getCurrentExcludingOrExpiring(
-    current,
-    idtyValidtyTime,
-    that.indicatorsDAL.getCurrentIdentityExpiringBlock.bind(that.indicatorsDAL),
-    that.indicatorsDAL.writeCurrentExpiringForIdty.bind(that.indicatorsDAL)
-  );
-
-  this.getCertificationExpiringBlock = (current, certWindow) => getCurrentExcludingOrExpiring(
-    current,
-    certWindow,
-    that.indicatorsDAL.getCurrentCertificationExpiringBlock.bind(that.indicatorsDAL),
-    that.indicatorsDAL.writeCurrentExpiringForCert.bind(that.indicatorsDAL)
-  );
-
-  this.getMembershipExpiringBlock = (current, msWindow) => getCurrentExcludingOrExpiring(
-    current,
-    msWindow,
-    that.indicatorsDAL.getCurrentMembershipExpiringBlock.bind(that.indicatorsDAL),
-    that.indicatorsDAL.writeCurrentExpiringForMembership.bind(that.indicatorsDAL)
-  );
-
-  this.nextBlockWithDifferentMedianTime = (block) => that.blockDAL.nextBlockWithDifferentMedianTime(block);
-
-  function getCurrentExcludingOrExpiring(current, delayMax, currentGetter, currentSetter) {
-    return co(function *() {
-      let currentExcluding;
-      if (current.number > 0) {
-        try {
-          currentExcluding = yield currentGetter();
-        } catch (e) {
-          currentExcluding = null;
-        }
-      }
-      if (!currentExcluding) {
-        const root = yield that.getRootBlock();
-        const delaySinceStart = current.medianTime - root.medianTime;
-        if (delaySinceStart > delayMax) {
-          currentExcluding = root;
-        }
-      }
-      if (currentExcluding) {
-        // Check current position
-        const nextBlock = yield that.nextBlockWithDifferentMedianTime(currentExcluding);
-        if (isExcluding(current, currentExcluding, nextBlock, delayMax)) {
-          return currentExcluding;
-        } else {
-          // Have to look for new one
-          const start = currentExcluding.number;
-          let newExcluding;
-          let top = current.number;
-          let bottom = start;
-          // Binary tree search
-          do {
-            let middle = top - bottom;
-            if (middle % 2 != 0) {
-              middle = middle + 1;
-            }
-            middle /= 2;
-            middle += bottom;
-            if (middle == top) {
-              middle--;
-              bottom--; // Helps not being stuck looking at 'top'
-            }
-            const middleBlock = yield that.getBlock(middle);
-            const middleNextB = yield that.getBlock(middle + 1);
-            const delaySinceMiddle = current.medianTime - middleBlock.medianTime;
-            const delaySinceNextB = current.medianTime - middleNextB.medianTime;
-            const isValidPeriod = delaySinceMiddle <= delayMax;
-            const isValidPeriodB = delaySinceNextB <= delayMax;
-            const isExcludin = !isValidPeriod && isValidPeriodB;
-            //console.log('CRT: Search between %s and %s: %s => %s,%s', bottom, top, middle, isValidPeriod ? 'DOWN' : 'UP', isValidPeriodB ? 'DOWN' : 'UP');
-            if (isExcludin) {
-              // Found
-              yield currentSetter(middleBlock);
-              newExcluding = middleBlock;
-            }
-            else if (isValidPeriod) {
-              // Look down in the blockchain
-              top = middle;
-            }
-            else {
-              // Look up in the blockchain
-              bottom = middle;
-            }
-          } while (!newExcluding);
-          return newExcluding;
-        }
-      }
-    });
-  }
-
-  /**
-   * Checks if `excluding` is still an excluding block, and its follower `nextBlock` is not, in reference to `current`.
-   * @param current HEAD of the blockchain.
-   * @param excluding The block we test if it is still excluding.
-   * @param nextBlock The block that might be the new excluding block.
-   * @param maxWindow The time window for exclusion.
-   * @returns {boolean}
-   */
-  const isExcluding = (current, excluding, nextBlock, maxWindow) => {
-    const delayFromExcludingToHead = current.medianTime - excluding.medianTime;
-    const delayFromNextToHead = current.medianTime - nextBlock.medianTime;
-    const isValidPeriod = delayFromExcludingToHead <= maxWindow;
-    const isValidPeriodB = delayFromNextToHead <= maxWindow;
-    return !isValidPeriod && isValidPeriodB;
-  };
-
-  this.flagExpiredIdentities = (maxNumber, onNumber) => this.idtyDAL.flagExpiredIdentities(maxNumber, onNumber);
-  this.flagExpiredCertifications = (maxNumber, onNumber) => this.certDAL.flagExpiredCertifications(maxNumber, onNumber);
-  this.flagExpiredMemberships = (maxNumber, onNumber) => this.msDAL.flagExpiredMemberships(maxNumber, onNumber);
-  this.kickWithOutdatedMemberships = (maxNumber) => this.idtyDAL.kickMembersForMembershipBelow(maxNumber);
-  this.revokeWithOutdatedMemberships = (maxNumber) => this.idtyDAL.revokeMembersForMembershipBelow(maxNumber);
+  this.setRevocating = (existing, revocation_sig) => co(function *() {
+    existing.revocation_sig = revocation_sig;
+    existing.revoked = false;
+    return that.idtyDAL.saveIdentity(existing);
+  });
 
-  this.getPeerOrNull = (pubkey) => nullIfError(that.getPeer(pubkey));
+  this.getPeerOrNull = (pubkey) => co(function*() {
+    let peer = null;
+    try {
+      peer = yield that.getPeer(pubkey);
+    } catch (e) {
+      if (e != constants.ERROR.BLOCK.NO_CURRENT_BLOCK) {
+        throw e;
+      }
+    }
+    return peer;
+  });
 
   this.findAllPeersNEWUPBut = (pubkeys) => co(function*() {
     const peers = yield that.listAllPeers();
@@ -667,23 +536,53 @@ function FileDAL(params) {
     block.wrong = false;
     yield [
       that.saveBlockInFile(block, true),
-      that.saveTxsInFiles(block.transactions, {block_number: block.number, time: block.medianTime }),
-      that.saveMemberships('join', block.joiners, block.number),
-      that.saveMemberships('active', block.actives, block.number),
-      that.saveMemberships('leave', block.leavers, block.number)
+      that.saveTxsInFiles(block.transactions, {block_number: block.number, time: block.medianTime, currency: block.currency })
     ];
   });
 
-  this.saveMemberships = (type, mss, blockNumber) => {
-    const msType = type == 'leave' ? 'out' : 'in';
-    return mss.reduce((p, msRaw) => p.then(() => {
-      const ms = Membership.statics.fromInline(msRaw, type == 'leave' ? 'OUT' : 'IN', that.getCurrency());
-      ms.type = type;
-      ms.hash = String(hashf(ms.getRawSigned())).toUpperCase();
-      ms.idtyHash = (hashf(ms.userid + ms.certts + ms.issuer) + "").toUpperCase();
-      return that.msDAL.saveOfficialMS(msType, ms, blockNumber);
-    }), Q());
-  };
+  this.generateIndexes = (block, conf) => co(function*() {
+    const index = indexer.localIndex(block, conf);
+    let mindex = indexer.mindex(index);
+    let iindex = indexer.iindex(index);
+    let sindex = indexer.sindex(index);
+    let cindex = indexer.cindex(index);
+    const HEAD = yield indexer.completeGlobalScope(block, conf, index, that);
+    sindex = sindex.concat(yield indexer.ruleIndexGenDividend(HEAD, that));
+    cindex = cindex.concat(yield indexer.ruleIndexGenCertificationExpiry(HEAD, that));
+    mindex = mindex.concat(yield indexer.ruleIndexGenMembershipExpiry(HEAD, that));
+    iindex = iindex.concat(yield indexer.ruleIndexGenExclusionByMembership(HEAD, mindex));
+    iindex = iindex.concat(yield indexer.ruleIndexGenExclusionByCertificatons(HEAD, cindex, conf, that));
+    mindex = mindex.concat(yield indexer.ruleIndexGenImplicitRevocation(HEAD, that));
+    yield indexer.ruleIndexCorrectMembershipExpiryDate(HEAD, mindex, that);
+    yield indexer.ruleIndexCorrectCertificationExpiryDate(HEAD, cindex, that);
+    return { HEAD, mindex, iindex, sindex, cindex };
+  });
+
+  this.updateWotbLinks = (cindex) => co(function*() {
+    for (const entry of cindex) {
+      const from = yield that.getWrittenIdtyByPubkey(entry.issuer);
+      const to = yield that.getWrittenIdtyByPubkey(entry.receiver);
+      if (entry.op == constants.IDX_CREATE) {
+        that.wotb.addLink(from.wotb_id, to.wotb_id, true);
+      } else {
+        // Update = removal
+        that.wotb.removeLink(from.wotb_id, to.wotb_id, true);
+      }
+    }
+  });
+
+  this.trimIndexes = (block, conf) => co(function*() {
+    // TODO: trim should be done on a fork window size
+    // yield that.cindexDAL.trimExpiredCerts();
+    return true;
+  });
+
+  this.trimSandboxes = (block, conf) => co(function*() {
+    yield that.certDAL.trimExpiredCerts(block.medianTime);
+    yield that.msDAL.trimExpiredMemberships(block.medianTime);
+    yield that.idtyDAL.trimExpiredIdentities(block.medianTime);
+    return true;
+  });
 
   this.savePendingMembership = (ms) => that.msDAL.savePendingMembership(ms);
 
@@ -696,7 +595,6 @@ function FileDAL(params) {
   this.saveTxsInFiles = (txs, extraProps) => {
     return Q.all(txs.map((tx) => co(function*() {
       _.extend(tx, extraProps);
-      _.extend(tx, {currency: that.getCurrency()});
       if (tx.version >= 3) {
         const sp = tx.blockstamp.split('-');
         tx.blockstampTime = (yield that.getBlockByNumberAndHash(sp[0], sp[1])).medianTime;
@@ -715,56 +613,13 @@ function FileDAL(params) {
     return merkle;
   });
 
-  this.removeLink = (link) => that.linksDAL.removeLink(link);
-
-  this.removeAllSourcesOfBlock = (number) => that.sourcesDAL.removeAllSourcesOfBlock(number);
-
-  this.unConsumeSource = (identifier, noffset) => that.sourcesDAL.unConsumeSource(identifier, noffset);
-
-  this.unflagExpiredIdentitiesOf = (number) => that.idtyDAL.unflagExpiredIdentitiesOf(number);
-  
-  this.unflagExpiredCertificationsOf = (number) => that.certDAL.unflagExpiredCertificationsOf(number);
-  
-  this.unflagExpiredMembershipsOf = (number) => that.msDAL.unflagExpiredMembershipsOf(number);
-
-  this.saveSource = (src) => that.sourcesDAL.addSource(src.type, src.number, src.identifier, src.noffset,
-      src.amount, src.base, src.block_hash, src.time, src.conditions);
-
-  this.updateSources = (sources) => that.sourcesDAL.updateBatchOfSources(sources);
-
-  this.updateCertifications = (certs) => that.certDAL.updateBatchOfCertifications(certs);
-
-  this.updateMemberships = (certs) => that.msDAL.updateBatchOfMemberships(certs);
-
-  this.updateLinks = (certs) => that.linksDAL.updateBatchOfLinks(certs);
+  this.removeAllSourcesOfBlock = (blockstamp) => that.sindexDAL.removeBlock(blockstamp);
 
   this.updateTransactions = (txs) => that.txsDAL.insertBatchOfTxs(txs);
 
-  this.officializeCertification = (cert) => that.certDAL.saveOfficial(cert);
-
-  this.saveCert = (cert) =>
-      // TODO: create a specific method with a different name and hide saveCert()
-      that.certDAL.saveCert(cert);
-
-  this.savePendingIdentity = (idty) =>
-      // TODO: create a specific method with a different name and hide saveIdentity()
-      that.idtyDAL.saveIdentity(idty);
-
-  this.revokeIdentity = (pubkey, number) => that.idtyDAL.revokeIdentity(pubkey, number);
+  this.savePendingIdentity = (idty) => that.idtyDAL.saveIdentity(idty);
 
-  this.unrevokeIdentity = (pubkey) => that.idtyDAL.unrevokeIdentity(pubkey);
-
-  this.excludeIdentity = (pubkey) => that.idtyDAL.excludeIdentity(pubkey);
-
-  this.newIdentity = (idty) => co(function *() {
-    return that.idtyDAL.newIdentity(idty);
-  });
-
-  this.joinIdentity = (pubkey, number) => that.idtyDAL.joinIdentity(pubkey, number);
-
-  this.activeIdentity = (pubkey, number) => that.idtyDAL.activeIdentity(pubkey, number);
-
-  this.leaveIdentity = (pubkey, number) => that.idtyDAL.leaveIdentity(pubkey, number);
+  this.revokeIdentity = (pubkey) => that.idtyDAL.revokeIdentity(pubkey);
 
   this.removeUnWrittenWithPubkey = (pubkey) => co(function*() {
     return yield that.idtyDAL.removeUnWrittenWithPubkey(pubkey)
@@ -774,42 +629,6 @@ function FileDAL(params) {
     return yield that.idtyDAL.removeUnWrittenWithUID(pubkey);
   });
 
-  this.unacceptIdentity = that.idtyDAL.unacceptIdentity;
-
-  this.getPreviousMembershipsInfos = (ms) => co(function*() {
-    const previousMS = yield that.msDAL.previousMS(ms.issuer, ms.number);
-    let previousIN = previousMS;
-    if (previousMS.membership !== 'IN') {
-      previousIN = yield that.msDAL.previousIN(ms.issuer, ms.number);
-    }
-    return {
-      previousIN: previousIN,
-      previousMS: previousMS
-    };
-  });
-
-  this.unJoinIdentity = (ms) => co(function *() {
-    const previousMSS = yield that.getPreviousMembershipsInfos(ms);
-    yield that.idtyDAL.unJoinIdentity(ms, previousMSS.previousMS, previousMSS.previousIN);
-    yield that.msDAL.unwriteMS(ms);
-  });
-
-  this.unRenewIdentity = (ms) => co(function *() {
-    const previousMSS = yield that.getPreviousMembershipsInfos(ms);
-    yield that.idtyDAL.unRenewIdentity(ms, previousMSS.previousMS, previousMSS.previousIN);
-    yield that.msDAL.unwriteMS(ms);
-  });
-
-  this.unLeaveIdentity = (ms) => co(function *() {
-    const previousMSS = yield that.getPreviousMembershipsInfos(ms);
-    yield that.idtyDAL.unLeaveIdentity(ms, previousMSS.previousMS, previousMSS.previousIN);
-    yield that.msDAL.unwriteMS(ms);
-  });
-
-  this.unFlagToBeKicked = that.idtyDAL.unFlagToBeKicked.bind(that.idtyDAL);
-
-  this.unExcludeIdentity = that.idtyDAL.unExcludeIdentity;
-
   this.registerNewCertification = (cert) => that.certDAL.saveNewCertification(cert);
 
   this.saveTransaction = (tx) => that.txsDAL.addPending(tx);
@@ -835,7 +654,7 @@ function FileDAL(params) {
   });
 
   this.getUDHistory = (pubkey) => co(function *() {
-    const sources = yield that.sourcesDAL.getUDSources(pubkey);
+    const sources = yield that.sindexDAL.getUDSources(pubkey);
     return {
       history: sources.map((src) => _.extend({
         block_number: src.number
@@ -853,6 +672,28 @@ function FileDAL(params) {
     return _.chain(blocks).pluck('issuer').uniq().value();
   });
 
+  /**
+   * Gets a range of entries for the last `start`th to the last `end`th HEAD entry.
+   * @param start The starting entry number (min. 1)
+   * @param end The ending entry (max. BINDEX length)
+   * @param property If provided, transforms the range of entries into an array of the asked property.
+   */
+  this.range = (start, end, property) => co(function*() {
+    const range = yield that.bindexDAL.range(start, end);
+    if (property) {
+      // Filter on a particular property
+      return range.map((b) => b[property]);
+    } else {
+      return range;
+    }
+  });
+
+  /**
+   * Get the last `n`th entry from the BINDEX.
+   * @param n The entry number (min. 1).
+   */
+  this.head = (n) => this.bindexDAL.head(n);
+
   /***********************
    *    CONFIGURATION
    **********************/
diff --git a/app/lib/dal/fileDALs/IndicatorsDAL.js b/app/lib/dal/fileDALs/IndicatorsDAL.js
index 433011f1e60ca66fc38430a84673c519151feab8..64b2a93282bb10ad8b18a76d76b5ea5c5be8d17a 100644
--- a/app/lib/dal/fileDALs/IndicatorsDAL.js
+++ b/app/lib/dal/fileDALs/IndicatorsDAL.js
@@ -23,38 +23,5 @@ function IndicatorsDAL(rootPath, qioFS, parentCore, localDAL, AbstractStorage) {
     });
   };
 
-  const cache = {};
 
-  function setBlock(key, block) {
-    cache[key] = block;
-    return Promise.resolve(block);
-  }
-
-  function getBlock(key) {
-    return Promise.resolve(cache[key] || null);
-  }
-
-  this.writeCurrentExcluding = (excluding) => setBlock('excludingMS', excluding);
-
-  this.writeCurrentRevocating = (revocating) => setBlock('revocatingMS', revocating);
-
-  this.writeCurrentExcludingForCert = (excluding) => setBlock('excludingCRT', excluding);
-
-  this.writeCurrentExpiringForCert = (excluding) => setBlock('expiringCRT', excluding);
-
-  this.writeCurrentExpiringForIdty = (excluding) => setBlock('expiringIDTY', excluding);
-
-  this.writeCurrentExpiringForMembership = (excluding) => setBlock('expiringMS', excluding);
-
-  this.getCurrentMembershipExcludingBlock = () => getBlock('excludingMS');
-
-  this.getCurrentMembershipRevocatingBlock = () => getBlock('revocatingMS');
-
-  this.getCurrentCertificationExpiringBlock = () => getBlock('expiringCRT');
-
-  this.getCurrentCertificationExcludingBlock = () => getBlock('excludingCRT');
-
-  this.getCurrentIdentityExpiringBlock = () => getBlock('expiringIDTY');
-
-  this.getCurrentMembershipExpiringBlock = () => getBlock('expiringMS');
 }
diff --git a/app/lib/dal/sqliteDAL/AbstractSQLite.js b/app/lib/dal/sqliteDAL/AbstractSQLite.js
index 22580329cb8ab1016dd2fc34cba055a3458355e5..cf2ea2f65ec2ae4af071861f65d9fffe35ee15c7 100644
--- a/app/lib/dal/sqliteDAL/AbstractSQLite.js
+++ b/app/lib/dal/sqliteDAL/AbstractSQLite.js
@@ -2,7 +2,6 @@
  * Created by cgeek on 22/08/15.
  */
 
-const Q = require('q');
 const _ = require('underscore');
 const co = require('co');
 const colors = require('colors');
@@ -73,16 +72,6 @@ function AbstractSQLite(driver) {
     return that.query('SELECT * FROM ' + that.table + ' WHERE ' + keys.map((k) => '`' + k + '` like ?').join(' or '), keys.map((k) => obj[k].toUpperCase()), sort);
   });
 
-  this.sqlUpdateWhere = (obj, where) => co(function *() {
-    // Valorizations
-    const setInstructions = toSetArray(obj).join(', ');
-    const setValues = toParams(obj);
-    // Conditions
-    const conditions = toConditionsArray(where).join(' AND ');
-    const condValues = toParams(where);
-    return that.query('UPDATE ' + that.table + ' SET ' + setInstructions + ' WHERE ' + conditions, setValues.concat(condValues));
-  });
-
   this.sqlRemoveWhere = (obj) => co(function *() {
     const keys = _.keys(obj);
     return that.query('DELETE FROM ' + that.table + ' WHERE ' + keys.map((k) => '`' + k + '` = ?').join(' and '), keys.map((k) => obj[k]));
@@ -107,17 +96,16 @@ function AbstractSQLite(driver) {
     yield that.insert(toSave);
   });
 
-  this.updateEntity = (entity, values) => co(function *() {
-    const toSave = toRow(entity);
-    if (that.beforeSaveHook) {
-      that.beforeSaveHook(toSave);
-    }
-    const valuesKeys = _.keys(values);
-    const valorizations = valuesKeys.map((field) => '`' + field + '` = ?').join(', ');
+  this.insert = (entity) => co(function *() {
+    const row = toRow(entity);
+    const values = that.fields.map((f) => row[f]);
+    yield that.query(that.getInsertQuery(), values);
+  });
+
+  this.getEntity = (entity) => co(function *() {
     const conditions = getPKFields().map((field) => '`' + field + '` = ?').join(' and ');
-    const setValues = valuesKeys.map((field) => values[field]);
-    const condValues = getPKFields().map((k) => toSave[k]);
-    return that.query('UPDATE ' + that.table + ' SET ' + valorizations + ' WHERE ' + conditions, setValues.concat(condValues));
+    const params = toParams(entity, getPKFields());
+    return (yield that.query('SELECT * FROM ' + that.table + ' WHERE ' + conditions, params))[0];
   });
 
   this.deleteEntity = (entity) => co(function *() {
@@ -130,18 +118,6 @@ function AbstractSQLite(driver) {
     return that.query('DELETE FROM ' + that.table + ' WHERE ' + conditions, condValues);
   });
 
-  this.insert = (entity) => co(function *() {
-    const row = toRow(entity);
-    const values = that.fields.map((f) => row[f]);
-    yield that.query(that.getInsertQuery(), values);
-  });
-
-  this.getEntity = (entity) => co(function *() {
-    const conditions = getPKFields().map((field) => '`' + field + '` = ?').join(' and ');
-    const params = toParams(entity, getPKFields());
-    return (yield that.query('SELECT * FROM ' + that.table + ' WHERE ' + conditions, params))[0];
-  });
-
   this.exec = (sql) => co(function *() {
     try {
       // logger.trace(sql);
@@ -170,38 +146,6 @@ function AbstractSQLite(driver) {
     return "(" + values.join(',') + ")";
   };
 
-  this.getUpdateRawQuery = (toSave, values) => {
-    if (that.beforeSaveHook) {
-      that.beforeSaveHook(toSave);
-    }
-    const valuesKeys = _.keys(values);
-    const valorizations = valuesKeys.map((field) => '`' + field + '` = ' + escapeToSQLite(values[field])).join(', ');
-    const conditions = getPKFields().map((field) => '`' + field + '` = ' + escapeToSQLite(toSave[field])).join(' and ');
-    return 'UPDATE ' + that.table + ' SET ' + valorizations + ' WHERE ' + conditions + ';';
-  };
-
-  this.getDeleteRawQuery = (toSave) => {
-    if (that.beforeSaveHook) {
-      that.beforeSaveHook(toSave);
-    }
-    const conditions = getPKFields().map((field) => '`' + field + '` = ' + escapeToSQLite(toSave[field])).join(' and ');
-    return 'DELETE FROM ' + that.table + ' WHERE ' + conditions + ';';
-  };
-
-  this.getConsumeHead = () => {
-    return 'UPDATE ' + that.table + " SET consumed = 1 WHERE ";
-  };
-
-  this.getConsumeValues = (entities) => {
-    return entities.map((toSave) => {
-      if (that.beforeSaveHook) {
-        that.beforeSaveHook(toSave);
-      }
-      const conditions = getPKFields().map((field) => '`' + field + '` = ' + escapeToSQLite(toSave[field])).join(' and ');
-      return "(" + conditions + ")";
-    }).join(' OR\n ');
-  };
-
   this.toInsertValues = (entity) => {
     const row = toRow(entity);
     const values = that.fields.map((f) => row[f]);
@@ -209,6 +153,22 @@ function AbstractSQLite(driver) {
     return "(" + formatted.join(',') + ")";
   };
 
+  /**
+   * Make a batch insert.
+   * @param records The records to insert as a batch.
+   */
+  this.insertBatch = (records) => co(function *() {
+    const queries = [];
+    if (records.length) {
+      const insert = that.getInsertHead();
+      const values = records.map((src) => that.getInsertValue(src));
+      queries.push(insert + '\n' + values.join(',\n') + ';');
+    }
+    if (queries.length) {
+      return that.exec(queries.join('\n'));
+    }
+  });
+
   function toConditionsArray(obj) {
     return _.keys(obj).map((k) => {
       if (obj[k].$lte !== undefined) {
@@ -216,7 +176,7 @@ function AbstractSQLite(driver) {
       } else if (obj[k].$gte !== undefined) {
         return '`' + k + '` >= ?';
       } else if (obj[k].$gt !== undefined) {
-        return '`' + k + '` >= ?';
+        return '`' + k + '` > ?';
       }  else if (obj[k].$lt !== undefined) {
         return '`' + k + '` < ?';
       }  else if (obj[k].$null !== undefined) {
@@ -229,11 +189,6 @@ function AbstractSQLite(driver) {
     });
   }
 
-  const toSetArray= (obj) => {
-    const row = toRow(obj);
-    return _.keys(row).map((k) => '`' + k + '` = ?');
-  };
-
   const toParams = (obj, fields) => {
     let params = [];
     (fields || _.keys(obj)).forEach((f) => {
@@ -259,7 +214,11 @@ function AbstractSQLite(driver) {
   const escapeToSQLite = (val) => {
     if (typeof val == "boolean") {
       // SQLite specific: true => 1, false => 0
-      return val ? 1 : 0;
+      if (val !== null && val !== undefined) {
+        return val ? 1 : 0;
+      } else {
+        return null;
+      }
     }
     else if (typeof val == "string") {
       return "'" + val.replace(/'/g, "\\'") + "'";
@@ -295,7 +254,7 @@ function AbstractSQLite(driver) {
     }
     // Booleans
     for (const f of that.booleans) {
-      row[f] = Boolean(row[f]);
+      row[f] = row[f] !== null ? Boolean(row[f]) : null;
     }
     // Transient
     for (const f of (that.transientFields || [])) {
@@ -324,5 +283,5 @@ function AbstractSQLite(driver) {
       row[toTranslate[objField]] = row[objField];
     }
     return row;
-  };
+  }
 }
\ No newline at end of file
diff --git a/app/lib/dal/sqliteDAL/BlockDAL.js b/app/lib/dal/sqliteDAL/BlockDAL.js
index f00f368eb9616976f7a991a7307717d0c80526ca..647d6bc59a1fb3c044204ddb4ca4de5872e042c9 100644
--- a/app/lib/dal/sqliteDAL/BlockDAL.js
+++ b/app/lib/dal/sqliteDAL/BlockDAL.js
@@ -108,15 +108,6 @@ function BlockDAL(driver) {
     return res[0].quantity;
   });
 
-  this.getNbIssuedFrom = (issuer, from) => co(function *() {
-    let res = yield that.query('SELECT COUNT(*) as quantity FROM block WHERE issuer = ? and number >= ? and NOT fork', [issuer, from]);
-    return res[0].quantity;
-  });
-
-  this.getMoreRecentBlockWithTimeEqualBelow = (maxTime) => co(function *() {
-    return (yield that.query('SELECT * FROM block WHERE medianTime <= ? and NOT fork ORDER BY number DESC LIMIT 1', [maxTime]))[0];
-  });
-
   this.getForkBlocks = () => {
     return that.query('SELECT * FROM block WHERE fork ORDER BY number');
   };
@@ -125,10 +116,6 @@ function BlockDAL(driver) {
     return that.query('SELECT * FROM block WHERE dividend IS NOT NULL ORDER BY number');
   };
 
-  this.nextBlockWithDifferentMedianTime = (block) => co(function *() {
-    return (yield that.query('SELECT * FROM block WHERE number > ? AND medianTime > ? and NOT fork ORDER BY number ASC LIMIT 1', [block.number, block.medianTime]))[0];
-  });
-
   this.saveBunch = (blocks) => co(function *() {
     let queries = "INSERT INTO block (" + that.fields.join(',') + ") VALUES ";
     for (let i = 0, len = blocks.length; i < len; i++) {
diff --git a/app/lib/dal/sqliteDAL/CertDAL.js b/app/lib/dal/sqliteDAL/CertDAL.js
index f38cb8d06ca9cb2a524247c52d441bca074c6388..195168476162e7001f00c60bc0e05582e53b372d 100644
--- a/app/lib/dal/sqliteDAL/CertDAL.js
+++ b/app/lib/dal/sqliteDAL/CertDAL.js
@@ -31,7 +31,8 @@ function CertDAL(driver) {
     'to',
     'from',
     'block',
-    'expired'
+    'expired',
+    'expires_on'
   ];
   this.arrays = [];
   this.booleans = ['linked', 'written'];
@@ -52,6 +53,7 @@ function CertDAL(driver) {
       'written BOOLEAN NOT NULL,' +
       'written_block INTEGER,' +
       'written_hash VARCHAR(64),' +
+      'expires_on INTEGER NULL,' +
       'PRIMARY KEY (`from`, target, sig, written_block)' +
       ');' +
       'CREATE INDEX IF NOT EXISTS idx_cert_from ON cert (`from`);' +
@@ -68,7 +70,7 @@ function CertDAL(driver) {
     target: hash
   });
 
-  this.getFromPubkey = (pubkey) => this.sqlFind({
+  this.getFromPubkeyCerts = (pubkey) => this.sqlFind({
     from: pubkey
   });
 
@@ -81,43 +83,13 @@ function CertDAL(driver) {
     linked: false
   });
 
-  this.listLocalPending = () => Q([]);
-
-  this.saveOfficial = (cert) => {
-    cert.linked = true;
-    return this.saveEntity(cert);
-  };
-
-  this.saveCert = (cert) => this.saveEntity(cert);
-
   this.saveNewCertification = (cert) => this.saveEntity(cert);
 
   this.existsGivenCert = (cert) => Q(this.sqlExisting(cert));
 
-  this.updateBatchOfCertifications = (certs) => co(function *() {
-    const queries = [];
-    const insert = that.getInsertHead();
-    const values = certs.map((cert) => that.getInsertValue(cert));
-    if (certs.length) {
-      queries.push(insert + '\n' + values.join(',\n') + ';');
-    }
-    if (queries.length) {
-      return that.exec(queries.join('\n'));
-    }
-  });
-
-  this.flagExpiredCertifications = (maxNumber, onNumber) => co(function *() {
-    yield that.exec('UPDATE ' + that.table + ' ' +
-      'SET expired = ' + onNumber + ' ' +
-      'WHERE expired IS NULL ' +
-      'AND block_number <= ' + maxNumber);
-  });
+  this.deleteCert = (cert) => this.deleteEntity(cert);
 
-  this.unflagExpiredCertificationsOf = (onNumber) => co(function *() {
-    yield that.exec('UPDATE ' + that.table + ' ' +
-      'SET expired = NULL ' +
-      'WHERE expired = ' + onNumber);
-  });
+  this.trimExpiredCerts = (medianTime) => this.exec('DELETE FROM ' + this.table + ' WHERE expires_on IS NULL OR expires_on < ' + medianTime);
 
   /**************************
    * SANDBOX STUFF
diff --git a/app/lib/dal/sqliteDAL/IdentityDAL.js b/app/lib/dal/sqliteDAL/IdentityDAL.js
index 5fc298fda9decf41ef51338b4f9c9c66eb7351fa..8befe1751ad1aa259ae46e77d669ee6f1338b9c0 100644
--- a/app/lib/dal/sqliteDAL/IdentityDAL.js
+++ b/app/lib/dal/sqliteDAL/IdentityDAL.js
@@ -11,7 +11,7 @@ const SandBox = require('./SandBox');
 
 module.exports = IdentityDAL;
 
-function IdentityDAL(driver, wotb) {
+function IdentityDAL(driver) {
 
   "use strict";
 
@@ -37,7 +37,8 @@ function IdentityDAL(driver, wotb) {
     'hash',
     'written',
     'wotb_id',
-    'expired'
+    'expired',
+    'expires_on'
   ];
   this.arrays = [];
   this.booleans = ['revoked', 'member', 'kick', 'leaving', 'wasMember', 'written'];
@@ -49,20 +50,21 @@ function IdentityDAL(driver, wotb) {
     return that.exec('BEGIN;' +
       'CREATE TABLE IF NOT EXISTS ' + that.table + ' (' +
       'revoked BOOLEAN NOT NULL,' +
-      'currentMSN INTEGER NOT NULL,' +
-      'currentINN INTEGER NOT NULL,' +
+      'currentMSN INTEGER NULL,' +
+      'currentINN INTEGER NULL,' +
       'buid VARCHAR(100) NOT NULL,' +
       'member BOOLEAN NOT NULL,' +
       'kick BOOLEAN NOT NULL,' +
-      'leaving BOOLEAN NOT NULL,' +
+      'leaving BOOLEAN NULL,' +
       'wasMember BOOLEAN NOT NULL,' +
       'pubkey VARCHAR(50) NOT NULL,' +
       'uid VARCHAR(255) NOT NULL,' +
       'sig VARCHAR(100) NOT NULL,' +
       'revocation_sig VARCHAR(100) NULL,' +
       'hash VARCHAR(64) NOT NULL,' +
-      'written BOOLEAN NOT NULL,' +
+      'written BOOLEAN NULL,' +
       'wotb_id INTEGER NULL,' +
+      'expires_on INTEGER NULL,' +
       'PRIMARY KEY (pubkey,uid,hash)' +
       ');' +
       'CREATE INDEX IF NOT EXISTS idx_idty_pubkey ON idty (pubkey);' +
@@ -77,131 +79,7 @@ function IdentityDAL(driver, wotb) {
       'COMMIT;', []);
   });
 
-  this.revokeIdentity = (pubkey, number) => {
-    return co(function *() {
-      const idty = yield that.getFromPubkey(pubkey);
-      idty.revoked = true;
-      idty.revoked_on = number;
-      return that.saveIdentity(idty);
-    });
-  };
-
-  this.unrevokeIdentity = (pubkey) => co(function *() {
-    const idty = yield that.getFromPubkey(pubkey);
-    idty.revoked = false;
-    idty.revoked_on = null;
-    return that.saveIdentity(idty);
-  });
-
-  this.excludeIdentity = (pubkey) => co(function *() {
-    const idty = yield that.getFromPubkey(pubkey);
-    idty.member = false;
-    idty.kick = false;
-    wotb.setEnabled(false, idty.wotb_id);
-    return that.saveIdentity(idty);
-  });
-
-  this.unacceptIdentity = (pubkey) => co(function *() {
-    const idty = yield that.getFromPubkey(pubkey);
-    idty.currentMSN = -1;
-    idty.currentINN = -1;
-    idty.written = false;
-    idty.wasMember = false;
-    idty.member = false;
-    idty.kick = false;
-    idty.leaving = false;
-    idty.wotb_id = wotb.removeNode();
-    return that.saveIdentity(idty);
-  });
-
-  this.unJoinIdentity = (ms, previousMS, previousIN) => co(function *() {
-    const idty = yield that.getFromPubkey(ms.issuer);
-    idty.currentMSN = previousMS.number;
-    idty.currentINN = previousIN.number;
-    if (previousMS.membership === 'OUT') {
-      idty.leaving = true;
-    }
-    idty.member = false;
-    /**
-     * Note: it is not required to do:
-     *
-     *     `idty.wasMember = false;`
-     *
-     * because this is already done by `unacceptIdentity` method.
-     */
-    wotb.setEnabled(false, idty.wotb_id);
-    return that.saveIdentity(idty);
-  });
-
-  this.unRenewIdentity = (ms, previousMS, previousIN) => co(function *() {
-    const idty = yield that.getFromPubkey(ms.issuer);
-    idty.currentMSN = previousMS.number;
-    idty.currentINN = previousIN.number;
-    if (previousMS.membership === 'OUT') {
-      idty.leaving = true;
-    }
-    return that.saveIdentity(idty);
-  });
-
-  this.unLeaveIdentity = (ms, previousMS, previousIN) => co(function *() {
-    const idty = yield that.getFromPubkey(ms.issuer);
-    idty.currentMSN = previousMS.number;
-    idty.currentINN = previousIN.number;
-    idty.leaving = false;
-    if (previousMS.membership === 'OUT') {
-      idty.leaving = true;
-    }
-    return that.saveIdentity(idty);
-  });
-
-  this.unExcludeIdentity = (pubkey, causeWasRevocation) => co(function *() {
-    const idty = yield that.getFromPubkey(pubkey);
-    idty.member = true;
-    idty.kick = !causeWasRevocation;
-    wotb.setEnabled(true, idty.wotb_id);
-    return that.saveIdentity(idty);
-  });
-
-  this.newIdentity = function(idty) {
-    idty.currentMSN = -1; // Will be overidden by joinIdentity()
-    idty.currentINN = -1; // Will be overidden by joinIdentity()
-    idty.member = true;
-    idty.wasMember = true;
-    idty.kick = false;
-    idty.written = true;
-    idty.wotb_id = wotb.addNode();
-    logger.trace('%s was affected wotb_id %s', idty.uid, idty.wotb_id);
-    return that.saveIdentity(idty);
-  };
-
-  this.joinIdentity = (pubkey, currentMSN) =>  co(function *() {
-    const idty = yield that.getFromPubkey(pubkey);
-    idty.currentMSN = currentMSN;
-    idty.currentINN = currentMSN;
-    idty.member = true;
-    idty.wasMember = true;
-    idty.leaving = false;
-    wotb.setEnabled(true, idty.wotb_id);
-    return that.saveIdentity(idty);
-  });
-
-  this.activeIdentity = (pubkey, currentMSN) => co(function *() {
-    const idty = yield that.getFromPubkey(pubkey);
-    idty.currentMSN = currentMSN;
-    idty.currentINN = currentMSN;
-    idty.member = true;
-    idty.kick = false;
-    idty.leaving = false;
-    wotb.setEnabled(true, idty.wotb_id);
-    return that.saveIdentity(idty);
-  });
-
-  this.leaveIdentity = (pubkey, currentMSN) => co(function *() {
-    const idty = yield that.getFromPubkey(pubkey);
-    idty.currentMSN = currentMSN;
-    idty.leaving = true;
-    return that.saveIdentity(idty);
-  });
+  this.revokeIdentity = (pubkey) => this.exec('DELETE FROM ' + this.table + ' WHERE pubkey = \'' + pubkey + '\'');
 
   this.removeUnWrittenWithPubkey = (pubkey) => this.sqlRemoveWhere({
     pubkey: pubkey,
@@ -213,33 +91,13 @@ function IdentityDAL(driver, wotb) {
     written: false
   });
 
-  this.getFromPubkey = (pubkey) => this.sqlFindOne({
-    pubkey: pubkey,
-    wasMember: true
-  });
-
-  this.getFromUID = (uid) => this.sqlFindOne({
-    uid: uid,
-    wasMember: true
-  });
-
   this.getByHash = (hash) => this.sqlFindOne({
     hash: hash
   });
 
-  this.getLatestMember = () => that.sqlFindOne({
-    wasMember: true
-  }, {
-    wotb_id: this.DESC
-  });
-
   this.saveIdentity = (idty) =>
     this.saveEntity(idty);
 
-  this.getWhoIsOrWasMember = () => that.sqlFind({
-    wasMember: true
-  });
-
   this.getToRevoke = () => that.sqlFind({
     revocation_sig: { $null: false },
     revoked: false,
@@ -247,56 +105,16 @@ function IdentityDAL(driver, wotb) {
   });
 
   this.getPendingIdentities = () => that.sqlFind({
-    wasMember: false
+    revocation_sig: { $null: false },
+    revoked: false
   });
 
-  this.listLocalPending = () => Q([]);
-
   this.searchThoseMatching = (search) => that.sqlFindLikeAny({
     pubkey: "%" + search + "%",
     uid: "%" + search + "%"
   });
 
-  this.flagExpiredIdentities = (maxNumber, onNumber) => co(function *() {
-    yield that.exec('UPDATE ' + that.table + ' ' +
-      'SET expired = ' + onNumber + ' ' +
-      'WHERE expired IS NULL ' +
-      'AND CAST(SUBSTR(buid, 0, INSTR(buid, "-")) as number) <= ' + maxNumber);
-  });
-
-  this.unflagExpiredIdentitiesOf = (onNumber) => co(function *() {
-    yield that.exec('UPDATE ' + that.table + ' ' +
-      'SET expired = NULL ' +
-      'WHERE expired = ' + onNumber);
-  });
-
-  this.unFlagToBeKicked = () => that.exec('UPDATE ' + that.table + ' SET kick = 0 WHERE kick');
-
-  this.kickMembersForMembershipBelow = (maxNumber) => co(function *() {
-    const toKick = yield that.sqlFind({
-      currentINN: { $lte: maxNumber },
-      kick: false,
-      member: true
-    });
-    for (const idty of toKick) {
-      logger.trace('Kick %s for currentINN <= %s', idty.uid, maxNumber);
-      idty.kick = true;
-      yield that.saveEntity(idty);
-    }
-  });
-
-  this.revokeMembersForMembershipBelow = (maxNumber) => co(function *() {
-    const toKick = yield that.sqlFind({
-      currentINN: { $lte: maxNumber },
-      kick: false,
-      member: true
-    });
-    for (const idty of toKick) {
-      logger.trace('Revoke %s for currentINN <= %s', idty.uid, maxNumber);
-      idty.revoked = true;
-      yield that.saveEntity(idty);
-    }
-  });
+  this.trimExpiredIdentities = (medianTime) => this.exec('DELETE FROM ' + this.table + ' WHERE expires_on IS NULL OR expires_on < ' + medianTime);
 
   /**************************
    * SANDBOX STUFF
diff --git a/app/lib/dal/sqliteDAL/LinksDAL.js b/app/lib/dal/sqliteDAL/LinksDAL.js
deleted file mode 100644
index 18aece48a1eeff4e6d62fa36adbbbb4a7145be88..0000000000000000000000000000000000000000
--- a/app/lib/dal/sqliteDAL/LinksDAL.js
+++ /dev/null
@@ -1,132 +0,0 @@
-/**
- * Created by cgeek on 22/08/15.
- */
-
-const Q = require('q');
-const co = require('co');
-const logger = require('../../logger')('linksDAL');
-const AbstractSQLite = require('./AbstractSQLite');
-
-module.exports = LinksDAL;
-
-function LinksDAL(driver, wotb) {
-
-  "use strict";
-
-  AbstractSQLite.call(this, driver);
-
-  const that = this;
-
-  this.table = 'link';
-  this.fields = [
-    'source',
-    'target',
-    'timestamp',
-    'block_number',
-    'block_hash',
-    'obsolete',
-    'from_wotb_id',
-    'to_wotb_id'
-  ];
-  this.arrays = [];
-  this.booleans = ['obsolete'];
-  this.pkFields = ['source', 'target', 'block_number', 'block_hash'];
-  this.translated = {};
-
-  this.init = () => co(function *() {
-    return that.exec('BEGIN;' +
-      'CREATE TABLE IF NOT EXISTS ' + that.table + ' (' +
-      'source VARCHAR(50) NOT NULL,' +
-      'target VARCHAR(50) NOT NULL,' +
-      'timestamp INTEGER NOT NULL,' +
-      'block_number INTEGER NOT NULL,' +
-      'block_hash VARCHAR(64),' +
-      'obsolete BOOLEAN NOT NULL,' +
-      'from_wotb_id INTEGER NULL,' +
-      'to_wotb_id INTEGER NULL,' +
-      'PRIMARY KEY (source,target,block_number,block_hash)' +
-      ');' +
-      'CREATE INDEX IF NOT EXISTS idx_link_source ON link (source);' +
-      'CREATE INDEX IF NOT EXISTS idx_link_obsolete ON link (obsolete);' +
-      'CREATE INDEX IF NOT EXISTS idx_link_target ON link (target);' +
-      'CREATE INDEX IF NOT EXISTS idx_link_timestamp ON link (timestamp);' +
-      'COMMIT;', []);
-  });
-
-  this.getValidLinksFrom = (pubkey) => this.sqlFind({
-    source: pubkey,
-    obsolete: false
-  });
-
-  this.getSimilarLinksFromDate = (from, to, minDate) => this.sqlFind({
-    source: from,
-    target: to,
-    timestamp: { $gte: minDate }
-  });
-
-  this.getValidLinksTo = (pubkey) => this.sqlFind({
-    target: pubkey,
-    obsolete: false
-  });
-
-  this.getLinksWithPath = (from, to) =>
-    this.sqlFind({
-      source: from,
-      target: to
-    });
-
-  this.getLinksFrom = (from) =>
-    this.sqlFind({
-      source: from
-    });
-
-  this.getLinksOfIssuerAbove = (from, aboveBlock) =>
-    this.sqlFind({
-      source: from,
-      block_number: { $gt: aboveBlock }
-    });
-
-  this.obsoletesLinks = (minTimestamp) => co(function *() {
-    const linksToObsolete = yield that.sqlFind({
-      timestamp: { $lte: minTimestamp },
-      obsolete: false
-    });
-    linksToObsolete.forEach((link) => wotb.removeLink(link.from_wotb_id, link.to_wotb_id, true));
-    return that.sqlUpdateWhere({ obsolete: true }, {
-      timestamp: { $lte: minTimestamp },
-      obsolete: false
-    });
-  });
-
-  this.unObsoletesLinks = (minTimestamp) => co(function *() {
-    const linksToUnObsolete = yield that.sqlFind({
-      timestamp: { $gte: minTimestamp },
-      obsolete: true
-    });
-    linksToUnObsolete.forEach((link) => wotb.addLink(link.from_wotb_id, link.to_wotb_id));
-    return that.sqlUpdateWhere({ obsolete: false }, {
-      timestamp: { $gte: minTimestamp }
-    });
-  });
-
-  this.removeLink = (link) => co(function *() {
-    wotb.removeLink(link.from_wotb_id, link.to_wotb_id);
-    return that.deleteEntity(link);
-  });
-
-  this.updateBatchOfLinks = (links) => co(function *() {
-    const queries = [];
-    const insert = that.getInsertHead();
-    const values = links.map((link) => {
-      wotb.addLink(link.from_wotb_id, link.to_wotb_id);
-      return that.getInsertValue(link);
-    });
-    if (links.length) {
-      queries.push(insert + '\n' + values.join(',\n') + ';');
-      logger.query(queries.join('\n'));
-    }
-    if (queries.length) {
-      return that.exec(queries.join('\n'));
-    }
-  });
-}
\ No newline at end of file
diff --git a/app/lib/dal/sqliteDAL/MembershipDAL.js b/app/lib/dal/sqliteDAL/MembershipDAL.js
index 261b3fc1d7bcb4824977e2f75e651c6c8afc4582..d4338a4ce89ce1fa85debd2b3456f1546c8b7119 100644
--- a/app/lib/dal/sqliteDAL/MembershipDAL.js
+++ b/app/lib/dal/sqliteDAL/MembershipDAL.js
@@ -33,6 +33,7 @@ function MembershipDAL(driver) {
     'idtyHash',
     'written',
     'written_number',
+    'expires_on',
     'signature',
     'expired'
   ];
@@ -56,6 +57,7 @@ function MembershipDAL(driver) {
       'idtyHash VARCHAR(64),' +
       'written BOOLEAN NOT NULL,' +
       'written_number INTEGER,' +
+      'expires_on INTEGER NULL,' +
       'signature VARCHAR(50),' +
       'PRIMARY KEY (issuer,signature)' +
       ');' +
@@ -65,112 +67,32 @@ function MembershipDAL(driver) {
       'COMMIT;', []);
   });
 
-  this.getMembershipOfIssuer = (ms) => this.sqlExisting(ms);
-
   this.getMembershipsOfIssuer = (issuer) => this.sqlFind({
     issuer: issuer
   });
 
-  this.getPendingINOfTarget = (hash) =>
-    this.sqlFind({
-      idtyHash: hash,
-      membership: 'IN',
-      written: false
+  this.getPendingINOfTarget = (hash) => this.sqlFind({
+    idtyHash: hash,
+    membership: 'IN'
   });
 
   this.getPendingIN = () => this.sqlFind({
-    membership: 'IN',
-    written: false
+    membership: 'IN'
   });
 
   this.getPendingOUT = () => this.sqlFind({
-    membership: 'OUT',
-    written: false
-  });
-
-  this.previousMS = (pubkey, maxNumber) => co(function *() {
-    let previous = yield that.sqlFindOne({
-      issuer: pubkey,
-      number: { $lt: maxNumber },
-      written: true
-    }, {
-      number: 'DESC'
-    });
-    if (!previous) {
-      previous = {
-        number: -1
-      };
-    }
-    return previous;
-  });
-
-  this.previousIN = (pubkey, maxNumber) => co(function *() {
-    let previous = yield that.sqlFindOne({
-      issuer: pubkey,
-      membership: 'IN',
-      number: { $lt: maxNumber },
-      written: true
-    }, {
-      number: 'DESC'
-    });
-    if (!previous) {
-      previous = {
-        number: -1
-      };
-    }
-    return previous;
-  });
-
-  this.unwriteMS = (ms) => co(function *() {
-    const existing = yield that.sqlExisting({
-      issuer: ms.issuer,
-      signature: ms.signature
-    });
-    if (existing) {
-      existing.written = false;
-      existing.written_number = null;
-      that.saveEntity(existing);
-    }
+    membership: 'OUT'
   });
 
-  this.saveOfficialMS = (type, ms, blockNumber) => {
-    const obj = _.extend({}, ms);
-    obj.membership = type.toUpperCase();
-    obj.written = true;
-    obj.written_number = blockNumber;
-    return this.saveEntity(_.pick(obj, 'membership', 'issuer', 'number', 'blockNumber', 'blockHash', 'userid', 'certts', 'block', 'fpr', 'idtyHash', 'written', 'written_number', 'signature'));
-  };
-
   this.savePendingMembership = (ms) => {
     ms.membership = ms.membership.toUpperCase();
     ms.written = false;
-    return this.saveEntity(_.pick(ms, 'membership', 'issuer', 'number', 'blockNumber', 'blockHash', 'userid', 'certts', 'block', 'fpr', 'idtyHash', 'written', 'written_number', 'signature'));
+    return this.saveEntity(_.pick(ms, 'membership', 'issuer', 'number', 'blockNumber', 'blockHash', 'userid', 'certts', 'block', 'fpr', 'idtyHash', 'expires_on', 'written', 'written_number', 'signature'));
   };
 
-  this.updateBatchOfMemberships = (mss) => co(function *() {
-    const queries = [];
-    const insert = that.getInsertHead();
-    const values = mss.map((cert) => that.getInsertValue(cert));
-    if (mss.length) {
-      queries.push(insert + '\n' + values.join(',\n') + ';');
-    }
-    if (queries.length) {
-      return that.exec(queries.join('\n'));
-    }
-  });
-
-  this.flagExpiredMemberships = (maxNumber, onNumber) => co(function *() {
-    yield that.exec('UPDATE ' + that.table + ' ' +
-      'SET expired = ' + onNumber + ' ' +
-      'WHERE expired IS NULL ' +
-      'AND blockNumber <= ' + maxNumber);
-  });
+  this.deleteMS = (ms) => this.deleteEntity(ms);
 
-  this.unflagExpiredMembershipsOf = (onNumber) => co(function *() {
-    yield that.exec('UPDATE ' + that.table + ' ' +
-      'SET expired = NULL ' +
-      'WHERE expired = ' + onNumber);
-  });
+  this.trimExpiredMemberships = (medianTime) => this.exec('DELETE FROM ' + this.table + ' WHERE expires_on IS NULL OR expires_on < ' + medianTime);
 
   /**************************
    * SANDBOX STUFF
diff --git a/app/lib/dal/sqliteDAL/MetaDAL.js b/app/lib/dal/sqliteDAL/MetaDAL.js
index dfbcfaf48d82f4272b138dfe73e0c2126021890b..170df720b140b82ba7256afd86cd1b1b16cff602 100644
--- a/app/lib/dal/sqliteDAL/MetaDAL.js
+++ b/app/lib/dal/sqliteDAL/MetaDAL.js
@@ -162,13 +162,14 @@ function MetaDAL(driver) {
     15: () => co(function *() {
       let blockDAL = new (require('./BlockDAL'))(driver);
       let idtyDAL = new (require('./IdentityDAL'))(driver);
+      let iindexDAL = new (require('./index/IIndexDAL'))(driver);
       yield idtyDAL.exec('ALTER TABLE idty ADD COLUMN revoked_on INTEGER NULL');
       const blocks = yield blockDAL.query('SELECT * FROM block WHERE revoked NOT LIKE ?', ['[]']);
       for (const block of blocks) {
         // Explicit revocations only
         for (const inlineRevocation of block.revoked) {
           const revocation = Revocation.statics.fromInline(inlineRevocation);
-          const idty = yield idtyDAL.getFromPubkey(revocation.pubkey);
+          const idty = yield iindexDAL.getFromPubkey(revocation.pubkey);
           idty.revoked_on = block.number;
           yield idtyDAL.saveIdentity(idty);
         }
diff --git a/app/lib/dal/sqliteDAL/SourcesDAL.js b/app/lib/dal/sqliteDAL/SourcesDAL.js
deleted file mode 100644
index 7e429aad129eba0c9b1732cbc139c68ddba5537b..0000000000000000000000000000000000000000
--- a/app/lib/dal/sqliteDAL/SourcesDAL.js
+++ /dev/null
@@ -1,142 +0,0 @@
-/**
- * Created by cgeek on 22/08/15.
- */
-
-const co = require('co');
-const _ = require('underscore');
-const AbstractSQLite = require('./AbstractSQLite');
-
-module.exports = SourcesDAL;
-
-function SourcesDAL(driver) {
-
-  "use strict";
-
-  AbstractSQLite.call(this, driver);
-
-  const that = this;
-
-  this.table = 'source';
-  this.fields = [
-    'type',
-    'number',
-    'time',
-    'identifier',
-    'amount',
-    'base',
-    'noffset',
-    'block_hash',
-    'consumed',
-    'conditions'
-  ];
-  this.arrays = [];
-  this.bigintegers = ['amount'];
-  this.booleans = ['consumed'];
-  this.pkFields = ['identifier', 'noffset'];
-  this.translated = {};
-
-  this.init = () => co(function *() {
-    return that.exec('BEGIN;' +
-      'CREATE TABLE IF NOT EXISTS ' + that.table + ' (' +
-      'type VARCHAR(1) NOT NULL,' +
-      'number INTEGER NOT NULL,' +
-      'time DATETIME,' +
-      'identifier VARCHAR(64) NOT NULL,' +
-      'noffset INTEGER NOT NULL,' +
-      'amount VARCHAR(50) NOT NULL,' +
-      'base INTEGER NOT NULL,' +
-      'block_hash VARCHAR(64) NOT NULL,' +
-      'consumed BOOLEAN NOT NULL,' +
-      'conditions TEXT,' +
-      'PRIMARY KEY (identifier,noffset)' +
-      ');' +
-      'CREATE INDEX IF NOT EXISTS idx_source_type ON source (type);' +
-      'CREATE INDEX IF NOT EXISTS idx_source_number ON source (number);' +
-      'CREATE INDEX IF NOT EXISTS idx_source_identifier ON source (identifier);' +
-      'CREATE INDEX IF NOT EXISTS idx_source_noffset ON source (noffset);' +
-      'CREATE INDEX IF NOT EXISTS idx_source_conditions ON source (conditions);' +
-      'COMMIT;', []);
-  });
-
-  this.getAvailableForPubkey = (pubkey) => this.sqlFind({
-    conditions: { $contains: pubkey },
-    consumed: false
-  });
-
-  this.getUDSources = (pubkey) => this.sqlFind({
-    conditions: { $contains: pubkey },
-    type: 'D'
-  });
-
-  this.getSource = (identifier, index) => this.sqlFindOne({
-    identifier: identifier,
-    noffset: index
-  });
-
-  this.getSource = (identifier, noffset) => that.sqlExisting({
-    identifier: identifier,
-    noffset: noffset
-  });
-
-  this.consumeSource = (identifier, index) => co(function *() {
-    return that.updateEntity({
-      identifier: identifier,
-      noffset: index
-    },{
-      consumed: true
-    });
-  });
-
-  this.addSource = (type, number, identifier, noffset, amount, base, block_hash, time, conditions) => this.saveEntity({
-    type: type,
-    number: number,
-    identifier: identifier,
-    noffset: noffset,
-    amount: amount,
-    base: base,
-    time: time,
-    block_hash: block_hash,
-    consumed: false,
-    conditions: conditions
-  });
-
-  this.unConsumeSource = (identifier, index) => co(function *() {
-    let src = yield that.sqlExisting({
-      identifier: identifier,
-      noffset: index
-    });
-    if (!src) {
-      throw "Cannot revert: inputs used by the blocks were removed from the DB";
-    } else {
-      return that.updateEntity({
-        identifier: identifier,
-        noffset: index
-      },{
-        consumed: false
-      });
-    }
-  });
-
-  this.updateBatchOfSources = (sources) => co(function *() {
-    const inserts = _.filter(sources, { toConsume: false });
-    const updates = _.filter(sources, { toConsume: true });
-    const queries = [];
-    if (inserts.length) {
-      const insert = that.getInsertHead();
-      const values = inserts.map((src) => that.getInsertValue(_.extend(src, { consumed: false })));
-      queries.push(insert + '\n' + values.join(',\n') + ';');
-    }
-    if (updates.length) {
-      const del = that.getConsumeHead();
-      const values = that.getConsumeValues(updates);
-      queries.push(del + '\n' + values + ';');
-    }
-    if (queries.length) {
-      return that.exec(queries.join('\n'));
-    }
-  });
-
-  this.removeAllSourcesOfBlock = (number) => this.sqlRemoveWhere({
-    number: number
-  });
-}
diff --git a/app/lib/dal/sqliteDAL/index/BIndexDAL.js b/app/lib/dal/sqliteDAL/index/BIndexDAL.js
new file mode 100644
index 0000000000000000000000000000000000000000..147c4e322f40d626af5cd0c141e27f0924255d73
--- /dev/null
+++ b/app/lib/dal/sqliteDAL/index/BIndexDAL.js
@@ -0,0 +1,100 @@
+/**
+ * Created by cgeek on 22/08/15.
+ */
+
+const co = require('co');
+const _ = require('underscore');
+const AbstractSQLite = require('./../AbstractSQLite');
+
+module.exports = BIndexDAL;
+
+function BIndexDAL(driver) {
+
+  "use strict";
+
+  AbstractSQLite.call(this, driver);
+
+  const that = this;
+
+  this.table = 'b_index';
+  this.fields = [
+    'version',
+    'bsize',
+    'hash',
+    'issuer',
+    'time',
+    'number',
+    'membersCount',
+    'issuersCount',
+    'issuersFrame',
+    'issuersFrameVar',
+    'issuerDiff',
+    'avgBlockSize',
+    'medianTime',
+    'dividend',
+    'mass',
+    'unitBase',
+    'powMin',
+    'udTime',
+    'diffNumber',
+    'speed'
+  ];
+  this.arrays = [];
+  this.bigintegers = ['mass'];
+  this.booleans = ['leaving'];
+  this.pkFields = ['number'];
+  this.translated = {};
+
+  this.init = () => co(function *() {
+    return that.exec('BEGIN;' +
+      'CREATE TABLE IF NOT EXISTS ' + that.table + ' (' +
+      'version INTEGER NOT NULL,' +
+      'bsize INTEGER NOT NULL,' +
+      'hash VARCHAR(64) NOT NULL,' +
+      'issuer VARCHAR(50) NOT NULL,' +
+      'time INTEGER NOT NULL,' +
+      'number INTEGER NOT NULL,' +
+      'membersCount INTEGER NOT NULL,' +
+      'issuersCount INTEGER NOT NULL,' +
+      'issuersFrame INTEGER NOT NULL,' +
+      'issuersFrameVar INTEGER NOT NULL,' +
+      'issuerDiff INTEGER NULL,' +
+      'avgBlockSize INTEGER NOT NULL,' +
+      'medianTime INTEGER NOT NULL,' +
+      'dividend INTEGER NOT NULL,' +
+      'mass VARCHAR(100) NOT NULL,' +
+      'unitBase INTEGER NOT NULL,' +
+      'powMin INTEGER NOT NULL,' +
+      'udTime INTEGER NOT NULL,' +
+      'diffNumber INTEGER NOT NULL,' +
+      'speed FLOAT NOT NULL,' +
+      'PRIMARY KEY (number)' +
+      ');' +
+      'CREATE INDEX IF NOT EXISTS idx_bindex_number ON b_index (number);' +
+      'CREATE INDEX IF NOT EXISTS idx_bindex_issuer ON b_index (issuer);' +
+      'COMMIT;', []);
+  });
+
+  /**
+   * Get HEAD~n
+   * @param n Position
+   */
+  this.head = (n) => co(function*() {
+    // Default to HEAD~1
+    n = n || 1;
+    const headRecords = yield that.query('SELECT * FROM ' + that.table + ' ORDER BY number DESC LIMIT 1 OFFSET ?', [n - 1]);
+    return headRecords[0];
+  });
+
+  /**
+   * Get HEAD~n..m
+   * @param n
+   * @param m
+   */
+  this.range = (n, m) => co(function*() {
+    const count = m - n + 1;
+    return that.query('SELECT * FROM ' + that.table + ' ORDER BY number DESC LIMIT ? OFFSET ?', [count, n - 1]);
+  });
+
+  this.removeBlock = (number) => that.exec('DELETE FROM ' + that.table + ' WHERE number = ' + number);
+}
diff --git a/app/lib/dal/sqliteDAL/index/CIndexDAL.js b/app/lib/dal/sqliteDAL/index/CIndexDAL.js
new file mode 100644
index 0000000000000000000000000000000000000000..d90d793b6975242c3938aa21bea344aacaa8f330
--- /dev/null
+++ b/app/lib/dal/sqliteDAL/index/CIndexDAL.js
@@ -0,0 +1,123 @@
+/**
+ * Created by cgeek on 22/08/15.
+ */
+
+const co = require('co');
+const constants = require('./../../../constants');
+const indexer = require('./../../../dup/indexer');
+const AbstractSQLite = require('./../AbstractSQLite');
+
+module.exports = CIndexDAL;
+
+function CIndexDAL(driver) {
+
+  "use strict";
+
+  AbstractSQLite.call(this, driver);
+
+  const that = this;
+
+  this.table = 'c_index';
+  this.fields = [
+    'op',
+    'issuer',
+    'receiver',
+    'created_on',
+    'written_on',
+    'sig',
+    'expires_on',
+    'expired_on',
+    'chainable_on',
+    'from_wid',
+    'to_wid'
+  ];
+  this.arrays = [];
+  this.bigintegers = [];
+  this.booleans = [];
+  this.pkFields = ['op', 'issuer', 'receiver', 'written_on'];
+  this.translated = {};
+
+  this.init = () => co(function *() {
+    return that.exec('BEGIN;' +
+      'CREATE TABLE IF NOT EXISTS ' + that.table + ' (' +
+      'op VARCHAR(10) NOT NULL,' +
+      'issuer VARCHAR(50) NOT NULL,' +
+      'receiver VARCHAR(50) NOT NULL,' +
+      'created_on VARCHAR(80) NOT NULL,' +
+      'written_on VARCHAR(80) NOT NULL,' +
+      'sig VARCHAR(100) NULL,' +
+      'expires_on INTEGER NULL,' +
+      'expired_on INTEGER NULL,' +
+      'chainable_on INTEGER NULL,' +
+      'from_wid INTEGER NULL,' +
+      'to_wid INTEGER NULL,' +
+      'PRIMARY KEY (op,issuer,receiver,written_on)' +
+      ');' +
+      'CREATE INDEX IF NOT EXISTS idx_cindex_issuer ON c_index (issuer);' +
+      'CREATE INDEX IF NOT EXISTS idx_cindex_receiver ON c_index (receiver);' +
+      'CREATE INDEX IF NOT EXISTS idx_cindex_chainable_on ON c_index (chainable_on);' +
+      'COMMIT;', []);
+  });
+
+  this.reducablesFrom = (from) => co(function*() {
+    const reducables = yield that.query('SELECT * FROM ' + that.table + ' WHERE issuer = ? ORDER BY CAST(written_on as integer) ASC', [from]);
+    return indexer.DUP_HELPERS.reduceBy(reducables, ['issuer', 'receiver', 'created_on']);
+  });
+
+  this.trimExpiredCerts = () => co(function*() {
+    const toDelete = yield that.sqlFind({ expired_on: { $gt: 0 }});
+    for (const row of toDelete) {
+      yield that.exec("DELETE FROM " + that.table + " " +
+        "WHERE issuer like '" + row.issuer + "' " +
+        "AND receiver = '" + row.receiver + "' " +
+        "AND created_on like '" + row.created_on + "'");
+    }
+  });
+
+  this.getWrittenOn = (blockstamp) => that.sqlFind({ written_on: blockstamp });
+
+  this.findExpired = (medianTime) => that.query('SELECT * FROM ' + that.table + ' c1 WHERE expires_on <= ? ' +
+    'AND NOT EXISTS (' +
+    ' SELECT * FROM c_index c2' +
+    ' WHERE c1.issuer = c2.issuer' +
+    ' AND c1.receiver = c2.receiver' +
+    ' AND c1.created_on = c2.created_on' +
+    ' AND c2.op = ?' +
+    ')', [medianTime, constants.IDX_UPDATE]);
+
+  this.getValidLinksTo = (receiver) => that.query('SELECT * FROM ' + that.table + ' c1 ' +
+    'WHERE c1.receiver = ? ' +
+    'AND NOT EXISTS (' +
+    ' SELECT * FROM c_index c2' +
+    ' WHERE c1.issuer = c2.issuer' +
+    ' AND c1.receiver = c2.receiver' +
+    ' AND c1.created_on = c2.created_on' +
+    ' AND c2.op = ?' +
+    ')', [receiver, constants.IDX_UPDATE]);
+
+  this.getValidLinksFrom = (issuer) => that.query('SELECT * FROM ' + that.table + ' c1 ' +
+    'WHERE c1.issuer = ? ' +
+    'AND NOT EXISTS (' +
+    ' SELECT * FROM c_index c2' +
+    ' WHERE c1.issuer = c2.issuer' +
+    ' AND c1.receiver = c2.receiver' +
+    ' AND c1.created_on = c2.created_on' +
+    ' AND c2.op = ?' +
+    ')', [issuer, constants.IDX_UPDATE]);
+
+  this.existsNonReplayableLink = (issuer, receiver) => co(function*() {
+    const results = that.query('SELECT * FROM ' + that.table + ' c1 ' +
+      'WHERE c1.issuer = ? ' +
+      'AND c1.receiver = ? ' +
+      'AND NOT EXISTS (' +
+      ' SELECT * FROM c_index c2' +
+      ' WHERE c1.issuer = c2.issuer' +
+      ' AND c1.receiver = c2.receiver' +
+      ' AND c1.created_on = c2.created_on' +
+      ' AND c2.op = ?' +
+      ')', [issuer, receiver, constants.IDX_UPDATE]);
+    return results.length > 0;
+  });
+
+  this.removeBlock = (blockstamp) => that.exec('DELETE FROM ' + that.table + ' WHERE written_on = \'' + blockstamp + '\'');
+}
diff --git a/app/lib/dal/sqliteDAL/index/IIndexDAL.js b/app/lib/dal/sqliteDAL/index/IIndexDAL.js
new file mode 100644
index 0000000000000000000000000000000000000000..36620d906a3cb889e726b948b46b551513cc9a0a
--- /dev/null
+++ b/app/lib/dal/sqliteDAL/index/IIndexDAL.js
@@ -0,0 +1,132 @@
+/**
+ * Created by cgeek on 22/08/15.
+ */
+
+const co = require('co');
+const _ = require('underscore');
+const indexer = require('./../../../dup/indexer');
+const AbstractSQLite = require('./../AbstractSQLite');
+
+module.exports = IIndexDAL;
+
+function IIndexDAL(driver) {
+
+  "use strict";
+
+  AbstractSQLite.call(this, driver);
+
+  const that = this;
+
+  this.table = 'i_index';
+  this.fields = [
+    'op',
+    'uid',
+    'pub',
+    'hash',
+    'sig',
+    'created_on',
+    'written_on',
+    'member',
+    'wasMember',
+    'kick',
+    'wotb_id'
+  ];
+  this.arrays = [];
+  this.bigintegers = [];
+  this.booleans = ['member', 'wasMember', 'kick'];
+  this.pkFields = ['op', 'pub', 'created_on', 'written_on'];
+  this.translated = {};
+
+  this.init = () => co(function *() {
+    return that.exec('BEGIN;' +
+      'CREATE TABLE IF NOT EXISTS ' + that.table + ' (' +
+      'op VARCHAR(10) NOT NULL,' +
+      'uid VARCHAR(100) NULL,' +
+      'pub VARCHAR(50) NOT NULL,' +
+      'hash VARCHAR(80) NULL,' +
+      'sig VARCHAR(80) NULL,' +
+      'created_on VARCHAR(80) NULL,' +
+      'written_on VARCHAR(80) NOT NULL,' +
+      'member BOOLEAN NULL,' +
+      'wasMember BOOLEAN NULL,' +
+      'kick BOOLEAN NULL,' +
+      'wotb_id INTEGER NULL,' +
+      'PRIMARY KEY (op,pub,created_on,written_on)' +
+      ');' +
+      'CREATE INDEX IF NOT EXISTS idx_iindex_pub ON i_index (pub);' +
+      'COMMIT;', []);
+  });
+
+  this.getMembers = () => co(function*() {
+    // All those who has been subject to, or who are currently subject to kicking. Make one result per pubkey.
+    const pubkeys = yield that.query('SELECT DISTINCT(pub) FROM ' + that.table);
+    // We get the full representation for each member
+    const reduced = yield pubkeys.map((entry) => co(function*() {
+      const reducable = yield that.reducable(entry.pub);
+      return indexer.DUP_HELPERS.reduce(reducable);
+    }));
+    // Filter on those to be kicked, return their pubkey
+    const filtered = _.filter(reduced, (entry) => entry.member);
+    return filtered.map(toCorrectEntity);
+  });
+
+  this.getLatestMember = () => co(function*() {
+    const max_wotb_id = (yield that.query('SELECT MAX(wotb_id) as max_wotb_id FROM ' + that.table))[0].max_wotb_id;
+    return entityOrNull('wotb_id', max_wotb_id);
+  });
+
+  this.getToBeKickedPubkeys = () => co(function*() {
+    // All those who has been subject to, or who are currently subject to kicking. Make one result per pubkey.
+    const reducables = indexer.DUP_HELPERS.reduceBy(yield that.sqlFind({ kick: true }), ['pub']);
+    // We get the full representation for each member
+    const reduced = yield reducables.map((entry) => co(function*() {
+      const reducable = yield that.reducable(entry.pub);
+      return indexer.DUP_HELPERS.reduce(reducable);
+    }));
+    // Filter on those to be kicked, return their pubkey
+    return _.filter(reduced, (entry) => entry.kick).map((entry) => entry.pub);
+  });
+
+  this.searchThoseMatching = (search) => co(function*() {
+    const reducables = indexer.DUP_HELPERS.reduceBy(yield that.sqlFindLikeAny({
+        pub: "%" + search + "%",
+        uid: "%" + search + "%"
+      }), ['pub']);
+    // We get the full representation for each member
+    return yield reducables.map((entry) => co(function*() {
+      return toCorrectEntity(indexer.DUP_HELPERS.reduce(yield that.reducable(entry.pub)));
+    }));
+  });
+
+  this.getFromPubkey = (pubkey) => entityOrNull('pub', pubkey);
+
+  this.getFromUID = (uid) => entityOrNull('uid', uid);
+
+  this.getFromHash = (hash) => entityOrNull('hash', hash, 'pub');
+
+  this.reducable = (pub) => this.query('SELECT * FROM ' + this.table + ' WHERE pub = ? ORDER BY CAST(written_on as integer) ASC', [pub]);
+
+  this.removeBlock = (blockstamp) => that.exec('DELETE FROM ' + that.table + ' WHERE written_on = \'' + blockstamp + '\'');
+
+  function entityOrNull(field, value, retrieveOnField) {
+    return co(function*() {
+      let reducable = yield that.query('SELECT * FROM ' + that.table + ' WHERE ' + field + ' = ?', [value]);
+      if (reducable.length) {
+        if (retrieveOnField) {
+          // Force full retrieval on `pub` field
+          reducable = yield that.query('SELECT * FROM ' + that.table + ' WHERE pub = ? ORDER BY CAST(written_on as int) ASC', [reducable[0].pub]);
+        }
+        return toCorrectEntity(indexer.DUP_HELPERS.reduce(reducable));
+      }
+      return null;
+    });
+  }
+
+  function toCorrectEntity(row) {
+    // Old field
+    row.pubkey = row.pub;
+    row.buid = row.created_on;
+    row.revocation_sig = null;
+    return row;
+  }
+}
diff --git a/app/lib/dal/sqliteDAL/index/MIndexDAL.js b/app/lib/dal/sqliteDAL/index/MIndexDAL.js
new file mode 100644
index 0000000000000000000000000000000000000000..67579f8a89edecd9ab3f5d154ae8159535065100
--- /dev/null
+++ b/app/lib/dal/sqliteDAL/index/MIndexDAL.js
@@ -0,0 +1,68 @@
+/**
+ * Created by cgeek on 22/08/15.
+ */
+
+const co = require('co');
+const indexer = require('./../../../dup/indexer');
+const AbstractSQLite = require('./../AbstractSQLite');
+
+module.exports = MIndexDAL;
+
+function MIndexDAL(driver) {
+
+  "use strict";
+
+  AbstractSQLite.call(this, driver);
+
+  const that = this;
+
+  this.table = 'm_index';
+  this.fields = [
+    'op',
+    'pub',
+    'created_on',
+    'written_on',
+    'expires_on',
+    'expired_on',
+    'revokes_on',
+    'revoked_on',
+    'leaving',
+    'revocation'
+  ];
+  this.arrays = [];
+  this.bigintegers = [];
+  this.booleans = ['leaving'];
+  this.pkFields = ['op', 'pub', 'created_on', 'written_on'];
+  this.translated = {};
+
+  this.init = () => co(function *() {
+    return that.exec('BEGIN;' +
+      'CREATE TABLE IF NOT EXISTS ' + that.table + ' (' +
+      'op VARCHAR(10) NOT NULL,' +
+      'pub VARCHAR(50) NOT NULL,' +
+      'created_on VARCHAR(80) NOT NULL,' +
+      'written_on VARCHAR(80) NOT NULL,' +
+      'expires_on INTEGER NULL,' +
+      'expired_on INTEGER NULL,' +
+      'revokes_on INTEGER NULL,' +
+      'revoked_on INTEGER NULL,' +
+      'leaving BOOLEAN NULL,' +
+      'revocation VARCHAR(80) NULL,' +
+      'PRIMARY KEY (op,pub,created_on,written_on)' +
+      ');' +
+      'CREATE INDEX IF NOT EXISTS idx_mindex_pub ON m_index (pub);' +
+      'COMMIT;', []);
+  });
+
+  this.getReducedMS = (pub) => co(function*() {
+    const reducables = yield that.reducable(pub);
+    if (reducables.length) {
+      return indexer.DUP_HELPERS.reduce(reducables);
+    }
+    return null;
+  });
+
+  this.reducable = (pub) => this.query('SELECT * FROM ' + this.table + ' WHERE pub = ? ORDER BY CAST(written_on as integer) ASC', [pub]);
+
+  this.removeBlock = (blockstamp) => that.exec('DELETE FROM ' + that.table + ' WHERE written_on = \'' + blockstamp + '\'');
+}
diff --git a/app/lib/dal/sqliteDAL/index/SIndexDAL.js b/app/lib/dal/sqliteDAL/index/SIndexDAL.js
new file mode 100644
index 0000000000000000000000000000000000000000..3f28d6ba019cb7b2390d85ac835e8ac229065559
--- /dev/null
+++ b/app/lib/dal/sqliteDAL/index/SIndexDAL.js
@@ -0,0 +1,99 @@
+/**
+ * Created by cgeek on 22/08/15.
+ */
+
+const _ = require('underscore');
+const co = require('co');
+const indexer = require('../../../dup/indexer');
+const AbstractSQLite = require('./../AbstractSQLite');
+
+module.exports = SIndexDAL;
+
+function SIndexDAL(driver) {
+
+  "use strict";
+
+  AbstractSQLite.call(this, driver);
+
+  const that = this;
+
+  this.table = 's_index';
+  this.fields = [
+    'op',
+    'tx',
+    'identifier',
+    'pos',
+    'created_on',
+    'written_on',
+    'written_time',
+    'amount',
+    'base',
+    'locktime',
+    'consumed',
+    'conditions'
+  ];
+  this.arrays = [];
+  this.bigintegers = ['amount'];
+  this.booleans = ['consumed'];
+  this.pkFields = ['op', 'identifier', 'pos', 'written_on'];
+  this.translated = {};
+
+  this.init = () => co(function *() {
+    return that.exec('BEGIN;' +
+      'CREATE TABLE IF NOT EXISTS ' + that.table + ' (' +
+      'op VARCHAR(10) NOT NULL,' +
+      'tx VARCHAR(80) NULL,' +
+      'identifier VARCHAR(64) NOT NULL,' +
+      'pos INTEGER NOT NULL,' +
+      'created_on VARCHAR(80) NULL,' +
+      'written_on VARCHAR(80) NOT NULL,' +
+      'written_time INTEGER NOT NULL,' +
+      'amount VARCHAR(50) NULL,' +
+      'base INTEGER NULL,' +
+      'locktime INTEGER NULL,' +
+      'consumed BOOLEAN NOT NULL,' +
+      'conditions TEXT,' +
+      'PRIMARY KEY (op,identifier,pos,written_on)' +
+      ');' +
+      'CREATE INDEX IF NOT EXISTS idx_sindex_identifier ON s_index (identifier);' +
+      'CREATE INDEX IF NOT EXISTS idx_sindex_pos ON s_index (pos);' +
+      'COMMIT;', []);
+  });
+
+  this.removeBlock = (blockstamp) => that.exec('DELETE FROM ' + that.table + ' WHERE written_on = \'' + blockstamp + '\'');
+
+  this.getSource = (identifier, pos) => co(function*() {
+    const reducable = yield that.sqlFind({ identifier, pos });
+    if (reducable.length == 0) {
+      return null;
+    } else {
+      const src = indexer.DUP_HELPERS.reduce(reducable);
+      src.type = src.tx ? 'T' : 'D';
+      return src;
+    }
+  });
+
+  this.getUDSources = (pubkey) => co(function*() {
+    const reducables = yield that.sqlFind({
+      conditions: { $contains: pubkey },
+      tx: { $null: true }
+    });
+    const reduced = indexer.DUP_HELPERS.reduceBy(reducables, ['identifier', 'pos']).map((src) => {
+      src.type = src.tx ? 'T' : 'D';
+      return src;
+    });
+    return _.sortBy(reduced, (row) => row.type == 'D' ? 0 : 1);
+  });
+
+  this.getAvailableForPubkey = (pubkey) => co(function*() {
+    const reducables = yield that.sqlFind({
+      conditions: { $contains: 'SIG(' + pubkey + ')' }
+    });
+    const sources = indexer.DUP_HELPERS.reduceBy(reducables, ['identifier', 'pos']).map((src) => {
+      src.type = src.tx ? 'T' : 'D';
+      return src;
+    });
+    const filtered = _.filter(sources, (src) => !src.consumed);
+    return _.sortBy(filtered, (row) => row.type == 'D' ? 0 : 1);
+  });
+}
diff --git a/app/lib/dup/indexer.js b/app/lib/dup/indexer.js
new file mode 100644
index 0000000000000000000000000000000000000000..1cd4d0ab8140db2213553b78baddf050c247aaf6
--- /dev/null
+++ b/app/lib/dup/indexer.js
@@ -0,0 +1,1785 @@
+"use strict";
+
+const co              = require('co');
+const _               = require('underscore');
+const constants       = require('../constants');
+const rawer           = require('../ucp/rawer');
+const unlock          = require('../ucp/txunlock');
+const keyring         = require('../crypto/keyring');
+const Block           = require('../entity/block');
+const Identity        = require('../entity/identity');
+const Certification   = require('../entity/certification');
+const Membership      = require('../entity/membership');
+const Transaction     = require('../entity/transaction');
+
+const indexer = module.exports = {
+
+  localIndex: (block, conf) => {
+
+    /********************
+     * GENERAL BEHAVIOR
+     *
+     * * for each newcomer: 2 indexes (1 iindex CREATE, 1 mindex CREATE)
+     * * for each comeback: 2 indexes (1 iindex UPDATE, 1 mindex UPDATE)
+     * * for each renewer:  1 index   (                 1 mindex UPDATE)
+     * * for each leaver:   1 index   (                 1 mindex UPDATE)
+     * * for each revoked:  1 index   (                 1 mindex UPDATE)
+     * * for each excluded: 1 indexes (1 iindex UPDATE)
+     *
+     * * for each certification: 1 index (1 cindex CREATE)
+     *
+     * * for each tx output: 1 index (1 sindex CREATE)
+     * * for each tx input:  1 index (1 sindex UPDATE)
+     */
+
+    const index = [];
+
+    /***************************
+     * IDENTITIES INDEX (IINDEX)
+     **************************/
+    for (const identity of block.identities) {
+      const idty = Identity.statics.fromInline(identity);
+      // Computes the hash if not done yet
+      index.push({
+        index: constants.I_INDEX,
+        op: constants.IDX_CREATE,
+        uid: idty.uid,
+        pub: idty.pubkey,
+        hash: idty.hash,
+        sig: idty.sig,
+        created_on: idty.buid,
+        written_on: [block.number, block.hash].join('-'),
+        member: true,
+        wasMember: true,
+        kick: false,
+        wid: null // wotb id
+      });
+    }
+
+    /****************************
+     * MEMBERSHIPS INDEX (MINDEX)
+     ***************************/
+    // Joiners (newcomer or join back)
+    for (const inlineMS of block.joiners) {
+      const ms = Membership.statics.fromInline(inlineMS);
+      const matchesANewcomer = _.filter(index, (row) => row.index == constants.I_INDEX && row.pub == ms.issuer).length > 0;
+      if (matchesANewcomer) {
+        // Newcomer
+        index.push({
+          index: constants.M_INDEX,
+          op: constants.IDX_CREATE,
+          pub: ms.issuer,
+          created_on: [ms.number, ms.fpr].join('-'),
+          written_on: [block.number, block.hash].join('-'),
+          type: 'JOIN',
+          expires_on: conf.msValidity,
+          revokes_on: conf.msValidity * constants.REVOCATION_FACTOR,
+          revoked_on: null,
+          leaving: false
+        });
+      } else {
+        // Join back
+        index.push({
+          index: constants.M_INDEX,
+          op: constants.IDX_UPDATE,
+          pub: ms.issuer,
+          created_on: [ms.number, ms.fpr].join('-'),
+          written_on: [block.number, block.hash].join('-'),
+          type: 'JOIN',
+          expires_on: conf.msValidity,
+          revokes_on: conf.msValidity * constants.REVOCATION_FACTOR,
+          revoked_on: null,
+          leaving: null
+        });
+        index.push({
+          index: constants.I_INDEX,
+          op: constants.IDX_UPDATE,
+          uid: null,
+          pub: ms.issuer,
+          created_on: null,
+          written_on: [block.number, block.hash].join('-'),
+          member: true,
+          wasMember: null,
+          kick: null,
+          wid: null
+        });
+      }
+    }
+    // Actives
+    for (const inlineMS of block.actives) {
+      const ms = Membership.statics.fromInline(inlineMS);
+      // Renew
+      index.push({
+        index: constants.M_INDEX,
+        op: constants.IDX_UPDATE,
+        pub: ms.issuer,
+        created_on: [ms.number, ms.fpr].join('-'),
+        written_on: [block.number, block.hash].join('-'),
+        type: 'ACTIVE',
+        expires_on: conf.msValidity,
+        revokes_on: conf.msValidity * constants.REVOCATION_FACTOR,
+        revoked_on: null,
+        leaving: null
+      });
+    }
+    // Leavers
+    for (const inlineMS of block.leavers) {
+      const ms = Membership.statics.fromInline(inlineMS);
+      index.push({
+        index: constants.M_INDEX,
+        op: constants.IDX_UPDATE,
+        pub: ms.issuer,
+        created_on: [ms.number, ms.fpr].join('-'),
+        written_on: [block.number, block.hash].join('-'),
+        type: 'LEAVE',
+        expires_on: null,
+        revokes_on: null,
+        revoked_on: null,
+        leaving: true
+      });
+    }
+    // Revoked
+    for (const inlineRevocation of block.revoked) {
+      const revocation = Identity.statics.revocationFromInline(inlineRevocation);
+      index.push({
+        index: constants.M_INDEX,
+        op: constants.IDX_UPDATE,
+        pub: revocation.pubkey,
+        created_on: [block.number, block.hash].join('-'),
+        written_on: [block.number, block.hash].join('-'),
+        expires_on: null,
+        revokes_on: null,
+        revoked_on: [block.number, block.hash].join('-'),
+        revocation: revocation.sig,
+        leaving: false
+      });
+    }
+    // Excluded
+    for (const excluded of block.excluded) {
+      index.push({
+        index: constants.I_INDEX,
+        op: constants.IDX_UPDATE,
+        uid: null,
+        pub: excluded,
+        created_on: null,
+        written_on: [block.number, block.hash].join('-'),
+        member: false,
+        wasMember: null,
+        kick: false,
+        wid: null
+      });
+    }
+
+    /*******************************
+     * CERTIFICATIONS INDEX (CINDEX)
+     ******************************/
+    for (const inlineCert of block.certifications) {
+      const cert = Certification.statics.fromInline(inlineCert);
+      index.push({
+        index: constants.C_INDEX,
+        op: constants.IDX_CREATE,
+        issuer: cert.pubkey,
+        receiver: cert.to,
+        created_on: cert.block_number,
+        written_on: [block.number, block.hash].join('-'),
+        sig: cert.sig,
+        chainable_on: parseInt(block.medianTime)  + conf.sigPeriod,
+        expires_on: conf.sigValidity,
+        expired_on: 0,
+        from_wid: null,
+        to_wid: null
+      });
+    }
+
+    return index.concat(module.exports.localSIndex(block));
+  },
+
+  localSIndex: (block) => {
+    /*******************************
+     * SOURCES INDEX (SINDEX)
+     ******************************/
+    const index = [];
+    if (!block.transactions && block.getTransactions) {
+      const txs = block.getTransactions();
+      block.transactions = [];
+      for (const tx of txs) {
+        block.transactions.push({
+          version: tx.version,
+          comment: tx.comment,
+          issuers: tx.issuers,
+          signatures: tx.signatures,
+          inputs: tx.inputs.map((i) => i.raw),
+          outputs: tx.outputs.map((o) => o.raw)
+        });
+      }
+    }
+    for (const obj of block.transactions) {
+      obj.currency = block.currency;
+      obj.issuers = obj.signatories;
+      const tx = new Transaction(obj);
+      const txObj = tx.getTransaction();
+      const txHash = tx.getHash(true);
+      let k = 0;
+      for (const input of txObj.inputs) {
+        index.push({
+          index: constants.S_INDEX,
+          op: constants.IDX_UPDATE,
+          tx: txHash,
+          identifier: input.identifier,
+          pos: input.pos,
+          created_on: tx.blockstamp,
+          written_on: [block.number, block.hash].join('-'),
+          written_time: block.medianTime,
+          locktime: obj.locktime,
+          unlock: txObj.unlocks[k],
+          amount: input.amount,
+          base: input.base,
+          consumed: true,
+          conditions: null,
+          txObj: txObj
+        });
+        k++;
+      }
+
+      let i = 0;
+      for (const output of txObj.outputs) {
+        index.push({
+          index: constants.S_INDEX,
+          op: constants.IDX_CREATE,
+          tx: txHash,
+          identifier: txHash,
+          pos: i++,
+          written_on: [block.number, block.hash].join('-'),
+          written_time: block.medianTime,
+          locktime: obj.locktime,
+          amount: output.amount,
+          base: output.base,
+          consumed: false,
+          conditions: output.conditions,
+          txObj: obj
+        });
+      }
+    }
+    return index;
+  },
+
+  quickCompleteGlobalScope: (block, conf, bindex, iindex, mindex, cindex, sindex, dal) => co(function*() {
+
+    function range(start, end, property) {
+      return co(function*() {
+        let range;
+        end = Math.min(end, bindex.length);
+        if (start == 1) {
+          range = bindex.slice(-end);
+        } else {
+          range = bindex.slice(-end, -start + 1);
+        }
+        range.reverse();
+        if (property) {
+          // Filter on a particular property
+          return range.map((b) => b[property]);
+        } else {
+          return range;
+        }
+      });
+    }
+
+    function head(n) {
+      return co(function*() {
+        return (yield range(n, n))[0];
+      });
+    }
+
+    const HEAD = {
+      version: block.version,
+      bsize: Block.statics.getLen(block),
+      hash: Block.statics.getHash(block),
+      issuer: block.issuer,
+      time: block.time,
+      medianTime: block.medianTime,
+      number: block.number,
+      powMin: block.powMin,
+      unitBase: block.unitbase,
+      membersCount: block.membersCount,
+      dividend: block.dividend
+    };
+    const HEAD_1 = yield head(1);
+
+    if (HEAD.number == 0) {
+      HEAD.dividend = conf.ud0;
+    }
+    else if (!HEAD.dividend) {
+      HEAD.dividend = HEAD_1.dividend;
+    } else {
+      HEAD.new_dividend = HEAD.dividend;
+    }
+
+    // BR_G04
+    yield indexer.prepareIssuersCount(HEAD, range, HEAD_1);
+
+    // BR_G05
+    indexer.prepareIssuersFrame(HEAD, HEAD_1);
+
+    // BR_G06
+    indexer.prepareIssuersFrameVar(HEAD, HEAD_1);
+
+    // BR_G07
+    yield indexer.prepareAvgBlockSize(HEAD, range);
+
+    // BR_G09
+    indexer.prepareDiffNumber(HEAD, HEAD_1, conf);
+
+    // BR_G11
+    indexer.prepareUDTime(HEAD, HEAD_1, conf);
+
+    // BR_G15
+    indexer.prepareMass(HEAD, HEAD_1);
+
+    // BR_G16
+    yield indexer.prepareSpeed(HEAD, head, conf);
+
+    // BR_G19
+    yield indexer.prepareIdentitiesAge(iindex, HEAD, HEAD_1, conf, dal);
+
+    // BR_G22
+    yield indexer.prepareMembershipsAge(mindex, HEAD, HEAD_1, conf, dal);
+
+    // BR_G37
+    yield indexer.prepareCertificationsAge(cindex, HEAD, HEAD_1, conf, dal);
+
+    // BR_G104
+    yield indexer.ruleIndexCorrectMembershipExpiryDate(HEAD, mindex, dal);
+
+    // BR_G105
+    yield indexer.ruleIndexCorrectCertificationExpiryDate(HEAD, cindex, dal);
+
+    return HEAD;
+  }),
+
+  completeGlobalScope: (block, conf, index, dal) => co(function*() {
+
+    const iindex = module.exports.iindex(index);
+    const mindex = module.exports.mindex(index);
+    const cindex = module.exports.cindex(index);
+    const sindex = module.exports.sindex(index);
+
+    const range = dal.range;
+    const head = dal.head;
+
+    const HEAD = {
+      version: block.version,
+      bsize: Block.statics.getLen(block),
+      hash: Block.statics.getHash(block),
+      issuer: block.issuer,
+      time: block.time,
+      powMin: block.powMin
+    };
+    const HEAD_1 = yield head(1);
+    if (HEAD_1) {
+      HEAD_1.currency = conf.currency;
+    }
+
+    // BR_G01
+    indexer.prepareNumber(HEAD, HEAD_1);
+
+    // BR_G02
+    if (HEAD.number > 0) {
+      HEAD.previousHash = HEAD_1.hash;
+    } else {
+      HEAD.previousHash = null;
+    }
+
+    // BR_G99
+    if (HEAD.number > 0) {
+      HEAD.currency = HEAD_1.currency;
+    } else {
+      HEAD.currency = null;
+    }
+
+    // BR_G03
+    if (HEAD.number > 0) {
+      HEAD.previousIssuer = HEAD_1.issuer;
+    } else {
+      HEAD.previousIssuer = null;
+    }
+
+    // BR_G03
+    if (HEAD.number > 0) {
+      HEAD.issuerIsMember = reduce(yield dal.iindexDAL.reducable(HEAD.issuer)).member;
+    } else {
+      HEAD.issuerIsMember = reduce(_.where(iindex, { pub: HEAD.issuer })).member;
+    }
+
+    // BR_G04
+    yield indexer.prepareIssuersCount(HEAD, range, HEAD_1);
+
+    // BR_G05
+    indexer.prepareIssuersFrame(HEAD, HEAD_1);
+
+    // BR_G06
+    indexer.prepareIssuersFrameVar(HEAD, HEAD_1);
+
+    // BR_G07
+    yield indexer.prepareAvgBlockSize(HEAD, range);
+
+    // BR_G08
+    if (HEAD.number > 0) {
+      HEAD.medianTime = average(yield range(1, Math.min(conf.medianTimeBlocks, HEAD.number), 'time')); // TODO: median
+    } else {
+      HEAD.medianTime = HEAD.time;
+    }
+
+    // BR_G09
+    indexer.prepareDiffNumber(HEAD, HEAD_1, conf);
+
+    // BR_G10
+    if (HEAD.number == 0) {
+      HEAD.membersCount = count(_.filter(iindex, (entry) => entry.member === true));
+    } else {
+      HEAD.membersCount = HEAD_1.membersCount
+        + count(_.filter(iindex, (entry) => entry.member === true))
+        - count(_.filter(iindex, (entry) => entry.member === false));
+    }
+
+    // BR_G11
+    indexer.prepareUDTime(HEAD, HEAD_1, conf);
+
+    // BR_G12
+    if (HEAD.number == 0) {
+      HEAD.unitBase = 0;
+    } else {
+      HEAD.unitBase = HEAD_1.unitBase;
+    }
+
+    // BR_G13
+    indexer.prepareDividend(HEAD, HEAD_1, conf);
+
+    // BR_G14
+    indexer.prepareUnitBase(HEAD, HEAD_1, conf);
+
+    // BR_G15
+    indexer.prepareMass(HEAD, HEAD_1);
+
+    // BR_G16
+    yield indexer.prepareSpeed(HEAD, head, conf);
+
+    // BR_G17
+    if (HEAD.number > 0) {
+
+      const ratio = HEAD.version > 3 ? 1.189 : Math.sqrt(1.066);
+      const maxGenTime = Math.ceil(conf.avgGenTime * ratio);
+      const minGenTime = Math.floor(conf.avgGenTime / ratio);
+      const minSpeed = 1/ maxGenTime;
+      const maxSpeed = 1/ minGenTime;
+
+      if (HEAD.diffNumber != HEAD_1.diffNumber && HEAD.speed >= maxSpeed && (HEAD_1.powMin + 2) % 16 == 0) {
+        HEAD.powMin = HEAD_1.powMin + 2;
+      } else if (HEAD.diffNumber != HEAD_1.diffNumber && HEAD.speed >= maxSpeed) {
+        HEAD.powMin = HEAD_1.powMin + 1;
+      } else if (HEAD.diffNumber != HEAD_1.diffNumber && HEAD.speed <= minSpeed && HEAD_1.powMin % 16 == 0) {
+        HEAD.powMin = Math.max(0, HEAD_1.powMin - 2);
+      } else if (HEAD.diffNumber != HEAD_1.diffNumber && HEAD.speed <= minSpeed) {
+        HEAD.powMin = Math.max(0, HEAD_1.powMin - 1);
+      } else {
+        HEAD.powMin = HEAD_1.powMin;
+      }
+    }
+
+    // BR_G18
+    yield indexer.preparePersonalizedPoW(HEAD, HEAD_1, range, conf);
+
+    // BR_G19
+    yield indexer.prepareIdentitiesAge(iindex, HEAD, HEAD_1, conf, dal);
+
+    // BR_G20
+    yield iindex.map((ENTRY) => co(function*() {
+      if (ENTRY.op == constants.IDX_CREATE) {
+        ENTRY.uidUnique = count(yield dal.iindexDAL.sqlFind({ uid: ENTRY.uid })) == 0;
+      } else {
+        ENTRY.uidUnique = true;
+      }
+    }));
+
+    // BR_G21
+    yield iindex.map((ENTRY) => co(function*() {
+      if (ENTRY.op == constants.IDX_CREATE) {
+        ENTRY.pubUnique = count(yield dal.iindexDAL.sqlFind({pub: ENTRY.pub})) == 0;
+      } else {
+        ENTRY.pubUnique = true;
+      }
+    }));
+
+    // BR_G33
+    yield iindex.map((ENTRY) => co(function*() {
+      if (ENTRY.member !== false) {
+        ENTRY.excludedIsMember = true;
+      } else {
+        ENTRY.excludedIsMember = reduce(yield dal.iindexDAL.reducable(ENTRY.pub)).member;
+      }
+    }));
+
+    // BR_G35
+    yield iindex.map((ENTRY) => co(function*() {
+      ENTRY.isBeingKicked = ENTRY.member === false;
+    }));
+
+    // BR_G36
+    yield iindex.map((ENTRY) => co(function*() {
+      ENTRY.hasToBeExcluded = reduce(yield dal.iindexDAL.reducable(ENTRY.pub)).kick;
+    }));
+
+    // BR_G22
+    yield indexer.prepareMembershipsAge(mindex, HEAD, HEAD_1, conf, dal);
+
+    // BR_G23
+    yield mindex.map((ENTRY) => co(function*() {
+      if (!ENTRY.revoked_on) {
+        const created_on = reduce(yield dal.mindexDAL.reducable(ENTRY.pub)).created_on;
+        if (created_on != null) {
+          ENTRY.numberFollowing = number(ENTRY.created_on) > number(created_on);
+        } else {
+          ENTRY.numberFollowing = true; // Follows nothing
+        }
+      } else {
+        ENTRY.numberFollowing = true;
+      }
+    }));
+
+    // BR_G24
+    // Global testing, because of wotb
+    const oneIsOutdistanced = yield checkPeopleAreNotOudistanced(
+      HEAD.version,
+      _.filter(mindex, (entry) => !entry.revoked_on).map((entry) => entry.pub),
+      cindex.reduce((newLinks, c) => {
+        newLinks[c.receiver] = newLinks[c.receiver] || [];
+        newLinks[c.receiver].push(c.issuer);
+        return newLinks;
+      }, {}),
+      // Newcomers
+      _.where(iindex, { op: constants.IDX_CREATE }).map((entry) => entry.pub),
+      conf,
+      dal
+    );
+    mindex.map((ENTRY) => {
+      if (ENTRY.expires_on) {
+        ENTRY.distanceOK = !oneIsOutdistanced;
+      } else {
+        ENTRY.distanceOK = true;
+      }
+    });
+
+    // BR_G25
+    yield mindex.map((ENTRY) => co(function*() {
+      ENTRY.onRevoked = reduce(yield dal.mindexDAL.reducable(ENTRY.pub)).revoked_on != null;
+    }));
+
+    // BR_G26
+    yield _.filter(mindex, (entry) => entry.op == constants.IDX_UPDATE && entry.expired_on === 0).map((ENTRY) => co(function*() {
+      ENTRY.joinsTwice = reduce(yield dal.iindexDAL.reducable(ENTRY.pub)).member == true;
+    }));
+
+    // BR_G27
+    yield mindex.map((ENTRY) => co(function*() {
+      if (ENTRY.type == 'JOIN' || ENTRY.type == 'ACTIVE') {
+        const existing = count(yield dal.cindexDAL.sqlFind({ receiver: ENTRY.pub, expired_on: 0 }));
+        const pending = count(_.filter(cindex, (c) => c.receiver == ENTRY.pub && c.expired_on == 0));
+        ENTRY.enoughCerts = (existing + pending) >= conf.sigQty;
+      } else {
+        ENTRY.enoughCerts = true;
+      }
+    }));
+
+    // BR_G28
+    yield mindex.map((ENTRY) => co(function*() {
+      if (ENTRY.type == 'LEAVE') {
+        ENTRY.leaverIsMember = reduce(yield dal.iindexDAL.reducable(ENTRY.pub)).member;
+      } else {
+        ENTRY.leaverIsMember = true;
+      }
+    }));
+
+    // BR_G29
+    yield mindex.map((ENTRY) => co(function*() {
+      if (ENTRY.type == 'ACTIVE') {
+        const reducable = yield dal.iindexDAL.reducable(ENTRY.pub);
+        ENTRY.activeIsMember = reduce(reducable).member;
+      } else {
+        ENTRY.activeIsMember = true;
+      }
+    }));
+
+    // BR_G30
+    yield mindex.map((ENTRY) => co(function*() {
+      if (!ENTRY.revoked_on) {
+        ENTRY.revokedIsMember = true;
+      } else {
+        ENTRY.revokedIsMember = reduce(yield dal.iindexDAL.reducable(ENTRY.pub)).member;
+      }
+    }));
+
+    // BR_G31
+    yield mindex.map((ENTRY) => co(function*() {
+      if (!ENTRY.revoked_on) {
+        ENTRY.alreadyRevoked = false;
+      } else {
+        ENTRY.alreadyRevoked = reduce(yield dal.mindexDAL.reducable(ENTRY.pub)).revoked_on;
+      }
+    }));
+
+    // BR_G32
+    yield mindex.map((ENTRY) => co(function*() {
+      if (!ENTRY.revoked_on) {
+        ENTRY.revocationSigOK = true;
+      } else {
+        ENTRY.revocationSigOK = yield sigCheckRevoke(ENTRY, dal, block.currency);
+      }
+    }));
+
+    // BR_G34
+    yield mindex.map((ENTRY) => co(function*() {
+      ENTRY.isBeingRevoked = ENTRY.revoked;
+    }));
+
+    // BR_G37
+    yield indexer.prepareCertificationsAge(cindex, HEAD, HEAD_1, conf, dal);
+
+    // BR_G38
+    if (HEAD.number > 0) {
+      yield cindex.map((ENTRY) => co(function*() {
+        const rows = yield dal.cindexDAL.sqlFind({ issuer: ENTRY.issuer, chainable_on: { $gt: HEAD_1.medianTime }});
+        ENTRY.unchainables = count(rows);
+      }));
+    }
+
+    // BR_G39
+    yield cindex.map((ENTRY) => co(function*() {
+      ENTRY.stock = count(yield dal.cindexDAL.sqlFind({ issuer: ENTRY.issuer, expired_on: 0 }));
+    }));
+
+    // BR_G40
+    yield cindex.map((ENTRY) => co(function*() {
+      ENTRY.fromMember = reduce(yield dal.iindexDAL.reducable(ENTRY.issuer)).member;
+    }));
+
+    // BR_G41
+    yield cindex.map((ENTRY) => co(function*() {
+      ENTRY.toMember = reduce(yield dal.iindexDAL.reducable(ENTRY.receiver)).member;
+    }));
+
+    // BR_G42
+    yield cindex.map((ENTRY) => co(function*() {
+      ENTRY.toNewcomer = count(_.where(iindex, { member: true, pub: ENTRY.receiver })) > 0;
+    }));
+
+    // BR_G43
+    yield cindex.map((ENTRY) => co(function*() {
+      ENTRY.toLeaver = reduce(yield dal.mindexDAL.reducable(ENTRY.pub)).leaving;
+    }));
+
+    // BR_G44
+    yield cindex.map((ENTRY) => co(function*() {
+      const reducable = yield dal.cindexDAL.sqlFind({ issuer: ENTRY.issuer, receiver: ENTRY.receiver });
+      ENTRY.isReplay = count(reducable) > 0 && reduce(reducable).expired_on === 0;
+    }));
+
+    // BR_G45
+    yield cindex.map((ENTRY) => co(function*() {
+      ENTRY.sigOK = checkCertificationIsValid(block, ENTRY, (pub) => {
+        let localInlineIdty = block.getInlineIdentity(pub);
+        if (localInlineIdty) {
+          return Identity.statics.fromInline(localInlineIdty);
+        }
+        return dal.getWrittenIdtyByPubkey(pub);
+      }, conf, dal);
+    }));
+
+    // BR_G102
+    yield _.where(sindex, { op: constants.IDX_UPDATE }).map((ENTRY) => co(function*() {
+      if (HEAD.number == 0 && ENTRY.created_on == '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855') {
+        ENTRY.age = 0;
+      } else {
+        if (HEAD.version >= 3) {
+          let ref = yield dal.getBlockByBlockstamp(ENTRY.created_on);
+          if (ref && blockstamp(ref.number, ref.hash) == ENTRY.created_on) {
+            ENTRY.age = HEAD_1.medianTime - ref.medianTime;
+          } else {
+            ENTRY.age = constants.TRANSACTION_EXPIRY_DELAY + 1;
+          }
+        } else {
+          ENTRY.age = 0;
+        }
+      }
+    }));
+
+    // BR_G46
+    yield _.where(sindex, { op: constants.IDX_UPDATE }).map((ENTRY) => co(function*() {
+      if (HEAD.version > 2) {
+        const reducable = yield dal.sindexDAL.sqlFind({
+          identifier: ENTRY.identifier,
+          pos: ENTRY.pos,
+          amount: ENTRY.amount,
+          base: ENTRY.base
+        });
+        // TODO: not in v1.0
+        let available;
+        if (count(reducable) > 0) {
+          available = reduce(reducable).consumed === false;
+        } else {
+          const src = yield dal.getSource(ENTRY.identifier, ENTRY.pos);
+          available = src.consumed === false;
+        }
+        ENTRY.available = available;
+      } else {
+        ENTRY.available = reduce(yield dal.sindexDAL.sqlFind({
+            identifier: ENTRY.identifier,
+            pos: ENTRY.pos
+          })).consumed === false;
+      }
+    }));
+
+    // BR_G47
+    yield _.where(sindex, { op: constants.IDX_UPDATE }).map((ENTRY) => co(function*() {
+      if (HEAD.version > 2) {
+        let source = _.filter(sindex, (src) => src.identifier == ENTRY.identifier && src.pos == ENTRY.pos && src.conditions)[0];
+        if (!source) {
+          const reducable = yield dal.sindexDAL.sqlFind({
+            identifier: ENTRY.identifier,
+            pos: ENTRY.pos,
+            amount: ENTRY.amount,
+            base: ENTRY.base
+          });
+          // TODO: not in v1.0
+          if (count(reducable) > 0) {
+            source = reduce(reducable);
+          } else {
+            source = yield dal.getSource(ENTRY.identifier, ENTRY.pos);
+          }
+        }
+        ENTRY.conditions = source.conditions;
+        ENTRY.isLocked = !txSourceUnlock(ENTRY, source);
+      } else {
+        const source = reduce(yield dal.sindexDAL.sqlFind({
+          identifier: ENTRY.identifier,
+          pos: ENTRY.pos
+        }));
+        ENTRY.conditions = source.conditions;
+        ENTRY.isLocked = !txSourceUnlock(ENTRY, source);
+      }
+    }));
+
+    // BR_G48
+    yield _.where(sindex, { op: constants.IDX_UPDATE }).map((ENTRY) => co(function*() {
+      if (HEAD.version > 2) {
+        ENTRY.isTimeLocked = ENTRY.written_time - reduce(yield dal.sindexDAL.sqlFind({
+            identifier: ENTRY.identifier,
+            pos: ENTRY.pos,
+            amount: ENTRY.amount,
+            base: ENTRY.base
+          })).written_time < ENTRY.locktime;
+      } else {
+        ENTRY.isTimeLocked = ENTRY.written_time - reduce(yield dal.sindexDAL.sqlFind({
+            identifier: ENTRY.identifier,
+            pos: ENTRY.pos
+          })).written_time < ENTRY.locktime;
+      }
+    }));
+
+    return HEAD;
+  }),
+
+  // BR_G01
+  prepareNumber: (HEAD, HEAD_1) => {
+    if (HEAD_1) {
+      HEAD.number = HEAD_1.number + 1;
+    } else {
+      HEAD.number = 0;
+    }
+  },
+
+  // BR_G04
+  prepareIssuersCount: (HEAD, range, HEAD_1) => co(function*() {
+    if (HEAD.number == 0) {
+      HEAD.issuersCount = 0;
+    } else if(HEAD_1.version > 2) {
+      HEAD.issuersCount = count(uniq(yield range(1, HEAD_1.issuersFrame, 'issuer')));
+    } else {
+      HEAD.issuersCount = count(uniq(yield range(1, 40, 'issuer')));
+    }
+  }),
+
+  // BR_G05
+  prepareIssuersFrame: (HEAD, HEAD_1) => {
+    if (HEAD.number == 0) {
+      HEAD.issuersFrame = 1;
+    } else if (HEAD_1.version == 2) {
+      HEAD.issuersFrame = 40
+    } else if (HEAD_1.issuersFrameVar > 0) {
+      HEAD.issuersFrame = HEAD_1.issuersFrame + 1
+    } else if (HEAD_1.issuersFrameVar < 0) {
+      HEAD.issuersFrame = HEAD_1.issuersFrame - 1
+    } else {
+      HEAD.issuersFrame = HEAD_1.issuersFrame
+    }
+  },
+
+  // BR_G06
+  prepareIssuersFrameVar: (HEAD, HEAD_1) => {
+    if (HEAD.number == 0 || HEAD_1.version == 2) {
+      HEAD.issuersFrameVar = 0;
+    } else {
+      // TODO: remove this bug
+      let issuersVar = (HEAD.issuersCount - HEAD_1.issuersCount);
+      if (issuersVar > 0) issuersVar = 1;
+      if (issuersVar < 0) issuersVar = -1;
+      if (issuersVar == 0) issuersVar = 0;
+      if (HEAD_1.issuersFrameVar > 0) {
+        HEAD.issuersFrameVar = HEAD_1.issuersFrameVar + 5*issuersVar - 1;
+      } else if (HEAD_1.issuersFrameVar < 0) {
+        HEAD.issuersFrameVar = HEAD_1.issuersFrameVar + 5*issuersVar + 1;
+      } else {
+        HEAD.issuersFrameVar = HEAD_1.issuersFrameVar + 5*issuersVar;
+      }
+    }
+  },
+
+  // BR_G07
+  prepareAvgBlockSize: (HEAD, range) => co(function*() {
+    HEAD.avgBlockSize = average(yield range(1, HEAD.issuersCount, 'bsize'));
+  }),
+
+  // BR_G09
+  prepareDiffNumber: (HEAD, HEAD_1, conf) => {
+    if (HEAD.number == 0) {
+      HEAD.diffNumber = HEAD.number + conf.dtDiffEval;
+    } else if (HEAD_1.diffNumber <= HEAD.number) {
+      HEAD.diffNumber = HEAD_1.diffNumber + conf.dtDiffEval;
+    } else {
+      HEAD.diffNumber = HEAD_1.diffNumber;
+    }
+  },
+
+  // BR_G11
+  prepareUDTime: (HEAD, HEAD_1, conf) => {
+    if (HEAD.number == 0) {
+      HEAD.udTime = HEAD.medianTime + conf.dt;
+    } else if (HEAD_1.udTime <= HEAD.medianTime) {
+      HEAD.udTime = HEAD_1.udTime + conf.dt;
+    } else {
+      HEAD.udTime = HEAD_1.udTime;
+    }
+  },
+
+  // BR_G13
+  prepareDividend: (HEAD, HEAD_1, conf) => {
+    if (HEAD.number == 0) {
+      HEAD.dividend = conf.ud0;
+      HEAD.new_dividend = null;
+    } else if (HEAD.udTime != HEAD_1.udTime) {
+      if (HEAD.version == 2) {
+        // DUA
+        const M = HEAD_1.mass;
+        const N = HEAD.membersCount;
+        const previousUB = HEAD_1.unitBase;
+        const previousUD = HEAD_1.dividend;
+        const c = conf.c;
+        HEAD.dividend = Math.ceil(Math.max(previousUD, c * M / Math.pow(10, previousUB) / N));
+        HEAD.new_dividend = HEAD.dividend;
+      }
+      else if (HEAD.version == 3) {
+        // DUB
+        const c = conf.c;
+        HEAD.dividend = parseInt(((1 + c) * HEAD_1.dividend).toFixed(0));
+        HEAD.new_dividend = HEAD.dividend;
+      }
+      else if (HEAD.version >= 4) {
+        // DUG
+        const previousUB = HEAD_1.unitBase;
+        // TODO: ceiling for v1.0
+        HEAD.dividend = parseInt((HEAD_1.dividend + Math.pow(conf.c, 2) * HEAD_1.mass / HEAD.membersCount / Math.pow(10, previousUB)).toFixed(0));
+        HEAD.new_dividend = HEAD.dividend;
+      }
+    } else {
+      HEAD.dividend = HEAD_1.dividend;
+      HEAD.new_dividend = null;
+    }
+  },
+
+  // BR_G14
+  prepareUnitBase: (HEAD) => {
+    if (HEAD.dividend >= Math.pow(10, 6)) {
+      HEAD.dividend = Math.ceil(HEAD.dividend / 10);
+      HEAD.new_dividend = HEAD.dividend;
+      HEAD.unitBase = HEAD.unitBase + 1;
+    }
+  },
+
+  // BR_G15
+  prepareMass: (HEAD, HEAD_1) => {
+    if (HEAD.number == 0) {
+      HEAD.mass = 0;
+    }
+    else if (HEAD.udTime != HEAD_1.udTime) {
+      HEAD.mass = HEAD_1.mass + HEAD.dividend * Math.pow(10, HEAD.unitBase) * HEAD.membersCount;
+    } else {
+      HEAD.mass = HEAD_1.mass;
+    }
+  },
+
+  // BR_G16
+  prepareSpeed: (HEAD, head, conf) => co(function*() {
+    if (HEAD.number == 0) {
+      HEAD.speed = 0;
+    } else {
+      const quantity = Math.min(conf.dtDiffEval, HEAD.number);
+      const elapsed = (HEAD.medianTime - (yield head(quantity)).medianTime);
+      if (!elapsed) {
+        HEAD.speed = 100;
+      } else {
+        HEAD.speed = quantity / elapsed;
+      }
+    }
+  }),
+
+  // BR_G18
+  preparePersonalizedPoW: (HEAD, HEAD_1, range, conf) => co(function*() {
+    let nbPersonalBlocksInFrame, medianOfBlocksInFrame, blocksOfIssuer;
+    let nbPreviousIssuers = 0, nbBlocksSince = 0;
+    if (HEAD.number == 0) {
+      nbPersonalBlocksInFrame = 0;
+      medianOfBlocksInFrame = 1;
+    } else {
+      const blocksInFrame = yield range(1, HEAD_1.issuersFrame);
+      const issuersInFrame = yield range(1, HEAD_1.issuersFrame, 'issuer');
+      blocksOfIssuer = _.filter(blocksInFrame, (entry) => entry.issuer == HEAD.issuer);
+      nbPersonalBlocksInFrame = count(blocksOfIssuer);
+      const blocksPerIssuerInFrame = uniq(issuersInFrame).map((issuer) => count(_.where(blocksInFrame, { issuer })));
+      medianOfBlocksInFrame = median(blocksPerIssuerInFrame);
+      if (nbPersonalBlocksInFrame == 0) {
+        nbPreviousIssuers = 0;
+        nbBlocksSince = 0;
+      } else {
+        const last = blocksOfIssuer[0];
+        nbPreviousIssuers = last.issuersCount;
+        nbBlocksSince = HEAD_1.number - last.number;
+      }
+    }
+
+    if (HEAD.version == 2) {
+
+      // V0.2 Hardness
+      if (nbPersonalBlocksInFrame > 0) {
+        nbPreviousIssuers--;
+      }
+      HEAD.issuerDiff = Math.max(HEAD.powMin, HEAD.powMin * Math.floor(conf.percentRot * (1 + nbPreviousIssuers) / (1 + nbBlocksSince)));
+
+    } else if (HEAD.version == 3) {
+
+      // V0.3 Hardness
+      HEAD.issuerDiff = Math.max(HEAD.powMin, HEAD.powMin * Math.floor(conf.percentRot * nbPreviousIssuers / (1 + nbBlocksSince)));
+
+    } else if (HEAD.version == 4) {
+
+      // V0.4 Hardness
+      const PERSONAL_EXCESS = Math.max(0, (nbPersonalBlocksInFrame / 5) - 1);
+      const PERSONAL_HANDICAP = Math.floor(Math.log(1 + PERSONAL_EXCESS) / Math.log(1.189));
+      HEAD.issuerDiff = Math.max(HEAD.powMin, HEAD.powMin * Math.floor(conf.percentRot * nbPreviousIssuers / (1 + nbBlocksSince))) + PERSONAL_HANDICAP;
+
+    } else if (HEAD.version == 5) {
+
+      // V0.5 Hardness
+      const PERSONAL_EXCESS = Math.max(0, ((nbPersonalBlocksInFrame + 1)/ medianOfBlocksInFrame) - 1);
+      const PERSONAL_HANDICAP = Math.floor(Math.log(1 + PERSONAL_EXCESS) / Math.log(1.189));
+      HEAD.issuerDiff = HEAD.powMin + PERSONAL_HANDICAP;
+
+    } else if (HEAD.version >= 6) {
+
+      // V0.6 Hardness
+      const PERSONAL_EXCESS = Math.max(0, ( (nbPersonalBlocksInFrame + 1) / medianOfBlocksInFrame) - 1);
+      const PERSONAL_HANDICAP = Math.floor(Math.log(1 + PERSONAL_EXCESS) / Math.log(1.189));
+      HEAD.issuerDiff = Math.max(HEAD.powMin, HEAD.powMin * Math.floor(conf.percentRot * nbPreviousIssuers / (1 + nbBlocksSince))) + PERSONAL_HANDICAP;
+      if ((HEAD.issuerDiff + 1) % 16 == 0) {
+        HEAD.issuerDiff += 1;
+      }
+    }
+
+    HEAD.powRemainder = HEAD.issuerDiff  % 16;
+    HEAD.powZeros = (HEAD.issuerDiff - HEAD.powRemainder) / 16;
+  }),
+
+  // BR_G19
+  prepareIdentitiesAge: (iindex, HEAD, HEAD_1, conf, dal) => co(function*() {
+    yield _.where(iindex, { op: constants.IDX_CREATE }).map((ENTRY) => co(function*() {
+      if (HEAD.number == 0 && ENTRY.created_on == '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855') {
+        ENTRY.age = 0;
+      } else {
+        if (HEAD.version <= 4) {
+          let ref = yield dal.getBlock(number(ENTRY.created_on));
+          if (ref) { // blockstamp was not checked prior to 0.5
+            ENTRY.age = HEAD_1.medianTime - ref.medianTime;
+          } else {
+            ENTRY.age = conf.idtyWindow + 1;
+          }
+        } else {
+          let ref = yield dal.getBlockByBlockstamp(ENTRY.created_on);
+          if (ref && blockstamp(ref.number, ref.hash) == ENTRY.created_on) {
+            ENTRY.age = HEAD_1.medianTime - ref.medianTime;
+          } else {
+            ENTRY.age = conf.idtyWindow + 1;
+          }
+        }
+      }
+    }));
+  }),
+
+  // BR_G22
+  prepareMembershipsAge: (mindex, HEAD, HEAD_1, conf, dal) => co(function*() {
+    yield _.filter(mindex, (entry) => !entry.revoked_on).map((ENTRY) => co(function*() {
+      if (HEAD.number == 0 && ENTRY.created_on == '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855') {
+        ENTRY.age = 0;
+      } else {
+        let ref = yield dal.getBlockByBlockstamp(ENTRY.created_on);
+        if (ref && blockstamp(ref.number, ref.hash) == ENTRY.created_on) {
+          ENTRY.age = HEAD_1.medianTime - ref.medianTime;
+        } else {
+          ENTRY.age = conf.msWindow + 1;
+        }
+      }
+    }));
+  }),
+
+  // BR_G37
+  prepareCertificationsAge: (cindex, HEAD, HEAD_1, conf, dal) => co(function*() {
+    yield cindex.map((ENTRY) => co(function*() {
+      if (HEAD.number == 0) {
+        ENTRY.age = 0;
+      } else {
+        let ref = yield dal.getBlock(number(ENTRY.created_on));
+        if (ref) {
+          ENTRY.age = HEAD_1.medianTime - ref.medianTime;
+        } else {
+          ENTRY.age = conf.sigWindow + 1;
+        }
+      }
+    }));
+  }),
+
+  // BR_G49
+  ruleVersion: (HEAD, HEAD_1) => {
+    if (HEAD.number > 0) {
+      return HEAD.version == HEAD_1.version || HEAD.version == HEAD_1.version + 1;
+    }
+    return true;
+  },
+
+  // BR_G50
+  ruleBlockSize: (HEAD) => HEAD.bsize < indexer.DUP_HELPERS.getMaxBlockSize(HEAD),
+
+  // BR_G98
+  ruleCurrency: (block, HEAD) => {
+    if (HEAD.number > 0) {
+      return block.currency === HEAD.currency;
+    }
+    return true;
+  },
+
+  // BR_G51
+  ruleNumber: (block, HEAD) => block.number == HEAD.number,
+
+  // BR_G52
+  rulePreviousHash: (block, HEAD) => block.previousHash == HEAD.previousHash || (!block.previousHash && !HEAD.previousHash),
+
+  // BR_G53
+  rulePreviousIssuer: (block, HEAD) => block.previousIssuer == HEAD.previousIssuer || (!block.previousIssuer && !HEAD.previousIssuer),
+
+  // BR_G101
+  ruleIssuerIsMember: (HEAD) => HEAD.issuerIsMember == true,
+
+  // BR_G54
+  ruleIssuersCount: (block, HEAD) => {
+    if (HEAD.version > 2) {
+      return block.issuersCount == HEAD.issuersCount;
+    }
+  },
+
+  // BR_G55
+  ruleIssuersFrame: (block, HEAD) => {
+    if (HEAD.version > 2) {
+      return block.issuersFrame == HEAD.issuersFrame;
+    }
+  },
+
+  // BR_G56
+  ruleIssuersFrameVar: (block, HEAD) => {
+    if (HEAD.version > 2) {
+      return block.issuersFrameVar == HEAD.issuersFrameVar;
+    }
+  },
+
+  // BR_G57
+  ruleMedianTime: (block, HEAD) => block.medianTime == HEAD.medianTime,
+
+  // BR_G58
+  ruleDividend: (block, HEAD) => block.dividend == HEAD.new_dividend,
+
+  // BR_G59
+  ruleUnitBase: (block, HEAD) => {
+    if (HEAD.version >= 3) {
+      return block.unitbase == HEAD.unitBase;
+    }
+  },
+
+  // BR_G60
+  ruleMembersCount: (block, HEAD) => block.membersCount == HEAD.membersCount,
+
+  // BR_G61
+  rulePowMin: (block, HEAD) => {
+    if (HEAD.number > 0) {
+      return block.powMin == HEAD.powMin;
+    }
+  },
+
+  // BR_G62
+  ruleProofOfWork: (HEAD) => {
+    // Compute exactly how much zeros are required for this block's issuer
+    const remainder = HEAD.powRemainder;
+    const nbZerosReq = HEAD.powZeros;
+    const highMark = constants.PROOF_OF_WORK.UPPER_BOUND[remainder];
+    const powRegexp = new RegExp('^0{' + nbZerosReq + '}' + '[0-' + highMark + ']');
+    try {
+      if (!HEAD.hash.match(powRegexp)) {
+        const givenZeros = Math.max(0, Math.min(nbZerosReq, HEAD.hash.match(/^0*/)[0].length));
+        const c = HEAD.hash.substr(givenZeros, 1);
+        throw Error('Wrong proof-of-work level: given ' + givenZeros + ' zeros and \'' + c + '\', required was ' + nbZerosReq + ' zeros and an hexa char between [0-' + highMark + ']');
+      }
+      return true;
+    } catch (e) {
+      return false;
+    }
+  },
+
+  // BR_G63
+  ruleIdentityWritability: (iindex, conf) => {
+    for (const ENTRY of iindex) {
+      if (ENTRY.age > conf.idtyWindow) return false;
+    }
+  },
+
+  // BR_G64
+  ruleMembershipWritability: (mindex, conf) => {
+    for (const ENTRY of mindex) {
+      if (ENTRY.age > conf.msWindow) return false;
+    }
+  },
+
+  // BR_G65
+  ruleCertificationWritability: (cindex, conf) => {
+    for (const ENTRY of cindex) {
+      if (ENTRY.age > conf.sigWindow) return false;
+    }
+  },
+
+  // BR_G66
+  ruleCertificationStock: (cindex, conf) => {
+    for (const ENTRY of cindex) {
+      if (ENTRY.stock > conf.sigStock) return false;
+    }
+  },
+
+  // BR_G67
+  ruleCertificationPeriod: (cindex) => {
+    for (const ENTRY of cindex) {
+      if (ENTRY.unchainables > 0) return false;
+    }
+  },
+
+  // BR_G68
+  ruleCertificationFromMember: (HEAD, cindex) => {
+    if (HEAD.number > 0) {
+      for (const ENTRY of cindex) {
+        if (!ENTRY.fromMember) return false;
+      }
+    }
+  },
+
+  // BR_G69
+  ruleCertificationToMemberOrNewcomer: (cindex) => {
+    for (const ENTRY of cindex) {
+      if (!ENTRY.toMember && !ENTRY.toNewcomer) return false;
+    }
+  },
+
+  // BR_G70
+  ruleCertificationToLeaver: (cindex) => {
+    for (const ENTRY of cindex) {
+      if (ENTRY.toLeaver) return false;
+    }
+  },
+
+  // BR_G71
+  ruleCertificationReplay: (cindex) => {
+    for (const ENTRY of cindex) {
+      if (ENTRY.isReplay) return false;
+    }
+  },
+
+  // BR_G72
+  ruleCertificationSignature: (cindex) => {
+    for (const ENTRY of cindex) {
+      if (!ENTRY.sigOK) return false;
+    }
+  },
+
+  // BR_G73
+  ruleIdentityUIDUnicity: (iindex) => {
+    for (const ENTRY of iindex) {
+      if (!ENTRY.uidUnique) return false;
+    }
+  },
+
+  // BR_G74
+  ruleIdentityPubkeyUnicity: (iindex) => {
+    for (const ENTRY of iindex) {
+      if (!ENTRY.pubUnique) return false;
+    }
+  },
+
+  // BR_G75
+  ruleMembershipSuccession: (mindex) => {
+    for (const ENTRY of mindex) {
+      if (!ENTRY.numberFollowing) return false;
+    }
+  },
+
+  // BR_G76
+  ruleMembershipDistance: (mindex) => {
+    for (const ENTRY of mindex) {
+      if (!ENTRY.distanceOK) return false;
+    }
+  },
+
+  // BR_G77
+  ruleMembershipOnRevoked: (mindex) => {
+    for (const ENTRY of mindex) {
+      if (ENTRY.onRevoked) return false;
+    }
+  },
+
+  // BR_G78
+  ruleMembershipJoinsTwice: (mindex) => {
+    for (const ENTRY of mindex) {
+      if (ENTRY.joinsTwice) return false;
+    }
+  },
+
+  // BR_G79
+  ruleMembershipEnoughCerts: (mindex) => {
+    for (const ENTRY of mindex) {
+      if (!ENTRY.enoughCerts) return false;
+    }
+  },
+
+  // BR_G80
+  ruleMembershipLeaverIsMember: (mindex) => {
+    for (const ENTRY of mindex) {
+      if (!ENTRY.leaverIsMember) return false;
+    }
+  },
+
+  // BR_G81
+  ruleMembershipActiveIsMember: (mindex) => {
+    for (const ENTRY of mindex) {
+      if (!ENTRY.activeIsMember) return false;
+    }
+  },
+
+  // BR_G82
+  ruleMembershipRevokedIsMember: (mindex) => {
+    for (const ENTRY of mindex) {
+      if (!ENTRY.revokedIsMember) return false;
+    }
+  },
+
+  // BR_G83
+  ruleMembershipRevokedSingleton: (mindex) => {
+    for (const ENTRY of mindex) {
+      if (ENTRY.alreadyRevoked) return false;
+    }
+  },
+
+  // BR_G84
+  ruleMembershipRevocationSignature: (mindex) => {
+    for (const ENTRY of mindex) {
+      if (!ENTRY.revocationSigOK) return false;
+    }
+  },
+
+  // BR_G85
+  ruleMembershipExcludedIsMember: (iindex) => {
+    for (const ENTRY of iindex) {
+      if (!ENTRY.excludedIsMember) return false;
+    }
+  },
+
+  // BR_G86
+  ruleToBeKickedArePresent: (mindex, dal) => co(function*() {
+    const toBeKicked = yield dal.iindexDAL.getToBeKickedPubkeys();
+    for (const toKick of toBeKicked) {
+      if (count(_.where(mindex, { pub: toKick, isBeingKicked: true })) !== 1) {
+        return false;
+      }
+    }
+  }),
+
+  // BR_G103
+  ruleTxWritability: (sindex) => {
+    for (const ENTRY of sindex) {
+      if (ENTRY.age > constants.TRANSACTION_EXPIRY_DELAY) return false;
+    }
+  },
+
+  // BR_G87
+  ruleInputIsAvailable: (sindex) => {
+    const inputs = _.where(sindex, { op: constants.IDX_UPDATE });
+    for (const ENTRY of inputs) {
+      if (!ENTRY.available) {
+        return false;
+      }
+    }
+  },
+
+  // BR_G88
+  ruleInputIsUnlocked: (sindex) => {
+    const inputs = _.where(sindex, { op: constants.IDX_UPDATE });
+    for (const ENTRY of inputs) {
+      if (ENTRY.isLocked) {
+        return false;
+      }
+    }
+  },
+
+  // BR_G89
+  ruleInputIsTimeUnlocked: (sindex) => {
+    const inputs = _.where(sindex, { op: constants.IDX_UPDATE });
+    for (const ENTRY of inputs) {
+      if (ENTRY.isTimeLocked) {
+        return false;
+      }
+    }
+  },
+
+  // BR_G90
+  ruleOutputBase: (sindex, HEAD_1) => {
+    const inputs = _.where(sindex, { op: constants.IDX_CREATE });
+    for (const ENTRY of inputs) {
+      if (ENTRY.unitBase > HEAD_1.unitBase) {
+        return false;
+      }
+    }
+  },
+
+  // BR_G91
+  ruleIndexGenDividend: (HEAD, dal) => co(function*() {
+    const dividends = [];
+    if (HEAD.new_dividend) {
+      const potentials = reduceBy(yield dal.iindexDAL.sqlFind({ member: true }), ['pub']);
+      for (const potential of potentials) {
+        const MEMBER = reduce(yield dal.iindexDAL.reducable(potential.pub));
+        if (MEMBER.member) {
+          dividends.push({
+            op: 'CREATE',
+            identifier: MEMBER.pub,
+            pos: HEAD.number,
+            written_on: [HEAD.number, HEAD.hash].join('-'),
+            written_time: HEAD.medianTime,
+            amount: HEAD.dividend,
+            base: HEAD.unitBase,
+            locktime: null,
+            conditions: 'SIG(' + MEMBER.pub + ')',
+            consumed: false
+          });
+        }
+      }
+    }
+    return dividends;
+  }),
+
+  // BR_G92
+  ruleIndexGenCertificationExpiry: (HEAD, dal) => co(function*() {
+    const expiries = [];
+    const certs = yield dal.cindexDAL.findExpired(HEAD.medianTime);
+    for (const CERT of certs) {
+      expiries.push({
+        op: 'UPDATE',
+        issuer: CERT.issuer,
+        receiver: CERT.receiver,
+        created_on: CERT.created_on,
+        written_on: [HEAD.number, HEAD.hash].join('-'),
+        expired_on: HEAD.medianTime
+      });
+    }
+    return expiries;
+  }),
+
+  // BR_G93
+  ruleIndexGenMembershipExpiry: (HEAD, dal) => co(function*() {
+    const expiries = [];
+    const memberships = reduceBy(yield dal.mindexDAL.sqlFind({ expires_on: { $lte: HEAD.medianTime } }), ['pub']);
+    for (const POTENTIAL of memberships) {
+      const MS = yield dal.mindexDAL.getReducedMS(POTENTIAL.pub);
+      const hasRenewedSince = MS.expires_on > HEAD.medianTime;
+      if (!MS.expired_on && !hasRenewedSince) {
+        let shouldBeKicked = true;
+        // ------ Fast synchronization specific code ------
+        const idty = yield dal.iindexDAL.getFromPubkey(POTENTIAL.pub);
+        if (!idty.member) {
+          shouldBeKicked = false;
+        }
+        // ------------------------------------------------
+        if (shouldBeKicked) {
+          expiries.push({
+            op: 'UPDATE',
+            pub: MS.pub,
+            created_on: MS.created_on,
+            written_on: [HEAD.number, HEAD.hash].join('-'),
+            expired_on: HEAD.medianTime
+          });
+        }
+      }
+    }
+    return expiries;
+  }),
+
+  // BR_G94
+  ruleIndexGenExclusionByMembership: (HEAD, mindex) => co(function*() {
+    const exclusions = [];
+    const memberships = _.filter(mindex, (entry) => entry.expired_on);
+    for (const MS of memberships) {
+      exclusions.push({
+        op: 'UPDATE',
+        pub: MS.pub,
+        written_on: [HEAD.number, HEAD.hash].join('-'),
+        kick: true
+      });
+    }
+    return exclusions;
+  }),
+
+  // BR_G95
+  ruleIndexGenExclusionByCertificatons: (HEAD, cindex, conf, dal) => co(function*() {
+    const exclusions = [];
+    const expiredCerts = _.filter(cindex, (c) => c.expired_on > 0);
+    for (const CERT of expiredCerts) {
+      const just_expired = _.filter(cindex, (c) => c.receiver == CERT.receiver && c.expired_on > 0);
+      const just_received = _.filter(cindex, (c) => c.receiver == CERT.receiver && c.expired_on == 0);
+      const non_expired_global = yield dal.cindexDAL.sqlFind({ receiver: CERT.receiver, expired_on: 0 });
+      if ((count(non_expired_global) - count(just_expired) + count(just_received)) < conf.sigQty) {
+        exclusions.push({
+          op: 'UPDATE',
+          pub: CERT.receiver,
+          written_on: [HEAD.number, HEAD.hash].join('-'),
+          kick: true
+        });
+      }
+    }
+    return exclusions;
+  }),
+
+  // BR_G96
+  ruleIndexGenImplicitRevocation: (HEAD, dal) => co(function*() {
+    const revocations = [];
+    const pending = yield dal.mindexDAL.sqlFind({ revokes_on: { $lte: HEAD.medianTime}, revoked_on: { $null: true } });
+    for (const MS of pending) {
+      const REDUCED = reduce(yield dal.mindexDAL.sqlFind({ pub: MS.pub }));
+      if (REDUCED.revokes_on <= HEAD.medianTime && !REDUCED.revoked_on) {
+        revocations.push({
+          op: 'UPDATE',
+          pub: MS.receiver,
+          written_on: [HEAD.number, HEAD.hash].join('-'),
+          revoked_on: HEAD.medianTime
+        });
+      }
+    }
+    return revocations;
+  }),
+
+  // BR_G104
+  ruleIndexCorrectMembershipExpiryDate: (HEAD, mindex, dal) => co(function*() {
+    for (const MS of mindex) {
+      if (MS.type == 'JOIN' || MS.type == 'ACTIVE') {
+        let basedBlock = { medianTime: 0 };
+        if (HEAD.number == 0) {
+          basedBlock = HEAD;
+        } else {
+          basedBlock = yield dal.getBlockByBlockstamp(MS.created_on);
+        }
+        MS.expires_on += basedBlock.medianTime;
+        MS.revokes_on += basedBlock.medianTime;
+      }
+    }
+  }),
+
+  // BR_G105
+  ruleIndexCorrectCertificationExpiryDate: (HEAD, cindex, dal) => co(function*() {
+    for (const CERT of cindex) {
+      let basedBlock = { medianTime: 0 };
+      if (HEAD.number == 0) {
+        basedBlock = HEAD;
+      } else {
+        basedBlock = yield dal.getBlock(CERT.created_on);
+      }
+      CERT.expires_on += basedBlock.medianTime;
+    }
+  }),
+
+  iindexCreate: (index) => _(index).filter({ index: constants.I_INDEX, op: constants.IDX_CREATE }),
+  mindexCreate: (index) => _(index).filter({ index: constants.M_INDEX, op: constants.IDX_CREATE }),
+  iindex:       (index) => _(index).filter({ index: constants.I_INDEX }),
+  mindex:       (index) => _(index).filter({ index: constants.M_INDEX }),
+  cindex:       (index) => _(index).filter({ index: constants.C_INDEX }),
+  sindex:       (index) => _(index).filter({ index: constants.S_INDEX }),
+
+  DUP_HELPERS: {
+
+    reduce: reduce,
+    reduceBy: reduceBy,
+    getMaxBlockSize: (HEAD) => Math.max(500, Math.ceil(1.1 * HEAD.avgBlockSize)),
+    checkPeopleAreNotOudistanced: checkPeopleAreNotOudistanced
+  }
+};
+
+function count(range) {
+  return range.length;
+}
+
+function uniq(range) {
+  return _.uniq(range);
+}
+
+function average(values) {
+  // No values => 0 average
+  if (!values.length) return 0;
+  // Otherwise, real average
+  const avg = values.reduce((sum, val) => sum + val, 0) / values.length;
+  return Math.floor(avg);
+}
+
+function median(values) {
+  let med = null;
+  values.sort((a, b) => a < b ? -1 : (a > b ? 1 : 0));
+  const nbValues = values.length;
+  if (nbValues % 2 === 0) {
+    // Even number: the median is the average between the 2 central values, ceil rounded.
+    const firstValue = values[nbValues / 2];
+    const secondValue = values[nbValues / 2 - 1];
+    med = ((firstValue + secondValue) / 2); // TODO v1.0 median ceil rounded
+  } else {
+    med = values[(nbValues + 1) / 2 - 1];
+  }
+  return med;
+}
+
+function number(blockstamp) {
+  return parseInt(blockstamp);
+}
+
+function blockstamp(number, hash) {
+  return [number, hash].join('-');
+}
+
+function reduce(records) {
+  return records.reduce((obj, record) => {
+    const keys = Object.keys(record);
+    for (const k of keys) {
+      if (record[k] !== undefined && record[k] !== null) {
+        obj[k] = record[k];
+      }
+    }
+    return obj;
+  }, {});
+}
+
+function reduceBy(reducables, properties) {
+  const reduced = reducables.reduce((map, entry) => {
+    const id = properties.map((prop) => entry[prop]).join('-');
+    map[id] = map[id] || [];
+    map[id].push(entry);
+    return map;
+  }, {});
+  return Object.values(reduced).map((value) => indexer.DUP_HELPERS.reduce(value));
+}
+
+function checkPeopleAreNotOudistanced (version, pubkeys, newLinks, newcomers, conf, dal) {
+  return co(function *() {
+    let wotb = dal.wotb;
+    let current = yield dal.getCurrentBlockOrNull();
+    let membersCount = current ? current.membersCount : 0;
+    // TODO: make a temporary copy of the WoT in RAM
+    // We add temporarily the newcomers to the WoT, to integrate their new links
+    let nodesCache = newcomers.reduce((map, pubkey) => {
+      let nodeID = wotb.addNode();
+      map[pubkey] = nodeID;
+      wotb.setEnabled(false, nodeID); // These are not members yet
+      return map;
+    }, {});
+    // Add temporarily the links to the WoT
+    let tempLinks = [];
+    let toKeys = _.keys(newLinks);
+    for (const toKey of toKeys) {
+      let toNode = yield getNodeIDfromPubkey(nodesCache, toKey, dal);
+      for (const fromKey of newLinks[toKey]) {
+        let fromNode = yield getNodeIDfromPubkey(nodesCache, fromKey, dal);
+        tempLinks.push({ from: fromNode, to: toNode });
+      }
+    }
+    tempLinks.forEach((link) => wotb.addLink(link.from, link.to));
+    // Checking distance of each member against them
+    let error;
+    for (const pubkey of pubkeys) {
+      let nodeID = yield getNodeIDfromPubkey(nodesCache, pubkey, dal);
+      let dSen;
+      if (version <= 3) {
+        dSen = Math.ceil(constants.CONTRACT.DSEN_P * Math.exp(Math.log(membersCount) / conf.stepMax));
+      } else {
+        dSen = Math.ceil(Math.pow(membersCount, 1 / conf.stepMax));
+      }
+      let isOutdistanced = wotb.isOutdistanced(nodeID, dSen, conf.stepMax, conf.xpercent);
+      if (isOutdistanced) {
+        error = Error('Joiner/Active is outdistanced from WoT');
+        break;
+      }
+    }
+    // Undo temp links/nodes
+    tempLinks.forEach((link) => wotb.removeLink(link.from, link.to));
+    newcomers.forEach(() => wotb.removeNode());
+    return error ? true : false;
+  });
+}
+
+function getNodeIDfromPubkey(nodesCache, pubkey, dal) {
+  return co(function *() {
+    let toNode = nodesCache[pubkey];
+    // Eventually cache the target nodeID
+    if (toNode === null || toNode === undefined) {
+      let idty = yield dal.getWrittenIdtyByPubkey(pubkey);
+      toNode = idty.wotb_id;
+      nodesCache[pubkey] = toNode;
+    }
+    return toNode;
+  });
+}
+
+function sigCheckRevoke(entry, dal, currency) {
+  return co(function*() {
+    try {
+      let pubkey = entry.pub, sig = entry.revocation;
+      let idty = yield dal.getWrittenIdtyByPubkey(pubkey);
+      if (!idty) {
+        throw Error("A pubkey who was never a member cannot be revoked");
+      }
+      if (idty.revoked) {
+        throw Error("A revoked identity cannot be revoked again");
+      }
+      let rawRevocation = rawer.getOfficialRevocation({
+        currency: currency,
+        issuer: idty.pubkey,
+        uid: idty.uid,
+        buid: idty.buid,
+        sig: idty.sig,
+        revocation: ''
+      });
+      let sigOK = keyring.verify(rawRevocation, sig, pubkey);
+      if (!sigOK) {
+        throw Error("Revocation signature must match");
+      }
+      return true;
+    } catch (e) {
+      return false;
+    }
+  });
+}
+
+
+
+function checkCertificationIsValid (block, cert, findIdtyFunc, conf, dal) {
+  return co(function *() {
+    if (block.number == 0 && cert.created_on != 0) {
+      throw Error('Number must be 0 for root block\'s certifications');
+    } else {
+      try {
+        let basedBlock = {
+          hash: constants.BLOCK.SPECIAL_HASH
+        };
+        if (block.number != 0) {
+          try {
+            basedBlock = yield dal.getBlock(cert.created_on);
+          } catch (e) {
+            throw Error('Certification based on an unexisting block');
+          }
+        }
+        let idty = yield findIdtyFunc(block, cert.to, dal);
+        let current = block.number == 0 ? null : yield dal.getCurrentBlockOrNull();
+        if (!idty) {
+          throw Error('Identity does not exist for certified');
+        }
+        else if (current && current.medianTime > basedBlock.medianTime + conf.sigValidity) {
+          throw Error('Certification has expired');
+        }
+        else if (cert.from == idty.pubkey)
+          throw Error('Rejected certification: certifying its own self-certification has no meaning');
+        else {
+          const buid = [cert.created_on, basedBlock.hash].join('-');
+          idty.currency = conf.currency;
+          const raw = rawer.getOfficialCertification(_.extend(idty, {
+            idty_issuer: idty.pubkey,
+            idty_uid: idty.uid,
+            idty_buid: idty.buid,
+            idty_sig: idty.sig,
+            issuer: cert.from,
+            buid: buid,
+            sig: ''
+          }));
+          const verified = keyring.verify(raw, cert.sig, cert.from);
+          if (!verified) {
+            throw Error('Wrong signature for certification');
+          }
+          return true;
+        }
+      } catch (e) {
+        return false;
+      }
+    }
+  });
+}
+
+function txSourceUnlock(ENTRY, source) {
+  const tx = ENTRY.txObj;
+  let sigResults = require('../rules/local_rules').HELPERS.getSigResult(tx, 'a');
+  let unlocksForCondition = [];
+  let unlockValues = ENTRY.unlock;
+  if (source.conditions) {
+    if (unlockValues) {
+      // Evaluate unlock values
+      let sp = unlockValues.split(' ');
+      for (const func of sp) {
+        let param = func.match(/\((.+)\)/)[1];
+        if (func.match(/SIG/)) {
+          let pubkey = tx.issuers[parseInt(param)];
+          if (!pubkey) {
+            return false;
+          }
+          unlocksForCondition.push({
+            pubkey: pubkey,
+            sigOK: sigResults.sigs[pubkey] && sigResults.sigs[pubkey].matching || false
+          });
+        } else {
+          // XHX
+          unlocksForCondition.push(param);
+        }
+      }
+    }
+    if (unlock(source.conditions, unlocksForCondition)) {
+      return true;
+    }
+  }
+  return false;
+}
diff --git a/app/lib/entity/block.js b/app/lib/entity/block.js
index edec7ccaceaacbda34f0ba9483841860fe6da846..760e96b41aa41c8b4cc689ffe7137ad5480e0658 100644
--- a/app/lib/entity/block.js
+++ b/app/lib/entity/block.js
@@ -162,33 +162,6 @@ function Block(json) {
     return found;
   };
 
-  this.isLeaving = (pubkey) => {
-    let i = 0;
-    let found = false;
-    while (!found && i < this.leavers.length) {
-      if (this.leavers[i].match(new RegExp('^' + pubkey)))
-        found = true;
-      i++;
-    }
-    while (!found && i < this.excluded.length) {
-      if (this.excluded[i].match(new RegExp('^' + pubkey)))
-        found = true;
-      i++;
-    }
-    return found;
-  };
-
-  this.isJoining = (pubkey) => {
-    let i = 0;
-    let found = false;
-    while (!found && i < this.joiners.length) {
-      if (this.joiners[i].match(new RegExp('^' + pubkey)))
-        found = true;
-      i++;
-    }
-    return found;
-  };
-
   this.getTransactions = () => {
     const transactions = [];
     const version = this.version;
@@ -206,7 +179,7 @@ function Block(json) {
           base:       this.version >= 3 ? sp[1] : null,
           type:       this.version >= 3 ? sp[2] : sp[0],
           identifier: this.version >= 3 ? sp[3] : sp[1],
-          noffset:    this.version >= 3 ? parseInt(sp[4]) : parseInt(sp[2]),
+          pos:        this.version >= 3 ? parseInt(sp[4]) : parseInt(sp[2]),
           raw: input
         });
       });
@@ -245,3 +218,32 @@ Block.statics.getLen = (block) => block.identities.length +
     block.revoked.length +
     block.certifications.length +
     block.transactions.reduce((sum, tx) => sum + Transaction.statics.getLen(tx), 0);
+
+Block.statics.getHash = (block) => {
+  const entity = Block.statics.fromJSON(block);
+  return entity.getHash();
+};
+
+Block.statics.getConf = (block) => {
+  const sp = block.parameters.split(':');
+  const bconf = {};
+  bconf.c = parseFloat(sp[0]);
+  bconf.dt = parseInt(sp[1]);
+  bconf.ud0 = parseInt(sp[2]);
+  bconf.sigPeriod = parseInt(sp[3]);
+  bconf.sigStock = parseInt(sp[4]);
+  bconf.sigWindow = parseInt(sp[5]);
+  bconf.sigValidity = parseInt(sp[6]);
+  bconf.sigQty = parseInt(sp[7]);
+  bconf.idtyWindow = parseInt(sp[8]);
+  bconf.msWindow = parseInt(sp[9]);
+  bconf.xpercent = parseFloat(sp[10]);
+  bconf.msValidity = parseInt(sp[11]);
+  bconf.stepMax = parseInt(sp[12]);
+  bconf.medianTimeBlocks = parseInt(sp[13]);
+  bconf.avgGenTime = parseInt(sp[14]);
+  bconf.dtDiffEval = parseInt(sp[15]);
+  bconf.blocksRot = parseInt(sp[16]);
+  bconf.percentRot = parseFloat(sp[17]);
+  return bconf;
+};
diff --git a/app/lib/entity/identity.js b/app/lib/entity/identity.js
index c2edb5c071abe482517094348c020d5bdf6cb064..d02c23ea3b19373508b5e93cf03ff8a2f979281c 100644
--- a/app/lib/entity/identity.js
+++ b/app/lib/entity/identity.js
@@ -7,12 +7,9 @@ const Identity = function(json) {
 
   this.revoked = false;
   this.revoked_on = null;
-  this.currentMSN = -1;
-  this.currentINN = -1;
   this.member = false;
   this.buid = '';
   this.kick = false;
-  this.leaving = false;
   this.wasMember = false;
   this.signed = [];
   this.certs = [];
diff --git a/app/lib/entity/link.js b/app/lib/entity/link.js
deleted file mode 100644
index e88061d008e2352e57c6464cec0669cba8b3319b..0000000000000000000000000000000000000000
--- a/app/lib/entity/link.js
+++ /dev/null
@@ -1,15 +0,0 @@
-"use strict";
-const _ = require('underscore');
-
-module.exports = Link;
-
-function Link(json) {
-
-  _(json || {}).keys().forEach((key) => {
-    let value = json[key];
-    if (key == "number") {
-      value = parseInt(value);
-    }
-    this[key] = value;
-  });
-}
\ No newline at end of file
diff --git a/app/lib/entity/source.js b/app/lib/entity/source.js
index 489832540eb9686ef29b4438e6f51196c5df2fad..a903563aca3432c74c3f850af77c9717be41e6b8 100644
--- a/app/lib/entity/source.js
+++ b/app/lib/entity/source.js
@@ -19,7 +19,7 @@ function Source(json) {
   this.json = function () {
     return {
       "type": this.type,
-      "noffset": this.noffset,
+      "noffset": this.pos,
       "identifier": this.identifier,
       "amount": this.amount,
       "base": this.base
diff --git a/app/lib/entity/transaction.js b/app/lib/entity/transaction.js
index e2b7d8fc4b50166e029cf9639a635c80540ef3d5..080d14eacd6f206d00712d45b24e00da90b92e08 100644
--- a/app/lib/entity/transaction.js
+++ b/app/lib/entity/transaction.js
@@ -63,7 +63,7 @@ let Transaction = function(obj, currency) {
         base:       this.version >= 3 ? sp[1] : null,
         type:       this.version >= 3 ? sp[2] : sp[0],
         identifier: this.version >= 3 ? sp[3] : sp[1],
-        noffset:    this.version >= 3 ? parseInt(sp[4]) : parseInt(sp[2]),
+        pos:        this.version >= 3 ? parseInt(sp[4]) : parseInt(sp[2]),
         raw: input
       });
     });
diff --git a/app/lib/rules/global_rules.js b/app/lib/rules/global_rules.js
index 814b0331f8fad327ce097c5e1638c795d5948a01..0cabed0a4e9b8ade7b31984b3e8a7e468bb54cae 100644
--- a/app/lib/rules/global_rules.js
+++ b/app/lib/rules/global_rules.js
@@ -6,6 +6,7 @@ const _              = require('underscore');
 const constants      = require('../constants');
 const keyring         = require('../crypto/keyring');
 const rawer          = require('../ucp/rawer');
+const indexer        = require('../dup/indexer');
 const Identity       = require('../entity/identity');
 const Membership     = require('../entity/membership');
 const Certification  = require('../entity/certification');
@@ -16,246 +17,9 @@ const local_rules    = require('./local_rules');
 
 let rules = {};
 
-rules.FUNCTIONS = {
-
-  checkVersion: (block, dal) => co(function *() {
-    let current = yield dal.getCurrentBlockOrNull();
-    if (current && current.version > 2 && block.version == 2) {
-      throw Error('`Version: 2` must follow another V2 block or be the root block');
-    }
-    else if (block.version > 2) {
-      if (current && current.version != (block.version - 1) && current.version != block.version) {
-        throw Error('`Version: ' + block.version + '` must follow another V' + block.version + ' block, a V' + (block.version - 1) + ' block or be the root block');
-      }
-    }
-    return true;
-  }),
-
-  checkBlockLength: (block, dal) => co(function *() {
-    if (block.len > 500) {
-      const maxSize = yield rules.HELPERS.getMaxBlockSize(dal);
-      if (block.len > maxSize) {
-        throw Error('Block size is too high');
-      }
-    } else {
-      // There is no problem with blocks <= 500 lines
-      return true;
-    }
-  }),
-
-  checkNumber: (block, dal) => co(function *() {
-    let current = yield dal.getCurrentBlockOrNull();
-    if (!current && block.number != 0) {
-      throw Error('Root block required first');
-    }
-    else if (current && block.number <= current.number) {
-      throw Error('Too late for this block');
-    }
-    else if (current && block.number > current.number + 1) {
-      throw Error('Too early for this block');
-    }
-    return true;
-  }),
-
-  checkPoWMin: (block, conf, dal) => co(function *() {
-    if (block.number > 0) {
-      let correctPowMin = yield getPoWMinFor(block.version, block.number, conf, dal);
-      if (block.powMin < correctPowMin) {
-        throw Error('PoWMin value must be incremented');
-      }
-      else if (correctPowMin < block.powMin) {
-        throw Error('PoWMin value must be decremented');
-      }
-    }
-    return true;
-  }),
-
-  checkProofOfWork: (block, conf, dal) => co(function *() {
-    // Compute exactly how much zeros are required for this block's issuer
-    let difficulty = yield getTrialLevel(block.version, block.issuer, conf, dal);
-    const remainder = difficulty % 16;
-    const nbZerosReq = Math.max(0, (difficulty - remainder) / 16);
-    const highMark = constants.PROOF_OF_WORK.UPPER_BOUND[remainder];
-    const powRegexp = new RegExp('^0{' + nbZerosReq + '}' + '[0-' + highMark + ']');
-    if (!block.hash.match(powRegexp)) {
-      const givenZeros = Math.max(0, Math.min(nbZerosReq, block.hash.match(/^0*/)[0].length));
-      const c = block.hash.substr(givenZeros, 1);
-      throw Error('Wrong proof-of-work level: given ' + givenZeros + ' zeros and \'' + c + '\', required was ' + nbZerosReq + ' zeros and an hexa char between [0-' + highMark + ']');
-    }
-    return true;
-  }),
-
-  checkUD: (block, conf, dal) => co(function *() {
-    let current = yield dal.getCurrentBlockOrNull();
-    let nextUD = yield rules.HELPERS.getNextUD(dal, conf, block.version, block.medianTime, current, block.membersCount);
-    if (!current && block.dividend) {
-      throw Error('Root block cannot have UniversalDividend field');
-    }
-    else if (current && nextUD && !block.dividend) {
-      throw Error('Block must have a UniversalDividend field');
-    }
-    else if (current && nextUD && block.dividend != nextUD.dividend) {
-      throw Error('UniversalDividend must be equal to ' + nextUD.dividend);
-    }
-    else if (current && !nextUD && block.dividend) {
-      throw Error('This block cannot have UniversalDividend');
-    }
-    else if (current && nextUD && block.unitbase != nextUD.unitbase) {
-      throw Error('UnitBase must be equal to ' + nextUD.unitbase);
-    }
-    else if (block.version > 2 && current && !nextUD && block.unitbase != current.unitbase) {
-      throw Error('UnitBase must be equal to previous unit base = ' + current.unitbase);
-    }
-    return true;
-  }),
-
-  checkPreviousHash: (block, dal) => co(function *() {
-    let current = yield dal.getCurrentBlockOrNull();
-    if (current && block.previousHash != current.hash) {
-      throw Error('PreviousHash not matching hash of current block');
-    }
-    return true;
-  }),
-
-  checkPreviousIssuer: (block, dal) => co(function *() {
-    let current = yield dal.getCurrentBlockOrNull();
-    if (current && block.previousIssuer != current.issuer) {
-      throw Error('PreviousIssuer not matching issuer of current block');
-    }
-    return true;
-  }),
-
-  checkMembersCountIsGood: (block, dal) => co(function *() {
-    let current = yield dal.getCurrentBlockOrNull();
-    const currentCount = current ? current.membersCount : 0;
-    const constiation = block.joiners.length - block.excluded.length;
-    if (block.membersCount != currentCount + constiation) {
-      throw Error('Wrong members count');
-    }
-    return true;
-  }),
-
-  checkIssuerIsMember: (block, dal) => co(function *() {
-    let isMember = block.number == 0 ? isLocalMember(block.issuer, block) : yield dal.isMember(block.issuer);
-    if (!isMember) {
-      throw Error('Issuer is not a member');
-    }
-    return true;
-  }),
-
-  checkDifferentIssuersCount: (block, conf, dal) => co(function *() {
-    if (block.version > 2) {
-      const isGood = block.issuersCount == (yield rules.HELPERS.getDifferentIssuers(dal));
-      if (!isGood) {
-        throw Error('DifferentIssuersCount is not correct');
-      }
-      return isGood;
-    }
-  }),
-
-  checkIssuersFrame: (block, conf, dal) => co(function *() {
-    if (block.version > 2) {
-      const isGood = block.issuersFrame == (yield rules.HELPERS.getIssuersFrame(dal));
-      if (!isGood) {
-        throw Error('IssuersFrame is not correct');
-      }
-      return isGood;
-    }
-  }),
-
-  checkIssuersFrameVar: (block, conf, dal) => co(function *() {
-    if (block.version > 2) {
-      const isGood = block.issuersFrameVar == (yield rules.HELPERS.getIssuersFrameVar(block, dal));
-      if (!isGood) {
-        throw Error('IssuersFrameVar is not correct');
-      }
-      return isGood;
-    }
-  }),
-
-  checkTimes: (block, conf, dal) => co(function *() {
-    if (block.number > 0) {
-      let medianTime = yield getMedianTime(block.number, conf, dal);
-      if (medianTime != block.medianTime) {
-        throw Error('Wrong MedianTime');
-      }
-    }
-    return true;
-  }),
-
-  checkCertificationsAreMadeByMembers: (block, dal) => co(function *() {
-    for (const obj of block.certifications) {
-      let cert = Certification.statics.fromInline(obj);
-      let isMember = yield isAMember(cert.from, block, dal);
-      if (!isMember) {
-        throw Error('Certification from non-member');
-      }
-    }
-    return true;
-  }),
-
-  checkCertificationsAreValid: (block, conf, dal) => co(function *() {
-    for (const obj of block.certifications) {
-      let cert = Certification.statics.fromInline(obj);
-      yield checkCertificationIsValid(block, cert, (b, pub) => {
-        return getGlobalIdentity(b, pub, dal);
-      }, conf, dal);
-    }
-    return true;
-  }),
-
-  checkCertificationsAreMadeToMembers: (block, dal) => co(function *() {
-    for (const obj of block.certifications) {
-      let cert = Certification.statics.fromInline(obj);
-      let isMember = yield isMemberOrJoiner(cert.to, block, dal);
-      if (!isMember) {
-        throw Error('Certification to non-member');
-      }
-    }
-    return true;
-  }),
-
-  checkCertificationsAreMadeToNonLeaver: (block, dal) => co(function *() {
-    for (const obj of block.certifications) {
-      let cert = Certification.statics.fromInline(obj);
-      let isLeaving = yield dal.isLeaving(cert.to);
-      if (isLeaving) {
-        throw Error('Certification to leaver');
-      }
-    }
-    return true;
-  }),
-
-  checkCertificationsDelayIsRespected: (block, conf, dal) => co(function *() {
-    for (const obj of block.certifications) {
-      let cert = Certification.statics.fromInline(obj);
-      let previous = yield dal.getPreviousLinks(cert.from, cert.to);
-      let duration = previous && (block.medianTime - parseInt(previous.timestamp));
-      if (previous && (duration <= conf.sigValidity)) {
-        throw Error('A similar certification is already active');
-      }
-    }
-    return true;
-  }),
+// TODO: all the global rules should be replaced by index rule someday
 
-  checkCertificationsPeriodIsRespected: (block, conf, dal) => co(function *() {
-    let current = yield dal.getCurrentBlockOrNull();
-    for (const obj of block.certifications) {
-      let cert = Certification.statics.fromInline(obj);
-      let previous = yield dal.getLastValidFrom(cert.from);
-      if (previous) {
-        let duration = current.medianTime - parseInt(previous.timestamp);
-        if (duration < conf.sigPeriod) {
-          throw Error('Previous certification is not chainable yet');
-        }
-        let stock = yield dal.getValidLinksFrom(cert.from);
-        if (stock >= conf.sigStock) {
-          throw Error('Previous certification is not chainable yet');
-        }
-      }
-    }
-    return true;
-  }),
+rules.FUNCTIONS = {
 
   checkIdentitiesAreWritable: (block, conf, dal) => co(function *() {
     let current = yield dal.getCurrentBlockOrNull();
@@ -286,197 +50,6 @@ rules.FUNCTIONS = {
     return true;
   }),
 
-  checkCertificationsAreWritable: (block, conf, dal) => co(function *() {
-    let current = yield dal.getCurrentBlockOrNull();
-    for (const obj of block.certifications) {
-      let cert = Certification.statics.fromInline(obj);
-      if (current) {
-        // Because the window rule does not apply on initial certifications
-        let basedBlock = yield dal.getBlock(cert.block_number);
-        // Check if writable
-        let duration = current.medianTime - parseInt(basedBlock.medianTime);
-        if (duration > conf.sigWindow) {
-          throw Error('Certification is too old and cannot be written');
-        }
-      }
-    }
-    return true;
-  }),
-
-  checkMembershipsAreWritable: (block, conf, dal) => co(function *() {
-    let current = yield dal.getCurrentBlockOrNull();
-    let fields = ['joiners', 'actives', 'leavers'];
-    for (const field of fields) {
-      for (const obj of block[field]) {
-        let ms = Membership.statics.fromInline(obj);
-        if (ms.block != constants.BLOCK.SPECIAL_BLOCK) {
-          let msBasedBlock = yield dal.getBlock(ms.block);
-          let age = current.medianTime - msBasedBlock.medianTime;
-          if (age > conf.msWindow) {
-            throw 'Too old membership';
-          }
-        }
-      }
-    }
-    return true;
-  }),
-
-  checkIdentityUnicity: (block, conf, dal) => co(function *() {
-    for (const obj of block.identities) {
-      let idty = Identity.statics.fromInline(obj);
-      let found = yield dal.getWrittenIdtyByUID(idty.uid);
-      if (found) {
-        throw Error('Identity already used');
-      }
-    }
-    return true;
-  }),
-
-  checkPubkeyUnicity: (block, conf, dal) => co(function *() {
-    for (const obj of block.identities) {
-      let idty = Identity.statics.fromInline(obj);
-      let found = yield dal.getWrittenIdtyByPubkey(idty.pubkey);
-      if (found) {
-        throw Error('Pubkey already used');
-      }
-    }
-    return true;
-  }),
-
-  checkJoiners: (block, conf, dal) => co(function *() {
-    for (const obj of block.joiners) {
-      let ms = Membership.statics.fromInline(obj);
-      yield checkMSTarget(ms, block, conf, dal);
-      let idty = yield dal.getWrittenIdtyByPubkey(ms.issuer);
-      if (idty && idty.currentMSN != -1 && idty.currentMSN >= ms.number) {
-        throw Error('Membership\'s number must be greater than last membership of the pubkey');
-      }
-      if (idty && idty.member) {
-        throw Error('Cannot be in joiners if already a member');
-      }
-    }
-    return true;
-  }),
-
-  checkActives: (block, conf, dal) => co(function *() {
-    for (const obj of block.actives) {
-      let ms = Membership.statics.fromInline(obj);
-      yield checkMSTarget(ms, block, conf, dal);
-      let idty = yield dal.getWrittenIdtyByPubkey(ms.issuer);
-      if (idty && idty.currentMSN != -1 && idty.currentMSN >= ms.number) {
-        throw Error('Membership\'s number must be greater than last membership of the pubkey');
-      }
-      if (!idty || !idty.member) {
-        throw Error('Cannot be in actives if not a member');
-      }
-    }
-    return true;
-  }),
-
-  checkLeavers: (block, conf, dal) => co(function *() {
-    for (const obj of block.leavers) {
-      let ms = Membership.statics.fromInline(obj);
-      yield checkMSTarget(ms, block, conf, dal);
-      let idty = yield dal.getWrittenIdtyByPubkey(ms.issuer);
-      if (idty && idty.currentMSN != -1 && idty.currentMSN >= ms.number) {
-        throw Error('Membership\'s number must be greater than last membership of the pubkey');
-      }
-      if (!idty || !idty.member) {
-        throw Error('Cannot be in leavers if not a member');
-      }
-    }
-    return true;
-  }),
-
-  checkRevoked: (block, conf, dal) => co(function *() {
-    for (const revoked of block.revoked) {
-      let sp = revoked.split(':');
-      let pubkey = sp[0], sig = sp[1];
-      let idty = yield dal.getWrittenIdtyByPubkey(pubkey);
-      if (!idty) {
-        throw Error("A pubkey who was never a member cannot be revoked");
-      }
-      if (idty.revoked) {
-        throw Error("A revoked identity cannot be revoked again");
-      }
-      let rawRevocation = rawer.getOfficialRevocation({
-        currency: block.currency,
-        issuer: idty.pubkey,
-        uid: idty.uid,
-        buid: idty.buid,
-        sig: idty.sig,
-        revocation: ''
-      });
-      let sigOK = keyring.verify(rawRevocation, sig, pubkey);
-      if (!sigOK) {
-        throw Error("Revocation signature must match");
-      }
-    }
-    return true;
-  }),
-
-  checkExcluded: (block, conf, dal) => co(function *() {
-    for (const pubkey of block.excluded) {
-      let idty = yield dal.getWrittenIdtyByPubkey(pubkey);
-      if (!idty) {
-        throw Error("Cannot be in excluded if not a member");
-      }
-    }
-    return true;
-  }),
-
-  checkJoinersHaveEnoughCertifications: (block, conf, dal) => co(function *() {
-    if (block.number > 0) {
-      const newLinks = getNewLinks(block);
-      for (const obj of block.joiners) {
-        let ms = Membership.statics.fromInline(obj);
-        let links = yield dal.getValidLinksTo(ms.issuer);
-        let nbCerts = links.length + (newLinks[ms.issuer] || []).length;
-        if (nbCerts < conf.sigQty) {
-          throw Error('Joiner/Active does not gathers enough certifications');
-        }
-      }
-    }
-    return true;
-  }),
-
-  checkJoinersAreNotOudistanced: (block, conf, dal) => checkPeopleAreNotOudistanced(
-    block.version,
-    block.joiners.map((inlineMS) => Membership.statics.fromInline(inlineMS).issuer),
-    getNewLinks(block),
-    block.identities.map((inline) => Identity.statics.fromInline(inline).pubkey),
-    conf, dal),
-
-  checkActivesAreNotOudistanced: (block, conf, dal) => checkPeopleAreNotOudistanced(
-    block.version,
-    block.actives.map((inlineMS) => Membership.statics.fromInline(inlineMS).issuer),
-    getNewLinks(block),
-    block.identities.map((inline) => Identity.statics.fromInline(inline).pubkey),
-    conf, dal),
-
-  checkKickedMembersAreExcluded: (block, conf, dal) => co(function *() {
-    let identities = yield dal.getToBeKicked();
-    let remainingKeys = identities.map(function (idty) {
-      return idty.pubkey;
-    });
-    remainingKeys = _(remainingKeys).difference(block.excluded);
-    if (remainingKeys.length > 0) {
-      throw Error('All kicked members must be present under Excluded members')
-    }
-    return true;
-  }),
-
-  checkJoinersAreNotRevoked: (block, conf, dal) => co(function *() {
-    for (const obj of block.joiners) {
-      let ms = Membership.statics.fromInline(obj);
-      let idty = yield dal.getWrittenIdtyByPubkey(ms.issuer);
-      if (idty && idty.revoked) {
-        throw Error('Revoked pubkeys cannot join');
-      }
-    }
-    return true;
-  }),
-
   checkSourcesAvailability: (block, conf, dal, alsoCheckPendingTransactions) => co(function *() {
     let txs = block.getTransactions();
     const current = yield dal.getCurrentBlockOrNull();
@@ -491,8 +64,8 @@ rules.FUNCTIONS = {
       }
       for (let k = 0, len2 = tx.inputs.length; k < len2; k++) {
         let src = tx.inputs[k];
-        let dbSrc = yield dal.getSource(src.identifier, src.noffset);
-        logger.debug('Source %s:%s = %s', src.identifier, src.noffset, dbSrc && dbSrc.consumed);
+        let dbSrc = yield dal.getSource(src.identifier, src.pos);
+        logger.debug('Source %s:%s = %s', src.identifier, src.pos, dbSrc && dbSrc.consumed);
         if (!dbSrc && alsoCheckPendingTransactions) {
           // For chained transactions which are checked on sandbox submission, we accept them if there is already
           // a previous transaction of the chain already recorded in the pool
@@ -500,7 +73,7 @@ rules.FUNCTIONS = {
             let hypotheticSrc = null;
             let targetTX = yield dal.getTxByHash(src.identifier);
             if (targetTX) {
-              let outputStr = targetTX.outputs[src.noffset];
+              let outputStr = targetTX.outputs[src.pos];
               if (outputStr) {
                 hypotheticSrc = Transaction.statics.outputStr2Obj(outputStr);
                 hypotheticSrc.consumed = false;
@@ -511,11 +84,11 @@ rules.FUNCTIONS = {
           });
         }
         if (!dbSrc || dbSrc.consumed) {
-          logger.warn('Source ' + [src.type, src.identifier, src.noffset].join(':') + ' is not available');
+          logger.warn('Source ' + [src.type, src.identifier, src.pos].join(':') + ' is not available');
           throw constants.ERRORS.SOURCE_ALREADY_CONSUMED;
         }
         sumOfInputs += dbSrc.amount * Math.pow(10, dbSrc.base);
-        if (block.medianTime - dbSrc.time < tx.locktime) {
+        if (block.medianTime - dbSrc.written_time < tx.locktime) {
           throw constants.ERRORS.LOCKTIME_PREVENT;
         }
         let sigResults = local_rules.HELPERS.getSigResult(tx);
@@ -547,7 +120,7 @@ rules.FUNCTIONS = {
               throw Error('Locked');
             }
           } catch (e) {
-            logger.warn('Source ' + [src.type, src.identifier, src.noffset].join(':') + ' unlock fail');
+            logger.warn('Source ' + [src.type, src.identifier, src.pos].join(':') + ' unlock fail');
             throw constants.ERRORS.WRONG_UNLOCKER;
           }
         }
@@ -564,13 +137,6 @@ rules.FUNCTIONS = {
       }
     }
     return true;
-  }),
-
-  checkTransactionsBlockStamp: (block, conf, dal) => co(function *() {
-    for(const tx of block.getTransactions()) {
-      yield rules.HELPERS.checkTxBlockStamp(tx, dal);
-    }
-    return true;
   })
 };
 
@@ -588,19 +154,12 @@ rules.HELPERS = {
       return Q(false);
     }
     try {
-      yield checkPeopleAreNotOudistanced(version, [member], newLinks, newcomers, conf, dal);
-      return false;
+      return indexer.DUP_HELPERS.checkPeopleAreNotOudistanced(version, [member], newLinks, newcomers, conf, dal);
     } catch (e) {
       return true;
     }
   }),
 
-  getTrialLevel: (version, issuer, conf, dal) => getTrialLevel(version, issuer, conf, dal),
-
-  getPoWMin: (version, blockNumber, conf, dal) => getPoWMinFor(version, blockNumber, conf, dal),
-
-  getMedianTime: (blockNumber, conf, dal) => getMedianTime(blockNumber, conf, dal),
-
   checkExistsUserID: (uid, dal) => dal.getWrittenIdtyByUID(uid),
 
   checkExistsPubkey: (pub, dal) => dal.getWrittenIdtyByPubkey(pub),
@@ -610,64 +169,6 @@ rules.HELPERS = {
     medianTime: block.medianTime
   }, conf, dal, alsoCheckPendingTransactions),
 
-  getNextUD: (dal, conf, version, nextMedianTime, current, nextN) => co(function *() {
-    const lastUDBlock = yield dal.lastUDBlock();
-    let lastUDTime = lastUDBlock && lastUDBlock.UDTime;
-    if (!lastUDTime) {
-      const rootBlock = yield dal.getBlock(0);
-      lastUDTime = (rootBlock != null ? rootBlock.medianTime : 0);
-    }
-    if (lastUDTime == null) {
-      return null;
-    }
-    if (!current) {
-      return null;
-    }
-    if (lastUDTime + conf.dt <= nextMedianTime) {
-      const M = lastUDBlock ? lastUDBlock.monetaryMass : current.monetaryMass || 0;
-      const c = conf.c;
-      const N = nextN;
-      const previousUD = lastUDBlock ? lastUDBlock.dividend : conf.ud0;
-      const previousUB = lastUDBlock ? lastUDBlock.unitbase : constants.FIRST_UNIT_BASE;
-      if (version == 2) {
-        if (N > 0) {
-          const block = {
-            dividend: Math.ceil(Math.max(previousUD, c * M / Math.pow(10, previousUB) / N)),
-            unitbase: previousUB
-          };
-          if (block.dividend >= Math.pow(10, constants.NB_DIGITS_UD)) {
-            block.dividend = Math.ceil(block.dividend / 10.0);
-            block.unitbase++;
-          }
-          return block;
-        } else {
-          // The community has collapsed. RIP.
-          return null;
-        }
-      } else {
-        const block = {
-          unitbase: previousUB
-        };
-        if (version == 3) {
-          block.dividend = parseInt(((1 + c) * previousUD).toFixed(0));
-        } else {
-          if (N > 0) {
-            block.dividend = parseInt((previousUD + Math.pow(c, 2) * (M / N) / Math.pow(10, previousUB)).toFixed(0));
-          } else {
-            // The community has collapsed. RIP.
-            return null;
-          }
-        }
-        if (block.dividend >= Math.pow(10, constants.NB_DIGITS_UD)) {
-          block.dividend = Math.ceil(block.dividend / 10.0);
-          block.unitbase++;
-        }
-        return block;
-      }
-    }
-    return null;
-  }),
-
   checkTxBlockStamp: (tx, dal) => co(function *() {
     if (tx.version >= 3) {
       const number = tx.blockstamp.split('-')[0];
@@ -683,81 +184,6 @@ rules.HELPERS = {
         throw "Transaction has expired";
       }
     }
-  }),
-
-  getDifferentIssuers: (dal) => co(function *() {
-    const current = yield dal.getCurrentBlockOrNull();
-    let frameSize = 0;
-    let currentNumber = 0;
-    if (current) {
-      currentNumber = current.number;
-      if (current.version == 2) {
-        frameSize = 40;
-      } else {
-        frameSize = current.issuersFrame;
-      }
-    }
-    const blocksBetween = yield dal.getBlocksBetween(Math.max(0, currentNumber - frameSize + 1), currentNumber);
-    const issuers = _.pluck(blocksBetween, 'issuer');
-    return _.uniq(issuers).length;
-  }),
-
-  getIssuersFrame: (dal) => co(function *() {
-    const current = yield dal.getCurrentBlockOrNull();
-    let frame = 1;
-    if (!current) {
-      frame = 1;
-    }
-    else {
-      if (current.version == 2) {
-        frame = 40;
-      }
-      else if (current.version > 2) {
-        frame = current.issuersFrame;
-        // CONVERGENCE
-        if (current.issuersFrameVar > 0) {
-          frame++;
-        }
-        if (current.issuersFrameVar < 0) {
-          frame--;
-        }
-      }
-    }
-    return frame;
-  }),
-
-  getIssuersFrameVar: (block, dal) => co(function *() {
-    const current = yield dal.getCurrentBlockOrNull();
-    let frameVar = 0;
-    if (current && current.version > 2) {
-      frameVar = current.issuersFrameVar;
-      // CONVERGENCE
-      if (current.issuersFrameVar > 0) {
-        frameVar--;
-      }
-      if (current.issuersFrameVar < 0) {
-        frameVar++;
-      }
-      // NEW_ISSUER_INC
-      if (current.issuersCount < block.issuersCount) {
-        frameVar += 5;
-      }
-      // GONE_ISSUER_DEC
-      if (current.issuersCount > block.issuersCount) {
-        frameVar -= 5;
-      }
-    }
-    return frameVar;
-  }),
-
-  getMaxBlockSize: (dal) => co(function *() {
-    const current = yield dal.getCurrentBlockOrNull();
-    const start = current ? current.number - current.issuersCount : 0;
-    const end = current ? current.number : 0;
-    const blocks = yield dal.getBlocksBetween(start, end);
-    const avgSize = blocks.length ? blocks.reduce((lenSum, b) => lenSum + b.len, 0) / blocks.length : 0;
-    const maxSize = Math.ceil(1.1 * avgSize);
-    return Math.max(500, maxSize);
   })
 };
 
@@ -767,37 +193,6 @@ rules.HELPERS = {
  *
  *****************************/
 
-/**
- * Get an identity, using global scope.
- * Considers identity collision + existence have already been checked.
- **/
-function getGlobalIdentity (block, pubkey, dal) {
-  return co(function *() {
-    let localInlineIdty = block.getInlineIdentity(pubkey);
-    if (localInlineIdty) {
-      return Identity.statics.fromInline(localInlineIdty);
-    }
-    return dal.getWrittenIdtyByPubkey(pubkey);
-  });
-}
-
-function isAMember(pubkey, block, dal) {
-  if (block.number == 0) {
-    return Q(isLocalMember(pubkey, block));
-  } else {
-    return dal.isMember(pubkey);
-  }
-}
-
-function isMemberOrJoiner(pubkey, block, dal) {
-  let isJoiner = isLocalMember(pubkey, block);
-  return isJoiner ? Q(isJoiner) : dal.isMember(pubkey);
-}
-
-function isLocalMember(pubkey, block) {
-  return block.isJoining(pubkey);
-}
-
 function checkMSTarget (ms, block, conf, dal) {
   return co(function *() {
     if (block.number == 0 && ms.number != 0) {
@@ -807,7 +202,7 @@ function checkMSTarget (ms, block, conf, dal) {
       throw Error('Hash must be E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 for root block\'s memberships');
     }
     else if (block.number == 0) {
-      return true; // Valid for root block
+      return null; // Valid for root block
     } else {
       let basedBlock;
       try {
@@ -819,7 +214,7 @@ function checkMSTarget (ms, block, conf, dal) {
       if (current && current.medianTime > basedBlock.medianTime + conf.msValidity) {
         throw Error('Membership has expired');
       }
-      return true;
+      return basedBlock;
     }
   });
 }
@@ -873,314 +268,4 @@ function checkCertificationIsValid (block, cert, findIdtyFunc, conf, dal) {
   });
 }
 
-function checkPeopleAreNotOudistanced (version, pubkeys, newLinks, newcomers, conf, dal) {
-  return co(function *() {
-    let wotb = dal.wotb;
-    let current = yield dal.getCurrentBlockOrNull();
-    let membersCount = current ? current.membersCount : 0;
-    // TODO: make a temporary copy of the WoT in RAM
-    // We add temporarily the newcomers to the WoT, to integrate their new links
-    let nodesCache = newcomers.reduce((map, pubkey) => {
-      let nodeID = wotb.addNode();
-      map[pubkey] = nodeID;
-      wotb.setEnabled(false, nodeID); // These are not members yet
-      return map;
-    }, {});
-    // Add temporarily the links to the WoT
-    let tempLinks = [];
-    let toKeys = _.keys(newLinks);
-    for (const toKey of toKeys) {
-      let toNode = yield getNodeIDfromPubkey(nodesCache, toKey, dal);
-      for (const fromKey of newLinks[toKey]) {
-        let fromNode = yield getNodeIDfromPubkey(nodesCache, fromKey, dal);
-        tempLinks.push({ from: fromNode, to: toNode });
-      }
-    }
-    tempLinks.forEach((link) => wotb.addLink(link.from, link.to));
-    // Checking distance of each member against them
-    let error;
-    for (const pubkey of pubkeys) {
-      let nodeID = yield getNodeIDfromPubkey(nodesCache, pubkey, dal);
-      let dSen;
-      if (version <= 3) {
-        dSen = Math.ceil(constants.CONTRACT.DSEN_P * Math.exp(Math.log(membersCount) / conf.stepMax));
-      } else {
-        dSen = Math.ceil(Math.pow(membersCount, 1 / conf.stepMax));
-      }
-      let isOutdistanced = wotb.isOutdistanced(nodeID, dSen, conf.stepMax, conf.xpercent);
-      if (isOutdistanced) {
-        error = Error('Joiner/Active is outdistanced from WoT');
-        break;
-      }
-    }
-    // Undo temp links/nodes
-    tempLinks.forEach((link) => wotb.removeLink(link.from, link.to));
-    newcomers.forEach(() => wotb.removeNode());
-    if (error) {
-      throw error;
-    }
-  });
-}
-
-function getNodeIDfromPubkey(nodesCache, pubkey, dal) {
-  return co(function *() {
-    let toNode = nodesCache[pubkey];
-    // Eventually cache the target nodeID
-    if (toNode === null || toNode === undefined) {
-      let idty = yield dal.getWrittenIdtyByPubkey(pubkey);
-      toNode = idty.wotb_id;
-      nodesCache[pubkey] = toNode;
-    }
-    return toNode;
-  });
-}
-
-function getTrialLevel (version, issuer, conf, dal) {
-  return co(function *() {
-    if (version == 2) {
-      // Compute exactly how much zeros are required for this block's issuer
-      let percentRot = conf.percentRot;
-      let current = yield dal.getCurrentBlockOrNull();
-      if (!current) {
-        return conf.powMin || 0;
-      }
-      let last = yield dal.lastBlockOfIssuer(issuer);
-      let powMin = yield getPoWMinFor(version, current.number + 1, conf, dal);
-      let issuers = [];
-      if (last) {
-        let blocksBetween = yield dal.getBlocksBetween(last.number - 1 - conf.blocksRot, last.number - 1);
-        issuers = _.pluck(blocksBetween, 'issuer');
-      } else {
-        // So we can have nbPreviousIssuers = 0 & nbBlocksSince = 0 for someone who has never written any block
-        last = { number: current.number };
-      }
-      const nbPreviousIssuers = _(_(issuers).uniq()).without(issuer).length;
-      const nbBlocksSince = current.number - last.number;
-      let personal_diff = Math.max(powMin, powMin * Math.floor(percentRot * (1 + nbPreviousIssuers) / (1 + nbBlocksSince)));
-      if (personal_diff + 1 % 16 == 0) {
-        personal_diff++;
-      }
-      return personal_diff;
-    } else if (version > 2 && version < 5) {
-      // Compute exactly how much zeros are required for this block's issuer
-      let percentRot = conf.percentRot;
-      let current = yield dal.getCurrentBlockOrNull();
-      if (!current) {
-        return conf.powMin || 0;
-      }
-      let last = yield dal.lastBlockOfIssuer(issuer);
-      let powMin = yield getPoWMinFor(version, current.number + 1, conf, dal);
-      let nbPreviousIssuers = 0;
-      if (last) {
-        nbPreviousIssuers = last.issuersCount;
-      } else {
-        // So we have nbBlocksSince = 0 for someone who has never written any block
-        last = { number: current.number };
-      }
-      const nbBlocksSince = current.number - last.number;
-      let personal_diff = Math.max(powMin, powMin * Math.floor(percentRot * nbPreviousIssuers / (1 + nbBlocksSince)));
-      if (version > 3) {
-        const from = current.number - current.issuersFrame + 1;
-        const nbBlocksIssuedInFrame = yield dal.getNbIssuedInFrame(issuer, from);
-        const personal_excess = Math.max(0, (nbBlocksIssuedInFrame / 5) - 1);
-        // Personal_handicap
-        personal_diff += Math.floor(Math.log(1 + personal_excess) / Math.log(1.189));
-      }
-      if (personal_diff + 1 % 16 == 0) {
-        personal_diff++;
-      }
-      return personal_diff;
-    } else if (version == 5) {
-      // NB: no more use conf.percentRot
-      // Compute exactly how much zeros are required for this block's issuer
-      let current = yield dal.getCurrentBlockOrNull();
-      if (!current) {
-        return conf.powMin || 0;
-      }
-      let powMin = yield getPoWMinFor(version, current.number + 1, conf, dal);
-      let blocksBetween = [];
-      if (current) {
-        blocksBetween = yield dal.getBlocksBetween(current.number - current.issuersFrame + 1, current.number);
-      }
-      const blocksByIssuer = blocksBetween.reduce((oMap, block) => {
-        oMap[block.issuer] = oMap[block.issuer] || 0;
-        oMap[block.issuer]++;
-        return oMap;
-      }, {});
-      const counts = Object.values(blocksByIssuer);
-      let medianOfIssuedBlocks = null;
-      counts.sort((a, b) => a < b ? -1 : (a > b ? 1 : 0));
-      const nbIssuers = counts.length;
-      if (nbIssuers % 2 === 0) {
-        // Even number of nodes: the median is the average between the 2 central values
-        const firstValue = counts[nbIssuers / 2];
-        const secondValue = counts[nbIssuers / 2 - 1];
-        medianOfIssuedBlocks = (firstValue + secondValue) / 2;
-      } else {
-        medianOfIssuedBlocks = counts[(nbIssuers + 1) / 2 - 1];
-      }
-
-      const from = current.number - current.issuersFrame + 1;
-      const nbBlocksIssuedInFrame = yield dal.getNbIssuedInFrame(issuer, from);
-      const personal_excess = Math.max(0, ((nbBlocksIssuedInFrame + 1)/ medianOfIssuedBlocks) - 1);
-      // Personal_handicap
-      const handicap = Math.floor(Math.log(1 + personal_excess) / Math.log(1.189));
-      let personal_diff = powMin + handicap;
-      if (personal_diff + 1 % 16 == 0) {
-        personal_diff++;
-      }
-      return personal_diff;
-    } else if (version >= 6) {
-      // NB: no more use conf.percentRot
-      // Compute exactly how much zeros are required for this block's issuer
-      let current = yield dal.getCurrentBlockOrNull();
-      if (!current) {
-        return conf.powMin || 0;
-      }
-      let powMin = yield getPoWMinFor(version, current.number + 1, conf, dal);
-      let blocksBetween = [];
-      if (current) {
-        blocksBetween = yield dal.getBlocksBetween(current.number - current.issuersFrame + 1, current.number);
-      }
-      const blocksByIssuer = blocksBetween.reduce((oMap, block) => {
-        oMap[block.issuer] = oMap[block.issuer] || 0;
-        oMap[block.issuer]++;
-        return oMap;
-      }, {});
-      const counts = Object.values(blocksByIssuer);
-      let medianOfIssuedBlocks = null;
-      counts.sort((a, b) => a < b ? -1 : (a > b ? 1 : 0));
-      const nbIssuers = counts.length;
-      if (nbIssuers % 2 === 0) {
-        // Even number of nodes: the median is the average between the 2 central values
-        const firstValue = counts[nbIssuers / 2];
-        const secondValue = counts[nbIssuers / 2 - 1];
-        medianOfIssuedBlocks = (firstValue + secondValue) / 2;
-      } else {
-        medianOfIssuedBlocks = counts[(nbIssuers + 1) / 2 - 1];
-      }
-
-      const from = current.number - current.issuersFrame + 1;
-      const nbBlocksIssuedInFrame = yield dal.getNbIssuedInFrame(issuer, from);
-      const personal_excess = Math.max(0, ((nbBlocksIssuedInFrame + 1)/ medianOfIssuedBlocks) - 1);
-      // Personal_handicap
-      const handicap = Math.floor(Math.log(1 + personal_excess) / Math.log(1.189));
-
-      //-- 0.4 reintroduction
-      let percentRot = conf.percentRot;
-      let last = yield dal.lastBlockOfIssuer(issuer);
-      let nbPreviousIssuers = 0;
-      if (last) {
-        nbPreviousIssuers = last.issuersCount;
-      } else {
-        // So we have nbBlocksSince = 0 for someone who has never written any block
-        last = { number: current.number };
-      }
-      const nbBlocksSince = current.number - last.number;
-
-      let personal_diff = Math.max(powMin, powMin * Math.floor(percentRot * nbPreviousIssuers / (1 + nbBlocksSince))) + handicap;
-      if ((personal_diff + 1) % 16 == 0) {
-        personal_diff++;
-      }
-      return personal_diff;
-    }
-  });
-}
-
-/**
- * Deduce the PoWMin field for a given block number
- */
-function getPoWMinFor (blockVersion, blockNumber, conf, dal) {
-  return Q.Promise(function(resolve, reject){
-    if (blockNumber == 0) {
-      reject('Cannot deduce PoWMin for block#0');
-    } else if (blockNumber % conf.dtDiffEval != 0) {
-      co(function *() {
-        const previous = yield dal.getBlock(blockNumber - 1);
-        return previous.powMin;
-      })
-        .then(resolve)
-        .catch(function(err) {
-          reject(err);
-          throw err;
-        });
-    } else {
-      co(function *() {
-        const previous = yield dal.getBlock(blockNumber - 1);
-        const medianTime = yield getMedianTime(blockNumber, conf, dal);
-        const speedRange = Math.min(conf.dtDiffEval, blockNumber);
-        const lastDistant = yield dal.getBlock(Math.max(0, blockNumber - speedRange));
-        // Compute PoWMin value
-        const duration = medianTime - lastDistant.medianTime;
-        const speed = speedRange / duration;
-        const ratio = blockVersion > 3 ? constants.POW_DIFFICULTY_RANGE_RATIO_V4 : constants.POW_DIFFICULTY_RANGE_RATIO_V3;
-        const maxGenTime = Math.ceil(conf.avgGenTime * ratio);
-        const minGenTime = Math.floor(conf.avgGenTime / ratio);
-        const maxSpeed = 1.0 / minGenTime;
-        const minSpeed = 1.0 / maxGenTime;
-        // logger.debug('Current speed is', speed, '(' + conf.dtDiffEval + '/' + duration + ')', 'and must be [', minSpeed, ';', maxSpeed, ']');
-        if (speed >= maxSpeed) {
-          // Must increase difficulty
-          if ((previous.powMin + 2) % 16 == 0) {
-            // Avoid (16*n - 1) value
-            resolve(previous.powMin + 2);
-          } else {
-            resolve(previous.powMin + 1);
-          }
-        }
-        else if (speed <= minSpeed) {
-          // Must decrease difficulty
-          if (previous.powMin % 16 == 0) {
-            // Avoid (16*n - 1) value
-            resolve(Math.max(0, previous.powMin - 2));
-          } else {
-            resolve(Math.max(0, previous.powMin - 1));
-          }
-        }
-        else {
-          // Must not change difficulty
-          resolve(previous.powMin);
-        }
-      })
-        .catch(reject);
-    }
-  });
-}
-
-function getMedianTime (blockNumber, conf, dal) {
-  return co(function *() {
-    if (blockNumber == 0) {
-      // No rule to check for block#0
-      return 0;
-    }
-    // Get the number of blocks we can look back from this block
-    let blocksCount = blockNumber < conf.medianTimeBlocks ? blockNumber : conf.medianTimeBlocks;
-    // Get their 'time' value
-    // console.log('Times between ', blockNumber - blocksCount, blockNumber - 1);
-    let blocksBetween = yield dal.getBlocksBetween(blockNumber - blocksCount, blockNumber - 1);
-    let timeValues = _.pluck(blocksBetween, 'time');
-    timeValues.sort();
-    let sum = 0;
-    for (const timeValue of timeValues) {
-      sum += timeValue;
-    }
-    if (timeValues.length) {
-      return Math.floor(sum / timeValues.length);
-    }
-    else {
-      throw Error('No block found for MedianTime comparison');
-    }
-  });
-}
-
-function getNewLinks (block) {
-  const newLinks = {};
-  block.certifications.forEach(function(inlineCert){
-    const cert = Certification.statics.fromInline(inlineCert);
-    newLinks[cert.to] = newLinks[cert.to] || [];
-    newLinks[cert.to].push(cert.from);
-  });
-  return newLinks;
-}
-
 module.exports = rules;
diff --git a/app/lib/rules/index.js b/app/lib/rules/index.js
index f458d235b4d9b990c6318bd5e7aa918ac56eb189..2ca727c61c53339dac37200643bdba4083590260 100644
--- a/app/lib/rules/index.js
+++ b/app/lib/rules/index.js
@@ -29,17 +29,17 @@ rules.ALIAS = {
     yield rules.LOCAL.checkBlockSignature(block);
     yield rules.LOCAL.checkBlockTimes(block, conf);
     yield rules.LOCAL.checkIdentitiesSignature(block);
-    yield rules.LOCAL.checkIdentitiesUserIDConflict(block);
-    yield rules.LOCAL.checkIdentitiesPubkeyConflict(block);
-    yield rules.LOCAL.checkIdentitiesMatchJoin(block);
-    yield rules.LOCAL.checkRevokedNotInMemberships(block);
-    yield rules.LOCAL.checkRevokedUnicity(block);
-    yield rules.LOCAL.checkRevokedAreExcluded(block);
+    yield rules.LOCAL.checkIdentitiesUserIDConflict(block, conf);
+    yield rules.LOCAL.checkIdentitiesPubkeyConflict(block, conf);
+    yield rules.LOCAL.checkIdentitiesMatchJoin(block, conf);
+    yield rules.LOCAL.checkMembershipUnicity(block, conf);
+    yield rules.LOCAL.checkRevokedUnicity(block, conf);
+    yield rules.LOCAL.checkRevokedAreExcluded(block, conf);
     yield rules.LOCAL.checkMembershipsSignature(block);
     yield rules.LOCAL.checkPubkeyUnicity(block);
-    yield rules.LOCAL.checkCertificationOneByIssuer(block);
-    yield rules.LOCAL.checkCertificationUnicity(block);
-    yield rules.LOCAL.checkCertificationIsntForLeaverOrExcluded(block);
+    yield rules.LOCAL.checkCertificationOneByIssuer(block, conf);
+    yield rules.LOCAL.checkCertificationUnicity(block, conf);
+    yield rules.LOCAL.checkCertificationIsntForLeaverOrExcluded(block, conf);
     yield rules.LOCAL.checkTxVersion(block);
     yield rules.LOCAL.checkTxIssuers(block);
     yield rules.LOCAL.checkTxSources(block);
@@ -57,102 +57,23 @@ rules.ALIAS = {
     yield rules.LOCAL.checkUnitBase(block);
     yield rules.LOCAL.checkBlockTimes(block, conf);
     yield rules.LOCAL.checkIdentitiesSignature(block);
-    yield rules.LOCAL.checkIdentitiesUserIDConflict(block);
-    yield rules.LOCAL.checkIdentitiesPubkeyConflict(block);
-    yield rules.LOCAL.checkIdentitiesMatchJoin(block);
-    yield rules.LOCAL.checkRevokedNotInMemberships(block);
-    yield rules.LOCAL.checkRevokedUnicity(block);
-    yield rules.LOCAL.checkRevokedAreExcluded(block);
+    yield rules.LOCAL.checkIdentitiesUserIDConflict(block, conf);
+    yield rules.LOCAL.checkIdentitiesPubkeyConflict(block, conf);
+    yield rules.LOCAL.checkIdentitiesMatchJoin(block, conf);
+    yield rules.LOCAL.checkMembershipUnicity(block, conf);
+    yield rules.LOCAL.checkRevokedUnicity(block, conf);
+    yield rules.LOCAL.checkRevokedAreExcluded(block, conf);
     yield rules.LOCAL.checkMembershipsSignature(block);
     yield rules.LOCAL.checkPubkeyUnicity(block);
-    yield rules.LOCAL.checkCertificationOneByIssuer(block);
-    yield rules.LOCAL.checkCertificationUnicity(block);
-    yield rules.LOCAL.checkCertificationIsntForLeaverOrExcluded(block);
+    yield rules.LOCAL.checkCertificationOneByIssuer(block, conf);
+    yield rules.LOCAL.checkCertificationUnicity(block, conf);
+    yield rules.LOCAL.checkCertificationIsntForLeaverOrExcluded(block, conf);
     yield rules.LOCAL.checkTxVersion(block);
     yield rules.LOCAL.checkTxIssuers(block);
     yield rules.LOCAL.checkTxSources(block);
     yield rules.LOCAL.checkTxRecipients(block);
     yield rules.LOCAL.checkTxAmounts(block);
     yield rules.LOCAL.checkTxSignature(block);
-  }),
-
-  ALL_GLOBAL: (block, conf, dal) => co(function *() {
-    yield rules.GLOBAL.checkNumber(block, dal);
-    yield rules.GLOBAL.checkVersion(block, dal);
-    yield rules.GLOBAL.checkBlockLength(block, dal);
-    yield rules.GLOBAL.checkPreviousHash(block, dal);
-    yield rules.GLOBAL.checkPreviousIssuer(block, dal);
-    yield rules.GLOBAL.checkIssuerIsMember(block, dal);
-    yield rules.GLOBAL.checkIssuersFrame(block, conf, dal);
-    yield rules.GLOBAL.checkIssuersFrameVar(block, conf, dal);
-    yield rules.GLOBAL.checkDifferentIssuersCount(block, conf, dal);
-    yield rules.GLOBAL.checkTimes(block, conf, dal);
-    yield rules.GLOBAL.checkIdentityUnicity(block, conf, dal);
-    yield rules.GLOBAL.checkPubkeyUnicity(block, conf, dal);
-    yield rules.GLOBAL.checkIdentitiesAreWritable(block, conf, dal);
-    yield rules.GLOBAL.checkMembershipsAreWritable(block, conf, dal);
-    yield rules.GLOBAL.checkJoiners(block, conf, dal);
-    yield rules.GLOBAL.checkJoinersHaveEnoughCertifications(block, conf, dal);
-    yield rules.GLOBAL.checkJoinersAreNotOudistanced(block, conf, dal);
-    yield rules.GLOBAL.checkActives(block, conf, dal);
-    yield rules.GLOBAL.checkActivesAreNotOudistanced(block, conf, dal);
-    yield rules.GLOBAL.checkLeavers(block, conf, dal);
-    yield rules.GLOBAL.checkRevoked(block, conf, dal);
-    yield rules.GLOBAL.checkJoinersAreNotRevoked(block, conf, dal);
-    yield rules.GLOBAL.checkExcluded(block, conf, dal);
-    yield rules.GLOBAL.checkKickedMembersAreExcluded(block, conf, dal);
-    yield rules.GLOBAL.checkCertificationsAreWritable(block, conf, dal);
-    yield rules.GLOBAL.checkCertificationsAreMadeByMembers(block, dal);
-    yield rules.GLOBAL.checkCertificationsAreValid(block, conf, dal);
-    yield rules.GLOBAL.checkCertificationsAreMadeToMembers(block, dal);
-    yield rules.GLOBAL.checkCertificationsAreMadeToNonLeaver(block, dal);
-    yield rules.GLOBAL.checkCertificationsDelayIsRespected(block, conf, dal);
-    yield rules.GLOBAL.checkCertificationsPeriodIsRespected(block, conf, dal);
-    yield rules.GLOBAL.checkMembersCountIsGood(block, dal);
-    yield rules.GLOBAL.checkPoWMin(block, conf, dal);
-    yield rules.GLOBAL.checkProofOfWork(block, conf, dal);
-    yield rules.GLOBAL.checkUD(block, conf, dal);
-    yield rules.GLOBAL.checkTransactionsBlockStamp(block, conf, dal);
-    yield rules.GLOBAL.checkSourcesAvailability(block, conf, dal);
-  }),
-
-  ALL_GLOBAL_WITHOUT_POW: (block, conf, dal) => co(function *() {
-    yield rules.GLOBAL.checkNumber(block, dal);
-    yield rules.GLOBAL.checkVersion(block, dal);
-    yield rules.GLOBAL.checkBlockLength(block, dal);
-    yield rules.GLOBAL.checkPreviousHash(block, dal);
-    yield rules.GLOBAL.checkPreviousIssuer(block, dal);
-    yield rules.GLOBAL.checkIssuerIsMember(block, dal);
-    yield rules.GLOBAL.checkIssuersFrame(block, conf, dal);
-    yield rules.GLOBAL.checkIssuersFrameVar(block, conf, dal);
-    yield rules.GLOBAL.checkDifferentIssuersCount(block, conf, dal);
-    yield rules.GLOBAL.checkTimes(block, conf, dal);
-    yield rules.GLOBAL.checkIdentityUnicity(block, conf, dal);
-    yield rules.GLOBAL.checkPubkeyUnicity(block, conf, dal);
-    yield rules.GLOBAL.checkIdentitiesAreWritable(block, conf, dal);
-    yield rules.GLOBAL.checkMembershipsAreWritable(block, conf, dal);
-    yield rules.GLOBAL.checkJoiners(block, conf, dal);
-    yield rules.GLOBAL.checkJoinersHaveEnoughCertifications(block, conf, dal);
-    yield rules.GLOBAL.checkJoinersAreNotOudistanced(block, conf, dal);
-    yield rules.GLOBAL.checkActives(block, conf, dal);
-    yield rules.GLOBAL.checkActivesAreNotOudistanced(block, conf, dal);
-    yield rules.GLOBAL.checkLeavers(block, conf, dal);
-    yield rules.GLOBAL.checkRevoked(block, conf, dal);
-    yield rules.GLOBAL.checkJoinersAreNotRevoked(block, conf, dal);
-    yield rules.GLOBAL.checkExcluded(block, conf, dal);
-    yield rules.GLOBAL.checkKickedMembersAreExcluded(block, conf, dal);
-    yield rules.GLOBAL.checkCertificationsAreWritable(block, conf, dal);
-    yield rules.GLOBAL.checkCertificationsAreMadeByMembers(block, dal);
-    yield rules.GLOBAL.checkCertificationsAreValid(block, conf, dal);
-    yield rules.GLOBAL.checkCertificationsAreMadeToMembers(block, dal);
-    yield rules.GLOBAL.checkCertificationsAreMadeToNonLeaver(block, dal);
-    yield rules.GLOBAL.checkCertificationsDelayIsRespected(block, conf, dal);
-    yield rules.GLOBAL.checkCertificationsPeriodIsRespected(block, conf, dal);
-    yield rules.GLOBAL.checkMembersCountIsGood(block, dal);
-    yield rules.GLOBAL.checkPoWMin(block, conf, dal);
-    yield rules.GLOBAL.checkUD(block, conf, dal);
-    yield rules.GLOBAL.checkTransactionsBlockStamp(block, conf, dal);
-    yield rules.GLOBAL.checkSourcesAvailability(block, conf, dal);
   })
 };
 
@@ -160,8 +81,6 @@ rules.CHECK = {
   ASYNC: {
     ALL_LOCAL: checkLocal(rules.ALIAS.ALL_LOCAL),
     ALL_LOCAL_BUT_POW: checkLocal(rules.ALIAS.ALL_LOCAL_BUT_POW_AND_SIGNATURE),
-    ALL_GLOBAL: check(rules.ALIAS.ALL_GLOBAL),
-    ALL_GLOBAL_BUT_POW: check(rules.ALIAS.ALL_GLOBAL_WITHOUT_POW)
   }
 };
 
@@ -180,10 +99,10 @@ function checkLocal(contract) {
 }
 
 function check(contract) {
-  return (b, conf, dal, done) => {
+  return (b, conf, dal, bcContext, done) => {
     return co(function *() {
       const block = new Block(b);
-      yield contract(block, conf, dal);
+      yield contract(block, conf, dal, bcContext);
       done && done();
     })
       .catch((err) => {
diff --git a/app/lib/rules/local_rules.js b/app/lib/rules/local_rules.js
index 0e1dee584b57d763f1abf51db20ec48d34ed423e..7cf955c22e006546eb692c8ea0c4c6191a8f6498 100644
--- a/app/lib/rules/local_rules.js
+++ b/app/lib/rules/local_rules.js
@@ -1,7 +1,9 @@
 "use strict";
 
 const co         = require('co');
+const _          = require('underscore');
 const constants  = require('../constants');
+const indexer    = require('../dup/indexer');
 const hashf      = require('../ucp/hashf');
 const keyring    = require('../crypto/keyring');
 const rawer      = require('../ucp/rawer');
@@ -11,6 +13,8 @@ const Transaction = require('../entity/transaction');
 
 let rules = {};
 
+// TODO: make a global variable 'index' instead of recomputing the index each time
+
 rules.FUNCTIONS = {
 
   checkVersion: (block) => co(function*() {
@@ -114,113 +118,71 @@ rules.FUNCTIONS = {
     return true;
   }),
 
-  checkIdentitiesUserIDConflict: (block) => co(function *() {
-    const uids = [];
-    let i = 0;
-    let conflict = false;
-    while (!conflict && i < block.identities.length) {
-      const uid = block.identities[i].split(':')[3];
-      conflict = ~uids.indexOf(uid);
-      uids.push(uid);
-      i++;
-    }
-    if (conflict) {
+  checkIdentitiesUserIDConflict: (block, conf) => co(function *() {
+    const index = indexer.localIndex(block, conf);
+    const creates = indexer.iindexCreate(index);
+    const uids = _.chain(creates).pluck('uid').uniq().value();
+    if (creates.length !== uids.length) {
       throw Error('Block must not contain twice same identity uid');
     }
     return true;
   }),
 
-  checkIdentitiesPubkeyConflict: (block) => co(function *() {
-    const pubkeys = [];
-    let i = 0;
-    let conflict = false;
-    while (!conflict && i < block.identities.length) {
-      const pubk = block.identities[i].split(':')[0];
-      conflict = ~pubkeys.indexOf(pubk);
-      pubkeys.push(pubk);
-      i++;
-    }
-    if (conflict) {
+  checkIdentitiesPubkeyConflict: (block, conf) => co(function *() {
+    const index = indexer.localIndex(block, conf);
+    const creates = indexer.iindexCreate(index);
+    const pubkeys = _.chain(creates).pluck('pub').uniq().value();
+    if (creates.length !== pubkeys.length) {
       throw Error('Block must not contain twice same identity pubkey');
     }
     return true;
   }),
 
-  checkIdentitiesMatchJoin: (block) => co(function *() {
-    // N.B.: this function does not test for potential duplicates in
-    // identities and/or joiners, this is another test responsibility
-    const pubkeys = [];
-    block.identities.forEach(function(inline){
-      let sp = inline.split(':');
-      let pubk = sp[0], ts = sp[2], uid = sp[3];
-      pubkeys.push([pubk, uid, ts].join('-'));
-    });
-    let matchCount = 0;
-    let i = 0;
-    while (i < block.joiners.length) {
-      let sp = block.joiners[i].split(':');
-      let pubk = sp[0], ts = sp[3], uid = sp[4];
-      let idty = [pubk, uid, ts].join('-');
-      if (~pubkeys.indexOf(idty)) matchCount++;
-      i++;
-    }
-    let problem = matchCount != pubkeys.length;
-    if (problem) {
-      throw Error('Each identity must match a newcomer line with same userid and certts');
+  checkIdentitiesMatchJoin: (block, conf) => co(function *() {
+    const index = indexer.localIndex(block, conf);
+    const icreates = indexer.iindexCreate(index);
+    const mcreates = indexer.mindexCreate(index);
+    for (const icreate of icreates) {
+      const matching = _(mcreates).filter({ pub: icreate.pub });
+      if (matching.length == 0) {
+        throw Error('Each identity must match a newcomer line with same userid and certts');
+      }
     }
     return true;
   }),
 
-  checkRevokedAreExcluded: (block) => co(function *() {
-    // N.B.: this function does not test for potential duplicates in Revoked,
-    // this is another test responsability
-    const pubkeys = [];
-    block.revoked.forEach(function(inline){
-      let sp = inline.split(':');
-      let pubk = sp[0];
-      pubkeys.push(pubk);
-    });
-    let matchCount = 0;
-    let i = 0;
-    while (i < block.excluded.length) {
-      if (~pubkeys.indexOf(block.excluded[i])) matchCount++;
-      i++;
-    }
-    let problem = matchCount != pubkeys.length;
-    if (problem) {
-      throw Error('A revoked member must be excluded');
+  checkRevokedAreExcluded: (block, conf) => co(function *() {
+    const index = indexer.localIndex(block, conf);
+    const iindex = indexer.iindex(index);
+    const mindex = indexer.mindex(index);
+    const revocations = _.chain(mindex)
+      .filter((row) => row.op == constants.IDX_UPDATE && row.revoked_on !== null)
+      .pluck('pub')
+      .value();
+    for (const pub of revocations) {
+      const exclusions = _(iindex).where({ op: constants.IDX_UPDATE, member: false });
+      if (exclusions.length == 0) {
+        throw Error('A revoked member must be excluded');
+      }
     }
     return true;
   }),
 
-  checkRevokedUnicity: (block) => co(function *() {
-    let pubkeys = [];
-    let conflict = false;
-    let i = 0;
-    while (!conflict && i < block.revoked.length) {
-      let pubk = block.revoked[i].split(':')[0];
-      conflict = ~pubkeys.indexOf(pubk);
-      pubkeys.push(pubk);
-      i++;
-    }
-    if (conflict) {
+  checkRevokedUnicity: (block, conf) => co(function *() {
+    try {
+      yield rules.FUNCTIONS.checkMembershipUnicity(block, conf);
+    } catch (e) {
       throw Error('A single revocation per member is allowed');
     }
     return true;
   }),
 
-  checkRevokedNotInMemberships: (block) => co(function *() {
-    let i = 0;
-    let conflict = false;
-    while (!conflict && i < block.revoked.length) {
-      let pubk = block.revoked[i].split(':')[0];
-      conflict = existsPubkeyIn(pubk, block.joiners)
-        || existsPubkeyIn(pubk, block.actives)
-        || existsPubkeyIn(pubk, block.leavers);
-      i++;
-    }
-    if (conflict) {
-      throw Error('A revoked pubkey cannot have a membership in the same block');
+  checkMembershipUnicity: (block, conf) => co(function *() {
+    const index = indexer.localIndex(block, conf);
+    const mindex = indexer.mindex(index);
+    const pubkeys = _.chain(mindex).pluck('pub').uniq().value();
+    if (pubkeys.length !== mindex.length) {
+      throw Error('Unicity constraint PUBLIC_KEY on MINDEX is not respected');
     }
     return true;
   }),
@@ -296,60 +258,40 @@ rules.FUNCTIONS = {
     return true;
   }),
 
-  checkCertificationOneByIssuer: (block) => co(function *() {
-    let conflict = false;
+  checkCertificationOneByIssuer: (block, conf) => co(function *() {
     if (block.number > 0) {
-      const issuers = [];
-      let i = 0;
-      while (!conflict && i < block.certifications.length) {
-        const issuer = block.certifications[i].split(':')[0];
-        conflict = ~issuers.indexOf(issuer);
-        issuers.push(issuer);
-        i++;
+      const index = indexer.localIndex(block, conf);
+      const cindex = indexer.cindex(index);
+      const certFromA = _.uniq(cindex.map((row) => row.issuer));
+      if (certFromA.length !== cindex.length) {
+        throw Error('Block cannot contain two certifications from same issuer');
       }
     }
-    if (conflict) {
-      throw Error('Block cannot contain two certifications from same issuer');
-    }
     return true;
   }),
 
-  checkCertificationUnicity: (block) => co(function *() {
-    const certs = [];
-    let i = 0;
-    let conflict = false;
-    while (!conflict && i < block.certifications.length) {
-      const cert = block.certifications[i].split(':').slice(0,2).join(':');
-      conflict = ~certs.indexOf(cert);
-      certs.push(cert);
-      i++;
-    }
-    if (conflict) {
+  checkCertificationUnicity: (block, conf) => co(function *() {
+    const index = indexer.localIndex(block, conf);
+    const cindex = indexer.cindex(index);
+    const certAtoB = _.uniq(cindex.map((row) => row.issuer + row.receiver));
+    if (certAtoB.length !== cindex.length) {
       throw Error('Block cannot contain identical certifications (A -> B)');
     }
     return true;
   }),
 
-  checkCertificationIsntForLeaverOrExcluded: (block) => co(function *() {
-    const pubkeys = [];
-    block.leavers.forEach(function(leaver){
-      const pubk = leaver.split(':')[0];
-      pubkeys.push(pubk);
-    });
-    block.excluded.forEach(function(excluded){
-      pubkeys.push(excluded);
-    });
-    // Certifications
-    let conflict = false;
-    let i = 0;
-    while (!conflict && i < block.certifications.length) {
-      const sp = block.certifications[i].split(':');
-      const pubkFrom = sp[0], pubkTo = sp[1];
-      conflict = ~pubkeys.indexOf(pubkFrom) || ~pubkeys.indexOf(pubkTo);
-      i++;
-    }
-    if (conflict) {
-      throw Error('Block cannot contain certifications concerning leavers or excluded members');
+  checkCertificationIsntForLeaverOrExcluded: (block, conf) => co(function *() {
+    const index = indexer.localIndex(block, conf);
+    const cindex = indexer.cindex(index);
+    const iindex = indexer.iindex(index);
+    const mindex = indexer.mindex(index);
+    const certified = cindex.map((row) => row.receiver);
+    for (const pub of certified) {
+      const exclusions = _(iindex).where({ op: constants.IDX_UPDATE, member: false, pub: pub });
+      const leavers    = _(mindex).where({ op: constants.IDX_UPDATE, leaving: true, pub: pub });
+      if (exclusions.length > 0 || leavers.length > 0) {
+        throw Error('Block cannot contain certifications concerning leavers or excluded members');
+      }
     }
     return true;
   }),
@@ -392,26 +334,20 @@ rules.FUNCTIONS = {
     return true;
   }),
 
-  checkTxSources: (block) => co(function *() {
+  checkTxSources: (block, conf) => co(function *() {
     const txs = block.getTransactions();
-    const sources = [];
-    let i = 0;
-    let existsIdenticalSource = false;
-    while (!existsIdenticalSource && i < txs.length) {
-      const tx = txs[i];
+    for (const tx of txs) {
       if (!tx.inputs || tx.inputs.length == 0) {
         throw Error('A transaction must have at least 1 source');
       }
-      tx.inputs.forEach(function (input) {
-        if (~sources.indexOf(input.raw)) {
-          existsIdenticalSource = true;
-        } else {
-          sources.push(input.raw);
-        }
-      });
-      i++;
     }
-    if (existsIdenticalSource) {
+    const sindex = indexer.localSIndex(block);
+    const inputs = _.filter(sindex, (row) => row.op == constants.IDX_UPDATE).map((row) => [row.op, row.identifier, row.pos].join('-'));
+    if (inputs.length !== _.uniq(inputs).length) {
+      throw Error('It cannot exist 2 identical sources for transactions inside a given block');
+    }
+    const outputs = _.filter(sindex, (row) => row.op == constants.IDX_CREATE).map((row) => [row.op, row.identifier, row.pos].join('-'));
+    if (outputs.length !== _.uniq(outputs).length) {
       throw Error('It cannot exist 2 identical sources for transactions inside a given block');
     }
     return true;
@@ -465,22 +401,11 @@ function maxAcceleration (block, conf) {
   }
 }
 
-function existsPubkeyIn(pubk, memberships) {
-  let i = 0;
-  let conflict = false;
-  while (!conflict && i < memberships.length) {
-    let pubk2 = memberships[i].split(':')[0];
-    conflict = pubk == pubk2;
-    i++;
-  }
-  return conflict;
-}
-
 function checkSingleMembershipSignature(ms) {
   return keyring.verify(ms.getRaw(), ms.signature, ms.issuer);
 }
 
-function getSigResult(tx) {
+function getSigResult(tx, a) {
   let sigResult = { sigs: {}, matching: true };
   let json = { "version": tx.version, "currency": tx.currency, "blockstamp": tx.blockstamp, "locktime": tx.locktime, "inputs": [], "outputs": [], "issuers": tx.issuers, "signatures": [], "comment": tx.comment };
   tx.inputs.forEach(function (input) {
diff --git a/app/lib/sync.js b/app/lib/sync.js
index af174d632e5979af7e9fa9c9847021aaf2e88b22..815674238d9690c4b37c51592b5f3b45b52b2637 100644
--- a/app/lib/sync.js
+++ b/app/lib/sync.js
@@ -7,10 +7,12 @@ const _            = require('underscore');
 const moment       = require('moment');
 const contacter    = require('./contacter');
 const hashf        = require('./ucp/hashf');
+const indexer      = require('./dup/indexer');
 const dos2unix     = require('./system/dos2unix');
 const logger       = require('./logger')('sync');
 const rawer        = require('./ucp/rawer');
 const constants    = require('../lib/constants');
+const Block        = require('../lib/entity/block');
 const Peer         = require('../lib/entity/peer');
 const multimeter   = require('multimeter');
 const pulling      = require('../lib/pulling');
@@ -163,12 +165,22 @@ function Synchroniser (server, host, port, conf, interactive) {
       logger.info('Downloading Blockchain...');
 
       // We use cautious mode if it is asked, or not particulary asked but blockchain has been started
-      const cautious = (askedCautious === true || (askedCautious === undefined && localNumber >= 0));
+      const cautious = (askedCautious === true || localNumber >= 0);
       const downloader = new P2PDownloader(localNumber, to, rCurrent.hash, CONST_MAX_SIMULTANEOUS_DOWNLOADS, peers, watcher);
 
       downloader.start();
 
       let lastPullBlock = null;
+
+      let bindex = [];
+      let iindex = [];
+      let mindex = [];
+      let cindex = [];
+      let sindex = [];
+      let currConf = {};
+      let bindexSize = 0;
+      let allBlocks = [];
+
       let dao = pulling.abstractDao({
 
         // Get the local blockchain current block
@@ -206,10 +218,103 @@ function Synchroniser (server, host, port, conf, interactive) {
         applyBranch: (blocks) => co(function *() {
           if (cautious) {
             for (const block of blocks) {
+              if (block.number == 0) {
+                yield BlockchainService.saveParametersForRootBlock(block);
+                currConf = Block.statics.getConf(block);
+              }
               yield dao.applyMainBranch(block);
             }
           } else {
-            yield server.BlockchainService.saveBlocksInMainBranch(blocks);
+            const ctx = BlockchainService.getContext();
+            let blocksToSave = [];
+
+            for (const block of blocks) {
+              allBlocks.push(block);
+
+              if (block.number == 0) {
+                currConf = Block.statics.getConf(block);
+              }
+
+              if (block.number != to) {
+                blocksToSave.push(block);
+                const index = indexer.localIndex(block, currConf);
+                const local_iindex = indexer.iindex(index);
+                const local_cindex = indexer.cindex(index);
+                iindex = iindex.concat(local_iindex);
+                cindex = cindex.concat(local_cindex);
+                sindex = sindex.concat(indexer.sindex(index));
+                mindex = mindex.concat(indexer.mindex(index));
+                const HEAD = yield indexer.quickCompleteGlobalScope(block, currConf, bindex, iindex, mindex, cindex, sindex, {
+                  getBlock: (number) => {
+                    return Promise.resolve(allBlocks[number - 1]);
+                  },
+                  getBlockByBlockstamp: (blockstamp) => {
+                    return Promise.resolve(allBlocks[parseInt(blockstamp) - 1]);
+                  }
+                });
+                bindex.push(HEAD);
+
+                yield ctx.createNewcomers(local_iindex);
+
+                if (block.dividend
+                  || block.joiners.length
+                  || block.actives.length
+                  || block.revoked.length
+                  || block.excluded.length
+                  || block.certifications.length) {
+                  // Flush the INDEX (not bindex, which is particular)
+                  yield dal.mindexDAL.insertBatch(mindex);
+                  yield dal.iindexDAL.insertBatch(iindex);
+                  yield dal.sindexDAL.insertBatch(sindex);
+                  yield dal.cindexDAL.insertBatch(cindex);
+                  mindex = [];
+                  iindex = [];
+                  cindex = [];
+                  sindex = yield indexer.ruleIndexGenDividend(HEAD, dal);
+
+                  // Create/Update nodes in wotb
+                  yield ctx.updateMembers(block);
+
+                  // --> Update links
+                  yield dal.updateWotbLinks(local_cindex);
+                }
+
+                // Trim the bindex
+                bindexSize = [
+                  block.issuersCount,
+                  block.issuersFrame,
+                  conf.medianTimeBlocks,
+                  conf.dtDiffEval,
+                  CONST_BLOCKS_CHUNK
+                ].reduce((max, value) => {
+                  return Math.max(max, value);
+                }, 0);
+
+                if (bindexSize && bindex.length >= 2 * bindexSize) {
+                  // We trim it, not necessary to store it all (we already store the full blocks)
+                  bindex.splice(0, bindexSize);
+                }
+              } else {
+
+                if (blocksToSave.length) {
+                  yield server.BlockchainService.saveBlocksInMainBranch(blocksToSave);
+                }
+                blocksToSave = [];
+
+                // Save the INDEX
+                yield dal.bindexDAL.insertBatch(bindex);
+                yield dal.mindexDAL.insertBatch(mindex);
+                yield dal.iindexDAL.insertBatch(iindex);
+                yield dal.sindexDAL.insertBatch(sindex);
+                yield dal.cindexDAL.insertBatch(cindex);
+
+                // Last block: cautious mode to trigger all the INDEX expiry mechanisms
+                yield dao.applyMainBranch(block);
+              }
+            }
+            if (blocksToSave.length) {
+              yield server.BlockchainService.saveBlocksInMainBranch(blocksToSave);
+            }
           }
           lastPullBlock = blocks[blocks.length - 1];
           watcher.appliedPercent(Math.floor(blocks[blocks.length - 1].number / to * 100));
diff --git a/app/service/BlockchainService.js b/app/service/BlockchainService.js
index 5dfed2d9d71b998990688ea1c54d3ba4bdbdb3fd..d2d7d0badaba1e08a99c33fbae173926522a83cb 100644
--- a/app/service/BlockchainService.js
+++ b/app/service/BlockchainService.js
@@ -33,6 +33,8 @@ function BlockchainService (server) {
   const generator = blockGenerator(mainContext, prover);
   let conf, dal, keyPair, logger, selfPubkey;
 
+  this.getContext = () => mainContext;
+
   this.setConfDAL = (newConf, newDAL, newKeyPair) => {
     dal = newDAL;
     conf = newConf;
@@ -268,7 +270,7 @@ function BlockchainService (server) {
   /**
    * Generates next block, finding newcomers, renewers, leavers, certs, transactions, etc.
    */
-  this.generateNext = () => generator.nextBlock();
+  this.generateNext = (params) => generator.nextBlock(params);
 
   this.requirementsOfIdentities = (identities) => co(function *() {
     let all = [];
@@ -308,9 +310,11 @@ function BlockchainService (server) {
       certs = yield that.getValidCerts(pubkey, newCerts);
       outdistanced = yield rules.HELPERS.isOver3Hops(currentVersion, pubkey, newLinks, someNewcomers, current, conf, dal);
       // Expiration of current membershship
-      if (join.identity.currentMSN >= 0) {
+      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(join.identity.currentMSN);
+          const msBlock = yield dal.getBlock(currentMSN);
           if (msBlock && msBlock.medianTime) { // special case for block #0
             expiresMS = Math.max(0, (msBlock.medianTime + conf.msValidity - currentTime));
           }
@@ -360,7 +364,7 @@ function BlockchainService (server) {
 
   this.getValidCerts = (newcomer, newCerts) => co(function *() {
     const links = yield dal.getValidLinksTo(newcomer);
-    const certsFromLinks = links.map((lnk) => { return { from: lnk.source, to: lnk.target, timestamp: lnk.timestamp }; });
+    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) {
@@ -409,64 +413,11 @@ function BlockchainService (server) {
       }
       return block;
     });
-    // Insert a bunch of blocks
-    const lastPrevious = blocks[0].number == 0 ? null : yield dal.getBlock(blocks[0].number - 1);
-    const dividends = [];
-    for (let i = 0; i < blocks.length; i++) {
-      const previous = i > 0 ? blocks[i - 1] : lastPrevious;
-      const block = blocks[i];
-      block.len = Block.statics.getLen(block);
+    for (const block of blocks) {
       block.fork = false;
-      // Monetary mass & UD Time recording before inserting elements
-      block.monetaryMass = (previous && previous.monetaryMass) || 0;
-      block.unitbase = (block.dividend && block.unitbase) || (previous && previous.unitbase) || 0;
-      block.dividend = block.dividend || null;
-      // UD Time update
-      const previousBlock = i > 0 ? blocks[i - 1] : lastPrevious;
-      if (block.number == 0) {
-        block.UDTime = block.medianTime; // Root = first UD time
-      }
-      else if (block.dividend) {
-        block.UDTime = conf.dt + previousBlock.UDTime;
-        block.monetaryMass += block.dividend * Math.pow(10, block.unitbase || 0) * block.membersCount;
-      } else {
-        block.UDTime = previousBlock.UDTime;
-      }
-      yield mainContext.updateMembers(block);
-
-      // Dividends
-      if (block.dividend) {
-        // Get the members at THAT moment (only them should have the UD)
-        let idties = yield dal.getMembers();
-        for (const idty of idties) {
-          dividends.push({
-            'pubkey': idty.pubkey,
-            'identifier': idty.pubkey,
-            'noffset': block.number,
-            'type': 'D',
-            'number': block.number,
-            'time': block.medianTime,
-            'fingerprint': block.hash,
-            'block_hash': block.hash,
-            'amount': block.dividend,
-            'base': block.unitbase,
-            'consumed': false,
-            'toConsume': false,
-            'conditions': 'SIG(' + idty.pubkey + ')' // Only this pubkey can unlock its UD
-          });
-        }
-      }
     }
     // Transactions recording
     yield mainContext.updateTransactionsForBlocks(blocks, getBlockByNumberAndHash);
-    // Create certifications
-    yield mainContext.updateMembershipsForBlocks(blocks);
-    // Create certifications
-    yield mainContext.updateLinksForBlocks(blocks, getBlock);
-    // Create certifications
-    yield mainContext.updateCertificationsForBlocks(blocks);
-    // Create / Update sources
-    yield mainContext.updateTransactionSourcesForBlocks(blocks, dividends);
     logger.debug(blocks[0].number);
     yield dal.blockDAL.saveBunch(blocks);
     yield pushStatsForBlocks(blocks);
@@ -494,15 +445,6 @@ function BlockchainService (server) {
     return dal.pushStats(stats);
   }
 
-  this.getCertificationsExludingBlock = () => co(function*() {
-    try {
-      const current = yield dal.getCurrentBlockOrNull();
-      return yield dal.getCertificationExcludingBlock(current, conf.sigValidity);
-    } catch (err) {
-        return { number: -1 };
-    }
-  });
-
   this.blocksBetween = (from, count) => co(function *() {
     if (count > 5000) {
       throw 'Count is too high';
diff --git a/app/service/IdentityService.js b/app/service/IdentityService.js
index 2e75582471ac88ee23c7163e49713f05e7df8c97..4d0d5c7e5f2bc9812c711c78e684bebb2a08775f 100644
--- a/app/service/IdentityService.js
+++ b/app/service/IdentityService.js
@@ -91,6 +91,15 @@ function IdentityService () {
           throw constants.ERRORS.UID_ALREADY_USED;
         }
         const current = yield 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);
+          if (!basedBlock) {
+            throw constants.ERRORS.BLOCKSTAMP_DOES_NOT_MATCH_A_BLOCK;
+          }
+          idty.expires_on = basedBlock.medianTime + conf.idtyWindow;
+        }
         yield rules.GLOBAL.checkIdentitiesAreWritable({ identities: [idty.inline()], version: (current && current.version) || constants.BLOCK_GENERATED_VERSION }, conf, dal);
         idty = new Identity(idty);
         if (byAbsorption === BY_ABSORPTION) {
@@ -140,6 +149,8 @@ function IdentityService () {
             number: 0,
             hash: 'E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855'
           };
+        } else {
+          cert.expires_on = basedBlock.medianTime + conf.sigWindow;
         }
         cert.block_hash = basedBlock.hash;
         const mCert = new Certification({
@@ -148,7 +159,8 @@ function IdentityService () {
           block_number: cert.block_number,
           block_hash: cert.block_hash,
           target: targetHash,
-          to: idty.pubkey
+          to: idty.pubkey,
+          expires_on: cert.expires_on
         });
         let existingCert = yield dal.existsCert(mCert);
         if (!existingCert) {
@@ -185,7 +197,7 @@ function IdentityService () {
         else if (existing.revocation_sig) {
           throw 'Revocation already registered';
         } else {
-          yield dal.setRevocating(obj.hash, revoc.revocation);
+          yield dal.setRevocating(existing, revoc.revocation);
           return jsonResultTrue();
         }
       }
diff --git a/app/service/MembershipService.js b/app/service/MembershipService.js
index 04ad95c41b39ab9e23a2860b5281805f27903d03..3eb0ccb488da0e550fcc2df9d2c458eada1f3911 100644
--- a/app/service/MembershipService.js
+++ b/app/service/MembershipService.js
@@ -35,9 +35,12 @@ function MembershipService () {
       throw constants.ERRORS.WRONG_SIGNATURE_MEMBERSHIP;
     }
     // Get already existing Membership with same parameters
-    const found = yield dal.getMembershipForHashAndIssuer(entry);
-    if (found) {
+    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';
@@ -46,7 +49,10 @@ function MembershipService () {
       throw constants.ERRORS.MEMBERSHIP_A_NON_MEMBER_CANNOT_LEAVE;
     }
     const current = yield dal.getCurrentBlockOrNull();
-    yield rules.HELPERS.checkMembershipBlock(entry, current, conf, dal);
+    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;
diff --git a/doc/Protocol.md b/doc/Protocol.md
index de8701262625e4a2ee328be69e1f33ce4af48ff2..4091e587c437136728396dbeca19800087161e8f 100644
--- a/doc/Protocol.md
+++ b/doc/Protocol.md
@@ -1,4 +1,4 @@
-# UCP - Duniter Protocol
+# DUP - Duniter Protocol
 
 > This document is still regularly updated (as of August 2016)
 
@@ -81,7 +81,7 @@ The block ID of the block#433 is `433`. Its UID *might be* `433-FB11681FC1B3E36C
 
 A valid currency name is composed of alphanumeric characters, spaces, `-` or `_` and has a length of 2 to 50 characters.
 
-#### Dates
+#### Datesœ
 
 For any document using a date field, targeted date is to be understood as **UTC+0** reference.
 
@@ -808,6 +808,17 @@ Excluded              | Exluded members' public key                       | Alwa
 Transactions          | A list of compact transactions                    | Always
 InnerHash             | The hash value of the block's inner content       | Always
 Nonce                 | An arbitrary nonce value                          | Always
+BlockHash             | Hash from `InnerHash: ` to `SIGNATURE`            | Virtual
+BlockSize             | Hash from `InnerHash: ` to `SIGNATURE`            | Virtual
+
+##### BlockSize
+
+The block size is defined as the number of lines in multiline fields (`Identities`, `Joiners`, `Actives`, `Leavers`, `Revoked`, `Certifications`, `Transactions`) **except** `Excluded` field.
+
+For example:
+
+* 1 new identity + 1 joiner + 2 certifications = 4 lines sized block
+* 1 new identity + 1 joiner + 2 certifications + 5 lines transaction = 9 lines sized block
 
 #### Coherence
 
@@ -959,6 +970,7 @@ avgGenTime  | The average time for writing 1 block (wished time)
 dtDiffEval  | The number of blocks required to evaluate again `PoWMin` value
 blocksRot   | The number of previous blocks to check for personalized difficulty
 percentRot  | The percent of previous issuers to reach for personalized difficulty
+txWindow    | `= 3600 * 24 * 7`. Maximum delay a transaction can wait before being expired for non-writing.
 
 ### Computed variables
 
@@ -967,9 +979,9 @@ Variable  | Meaning
 members   | Synonym of `members(t = now)`, `wot(t)`, `community(t)` targeting the keys whose last active (non-expired) membership is either in `Joiners` or `Actives`.
 maxGenTime  | `= CEIL(avgGenTime * 1.189)`
 minGenTime  | `= FLOOR(avgGenTime / 1.189)`
+minSpeed | 1 / maxGenTime
+maxSpeed | 1 / minGenTime
 maxAcceleration | `= CEIL(maxGenTime * medianTimeBlocks)`
-dSen | `= CEIL(membersCount ^ (1 / stepMax))`
-sentries | Members with at least `dSen` active links *from* them
 
 ## Processing
 
@@ -983,7 +995,11 @@ Local validation verifies the coherence of a well-formatted block, without any o
 
 ##### Version and Number
 
-If `Number` is `0`, then `Version` must be `2`.
+Rule:
+
+    HEAD.version >= 2
+    AND
+    HEAD.version <= 6
 
 ##### InnerHash
 
@@ -995,7 +1011,7 @@ If `Number` is `0`, then `Version` must be `2`.
 
 ##### Proof of work
 
-To be valid, a block proof-of-work (hash from `InnerHash: ` to `SIGNATURE`) must start with a specific number of zeros. Locally, this hash must start with at least `NB_ZEROS` zeros:
+To be valid, the BlockHash must start with a specific number of zeros. Locally, this hash must start with at least `NB_ZEROS` zeros:
 
     REMAINDER = PowMin % 16
     NB_ZEROS = (PowMin - REMAINDER) / 16
@@ -1015,6 +1031,10 @@ To be valid, a block proof-of-work (hash from `InnerHash: ` to `SIGNATURE`) must
 * `Parameters` must be present if the value of the `Number` field is equal to `0`.
 * `Parameters` must not be present if the value of the `Number` field is greater than `0`.
 
+##### Universal Dividend
+
+If HEAD.number == 0, HEAD.dividend must equal `null`.
+
 ##### UnitBase
 
 * Block V2:
@@ -1029,52 +1049,260 @@ To be valid, a block proof-of-work (hash from `InnerHash: ` to `SIGNATURE`) must
 
 ##### Dates
 
-* A block must have its `Time` field be between [`MedianTime` ; `MedianTime` + `accelerationMax`].
+* A block must have its `Time` field be between [`MedianTime` ; `MedianTime` + `maxAcceleration`].
 * Root block's `Time` & `MedianTime` must be equal.
 
 ##### Identities
 
-* A block cannot contain identities whose signature does not match the identity's content
-* A block cannot have two or more identities sharing a same `USER_ID`.
-* A block cannot have two or more identities sharing a same `PUBLIC_KEY`.
-* Each identity of a block must match a `Joiners` line matching the same `PUBLIC_KEY`
+A block cannot contain identities whose signature does not match the identity's content
 
 ##### Memberships (Joiners, Actives, Leavers)
 
-* A block cannot contain memberships whose signature does not match membership's content
+A block cannot contain memberships whose signature does not match the membership's content
 
-##### Members changes (Joiners, Actives, Leavers, Excluded)
+##### Revoked
 
-* A block cannot contain more than 1 occurrence of a same `PUBLIC_KEY` in `Joiners`, `Actives`, `Leavers` and `Excluded` field as a whole. In other words, a given `PUBLIC_KEY` present in `Joiners` cannot be present in `Joiners` a second time, neither can it be present in `Actives`, `Leavers` or `Excluded`.
+A block cannot contain revocations whose signature does not match the revocation's content
+
+##### Transactions
+
+* A transaction in compact format cannot measure more than 100 lines
+* A transaction must have at least 1 source
+* A transaction cannot have `SIG(INDEX)` unlocks with `INDEX >= ` issuers count.
+* A transaction **must** have signatures matching its content **for each issuer**
+* A transaction's version:
+  * must be the same as its including block if the block's `Version` is `<= 3`
+  * must be equal to `3` if the block's `Version` is `> 3`
+* Signatures count must be the same as issuers count
+* Signatures are ordered by issuer
+* Signatures are made over the transaction's content, signatures excepted
+
+##### INDEX GENERATION
+
+##### Identities
+
+Each identity produces 2 new entries:
+
+    IINDEX (
+        op = 'CREATE'
+        uid = USER_ID
+        pub = PUBLIC_KEY
+        created_on = BLOCK_UID
+        written_on = BLOCKSTAMP
+        member = true
+        wasMember = true
+        kick = false
+    )
+
+    MINDEX (
+        op = 'CREATE'
+        pub = PUBLIC_KEY
+        created_on = BLOCK_UID
+        written_on = BLOCKSTAMP
+        expired_on = 0
+        expires_on = MedianTime + msValidity
+        revokes_on = MedianTime + msValidity*2
+        type = 'JOIN'
+        revoked_on = null
+        leaving = false
+    )
+
+##### Joiners
+
+Each join whose `PUBLIC_KEY` **does not match** a local MINDEX `CREATE, PUBLIC_KEY` produces 2 new entries:
+
+    IINDEX (
+        op = 'UPDATE'
+        uid = null
+        pub = PUBLIC_KEY
+        created_on = null
+        written_on = BLOCKSTAMP
+        member = true
+        wasMember = null
+        kick = null
+    )
+
+    MINDEX (
+        op = 'UPDATE'
+        pub = PUBLIC_KEY
+        created_on = BLOCK_UID
+        written_on = BLOCKSTAMP
+        expired_on = 0
+        expires_on = MedianTime + msValidity
+        revokes_on = MedianTime + msValidity*2
+        type = 'JOIN'
+        revoked_on = null
+        leaving = null
+    )
+
+##### Actives
+
+Each active produces 1 new entry:
+
+    MINDEX (
+        op = 'UPDATE'
+        pub = PUBLIC_KEY
+        created_on = BLOCK_UID
+        written_on = BLOCKSTAMP
+        expires_on = MedianTime + msValidity
+        revokes_on = MedianTime + msValidity*2
+        type = 'RENEW'
+        revoked_on = null
+        leaving = null
+    )
+
+##### Leavers
+
+Each leaver produces 1 new entry:
+
+    MINDEX (
+        op = 'UPDATE'
+        pub = PUBLIC_KEY
+        created_on = BLOCK_UID
+        written_on = BLOCKSTAMP
+        type = 'LEAVE'
+        expires_on = null
+        revokes_on = null
+        revoked_on = null
+        leaving = true
+    )
 
 ##### Revoked
 
-* Each `PUBLIC_KEY` under `Revoked` field **must not** be present under `Joiners`, `Actives` or `Leavers` fields.
-* A block cannot contain more than 1 occurrence of a same `PUBLIC_KEY` in `Revoked`.
+Each revocation produces 1 new entry:
+
+    MINDEX (
+        op = 'UPDATE'
+        pub = PUBLIC_KEY
+        created_on = BLOCK_UID
+        written_on = BLOCKSTAMP
+        type = 'REV'
+        expires_on = null
+        revokes_on = null
+        revoked_on = BLOCKSTAMP
+        revocation = REVOCATION_SIG
+        leaving = false
+    )
 
 ##### Excluded
 
-* Each `PUBLIC_KEY` under `Revoked` field **must** be present under `Excluded` field.
+Each exclusion produces 1 new entry:
+
+    IINDEX (
+        op = 'UPDATE'
+        uid = null
+        pub = PUBLIC_KEY
+        created_on = null
+        written_on = BLOCKSTAMP
+        member = false
+        wasMember = null
+        kick = false
+    )
 
 ##### Certifications
 
-* A block cannot have two certifications from a same `PUBKEY_FROM`, unless it is block#0.
-* A block cannot have two identical certifications (same `PUBKEY_FROM` and same `PUBKEY_TO` for the two certifications)
-* A block cannot have certifications for public keys present in either `Excluded` or `Leavers` fields.
+Each certification produces 1 new entry:
 
-##### Transactions
+    CINDEX (
+        op = 'CREATE'
+        issuer = PUBKEY_FROM
+        receiver = PUBKEY_TO
+        created_on = BLOCK_ID
+        written_on = BLOCKSTAMP
+        sig = SIGNATURE
+        expires_on = MedianTime + sigValidity
+        chainable_on = MedianTime + sigPeriod
+        expired_on = 0
+    )
 
-* A transaction in compact format cannot measure more than 100 lines
-* A transaction must have at least 1 issuer, 1 source and 1 recipient
-* For each issuer line, starting from line # `0`, there must be a source with an `INDEX` value equal to this line#
-* A transaction cannot have 2 identical inputs
-* A transaction cannot have 2 identical outputs
-* A transaction cannot have `SIG(INDEX)` unlocks with `INDEX >= ` issuers count.
-* A transaction **must** have signatures matching its content for each issuer
-* A transaction's version:
-  * must be the same as its including block if the block's `Version` is `<= 3`
-  * must be equal to `3` if the block's `Version` is `> 3`
-* There cannot be 2 transactions with the same source
+##### Sources
+
+Each transaction input produces 1 new entry:
+
+    SINDEX (
+        op = 'UPDATE'
+        tx = TRANSACTION_HASH
+        identifier = INPUT_IDENTIFIER
+        pos = INPUT_INDEX
+        created_on = TX_BLOCKSTAMP
+        written_on = BLOCKSTAMP
+        amount = INPUT_AMOUNT
+        base = INPUT_BASE
+        conditions = null
+        consumed = true
+    )
+
+Each transaction output produces 1 new entry:
+
+    SINDEX (
+        op = 'CREATE'
+        tx = TRANSACTION_HASH
+        identifier = TRANSACTION_HASH
+        pos = OUTPUT_INDEX_IN_TRANSACTION
+        written_on = BLOCKSTAMP
+        written_time = MedianTime
+        amount = OUTPUT_AMOUNT
+        base = OUTPUT_BASE
+        locktime = LOCKTIME
+        conditions = OUTPUT_CONDITIONS
+        consumed = false
+    )
+
+##### INDEX RULES
+
+###### UserID and PublicKey unicity
+
+* The local IINDEX has a unicity constraint on `USER_ID`.
+* The local IINDEX has a unicity constraint on `PUBLIC_KEY`.
+* Each local IINDEX `op = 'CREATE'` operation must match a single local MINDEX `op = 'CREATE', pub = PUBLIC_KEY` operation.
+
+> Functionally: UserID and public key must be unique in a block, an each new identity must have an opt-in document attached.
+
+###### Membership unicity
+
+* The local MINDEX has a unicity constraint on `PUBLIC_KEY`
+
+> Functionally: a user has only 1 status change allowed per block.
+
+###### Revocation implies exclusion
+
+* Each local MINDEX ̀`op = 'UPDATE', revoked_on = BLOCKSTAMP` operations must match a single local IINDEX `op = 'UPDATE', pub = PUBLIC_KEY, member = false` operation.
+
+> Functionally: a revoked member must be immediately excluded.
+
+###### Certifications
+
+* The local CINDEX has a unicity constraint on `PUBKEY_FROM, PUBKEY_TO`
+* The local CINDEX has a unicity constraint on `PUBKEY_FROM`, except for block#0
+* The local CINDEX must not match a MINDEX operation on `PUBLIC_KEY = PUBKEY_FROM, member = false` or `PUBLIC_KEY = PUBKEY_FROM, leaving = true`
+
+> Functionally:
+>
+> * a block cannot have 2 identical certifications (A -> B)
+> * a block cannot have 2 certifications from a same public key, except in block#0
+> * a block cannot have a certification to a leaver or an excluded
+
+###### Sources
+
+* The local SINDEX has a unicity constraint on `UPDATE, IDENTIFIER, POS`
+* The local SINDEX has a unicity constraint on `CREATE, IDENTIFIER, POS`
+
+> Functionally: 
+> * a same source cannot be consumed twice by the block
+> * a same output cannot be produced twice by block
+>
+> But a source can be both created and consumed in the same block, so a *chain of transactions* can be stored at once.
+
+##### Double-spending control
+
+Definitions:
+
+For each SINDEX unique `tx`:
+
+* **inputs** are the SINDEX row matching `UPDATE, tx`
+* **outputs** are the SINDEX row matching `CREATE, tx`
+
+> Functionally: we gather the sources for each transaction, in order to check them.
 
 ###### CommonBase
 
@@ -1141,373 +1369,1118 @@ TRUE
   * if `BaseDelta > 0`, then it must be inferior or equal to the sum of all preceding `BaseDelta`
 * *Rule*: The sum of all inputs in `CommonBase` must equal the sum of all outputs in `CommonBase`
 
-> Consequence: we cannot create money nor lose money through transactions. We can only transfer coins we own.
-
-###### About signatures
-
-* Signatures count must be the same as issuers count
-* Signatures are ordered by issuer
-* Signatures are made over the transaction's content, signatures excepted
+> Functionally: we cannot create nor lose money through transactions. We can only transfer coins we own.
 
 #### Global
 
 Global validation verifies the coherence of a locally-validated block, in the context of the whole blockchain, including the block.
 
-##### Definitions
+##### INDEX GENERATION
 
-###### Block size
+###### Definitions
 
-The block size is defined as the number of lines in multiline fields (`Identities`, `Joiners`, `Actives`, `Leavers`, `Revoked`, `Certifications`, `Transactions`) **except** `Excluded` field.
+Here are some references that will be used in next sections.
 
-For example:
+BINDEX references:
 
-* 1 new identity + 1 joiner + 2 certifications = 4 lines sized block
-* 1 new identity + 1 joiner + 2 certifications + 5 lines transaction = 9 lines sized block
+* *HEAD*: the BINDEX top entry (generated for incoming block, precisely)
+* *HEAD~1*: the BINDEX 1<sup>st</sup> entry before HEAD (= entry where `ENTRY.number = HEAD.number - 1`)
+* *HEAD~n*: the BINDEX n<sup>th</sup> entry before HEAD (= entry where `ENTRY.number = HEAD.number - n`)
+* *HEAD~n[field=value, ...]*: the BINDEX entry at *HEAD~n* if it fulfills the condition, null otherwise
+* *HEAD~n..m[field=value, ...]*: the BINDEX entries between *HEAD~n* and *HEAD~m*, included, where each entry fulfills the condition
+* *HEAD~n.property*: get a BINDEX entry property. Ex.: `HEAD~1.hash` looks at the hash of the entry preceding HEAD.
+* *(HEAD~n..m).property*: get all the values of *property* in BINDEX for entries between *HEAD~n* and *HEAD~m*, included.
+* *(HEAD~<n>..<m>).property*: same, but <n> and <m> are variables to be computed
 
-The maximum size of a block `MAX_BLOCK_SIZE` is defined by:
+Function references:
 
-`MAX_BLOCK_SIZE = MAX(500 ; CEIL(1.10 * AVERAGE_BLOCK_SIZE))`
+* *COUNT* returns the number of values in a list of values
+* *AVG* computes the average value in a list of values, floor rounded.
+* *MEDIAN* computes the median value in a list of values
+* *MAX* computes the maximum value in a list of values
 
-Where: `AVERAGE_BLOCK_SIZE` equals to the average block size of the `DifferentIssuersCount` of previous blocks.
+> If values count is even, the median is computed over the 2 centered values by an arithmetical median on them, ceil rounded.
 
-###### Block time
+* *UNIQ* returns a list of the unique values in a list of values
+* *INTEGER_PART* return the integer part of a number
+* *FIRST* return the first element in a list of values matching the given condition
+* *REDUCE* merges a set of elements into a single one, by extending the non-null properties from each record into the resulting record.
+* *REDUCE_BY* merges a set of elements into a new set, where each new element is the reduction of the first set sharing a given key.
 
-Block time is a special discrete time defined by the blocks themselves, where unit is *a block*, and values are *block number + fingerprint*.
+> If there is no elements, all its properties are `null`.
 
-So, refering to t<sub>block</sub> = 0-2B7A158B9FD052164005ED5B491699644A846CE2 is valid only if there exists a block#0 in the blockchain whose hash equals 2B7A158B9FD052164005ED5B491699644A846CE2.
+* *NUMBER* get the number part of blockstamp
+* *HASH* get the hash part of blockstamp
 
-###### UD time
+###### HEAD
 
-UD time is a special discrete time defined by the UDs written in the blockchain where unit is a *UD*.
+The block produces 1 new entry:
 
-Refering to UD(t = 1) means UD#1, and refers to the *first UD* written in the blockchain.
+    BINDEX (
+      version = Version
+      size = BlockSize
+      hash = BlockHash
+      issuer = Issuer
+      time = Time
+      number = null
+      currency = null
+      previousHash = null
+      previousIssuer = null
+      membersCount = null
+      issuersCount = null
+      issuersFrame = null
+      issuersFrameVar = null
+      issuerDiff = null
+      avgBlockSize = null
+      medianTime = null
+      dividend = null
+      mass = null
+      unitBase = null
+      powMin = PowMin
+      udTime = null
+      diffTime = null
+      speed = null
+    )
+    
+This entry will be refered to as *HEAD* in the following sections, and is added on the top of the BINDEX.
 
-> UD(t = 0) means UD#0 which does not exist. However, UD#0 is a currency parameter noted **[ud0]**.
+###### BR_G01 - HEAD.number
 
-###### Calendar time
+If HEAD~1 is defined:
 
-Calendar time is the one provided by the blocks under `MedianTime` field. This time is discrete and the unit is seconds.
+    HEAD.number = HEAD~1.number + 1
 
-> *Current time* is to be understood as the last block calendar time written in the blockchain.
+Else:
 
-###### Certification time
+    HEAD.number = 0
+    
+###### BR_G02 - HEAD.previousHash
 
-When making a certification, `BLOCK_ID` is a reference to *block time*.
+If `HEAD.number > 0`:
 
-###### Membership time
+    HEAD.previousHash = HEAD~1.hash
 
-When making a membership, `NUMBER` is a reference to *block time*.
+Else:
 
-###### Certification & Membership age
+    HEAD.previousHash = null
+    
+###### BR_G99 - HEAD.currency
 
-Age is defined as the number of seconds between the certification's or membership's *block time* and *current time*:
+If `HEAD.number > 0`:
 
-    AGE = current_time - block_time
+    HEAD.currency = HEAD~1.currency
 
-###### Identity writability
+Else:
 
-An identity is to be considered *non-writable* if its age is less than or equal to `[idtyWindow]`:
+    HEAD.currency = null
+    
+###### BR_G03 - HEAD.previousIssuer
 
-    VALID   = AGE <= [idtyWindow]
-    EXPIRED = AGE > [idtyWindow]
+If `HEAD.number > 0`:
 
-###### Membership writability
+    HEAD.previousIssuer = HEAD~1.issuer
 
-A membership is to be considered *non-writable* if its age is less than or equal to `[msWindow]`:
+Else:
 
-    VALID   = AGE <= [msWindow]
-    EXPIRED = AGE > [msWindow]
+    HEAD.previousIssuer = null
+    
+###### BR_G100 - HEAD.issuerIsMember
 
-###### Certification writability
+If `HEAD.number > 0`:
 
-A certification is to be considered *non-writable* if its age is less than or equal to `[sigWindow]`:
+    HEAD.issuerIsMember = REDUCE(GLOBAL_IINDEX[pub=HEAD.issuer]).member
 
-    VALID   = AGE <= [sigWindow]
-    EXPIRED = AGE > [sigWindow]
+Else:
 
-###### Certification validity
+    HEAD.issuerIsMember = REDUCE(LOCAL_IINDEX[pub=HEAD.issuer]).member
 
-A certification is to be considered *valid* if its age is less than or equal to `[sigValidity]`:
+###### BR_G04 - HEAD.issuersCount
+    
+If `HEAD.number == 0`:
 
-    VALID   = AGE <= [sigValidity]
-    EXPIRED = AGE > [sigValidity]
+    HEAD.issuersCount = 0
+    
+Else if `HEAD~1.version > 2`:
 
-###### Certification activity
+    HEAD.issuersCount = COUNT(UNIQ((HEAD~1..<HEAD~1.issuersFrame>).issuer))
+    
+Else:
 
-A certification is to be considered *active* if it is both written in the blockchain and *valid* (equivalent to not expired).
+    HEAD.issuersCount = COUNT(UNIQ((HEAD~1..40).issuer))
 
-###### Certification stock
+###### BR_G05 - HEAD.issuersFrame
 
-The stock of certification is defined per member and reflects the number of *active* certifications (i.e. not expired):
+If `HEAD.number == 0`:
 
-    STOCK = COUNT(active_certifications)
+    HEAD.issuersFrame = 1
+    
+Else if `HEAD~1.version == 2`:
 
-###### Membership validity
+    HEAD.issuersFrame = 40
+    
+Else if `HEAD~1.issuersFrameVar > 0`:
 
-A membership is to be considered valid if its age is less than or equal to `[msValidity]`:
+    HEAD.issuersFrame = HEAD~1.issuersFrame + 1 
+    
+Else if `HEAD~1.issuersFrameVar < 0`:
 
-    VALID   = AGE <= [msValidity]
-    EXPIRED = AGE > [msValidity]
+    HEAD.issuersFrame = HEAD~1.issuersFrame - 1 
+    
+Else:
 
-###### Membership activity
+    HEAD.issuersFrame = HEAD~1.issuersFrame
 
-A membership is to be considered *active* if it is both written in the blockchain and *valid* (i.e. not expired).
+###### BR_G06 - HEAD.issuersFrameVar
 
-###### Certification chaining
+If `HEAD.number == 0` OR `HEAD~1.version == 2`:
 
-A written certification is to be considered chainable if:
+    HEAD.issuersFrameVar = 0
+    
+Else if `HEAD~1.issuersFrameVar > 0`:
 
-* its age is greater or equal to `[sigPeriod]`:
-* the number of active certifications is lower than `[sigStock]`:
+    HEAD.issuersFrameVar = HEAD~1.issuersFrameVar + 5*(HEAD.issuersCount - HEAD~1.issuersCount) - 1
+    
+Else if `HEAD~1.issuersFrameVar < 0`:
 
-        CHAINABLE = AGE >= [sigPeriod] && STOCK < [sigStock]
+    HEAD.issuersFrameVar = HEAD~1.issuersFrameVar + 5*(HEAD.issuersCount - HEAD~1.issuersCount) + 1
+    
+Else:
 
-###### Member
+    HEAD.issuersFrameVar = HEAD~1.issuersFrameVar + 5*(HEAD.issuersCount - HEAD~1.issuersCount)
 
-A member is a `PUBLIC_KEY` matching a valid `Identity` whose last occurrence in the blockchain is either `Joiners`, `Actives` or `Leavers` **and is not expired**.
 
-A `PUBLIC_KEY` whose last occurrence in the blockchain is `Leavers` or `Excluded`, or has no occurrence in the blockchain **is not** a member.
+###### BR_G07 - HEAD.avgBlockSize
 
-###### Revocation
+    HEAD.avgBlockSize = AVG((HEAD~1..<HEAD.issuersCount>).size)
 
-An identity is considered *revoked* if either:
+###### BR_G08 - HEAD.medianTime
 
-* the age of its last `IN` membership is `>= 2 x [msValidity]` (implicit revocation)
-* there exists a block in which the identity is found under `Revoked` field (explicit revocation)
+If `HEAD.number > 0`:
 
-##### Number
+    HEAD.medianTime = MEDIAN((HEAD~1..<MIN(medianTimeBlocks, HEAD.number)>).time)
 
-* A block's `Number` must be exactly equal to previous block + 1.
-* If the blockchain is empty, `Number` must be `0` .
+Else:
 
-##### Version
+    HEAD.medianTime = HEAD.time
 
-`Version` must be between `[2, 5]`. Also, `Version` of incoming block must be equal to `Version` or `Version + 1` of current block.
+###### BR_G09 - HEAD.diffNumber
 
-##### PoWMin
+If `HEAD.number == 0`:
 
-###### Définitions
-* `speedRange = MIN(dtDiffEval, incomingNumber)`
-* `speed = speedRange / (medianTime(incomingNumber) - medianTime(incomingNumber - speedRange))`
-* `maxSpeed = 1/minGenTime`
-* `minSpeed = 1/maxGenTime`
+    HEAD.diffNumber = HEAD.number + dtDiffEval
 
-###### Rules
+Else if `HEAD~1.diffNumber <= HEAD.number`:
 
-We define `PPowMin` as the `PowMin` value of the previous block.
+    HEAD.diffNumber = HEAD~1.diffNumber + dtDiffEval
 
-* If the incoming block's `Number` is greater than 0 and a multiple of `dtDiffEval`, then:
-  * If `speed` is greater than or equal to `maxSpeed`, then:
-      *  if `(PPoWMin + 2) % 16 == 0` then `PoWMin = PPoWMin + 2`
-      *  else `PoWMin = PPoWMin + 1`
-  * If `speed` is less than or equal to `minSpeed`, then:
-	  * if `(PPoWMin) % 16 == 0` then `PoWMin = MAX(0, PPoWMin - 2)`
-	  * else `PoWMin = MAX(0, PPoWMin - 1)`
-* Else
-  * If `Number` is greater than 0, `PoWMin` must be equal to the previous block's `PoWMin`
+Else:
 
-##### PreviousHash
+    HEAD.diffNumber = HEAD~1.diffNumber
 
-* A block's `PreviousHash` must be exactly equal to the previous block's computed hash (a.k.a Proof-of-Work). Note that this hash **must** start with ` powZeroMin` zeros.
+###### BR_G10 - HEAD.membersCount
 
-##### PreviousIssuer
+If `HEAD.number == 0`:
 
-* A block's `PreviousIssuer` must be exactly equal to the previous block's `Issuer` field.
+    HEAD.membersCount = COUNT(LOCAL_IINDEX[member=true])
+    
+Else:
 
-##### IssuersFrame
+    HEAD.membersCount = HEAD~1.membersCount + COUNT(LOCAL_IINDEX[member=true]) - COUNT(LOCAL_IINDEX[member=false])
 
-`IssuersFrame(t) = IssuersFrame(t-1) + CONVERGENCE` where:
+###### BR_G11 - HEAD.udTime
 
-* `IssuersFrame(t)` is the value of the field in the local block
-* `IssuersFrame(t-1)` is the value of the field in the previous block (`1` in case of making root block or `40` if previous block is V2)
-* `CONVERGENCE` equals `+1` if IssuersFrameVar(t-1) is `> 0`, or `-1` if IssuersFrameVar(t-1) is `< 0`
+If `HEAD.number == 0`:
 
-##### DifferentIssuersCount
+    HEAD.udTime = HEAD.medianTime + dt
 
-This field counts the number of different block issuers between the `IssuersFrame(t-1)` previous blocks.
+Else if `HEAD~1.udTime <= HEAD.medianTime`:
 
-`IssuersFrame(t-1)`'s value is the one given by the previous block, or `0` when making a root block, or `40` if the previous block is V2.
+    HEAD.udTime = HEAD~1.udTime + dt
 
-##### IssuersFrameVar
+Else:
 
-`IssuersFrameVar(t) = IssuersFrameVar(t-1) + NEW_ISSUER_INC - GONE_ISSUER_DEC + CONVERGENCE` where:
+    HEAD.udTime = HEAD~1.udTime
 
-* `IssuersFrameVar(t)` is the value of the field in the local block
-* `IssuersFrameVar(t-1)` is the value of the field in the previous block (`0` for root block or if previous block is V2)
-* `NEW_ISSUER_INC` equals 5 if `DifferentIssuersCount` from local block equals `DifferentIssuersCount` of the previous block `+1` (equals `0` if previous block is V2)
-* `GONE_ISSUER_DEC` equals 5 if `DifferentIssuersCount` from local block equals `DifferentIssuersCount` of the previous block `-1` (equals `0` if previous block is V2)
-* `CONVERGENCE` equals `-1` if IssuersFrameVar(t-1) is `> 0`, or `+1` if IssuersFrameVar(t-1) is `< 0`
+###### BR_G12 - HEAD.unitBase
 
-##### Dates
+If `HEAD.number == 0`:
 
-* For any non-root block, `MedianTime` must be equal to the median value of the `Time` field for the last `medianTimeBlocks` blocks. If the number of available blocks is an even value, the median is computed over the 2 centered values by an arithmetical median on them, ceil rounded.
+    HEAD.unitBase = 0
+    
+Else:
 
-##### Identity
+    HEAD.unitBase = HEAD~1.unitBase
 
-* An identity must be writable to be included in the block.
-* The blockchain cannot contain two or more identities sharing a same `USER_ID`.
-* The blockchain cannot contain two or more identities sharing a same `PUBLIC_KEY`.
-* Block#0's identities' `BLOCK_UID` must be the special value `0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855`.
-* Other blocks' identities' `BLOCK_UID` field must match an existing block in the blockchain.
+###### BR_G13 - HEAD.dividend and HEAD.new_dividend
 
-##### Joiners, Actives, Leavers (block fingerprint based memberships)
+If `HEAD.number == 0`:
 
-* A membership must not be expired.
-* Block#0's memberships' `NUMBER` must be `0` and `HASH` the special value `E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855` (SHA1 of empty string).
-* Other blocks' memberships' `NUMBER` and `HASH` field must match an existing block in the blockchain.
-* Each membership's `NUMBER` must be higher than the previous membership's `NUMBER` of the same issuer.
+    HEAD.dividend = ud0
+    HEAD.new_dividend = null
 
-##### Joiners, Actives (Web of Trust distance constraint)
+If `HEAD.udTime != HEAD~1.udTime`:
 
-* A given `PUBLIC_KEY` cannot be in `Joiners` if it does not exist, for at least `xpercent`% of the sentries, a path using certifications (this block included) leading to the key `PUBLIC_KEY` with a maximum count of `[stepMax]` hops.
+    HEAD.dividend = CEIL(HEAD~1.dividend + c² * HEAD~1.mass / POW(10, HEAD~1.unitbase) / HEAD.membersCount)
+    HEAD.new_dividend = HEAD.dividend
 
-##### Joiners
+Else:
 
-* A revoked public key **cannot** be in `Joiners`
-* A given `PUBLIC_KEY` cannot be in `Joiners` if it is a member.
-* A given `PUBLIC_KEY` cannot be in `Joiners` if it does not have `[sigQty]` active certifications coming *to* it (incoming block included)
-* `PUBLIC_KEY` must match for exactly one identity of the blockchain (incoming block included).
+    HEAD.dividend = HEAD~1.dividend
+    HEAD.new_dividend = null
+    
+###### BR_G14 - HEAD.dividend and HEAD.unitbase and HEAD.new_dividend
 
-##### Actives
+If `HEAD.dividend >= 1000000`    :
 
-* A given `PUBLIC_KEY` **can** be in `Actives` **only if** it is a member.
+    HEAD.dividend = CEIL(HEAD.dividend / 10)
+    HEAD.new_dividend = HEAD.dividend
+    HEAD.unitBase = HEAD.unitBase + 1
+    
+###### BR_G15 - HEAD.mass
 
-##### Leavers
+If `HEAD.number == 0`:
 
-* A given `PUBLIC_KEY` cannot be in `Leavers` if it is not a member.
+    HEAD.mass = 0
+    
+Else if `HEAD.udTime != HEAD~1.udTime`:
 
-##### Revoked
+    HEAD.mass = HEAD~1.mass + HEAD.dividend * POWER(10, HEAD.unitBase) * HEAD.membersCount
 
-* A given `PUBLIC_KEY` cannot be in `Revoked` if it has never been a member.
-* A given `PUBLIC_KEY` cannot be in `Revoked` if its identity is already revoked.
-* A given `PUBLIC_KEY` cannot be in `Revoked` if its revocation signature does not match.
+Else:
 
-##### Excluded
+    HEAD.mass = HEAD~1.mass
 
-* A given `PUBLIC_KEY` cannot be in `Excluded` if it is not a member
-* Each `PUBLIC_KEY` with less than `[sigQty]` active certifications or whose last membership is either in `Joiners` or `Actives` is outdated **must** be present in this field.
-* Each `PUBLIC_KEY` whose last membership occurrence is either in `Joiners` or `Actives` *and* is outdated **must** be present in this field.
-* A given `PUBLIC_KEY` **cannot** be in `Excluded` field if it doesn't **have to** (i.e. if no **must** condition is matched).
+###### BR_G16 - HEAD.speed
 
-##### Certifications
+If `HEAD.number == 0`:
 
-* A certification's `PUBKEY_FROM` must be a member.
-* A certification must be writable.
-* A certification must not be expired.
-* A certification's `PUBKEY_TO`'s last membership occurrence **must not** be in `Leavers`.
-* A certification's `PUBKEY_TO` must be a member **or** be in the incoming block's `Joiners`.
-* A certification's signature must be valid over `PUBKEY_TO`'s self-certification, where signatory is `PUBKEY_FROM`.
-* Replayability: there cannot exist 2 *active* certifications with the same `PUBKEY_FROM` and `PUBKEY_TO`.
-* Chainability: a certification whose `PUBKEY_FROM` is the same than an existing certification in the blockchain can be written **only if** the last written certification (incoming block excluded) is considered chainable.
+    speed = 0
+    
+Else:
 
-##### MembersCount
+    range = MIN(dtDiffEval, HEAD.number)
+    elapsed = (HEAD.medianTime - HEAD~<range>.medianTime)
+    
+EndIf
 
-`MembersCount` field must be equal to the last block's `MembersCount` plus the incoming block's `Joiners` count, minus this block's `Excluded` count.
+If `elapsed == 0`:
 
-##### Proof-of-Work
+    speed = 100
 
-> As of Version 5.
+Else:
+    speed = range / elapsed
 
-To be valid, a block fingerprint (whole document + signature) must start with a specific number of zeros + a remaining mark character. Rules are the following, and **each relative to a particular member**:
+###### BR_G17 - HEAD.powMin
 
-```
-PERSONAL_EXCESS = MAX(0, ( (nbPersonalBlocksInFrame + 1) / medianOfBlocksInFrame) - 1)
-PERSONAL_HANDICAP = FLOOR(LN(1 + PERSONAL_EXCESS) / LN(1.189))
-PERSONAL_DIFF = MAX [ PoWMin ; PoWMin * FLOOR (percentRot * nbPreviousIssuers / (1 + nbBlocksSince)) ] + PERSONAL_HANDICAP
+If `HEAD.number > 0 AND HEAD.diffNumber != HEAD~1.diffNumber AND HEAD.speed >= maxSpeed AND (HEAD~1.powMin + 2) % 16 == 0`:
 
-if (PERSONAL_DIFF + 1) % 16 == 0 then PERSONAL_DIFF = PERSONAL_DIFF + 1
+    HEAD.powMin = HEAD~1.powMin + 2
+    
+Else if `HEAD.number > 0 AND HEAD.diffNumber != HEAD~1.diffNumber AND HEAD.speed >= maxSpeed`:
 
-REMAINDER = PERSONAL_DIFFICULTY  % 16
-NB_ZEROS = (PERSONAL_DIFFICULTY - REMAINDER) / 16
-```
+    HEAD.powMin = HEAD~1.powMin + 1
+     
+Else if `HEAD.number > 0 AND HEAD.diffNumber != HEAD~1.diffNumber AND HEAD.speed <= minSpeed AND (HEAD~1.powMin) % 16 == 0`:
 
-Where:
+    HEAD.powMin = MAX(0, HEAD~1.powMin - 2)
+    
+Else if `HEAD.number > 0 AND HEAD.diffNumber != HEAD~1.diffNumber AND HEAD.speed <= minSpeed`:
 
-* `[nbPersonalBlocksInFrame]` is the number of blocks written by the member from block (current `number` - current `issuersFrame` + 1) to current block (included)
-* `[medianOfBlocksInFrame]` is the median quantity of blocks issued per member from block (current `number` - current `issuersFrame` + 1) to current block (included)
-* `[PoWMin]` is the `PoWMin` value of the incoming block
-* `[percentRot]` is the protocol parameter
-* `[nbPreviousIssuers] = DifferentIssuersCount(last block of issuer)`
-* `[nbBlocksSince]` is the number of blocks written **since** the last block of the member (so, incoming block excluded).
-
-
-* If no block has been written by the member:
-  * `[nbPreviousIssuers] = 0`
-  * `[nbBlocksSince] = 0`
-
-The proof is considered valid if:
-
-* the proof starts with at least `NB_ZEROS` zeros
-* the `NB_ZEROS + 1`th character is:
-  * between `[0-F]` if `REMAINDER = 0`
-  * between `[0-E]` if `REMAINDER = 1`
-  * between `[0-D]` if `REMAINDER = 2`
-  * between `[0-C]` if `REMAINDER = 3`
-  * between `[0-B]` if `REMAINDER = 4`
-  * between `[0-A]` if `REMAINDER = 5`
-  * between `[0-9]` if `REMAINDER = 6`
-  * between `[0-8]` if `REMAINDER = 7`
-  * between `[0-7]` if `REMAINDER = 8`
-  * between `[0-6]` if `REMAINDER = 9`
-  * between `[0-5]` if `REMAINDER = 10`
-  * between `[0-4]` if `REMAINDER = 11`
-  * between `[0-3]` if `REMAINDER = 12`
-  * between `[0-2]` if `REMAINDER = 13`
-  * between `[0-1]` if `REMAINDER = 14`
-
-> N.B.: it is not possible to have REMAINDER = 15
-
-> Those rules of difficulty adaptation ensure a shared control of the blockchain writing.
+    HEAD.powMin = MAX(0, HEAD~1.powMin - 1)
 
-##### Universal Dividend
+Else if `HEAD.number > 0`:
+    
+    HEAD.powMin = HEAD~1.powMin
 
-* Root blocks do not have the `UniversalDividend` field.
-* Universal Dividend must be present if `MedianTime` value is greater than or equal to `lastUDTime` + `dt`.
-  * `lastUDTime` is the `MedianTime` of the last block with `UniversalDividend` in it.
-  * Initial value of `lastUDTime` equals to the root block's `MedianTime`.
-* UD(t = 0) = `ud0`
-* Value of `UniversalDividend` (`UD(t+1)`) equals to:
+###### BR_G18 - HEAD.powZeros and HEAD.powRemainder
 
-```
-UD(t+1) = INTEGER_PART(UD(t) + c² * M(t) / N(t+1))
-```
+If `HEAD.number == 0`:
 
-Where:
+    nbPersonalBlocksInFrame = 0
+    medianOfBlocksInFrame = 1
+    
+Else:
 
-* `t` is UD time
-* `UD(t)` is last UD value
-* `c` equals to `[c]` parameter of this protocol
-* `N(t+1)` equals to this block's `MembersCount` field
-* `M(t)` equals to the sum of all `UD(t)*N(t)` of the blockchain (from t = 0, to t = now) where:
-  * `N(t)` is the `MembersCount` for `UD(t)`
-  * `UD(0)` equals to `[ud0]` parameter of this protocol
+    blocksOfIssuer = HEAD~1..<HEAD~1.issuersFrame>[issuer=HEAD.issuer]
+    nbPersonalBlocksInFrame = COUNT(blocksOfIssuer)
+    blocksPerIssuerInFrame = MAP(
+        UNIQ((HEAD~1..<HEAD~1.issuersFrame>).issuer)
+            => COUNT(HEAD~1..<HEAD~1.issuersFrame>[issuer=HEAD.issuer]))
+    medianOfBlocksInFrame = MEDIAN(blocksPerIssuerInFrame)
+    
+EndIf
+    
+If `nbPersonalBlocksInFrame == 0`:
+    
+    nbPreviousIssuers = 0
+    nbBlocksSince = 0
 
-###### UD overflow
-If the `UniversalDividend` value is higher than or equal to `1000000` (1 million), then the `UniversalDividend` value has to be:
+Else:
 
-    UD(t+1) = CEIL(UD(t+1) / 10)
+    last = FIRST(blocksOfIssuer)
+    nbPreviousIssuers = last.issuersCount
+    nbBlocksSince = HEAD~1.number - last.number
+    
+EndIf
+    
+    PERSONAL_EXCESS = MAX(0, ( (nbPersonalBlocksInFrame + 1) / medianOfBlocksInFrame) - 1)
+    PERSONAL_HANDICAP = FLOOR(LN(1 + PERSONAL_EXCESS) / LN(1.189))
+    HEAD.issuerDiff = MAX [ HEAD.powMin ; HEAD.powMin * FLOOR (percentRot * nbPreviousIssuers / (1 + nbBlocksSince)) ] + PERSONAL_HANDICAP
 
-and `UnitBase` value must be incremented by `1` compared to its value at `UD(t)` (see UnitBase global rule).
+If `(HEAD.issuerDiff + 1) % 16 == 0`:
 
-##### UnitBase
+    HEAD.issuerDiff = HEAD.issuerDiff + 1
 
-The field must be either equal to:
+EndIf
 
-* `0` for root block
-* the previous block's `UnitBase` value if UD has no overflow
-* the previous block's `UnitBase` value `+ 1` if UD has an overflow
+Finally:
 
-##### Transactions
+    HEAD.powRemainder = HEAD.issuerDiff  % 16
+    HEAD.powZeros = (HEAD.issuerDiff - HEAD.powRemainder) / 16
 
-* For `D` sources, public key must be a member for the block `#NUMBER` (so, *before* the block's memberships were applied)
-* For `T` sources, the attached unlock condition must match
-* Transaction cannot be included if `BLOCK_MEDIAN_TIME - MOST_RECENT_INPUT_TIME < LOCKTIME`
+###### Local IINDEX augmentation
 
-###### Amounts
+####### BR_G19 - ENTRY.age
+
+For each ENTRY in local IINDEX where `op = 'CREATE'`:
+
+    REF_BLOCK = HEAD~<HEAD~1.number + 1 - NUMBER(ENTRY.created_on)>[hash=HASH(ENTRY.created_on)]
+    
+If `HEAD.number == 0 && ENTRY.created_on == '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855'`:
+    
+    ENTRY.age = 0
+    
+Else if `REF_BLOC != null`:
+
+    ENTRY.age = HEAD~1.medianTime - REF_BLOCK.medianTime
+    
+Else:
+
+    ENTRY.age = conf.idtyWindow + 1
+    
+EndIf
+
+###### BR_G20 - Identity UserID unicity
+
+For each ENTRY in local IINDEX:
+
+If `op = 'CREATE'`:
+
+    ENTRY.uidUnique = COUNT(GLOBAL_IINDEX[uid=ENTRY.uid) == 0
+    
+Else:
+
+    ENTRY.uidUnique = true
+
+###### BR_G21 - Identity pubkey unicity
+
+For each ENTRY in local IINDEX:
+
+If `op = 'CREATE'`:
+
+    ENTRY.pubUnique = COUNT(GLOBAL_IINDEX[pub=ENTRY.pub) == 0
+    
+Else:
+
+    ENTRY.pubUnique = true
+
+####### BR_G33 - ENTRY.excludedIsMember
+
+For each ENTRY in local IINDEX where `member != false`:
+
+    ENTRY.excludedIsMember = true 
+
+For each ENTRY in local IINDEX where `member == false`:
+
+    ENTRY.excludedIsMember = REDUCE(GLOBAL_IINDEX[pub=ENTRY.pub]).member
+
+####### BR_G35 - ENTRY.isBeingKicked
+
+For each ENTRY in local IINDEX where `member != false`:
+
+    ENTRY.isBeingKicked = false 
+
+For each ENTRY in local IINDEX where `member == false`:
+
+    ENTRY.isBeingKicked = true
+
+####### BR_G36 - ENTRY.hasToBeExcluded
+
+For each ENTRY in local IINDEX:
+
+    ENTRY.hasToBeExcluded = REDUCE(GLOBAL_IINDEX[pub=ENTRY.pub]).kick
+
+###### Local MINDEX augmentation
+
+####### BR_G22 - ENTRY.age
+
+For each ENTRY in local MINDEX where `revoked_on == null`:
+
+    REF_BLOCK = HEAD~<HEAD~1.number + 1 - NUMBER(ENTRY.created_on)>[hash=HASH(ENTRY.created_on)]
+    
+If `HEAD.number == 0 && ENTRY.created_on == '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855'`:
+    
+    ENTRY.age = 0
+    
+Else if `REF_BLOC != null`:
+
+    ENTRY.age = HEAD~1.medianTime - REF_BLOCK.medianTime
+    
+Else:
+
+    ENTRY.age = conf.msWindow + 1
+    
+EndIf
+
+####### BR_G23 - ENTRY.numberFollowing
+
+For each ENTRY in local MINDEX where `revoked_on == null`:
+
+    created_on = REDUCE(GLOBAL_MINDEX[pub=ENTRY.pub]).created_on
+    
+If `created_on != null`:
+
+    ENTRY.numberFollowing = NUMBER(ENTRY.created_ON) > NUMBER(created_on)
+    
+Else:
+
+    ENTRY.numberFollowing = true
+    
+EndIf
+
+For each ENTRY in local MINDEX where `revoked_on != null`:
+
+    ENTRY.numberFollowing = true
+
+####### BR_G24 - ENTRY.distanceOK
+
+For each ENTRY in local MINDEX where `type == 'JOIN' OR type == 'ACTIVE'`:
+
+    dSen = CEIL(HEAD.membersCount ^ (1 / stepMax))
+    
+    GRAPH = SET(LOCAL_CINDEX, 'issuer', 'receiver') + SET(GLOBAL_CINDEX, 'issuer', 'receiver')
+    SENTRIES = SUBSET(GRAPH, dSen, 'issuer')
+
+    ENTRY.distanceOK = EXISTS_PATH(xpercent, SENTRIES, GRAPH, ENTRY.pub, stepMax)
+    
+> Functionally: checks if it exists, for at least `xpercent`% of the sentries, a path using GLOBAL_CINDEX + LOCAL_CINDEX leading to the key `PUBLIC_KEY` with a maximum count of `[stepMax]` hops.
+
+For each ENTRY in local MINDEX where `!(type == 'JOIN' OR type == 'ACTIVE')`:
+
+    ENTRY.distanceOK = true
+
+####### BR_G25 - ENTRY.onRevoked
+
+For each ENTRY in local MINDEX:
+
+    ENTRY.onRevoked = REDUCE(GLOBAL_MINDEX[pub=ENTRY.pub]).revoked_on != null
+
+####### BR_G26 - ENTRY.joinsTwice
+
+For each ENTRY in local MINDEX where `op = 'UPDATE', expired_on = 0`:
+
+    ENTRY.joinsTwice = REDUCE(GLOBAL_IINDEX[pub=ENTRY.pub]).member == true
+
+####### BR_G27 - ENTRY.enoughCerts
+
+For each ENTRY in local MINDEX where `type == 'JOIN' OR type == 'ACTIVE'`:
+
+    ENTRY.enoughCerts = COUNT(GLOBAL_CINDEX[receiver=ENTRY.pub,expired_on=null]) + COUNT(LOCAL_CINDEX[receiver=ENTRY.pub,expired_on=null]) >= sigQty 
+    
+> Functionally: any member or newcomer needs `[sigQty]` certifications coming *to* him to be in the WoT
+
+For each ENTRY in local MINDEX where `!(type == 'JOIN' OR type == 'ACTIVE')`:
+
+    ENTRY.enoughCerts = true
+
+####### BR_G28 - ENTRY.leaverIsMember
+
+For each ENTRY in local MINDEX where `type == 'LEAVE'`:
+
+    ENTRY.leaverIsMember = REDUCE(GLOBAL_IINDEX[pub=ENTRY.pub]).member 
+    
+For each ENTRY in local MINDEX where `type != 'LEAVE'`:
+
+    ENTRY.leaverIsMember = true
+
+####### BR_G29 - ENTRY.activeIsMember
+
+For each ENTRY in local MINDEX where `type == 'ACTIVE'`:
+
+    ENTRY.activeIsMember = REDUCE(GLOBAL_IINDEX[pub=ENTRY.pub]).member
+
+For each ENTRY in local MINDEX where `type != 'ACTIVE'`:
+
+    ENTRY.activeIsMember = true
+
+####### BR_G30 - ENTRY.revokedIsMember
+
+For each ENTRY in local MINDEX where `revoked_on == null`:
+
+    ENTRY.revokedIsMember = true 
+
+For each ENTRY in local MINDEX where `revoked_on != null`:
+
+    ENTRY.revokedIsMember = REDUCE(GLOBAL_IINDEX[pub=ENTRY.pub]).member
+
+####### BR_G31 - ENTRY.alreadyRevoked
+
+For each ENTRY in local MINDEX where `revoked_on == null`:
+
+    ENTRY.alreadyRevoked = false 
+
+For each ENTRY in local MINDEX where `revoked_on != null`:
+
+    ENTRY.alreadyRevoked = REDUCE(GLOBAL_MINDEX[pub=ENTRY.pub]).revoked_on != null
+
+####### BR_G32 - ENTRY.revocationSigOK
+
+For each ENTRY in local MINDEX where `revoked_on == null`:
+
+    ENTRY.revocationSigOK = true 
+
+For each ENTRY in local MINDEX where `revoked_on != null`:
+
+    ENTRY.revocationSigOK = SIG_CHECK_REVOKE(REDUCE(GLOBAL_IINDEX[pub=ENTRY.pub]), ENTRY)
+
+####### BR_G34 - ENTRY.isBeingRevoked
+
+For each ENTRY in local MINDEX where `revoked_on == null`:
+
+    ENTRY.isBeingRevoked = false 
+
+For each ENTRY in local MINDEX where `revoked_on != null`:
+
+    ENTRY.isBeingRevoked = true
+    
+###### Local CINDEX augmentation
+
+For each ENTRY in local CINDEX:
+
+####### BR_G37 - ENTRY.age
+
+    REF_BLOCK = HEAD~<HEAD~1.number + 1 - NUMBER(ENTRY.created_on)>[hash=HASH(ENTRY.created_on)]
+    
+If `HEAD.number == 0 && ENTRY.created_on == '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855'`:
+    
+    ENTRY.age = 0
+    
+Else if `REF_BLOC != null`:
+
+    ENTRY.age = HEAD~1.medianTime - REF_BLOCK.medianTime
+    
+Else:
+
+    ENTRY.age = conf.sigWindow + 1
+    
+EndIf
+    
+####### BR_G38 - ENTRY.unchainables
+
+If `HEAD.number > 0`:
+
+    ENTRY.unchainables = COUNT(GLOBAL_CINDEX[issuer=ENTRY.issuer, chainable_on > HEAD~1.medianTime]))
+    
+####### BR_G39 - ENTRY.stock
+
+    ENTRY.stock = COUNT(GLOBAL_CINDEX[issuer=ENTRY.issuer, expired_on=0]))
+    
+####### BR_G40 - ENTRY.fromMember
+
+    ENTRY.fromMember = REDUCE(GLOBAL_IINDEX[pub=ENTRY.issuer]).member
+    
+####### BR_G41 - ENTRY.toMember
+
+    ENTRY.toMember = REDUCE(GLOBAL_IINDEX[pub=ENTRY.receiver]).member
+    
+####### BR_G42 - ENTRY.toNewcomer
+
+    ENTRY.toNewcomer = COUNT(LOCAL_IINDEX[member=true,pub=ENTRY.receiver]) > 0
+    
+####### BR_G43 - ENTRY.toLeaver
+
+    ENTRY.toLeaver = REDUCE(GLOBAL_MINDEX[pub=ENTRY.receiver]).leaving
+    
+####### BR_G44 - ENTRY.isReplay
+
+    reducable = GLOBAL_CINDEX[issuer=ENTRY.issuer,receiver=ENTRY.receiver,expired_on=0]
+    
+If `count(reducable) == 0`:
+
+    ENTRY.isReplay = false
+
+Else:
+
+    ENTRY.isReplay = reduce(reducable).expired_on == 0
+    
+####### BR_G45 - ENTRY.sigOK
+
+    ENTRY.sigOK = SIG_CHECK_CERT(REDUCE(GLOBAL_IINDEX[pub=ENTRY.receiver]), ENTRY)
+
+###### Local SINDEX augmentation
+
+####### BR_G102 - ENTRY.age
+
+For each ENTRY in local IINDEX where `op = 'UPDATE'`:
+
+    REF_BLOCK = HEAD~<HEAD~1.number + 1 - NUMBER(ENTRY.hash)>[hash=HASH(ENTRY.created_on)]
+    
+If `HEAD.number == 0 && ENTRY.created_on == '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855'`:
+    
+    ENTRY.age = 0
+    
+Else if `REF_BLOC != null`:
+
+    ENTRY.age = HEAD~1.medianTime - REF_BLOCK.medianTime
+    
+Else:
+
+    ENTRY.age = conf.txWindow + 1
+    
+EndIf
+
+####### BR_G46 - ENTRY.available
+
+For each `LOCAL_SINDEX[op='UPDATE'] as ENTRY`:
+
+    ENTRY.available = REDUCE(GLOBAL_SINDEX[identifier=ENTRY.identifier,pos=ENTRY.pos,amount=ENTRY.amount,base=ENTRY.base]).consumed == false
+
+####### BR_G47 - ENTRY.isLocked
+
+    ENTRY.isLocked = TX_SOURCE_UNLOCK(REDUCE(GLOBAL_SINDEX[identifier=ENTRY.identifier,pos=ENTRY.pos,amount=ENTRY.amount,base=ENTRY.base]).conditions, ENTRY)
+    
+####### BR_G48 - ENTRY.isTimeLocked
+
+    ENTRY.isTimeLocked = ENTRY.written_time - REDUCE(GLOBAL_SINDEX[identifier=ENTRY.identifier,pos=ENTRY.pos,amount=ENTRY.amount,base=ENTRY.base]).written_time < ENTRY.locktime
+
+##### Rules
+
+###### BR_G49 - Version
+
+Rule:
+
+If `HEAD.number > 0`:
+
+    HEAD.version == (HEAD~1.version OR HEAD~1.version + 1)
+
+###### BR_G50 - Block size
+
+Rule:
+
+    HEAD.size < MAX(500 ; CEIL(1.10 * HEAD.avgBlockSize))
+
+###### BR_G98 - Currency
+
+Rule:
+
+If `HEAD.number > 0`:
+
+    Currency = HEAD.currency
+
+###### BR_G51 - Number
+
+Rule:
+
+    Number = HEAD.number
+
+###### BR_G52 - PreviousHash
+
+Rule:
+
+    PreviousHash = HEAD.previousHash
+
+###### BR_G53 - PreviousIssuer
+
+Rule:
+
+    PreviousHash = HEAD.previousHash
+
+###### BR_G101 - Issuer
+
+Rule:
+
+    HEAD.issuerIsMember == true
+
+###### BR_G54 - DifferentIssuersCount
+
+Rule:
+
+If `HEAD.version > 2`:
+
+    DifferentIssuersCount = HEAD.issuersCount
+
+###### BR_G55 - IssuersFrame
+
+Rule:
+
+If `HEAD.version > 2`:
+
+    IssuersFrame = HEAD.issuersFrame
+
+###### BR_G56 - IssuersFrameVar
+
+Rule:
+
+If `HEAD.version > 2`:
+
+    IssuersFrameVar = HEAD.issuersFrameVar
+
+###### BR_G57 - MedianTime
+
+Rule:
+
+    MedianTime = HEAD.medianTime
+
+###### BR_G58 - UniversalDividend
+
+Rule:
+
+    UniversalDividend = HEAD.new_dividend
+
+###### BR_G59 - UnitBase
+
+Rule:
+
+    UnitBase = HEAD.unitBase
+
+###### BR_G60 - MembersCount
+
+Rule:
+
+    MembersCount = HEAD.membersCount
+
+###### BR_G61 - PowMin
+
+Rule:
+
+If `HEAD.number > 0`:
+
+    PowMin = HEAD.powMin
+
+###### BR_G62 - Proof-of-work
+
+Rule: the proof is considered valid if:
+
+* `HEAD.hash` starts with at least `HEAD.powZeros` zeros
+* `HEAD.hash`'s `HEAD.powZeros + 1`th character is:
+  * between `[0-F]` if `HEAD.powRemainder = 0`
+  * between `[0-E]` if `HEAD.powRemainder = 1`
+  * between `[0-D]` if `HEAD.powRemainder = 2`
+  * between `[0-C]` if `HEAD.powRemainder = 3`
+  * between `[0-B]` if `HEAD.powRemainder = 4`
+  * between `[0-A]` if `HEAD.powRemainder = 5`
+  * between `[0-9]` if `HEAD.powRemainder = 6`
+  * between `[0-8]` if `HEAD.powRemainder = 7`
+  * between `[0-7]` if `HEAD.powRemainder = 8`
+  * between `[0-6]` if `HEAD.powRemainder = 9`
+  * between `[0-5]` if `HEAD.powRemainder = 10`
+  * between `[0-4]` if `HEAD.powRemainder = 11`
+  * between `[0-3]` if `HEAD.powRemainder = 12`
+  * between `[0-2]` if `HEAD.powRemainder = 13`
+  * between `[0-1]` if `HEAD.powRemainder = 14`
+
+> N.B.: it is not possible to have HEAD.powRemainder = 15
+
+###### BR_G63 - Identity writability
+
+Rule:
+
+    ENTRY.age <= [idtyWindow]
+
+###### BR_G64 - Membership writability
+
+Rule:
+
+    ENTRY.age <= [msWindow]
+
+###### BR_G65 - Certification writability
+
+Rule:
+
+    ENTRY.age <= [sigWindow]
+
+###### BR_G66 - Certification stock
+
+Rule:
+
+    ENTRY.stock <= sigStock
+
+###### BR_G67 - Certification period
+
+Rule:
+
+    ENTRY.unchainables == 0
+
+###### BR_G68 - Certification from member
+
+Rule:
+
+If `HEAD.number > 0`:
+
+    ENTRY.fromMember == true
+
+###### BR_G69 - Certification to member or newcomer
+
+Rule:
+
+    ENTRY.toMember == true
+    OR
+    ENTRY.toNewcomer == true
+
+###### BR_G70 - Certification to non-leaver
+
+Rule:
+
+    ENTRY.toLeaver == false
+
+###### BR_G71 - Certification replay
+
+Rule:
+
+    ENTRY.isReplay == false
+
+###### BR_G72 - Certification signature
+
+Rule:
+
+    ENTRY.sigOK == true
+
+###### BR_G73 - Identity UserID unicity
+
+Rule:
+
+    ENTRY.uidUnique == true
+
+###### BR_G74 - Identity pubkey unicity
+
+Rule:
+
+    ENTRY.pubUnique == true
+
+###### BR_G75 - Membership succession
+
+Rule:
+
+    ENTRY.numberFollowing == true
+
+###### BR_G76 - Membership distance check
+
+Rule:
+
+    ENTRY.distanceOK == true
+
+###### BR_G77 - Membership on revoked
+
+Rule:
+
+    ENTRY.onRevoked == false
+
+###### BR_G78 - Membership joins twice
+
+Rule:
+
+    ENTRY.joinsTwice == false
+
+###### BR_G79 - Membership enough certifications
+
+Rule:
+
+    ENTRY.enoughCerts == true
+
+###### BR_G80 - Membership leaver
+
+Rule:
+
+    ENTRY.leaverIsMember == true
+
+###### BR_G81 - Membership active
+
+Rule:
+
+    ENTRY.activeIsMember == true
+
+###### BR_G82 - Revocation by a member
+
+Rule:
+
+    ENTRY.revokedIsMember == true
+
+###### BR_G83 - Revocation singleton
+
+Rule:
+
+    ENTRY.alreadyRevoked == false
+
+###### BR_G84 - Revocation signature
+
+Rule:
+
+    ENTRY.revocationSigOK == true
+    
+###### BR_G85 - Excluded is a member
+
+Rule:
+
+    ENTRY.excludedIsMember == true
+    
+###### BR_G86 - Excluded to be kicked
+
+Rule:
+
+For each `REDUCE_BY(GLOBAL_IINDEX[kick=true], 'pub') as TO_KICK`:
+
+    REDUCED = REDUCE(GLOBAL_IINDEX[pub=TO_KICK.pub])
+    
+If `REDUCED.kick` then:
+
+    COUNT(LOCAL_MINDEX[pub=REDUCED.pub,isBeingKicked=true]) == 1
+
+###### BR_G103 - Trancation writability
+
+Rule:
+
+    ENTRY.age <= [txWindow]
+    
+###### BR_G87 - Input is available
+
+For each `LOCAL_SINDEX[op='UPDATE'] as ENTRY`:
+
+Rule:
+
+    ENTRY.available == true
+    
+###### BR_G88 - Input is unlocked
+
+For each `LOCAL_SINDEX[op='UPDATE'] as ENTRY`:
+
+Rule:
+
+    ENTRY.isLocked == false
+    
+###### BR_G89 - Input is time unlocked
+
+For each `LOCAL_SINDEX[op='UPDATE'] as ENTRY`:
+
+Rule:
+
+    ENTRY.isTimeLocked == false
+    
+###### BR_G90 - Output base
+
+For each `LOCAL_SINDEX[op='CREATE'] as ENTRY`:
+
+Rule:
+
+    ENTRY.unitBase <= HEAD~1.unitBase
+
+##### Post-rules INDEX augmentation
+
+###### BR_G91 - Dividend
+
+If `HEAD.new_dividend != null`:
+
+For each `REDUCE_BY(GLOBAL_IINDEX[member=true], 'pub') as IDTY` then if `IDTY.member`, add a new LOCAL_SINDEX entry:
+
+    SINDEX (
+        op = 'CREATE'
+        identifier = IDTY.pub
+        pos = HEAD.number
+        written_on = BLOCKSTAMP
+        written_time = MedianTime
+        amount = HEAD.dividend
+        base = HEAD.unitBase
+        locktime = null
+        conditions = REQUIRE_SIG(MEMBER.pub)
+        consumed = false
+    )
+
+###### BR_G92 - Certification expiry
+
+For each `GLOBAL_CINDEX[expires_on<=HEAD.medianTime] as CERT`, add a new LOCAL_CINDEX entry:
+
+If `reduce(GLOBAL_CINDEX[issuer=CERT.issuer,receiver=CERT.receiver,created_on=CERT.created_on]).expired_on == 0`:
+
+    CINDEX (
+        op = 'UPDATE'
+        issuer = CERT.issuer
+        receiver = CERT.receiver
+        created_on = CERT.created_on
+        expired_on = HEAD.medianTime
+    )
+
+###### BR_G93 - Membership expiry
+
+For each `REDUCE_BY(GLOBAL_MINDEX[expires_on<=HEAD.medianTime], 'pub') as POTENTIAL` then consider `REDUCE(GLOBAL_MINDEX[pub=POTENTIAL.pub]) AS MS`.
+
+If `MS.expired_on == null OR MS.expired_on == 0`, add a new LOCAL_MINDEX entry:
+
+    MINDEX (
+        op = 'UPDATE'
+        pub = MS.pub
+        written_on = BLOCKSTAMP
+        expired_on = HEAD.medianTime
+    )
+    
+###### BR_G94 - Exclusion by membership
+
+For each `LOCAL_MINDEX[expired_on!=0] as MS`, add a new LOCAL_IINDEX entry:
+
+    IINDEX (
+        op = 'UPDATE'
+        pub = MS.pub
+        written_on = BLOCKSTAMP
+        kick = true
+    )
+    
+###### BR_G95 - Exclusion by certification
+
+For each `LOCAL_CINDEX[expired_on!=0] as CERT`:
+
+If `COUNT(GLOBAL_CINDEX[receiver=CERT.receiver]) + COUNT(LOCAL_CINDEX[receiver=CERT.receiver,expired_on=0]) - COUNT(LOCAL_CINDEX[receiver=CERT.receiver,expired_on!=0]) < sigQty`, add a new LOCAL_IINDEX entry:
+
+    IINDEX (
+        op = 'UPDATE'
+        pub = CERT.receiver
+        written_on = BLOCKSTAMP
+        kick = true
+    )
+
+###### BR_G96 - Implicit revocation
+
+For each `GLOBAL_MINDEX[revokes_on<=HEAD.medianTime,revoked_on=null] as MS`:
+
+    REDUCED = REDUCE(GLOBAL_MINDEX[pub=MS.pub])
+
+If `REDUCED.revokes_on<=HEAD.medianTime AND REDUCED.revoked_on==null`, add a new LOCAL_MINDEX entry:
+
+    MINDEX (
+        op = 'UPDATE'
+        pub = MS.pub
+        written_on = BLOCKSTAMP
+        revoked_on = HEAD.medianTime
+    )
+
+###### BR_G104 - Membership expiry date correction
+
+For each `LOCAL_MINDEX[type='JOIN'] as MS`:
+
+    MS.expires_on = MS.expires_on - MS.age
+    MS.revokes_on = MS.revokes_on - MS.age
+
+For each `LOCAL_MINDEX[type='ACTIVE'] as MS`:
+
+    MS.expires_on = MS.expires_on - MS.age
+    MS.revokes_on = MS.revokes_on - MS.age
+
+###### BR_G105 - Certification expiry date correction
+
+For each `LOCAL_CINDEX as CERT`:
+
+    CERT.expires_on = CERT.expires_on - CERT.age
+
+##### BR_G97 - Final INDEX operations
 
-* *Rule*: For each UD source, the amount must match the exact targeted UD value
-* *Def.*: `MaxOutputBase` is the maximum value of all `OutputBase`
-* *Rule*: `MaxOutputBase` cannot be higher than the current block's `UnitBase`
+If all the rules [BR_G49 ; BR_G90] pass, then all the LOCAL INDEX values (IINDEX, MINDEX, CINDEX, SINDEX, BINDEX) have to be appended to the GLOBAL INDEX.
 
 ### Peer
 
diff --git a/server.js b/server.js
index 5f57029ce5075bc10f5ceba6257e2ffefd6b4256..b52289efe56d0d02ffe07ce975cf3f8b4fe4a157 100644
--- a/server.js
+++ b/server.js
@@ -72,6 +72,8 @@ function Server (dbConf, overrideConf) {
     }
   };
 
+  this.getBcContext = () => this.BlockchainService.getContext();
+
   this.plugFileSystem = () => co(function *() {
     logger.debug('Plugging file system...');
     const params = yield paramsP;
diff --git a/test/data/blocks.js b/test/data/blocks.js
index cb3c3b8a19c3e688b09efbec3892d6588a33e0ee..7ca50f030c6626529a6c4637e206397f1a389476 100644
--- a/test/data/blocks.js
+++ b/test/data/blocks.js
@@ -1325,155 +1325,7 @@ module.exports = {
     "Nonce: 5\n" +
     "kNsKdC8eH0d4zdHh1djyMzRXjFrwk3Bc3M8wo4DV/7clE9J66K/U0FljyS79SI78ZZUPaVmrImKJ9SNiubCiBg==\n",
 
-  ROOT_BLOCK_REQUIRED: 
-    "Version: 2\n" +
-    "Type: Block\n" +
-    "Currency: beta_brousouf\n" +
-    "Number: 1\n" +
-    "PoWMin: 1\n" +
-    "Time: 1411776000\n" +
-    "MedianTime: 1411776000\n" +
-    "Issuer: HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd\n" +
-    "PreviousHash: 52DC8A585C5D89571C511BB83F7E7D3382F0041452064B1272E65F0B42B82D57\n" +
-    "PreviousIssuer: G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU\n" +
-    "MembersCount: 3\n" +
-    "Identities:\n" +
-    "Joiners:\n" +
-    "Actives:\n" +
-    "Leavers:\n" +
-    "Revoked:\n" +
-    "Excluded:\n" +
-    "Certifications:\n" +
-    "Transactions:\n" +
-    "InnerHash: DE837CA3F49C423A6A6C124819ABA31A41C1C4A4E2728B5721DF891B98FA8D0D\n" +
-    "Nonce: 1\n" +
-    "kNsKdC8eH0d4zdHh1djyMzRXjFrwk3Bc3M8wo4DV/7clE9J66K/U0FljyS79SI78ZZUPaVmrImKJ9SNiubCiBg==\n",
-
-  V2_CANNOT_FOLLOW_V3: 
-    "Version: 2\n" +
-    "Type: Block\n" +
-    "Currency: beta_brousouf\n" +
-    "Number: 1\n" +
-    "PoWMin: 1\n" +
-    "Time: 1411776000\n" +
-    "MedianTime: 1411776000\n" +
-    "Issuer: HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd\n" +
-    "PreviousHash: 52DC8A585C5D89571C511BB83F7E7D3382F0041452064B1272E65F0B42B82D57\n" +
-    "PreviousIssuer: G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU\n" +
-    "MembersCount: 3\n" +
-    "Identities:\n" +
-    "Joiners:\n" +
-    "Actives:\n" +
-    "Leavers:\n" +
-    "Revoked:\n" +
-    "Excluded:\n" +
-    "Certifications:\n" +
-    "Transactions:\n" +
-    "InnerHash: DE837CA3F49C423A6A6C124819ABA31A41C1C4A4E2728B5721DF891B98FA8D0D\n" +
-    "Nonce: 1\n" +
-    "kNsKdC8eH0d4zdHh1djyMzRXjFrwk3Bc3M8wo4DV/7clE9J66K/U0FljyS79SI78ZZUPaVmrImKJ9SNiubCiBg==\n",
-
-  V3_CANNOT_FOLLOW_V4:
-    "Version: 3\n" +
-    "Type: Block\n" +
-    "Currency: beta_brousouf\n" +
-    "Number: 51\n" +
-    "PoWMin: 1\n" +
-    "Time: 1411776000\n" +
-    "MedianTime: 1411776000\n" +
-    "UnitBase: 2\n" +
-    "Issuer: HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd\n" +
-    "IssuersFrame: 100\n" +
-    "IssuersFrameVar: 0\n" +
-    "DifferentIssuersCount: 3\n" +
-    "PreviousHash: 4C8800825C44A22F230AFC0D140BF1930331A686899D16EBE4C58C9F34C609E8\n" +
-    "PreviousIssuer: G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU\n" +
-    "MembersCount: 3\n" +
-    "Identities:\n" +
-    "Joiners:\n" +
-    "Actives:\n" +
-    "Leavers:\n" +
-    "Revoked:\n" +
-    "Excluded:\n" +
-    "Certifications:\n" +
-    "Transactions:\n" +
-    "InnerHash: DE837CA3F49C423A6A6C124819ABA31A41C1C4A4E2728B5721DF891B98FA8D0D\n" +
-    "Nonce: 1\n" +
-    "kNsKdC8eH0d4zdHh1djyMzRXjFrwk3Bc3M8wo4DV/7clE9J66K/U0FljyS79SI78ZZUPaVmrImKJ9SNiubCiBg==\n",
-
-  SAME_BLOCK_NUMBER: 
-    "Version: 2\n" +
-    "Type: Block\n" +
-    "Currency: beta_brousouf\n" +
-    "Number: 50\n" +
-    "PoWMin: 1\n" +
-    "Time: 1411776000\n" +
-    "MedianTime: 1411776000\n" +
-    "Issuer: HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd\n" +
-    "PreviousHash: 52DC8A585C5D89571C511BB83F7E7D3382F0041452064B1272E65F0B42B82D57\n" +
-    "PreviousIssuer: G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU\n" +
-    "MembersCount: 3\n" +
-    "Identities:\n" +
-    "Joiners:\n" +
-    "Actives:\n" +
-    "Leavers:\n" +
-    "Revoked:\n" +
-    "Excluded:\n" +
-    "Certifications:\n" +
-    "Transactions:\n" +
-    "InnerHash: DE837CA3F49C423A6A6C124819ABA31A41C1C4A4E2728B5721DF891B98FA8D0D\n" +
-    "Nonce: 1\n" +
-    "kNsKdC8eH0d4zdHh1djyMzRXjFrwk3Bc3M8wo4DV/7clE9J66K/U0FljyS79SI78ZZUPaVmrImKJ9SNiubCiBg==\n",
-
-  OLD_BLOCK_NUMBER: 
-    "Version: 2\n" +
-    "Type: Block\n" +
-    "Currency: beta_brousouf\n" +
-    "Number: 49\n" +
-    "PoWMin: 1\n" +
-    "Time: 1411776000\n" +
-    "MedianTime: 1411776000\n" +
-    "Issuer: HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd\n" +
-    "PreviousHash: 52DC8A585C5D89571C511BB83F7E7D3382F0041452064B1272E65F0B42B82D57\n" +
-    "PreviousIssuer: G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU\n" +
-    "MembersCount: 3\n" +
-    "Identities:\n" +
-    "Joiners:\n" +
-    "Actives:\n" +
-    "Leavers:\n" +
-    "Revoked:\n" +
-    "Excluded:\n" +
-    "Certifications:\n" +
-    "Transactions:\n" +
-    "InnerHash: DE837CA3F49C423A6A6C124819ABA31A41C1C4A4E2728B5721DF891B98FA8D0D\n" +
-    "Nonce: 1\n" +
-    "kNsKdC8eH0d4zdHh1djyMzRXjFrwk3Bc3M8wo4DV/7clE9J66K/U0FljyS79SI78ZZUPaVmrImKJ9SNiubCiBg==\n",
-
-  FAR_FUTURE_BLOCK_NUMBER: 
-    "Version: 2\n" +
-    "Type: Block\n" +
-    "Currency: beta_brousouf\n" +
-    "Number: 52\n" +
-    "PoWMin: 1\n" +
-    "Time: 1411776000\n" +
-    "MedianTime: 1411776000\n" +
-    "Issuer: HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd\n" +
-    "PreviousHash: 52DC8A585C5D89571C511BB83F7E7D3382F0041452064B1272E65F0B42B82D57\n" +
-    "PreviousIssuer: G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU\n" +
-    "MembersCount: 3\n" +
-    "Identities:\n" +
-    "Joiners:\n" +
-    "Actives:\n" +
-    "Leavers:\n" +
-    "Revoked:\n" +
-    "Excluded:\n" +
-    "Certifications:\n" +
-    "Transactions:\n" +
-    "InnerHash: DE837CA3F49C423A6A6C124819ABA31A41C1C4A4E2728B5721DF891B98FA8D0D\n" +
-    "Nonce: 1\n" +
-    "kNsKdC8eH0d4zdHh1djyMzRXjFrwk3Bc3M8wo4DV/7clE9J66K/U0FljyS79SI78ZZUPaVmrImKJ9SNiubCiBg==\n",
-
-  WRONG_PREVIOUS_HASH: 
+  WRONG_PREVIOUS_HASH:
     "Version: 2\n" +
     "Type: Block\n" +
     "Currency: beta_brousouf\n" +
@@ -3006,36 +2858,6 @@ module.exports = {
     "Nonce: 1\n" +
     "kNsKdC8eH0d4zdHh1djyMzRXjFrwk3Bc3M8wo4DV/7clE9J66K/U0FljyS79SI78ZZUPaVmrImKJ9SNiubCiBg==\n",
 
-  V3_HAS_MAXIMUM_SIZE:
-    "Version: 3\n" +
-    "Type: Block\n" +
-    "Currency: beta_brousouf\n" +
-    "Number: 83\n" +
-    "PoWMin: 1\n" +
-    "Time: 1411777000\n" +
-    "MedianTime: 1411777000\n" +
-    "UniversalDividend: 100\n" +
-    "UnitBase: 0\n" +
-    "Issuer: HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd\n" +
-    "IssuersFrame: 101\n" +
-    "IssuersFrameVar: 11\n" +
-    "DifferentIssuersCount: 4\n" +
-    "PreviousHash: 2A27BD040B16B7AF59DDD88890E616987F4DD28AA47B9ABDBBEE46257B88E945\n" +
-    "PreviousIssuer: HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd\n" +
-    "MembersCount: 3\n" +
-    "Identities:\n" +
-    "HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd:YvMQqaOAgLtnJzg5ZGhI17sZvXjGgzpSMxNz8ikttMspU5/45MQAqnOfuJnfbrzkkspGlUUjDnUPsOmHPcVyBQ==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:CAT\n".repeat(544) +
-    "Joiners:\n" +
-    "Actives:\n" +
-    "Leavers:\n" +
-    "Revoked:\n" +
-    "Excluded:\n" +
-    "Certifications:\n" +
-    "Transactions:\n" +
-    "InnerHash: DE837CA3F49C423A6A6C124819ABA31A41C1C4A4E2728B5721DF891B98FA8D0D\n" +
-    "Nonce: 1\n" +
-    "kNsKdC8eH0d4zdHh1djyMzRXjFrwk3Bc3M8wo4DV/7clE9J66K/U0FljyS79SI78ZZUPaVmrImKJ9SNiubCiBg==\n",
-
   TRANSACTION_WITHOUT_ISSUERS:
     "Version: 2\n" +
     "Type: Block\n" +
diff --git a/test/fast/block/block_global.js b/test/fast/block/block_global.excluded
similarity index 88%
rename from test/fast/block/block_global.js
rename to test/fast/block/block_global.excluded
index b214fea2d25a2ed89b09922f5334eb856ba56e73..35a3cb581c60362034beefdf2a94d668e244534a 100644
--- a/test/fast/block/block_global.js
+++ b/test/fast/block/block_global.excluded
@@ -49,9 +49,19 @@ function getDAL(overrides) {
   }, overrides);
 }
 
-describe("Block global coherence:", function(){
+/**
+ * TODO: reimplement tests according to new convention:
+ *
+ *   - Name: protocol-brg<number>-<title>.js
+ *   - Content: see existing tests
+ */
 
-  it('a valid block should not have any error', validate(blocks.VALID_ROOT, getDAL(), function (err) {
+describe.skip("Block global coherence:", function(){
+
+  it('a valid block should not have any error', validate(blocks.VALID_ROOT, getDAL(), {
+    getIssuerPersonalizedDifficulty: () => Q(1),
+    getvHEAD_1: () => Q({ version : 2 })
+  }, function (err) {
     should.not.exist(err);
   }));
 
@@ -68,73 +78,11 @@ describe("Block global coherence:", function(){
     },
     isMember: () => Q(true),
     getBlocksBetween: () => Q([{time:1411776000},{time:1411776000},{time:1411776000}])
-  }), function (err) {
-    should.not.exist(err);
-  }));
-
-  it('a V2 number cannot follow V3', test(rules.GLOBAL.checkVersion, blocks.V2_CANNOT_FOLLOW_V3, {
-    getCurrentBlockOrNull: () => Q({
-      version: 3
-    })
-  }, function (err) {
-    should.exist(err);
-    err.message.should.equal('`Version: 2` must follow another V2 block or be the root block');
-  }));
-
-  it('a V2 number cannot follow V4', test(rules.GLOBAL.checkVersion, blocks.V2_CANNOT_FOLLOW_V3, {
-    getCurrentBlockOrNull: () => Q({
-      version: 4
-    })
-  }, function (err) {
-    should.exist(err);
-    err.message.should.equal('`Version: 2` must follow another V2 block or be the root block');
-  }));
-
-  it('a V3 number cannot follow V4', test(rules.GLOBAL.checkVersion, blocks.V3_CANNOT_FOLLOW_V4, {
-    getCurrentBlockOrNull: () => Q({
-      version: 4
-    })
-  }, function (err) {
-    should.exist(err);
-    err.message.should.equal('`Version: 3` must follow another V3 block, a V2 block or be the root block');
-  }));
-
-  it('a V3 block has a limited size', test(rules.GLOBAL.checkBlockLength, blocks.V3_HAS_MAXIMUM_SIZE, {
-    getCurrentBlockOrNull: () => Q({ version: 3, len: 200 }),
-    getBlocksBetween: () => Q([
-      { len: 450 },{ len: 500 },{ len: 530 }
-    ])
-  }, function (err) {
-    should.exist(err);
-    err.message.should.equal('Block size is too high');
-  }));
-
-  it('a block with positive number while no root exists should fail', test(rules.GLOBAL.checkNumber, blocks.ROOT_BLOCK_REQUIRED, {
-    getCurrentBlockOrNull: () => Q(null)
-  }, function (err) {
-    should.exist(err);
-    err.message.should.equal('Root block required first');
-  }));
-
-  it('a block with same number as current should fail', test(rules.GLOBAL.checkNumber, blocks.SAME_BLOCK_NUMBER, {
-    getCurrentBlockOrNull: () => Q({ number: 50, hash: '4C8800825C44A22F230AFC0D140BF1930331A686899D16EBE4C58C9F34C609E8', issuer: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', membersCount: 3 })
+  }), {
+    getIssuerPersonalizedDifficulty: () => Q(2),
+    getvHEAD_1: () => Q({ version : 2 })
   }, function (err) {
-    should.exist(err);
-    err.message.should.equal('Too late for this block');
-  }));
-
-  it('a block with older number than current should fail', test(rules.GLOBAL.checkNumber, blocks.OLD_BLOCK_NUMBER, {
-    getCurrentBlockOrNull: () => Q({ number: 50, hash: '4C8800825C44A22F230AFC0D140BF1930331A686899D16EBE4C58C9F34C609E8', issuer: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', membersCount: 3 })
-  }, function (err) {
-    should.exist(err);
-    err.message.should.equal('Too late for this block');
-  }));
-
-  it('a block with too far future number than current should fail', test(rules.GLOBAL.checkNumber, blocks.FAR_FUTURE_BLOCK_NUMBER, {
-    getCurrentBlockOrNull: () => Q({ number: 50, hash: '4C8800825C44A22F230AFC0D140BF1930331A686899D16EBE4C58C9F34C609E8', issuer: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', membersCount: 3 })
-  }, function (err) {
-    should.exist(err);
-    err.message.should.equal('Too early for this block');
+    should.not.exist(err);
   }));
 
   it('a block with wrong PreviousHash should fail', test(rules.GLOBAL.checkPreviousHash, blocks.WRONG_PREVIOUS_HASH, {
@@ -393,46 +341,44 @@ describe("Block global coherence:", function(){
   }));
 
   it('a block not starting with a leading zero should fail', test(rules.GLOBAL.checkProofOfWork, blocks.NO_LEADING_ZERO, {
-    getCurrentBlockOrNull: () => Q({ number: 2 }),
-    lastBlockOfIssuer: () => Q({ number: 2 }),
-    getBlock: () => Q({ powMin: 8 }),
-    getBlocksBetween: () => Q([{ issuer: 'a' }])
+    bcContext: {
+      getIssuerPersonalizedDifficulty: () => Q(8)
+    }
   }, function (err) {
     should.exist(err);
     err.message.should.equal('Wrong proof-of-work level: given 0 zeros and \'F\', required was 0 zeros and an hexa char between [0-7]');
   }));
 
   it('a block requiring 2 leading zeros but providing less should fail', test(rules.GLOBAL.checkProofOfWork, blocks.REQUIRES_7_LEADING_ZEROS, {
-    getCurrentBlockOrNull: () => Q({ number: 2 }),
-    lastBlockOfIssuer: () => Q({ number: 2 }),
-    getBlock: () => Q({ powMin: 6 }),
-    getBlocksBetween: () => Q([{ issuer: 'a' },{ issuer: 'b' }])
+    bcContext: {
+      getIssuerPersonalizedDifficulty: () => Q(12)
+    }
   }, function (err) {
     should.exist(err);
     err.message.should.equal('Wrong proof-of-work level: given 0 zeros and \'B\', required was 0 zeros and an hexa char between [0-3]');
   }));
 
   it('a block requiring 1 leading zeros but providing less should fail', test(rules.GLOBAL.checkProofOfWork, blocks.REQUIRES_6_LEADING_ZEROS, {
-    getCurrentBlockOrNull: () => Q({ number: 2 }),
-    lastBlockOfIssuer: () => Q({ number: 2 }),
-    getBlock: () => Q({ powMin: 8 }),
-    getBlocksBetween: () => Q([{ issuer: 'a' }])
+    bcContext: {
+      getIssuerPersonalizedDifficulty: () => Q(8)
+    }
   }, function (err) {
     should.exist(err);
     err.message.should.equal('Wrong proof-of-work level: given 0 zeros and \'8\', required was 0 zeros and an hexa char between [0-7]');
   }));
 
   it('a block requiring 1 leading zeros as first block of newcomer should succeed', test(rules.GLOBAL.checkProofOfWork, blocks.FIRST_BLOCK_OF_NEWCOMER, {
-    getCurrentBlockOrNull: () => Q(null)
+    bcContext: {
+      getIssuerPersonalizedDifficulty: () => Q(1)
+    }
   }, function (err) {
     should.not.exist(err);
   }));
 
   it('a block requiring 40 leading zeros as second block of newcomer should fail', test(rules.GLOBAL.checkProofOfWork, blocks.SECOND_BLOCK_OF_NEWCOMER, {
-    getCurrentBlockOrNull: () => Q({ number: 2 }),
-    lastBlockOfIssuer: () => Q({ number: 2 }),
-    getBlock: () => Q({ powMin: 160 }),
-    getBlocksBetween: () => Q([{ issuer: 'a' }])
+    bcContext: {
+      getIssuerPersonalizedDifficulty: () => Q(160)
+    }
   }, function (err) {
     should.exist(err);
     err.message.should.equal('Wrong proof-of-work level: given 0 zeros and \'F\', required was 10 zeros and an hexa char between [0-9A-F]');
@@ -701,7 +647,9 @@ function test (rule, raw, dal, callback) {
     return co(function *() {
       let obj = parser.syncWrite(raw);
       let block = new Block(obj);
-      if (rule.length == 2) {
+      if (rule == rules.GLOBAL.checkProofOfWork || rule == rules.GLOBAL.checkVersion) {
+        yield rule(block, dal.bcContext);
+      } else if (rule.length == 2) {
         yield rule(block, dal);
       } else {
         yield rule(block, conf, dal);
@@ -711,14 +659,14 @@ function test (rule, raw, dal, callback) {
   };
 }
 
-function validate (raw, dal, callback) {
+function validate (raw, dal, bcContext, callback) {
   var block;
   return function() {
     return Q.Promise(function(resolve, reject){
       async.waterfall([
         function (next){
           block = new Block(parser.syncWrite(raw));
-          rules.CHECK.ASYNC.ALL_GLOBAL(block, conf, dal, next);
+          rules.CHECK.ASYNC.ALL_GLOBAL(block, conf, dal, bcContext, next);
         }
       ], function (err) {
         err && console.error(err.stack);
diff --git a/test/fast/block/block_local.js b/test/fast/block/block_local.js
index 882f66eda57a9cfd064654dd768c470f40d1f164..ca8c79bded93404d7d4464cdbef9ca251dbf5e7e 100644
--- a/test/fast/block/block_local.js
+++ b/test/fast/block/block_local.js
@@ -54,7 +54,7 @@ describe("Block local coherence", function(){
     it('Block cannot contain a same pubkey more than once in leavers',                                test(rules.LOCAL.checkPubkeyUnicity, blocks.MULTIPLE_LEAVES, 'Block cannot contain a same pubkey more than once in joiners, actives, leavers and excluded'));
     it('Block cannot contain a same pubkey more than once in excluded',                               test(rules.LOCAL.checkPubkeyUnicity, blocks.MULTIPLE_EXCLUDED, 'Block cannot contain a same pubkey more than once in joiners, actives, leavers and excluded'));
     it('Block cannot contain a same pubkey more than once in joiners, actives, leavers and excluded', test(rules.LOCAL.checkPubkeyUnicity, blocks.MULTIPLE_OVER_ALL, 'Block cannot contain a same pubkey more than once in joiners, actives, leavers and excluded'));
-    it('Block cannot have revoked key in joiners,actives,leavers',                                    test(rules.LOCAL.checkRevokedNotInMemberships, blocks.REVOKED_WITH_MEMBERSHIPS, 'A revoked pubkey cannot have a membership in the same block'));
+    it('Block cannot have revoked key in joiners,actives,leavers',                                    test(rules.LOCAL.checkMembershipUnicity, blocks.REVOKED_WITH_MEMBERSHIPS, 'Unicity constraint PUBLIC_KEY on MINDEX is not respected'));
     it('Block cannot have revoked key duplicates',                                                    test(rules.LOCAL.checkRevokedUnicity, blocks.REVOKED_WITH_DUPLICATES, 'A single revocation per member is allowed'));
     it('Block revoked keys must be in excluded',                                                      test(rules.LOCAL.checkRevokedAreExcluded, blocks.REVOKED_NOT_IN_EXCLUDED, 'A revoked member must be excluded'));
     it('Block cannot contain 2 certifications from same issuer',                                      test(rules.LOCAL.checkCertificationOneByIssuer, blocks.MULTIPLE_CERTIFICATIONS_FROM_SAME_ISSUER, 'Block cannot contain two certifications from same issuer'));
diff --git a/test/fast/block/protocol-brg49-version.js b/test/fast/block/protocol-brg49-version.js
new file mode 100644
index 0000000000000000000000000000000000000000..1aa80be8a9a830faa2ebc4e5e95977a2659e821a
--- /dev/null
+++ b/test/fast/block/protocol-brg49-version.js
@@ -0,0 +1,34 @@
+"use strict";
+const co            = require('co');
+const should        = require('should');
+const indexer       = require('../../../app/lib/dup/indexer');
+
+const FAIL = false;
+const SUCCESS = true;
+
+describe("Protocol BR_G49 - Version", function(){
+
+  it('V3 following V2 should fail', () => co(function*(){
+    const HEAD_1 = { number: 17, version: 3 };
+    const HEAD   = { number: 18, version: 2 };
+    indexer.ruleVersion(HEAD, HEAD_1).should.equal(FAIL);
+  }));
+
+  it('V4 following V2 should fail', () => co(function*(){
+    const HEAD_1 = { number: 17, version: 4 };
+    const HEAD   = { number: 18, version: 2 };
+    indexer.ruleVersion(HEAD, HEAD_1).should.equal(FAIL);
+  }));
+
+  it('V3 following V4 should succeed', () => co(function*(){
+    const HEAD_1 = { number: 17, version: 3 };
+    const HEAD   = { number: 18, version: 4 };
+    indexer.ruleVersion(HEAD, HEAD_1).should.equal(SUCCESS);
+  }));
+
+  it('V3 following V5 should fail', () => co(function*(){
+    const HEAD_1 = { number: 17, version: 3 };
+    const HEAD   = { number: 18, version: 5 };
+    indexer.ruleVersion(HEAD, HEAD_1).should.equal(FAIL);
+  }));
+});
diff --git a/test/fast/block/protocol-brg50-blocksize.js b/test/fast/block/protocol-brg50-blocksize.js
new file mode 100644
index 0000000000000000000000000000000000000000..475934eaf069d12140c16581dae8521dc82a16b0
--- /dev/null
+++ b/test/fast/block/protocol-brg50-blocksize.js
@@ -0,0 +1,50 @@
+"use strict";
+const co            = require('co');
+const should        = require('should');
+const indexer       = require('../../../app/lib/dup/indexer');
+
+const FAIL = false;
+const SUCCESS = true;
+
+describe("Protocol BR_G50 - Block size", function(){
+
+  it('2 for an AVG(10) should succeed', () => co(function*(){
+    const HEAD   = { bsize: 2, avgBlockSize: 10 };
+    indexer.ruleBlockSize(HEAD).should.equal(SUCCESS);
+  }));
+
+  it('400 for an AVG(10) should succeed', () => co(function*(){
+    const HEAD   = { bsize: 400, avgBlockSize: 10 };
+    indexer.ruleBlockSize(HEAD).should.equal(SUCCESS);
+  }));
+
+  it('499 for an AVG(10) should succeed', () => co(function*(){
+    const HEAD   = { bsize: 499, avgBlockSize: 10 };
+    indexer.ruleBlockSize(HEAD).should.equal(SUCCESS);
+  }));
+
+  it('500 for an AVG(10) should fail', () => co(function*(){
+    const HEAD   = { bsize: 500, avgBlockSize: 10 };
+    indexer.ruleBlockSize(HEAD).should.equal(FAIL);
+  }));
+
+  it('500 for an AVG(454) should fail', () => co(function*(){
+    const HEAD   = { bsize: 500, avgBlockSize: 454 };
+    indexer.ruleBlockSize(HEAD).should.equal(FAIL);
+  }));
+
+  it('500 for an AVG(455) should succeed', () => co(function*(){
+    const HEAD   = { bsize: 500, avgBlockSize: 455 };
+    indexer.ruleBlockSize(HEAD).should.equal(SUCCESS);
+  }));
+
+  it('1100 for an AVG(1000) should fail', () => co(function*(){
+    const HEAD   = { bsize: 1100, avgBlockSize: 1000 };
+    indexer.ruleBlockSize(HEAD).should.equal(FAIL);
+  }));
+
+  it('1100 for an AVG(1001) should succeed', () => co(function*(){
+    const HEAD   = { bsize: 1100, avgBlockSize: 1001 };
+    indexer.ruleBlockSize(HEAD).should.equal(SUCCESS);
+  }));
+});
diff --git a/test/fast/block/protocol-brg51-number.js b/test/fast/block/protocol-brg51-number.js
new file mode 100644
index 0000000000000000000000000000000000000000..12abe494540e15401dc9eded2d523bec30395b58
--- /dev/null
+++ b/test/fast/block/protocol-brg51-number.js
@@ -0,0 +1,67 @@
+"use strict";
+const co            = require('co');
+const should        = require('should');
+const indexer       = require('../../../app/lib/dup/indexer');
+
+const FAIL = false;
+const SUCCESS = true;
+
+describe("Protocol BR_G51 - Number", function(){
+
+  it('1 following 1 should fail', () => co(function*(){
+    const block  = { number: 1 };
+    const HEAD_1 = { number: 1 };
+    const HEAD   = {};
+    indexer.prepareNumber(HEAD, HEAD_1);
+    indexer.ruleNumber(block, HEAD).should.equal(FAIL);
+  }));
+
+  it('1 following 0 should succeed', () => co(function*(){
+    const block  = { number: 1 };
+    const HEAD_1 = { number: 0 };
+    const HEAD   = {};
+    indexer.prepareNumber(HEAD, HEAD_1);
+    indexer.ruleNumber(block, HEAD).should.equal(SUCCESS);
+  }));
+
+  it('0 following 0 should fail', () => co(function*(){
+    const block  = { number: 0 };
+    const HEAD_1 = { number: 0 };
+    const HEAD   = {};
+    indexer.prepareNumber(HEAD, HEAD_1);
+    indexer.ruleNumber(block, HEAD).should.equal(FAIL);
+  }));
+
+  it('0 following nothing should succeed', () => co(function*(){
+    const block  = { number: 0 };
+    const HEAD_1 = null;
+    const HEAD   = {};
+    indexer.prepareNumber(HEAD, HEAD_1);
+    indexer.ruleNumber(block, HEAD).should.equal(SUCCESS);
+  }));
+
+  it('4 following nothing should fail', () => co(function*(){
+    const block  = { number: 4 };
+    const HEAD_1 = null;
+    const HEAD   = {};
+    indexer.prepareNumber(HEAD, HEAD_1);
+    indexer.ruleNumber(block, HEAD).should.equal(FAIL);
+  }));
+
+  it('4 following 2 should fail', () => co(function*(){
+    const block  = { number: 4 };
+    const HEAD_1 = { number: 2 };
+    const HEAD   = {};
+    indexer.prepareNumber(HEAD, HEAD_1);
+    indexer.ruleNumber(block, HEAD).should.equal(FAIL);
+  }));
+
+  it('4 following 3 should succeed', () => co(function*(){
+    const block  = { number: 4 };
+    const HEAD_1 = { number: 3 };
+    const HEAD   = {};
+    indexer.prepareNumber(HEAD, HEAD_1);
+    indexer.ruleNumber(block, HEAD).should.equal(SUCCESS);
+  }));
+
+});
diff --git a/test/fast/database.js b/test/fast/database.js
index bfa47986b358f2f759d760d663a1cb56c5c04284..f761b82953ac3178421ce9dbd2b9bea514186de4 100644
--- a/test/fast/database.js
+++ b/test/fast/database.js
@@ -38,7 +38,7 @@ describe("SQLite driver", function() {
       yield driver.closeConnection();
       yield driver.executeAll(SELECT_FROM_TABLE, [])
         .then(() => 'Should have thrown an exception')
-        .catch((err) => err.should.have.property('message').match(/^SQLITE_ERROR: no such table: duniter$/));
+        .catch((err) => err.should.have.property('message').match(/SQLITE_ERROR: no such table: duniter/));
       // But if we populate it again, it will work
       yield driver.executeSql(CREATE_TABLE_SQL);
       rows = yield driver.executeAll(SELECT_FROM_TABLE, []);
@@ -48,7 +48,7 @@ describe("SQLite driver", function() {
       yield driver.destroyDatabase();
       yield driver.executeAll(SELECT_FROM_TABLE, [])
         .then(() => 'Should have thrown an exception')
-        .catch((err) => err.should.have.property('message').match(/^SQLITE_ERROR: no such table: duniter$/));
+        .catch((err) => err.should.have.property('message').match(/SQLITE_ERROR: no such table: duniter/));
       // But if we populate it again, it will work
       yield driver.executeSql(CREATE_TABLE_SQL);
       rows = yield driver.executeAll(SELECT_FROM_TABLE, []);
@@ -79,7 +79,7 @@ describe("SQLite driver", function() {
       yield driver.destroyDatabase();
       yield driver.executeAll(SELECT_FROM_TABLE, [])
         .then(() => 'Should have thrown an exception')
-        .catch((err) => err.should.have.property('message').match(/^SQLITE_ERROR: no such table: duniter$/));
+        .catch((err) => err.should.have.property('message').match(/SQLITE_ERROR: no such table: duniter/));
     }));
 
     it('should be able to open the file after being removed', () => co(function*() {
diff --git a/test/fast/v1.0-local-index.js b/test/fast/v1.0-local-index.js
new file mode 100644
index 0000000000000000000000000000000000000000..87402da287adadb9d3c2b233fb38bdb6bfa0c6ea
--- /dev/null
+++ b/test/fast/v1.0-local-index.js
@@ -0,0 +1,154 @@
+"use strict";
+
+const _       = require('underscore');
+const should  = require('should');
+const parsers = require('../../app/lib/streams/parsers');
+const indexer = require('../../app/lib/dup/indexer');
+const constants = require('../../app/lib/constants');
+
+const raw = "Version: 5\n" +
+  "Type: Block\n" +
+  "Currency: beta_brousouf\n" +
+  "Number: 10\n" +
+  "PoWMin: 1\n" +
+  "Time: 1411785481\n" +
+  "MedianTime: 1411776000\n" +
+  "UnitBase: 2\n" +
+  "Issuer: HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd\n" +
+  "IssuersFrame: 100\n" +
+  "IssuersFrameVar: 0\n" +
+  "DifferentIssuersCount: 3\n" +
+  "PreviousHash: 2A27BD040B16B7AF59DDD88890E616987F4DD28AA47B9ABDBBEE46257B88E945\n" +
+  "PreviousIssuer: HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd\n" +
+  "MembersCount: 3\n" +
+  "Identities:\n" +
+  "HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd:CTmlh3tO4B8f8IbL8iDy5ZEr3jZDcxkPmDmRPQY74C39MRLXi0CKUP+oFzTZPYmyUC7fZrUXrb3LwRKWw1jEBQ==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:cat\n" +
+  "Joiners:\n" +
+  "HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd:iSQvl1VVc6+b1AUaBJ/VTTurGGHgaIcjASBhIlzI7M/7KVQV2Wi3oGUZUzLWqCAtGUsPcsj1HCV2/sRyxHmqAw==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:cat\n" +
+  "65dKz7JEvZzy6Znr9hATtvm7Kd9fCwxhWKgyrbyL2jhX:25xK7+ph7IYeN9Hu8PvuIBjYdVURYtvKayPHZg7zrrYTs6ii2fMtk5J65a3bT/NKr2Qsd7I5TCL29QyiAXa7BA==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:tac\n" +
+  "Actives:\n" +
+  "F5PtTpt8QFYMGtpZaETygB2C2yxCSxH1UW1VopBNZ6qg:ze+ftHWFLYmjfvXyrx4a15N2VQjf6oen8kkMiYNYrVllbpb5IUcb28CenlOQbVd9cZCNGSkTP7xP5bt8KAqUAw==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:toc\n" +
+  "Leavers:\n" +
+  "HEgBcwtkrnWBgwDqELYht6aBZrmjm8jQY4DtFRjcB437:25xK7+ph7IYeN9Hu8PvuIBjYdVURYtvKayPHZg7zrrYTs6ii2fMtk5J65a3bT/NKr2Qsd7I5TCL29QyiAXa7BA==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:tac\n" +
+  "Revoked:\n" +
+  "EKWJvwPaYuLTv1VoCEtZLmtUTxTC5gWVfdWeRgKgZChN:iSQvl1VVc6+b1AUaBJ/VTTurGGHgaIcjASBhIlzI7M/7KVQV2Wi3oGUZUzLWqCAtGUsPcsj1HCV2/sRyxHmqAw==\n" +
+  "Excluded:\n" +
+  "EKWJvwPaYuLTv1VoCEtZLmtUTxTC5gWVfdWeRgKgZChN\n" +
+  "BNmj8fnZuDtpvismiWnFneJkPHpB98bZdc5ozNYzBW78\n" +
+  "Certifications:\n" +
+  "G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU:HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd:0:CK6UDDJM3d0weE1RVtzFJnw/+J507lPAtspleHc59T4+N1tzQj1RRGWrzPiTknCjnCO6SxBSJX0B+MIUWrpNAw==\n" +
+  "F5PtTpt8QFYMGtpZaETygB2C2yxCSxH1UW1VopBNZ6qg:HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd:0:a7SFapoVaXq27NU+wZj4afmxp0SbwLGqLJih8pfX6TRKPvNp/V93fbKixbqg10cwa1CadNenztxq3ZgOivqADw==\n" +
+  "HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd:G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU:0:bJyoM2Tz4hltVXkLvYHOOmLP4qqh2fx7aMLkS5q0cMoEg5AFER3iETj13uoFyhz8yiAKESyAZSDjjQwp8A1QDw==\n" +
+  "F5PtTpt8QFYMGtpZaETygB2C2yxCSxH1UW1VopBNZ6qg:G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU:0:h8D/dx/z5K2dx06ktp7fnmLRdxkdV5wRkJgnmEvKy2k55mM2RyREpHfD7t/1CC5Ew+UD0V9N27PfaoLxZc1KCQ==\n" +
+  "HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd:F5PtTpt8QFYMGtpZaETygB2C2yxCSxH1UW1VopBNZ6qg:0:eefk9Gg0Ijz0GvrNnRc55CCCBd4yk8j0fNzWzVZFKR3kZ7lsKav6dWyAsaVhlNG5S6XwEwvPoMwKJq1Vn7OjBg==\n" +
+  "Transactions:\n" +
+  "TX:3:1:6:6:8:1:0\n" +
+  "33753-0000054FC8AC7B450BA7D8BA7ED873FEDD5BF1E98D5D3B0DEE38DED55CB80CB3\n" +
+  "G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU\n" +
+  "150605:3:T:01B1AB40E7C1021712FF40D5605037C0ACEECA547BF519ABDCB6473A9F6BDF45:1\n" +
+  "297705:3:D:G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU:33530\n" +
+  "2244725:3:T:507CBE120DB654645B55431A9967789ACB7CD260EA962B839F1708834D1E5491:0\n" +
+  "972091:2:D:G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU:30324\n" +
+  "3808457:2:T:657229C5433FB9FFE64BF2E795E79DA796E0B1AF536DC740ECC26CCBBE104C33:1\n" +
+  "4:2:T:507CBE120DB654645B55431A9967789ACB7CD260EA962B839F1708834D1E5491:1\n" +
+  "0:SIG(0)\n" +
+  "1:SIG(0)\n" +
+  "2:SIG(0)\n" +
+  "3:SIG(0)\n" +
+  "4:SIG(0)\n" +
+  "5:SIG(0)\n" +
+  "3171064:3:SIG(5ocqzyDMMWf1V8bsoNhWb1iNwax1e9M7VTUN6navs8of)\n" +
+  "3:2:SIG(5ocqzyDMMWf1V8bsoNhWb1iNwax1e9M7VTUN6navs8of)\n" +
+  "4:1:SIG(5ocqzyDMMWf1V8bsoNhWb1iNwax1e9M7VTUN6navs8of)\n" +
+  "8:0:SIG(5ocqzyDMMWf1V8bsoNhWb1iNwax1e9M7VTUN6navs8of)\n" +
+  "25:3:SIG(G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU)\n" +
+  "8:2:SIG(G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU)\n" +
+  "5:1:SIG(G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU)\n" +
+  "2:0:SIG(G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU)\n" +
+  "all 10.6517\n" +
+  "42yQm4hGTJYWkPg39hQAUgP6S6EQ4vTfXdJuxKEHL1ih6YHiDL2hcwrFgBHjXLRgxRhj2VNVqqc6b4JayKqTE14r\n" +
+  "TX:3:1:1:1:1:0:0\n" +
+  "5-2C31D8915801E759F6D4FF3DA8DA983D7D56DCF4F8D94619FCFAD4B128362326\n" +
+  "HsLShAtzXTVxeUtQd7yi5Z5Zh4zNvbu8sTEZ53nfKcqY\n" +
+  "10:3:T:2C31D8915801E759F6D4FF3DA8DA983D7D56DCF4F8D94619FCFAD4B128362326:88\n" +
+  "0:SIG(0)\n" +
+  "1:4:SIG(BYfWYFrsyjpvpFysgu19rGK3VHBkz4MqmQbNyEuVU64g)\n" +
+  "I6gJkJIQJ9vwDRXZ6kdBsOArQ3zzMYPmFxDbJqseBVq5NWlmJ7l7oY9iWtqhPF38rp7/iitbgyftsRR8djOGDg==\n" +
+  "InnerHash: DE837CA3F49C423A6A6C124819ABA31A41C1C4A4E2728B5721DF891B98FA8D0D\n" +
+  "Nonce: 1\n" +
+  "kNsKdC8eH0d4zdHh1djyMzRXjFrwk3Bc3M8wo4DV/7clE9J66K/U0FljyS79SI78ZZUPaVmrImKJ9SNiubCiBg==\n";
+
+describe("v1.0 Local Index", function(){
+
+  const block = parsers.parseBlock.syncWrite(raw);
+  const index = indexer.localIndex(block, { sigValidity: 100, msValidity: 40 });
+
+  it('should have 30 index entries', () => {
+    index.should.have.length(30);
+  });
+
+  /*********
+   * IINDEX
+   ********/
+
+  it('should have 4 iindex entries', () => {
+    _(index).where({ index: constants.I_INDEX}).should.have.length(4);
+  });
+
+  it('should have 1 iindex CREATE entries', () => {
+    _(index).where({ index: constants.I_INDEX, op: constants.IDX_CREATE }).should.have.length(1);
+  });
+
+  it('should have 3 iindex UPDATE entries', () => {
+    _(index).where({ index: constants.I_INDEX, op: constants.IDX_UPDATE }).should.have.length(3);
+  });
+
+  /*********
+   * MINDEX
+   ********/
+
+  it('should have 5 mindex entries', () => {
+    _(index).where({ index: constants.M_INDEX}).should.have.length(5);
+  });
+
+  it('should have 1 mindex CREATE entries', () => {
+    _(index).where({ index: constants.M_INDEX, op: constants.IDX_CREATE }).should.have.length(1);
+  });
+
+  it('should have 4 mindex UPDATE entries', () => {
+    _(index).where({ index: constants.M_INDEX, op: constants.IDX_UPDATE }).should.have.length(4);
+  });
+
+  /*********
+   * CINDEX
+   ********/
+
+  it('should have 5 cindex entries', () => {
+    _(index).where({ index: constants.C_INDEX}).should.have.length(5);
+  });
+
+  it('should have 5 cindex CREATE entries', () => {
+    _(index).where({ index: constants.C_INDEX, op: constants.IDX_CREATE }).should.have.length(5);
+  });
+
+  it('should have 0 cindex UPDATE entries', () => {
+    _(index).where({ index: constants.C_INDEX, op: constants.IDX_UPDATE }).should.have.length(0);
+  });
+
+  /*********
+   * SINDEX
+   ********/
+
+  it('should have 16 cindex entries', () => {
+    _(index).where({ index: constants.S_INDEX}).should.have.length(16);
+  });
+
+  it('should have 9 cindex CREATE entries', () => {
+    _(index).where({ index: constants.S_INDEX, op: constants.IDX_CREATE }).should.have.length(9);
+  });
+
+  it('should have 7 cindex UPDATE entries', () => {
+    _(index).where({ index: constants.S_INDEX, op: constants.IDX_UPDATE }).should.have.length(7);
+  });
+
+});
diff --git a/test/integration/branches_pending_data.js b/test/integration/branches_pending_data.js
index 8b4fbb51aa054fb257cd1c875fd9a8ca7cef3ea3..5eb7f35da4cb08801c18a44341917d258dd6f765 100644
--- a/test/integration/branches_pending_data.js
+++ b/test/integration/branches_pending_data.js
@@ -2,7 +2,7 @@
 
 const co = require('co');
 const _         = require('underscore');
-const ucoin     = require('../../index');
+const duniter   = require('../../index');
 const bma       = require('../../app/lib/streams/bma');
 const user      = require('./tools/user');
 const rp        = require('request-promise');
@@ -22,7 +22,7 @@ const commonConf = {
   sigQty: 1
 };
 
-const s1 = ucoin({
+const s1 = duniter({
   memory: MEMORY_MODE,
   name: 'bb6'
 }, _.extend({
diff --git a/test/integration/branches_revert.js b/test/integration/branches_revert.js
index fee75afc00b922b5646b7738ddc3d82269b2e8ac..066b05ad5226add5711016dd69dbf9d37d8c9a54 100644
--- a/test/integration/branches_revert.js
+++ b/test/integration/branches_revert.js
@@ -2,17 +2,11 @@
 
 const co = require('co');
 const _         = require('underscore');
-const ucoin     = require('../../index');
 const bma       = require('../../app/lib/streams/bma');
 const user      = require('./tools/user');
-const rp        = require('request-promise');
-const httpTest  = require('./tools/http');
+const toolbox   = require('./tools/toolbox');
 const commit    = require('./tools/commit');
 
-const expectJSON     = httpTest.expectJSON;
-const expectHttpCode = httpTest.expectHttpCode;
-
-const MEMORY_MODE = true;
 const commonConf = {
   ipv4: '127.0.0.1',
   currency: 'bb',
@@ -22,17 +16,13 @@ const commonConf = {
   sigQty: 1
 };
 
-const s1 = ucoin({
-  memory: MEMORY_MODE,
-  name: 'bb11'
-}, _.extend({
-  port: '7711',
+const s1 = toolbox.server(_.extend({
   pair: {
     pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd',
     sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'
   },
   participate: false, rootoffset: 10,
-  sigQty: 1, dt: 0, ud0: 120
+  sigQty: 1, dt: 1, ud0: 120
 }, commonConf));
 
 const cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
@@ -51,36 +41,24 @@ describe("Revert root", function() {
       yield cat.join();
       yield toc.join();
       yield commit(s1)();
-      yield s1.revert();
-      yield commit(s1)();
-      yield commit(s1)();
     });
   });
 
-  describe("Server 1 /blockchain", function() {
-
-    it('/block/0 should exist', function() {
-      return expectJSON(rp('http://127.0.0.1:7711/blockchain/block/0', { json: true }), {
-        number: 0
-      });
-    });
-
-    it('/block/1 should exist', function() {
-      return expectJSON(rp('http://127.0.0.1:7711/blockchain/block/1', { json: true }), {
-        number: 1
-      });
-    });
+  it('/block/0 should exist', () => s1.expectJSON('/blockchain/block/0', {
+    number: 0
+  }));
 
-    it('/block/2 should exist', function() {
-      return httpTest.expectError(404, 'Block not found', rp('http://127.0.0.1:7711/blockchain/block/2', { json: true }));
-    });
+  it('/wot/cat should exist', () => s1.expectThat('/wot/lookup/cat', (res) => {
+    res.should.have.property('results').length(1);
+    res.results[0].should.have.property('uids').length(1);
+    res.results[0].uids[0].should.have.property('uid').equal('cat');
+    res.results[0].uids[0].should.have.property('others').length(1);
+  }));
 
-    // Revert then Commit should be a neutral operation
-
-    //it('should conserve newcomers', function() {});
-    //it('should conserve identity joining back', function() {});
-    //it('should conserve identity active', function() {});
-    //it('should conserve identity leaving', function() {});
-    //it('should conserve excluded', function() {});
-  });
+  it('reverting should erase everything', () => co(function*() {
+    yield s1.revert();
+    yield s1.expectError('/blockchain/current', 404, 'No current block');
+    yield s1.expectError('/blockchain/block/0', 404, 'Block not found');
+    yield s1.expectError('/wot/lookup/cat', 404, 'No matching identity'); // Revert completely removes the identity
+  }));
 });
diff --git a/test/integration/branches_revert2.js b/test/integration/branches_revert2.js
index 04a8529cbb085550c8b756b2281e505507391a40..c364c848ba0c485493f86610e9483b028cb4f920 100644
--- a/test/integration/branches_revert2.js
+++ b/test/integration/branches_revert2.js
@@ -33,7 +33,7 @@ const s1 = ucoin({
     sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'
   },
   participate: false, rootoffset: 10,
-  sigQty: 1, dt: 0, ud0: 120
+  sigQty: 1, dt: 1, ud0: 120
 }, commonConf));
 
 const cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
@@ -41,6 +41,8 @@ const toc = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', s
 
 describe("Revert two blocks", function() {
 
+  const now = Math.floor(Date.now() / 1000);
+
   before(function() {
 
     return co(function *() {
@@ -51,10 +53,11 @@ describe("Revert two blocks", function() {
       yield cat.cert(toc);
       yield cat.join();
       yield toc.join();
-      yield commit(s1)({ version: 2 });
-      yield commit(s1)({ version: 2 });
+      yield commit(s1)({ version: 2, time: now });
+      yield commit(s1)({ version: 2, time: now + 10 });
+      yield commit(s1)({ version: 2, time: now + 10 });
       yield cat.sendP(51, toc);
-      yield commit(s1)({ version: 2 });
+      yield commit(s1)({ version: 2, time: now + 10 });
       yield s1.revert();
     });
   });
@@ -68,8 +71,8 @@ describe("Revert two blocks", function() {
     });
 
     it('/block/1 should exist', function() {
-      return expectJSON(rp('http://127.0.0.1:7712/blockchain/block/1', { json: true }), {
-        number: 1,
+      return expectJSON(rp('http://127.0.0.1:7712/blockchain/block/2', { json: true }), {
+        number: 2,
         dividend: 120
       });
     });
@@ -80,7 +83,7 @@ describe("Revert two blocks", function() {
         res.sources.should.have.length(1);
         res.sources[0].should.have.property('identifier').equal('HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd');
         res.sources[0].should.have.property('type').equal('D');
-        res.sources[0].should.have.property('noffset').equal(1);
+        res.sources[0].should.have.property('noffset').equal(2);
         res.sources[0].should.have.property('amount').equal(120);
       });
     });
@@ -91,7 +94,7 @@ describe("Revert two blocks", function() {
         res.sources.should.have.length(1);
         res.sources[0].should.have.property('identifier').equal('DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo');
         res.sources[0].should.have.property('type').equal('D');
-        res.sources[0].should.have.property('noffset').equal(1);
+        res.sources[0].should.have.property('noffset').equal(2);
         res.sources[0].should.have.property('amount').equal(120);
       });
     });
diff --git a/test/integration/branches_revert_memberships.js b/test/integration/branches_revert_memberships.js
index 2ae7efc431cfd1e722400c2bbfdec99483de24ad..35479468077a5d325d0b67825569f710ebc887c2 100644
--- a/test/integration/branches_revert_memberships.js
+++ b/test/integration/branches_revert_memberships.js
@@ -5,7 +5,6 @@ const should    = require('should');
 const bma       = require('../../app/lib/streams/bma');
 const user      = require('./tools/user');
 const commit    = require('./tools/commit');
-const until     = require('./tools/until');
 const toolbox   = require('./tools/toolbox');
 const limiter   = require('../../app/lib/system/limiter');
 const multicaster = require('../../app/lib/streams/multicaster');
@@ -25,7 +24,7 @@ const i3 = user('i3',   { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', s
 
 describe("Revert memberships", function() {
 
-  const now = Math.round(Date.now() / 1000);
+  const now = 1482000000;
 
   before(() => co(function*() {
 
@@ -106,7 +105,7 @@ describe("Revert memberships", function() {
   }));
 
   it('should exist a kicked member', () => co(function*() {
-    yield s1.commit({ time: now + 55 });
+    yield s1.commit({ time: now + 25 });
     // yield s1.expect('/blockchain/current', (res) => { res.number.should.equal(7); (res.medianTime - now).should.equal(25); });
     // yield s1.commit({ time: now + 30 });
     // yield s1.expect('/blockchain/current', (res) => { res.number.should.equal(8); (res.medianTime - now).should.equal(18); });
@@ -133,46 +132,45 @@ describe("Revert memberships", function() {
   it('revert the join back', () => co(function*() {
     yield s1.revert();
     yield shouldHaveBeenKicked();
-    yield shouldHavePendingMS(1); // Keep track of undone membership
+    yield shouldHavePendingMS(0); // Undone memberships are lost
   }));
 
   it('revert excluded member', () => co(function*() {
     yield s1.revert();
     yield shouldBeBeingKicked();
-    yield shouldHavePendingMS(1); // Keep track of undone membership
+    yield shouldHavePendingMS(0); // Undone memberships are lost
   }));
 
   it('revert being kicked', () => co(function*() {
     yield s1.revert();
     yield shouldBeLeaving();
-    yield shouldHavePendingMS(1); // Keep track of undone membership
+    yield shouldHavePendingMS(0); // Undone memberships are lost
   }));
 
   it('revert leaving', () => co(function*() {
     yield s1.revert();
     yield shouldBeRenewed();
-    yield shouldHavePendingMS(2); // Keep track of undone membership
+    yield shouldHavePendingMS(0); // Undone memberships are lost
   }));
 
   it('revert 2 neutral blocks for i3', () => co(function*() {
     yield s1.revert();
     yield shouldBeRenewed();
-    yield shouldHavePendingMS(4); // Keep track of undone membership
+    yield shouldHavePendingMS(0); // Undone memberships are lost
     yield s1.revert();
     yield shouldBeRenewed();
-    yield shouldHavePendingMS(4); // Keep track of undone membership
+    yield shouldHavePendingMS(0); // Undone memberships are lost
   }));
 
   it('revert renewal block', () => co(function*() {
     yield s1.revert();
     yield shouldHaveJoined();
-    yield shouldHavePendingMS(5); // Keep track of undone membership
+    yield shouldHavePendingMS(0); // Undone memberships are lost
   }));
 
   it('revert join block', () => co(function*() {
     yield s1.revert();
-    yield shouldBeJoining();
-    yield shouldHavePendingMS(6); // Keep track of undone membership
+    yield shouldHavePendingMS(0); // Undone memberships are lost
   }));
 
   /*********
@@ -192,13 +190,10 @@ describe("Revert memberships", function() {
   function shouldBeFreshlyCreated() {
     return co(function*() {
       const idty = (yield s1.dal.idtyDAL.searchThoseMatching(i3.pub))[0];
-      idty.should.have.property('currentMSN').equal(-1);
-      idty.should.have.property('currentINN').equal(-1);
       idty.should.have.property('wasMember').equal(false);
       idty.should.have.property('written').equal(false);
       idty.should.have.property('kick').equal(false);
       idty.should.have.property('member').equal(false);
-      idty.should.have.property('leaving').equal(false);
     });
   }
 
@@ -208,80 +203,56 @@ describe("Revert memberships", function() {
 
   function shouldHaveJoined() {
     return co(function*() {
-      const idty = yield s1.dal.idtyDAL.getFromPubkey(i3.pub);
-      idty.should.have.property('currentMSN').equal(1);
-      idty.should.have.property('currentINN').equal(1);
+      const idty = yield s1.dal.iindexDAL.getFromPubkey(i3.pub);
       idty.should.have.property('wasMember').equal(true);
-      idty.should.have.property('written').equal(true);
       idty.should.have.property('kick').equal(false);
       idty.should.have.property('member').equal(true);
-      idty.should.have.property('leaving').equal(false);
     });
   }
 
   function shouldBeRenewed() {
     return co(function*() {
-      const idty = yield s1.dal.idtyDAL.getFromPubkey(i3.pub);
-      idty.should.have.property('currentMSN').equal(2);
-      idty.should.have.property('currentINN').equal(2);
+      const idty = yield s1.dal.iindexDAL.getFromPubkey(i3.pub);
       idty.should.have.property('wasMember').equal(true);
-      idty.should.have.property('written').equal(true);
       idty.should.have.property('kick').equal(false);
       idty.should.have.property('member').equal(true);
-      idty.should.have.property('leaving').equal(false);
     });
   }
 
   function shouldBeLeaving() {
     return co(function*() {
-      const idty = yield s1.dal.idtyDAL.getFromPubkey(i3.pub);
-      idty.should.have.property('currentMSN').equal(5);
-      idty.should.have.property('currentINN').equal(2);
+      const idty = yield s1.dal.iindexDAL.getFromPubkey(i3.pub);
       idty.should.have.property('wasMember').equal(true);
-      idty.should.have.property('written').equal(true);
       idty.should.have.property('kick').equal(false);
       idty.should.have.property('member').equal(true);
-      idty.should.have.property('leaving').equal(true);
     });
   }
 
   function shouldBeBeingKicked() {
     return co(function*() {
-      // Should be set as kicked bow
-      const idty = yield s1.dal.idtyDAL.getFromPubkey(i3.pub);
-      idty.should.have.property('currentMSN').equal(5);
-      idty.should.have.property('currentINN').equal(2);
+      // Should be set as kicked now
+      const idty = yield s1.dal.iindexDAL.getFromPubkey(i3.pub);
       idty.should.have.property('wasMember').equal(true);
-      idty.should.have.property('written').equal(true);
       idty.should.have.property('kick').equal(true);
       idty.should.have.property('member').equal(true);
-      idty.should.have.property('leaving').equal(true);
     });
   }
 
   function shouldHaveBeenKicked() {
     return co(function*() {
-      const idty = yield s1.dal.idtyDAL.getFromPubkey(i3.pub);
-      idty.should.have.property('currentMSN').equal(5);
-      idty.should.have.property('currentINN').equal(2);
+      const idty = yield s1.dal.iindexDAL.getFromPubkey(i3.pub);
       idty.should.have.property('wasMember').equal(true);
-      idty.should.have.property('written').equal(true);
       idty.should.have.property('kick').equal(false);
       idty.should.have.property('member').equal(false);
-      idty.should.have.property('leaving').equal(true);
     });
   }
 
   function shouldHaveComeBack() {
     return co(function*() {
-      let idty = yield s1.dal.idtyDAL.getFromPubkey(i3.pub);
-      idty.should.have.property('currentMSN').equal(8);
-      idty.should.have.property('currentINN').equal(8);
+      let idty = yield s1.dal.iindexDAL.getFromPubkey(i3.pub);
       idty.should.have.property('wasMember').equal(true);
-      idty.should.have.property('written').equal(true);
       idty.should.have.property('kick').equal(false);
       idty.should.have.property('member').equal(true);
-      idty.should.have.property('leaving').equal(false);
     });
   }
 });
diff --git a/test/integration/branches_switch.js b/test/integration/branches_switch.js
index 22a0bac6c0e0d693e47faadb3acd57b98f70ffb4..c16b1ec12a6f00e3190c1b99c1ca6226c85bd014 100644
--- a/test/integration/branches_switch.js
+++ b/test/integration/branches_switch.js
@@ -34,7 +34,7 @@ const s1 = ucoin({
     sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'
   },
   participate: false, rootoffset: 10,
-  sigQty: 1, dt: 0, ud0: 120
+  sigQty: 1, dt: 1, ud0: 120
 }, commonConf));
 
 const s2 = ucoin({
@@ -105,18 +105,5 @@ describe("Switch", function() {
         number: 8
       });
     });
-
-    it('/block/7 should have valid monetary mass', function() {
-      return co(function *() {
-        let block = yield s1.dal.getBlock(7);
-        block.should.have.property('UDTime').not.equal(null);
-      });
-    });
-
-    it('/block/8 should have valid monetary mass', () => co(function *() {
-        let block = yield s1.dal.getBlock(8);
-        block.should.have.property('UDTime').not.equal(null);
-      })
-    );
   });
 });
diff --git a/test/integration/certification_chainability.js b/test/integration/certification_chainability.js
index 5497764141be892c303ebbd2cd47f095ab5c53ba..4fa896612010befedfa7a24d242b115ea0c85977 100644
--- a/test/integration/certification_chainability.js
+++ b/test/integration/certification_chainability.js
@@ -44,7 +44,7 @@ describe("Certification chainability", function() {
 
   before(function() {
 
-    const now = Math.round(new Date().getTime() / 1000);
+    const now = 1482220000;
 
     const commitS1 = commit(s1);
 
diff --git a/test/integration/collapse.js b/test/integration/collapse.js
index 8a49e456724c899fac222cd3b73e19ba78d07fa2..79878d625a71ab4e63421e908469a623c3bd26b1 100644
--- a/test/integration/collapse.js
+++ b/test/integration/collapse.js
@@ -29,27 +29,36 @@ const s1 = ucoin({
     sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'
   },
   participate: false, rootoffset: 10,
-  sigQty: 1, dt: 0, ud0: 120
+  sigQty: 1, dt: 100, ud0: 120, sigValidity: 1
 }, commonConf));
 
 const cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+const tac = user('tac', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
 
 describe("Community collapse", function() {
 
+  const now = Math.round(Date.now() / 1000);
+
   before(function() {
 
     return co(function *() {
       yield s1.initWithDAL().then(bma).then((bmapi) => bmapi.openConnections());
       yield cat.createIdentity();
+      yield tac.createIdentity();
       yield cat.join();
-      yield commit(s1)();
-      yield commit(s1)();
+      yield tac.join();
+      yield cat.cert(tac);
+      yield tac.cert(cat);
+      yield commit(s1)({ time: now });
+      yield commit(s1)({ time: now + 10 });
+      yield commit(s1)({ time: now + 10 });
+      yield commit(s1)({ time: now + 10 });
     });
   });
 
   it('should be handled', function() {
-    return httpTest.expectJSON(rp('http://127.0.0.1:9340/blockchain/block/1', { json: true }), {
-      number: 1
+    return httpTest.expectJSON(rp('http://127.0.0.1:9340/blockchain/block/2', { json: true }), {
+      number: 2
     });
   });
 });
diff --git a/test/integration/crosschain-test.js b/test/integration/crosschain-test.js
index 31302c73425bff47ac3337515c0849a8de2d8127..400532538ac2a39b9634e70d38bc22d3bfa9476d 100644
--- a/test/integration/crosschain-test.js
+++ b/test/integration/crosschain-test.js
@@ -14,12 +14,14 @@ const httpTest  = require('./tools/http');
 
 describe("Crosschain transactions", function() {
 
+  const now = Math.round(Date.now() / 1000);
+
   const MEMORY_MODE = true;
   const commonConf = {
     ipv4: '127.0.0.1',
     httpLogs: true,
     forksize: 3,
-    dt: 0, ud0: 120, rootoffset: 10,
+    dt: 1, ud0: 120, rootoffset: 10,
     parcatipate: false, // TODO: to remove when startGeneration will be an explicit call
     sigQty: 1
   };
@@ -70,14 +72,15 @@ describe("Crosschain transactions", function() {
         yield ticB.cert(tocB);
         yield ticB.join();
         yield tocB.join();
-        yield commit(sB)({ version: 2 });
-        yield commit(sB)({ version: 2 });
+        yield commit(sB)({ version: 2, time: now });
+        yield commit(sB)({ version: 2, time: now + 10 });
+        yield commit(sB)({ version: 2, time: now + 10 });
         // Preparation: we create a source transaction for our transfer
         btx0 = yield tocB.prepareITX(120, tocB);
         // We submit it to the network
         yield tocB.sendTX(btx0);
         // Written
-        yield commit(sB)({ version: 2 });
+        yield commit(sB)({ version: 2, time: now + 10 });
 
         // Initialize META
         yield ticM.createIdentity();
@@ -86,8 +89,9 @@ describe("Crosschain transactions", function() {
         yield ticM.cert(tocM);
         yield ticM.join();
         yield tocM.join();
-        yield commit(sM)({ version: 2 });
-        yield commit(sM)({ version: 2 });
+        yield commit(sM)({ version: 2, time: now });
+        yield commit(sM)({ version: 2, time: now + 10 });
+        yield commit(sM)({ version: 2, time: now + 10 });
         // Preparation: we create a source transaction for our transfer
         mtx0 = yield ticM.prepareITX(120, ticM);
         // We submit it to the network
@@ -241,8 +245,9 @@ describe("Crosschain transactions", function() {
         yield ticB.cert(tocB);
         yield ticB.join();
         yield tocB.join();
-        yield commit(sB)({ version: 2 });
-        yield commit(sB)({ version: 2 });
+        yield commit(sB)({ version: 2, time: now });
+        yield commit(sB)({ version: 2, time: now + 10 });
+        yield commit(sB)({ version: 2, time: now + 10 });
         // Preparation: we create a source transaction for our transfer
         btx0 = yield tocB.prepareITX(120, tocB);
         // We submit it to the network
@@ -257,8 +262,9 @@ describe("Crosschain transactions", function() {
         yield ticM.cert(tocM);
         yield ticM.join();
         yield tocM.join();
-        yield commit(sM)({ version: 2 });
-        yield commit(sM)({ version: 2 });
+        yield commit(sM)({ version: 2, time: now });
+        yield commit(sM)({ version: 2, time: now + 10 });
+        yield commit(sM)({ version: 2, time: now + 10 });
         // Preparation: we create a source transaction for our transfer
         mtx0 = yield ticM.prepareITX(120, ticM);
         // We submit it to the network
diff --git a/test/integration/forwarding.js b/test/integration/forwarding.js
index 1fd477872f5b02712cf3c85d03196c6e8bf5a23b..b6ada196caa15f2270d97b98486eb578dcfa9b96 100644
--- a/test/integration/forwarding.js
+++ b/test/integration/forwarding.js
@@ -27,60 +27,47 @@ describe("Forwarding", function() {
     const tic = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, node1);
     const toc = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, node1);
 
-    before(function(done) {
-      Promise.all([node1, node2].map(function(node) {
-        return node
-          .startTesting();
-      }))
-        .then(function() {
-          return new Promise(function(resolve, reject){
-            async.waterfall([
-              function(next) {
-                node2.peering(next);
-              },
-              function(peer, next) {
-                node1.submitPeer(peer, function(err) {
-                  next(err);
-                });
-              },
-              function(next) {
-                node1.peering(next);
-              },
-              function(peer, next) {
-                node2.submitPeer(peer, next);
-              }
-            ], function(err) {
-              err ? reject(err) : resolve();
+    before(() => co(function*(){
+      yield [node1, node2].map((node) => node.startTesting());
+      yield new Promise(function(resolve, reject){
+        async.waterfall([
+          function(next) {
+            node2.peering(next);
+          },
+          function(peer, next) {
+            node1.submitPeer(peer, function(err) {
+              next(err);
             });
-          });
-        })
-        .then(function(){
-          return Promise.all([
-            node2.until('identity', 4),
-            node2.until('block', 1),
-            co(function *() {
-
-              // Self certifications
-              yield cat.createIdentity();
-              yield tac.createIdentity();
-              yield tic.createIdentity();
-              yield toc.createIdentity();
-              // Certifications
-              yield cat.cert(tac);
-              yield tac.cert(cat);
-              yield cat.join();
-              yield tac.join();
-              yield node1.commitP();
-            })
-          ]);
-        })
-        .then(function(){
-          done();
-        })
-        .catch(function(err){
-          done(err);
+          },
+          function(next) {
+            node1.peering(next);
+          },
+          function(peer, next) {
+            node2.submitPeer(peer, next);
+          }
+        ], function(err) {
+          err ? reject(err) : resolve();
         });
-    });
+      });
+      yield [
+        node2.until('identity', 4),
+        node2.until('block', 1),
+        co(function *() {
+
+          // Self certifications
+          yield cat.createIdentity();
+          yield tac.createIdentity();
+          yield tic.createIdentity();
+          yield toc.createIdentity();
+          // Certifications
+          yield cat.cert(tac);
+          yield tac.cert(cat);
+          yield cat.join();
+          yield tac.join();
+          yield node1.commitP();
+        })
+      ];
+    }));
 
     describe("Testing technical API", function(){
 
@@ -118,52 +105,70 @@ function doTests(node) {
     describe("user cat", function(){
 
       it('should give only 1 result', node.lookup('cat', function(res, done){
-        should.exists(res);
-        assert.equal(res.results.length, 1);
-        done();
+        try {
+          should.exists(res);
+          assert.equal(res.results.length, 1);
+          done();
+        } catch (e) {
+          done(e);
+        }
       }));
 
       it('should have sent 1 signature', node.lookup('cat', function(res, done){
-        should.exists(res);
-        assert.equal(res.results[0].signed.length, 1);
-        should.exists(res.results[0].signed[0].isMember);
-        should.exists(res.results[0].signed[0].wasMember);
-        assert.equal(res.results[0].signed[0].isMember, true);
-        assert.equal(res.results[0].signed[0].wasMember, true);
-        done();
+        try {
+          should.exists(res);
+          assert.equal(res.results[0].signed.length, 1);
+          should.exists(res.results[0].signed[0].isMember);
+          should.exists(res.results[0].signed[0].wasMember);
+          assert.equal(res.results[0].signed[0].isMember, true);
+          assert.equal(res.results[0].signed[0].wasMember, true);
+          done();
+        } catch (e) {
+          done(e);
+        }
       }));
     });
 
     describe("user tac", function(){
 
       it('should give only 1 result', node.lookup('tac', function(res, done){
-        should.exists(res);
-        assert.equal(res.results.length, 1);
-        done();
+        try {
+          should.exists(res);
+          assert.equal(res.results.length, 1);
+          done();
+        } catch (e) {
+          done(e);
+        }
       }));
 
       it('should have 1 signature', node.lookup('tac', function(res, done){
-        should.exists(res);
-        assert.equal(res.results[0].uids[0].others.length, 1);
-        done();
+        try {
+          should.exists(res);
+          assert.equal(res.results[0].uids[0].others.length, 1);
+          done();
+        } catch (e) {
+          done(e);
+        }
       }));
 
       it('should have sent 1 signature', node.lookup('tac', function(res, done){
-        should.exists(res);
-        assert.equal(res.results[0].signed.length, 1);
-        done();
+        try {
+          should.exists(res);
+          assert.equal(res.results[0].signed.length, 1);
+          done();
+        } catch (e) {
+          done(e);
+        }
       }));
     });
 
     it('toc should give only 1 result', node.lookup('toc', function(res, done){
-      should.exists(res);
-      assert.equal(res.results.length, 1);
+      should.not.exists(res);
       done();
     }));
 
     it('tic should give only 1 results', node.lookup('tic', function(res, done){
-      should.exists(res);
-      assert.equal(res.results.length, 1);
+      should.not.exists(res);
       done();
     }));
   };
diff --git a/test/integration/identity-expiry.js b/test/integration/identity-expiry.js
index 2b5c8b35401588a62dc56db25205b7134f73f3b6..6a0a67191ce180ac42e2328796a6407d9432b57f 100644
--- a/test/integration/identity-expiry.js
+++ b/test/integration/identity-expiry.js
@@ -11,7 +11,8 @@ const rp        = require('request-promise');
 const httpTest  = require('./tools/http');
 const commit    = require('./tools/commit');
 
-const expectAnswer   = httpTest.expectAnswer;
+const expectAnswer = httpTest.expectAnswer;
+const expectError  = httpTest.expectError;
 
 const MEMORY_MODE = true;
 const commonConf = {
@@ -42,15 +43,15 @@ const tac = user('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', s
 const tic = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
 const toc = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
 
+const now = 1482300000;
+const commitS1 = commit(s1);
+
 describe("Identities expiry", function() {
 
   before(function() {
 
-    const commitS1 = commit(s1);
-
     return co(function *() {
 
-      const now = Math.round(new Date().getTime() / 1000);
       yield s1.initWithDAL().then(bma).then((bmapi) => bmapi.openConnections());
       yield cat.createIdentity();
       yield tac.createIdentity();
@@ -59,25 +60,20 @@ describe("Identities expiry", function() {
       yield tac.cert(cat);
       yield cat.join();
       yield tac.join();
-      yield commitS1();
-      yield toc.createIdentity();
-      yield toc.join();
       yield commitS1({
-        time: now + 10
+        time: now
       });
+      yield toc.createIdentity();
+      yield toc.join();
       yield commitS1({
-        time: now + 10
+        time: now + 5
       });
     });
   });
 
   it('should have requirements failing for tic', function() {
-    return expectAnswer(rp('http://127.0.0.1:8560/wot/requirements/DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', { json: true }), (res) => {
-      res.should.have.property('identities').length(1);
-      res.identities[0].should.have.property('pubkey').equal('DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV');
-      res.identities[0].should.have.property('uid').equal('tic');
-      res.identities[0].should.have.property('expired').equal(true);
-    });
+    // tic has been cleaned up, since its identity has expired after the root block
+    return expectError(404, 'No identity matching this pubkey or uid', rp('http://127.0.0.1:8560/wot/requirements/DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', { json: true }));
   });
 
   it('should have requirements failing for toc', function() {
@@ -85,7 +81,15 @@ describe("Identities expiry", function() {
       res.should.have.property('identities').length(1);
       res.identities[0].should.have.property('pubkey').equal('DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo');
       res.identities[0].should.have.property('uid').equal('toc');
-      res.identities[0].should.have.property('expired').equal(true);
+      res.identities[0].should.have.property('expired').equal(false);
     });
   });
+
+  it('should have requirements failing for toc', () => co(function*() {
+    // tic has been cleaned up after the block#2
+    yield commitS1({
+      time: now + 5
+    });
+    return expectError(404, 'No identity matching this pubkey or uid', rp('http://127.0.0.1:8560/wot/requirements/DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', { json: true }));
+  }));
 });
diff --git a/test/integration/identity-test.js b/test/integration/identity-test.js
index 036ec9dc60edb4f25be96ee40aaf7f7b4f277b4d..bb0a217a2375e3cb7bca51087345453b870d5c2d 100644
--- a/test/integration/identity-test.js
+++ b/test/integration/identity-test.js
@@ -283,26 +283,30 @@ describe("Identities collision", function() {
       res.should.have.property('sigDate').be.a.Number;
       res.should.have.property('certifications').length(2);
       let certs = res.certifications;
-      certs[0].should.have.property('pubkey').equal('DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo');
-      certs[0].should.have.property('uid').equal('toc');
-      certs[0].should.have.property('isMember').equal(true);
-      certs[0].should.have.property('wasMember').equal(true);
-      certs[0].should.have.property('sigDate').be.a.Number;
-      certs[0].should.have.property('cert_time').property('block').be.a.Number;
-      certs[0].should.have.property('cert_time').property('medianTime').be.a.Number;
-      certs[0].should.have.property('written').property('number').equal(0);
-      certs[0].should.have.property('written').property('hash').not.equal('');
-      certs[0].should.have.property('signature').not.equal('');
-      certs[1].should.have.property('pubkey').equal('DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV');
-      certs[1].should.have.property('uid').equal('tic');
-      certs[1].should.have.property('isMember').equal(true);
-      certs[1].should.have.property('wasMember').equal(true);
-      certs[1].should.have.property('sigDate').be.a.Number;
-      certs[1].should.have.property('cert_time').property('block').be.a.Number;
-      certs[1].should.have.property('cert_time').property('medianTime').be.a.Number;
-      certs[1].should.have.property('written').property('number').equal(0);
-      certs[1].should.have.property('written').property('hash').not.equal('');
-      certs[1].should.have.property('signature').not.equal('');
+      for (const cert of certs) {
+        if (cert.pubkey == 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo') {
+          cert.should.have.property('uid').equal('toc');
+          cert.should.have.property('isMember').equal(true);
+          cert.should.have.property('wasMember').equal(true);
+          cert.should.have.property('sigDate').be.a.Number;
+          cert.should.have.property('cert_time').property('block').be.a.Number;
+          cert.should.have.property('cert_time').property('medianTime').be.a.Number;
+          cert.should.have.property('written').property('number').equal(0);
+          cert.should.have.property('written').property('hash').not.equal('');
+          cert.should.have.property('signature').not.equal('');
+        } else {
+          cert.should.have.property('pubkey').equal('DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV');
+          cert.should.have.property('uid').equal('tic');
+          cert.should.have.property('isMember').equal(true);
+          cert.should.have.property('wasMember').equal(true);
+          cert.should.have.property('sigDate').be.a.Number;
+          cert.should.have.property('cert_time').property('block').be.a.Number;
+          cert.should.have.property('cert_time').property('medianTime').be.a.Number;
+          cert.should.have.property('written').property('number').equal(0);
+          cert.should.have.property('written').property('hash').not.equal('');
+          cert.should.have.property('signature').not.equal('');
+        }
+      }
     });
   });
 
@@ -341,34 +345,27 @@ describe("Identities collision", function() {
       res.should.have.property('pubkey').equal('DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV');
       res.should.have.property('uid').equal('tic');
       res.should.have.property('sigDate').be.a.Number;
-      res.should.have.property('memberships').length(2);
-      // Initial membership
+      res.should.have.property('memberships').length(1); // We no more conserve the memberships in sandbox
+      // Renew membership, not written
       res.memberships[0].should.have.property('version').equal(constants.DOCUMENTS_VERSION);
       res.memberships[0].should.have.property('currency').equal('bb');
       res.memberships[0].should.have.property('membership').equal('IN');
       res.memberships[0].should.have.property('blockNumber').equal(1);
       res.memberships[0].should.have.property('blockHash').not.equal('E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855');
       res.memberships[0].should.have.property('written').equal(null);
-      // Renew membership, not written
-      res.memberships[1].should.have.property('version').equal(constants.DOCUMENTS_VERSION);
-      res.memberships[1].should.have.property('currency').equal('bb');
-      res.memberships[1].should.have.property('membership').equal('IN');
-      res.memberships[1].should.have.property('blockNumber').equal(0);
-      res.memberships[1].should.have.property('blockHash').equal('E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855');
-      res.memberships[1].should.have.property('written').equal(0);
     });
   });
 
-  it('memberships of man3', function() {
-    return httpTest.expectHttpCode(404, rp('http://127.0.0.1:7799/blockchain/memberships/man3'));
-  });
-
-  it('difficulties', function() {
-    return expectAnswer(rp('http://127.0.0.1:7799/blockchain/difficulties', { json: true }), function(res) {
-      res.should.have.property('block').equal(2);
-      res.should.have.property('levels').length(1);
-      res.levels[0].should.have.property('uid').equal('cat');
-      res.levels[0].should.have.property('level').equal(4);
-    });
-  });
+  // it('memberships of man3', function() {
+  //   return httpTest.expectHttpCode(404, rp('http://127.0.0.1:7799/blockchain/memberships/man3'));
+  // });
+  //
+  // it('difficulties', function() {
+  //   return expectAnswer(rp('http://127.0.0.1:7799/blockchain/difficulties', { json: true }), function(res) {
+  //     res.should.have.property('block').equal(2);
+  //     res.should.have.property('levels').length(1);
+  //     res.levels[0].should.have.property('uid').equal('cat');
+  //     res.levels[0].should.have.property('level').equal(4);
+  //   });
+  // });
 });
diff --git a/test/integration/migrations.js b/test/integration/migrations.js
index c91f9c6cb03b9cead7d83e2b4b37f48fc93ef3b1..e10f313f8930cc4263ccb88f82657ff46f2c8a8b 100644
--- a/test/integration/migrations.js
+++ b/test/integration/migrations.js
@@ -34,7 +34,7 @@ describe("Migration", function() {
     }));
 
     it('should be able to commit the transaction', () => co(function*() {
-      yield node.commitP();
+      yield node.commitP({ time: 1481800000 + 100 });
       // The recipients are wrongly valued in this version
       yield node.server.dal.txsDAL.exec('UPDATE txs SET recipients = "[]";');
     }));
diff --git a/test/integration/revocation-test.js b/test/integration/revocation-test.js
index c6f4c89f33b6ecbbbf559c11668a497a0add3561..b6f0dc8ea7d7af9541926783fde981eb7165e1ed 100644
--- a/test/integration/revocation-test.js
+++ b/test/integration/revocation-test.js
@@ -2,7 +2,8 @@
 
 const _         = require('underscore');
 const co        = require('co');
-const ucoin     = require('../../index');
+const should    = require('should');
+const duniter   = require('../../index');
 const bma       = require('../../app/lib/streams/bma');
 const user      = require('./tools/user');
 const rp        = require('request-promise');
@@ -12,7 +13,7 @@ const limiter = require('../../app/lib/system/limiter');
 
 limiter.noLimit();
 
-const expectAnswer   = httpTest.expectAnswer;
+const expectAnswer  = httpTest.expectAnswer;
 
 const MEMORY_MODE = true;
 const commonConf = {
@@ -26,7 +27,7 @@ const commonConf = {
   sigQty: 1
 };
 
-const s1 = ucoin({
+const s1 = duniter({
   memory: MEMORY_MODE,
   name: 'bb12'
 }, _.extend({
@@ -37,9 +38,9 @@ const s1 = ucoin({
   }
 }, commonConf));
 
-const s2 = ucoin({
+const s2 = duniter({
   memory: MEMORY_MODE,
-  name: 'bb12'
+  name: 'bb13'
 }, _.extend({
   port: '9965',
   pair: {
@@ -149,7 +150,28 @@ describe("Revocation", function() {
     });
   }));
 
+  it('should have 2 members', function() {
+    return expectAnswer(rp('http://127.0.0.1:9964/wot/members', { json: true }), function(res) {
+      res.should.have.property('results').length(2);
+      _.pluck(res.results, 'uid').sort().should.deepEqual(['tic','toc']);
+    });
+  });
+
+  it('cat should not be able to join back', () => co(function *() {
+    try {
+      yield cat.join();
+    } catch (e) {
+      should.exists(e);
+    }
+    yield commitS1();
+    return expectAnswer(rp('http://127.0.0.1:9964/wot/members', { json: true }), function(res) {
+      res.should.have.property('results').length(2);
+      _.pluck(res.results, 'uid').sort().should.deepEqual(['tic','toc']);
+    });
+  }));
+
   it('if we revert the commit, cat should not be revoked', () => co(function *() {
+    yield s1.revert();
     yield s1.revert();
     return expectAnswer(rp('http://127.0.0.1:9964/wot/lookup/cat', { json: true }), function(res) {
       res.should.have.property('results').length(1);
@@ -157,37 +179,21 @@ describe("Revocation", function() {
       res.results[0].uids[0].should.have.property('uid').equal('cat');
       res.results[0].uids[0].should.have.property('revoked').equal(false);
       res.results[0].uids[0].should.have.property('revoked_on').equal(null);
-      res.results[0].uids[0].should.have.property('revocation_sig').not.equal(null);
-      res.results[0].uids[0].should.have.property('revocation_sig').not.equal('');
+      res.results[0].uids[0].should.have.property('revocation_sig').equal(null); // We loose the revocation
+      res.results[0].uids[0].should.have.property('revocation_sig').equal(null);
     });
   }));
 
-  it('if we commit again, cat should be revoked', () => co(function *() {
+  it('if we commit again, cat should NOT be revoked (we have lost the revocation)', () => co(function *() {
     yield commitS1();
     return expectAnswer(rp('http://127.0.0.1:9964/wot/lookup/cat', { json: true }), function(res) {
       res.should.have.property('results').length(1);
       res.results[0].should.have.property('uids').length(1);
       res.results[0].uids[0].should.have.property('uid').equal('cat');
-      res.results[0].uids[0].should.have.property('revoked').equal(true);
-      res.results[0].uids[0].should.have.property('revoked_on').equal(1);
-      res.results[0].uids[0].should.have.property('revocation_sig').not.equal(null);
-      res.results[0].uids[0].should.have.property('revocation_sig').not.equal('');
-    });
-  }));
-
-  it('should have 2 members', function() {
-    return expectAnswer(rp('http://127.0.0.1:9964/wot/members', { json: true }), function(res) {
-      res.should.have.property('results').length(2);
-      _.pluck(res.results, 'uid').sort().should.deepEqual(['tic','toc']);
-    });
-  });
-
-  it('cat should not be able to join back', () => co(function *() {
-    yield cat.join();
-    yield commitS1();
-    return expectAnswer(rp('http://127.0.0.1:9964/wot/members', { json: true }), function(res) {
-      res.should.have.property('results').length(2);
-      _.pluck(res.results, 'uid').sort().should.deepEqual(['tic','toc']);
+      res.results[0].uids[0].should.have.property('revoked').equal(false);
+      res.results[0].uids[0].should.have.property('revoked_on').equal(null);
+      res.results[0].uids[0].should.have.property('revocation_sig').equal(null); // We loose the revocation
+      res.results[0].uids[0].should.have.property('revocation_sig').equal(null);
     });
   }));
 
diff --git a/test/integration/server-sandbox.js b/test/integration/server-sandbox.js
index 72f5036b7ecc7e91f2b4ea4a4502bfc9a5bb1a3d..4f423316cdffc8306a53d2fd1ea85ceda2e03c6a 100644
--- a/test/integration/server-sandbox.js
+++ b/test/integration/server-sandbox.js
@@ -14,7 +14,7 @@ limiter.noLimit();
 
 const s1 = toolbox.server({
   idtyWindow: 10,
-  certWindow: 10,
+  sigWindow: 10,
   msWindow: 10,
   dt: 10,
   pair: {
@@ -57,7 +57,7 @@ const i14 = user('i14', { pub: 'H9dtBFmJohAwMNXSbfoL6xfRtmrqMw8WZnjXMHr4vEHX', s
 
 describe("Sandboxes", function() {
 
-  const now = parseInt(Date.now() / 1000);
+  const now = 1482300000;
 
   before(() => co(function*() {
 
@@ -101,9 +101,12 @@ describe("Sandboxes", function() {
       yield i1.join();
       yield i4.join();
       (yield s1.dal.idtyDAL.getSandboxRoom()).should.equal(0);
-      yield s1.commit();
-      (yield s1.dal.idtyDAL.getSandboxRoom()).should.equal(1); // i2, i3
-      yield s1.commit();
+      yield s1.commit({ time: now });
+      (yield s1.dal.idtyDAL.getSandboxRoom()).should.equal(3); // i2, i3 were removed for too old identities (based on virtual root block)
+      yield i2.createIdentity();
+      yield i3.createIdentity();
+      (yield s1.dal.idtyDAL.getSandboxRoom()).should.equal(1);
+      yield s1.commit({ time: now });
       yield s2.syncFrom(s1, 0, 1);
       yield s3.syncFrom(s1, 0, 1);
     }));
@@ -121,8 +124,11 @@ describe("Sandboxes", function() {
       yield i7.revoke(idty);
     })));
 
-    it('should accept i7(1), i8(1), i9(1) by i1->i7(1), i1->i8(1), i1->i9(1)', () => co(function *() {
+    it('should reject i1 -> i7 by revocation', () => shouldThrow(co(function *() {
       yield i1.cert(i7, s2);
+    })));
+
+    it('should accept i7(1), i8(1), i9(1) by i1->i7(1), i1->i8(1), i1->i9(1)', () => co(function *() {
       (yield s1.dal.idtyDAL.getSandboxRoom()).should.equal(0);
       yield i8.createIdentity(null, s2);
       yield i1.cert(i8, s2);
@@ -175,6 +181,9 @@ describe("Sandboxes", function() {
   describe('Certifications', () => {
 
     it('should accept i4->i7(0),i4->i8(0),i4->i9(0)', () => co(function *() {
+      yield i7.createIdentity();
+      yield i8.createIdentity();
+      yield i9.createIdentity();
       s1.dal.certDAL.setSandboxSize(3);
       (yield s1.dal.certDAL.getSandboxRoom()).should.equal(3);
       yield i4.cert(i7);
@@ -204,6 +213,8 @@ describe("Sandboxes", function() {
   describe('Memberships', () => {
 
     it('should accept i8,i9', () => co(function *() {
+      yield i8.createIdentity(); // Identities have changed
+      yield i9.createIdentity();
       (yield s1.dal.msDAL.getSandboxRoom()).should.equal(2);
       yield i8.join();
       yield i9.join();
diff --git a/test/integration/tests.js b/test/integration/tests.js
index ab2c03cceb13b8e9e7ce1c48f3cb8eb5baa3b729..9b23de8cbf81e8714655b2b737949d80980e0bce 100644
--- a/test/integration/tests.js
+++ b/test/integration/tests.js
@@ -187,7 +187,7 @@ describe("Integration", function() {
       return co(function *() {
 
         yield node3.initWithDAL().then(bma).then((bmapi) => bmapi.openConnections());
-        let now = Math.round(new Date().getTime() / 1000);
+        const now = 1482220000;
 
         // Self certifications
         yield cat.createIdentity();
@@ -209,9 +209,13 @@ describe("Integration", function() {
         yield commit(node3)({
           time: now
         });
-        yield commit(node3)();
+        yield commit(node3)({
+          time: now
+        });
         yield toc.leave();
-        yield commit(node3)();
+        yield commit(node3)({
+          time: now
+        });
         yield tac.cert(toc);
         yield tic.cert(toc);
         yield toc.cert(tic); // Should be taken in 1 block
@@ -222,7 +226,9 @@ describe("Integration", function() {
         yield commit(node3)({
           time: now + 200
         });
-        yield commit(node3)();
+        yield commit(node3)({
+          time: now + 200
+        });
       });
     });
 
@@ -269,8 +275,8 @@ describe("Integration", function() {
     blockShouldHaveCerts(1, 0);
     blockShouldHaveCerts(2, 0);
     blockShouldHaveCerts(3, 1);
-    blockShouldHaveCerts(4, 0);
-    blockShouldHaveCerts(5, 1);
+    blockShouldHaveCerts(4, 1);
+    blockShouldHaveCerts(5, 0);
 
     function blockShouldHaveCerts(number, certificationsCount) {
       it('it should exist block#' + number + ' with ' + certificationsCount + ' certification', () => expectAnswer(rp('http://127.0.0.1:9997/blockchain/block/' + number, { json: true }), function(block) {
diff --git a/test/integration/tools/node.js b/test/integration/tools/node.js
index c3c18d68093f336927985bf50247d138d4b41ea1..d5f787bd35b7115575ea50cd613bb3a609555255 100644
--- a/test/integration/tools/node.js
+++ b/test/integration/tools/node.js
@@ -38,6 +38,7 @@ module.exports.statics = {
 function getTxNode(testSuite, afterBeforeHook){
 
   let port = ++AUTO_PORT;
+  const now = 1481800000;
 
   var node2 = new Node({ name: "db_" + port, memory: MEMORY_MODE }, { currency: 'cc', ipv4: 'localhost', port: port, remoteipv4: 'localhost', remoteport: port, upnp: false, httplogs: false,
     pair: {
@@ -46,7 +47,7 @@ function getTxNode(testSuite, afterBeforeHook){
     },
     forksize: 3,
     participate: false, rootoffset: 10,
-    sigQty: 1, dt: 0, ud0: 120
+    sigQty: 1, dt: 1, ud0: 120
   });
 
   var tic = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, node2);
@@ -62,8 +63,9 @@ function getTxNode(testSuite, afterBeforeHook){
     yield toc.cert(tic);
     yield tic.join();
     yield toc.join();
-    yield node2.commitP();
-    yield node2.commitP();
+    yield node2.commitP({ time: now });
+    yield node2.commitP({ time: now + 10 });
+    yield node2.commitP({ time: now + 10 });
     yield tic.sendP(51, toc);
 
     if (afterBeforeHook) {
@@ -130,16 +132,16 @@ function Node (dbName, options) {
    * Generates next block and submit it to local node.
    * @returns {Function}
    */
-  this.commit = function() {
+  this.commit = function(params) {
     return function(done) {
       async.waterfall([
         function(next) {
           async.parallel({
             block: function(callback){
               co(function *() {
-                const block2 = yield that.server.BlockchainService.generateNext();
-                const trial2 = yield rules.HELPERS.getTrialLevel(block2.version, that.server.keyPair.publicKey, that.server.conf, that.server.dal);
-                return that.server.BlockchainService.makeNextBlock(block2, trial2);
+                const block2 = yield that.server.BlockchainService.generateNext(params);
+                const trial2 = yield that.server.getBcContext().getIssuerPersonalizedDifficulty(block2.version, that.server.keyPair.publicKey);
+                return that.server.BlockchainService.makeNextBlock(block2, trial2, params);
               })
                 .then((block) => callback(null, block))
                 .catch(callback);
@@ -236,6 +238,9 @@ function Node (dbName, options) {
           that.http.getLookup(search).then((o) => next(null, o)).catch(next);
         }
       ], function(err, res) {
+        if (err) {
+          logger.error(err);
+        }
         callback(res, done);
       });
     };
@@ -302,5 +307,5 @@ function Node (dbName, options) {
 
   this.submitPeerP = (peer) => Q.nfcall(this.submitPeer, peer);
 
-  this.commitP = () => Q.nfcall(this.commit());
+  this.commitP = (params) => Q.nfcall(this.commit(params));
 }
diff --git a/test/integration/tools/toolbox.js b/test/integration/tools/toolbox.js
index b9003d876726bd90b0a9d47910ad28ef05121dc8..a6b606f0fb831d9dee3160ecea58e42ff80693eb 100644
--- a/test/integration/tools/toolbox.js
+++ b/test/integration/tools/toolbox.js
@@ -200,6 +200,7 @@ module.exports = {
     server.expect = (uri, expectations) => typeof expectations == 'function' ? httpTest.expectAnswer(rp(server.url(uri), { json: true }), expectations) : httpTest.expectJSON(rp(server.url(uri), { json: true }), expectations);
     server.expectThat = (uri, expectations) => httpTest.expectAnswer(rp(server.url(uri), { json: true }), expectations);
     server.expectJSON = (uri, expectations) => httpTest.expectJSON(rp(server.url(uri), { json: true }), expectations);
+    server.expectError = (uri, code, message) => httpTest.expectError(code, message, rp(server.url(uri), { json: true }));
 
     server.syncFrom = (otherServer, fromIncuded, toIncluded) => sync(fromIncuded, toIncluded, otherServer, server);
 
diff --git a/test/integration/transactions-pruning.js b/test/integration/transactions-pruning.js
index 3678bf982cfb4fd27688a2ba1a013c8334c62967..f70685dde3c6ad0a3fa6b3342b39a2104530cad9 100644
--- a/test/integration/transactions-pruning.js
+++ b/test/integration/transactions-pruning.js
@@ -7,6 +7,7 @@ const commit    = require('./tools/commit');
 const until     = require('./tools/until');
 const toolbox   = require('./tools/toolbox');
 const Peer = require('../../app/lib/entity/peer');
+const constants = require('../../app/lib/constants');
 
 const s1 = toolbox.server({
   currency: 'currency_one',
@@ -59,9 +60,12 @@ describe("Transactions pruning", function() {
   }));
 
   it('double spending transaction should have been pruned', () => co(function*() {
+    const tmp = constants.TRANSACTION_MAX_TRIES;
+    constants.TRANSACTION_MAX_TRIES = 1;
     yield s1.commit();
     yield s1.expect('/tx/history/HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', (res) => {
       res.history.should.have.property('sending').length(0);
     });
+    constants.TRANSACTION_MAX_TRIES = tmp;
   }));
 });
diff --git a/test/integration/transactions-test.js b/test/integration/transactions-test.js
index 13efa619034145deccf114105901fc3c700fd629..89ef22e2654c5f8e85f46d06aed99194e060f087 100644
--- a/test/integration/transactions-test.js
+++ b/test/integration/transactions-test.js
@@ -138,7 +138,7 @@ describe("Testing transactions", function() {
       let tx8 = yield tic.prepareUTX(tx4, ['XHX(1872767826647264)'], [{ qty: 120, base: 0, lock: 'SIG(' + toc.pub + ')' }], { comment: 'okk'}); // tic unlocks the XHX locked amount, and gives it to toc!
       yield unit.shouldFail(toc.sendTX(tx7), 'Wrong unlocker in transaction');
       yield unit.shouldNotFail(toc.sendTX(tx8));
-      yield s1.commit({ version: 2 }); // TX4 commited
+      yield s1.commit({ version: 2 }); // TX8 commited
       (yield s1.get('/tx/sources/DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo')).should.have.property('sources').length(1); // That's why toc now has 1 more source...
       (yield s1.get('/tx/sources/DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV')).should.have.property('sources').length(3); // ...and why tic's number of sources hasn't changed
     }));
diff --git a/test/integration/v0.3-dividend.js b/test/integration/v0.3-dividend.js
index dcd67ccd8bdb64b636fcdbcd073663697039ff0e..7b3cadee91b448091f9ebb25211f0a037b33d4cf 100644
--- a/test/integration/v0.3-dividend.js
+++ b/test/integration/v0.3-dividend.js
@@ -25,7 +25,7 @@ const tic = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', s
 
 let now;
 
-describe("Protocol 0.3", function() {
+describe("Protocol 0.3 Dividend", function() {
 
   before(() => co(function*() {
 
diff --git a/test/integration/v0.4-dividend.js b/test/integration/v0.4-dividend.js
index 4cf7cf003c67ae6cbc668c10c24fcb34fdd83532..b9616c8e69eae139eb4ecf4d7651c8c8c4d01cbc 100644
--- a/test/integration/v0.4-dividend.js
+++ b/test/integration/v0.4-dividend.js
@@ -25,7 +25,7 @@ const tic = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', s
 
 let now;
 
-describe("Protocol 0.3", function() {
+describe("Protocol 0.4 Dividend", function() {
 
   before(() => co(function*() {
 
diff --git a/test/integration/v0.5-difficulties.js b/test/integration/v0.5-difficulties.js
index 6e445d043792998c03bc9b1ee11db5d46a5dfb71..f1d059f6aa5c3c625feecb8059529057017e0f65 100644
--- a/test/integration/v0.5-difficulties.js
+++ b/test/integration/v0.5-difficulties.js
@@ -30,19 +30,19 @@ describe("Protocol 0.5 Difficulties", function() {
     s1 = res.s1;
     s2 = res.s2;
     yield [
-      s1.commit({ time: now }),
+      s1.commit({ time: now, version: 5 }),
       s2.until('block', 1)
     ];
   }));
 
   it('should be able to emit a block#1 by a different user', () => co(function*() {
     yield [
-      s1.commit({ time: now }), // medianOfBlocksInFrame = MEDIAN([1]) = 1
+      s1.commit({ time: now, version: 5 }), // medianOfBlocksInFrame = MEDIAN([1]) = 1
       s2.until('block', 1),
       s1.until('block', 1)
     ];
     yield [
-      s2.commit({ time: now }), // medianOfBlocksInFrame = MEDIAN([1]) = 1
+      s2.commit({ time: now, version: 5 }), // medianOfBlocksInFrame = MEDIAN([1]) = 1
       s2.until('block', 1),
       s1.until('block', 1)
     ];
diff --git a/test/integration/wotb.js b/test/integration/wotb.js
index dad0c187c415d92be762b3b0881f4178f880f49b..4aa0e4f599d126907996d4f7c4593f0ae4e246b6 100644
--- a/test/integration/wotb.js
+++ b/test/integration/wotb.js
@@ -28,7 +28,7 @@ const s1 = ucoin({
     sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'
   },
   participate: false, rootoffset: 10,
-  sigQty: 1, dt: 0, ud0: 120
+  sigQty: 1, dt: 1, ud0: 120
 }, commonConf));
 
 const s2 = ucoin({
@@ -41,7 +41,7 @@ const s2 = ucoin({
     sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'
   },
   participate: false, rootoffset: 10,
-  sigQty: 1, dt: 0, ud0: 120,
+  sigQty: 1, dt: 1, ud0: 120,
   msValidity: 400 // Memberships expire after 400 second delay
 }, commonConf));
 
@@ -55,7 +55,7 @@ const s3 = ucoin({
     sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'
   },
   participate: false, rootoffset: 10,
-  sigQty: 1, dt: 0, ud0: 120,
+  sigQty: 1, dt: 1, ud0: 120,
   sigValidity: 1400, sigPeriod: 0
 }, commonConf));
 
@@ -71,7 +71,7 @@ const cat3 = user('cat3', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd',
 const toc3 = user('toc3', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s3 });
 const tic3 = user('tic3', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s3 });
 
-const now = Math.round(new Date().getTime() / 1000);
+const now = 1482000000;
 const _100_PERCENT = 1.0;
 const MAX_DISTANCE_1 = 1;
 const MAX_DISTANCE_2 = 2;
@@ -179,16 +179,19 @@ describe("WOTB module", function() {
         });
         // Should make MS expire for toc2
         yield commit(s2)({
-          time: now + 800
+          time: now + 500
         });
         yield commit(s2)({
-          time: now + 800
+          time: now + 600
         });
         yield cat2.join(); // Renew for not to be kicked!
         yield tic2.join(); // Renew for not to be kicked!
         yield commit(s2)({
           time: now + 800
         });
+        yield commit(s2)({
+          time: now + 800
+        });
         // Members excluded
         yield commit(s2)({
           time: now + 800
@@ -254,7 +257,7 @@ describe("WOTB module", function() {
 
     it('two first commits: the WoT is new and OK', function() {
       return co(function *() {
-        yield commit(s3)();
+        yield commit(s3)({ time: now });
         yield commit(s3)({
           time: now + 1200
         });
@@ -346,7 +349,7 @@ describe("WOTB module", function() {
         yield tic3.cert(cat3);
         yield cat3.join();
         yield commit(s3)({
-          time: now + 2700
+          time: now + 5000
         });
         /**
          *  cat <-- tic <-- [toc]