diff --git a/.eslintignore b/.eslintignore
index 43f35ac5216e0577bbde544a4dd37c5cc04667b9..c36eb8da21274c661dec572509b0cffbf3e4e7d0 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -12,5 +12,6 @@ app/lib/dal/fileDALs/*.js
 app/lib/dal/fileDAL.js
 app/service/*.js
 app/lib/rules/local_rules.js
+app/lib/rules/global_rules.js
 test/*.js
 test/**/*.js
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 21bbc63cf014d14df36e8b57d223f2e9411bfb05..8f6944460e40e9546eb2fadefecb3ba95f01ea3c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -48,6 +48,7 @@ app/lib/dal/sqliteDAL/index/*.js*
 app/lib/dal/fileDALs/*.js*
 app/lib/dal/fileDAL.js*
 app/lib/rules/local_rules*.js*
+app/lib/rules/global_rules*.js*
 app/lib/logger*js*
 app/service/*.js*
 app/lib/wot.js*
\ No newline at end of file
diff --git a/app/lib/rules/global_rules.js b/app/lib/rules/global_rules.js
index a5db1ab3db2951a506aabda0d985405dcf226bb2..579551a146c7912ff3a78d5f037fb733c686eba1 100644
--- a/app/lib/rules/global_rules.js
+++ b/app/lib/rules/global_rules.js
@@ -1,289 +1,286 @@
 "use strict";
-
-const co             = require('co');
-const _              = require('underscore');
-const common         = require('duniter-common');
-const indexer        = require('../indexer').Indexer
-const local_rules    = require('../rules/local_rules');
-
-const constants      = common.constants
-const keyring        = common.keyring
-const rawer          = common.rawer
-const Identity       = common.document.Identity
-const Transaction    = common.document.Transaction
-const unlock         = common.txunlock
-
-let rules = {};
-
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+    return new (P || (P = Promise))(function (resolve, reject) {
+        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+        step((generator = generator.apply(thisArg, _arguments || [])).next());
+    });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const co = require('co');
+const _ = require('underscore');
+const common = require('duniter-common');
+const indexer = require('../indexer').Indexer;
+const local_rules = require('../rules/local_rules');
+const constants = common.constants;
+const keyring = common.keyring;
+const rawer = common.rawer;
+const Identity = common.document.Identity;
+const Transaction = common.document.Transaction;
+const unlock = common.txunlock;
+exports.rules = {};
 // Empty logger by default
 let logger = {
-  debug: () => {},
-  warn: () => {}
-}
-
+    debug: (...args) => { },
+    warn: (...args) => { }
+};
 // TODO: all the global rules should be replaced by index rule someday
-
-rules.FUNCTIONS = {
-
-  checkIdentitiesAreWritable: (block, conf, dal) => co(function *() {
-    let current = yield dal.getCurrentBlockOrNull();
-    for (const obj of block.identities) {
-      let idty = Identity.fromInline(obj);
-      let found = yield dal.getWrittenIdtyByUID(idty.uid);
-      if (found) {
-        throw Error('Identity already used');
-      }
-      // Because the window rule does not apply on initial certifications
-      if (current && idty.buid != constants.SPECIAL_BLOCK) {
-        // From DUP 0.5: we fully check the blockstamp
-        const basedBlock = yield dal.getBlockByBlockstamp(idty.buid);
-        // Check if writable
-        let duration = current.medianTime - parseInt(basedBlock.medianTime);
-        if (duration > conf.idtyWindow) {
-          throw Error('Identity is too old and cannot be written');
-        }
-      }
-    }
-    return true;
-  }),
-
-  checkSourcesAvailability: (block, conf, dal, alsoCheckPendingTransactions) => co(function *() {
-    const txs = block.transactions
-    const current = yield dal.getCurrentBlockOrNull();
-    for (const tx of txs) {
-      const inputs = tx.inputsAsObjects()
-      const outputs = tx.outputsAsObjects()
-      let unlocks = {};
-      let sumOfInputs = 0;
-      let maxOutputBase = current.unitbase;
-      for (const theUnlock of tx.unlocks) {
-        let sp = theUnlock.split(':');
-        let index = parseInt(sp[0]);
-        unlocks[index] = sp[1];
-      }
-      for (let k = 0, len2 = inputs.length; k < len2; k++) {
-        let src = inputs[k];
-        let dbSrc = yield dal.getSource(src.identifier, src.pos);
-        logger.debug('Source %s:%s:%s:%s = %s', src.amount, src.base, 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
-          dbSrc = yield co(function*() {
-            let hypotheticSrc = null;
-            let targetTX = yield dal.getTxByHash(src.identifier);
-            if (targetTX) {
-              let outputStr = targetTX.outputs[src.pos];
-              if (outputStr) {
-                hypotheticSrc = Transaction.outputStr2Obj(outputStr);
-                hypotheticSrc.consumed = false;
-                hypotheticSrc.time = 0;
-              }
+exports.rules.FUNCTIONS = {
+    checkIdentitiesAreWritable: (block, conf, dal) => __awaiter(this, void 0, void 0, function* () {
+        let current = yield dal.getCurrentBlockOrNull();
+        for (const obj of block.identities) {
+            let idty = Identity.fromInline(obj);
+            let found = yield dal.getWrittenIdtyByUID(idty.uid);
+            if (found) {
+                throw Error('Identity already used');
+            }
+            // Because the window rule does not apply on initial certifications
+            if (current && idty.buid != constants.SPECIAL_BLOCK) {
+                // From DUP 0.5: we fully check the blockstamp
+                const basedBlock = yield dal.getBlockByBlockstamp(idty.buid);
+                // Check if writable
+                let duration = current.medianTime - parseInt(basedBlock.medianTime);
+                if (duration > conf.idtyWindow) {
+                    throw Error('Identity is too old and cannot be written');
+                }
             }
-            return hypotheticSrc;
-          });
-        }
-        if (!dbSrc || dbSrc.consumed) {
-          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.written_time < tx.locktime) {
-          throw constants.ERRORS.LOCKTIME_PREVENT;
         }
-        let sigResults = local_rules.HELPERS.getSigResult(tx);
-        let unlocksForCondition = [];
-        let unlocksMetadata = {};
-        let unlockValues = unlocks[k];
-        if (dbSrc.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) {
-                  logger.warn('Source ' + [src.amount, src.base, src.type, src.identifier, src.pos].join(':') + ' unlock fail (unreferenced signatory)');
-                  throw constants.ERRORS.WRONG_UNLOCKER;
+        return true;
+    }),
+    checkSourcesAvailability: (block, conf, dal, alsoCheckPendingTransactions) => __awaiter(this, void 0, void 0, function* () {
+        const txs = block.transactions;
+        const current = yield dal.getCurrentBlockOrNull();
+        for (const tx of txs) {
+            const inputs = tx.inputsAsObjects();
+            const outputs = tx.outputsAsObjects();
+            let unlocks = {};
+            let sumOfInputs = 0;
+            let maxOutputBase = current.unitbase;
+            for (const theUnlock of tx.unlocks) {
+                let sp = theUnlock.split(':');
+                let index = parseInt(sp[0]);
+                unlocks[index] = sp[1];
+            }
+            for (let k = 0, len2 = inputs.length; k < len2; k++) {
+                let src = inputs[k];
+                let dbSrc = yield dal.getSource(src.identifier, src.pos);
+                logger.debug('Source %s:%s:%s:%s = %s', src.amount, src.base, 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
+                    dbSrc = yield (() => __awaiter(this, void 0, void 0, function* () {
+                        let hypotheticSrc = null;
+                        let targetTX = yield dal.getTxByHash(src.identifier);
+                        if (targetTX) {
+                            let outputStr = targetTX.outputs[src.pos];
+                            if (outputStr) {
+                                hypotheticSrc = Transaction.outputStr2Obj(outputStr);
+                                hypotheticSrc.consumed = false;
+                                hypotheticSrc.time = 0;
+                            }
+                        }
+                        return hypotheticSrc;
+                    }))();
+                }
+                if (!dbSrc || dbSrc.consumed) {
+                    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.written_time < tx.locktime) {
+                    throw constants.ERRORS.LOCKTIME_PREVENT;
+                }
+                let sigResults = local_rules.HELPERS.getSigResult(tx);
+                let unlocksForCondition = [];
+                let unlocksMetadata = {};
+                let unlockValues = unlocks[k];
+                if (dbSrc.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) {
+                                    logger.warn('Source ' + [src.amount, src.base, src.type, src.identifier, src.pos].join(':') + ' unlock fail (unreferenced signatory)');
+                                    throw constants.ERRORS.WRONG_UNLOCKER;
+                                }
+                                unlocksForCondition.push({
+                                    pubkey: pubkey,
+                                    sigOK: sigResults.sigs[pubkey] && sigResults.sigs[pubkey].matching || false
+                                });
+                            }
+                            else if (func.match(/^XHX/)) {
+                                unlocksForCondition.push(param);
+                            }
+                        }
+                    }
+                    if (dbSrc.conditions.match(/CLTV/)) {
+                        unlocksMetadata.currentTime = block.medianTime;
+                    }
+                    if (dbSrc.conditions.match(/CSV/)) {
+                        unlocksMetadata.elapsedTime = block.medianTime - dbSrc.written_time;
+                    }
+                    try {
+                        if (!unlock(dbSrc.conditions, unlocksForCondition, unlocksMetadata)) {
+                            throw Error('Locked');
+                        }
+                    }
+                    catch (e) {
+                        logger.warn('Source ' + [src.amount, src.base, src.type, src.identifier, src.pos].join(':') + ' unlock fail');
+                        throw constants.ERRORS.WRONG_UNLOCKER;
+                    }
                 }
-                unlocksForCondition.push({
-                  pubkey: pubkey,
-                  sigOK: sigResults.sigs[pubkey] && sigResults.sigs[pubkey].matching || false
-                });
-              } else if (func.match(/^XHX/)) {
-                unlocksForCondition.push(param);
-              }
             }
-          }
-
-          if (dbSrc.conditions.match(/CLTV/)) {
-            unlocksMetadata.currentTime = block.medianTime;
-          }
-
-          if (dbSrc.conditions.match(/CSV/)) {
-            unlocksMetadata.elapsedTime = block.medianTime - dbSrc.written_time;
-          }
-
-          try {
-            if (!unlock(dbSrc.conditions, unlocksForCondition, unlocksMetadata)) {
-              throw Error('Locked');
+            let sumOfOutputs = outputs.reduce(function (p, output) {
+                if (output.base > maxOutputBase) {
+                    throw constants.ERRORS.WRONG_OUTPUT_BASE;
+                }
+                return p + output.amount * Math.pow(10, output.base);
+            }, 0);
+            if (sumOfInputs !== sumOfOutputs) {
+                logger.warn('Inputs/Outputs != 1 (%s/%s)', sumOfInputs, sumOfOutputs);
+                throw constants.ERRORS.WRONG_AMOUNTS;
             }
-          } catch (e) {
-            logger.warn('Source ' + [src.amount, src.base, src.type, src.identifier, src.pos].join(':') + ' unlock fail');
-            throw constants.ERRORS.WRONG_UNLOCKER;
-          }
         }
-      }
-      let sumOfOutputs = outputs.reduce(function(p, output) {
-        if (output.base > maxOutputBase) {
-          throw constants.ERRORS.WRONG_OUTPUT_BASE;
-        }
-        return p + output.amount * Math.pow(10, output.base);
-      }, 0);
-      if (sumOfInputs !== sumOfOutputs) {
-        logger.warn('Inputs/Outputs != 1 (%s/%s)', sumOfInputs, sumOfOutputs);
-        throw constants.ERRORS.WRONG_AMOUNTS;
-      }
-    }
-    return true;
-  })
+        return true;
+    })
 };
-
-rules.HELPERS = {
-
-  // Functions used in an external context too
-  checkMembershipBlock: (ms, current, conf, dal) => checkMSTarget(ms, current ? { number: current.number + 1} : { number: 0 }, conf, dal),
-
-  checkCertificationIsValid: (cert, current, findIdtyFunc, conf, dal) => checkCertificationIsValid(current ? current : { number: 0 }, cert, findIdtyFunc, conf, dal),
-
-  checkCertificationIsValidForBlock: (cert, block, idty, conf, dal) => checkCertificationIsValid(block, cert, () => idty, conf, dal),
-
-  isOver3Hops: (member, newLinks, newcomers, current, conf, dal) => co(function *() {
-    if (!current) {
-      return Promise.resolve(false);
-    }
-    try {
-      return indexer.DUP_HELPERS.checkPeopleAreNotOudistanced([member], newLinks, newcomers, conf, dal);
-    } catch (e) {
-      return true;
-    }
-  }),
-
-  checkExistsUserID: (uid, dal) => dal.getWrittenIdtyByUID(uid),
-
-  checkExistsPubkey: (pub, dal) => dal.getWrittenIdtyByPubkey(pub),
-
-  checkSingleTransaction: (tx, block, conf, dal, alsoCheckPendingTransactions) => rules.FUNCTIONS.checkSourcesAvailability({
-    transactions: [tx],
-    medianTime: block.medianTime
-  }, conf, dal, alsoCheckPendingTransactions),
-
-  checkTxBlockStamp: (tx, dal) => co(function *() {
-    const number = tx.blockstamp.split('-')[0];
-    const hash = tx.blockstamp.split('-')[1];
-    const basedBlock = yield dal.getBlockByNumberAndHashOrNull(number, hash);
-    if (!basedBlock) {
-      throw "Wrong blockstamp for transaction";
-    }
-    // Valuates the blockstampTime field
-    tx.blockstampTime = basedBlock.medianTime;
-    const current = yield dal.getCurrentBlockOrNull();
-    if (current && current.medianTime > basedBlock.medianTime + constants.TX_WINDOW) {
-      throw "Transaction has expired";
-    }
-  })
+exports.rules.HELPERS = {
+    // Functions used in an external context too
+    checkMembershipBlock: (ms, current, conf, dal) => checkMSTarget(ms, current ? { number: current.number + 1 } : { number: 0 }, conf, dal),
+    checkCertificationIsValid: (cert, current, findIdtyFunc, conf, dal) => checkCertificationIsValid(current ? current : { number: 0 }, cert, findIdtyFunc, conf, dal),
+    checkCertificationIsValidForBlock: (cert, block, idty, conf, dal) => checkCertificationIsValid(block, cert, () => idty, conf, dal),
+    isOver3Hops: (member, newLinks, newcomers, current, conf, dal) => __awaiter(this, void 0, void 0, function* () {
+        if (!current) {
+            return Promise.resolve(false);
+        }
+        try {
+            return indexer.DUP_HELPERS.checkPeopleAreNotOudistanced([member], newLinks, newcomers, conf, dal);
+        }
+        catch (e) {
+            return true;
+        }
+    }),
+    checkExistsUserID: (uid, dal) => dal.getWrittenIdtyByUID(uid),
+    checkExistsPubkey: (pub, dal) => dal.getWrittenIdtyByPubkey(pub),
+    checkSingleTransaction: (tx, block, conf, dal, alsoCheckPendingTransactions) => exports.rules.FUNCTIONS.checkSourcesAvailability({
+        transactions: [tx],
+        medianTime: block.medianTime
+    }, conf, dal, alsoCheckPendingTransactions),
+    checkTxBlockStamp: (tx, dal) => __awaiter(this, void 0, void 0, function* () {
+        const number = parseInt(tx.blockstamp.split('-')[0]);
+        const hash = tx.blockstamp.split('-')[1];
+        const basedBlock = yield dal.getBlockByNumberAndHashOrNull(number, hash);
+        if (!basedBlock) {
+            throw "Wrong blockstamp for transaction";
+        }
+        // Valuates the blockstampTime field
+        tx.blockstampTime = basedBlock.medianTime;
+        const current = yield dal.getCurrentBlockOrNull();
+        if (current && current.medianTime > basedBlock.medianTime + constants.TX_WINDOW) {
+            throw "Transaction has expired";
+        }
+    })
 };
-
 /*****************************
  *
  *      UTILITY FUNCTIONS
  *
  *****************************/
-
-function checkMSTarget (ms, block, conf, dal) {
-  return co(function *() {
-    if (block.number == 0 && ms.number != 0) {
-      throw Error('Number must be 0 for root block\'s memberships');
-    }
-    else if (block.number == 0 && ms.fpr != 'E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855') {
-      throw Error('Hash must be E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 for root block\'s memberships');
-    }
-    else if (block.number == 0) {
-      return null; // Valid for root block
-    } else {
-      let basedBlock;
-      try {
-        basedBlock = yield dal.getBlockByNumberAndHash(ms.number, ms.fpr);
-      } catch (e) {
-        throw Error('Membership based on an unexisting block');
-      }
-      let current = yield dal.getCurrentBlockOrNull();
-      if (current && current.medianTime > basedBlock.medianTime + conf.msValidity) {
-        throw Error('Membership has expired');
-      }
-      return basedBlock;
-    }
-  });
-}
-
-function checkCertificationIsValid (block, cert, findIdtyFunc, conf, dal) {
-  return co(function *() {
-    if (block.number == 0 && cert.block_number != 0) {
-      throw Error('Number must be 0 for root block\'s certifications');
-    } else {
-      let basedBlock = {
-        hash: constants.SPECIAL_HASH
-      };
-      if (block.number != 0) {
-        try {
-          basedBlock = yield dal.getBlock(cert.block_number);
-        } catch (e) {
-          throw Error('Certification based on an unexisting block');
+function checkMSTarget(ms, block, conf, dal) {
+    return __awaiter(this, void 0, void 0, function* () {
+        if (block.number == 0 && ms.number != 0) {
+            throw Error('Number must be 0 for root block\'s memberships');
         }
-        try {
-          const issuer = yield dal.getWrittenIdtyByPubkey(cert.from)
-          if (!issuer || !issuer.member) {
-            throw Error('Issuer is not a member')
-          }
-        } catch (e) {
-          throw Error('Certifier must be a member')
+        else if (block.number == 0 && ms.fpr != 'E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855') {
+            throw Error('Hash must be E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 for root block\'s memberships');
         }
-      }
-      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.block_number, basedBlock.hash].join('-');
-        if (cert.block_hash && buid != [cert.block_number, cert.block_hash].join('-'))
-          throw Error('Certification based on an unexisting block buid. from ' + cert.from.substring(0,8) + ' to ' + idty.pubkey.substring(0,8));
-        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 constants.ERRORS.WRONG_SIGNATURE_FOR_CERT
+        else if (block.number == 0) {
+            return null; // Valid for root block
         }
-        return true;
-      }
-    }
-  });
+        else {
+            let basedBlock;
+            try {
+                basedBlock = yield dal.getBlockByNumberAndHash(ms.number, ms.fpr);
+            }
+            catch (e) {
+                throw Error('Membership based on an unexisting block');
+            }
+            let current = yield dal.getCurrentBlockOrNull();
+            if (current && current.medianTime > basedBlock.medianTime + conf.msValidity) {
+                throw Error('Membership has expired');
+            }
+            return basedBlock;
+        }
+    });
 }
-
-module.exports = {
-  setLogger: (newLogger) => logger = newLogger,
-  rules
-};
+function checkCertificationIsValid(block, cert, findIdtyFunc, conf, dal) {
+    return __awaiter(this, void 0, void 0, function* () {
+        if (block.number == 0 && cert.block_number != 0) {
+            throw Error('Number must be 0 for root block\'s certifications');
+        }
+        else {
+            let basedBlock = {
+                hash: constants.SPECIAL_HASH
+            };
+            if (block.number != 0) {
+                try {
+                    basedBlock = yield dal.getBlock(cert.block_number);
+                }
+                catch (e) {
+                    throw Error('Certification based on an unexisting block');
+                }
+                try {
+                    const issuer = yield dal.getWrittenIdtyByPubkey(cert.from);
+                    if (!issuer || !issuer.member) {
+                        throw Error('Issuer is not a member');
+                    }
+                }
+                catch (e) {
+                    throw Error('Certifier must be a member');
+                }
+            }
+            // TODO: weird call, we cannot just do "await findIdtyFunc(...)". There is a bug somewhere.
+            let idty = yield co(function* () {
+                return 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.block_number, basedBlock.hash].join('-');
+                if (cert.block_hash && buid != [cert.block_number, cert.block_hash].join('-'))
+                    throw Error('Certification based on an unexisting block buid. from ' + cert.from.substring(0, 8) + ' to ' + idty.pubkey.substring(0, 8));
+                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 constants.ERRORS.WRONG_SIGNATURE_FOR_CERT;
+                }
+                return true;
+            }
+        }
+    });
+}
+function setLogger(newLogger) {
+    logger = newLogger;
+}
+exports.setLogger = setLogger;
+//# sourceMappingURL=global_rules.js.map
\ No newline at end of file
diff --git a/app/lib/rules/global_rules.ts b/app/lib/rules/global_rules.ts
new file mode 100644
index 0000000000000000000000000000000000000000..633cb113f150657b912ad878efee655f8d456fcd
--- /dev/null
+++ b/app/lib/rules/global_rules.ts
@@ -0,0 +1,293 @@
+"use strict";
+import {BlockDTO} from "../dto/BlockDTO"
+import {ConfDTO} from "../dto/ConfDTO"
+import {FileDAL} from "../dal/fileDAL"
+import {DBBlock} from "../db/DBBlock"
+import {DBIdentity} from "../dal/sqliteDAL/IdentityDAL"
+import {TransactionDTO} from "../dto/TransactionDTO"
+
+const co             = require('co');
+const _              = require('underscore');
+const common         = require('duniter-common');
+const indexer        = require('../indexer').Indexer
+const local_rules    = require('../rules/local_rules');
+
+const constants      = common.constants
+const keyring        = common.keyring
+const rawer          = common.rawer
+const Identity       = common.document.Identity
+const Transaction    = common.document.Transaction
+const unlock         = common.txunlock
+
+export const rules:any = {}
+
+// Empty logger by default
+let logger = {
+  debug: (...args:any[]) => {},
+  warn: (...args:any[]) => {}
+}
+
+// TODO: all the global rules should be replaced by index rule someday
+
+rules.FUNCTIONS = {
+
+  checkIdentitiesAreWritable: async (block:BlockDTO, conf:ConfDTO, dal:FileDAL) => {
+    let current = await dal.getCurrentBlockOrNull();
+    for (const obj of block.identities) {
+      let idty = Identity.fromInline(obj);
+      let found = await dal.getWrittenIdtyByUID(idty.uid);
+      if (found) {
+        throw Error('Identity already used');
+      }
+      // Because the window rule does not apply on initial certifications
+      if (current && idty.buid != constants.SPECIAL_BLOCK) {
+        // From DUP 0.5: we fully check the blockstamp
+        const basedBlock = await dal.getBlockByBlockstamp(idty.buid);
+        // Check if writable
+        let duration = current.medianTime - parseInt(basedBlock.medianTime);
+        if (duration > conf.idtyWindow) {
+          throw Error('Identity is too old and cannot be written');
+        }
+      }
+    }
+    return true;
+  },
+
+  checkSourcesAvailability: async (block:BlockDTO, conf:ConfDTO, dal:FileDAL, alsoCheckPendingTransactions:boolean) => {
+    const txs = block.transactions
+    const current = await dal.getCurrentBlockOrNull();
+    for (const tx of txs) {
+      const inputs = tx.inputsAsObjects()
+      const outputs = tx.outputsAsObjects()
+      let unlocks:any = {};
+      let sumOfInputs = 0;
+      let maxOutputBase = current.unitbase;
+      for (const theUnlock of tx.unlocks) {
+        let sp = theUnlock.split(':');
+        let index = parseInt(sp[0]);
+        unlocks[index] = sp[1];
+      }
+      for (let k = 0, len2 = inputs.length; k < len2; k++) {
+        let src = inputs[k];
+        let dbSrc = await dal.getSource(src.identifier, src.pos);
+        logger.debug('Source %s:%s:%s:%s = %s', src.amount, src.base, 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
+          dbSrc = await (async () => {
+            let hypotheticSrc = null;
+            let targetTX = await dal.getTxByHash(src.identifier);
+            if (targetTX) {
+              let outputStr = targetTX.outputs[src.pos];
+              if (outputStr) {
+                hypotheticSrc = Transaction.outputStr2Obj(outputStr);
+                hypotheticSrc.consumed = false;
+                hypotheticSrc.time = 0;
+              }
+            }
+            return hypotheticSrc;
+          })()
+        }
+        if (!dbSrc || dbSrc.consumed) {
+          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.written_time < tx.locktime) {
+          throw constants.ERRORS.LOCKTIME_PREVENT;
+        }
+        let sigResults = local_rules.HELPERS.getSigResult(tx);
+        let unlocksForCondition = [];
+        let unlocksMetadata:any = {};
+        let unlockValues = unlocks[k];
+        if (dbSrc.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) {
+                  logger.warn('Source ' + [src.amount, src.base, src.type, src.identifier, src.pos].join(':') + ' unlock fail (unreferenced signatory)');
+                  throw constants.ERRORS.WRONG_UNLOCKER;
+                }
+                unlocksForCondition.push({
+                  pubkey: pubkey,
+                  sigOK: sigResults.sigs[pubkey] && sigResults.sigs[pubkey].matching || false
+                });
+              } else if (func.match(/^XHX/)) {
+                unlocksForCondition.push(param);
+              }
+            }
+          }
+
+          if (dbSrc.conditions.match(/CLTV/)) {
+            unlocksMetadata.currentTime = block.medianTime;
+          }
+
+          if (dbSrc.conditions.match(/CSV/)) {
+            unlocksMetadata.elapsedTime = block.medianTime - dbSrc.written_time;
+          }
+
+          try {
+            if (!unlock(dbSrc.conditions, unlocksForCondition, unlocksMetadata)) {
+              throw Error('Locked');
+            }
+          } catch (e) {
+            logger.warn('Source ' + [src.amount, src.base, src.type, src.identifier, src.pos].join(':') + ' unlock fail');
+            throw constants.ERRORS.WRONG_UNLOCKER;
+          }
+        }
+      }
+      let sumOfOutputs = outputs.reduce(function(p, output) {
+        if (output.base > maxOutputBase) {
+          throw constants.ERRORS.WRONG_OUTPUT_BASE;
+        }
+        return p + output.amount * Math.pow(10, output.base);
+      }, 0);
+      if (sumOfInputs !== sumOfOutputs) {
+        logger.warn('Inputs/Outputs != 1 (%s/%s)', sumOfInputs, sumOfOutputs);
+        throw constants.ERRORS.WRONG_AMOUNTS;
+      }
+    }
+    return true;
+  }
+}
+
+rules.HELPERS = {
+
+  // Functions used in an external context too
+  checkMembershipBlock: (ms:any, current:DBBlock, conf:ConfDTO, dal:FileDAL) => checkMSTarget(ms, current ? { number: current.number + 1} : { number: 0 }, conf, dal),
+
+  checkCertificationIsValid: (cert:any, current:DBBlock, findIdtyFunc:any, conf:ConfDTO, dal:FileDAL) => checkCertificationIsValid(current ? current : { number: 0 }, cert, findIdtyFunc, conf, dal),
+
+  checkCertificationIsValidForBlock: (cert:any, block:BlockDTO, idty:DBIdentity, conf:ConfDTO, dal:FileDAL) => checkCertificationIsValid(block, cert, () => idty, conf, dal),
+
+  isOver3Hops: async (member:any, newLinks:any, newcomers:string[], current:DBBlock, conf:ConfDTO, dal:FileDAL) => {
+    if (!current) {
+      return Promise.resolve(false);
+    }
+    try {
+      return indexer.DUP_HELPERS.checkPeopleAreNotOudistanced([member], newLinks, newcomers, conf, dal);
+    } catch (e) {
+      return true;
+    }
+  },
+
+  checkExistsUserID: (uid:string, dal:FileDAL) => dal.getWrittenIdtyByUID(uid),
+
+  checkExistsPubkey: (pub:string, dal:FileDAL) => dal.getWrittenIdtyByPubkey(pub),
+
+  checkSingleTransaction: (tx:TransactionDTO, block:BlockDTO, conf:ConfDTO, dal:FileDAL, alsoCheckPendingTransactions:boolean) => rules.FUNCTIONS.checkSourcesAvailability({
+    transactions: [tx],
+    medianTime: block.medianTime
+  }, conf, dal, alsoCheckPendingTransactions),
+
+  checkTxBlockStamp: async (tx:TransactionDTO, dal:FileDAL) => {
+    const number = parseInt(tx.blockstamp.split('-')[0])
+    const hash = tx.blockstamp.split('-')[1];
+    const basedBlock = await dal.getBlockByNumberAndHashOrNull(number, hash);
+    if (!basedBlock) {
+      throw "Wrong blockstamp for transaction";
+    }
+    // Valuates the blockstampTime field
+    tx.blockstampTime = basedBlock.medianTime;
+    const current = await dal.getCurrentBlockOrNull();
+    if (current && current.medianTime > basedBlock.medianTime + constants.TX_WINDOW) {
+      throw "Transaction has expired";
+    }
+  }
+}
+
+/*****************************
+ *
+ *      UTILITY FUNCTIONS
+ *
+ *****************************/
+
+async function checkMSTarget (ms:any, block:any, conf:ConfDTO, dal:FileDAL) {
+  if (block.number == 0 && ms.number != 0) {
+    throw Error('Number must be 0 for root block\'s memberships');
+  }
+  else if (block.number == 0 && ms.fpr != 'E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855') {
+    throw Error('Hash must be E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 for root block\'s memberships');
+  }
+  else if (block.number == 0) {
+    return null; // Valid for root block
+  } else {
+    let basedBlock;
+    try {
+      basedBlock = await dal.getBlockByNumberAndHash(ms.number, ms.fpr);
+    } catch (e) {
+      throw Error('Membership based on an unexisting block');
+    }
+    let current = await dal.getCurrentBlockOrNull();
+    if (current && current.medianTime > basedBlock.medianTime + conf.msValidity) {
+      throw Error('Membership has expired');
+    }
+    return basedBlock;
+  }
+}
+
+async function checkCertificationIsValid (block:any, cert:any, findIdtyFunc:any, conf:ConfDTO, dal:FileDAL) {
+  if (block.number == 0 && cert.block_number != 0) {
+    throw Error('Number must be 0 for root block\'s certifications');
+  } else {
+    let basedBlock:any = {
+      hash: constants.SPECIAL_HASH
+    };
+    if (block.number != 0) {
+      try {
+        basedBlock = await dal.getBlock(cert.block_number);
+      } catch (e) {
+        throw Error('Certification based on an unexisting block');
+      }
+      try {
+        const issuer = await dal.getWrittenIdtyByPubkey(cert.from)
+        if (!issuer || !issuer.member) {
+          throw Error('Issuer is not a member')
+        }
+      } catch (e) {
+        throw Error('Certifier must be a member')
+      }
+    }
+    // TODO: weird call, we cannot just do "await findIdtyFunc(...)". There is a bug somewhere.
+    let idty = await co(function*() {
+      return yield findIdtyFunc(block, cert.to, dal)
+    })
+    let current = block.number == 0 ? null : await 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.block_number, basedBlock.hash].join('-');
+      if (cert.block_hash && buid != [cert.block_number, cert.block_hash].join('-'))
+        throw Error('Certification based on an unexisting block buid. from ' + cert.from.substring(0,8) + ' to ' + idty.pubkey.substring(0,8));
+      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 constants.ERRORS.WRONG_SIGNATURE_FOR_CERT
+      }
+      return true;
+    }
+  }
+}
+
+export function setLogger(newLogger:any) {
+  logger = newLogger
+}