From 483efdced6f971dff5388bd17d7f5f10bb67c643 Mon Sep 17 00:00:00 2001
From: cgeek <cem.moreau@gmail.com>
Date: Mon, 24 Jul 2017 13:51:31 +0200
Subject: [PATCH] [fix] #1037 Migrate duniter-common back into duniter core

---
 .eslintignore                                 |   3 +
 app/common/index.js                           |  14 +
 app/common/lib/buid.js                        |  39 ++
 app/common/lib/constants.js                   | 228 +++++++++++
 app/common/lib/crypto/base58.js               |   9 +
 app/common/lib/crypto/keyring.js              |  75 ++++
 app/common/lib/crypto/nacl-util.js            |  50 +++
 app/common/lib/document/block.js              | 356 ++++++++++++++++++
 app/common/lib/document/certification.js      | 149 ++++++++
 app/common/lib/document/identity.js           | 187 +++++++++
 app/common/lib/document/index.js              |  10 +
 app/common/lib/document/membership.js         | 123 ++++++
 app/common/lib/document/peer.js               | 200 ++++++++++
 app/common/lib/document/transaction.js        | 189 ++++++++++
 app/common/lib/dos2unix.js                    |  23 ++
 app/common/lib/hashf.js                       |   8 +
 app/common/lib/parsers/GenericParser.js       | 108 ++++++
 app/common/lib/parsers/block.js               | 249 ++++++++++++
 app/common/lib/parsers/certification.js       |  43 +++
 app/common/lib/parsers/identity.js            |  47 +++
 app/common/lib/parsers/index.js               |  11 +
 app/common/lib/parsers/membership.js          |  75 ++++
 app/common/lib/parsers/peer.js                | 105 ++++++
 app/common/lib/parsers/revocation.js          |  52 +++
 app/common/lib/parsers/transaction.js         | 128 +++++++
 app/common/lib/rawer.js                       |  92 +++++
 app/common/lib/regex.js                       |   5 +
 app/common/lib/txunlock.js                    |  73 ++++
 app/lib/blockchain/DuniterBlockchain.ts       |   2 +-
 .../{common => common-libs}/crypto/base58.ts  |   0
 .../{common => common-libs}/crypto/keyring.ts |   0
 .../crypto/nacl-util.ts                       |   0
 app/lib/common.ts                             |   2 +-
 app/lib/constants.ts                          |   2 +-
 app/lib/dal/fileDAL.ts                        |   2 +-
 app/lib/dal/sqliteDAL/MetaDAL.ts              |   4 +-
 app/lib/dal/sqliteDAL/index/CIndexDAL.ts      |   2 +-
 app/lib/dal/sqliteDAL/index/SIndexDAL.ts      |   2 +-
 app/lib/indexer.ts                            |   4 +-
 app/lib/rules/global_rules.ts                 |   4 +-
 app/lib/rules/helpers.ts                      |   2 +-
 app/lib/rules/index.ts                        |   2 +-
 app/lib/rules/local_rules.ts                  |   4 +-
 app/modules/bma/lib/controllers/blockchain.ts |   2 +-
 .../bma/lib/controllers/transactions.ts       |   2 +-
 app/modules/bma/lib/controllers/wot.ts        |   2 +-
 app/modules/crawler/index.ts                  |   2 +-
 app/modules/crawler/lib/constants.ts          |   2 +-
 app/modules/crawler/lib/crawler.ts            |   2 +-
 app/modules/crawler/lib/req2fwd.ts            |   2 +-
 app/modules/crawler/lib/sandbox.ts            |   4 +-
 app/modules/crawler/lib/sync.ts               |   2 +-
 app/modules/keypair/index.ts                  |   2 +-
 app/modules/keypair/lib/scrypt.ts             |   4 +-
 app/modules/prover/index.ts                   |   2 +-
 app/modules/prover/lib/blockGenerator.ts      |   4 +-
 app/modules/prover/lib/blockProver.ts         |   2 +-
 app/modules/prover/lib/permanentProver.ts     |   2 +-
 app/modules/prover/lib/proof.ts               |   6 +-
 app/service/BlockchainService.ts              |   2 +-
 app/service/IdentityService.ts                |   2 +-
 app/service/MembershipService.ts              |   2 +-
 app/service/PeeringService.ts                 |   8 +-
 package.json                                  |   2 +-
 server.ts                                     |   6 +-
 test/fast/block_format.js                     |   2 +-
 test/fast/block_local.js                      |   4 +-
 test/fast/common/crypto.js                    |   6 +-
 test/fast/common/randomKey.js                 |   4 +-
 test/fast/modules/common/crypto.js            |  50 +++
 test/fast/modules/common/grammar-test.js      |  70 ++++
 test/fast/modules/common/peering.js           |  63 ++++
 test/fast/modules/common/randomKey.js         |  35 ++
 test/fast/modules/common/tx_format.js         |  28 ++
 test/fast/v1.0-local-index.js                 |   4 +-
 test/integration/cli.js                       |   2 +-
 test/integration/server-sandbox.js            |   2 +-
 test/integration/tools/user.js                |   4 +-
 test/integration/transactions-chaining.js     |   2 +-
 test/integration/transactions-pruning.js      |   2 +-
 test/integration/v1.0-modules-api.js          |   2 +-
 81 files changed, 2962 insertions(+), 65 deletions(-)
 create mode 100644 app/common/index.js
 create mode 100644 app/common/lib/buid.js
 create mode 100644 app/common/lib/constants.js
 create mode 100644 app/common/lib/crypto/base58.js
 create mode 100644 app/common/lib/crypto/keyring.js
 create mode 100644 app/common/lib/crypto/nacl-util.js
 create mode 100644 app/common/lib/document/block.js
 create mode 100644 app/common/lib/document/certification.js
 create mode 100644 app/common/lib/document/identity.js
 create mode 100644 app/common/lib/document/index.js
 create mode 100644 app/common/lib/document/membership.js
 create mode 100644 app/common/lib/document/peer.js
 create mode 100644 app/common/lib/document/transaction.js
 create mode 100644 app/common/lib/dos2unix.js
 create mode 100644 app/common/lib/hashf.js
 create mode 100644 app/common/lib/parsers/GenericParser.js
 create mode 100644 app/common/lib/parsers/block.js
 create mode 100644 app/common/lib/parsers/certification.js
 create mode 100644 app/common/lib/parsers/identity.js
 create mode 100644 app/common/lib/parsers/index.js
 create mode 100644 app/common/lib/parsers/membership.js
 create mode 100644 app/common/lib/parsers/peer.js
 create mode 100644 app/common/lib/parsers/revocation.js
 create mode 100644 app/common/lib/parsers/transaction.js
 create mode 100644 app/common/lib/rawer.js
 create mode 100644 app/common/lib/regex.js
 create mode 100644 app/common/lib/txunlock.js
 rename app/lib/{common => common-libs}/crypto/base58.ts (100%)
 rename app/lib/{common => common-libs}/crypto/keyring.ts (100%)
 rename app/lib/{common => common-libs}/crypto/nacl-util.ts (100%)
 create mode 100644 test/fast/modules/common/crypto.js
 create mode 100644 test/fast/modules/common/grammar-test.js
 create mode 100644 test/fast/modules/common/peering.js
 create mode 100644 test/fast/modules/common/randomKey.js
 create mode 100644 test/fast/modules/common/tx_format.js

diff --git a/.eslintignore b/.eslintignore
index 0601c0236..7775dfc5e 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,4 +1,7 @@
 app/cli.js
+app/common/*.js
+app/common/lib/*.js
+app/common/lib/document/*.js
 app/lib/blockchain/*.js
 app/lib/blockchain/interfaces/*.js
 app/lib/computation/*.js
diff --git a/app/common/index.js b/app/common/index.js
new file mode 100644
index 000000000..1606e4446
--- /dev/null
+++ b/app/common/index.js
@@ -0,0 +1,14 @@
+"use strict";
+
+module.exports = {
+  hashf: require('./lib/hashf'),
+  dos2unix: require('./lib/dos2unix'),
+  keyring: require('./lib/crypto/keyring'),
+  base58: require('./lib/crypto/base58'),
+  rawer: require('./lib/rawer'),
+  buid: require('./lib/buid'),
+  document: require('./lib/document'),
+  constants: require('./lib/constants'),
+  txunlock: require('./lib/txunlock'),
+  parsers: require('./lib/parsers')
+}
diff --git a/app/common/lib/buid.js b/app/common/lib/buid.js
new file mode 100644
index 000000000..55d12bb47
--- /dev/null
+++ b/app/common/lib/buid.js
@@ -0,0 +1,39 @@
+"use strict";
+const hashf = require('./hashf');
+
+const BLOCK_UID = /^(0|[1-9]\d{0,18})-[A-F0-9]{64}$/;
+
+const buidFunctions = function(number, hash) {
+  if (arguments.length === 2) {
+    return [number, hash].join('-');
+  }
+  if (arguments[0]) {
+    return [arguments[0].number, arguments[0].hash].join('-');
+  }
+  return '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855';
+};
+
+buidFunctions.fromTS = (line) => line.match(/TS:(.*)/)[1];
+buidFunctions.fromIdty = (idty) => this(idty.ts_number, idty.ts_hash);
+
+module.exports = {
+
+  format: {
+
+    hashf: (value) => hashf(String(value)).toUpperCase(),
+
+    isBuid: (value) => {
+      return (typeof value === 'string') && value.match(BLOCK_UID) ? true : false;
+    },
+
+    buid: buidFunctions,
+
+    obuid: (line) => {
+      let sp = this.buid.fromTS(line).split('-');
+      return {
+        number: sp[0],
+        hash: sp[1]
+      };
+    }
+  }
+};
diff --git a/app/common/lib/constants.js b/app/common/lib/constants.js
new file mode 100644
index 000000000..93b798990
--- /dev/null
+++ b/app/common/lib/constants.js
@@ -0,0 +1,228 @@
+"use strict";
+
+const CURRENCY     = "[a-zA-Z0-9-_ ]{2,50}"
+const BASE58       = "[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+"
+const PUBKEY       = "[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}"
+const SIGNATURE    = "[A-Za-z0-9+\\/=]{87,88}"
+const USER_ID      = "[A-Za-z0-9_-]{2,100}"
+const INTEGER      = "(0|[1-9]\\d{0,18})"
+const FINGERPRINT  = "[A-F0-9]{64}"
+const BLOCK_UID    = INTEGER + "-" + FINGERPRINT
+const BLOCK_VERSION = "(10)"
+const TX_VERSION   = "(10)"
+const DIVIDEND     = "[1-9][0-9]{0,5}"
+const ZERO_OR_POSITIVE_INT = "0|[1-9][0-9]{0,18}"
+const RELATIVE_INTEGER = "(0|-?[1-9]\\d{0,18})"
+const FLOAT        = "\\d+\.\\d+"
+const POSITIVE_INT = "[1-9][0-9]{0,18}"
+const TIMESTAMP    = "[1-9][0-9]{0,18}"
+const BOOLEAN      = "[01]"
+const SPECIAL_BLOCK = '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855'
+const META_TS      = "META:TS:" + BLOCK_UID
+const COMMENT      = "[ a-zA-Z0-9-_:/;*\\[\\]()?!^\\+=@&~#{}|\\\\<>%.]{0,255}"
+const CLTV_INTEGER = "([0-9]{1,10})";
+const CSV_INTEGER  = "([0-9]{1,8})";
+const XUNLOCK      = "[a-zA-Z0-9]{1,64}";
+const UNLOCK       = "(SIG\\(" + INTEGER + "\\)|XHX\\(" + XUNLOCK + "\\))"
+const CONDITIONS   = "(&&|\\|\\|| |[()]|(SIG\\(" + PUBKEY + "\\)|(XHX\\([A-F0-9]{64}\\)|CLTV\\(" + CLTV_INTEGER + "\\)|CSV\\(" + CSV_INTEGER + "\\))))*"
+
+const BMA_REGEXP  = /^BASIC_MERKLED_API( ([a-z_][a-z0-9-_.]*))?( ([0-9.]+))?( ([0-9a-f:]+))?( ([0-9]+))$/
+
+const MAXIMUM_LEN_OF_COMPACT_TX = 100
+const MAXIMUM_LEN_OF_OUTPUT = 2000
+const MAXIMUM_LEN_OF_UNLOCK = MAXIMUM_LEN_OF_OUTPUT
+
+module.exports = {
+
+  FORMATS: {
+    CURRENCY,
+    PUBKEY,
+    INTEGER,
+    FINGERPRINT
+  },
+
+  BLOCK_GENERATED_VERSION: 10,
+  LAST_VERSION_FOR_TX: 10,
+  TRANSACTION_VERSION: 10,
+  DOCUMENTS_VERSION: 10,
+  TRANSACTION_MAX_TRIES: 10,
+
+  BMA_REGEXP,
+  PUBLIC_KEY: exact(PUBKEY),
+  INTEGER: /^\d+$/,
+  BASE58: exact(BASE58),
+  FINGERPRINT: exact(FINGERPRINT),
+  SIG: exact(SIGNATURE),
+  BLOCK_UID: exact(BLOCK_UID),
+  USER_ID: exact(USER_ID), // Any format, by default
+
+  DOCUMENTS_VERSION_REGEXP: /^10$/,
+  BLOCKSTAMP_REGEXP: new RegExp("^" + BLOCK_UID + "$"),
+  DOCUMENTS_BLOCK_VERSION_REGEXP: new RegExp("^" + BLOCK_VERSION + "$"),
+  DOCUMENTS_TRANSACTION_VERSION_REGEXP: /^(10)$/,
+  SPECIAL_BLOCK,
+  SPECIAL_HASH: 'E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855',
+  MAXIMUM_LEN_OF_COMPACT_TX,
+  MAXIMUM_LEN_OF_OUTPUT,
+  MAXIMUM_LEN_OF_UNLOCK,
+
+  PROOF_OF_WORK: {
+    UPPER_BOUND: [
+      '9A-F',
+      '9A-E',
+      '9A-D',
+      '9A-C',
+      '9A-B',
+      '9A',
+      '9',
+      '8',
+      '7',
+      '6',
+      '5',
+      '4',
+      '3',
+      '2',
+      '1',
+      '1' // In case remainder 15 happens for some reason
+    ]
+  },
+
+  ERRORS: {
+    // Technical errors
+    WRONG_DOCUMENT:                       { httpCode: 400, uerr: { ucode: 1005, message: "Document has unkown fields or wrong line ending format" }},
+
+    // Business errors
+    WRONG_UNLOCKER:                       { httpCode: 400, uerr: { ucode: 2013, message: "Wrong unlocker in transaction" }},
+    LOCKTIME_PREVENT:                     { httpCode: 400, uerr: { ucode: 2014, message: "Locktime not elapsed yet" }},
+    SOURCE_ALREADY_CONSUMED:              { httpCode: 400, uerr: { ucode: 2015, message: "Source already consumed" }},
+    WRONG_AMOUNTS:                        { httpCode: 400, uerr: { ucode: 2016, message: "Sum of inputs must equal sum of outputs" }},
+    WRONG_OUTPUT_BASE:                    { httpCode: 400, uerr: { ucode: 2017, message: "Wrong unit base for outputs" }},
+    CANNOT_ROOT_BLOCK_NO_MEMBERS:         { httpCode: 400, uerr: { ucode: 2018, message: "Wrong new block: cannot make a root block without members" }},
+    IDENTITY_WRONGLY_SIGNED:              { httpCode: 400, uerr: { ucode: 2019, message: "Weird, the signature is wrong and in the database." }},
+    TOO_OLD_IDENTITY:                     { httpCode: 400, uerr: { ucode: 2020, message: "Identity has expired and cannot be written in the blockchain anymore." }},
+    TX_INPUTS_OUTPUTS_NOT_EQUAL:          { httpCode: 400, uerr: { ucode: 2024, message: "Transaction inputs sum must equal outputs sum" }},
+    TX_OUTPUT_SUM_NOT_EQUALS_PREV_DELTAS: { httpCode: 400, uerr: { ucode: 2025, message: "Transaction output base amount does not equal previous base deltas" }},
+    BLOCKSTAMP_DOES_NOT_MATCH_A_BLOCK:    { httpCode: 400, uerr: { ucode: 2026, message: "Blockstamp does not match a block" }},
+    A_TRANSACTION_HAS_A_MAX_SIZE:         { httpCode: 400, uerr: { ucode: 2027, message: 'A transaction has a maximum size of ' + MAXIMUM_LEN_OF_COMPACT_TX + ' lines' }},
+    TOO_OLD_MEMBERSHIP:                   { httpCode: 400, uerr: { ucode: 2029, message: "Too old membership." }},
+    MAXIMUM_LEN_OF_OUTPUT:                { httpCode: 400, uerr: { ucode: 2032, message: 'A transaction output has a maximum size of ' + MAXIMUM_LEN_OF_OUTPUT + ' characters' }},
+    MAXIMUM_LEN_OF_UNLOCK:                { httpCode: 400, uerr: { ucode: 2033, message: 'A transaction unlock has a maximum size of ' + MAXIMUM_LEN_OF_UNLOCK + ' characters' }},
+
+    WRONG_SIGNATURE_FOR_CERT:             { httpCode: 400, uerr: { ucode: 3000, message: 'Wrong signature for certification' }},
+  },
+
+  // INDEXES
+  M_INDEX: 'MINDEX',
+  I_INDEX: 'IINDEX',
+  S_INDEX: 'SINDEX',
+  C_INDEX: 'CINDEX',
+  IDX_CREATE: 'CREATE',
+  IDX_UPDATE: 'UPDATE',
+
+  // Protocol fixed values
+  NB_DIGITS_UD: 4,
+  REVOCATION_FACTOR: 2,
+  TX_WINDOW: 3600 * 24 * 7,
+  POW_DIFFICULTY_RANGE_RATIO: 1.189, // deduced from Hexadecimal relation between 2 chars ~= 16^(1/16)
+  ACCOUNT_MINIMUM_CURRENT_BASED_AMOUNT: 100,
+
+
+  DOCUMENTS: {
+    DOC_VERSION:    find('Version: (10)'),
+    DOC_CURRENCY:   find('Currency: (' + CURRENCY + ')'),
+    DOC_ISSUER:     find('Issuer: (' + PUBKEY + ')'),
+    TIMESTAMP:      find('Timestamp: (' + BLOCK_UID + ')')
+  },
+  IDENTITY: {
+    INLINE: exact(PUBKEY + ":" + SIGNATURE + ":" + BLOCK_UID + ":" + USER_ID),
+    IDTY_TYPE:      find('Type: (Identity)'),
+    IDTY_UID:       find('UniqueID: (' + USER_ID + ')')
+  },
+  BLOCK: {
+    NONCE:       find("Nonce: (" + ZERO_OR_POSITIVE_INT + ")"),
+    VERSION:     find("Version: " + BLOCK_VERSION),
+    TYPE:        find("Type: (Block)"),
+    CURRENCY:    find("Currency: (" + CURRENCY + ")"),
+    BNUMBER:     find("Number: (" + ZERO_OR_POSITIVE_INT + ")"),
+    POWMIN:      find("PoWMin: (" + ZERO_OR_POSITIVE_INT + ")"),
+    TIME:        find("Time: (" + TIMESTAMP + ")"),
+    MEDIAN_TIME: find("MedianTime: (" + TIMESTAMP + ")"),
+    UD:          find("UniversalDividend: (" + DIVIDEND + ")"),
+    UNIT_BASE:   find("UnitBase: (" + INTEGER + ")"),
+    PREV_HASH:   find("PreviousHash: (" + FINGERPRINT + ")"),
+    PREV_ISSUER: find("PreviousIssuer: (" + PUBKEY + ")"),
+    MEMBERS_COUNT:find("MembersCount: (" + ZERO_OR_POSITIVE_INT + ")"),
+    BLOCK_ISSUER:find('Issuer: (' + PUBKEY + ')'),
+    BLOCK_ISSUERS_FRAME:find('IssuersFrame: (' + INTEGER + ')'),
+    BLOCK_ISSUERS_FRAME_VAR:find('IssuersFrameVar: (' + RELATIVE_INTEGER + ')'),
+    DIFFERENT_ISSUERS_COUNT:find('DifferentIssuersCount: (' + INTEGER + ')'),
+    PARAMETERS:  find("Parameters: (" + FLOAT + ":" + INTEGER + ":" + INTEGER + ":" + INTEGER + ":" + INTEGER + ":" + INTEGER + ":" + INTEGER + ":" + INTEGER + ":" + INTEGER + ":" + INTEGER + ":" + FLOAT + ":" + INTEGER + ":" + INTEGER + ":" + INTEGER + ":" + INTEGER + ":" + INTEGER + ":" + FLOAT + ":" + INTEGER + ":" + INTEGER + ":" + INTEGER + ")"),
+    JOINER:   exact(PUBKEY + ":" + SIGNATURE + ":" + BLOCK_UID + ":" + BLOCK_UID + ":" + USER_ID),
+    ACTIVE:   exact(PUBKEY + ":" + SIGNATURE + ":" + BLOCK_UID + ":" + BLOCK_UID + ":" + USER_ID),
+    LEAVER:   exact(PUBKEY + ":" + SIGNATURE + ":" + BLOCK_UID + ":" + BLOCK_UID + ":" + USER_ID),
+    REVOCATION: exact(PUBKEY + ":" + SIGNATURE),
+    EXCLUDED: exact(PUBKEY),
+    INNER_HASH: find("InnerHash: (" + FINGERPRINT + ")"),
+    SPECIAL_HASH: 'E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855',
+    SPECIAL_BLOCK
+  },
+  CERT: {
+    SELF: {
+      UID: exact("UID:" + USER_ID),
+      META: exact(META_TS)
+    },
+    REVOKE: exact("UID:REVOKE"),
+    OTHER: {
+      META: exact(META_TS),
+      INLINE: exact(PUBKEY + ":" + PUBKEY + ":" + INTEGER + ":" + SIGNATURE)
+    }
+  },
+  CERTIFICATION: {
+    CERT_TYPE:      find('Type: (Certification)'),
+    IDTY_ISSUER:    find('IdtyIssuer: (' + PUBKEY + ')'),
+    IDTY_UID:       find('IdtyUniqueID: (' + USER_ID + ')'),
+    IDTY_TIMESTAMP: find('IdtyTimestamp: (' + BLOCK_UID + ')'),
+    IDTY_SIG:       find('IdtySignature: (' + SIGNATURE + ')'),
+    CERT_TIMESTAMP: find('CertTimestamp: (' + BLOCK_UID + ')')
+  },
+  REVOCATION: {
+    REVOC_TYPE:      find('Type: (Certification)'),
+    IDTY_ISSUER:     find('IdtyIssuer: (' + PUBKEY + ')'),
+    IDTY_UID:        find('IdtyUniqueID: (' + USER_ID + ')'),
+    IDTY_TIMESTAMP:  find('IdtyTimestamp: (' + BLOCK_UID + ')'),
+    IDTY_SIG:        find('IdtySignature: (' + SIGNATURE + ')')
+  },
+  MEMBERSHIP: {
+    BLOCK:      find('Block: (' + BLOCK_UID + ')'),
+    VERSION:    find('Version: (10)'),
+    CURRENCY:   find('Currency: (' + CURRENCY + ')'),
+    ISSUER:     find('Issuer: (' + PUBKEY + ')'),
+    MEMBERSHIP: find('Membership: (IN|OUT)'),
+    USERID:     find('UserID: (' + USER_ID + ')'),
+    CERTTS:     find('CertTS: (' + BLOCK_UID + ')')
+  },
+  TRANSACTION: {
+    HEADER:  exact("TX:" + TX_VERSION + ":" + INTEGER + ":" + INTEGER + ":" + INTEGER + ":" + INTEGER + ":" + BOOLEAN + ":" + INTEGER),
+    SENDER:  exact(PUBKEY),
+    SOURCE_V3:  exact("(" + POSITIVE_INT + ":" + INTEGER + ":T:" + FINGERPRINT + ":" + INTEGER + "|" + POSITIVE_INT + ":" + INTEGER + ":D:" + PUBKEY + ":" + POSITIVE_INT + ")"),
+    UNLOCK:  exact(INTEGER + ":" + UNLOCK + "( (" + UNLOCK + "))*"),
+    TARGET:  exact(POSITIVE_INT + ":" + INTEGER + ":" + CONDITIONS),
+    BLOCKSTAMP:find('Blockstamp: (' + BLOCK_UID + ')'),
+    COMMENT: find("Comment: (" + COMMENT + ")"),
+    LOCKTIME:find("Locktime: (" + INTEGER + ")"),
+    INLINE_COMMENT: exact(COMMENT),
+    OUTPUT_CONDITION: exact(CONDITIONS)
+  },
+  PEER: {
+    BLOCK: find("Block: (" + INTEGER + "-" + FINGERPRINT + ")"),
+    SPECIAL_BLOCK
+  },
+}
+
+function exact (regexpContent) {
+  return new RegExp("^" + regexpContent + "$");
+}
+
+function find (regexpContent) {
+  return new RegExp(regexpContent);
+}
diff --git a/app/common/lib/crypto/base58.js b/app/common/lib/crypto/base58.js
new file mode 100644
index 000000000..db14015cb
--- /dev/null
+++ b/app/common/lib/crypto/base58.js
@@ -0,0 +1,9 @@
+"use strict"
+
+const assert = require('assert')
+const bs58 = require('bs58')
+
+module.exports = {
+  encode: (bytes) => bs58.encode(bytes),
+  decode: (data) => new Uint8Array(bs58.decode(data))
+};
diff --git a/app/common/lib/crypto/keyring.js b/app/common/lib/crypto/keyring.js
new file mode 100644
index 000000000..ca74b8450
--- /dev/null
+++ b/app/common/lib/crypto/keyring.js
@@ -0,0 +1,75 @@
+"use strict";
+const nacl        = require('tweetnacl');
+const base58      = require('./base58');
+const seedrandom  = require('seedrandom');
+const naclBinding = require('naclb');
+
+nacl.util = require('./nacl-util');
+
+const crypto_sign_BYTES = 64;
+
+/**
+ * Verify a signature against data & public key.
+ * Return true of false as callback argument.
+ */
+function verify(rawMsg, rawSig, rawPub) {
+  const msg = nacl.util.decodeUTF8(rawMsg);
+  const sig = nacl.util.decodeBase64(rawSig);
+  const pub = base58.decode(rawPub);
+  const m = new Uint8Array(crypto_sign_BYTES + msg.length);
+  const sm = new Uint8Array(crypto_sign_BYTES + msg.length);
+  let i;
+  for (i = 0; i < crypto_sign_BYTES; i++) sm[i] = sig[i];
+  for (i = 0; i < msg.length; i++) sm[i+crypto_sign_BYTES] = msg[i];
+
+  // Call to verification lib...
+  return naclBinding.verify(m, sm, pub);
+}
+
+function Key(pub, sec) {
+  /*****************************
+  *
+  *      GENERAL CRYPTO
+  *
+  *****************************/
+
+  this.publicKey = pub;
+  this.secretKey = sec;
+
+  const rawSec = () => base58.decode(this.secretKey);
+
+  this.json = () => { return {
+    pub: this.publicKey,
+    sec: this.secretKey
+  }};
+
+  this.sign = (msg) => Promise.resolve(this.signSync(msg));
+
+  this.signSync = (msg) => {
+    const m = nacl.util.decodeUTF8(msg);
+    const signedMsg = naclBinding.sign(m, rawSec());
+    const sig = new Uint8Array(crypto_sign_BYTES);
+    for (let i = 0; i < sig.length; i++) {
+      sig[i] = signedMsg[i];
+    }
+    return nacl.util.encodeBase64(sig);
+  };
+}
+
+function randomKey() {
+  const byteseed = new Uint8Array(32)
+  for (let i = 0; i < 32; i++) {
+    byteseed[i] = Math.floor(seedrandom()() *  255) + 1
+  }
+  const keypair = nacl.sign.keyPair.fromSeed(byteseed)
+  return new Key(
+    base58.encode(keypair.publicKey),
+    base58.encode(keypair.secretKey)
+  )
+}
+
+module.exports ={
+  randomKey,
+  Key: (pub, sec) => new Key(pub, sec),
+  verify: verify
+};
diff --git a/app/common/lib/crypto/nacl-util.js b/app/common/lib/crypto/nacl-util.js
new file mode 100644
index 000000000..05cebc0f4
--- /dev/null
+++ b/app/common/lib/crypto/nacl-util.js
@@ -0,0 +1,50 @@
+// Written in 2014-2016 by Dmitry Chestnykh and Devi Mandiri.
+// Public domain.
+(function(root, f) {
+  'use strict';
+  if (typeof module !== 'undefined' && module.exports) module.exports = f();
+  else if (root.nacl) root.nacl.util = f();
+  else {
+    root.nacl = {};
+    root.nacl.util = f();
+  }
+}(this, function() {
+  'use strict';
+
+  let util = {};
+
+  util.decodeUTF8 = function(s) {
+    let i, d = unescape(encodeURIComponent(s)), b = new Uint8Array(d.length);
+    for (i = 0; i < d.length; i++) b[i] = d.charCodeAt(i);
+    return b;
+  };
+
+  util.encodeUTF8 = function(arr) {
+    let i, s = [];
+    for (i = 0; i < arr.length; i++) s.push(String.fromCharCode(arr[i]));
+    return decodeURIComponent(escape(s.join('')));
+  };
+
+  util.encodeBase64 = function(arr) {
+    if (typeof btoa === 'undefined' || !window) {
+      return (new Buffer(arr)).toString('base64');
+    } else {
+      let i, s = [], len = arr.length;
+      for (i = 0; i < len; i++) s.push(String.fromCharCode(arr[i]));
+      return btoa(s.join(''));
+    }
+  };
+
+  util.decodeBase64 = function(s) {
+    if (typeof atob === 'undefined' || !window) {
+      return new Uint8Array(Array.prototype.slice.call(new Buffer(s, 'base64'), 0));
+    } else {
+      let i, d = atob(s), b = new Uint8Array(d.length);
+      for (i = 0; i < d.length; i++) b[i] = d.charCodeAt(i);
+      return b;
+    }
+  };
+
+  return util;
+
+}));
diff --git a/app/common/lib/document/block.js b/app/common/lib/document/block.js
new file mode 100644
index 000000000..c0c3f9eea
--- /dev/null
+++ b/app/common/lib/document/block.js
@@ -0,0 +1,356 @@
+"use strict";
+const _ = require('underscore')
+const constants = require('../constants');
+const regex = require('../regex');
+const hashf = require('../hashf');
+const Transaction = require('./transaction');
+
+// Constants
+const SIGNED = false
+const UNSIGNED = !SIGNED
+
+module.exports = class Block {
+
+  constructor(
+    version,
+    nonce,
+    number,
+    powMin,
+    time,
+    medianTime,
+    membersCount,
+    monetaryMass,
+    unitbase,
+    issuersCount,
+    issuersFrame,
+    issuersFrameVar,
+    len,
+    currency,
+    issuer,
+    signature,
+    hash,
+    parameters,
+    previousHash,
+    previousIssuer,
+    inner_hash,
+    dividend,
+    identities,
+    joiners,
+    actives,
+    leavers,
+    revoked,
+    excluded,
+    certifications,
+    transactions
+  ) {
+    this.version         = parseInt(version)
+    this.nonce           = parseInt(nonce)
+    this.number          = parseInt(number)
+    this.powMin          = parseInt(powMin)
+    this.time            = parseInt(time)
+    this.medianTime      = parseInt(medianTime)
+    this.membersCount    = parseInt(membersCount)
+    this.monetaryMass    = parseInt(monetaryMass)
+    this.unitbase        = parseInt(unitbase)
+    this.issuersCount    = parseInt(issuersCount)
+    this.issuersFrame    = parseInt(issuersFrame)
+    this.issuersFrameVar = parseInt(issuersFrameVar)
+    this.len             = parseInt(len)
+    this.currency        = currency || ""
+    this.issuer          = issuer || ""
+    this.signature       = signature || ""
+    this.hash            = hash || ""
+    this.parameters      = parameters || ""
+    this.previousHash    = previousHash || null
+    this.previousIssuer  = previousIssuer || null
+    this.inner_hash      = inner_hash || null
+    this.dividend        = parseInt(dividend) || null
+    this.identities      = (identities || []).slice()
+    this.joiners         = (joiners || []).slice()
+    this.actives         = (actives || []).slice()
+    this.leavers         = (leavers || []).slice()
+    this.revoked         = (revoked || []).slice()
+    this.excluded        = (excluded || []).slice()
+    this.certifications  = (certifications || []).slice()
+    this.transactions    = (transactions || []).slice()
+  }
+
+  /**
+   * Aliases
+   */
+
+  get pub() {
+    return this.pubkey
+  }
+
+  get statics() {
+    return {
+    }
+  }
+
+  /**
+   * Methods
+   */
+
+  json() {
+    return {
+      version: this.version,
+      nonce: this.nonce,
+      number: this.number,
+      powMin: this.powMin,
+      time: this.time,
+      medianTime: this.medianTime,
+      membersCount: this.membersCount,
+      monetaryMass: this.monetaryMass,
+      unitbase: this.unitbase,
+      issuersCount: this.issuersCount,
+      issuersFrame: this.issuersFrame,
+      issuersFrameVar: this.issuersFrameVar,
+      len: this.len,
+      currency: this.currency,
+      issuer: this.issuer,
+      signature: this.signature,
+      hash: this.hash,
+      parameters: this.parameters,
+      previousHash: this.previousHash,
+      previousIssuer: this.previousIssuer,
+      inner_hash: this.inner_hash,
+      dividend: this.dividend,
+      identities: this.identities,
+      joiners: this.joiners,
+      actives: this.actives,
+      leavers: this.leavers,
+      revoked: this.revoked,
+      excluded: this.excluded,
+      certifications: this.certifications,
+      transactions: this.transactions.map((tx) => _.omit(tx, 'raw', 'certifiers', 'hash'))
+    }
+  }
+
+  getRawSigned() {
+    return Block.toRAWinnerPartWithHashAndNonce(this, SIGNED)
+  }
+
+  getRawInnerPart() {
+    return Block.toRAWInnerPart(this)
+  }
+
+  getSignedPart() {
+    return Block.toRAWHashAndNonce(this, UNSIGNED)
+  }
+
+  static getLen(block) {
+    return block.identities.length +
+      block.joiners.length +
+      block.actives.length +
+      block.leavers.length +
+      block.revoked.length +
+      block.certifications.length +
+      block.transactions.reduce((sum, tx) => sum + Transaction.getLen(tx), 0)
+  }
+
+  static getHash(json) {
+    const raw = Block.toRAWHashAndNonce(json)
+    return hashf(raw).toUpperCase()
+  }
+
+  static fromJSON(json) {
+    // Returns a new Peer only if `json` is defined and not null
+    if (!json) return null
+    return new Block(
+      json.version || constants.DOCUMENTS_VERSION,
+      json.nonce,
+      json.number,
+      json.powMin,
+      json.time,
+      json.medianTime,
+      json.membersCount,
+      json.monetaryMass,
+      json.unitbase,
+      json.issuersCount,
+      json.issuersFrame,
+      json.issuersFrameVar,
+      json.len,
+      json.currency,
+      json.issuer,
+      json.signature,
+      json.hash,
+      json.parameters,
+      json.previousHash,
+      json.previousIssuer,
+      json.inner_hash,
+      json.dividend,
+      json.identities,
+      json.joiners,
+      json.actives,
+      json.leavers,
+      json.revoked,
+      json.excluded,
+      json.certifications,
+      json.transactions
+    )
+  }
+
+  static toRAW(json, unsigned) {
+    const block = Block.fromJSON(json)
+    let raw = "";
+    raw += "Version: " + block.version + "\n";
+    raw += "Type: Block\n";
+    raw += "Currency: " + block.currency + "\n";
+    raw += "Number: " + block.number + "\n";
+    raw += "PoWMin: " + block.powMin + "\n";
+    raw += "Time: " + block.time + "\n";
+    raw += "MedianTime: " + block.medianTime + "\n";
+    if (block.dividend)
+      raw += "UniversalDividend: " + block.dividend + "\n";
+    raw += "UnitBase: " + block.unitbase + "\n";
+    raw += "Issuer: " + block.issuer + "\n";
+    raw += "IssuersFrame: " + block.issuersFrame + "\n";
+    raw += "IssuersFrameVar: " + block.issuersFrameVar + "\n";
+    raw += "DifferentIssuersCount: " + block.issuersCount + "\n";
+    if(block.previousHash)
+      raw += "PreviousHash: " + block.previousHash + "\n";
+    if(block.previousIssuer)
+      raw += "PreviousIssuer: " + block.previousIssuer + "\n";
+    if(block.parameters)
+      raw += "Parameters: " + block.parameters + "\n";
+    raw += "MembersCount: " + block.membersCount + "\n";
+    raw += "Identities:\n";
+    for (const idty of (block.identities || [])){
+      raw += idty + "\n";
+    }
+    raw += "Joiners:\n";
+    for (const joiner of (block.joiners || [])){
+      raw += joiner + "\n";
+    }
+    raw += "Actives:\n";
+    for (const active of (block.actives || [])){
+      raw += active + "\n";
+    }
+    raw += "Leavers:\n";
+    for (const leaver of (block.leavers || [])){
+      raw += leaver + "\n";
+    }
+    raw += "Revoked:\n";
+    for (const revoked of (block.revoked || [])){
+      raw += revoked + "\n";
+    }
+    raw += "Excluded:\n";
+    for (const excluded of (block.excluded || [])){
+      raw += excluded + "\n";
+    }
+    raw += "Certifications:\n";
+    for (const cert of (block.certifications || [])){
+      raw += cert + "\n";
+    }
+    raw += "Transactions:\n";
+    for (const tx of (block.transactions || [])){
+      raw += tx.raw || Block.getCompactTransaction(tx);
+    }
+    if (!unsigned) {
+      raw += block.signature + '\n'
+    }
+    return raw
+  }
+
+  static toRAWInnerPart(json) {
+    const block = Block.fromJSON(json)
+    let raw = "";
+    raw += "Version: " + block.version + "\n";
+    raw += "Type: Block\n";
+    raw += "Currency: " + block.currency + "\n";
+    raw += "Number: " + block.number + "\n";
+    raw += "PoWMin: " + block.powMin + "\n";
+    raw += "Time: " + block.time + "\n";
+    raw += "MedianTime: " + block.medianTime + "\n";
+    if (block.dividend)
+      raw += "UniversalDividend: " + block.dividend + "\n";
+    raw += "UnitBase: " + block.unitbase + "\n";
+    raw += "Issuer: " + block.issuer + "\n";
+    raw += "IssuersFrame: " + block.issuersFrame + "\n";
+    raw += "IssuersFrameVar: " + block.issuersFrameVar + "\n";
+    raw += "DifferentIssuersCount: " + block.issuersCount + "\n";
+    if(block.previousHash)
+      raw += "PreviousHash: " + block.previousHash + "\n";
+    if(block.previousIssuer)
+      raw += "PreviousIssuer: " + block.previousIssuer + "\n";
+    if(block.parameters)
+      raw += "Parameters: " + block.parameters + "\n";
+    raw += "MembersCount: " + block.membersCount + "\n";
+    raw += "Identities:\n";
+    for (const idty of (block.identities || [])){
+      raw += idty + "\n";
+    }
+    raw += "Joiners:\n";
+    for (const joiner of (block.joiners || [])){
+      raw += joiner + "\n";
+    }
+    raw += "Actives:\n";
+    for (const active of (block.actives || [])){
+      raw += active + "\n";
+    }
+    raw += "Leavers:\n";
+    for (const leaver of (block.leavers || [])){
+      raw += leaver + "\n";
+    }
+    raw += "Revoked:\n";
+    for (const revoked of (block.revoked || [])){
+      raw += revoked + "\n";
+    }
+    raw += "Excluded:\n";
+    for (const excluded of (block.excluded || [])){
+      raw += excluded + "\n";
+    }
+    raw += "Certifications:\n";
+    for (const cert of (block.certifications || [])){
+      raw += cert + "\n";
+    }
+    raw += "Transactions:\n";
+    for (const tx of (block.transactions || [])){
+      raw += tx.raw || Transaction.getCompactTransaction(tx);
+    }
+    return raw
+  }
+
+  static toRAWinnerPartWithHashAndNonce(json, unsigned) {
+    let raw = Block.toRAWInnerPart(json);
+    raw += "InnerHash: " + json.inner_hash + "\n"
+    raw += "Nonce: " + json.nonce + "\n"
+    if (unsigned === false) {
+      raw += json.signature + "\n"
+    }
+    return raw
+  };
+
+  static toRAWHashAndNonce(json, unsigned) {
+    let raw = "" +
+      "InnerHash: " + json.inner_hash + "\n" +
+      "Nonce: " + json.nonce + "\n"
+    if (!unsigned) {
+      raw += json.signature + "\n"
+    }
+    return raw
+  }
+
+  static getTransactions(json) {
+    const block = Block.fromJSON(json)
+    return block.transactions.slice().map((tx) => {
+      tx.inputs = tx.inputs.map((i) => {
+        if (typeof i === 'string') {
+          return Transaction.inputStr2Obj(i)
+        } else {
+          return i
+        }
+      })
+      tx.outputs = tx.outputs.map((o) => {
+        if (typeof o === 'string') {
+          return Transaction.outputStr2Obj(o)
+        } else {
+          return o
+        }
+      })
+      return tx
+    })
+  };
+
+}
diff --git a/app/common/lib/document/certification.js b/app/common/lib/document/certification.js
new file mode 100644
index 000000000..3cc70e69e
--- /dev/null
+++ b/app/common/lib/document/certification.js
@@ -0,0 +1,149 @@
+"use strict";
+const constants = require('../constants');
+const regex = require('../regex');
+const hashf = require('../hashf');
+
+// Constants
+const SIGNED = true
+const UNSIGNED = !SIGNED
+
+module.exports = class Certification {
+
+  constructor(
+    version,
+    currency,
+    issuer,
+    blockstamp,
+    idty_issuer,
+    idty_uid,
+    idty_buid,
+    idty_sig,
+    signature) {
+    this.version = version
+    this.currency = currency
+    this.issuer = issuer
+    this.blockstamp = blockstamp
+    this.idty_issuer = idty_issuer
+    this.idty_uid = idty_uid
+    this.idty_buid = idty_buid
+    this.idty_sig = idty_sig
+    this.signature = signature
+  }
+
+  /**
+   * Aliases
+   */
+
+  get buid() {
+    return this.blockstamp
+  }
+
+  set buid(buid) {
+    this.blockstamp = buid
+  }
+
+  get sig() {
+    return this.signature
+  }
+
+  set sig(sig) {
+    this.signature = sig
+  }
+
+  get pubkey() {
+    return this.issuer
+  }
+
+  set pubkey(pubkey) {
+    this.issuer = pubkey
+  }
+
+  get to() {
+    return this.idty_issuer
+  }
+
+  set to(to) {
+    this.idty_issuer = to
+  }
+
+  get blockNumber() {
+    if (!this.blockstamp) {
+      return null
+    }
+    return parseInt(this.blockstamp.split('-')[0])
+  }
+
+  get block_number() {
+    return this.blockNumber
+  }
+
+  get blockHash() {
+    if (!this.blockstamp) {
+      return null
+    }
+    return this.blockstamp.split('-')[1]
+  }
+
+  get block_hash() {
+    return this.blockHash
+  }
+
+  /**
+   * Methods
+   */
+
+  getRaw() {
+    return Certification.toRAW(this);
+  }
+
+  inline() {
+    return [this.pubkey, this.to, this.block_number, this.sig].join(':')
+  }
+
+  /**
+   * Statics
+   */
+
+  static fromJSON(json) {
+    const buid = [json.block_number, json.block_hash].join('-')
+    return new Certification(
+      json.version || constants.DOCUMENTS_VERSION,
+      json.currency,
+      json.issuer || json.pubkey || json.from,
+      json.blockstamp || json.buid || buid,
+      json.idty_issuer || json.to,
+      json.idty_uid,
+      json.idty_buid,
+      json.idty_sig,
+      json.signature || json.sig
+    )
+  }
+
+  static toRAW(json, withSig = true) {
+    const cert = Certification.fromJSON(json)
+    let raw = "";
+    raw += "Version: " + cert.version + "\n";
+    raw += "Type: Certification\n";
+    raw += "Currency: " + cert.currency + "\n";
+    raw += "Issuer: " + cert.issuer + "\n";
+    raw += "IdtyIssuer: " + cert.idty_issuer + '\n';
+    raw += "IdtyUniqueID: " + cert.idty_uid + '\n';
+    raw += "IdtyTimestamp: " + cert.idty_buid + '\n';
+    raw += "IdtySignature: " + cert.idty_sig + '\n';
+    raw += "CertTimestamp: " + cert.buid + '\n';
+    if (cert.sig && withSig) {
+      raw += cert.sig + '\n'
+    }
+    return raw
+  }
+
+  static fromInline(inline) {
+    const sp = inline.split(':')
+    return Certification.fromJSON({
+      pubkey: sp[0],
+      to: sp[1],
+      block_number: parseInt(sp[2]),
+      sig: sp[3]
+    })
+  }
+}
diff --git a/app/common/lib/document/identity.js b/app/common/lib/document/identity.js
new file mode 100644
index 000000000..2792fe73e
--- /dev/null
+++ b/app/common/lib/document/identity.js
@@ -0,0 +1,187 @@
+"use strict";
+const constants = require('../constants');
+const regex = require('../regex');
+const hashf = require('../hashf');
+
+// Constants
+const SIGNED = true
+const UNSIGNED = !SIGNED
+
+module.exports = class Identity {
+
+  constructor(
+    version,
+    currency,
+    pubkey,
+    uid,
+    blockstamp,
+    signature,
+    revoked,
+    revoked_on,
+    revocation_sig) {
+    this.version = version || constants.DOCUMENTS_VERSION
+    this.currency = currency
+    this.pubkey = pubkey
+    this.uid = uid
+    this.blockstamp = blockstamp
+    this.signature = signature
+    this.revoked = revoked
+    this.revoked_on = revoked_on
+    this.revocation_sig = revocation_sig
+    this.certs = []
+  }
+
+  /**
+   * Aliases
+   */
+
+  get buid() {
+    return this.blockstamp
+  }
+
+  set buid(buid) {
+    this.blockstamp = buid
+  }
+
+  get sig() {
+    return this.signature
+  }
+
+  set sig(sig) {
+    this.signature = sig
+  }
+
+  get blockNumber() {
+    if (!this.blockstamp) {
+      return null
+    }
+    return parseInt(this.blockstamp.split('-')[0])
+  }
+
+  get blockHash() {
+    if (!this.blockstamp) {
+      return null
+    }
+    return this.blockstamp.split('-')[1]
+  }
+
+  get hash() {
+    return this.getTargetHash()
+  }
+
+  /**
+   * Methods
+   */
+
+  getTargetHash() {
+    return hashf(this.uid + this.buid + this.pubkey).toUpperCase();
+  }
+
+  inline() {
+    return [this.pubkey, this.sig, this.buid, this.uid].join(':');
+  }
+
+  rawWithoutSig() {
+    return Identity.toRAW(this, UNSIGNED)
+  }
+
+  json() {
+    const others = [];
+    this.certs.forEach((cert) => {
+      others.push({
+        "pubkey": cert.from,
+        "meta": {
+          "block_number": cert.block_number,
+          "block_hash": cert.block_hash
+        },
+        "uids": cert.uids,
+        "isMember": cert.isMember,
+        "wasMember": cert.wasMember,
+        "signature": cert.sig
+      });
+    });
+    const uids = [{
+      "uid": this.uid,
+      "meta": {
+        "timestamp": this.buid
+      },
+      "revoked": this.revoked,
+      "revoked_on": this.revoked_on,
+      "revocation_sig": this.revocation_sig,
+      "self": this.sig,
+      "others": others
+    }];
+    const signed = [];
+    this.signed.forEach((cert) => {
+      signed.push({
+        "uid": cert.idty.uid,
+        "pubkey": cert.idty.pubkey,
+        "meta": {
+          "timestamp": cert.idty.buid
+        },
+        "cert_time": {
+          "block": cert.block_number,
+          "block_hash": cert.block_hash
+        },
+        "isMember": cert.idty.member,
+        "wasMember": cert.idty.wasMember,
+        "signature": cert.sig
+      });
+    });
+    return {
+      "pubkey": this.pubkey,
+      "uids": uids,
+      "signed": signed
+    }
+  }
+
+  /**
+   * Statics
+   */
+
+  static fromJSON(json) {
+    return new Identity(
+      json.version || constants.DOCUMENTS_VERSION,
+      json.currency,
+      json.pubkey || json.issuer,
+      json.uid,
+      json.blockstamp || json.buid,
+      json.signature || json.sig,
+      json.revoked,
+      json.revoked_on,
+      json.revocation_sig)
+  }
+
+  static toRAW(json, withSig) {
+    const idty = Identity.fromJSON(json)
+    let raw = ""
+    raw += "Version: " + idty.version + "\n"
+    raw += "Type: Identity\n"
+    raw += "Currency: " + idty.currency + "\n"
+    raw += "Issuer: " + (idty.issuer || idty.pubkey) + "\n"
+    raw += "UniqueID: " + idty.uid + '\n'
+    raw += "Timestamp: " + idty.buid + '\n'
+    if (idty.sig && withSig) {
+      raw += idty.sig + '\n'
+    }
+    return raw
+  }
+
+  static fromInline(inline) {
+    const sp = inline.split(':')
+    return Identity.fromJSON({
+      pubkey: sp[0],
+      sig: sp[1],
+      buid: sp[2],
+      uid: sp[3]
+    })
+  }
+
+  static revocationFromInline(inline) {
+    const sp = inline.split(':')
+    return {
+      pubkey: sp[0],
+      sig: sp[1]
+    }
+  }
+}
diff --git a/app/common/lib/document/index.js b/app/common/lib/document/index.js
new file mode 100644
index 000000000..c7c42fb9c
--- /dev/null
+++ b/app/common/lib/document/index.js
@@ -0,0 +1,10 @@
+"use strict";
+
+module.exports = {
+  Peer: require('./peer'),
+  Block: require('./block'),
+  Membership: require('./membership'),
+  Transaction: require('./transaction'),
+  Identity: require('./identity'),
+  Certification: require('./certification')
+}
\ No newline at end of file
diff --git a/app/common/lib/document/membership.js b/app/common/lib/document/membership.js
new file mode 100644
index 000000000..14844db8c
--- /dev/null
+++ b/app/common/lib/document/membership.js
@@ -0,0 +1,123 @@
+"use strict";
+const constants = require('../constants');
+const regex = require('../regex');
+
+// Constants
+const SIGNED = false
+const UNSIGNED = !SIGNED
+
+module.exports = class Membership {
+
+  constructor(
+    version,
+    currency,
+    issuer,
+    membership,
+    userid,
+    blockstamp,
+    certts,
+    signature) {
+    this.version = version
+    this.currency = currency
+    this.issuer = issuer
+    this.membership = membership
+    this.userid = userid
+    this.blockstamp = blockstamp
+    this.certts = certts
+    this.signature = signature
+  }
+
+  /**
+   * Aliases
+   */
+
+  get number() {
+    return this.blockNumber
+  }
+
+  get fpr() {
+    return this.blockHash
+  }
+
+  get block() {
+    return this.blockstamp
+  }
+
+  get blockNumber() {
+    if (!this.blockstamp) {
+      return null
+    }
+    return parseInt(this.blockstamp.split('-')[0])
+  }
+
+  get blockHash() {
+    if (!this.blockstamp) {
+      return null
+    }
+    return this.blockstamp.split('-')[1]
+  }
+
+  /**
+   * Methods
+   */
+
+  getRaw() {
+    return Membership.toRAW(this)
+  }
+
+  inline() {
+    return [
+      this.issuer,
+      this.signature,
+      this.blockstamp,
+      this.certts,
+      this.userid
+    ].join(':')
+  }
+
+  /**
+   * Statics
+   */
+
+  static toRAW(json) {
+    const ms = Membership.fromJSON(json)
+    let raw = ""
+    raw += "Version: " + ms.version + "\n"
+    raw += "Type: Membership\n"
+    raw += "Currency: " + ms.currency + "\n"
+    raw += "Issuer: " + ms.issuer + "\n"
+    raw += "Block: " + ms.block + "\n"
+    raw += "Membership: " + ms.membership + "\n"
+    if (ms.userid)
+      raw += "UserID: " + ms.userid + "\n"
+    if (ms.certts)
+      raw += "CertTS: " + ms.certts + "\n"
+    return raw
+  }
+
+  static fromJSON(json) {
+    return new Membership(
+      json.version || constants.DOCUMENTS_VERSION,
+      json.currency,
+      json.issuer,
+      json.membership,
+      json.userid,
+      json.blockstamp || json.block,
+      json.certts,
+      json.signature)
+  }
+
+  static fromInline(inline, type, currency) {
+    const sp = inline.split(':')
+    return Membership.fromJSON({
+      version:    constants.DOCUMENTS_VERSION,
+      currency:   currency,
+      issuer:     sp[0],
+      membership: type,
+      userid:     sp[4],
+      blockstamp: sp[2],
+      certts:     sp[3],
+      signature:  sp[1]
+    })
+  }
+}
diff --git a/app/common/lib/document/peer.js b/app/common/lib/document/peer.js
new file mode 100644
index 000000000..92c0ce055
--- /dev/null
+++ b/app/common/lib/document/peer.js
@@ -0,0 +1,200 @@
+"use strict";
+const constants = require('../constants');
+const regex = require('../regex');
+
+// Constants
+const SIGNED = false
+const UNSIGNED = !SIGNED
+
+module.exports = class Peer {
+
+  constructor(version, currency, pubkey, endpoints, blockstamp, signature) {
+
+    this.version = version
+    this.currency = currency
+    this.pubkey = pubkey
+    this.endpoints = endpoints || [];
+    this.blockstamp = blockstamp;
+    this.signature = signature;
+  }
+
+  /**
+   * Aliases
+   */
+
+  get pub() {
+    return this.pubkey
+  }
+
+  set pub(pub) {
+    this.pubkey = pub
+  }
+
+  get block() {
+    return this.blockstamp
+  }
+
+  get statics() {
+    return {
+      peerize: (json) => Peer.fromJSON(json),
+      fromJSON: (json) => Peer.fromJSON(json)
+    }
+  }
+
+  /**
+   * Methods
+   */
+
+  getDns() {
+    const bma = this.getBMA();
+    return bma.dns ? bma.dns : null;
+  }
+
+  getIPv4() {
+    const bma = this.getBMA();
+    return bma.ipv4 ? bma.ipv4 : null;
+  }
+
+  getIPv6() {
+    const bma = this.getBMA();
+    return bma.ipv6 ? bma.ipv6 : null;
+  }
+
+  getPort() {
+    const bma = this.getBMA();
+    return bma.port ? bma.port : null;
+  }
+
+  getBMA() {
+    let bma = null;
+    this.endpoints.forEach((ep) => {
+      const matches = !bma && ep.match(regex.BMA_REGEXP);
+      if (matches) {
+        bma = {
+          "dns": matches[2] || '',
+          "ipv4": matches[4] || '',
+          "ipv6": matches[6] || '',
+          "port": matches[8] || 9101
+        };
+      }
+    });
+    return bma || {};
+  }
+
+  getURL() {
+    const bma = this.getBMA();
+    let base = this.getHostPreferDNS();
+    if(bma.port)
+      base += ':' + bma.port;
+    return base;
+  }
+
+  getHostPreferDNS() {
+    const bma = this.getBMA();
+    return (bma.dns ? bma.dns :
+      (bma.ipv4 ? bma.ipv4 :
+        (bma.ipv6 ? bma.ipv6 : '')));
+  }
+
+  getRaw() {
+    return Peer.toRAW(this, SIGNED)
+  }
+
+  getRawUnsigned() {
+    return Peer.toRAW(this, UNSIGNED)
+  }
+
+  endpointSum() {
+    return this.endpoints.join('_')
+  }
+
+  blockNumber() {
+    return this.blockstamp.match(/^(\d+)-/)[1]
+  }
+
+  static fromJSON(json) {
+    // Returns a new Peer only if `json` is defined and not null
+    if (!json) return null
+    return new Peer(
+      json.version || constants.DOCUMENTS_VERSION,
+      json.currency,
+      json.pubkey || json.pub || "",
+      json.endpoints,
+      json.blockstamp || json.block,
+      json.signature
+    )
+  }
+
+  static toRAW(json, unsigned) {
+    const p = Peer.fromJSON(json)
+    let raw = ""
+    raw += "Version: " + p.version + "\n"
+    raw += "Type: Peer\n"
+    raw += "Currency: " + p.currency + "\n"
+    raw += "PublicKey: " + p.pubkey + "\n"
+    raw += "Block: " + p.blockstamp + "\n"
+    raw += "Endpoints:" + "\n"
+    for(const ep of p.endpoints) {
+      raw += ep + "\n"
+    }
+    if (!unsigned) {
+      raw += json.signature + '\n'
+    }
+    return raw
+  }
+
+  static endpoint2host(endpoint) {
+    return Peer.fromJSON({ endpoints: [endpoint] }).getURL()
+  }
+
+  static endpointSum(json) {
+    return Peer.fromJSON(json).endpointSum()
+  }
+
+  static blockNumber(json) {
+    const p = Peer.fromJSON(json)
+    return p ? p.blockNumber() : -1
+  }
+
+  keyID () {
+    return this.pubkey && this.pubkey.length > 10 ? this.pubkey.substring(0, 10) : "Unknown"
+  }
+
+  copyValues(to) {
+    ["version", "currency", "pub", "endpoints", "hash", "status", "statusTS", "block", "signature"].forEach((key)=> {
+      to[key] = this[key];
+    });
+  }
+
+  // this.copyValuesFrom = (from) => {
+  //   ["version", "currency", "pub", "endpoints", "block", "signature"].forEach((key) => {
+  //     this[key] = from[key];
+  //   });
+  // };
+  //
+  // this.json = () => {
+  //   const json = {};
+  //   ["version", "currency", "endpoints", "status", "block", "signature"].forEach((key) => {
+  //     json[key] = this[key];
+  //   });
+  //   json.raw = this.getRaw();
+  //   json.pubkey = this.pubkey;
+  //   return json;
+  // };
+
+  // this.hasValid4 = (bma) => !!(bma.ipv4 && !bma.ipv4.match(/^127.0/) && !bma.ipv4.match(/^192.168/));
+  //
+  // this.getNamedURL = () => this.getURL();
+  //
+  // this.getRaw = () => rawer.getPeerWithoutSignature(this);
+  //
+  // this.getRawSigned = () => rawer.getPeer(this);
+  //
+  // this.isReachable = () => {
+  //   return !!this.getURL();
+  // };
+  //
+  // this.containsEndpoint = (ep) => this.endpoints.reduce((found, endpoint) => found || endpoint === ep, false);
+  //
+  //
+}
diff --git a/app/common/lib/document/transaction.js b/app/common/lib/document/transaction.js
new file mode 100644
index 000000000..aad1e08c7
--- /dev/null
+++ b/app/common/lib/document/transaction.js
@@ -0,0 +1,189 @@
+"use strict";
+const constants = require('../constants');
+const regex = require('../regex');
+const hashf = require('../hashf');
+
+// Constants
+const SIGNED = false
+const UNSIGNED = !SIGNED
+
+module.exports = class Transaction {
+
+  constructor(
+    version,
+    currency,
+    issuers,
+    signatures,
+    inputs,
+    unlocks,
+    outputs,
+    comment,
+    blockstamp,
+    blockstampTime,
+    locktime,
+    hash
+  ) {
+    this.version = version
+    this.currency = currency
+    this.issuers = (issuers || [])
+    this.signatures = (signatures || [])
+    this.inputsRAW  = (inputs  || []).map(i => typeof i === 'string' ? i : Transaction.inputObj2Str(i))
+    this.outputsRAW = (outputs || []).map(o => typeof o === 'string' ? o : Transaction.outputObj2Str(o))
+    this.inputs = this.inputsRAW.map(i => Transaction.inputStr2Obj(i))
+    this.outputs = this.outputsRAW.map(o => Transaction.outputStr2Obj(o))
+    this.unlocks = (unlocks || [])
+    this.output_amount = this.outputs.reduce((sum, output) => sum + output.amount * Math.pow(10, output.base), 0)
+    this.output_base = this.outputs.reduce((maxBase, output) => Math.max(maxBase, parseInt(output.base)), 0)
+    this.comment = comment
+    this.blockstamp = blockstamp
+    this.blockstampTime = blockstampTime
+    this.locktime = locktime || 0
+    this.hash = hash
+  }
+
+  json() {
+    return {
+      'version': parseInt(this.version, 10),
+      'currency': this.currency,
+      'issuers': this.issuers.slice(),
+      'inputs': this.inputsRAW.slice(),
+      'unlocks': this.unlocks.slice(),
+      'outputs': this.outputsRAW.slice(),
+      'comment': this.comment,
+      'locktime': this.locktime,
+      'blockstamp': this.blockstamp,
+      'blockstampTime': this.blockstampTime,
+      'signatures': this.signatures.slice(),
+      'raw': Transaction.toRAW(this),
+      'hash': this.hash
+    }
+  }
+
+  compact() {
+    return Transaction.getCompactTransaction(this)
+  }
+
+  getHash(json) {
+    const raw = Transaction.toRAW(this)
+    return hashf(raw).toUpperCase()
+  }
+
+  /**
+   * Aliases
+   */
+
+  /**
+   * Methods
+   */
+
+  static getLen(tx) {
+    return 2 // header + blockstamp
+    + tx.issuers.length * 2 // issuers + signatures
+    + tx.inputs.length * 2 // inputs + unlocks
+    + (tx.comment ? 1 : 0)
+    + tx.outputs.length
+  }
+
+  static fromJSON(json) {
+    return new Transaction(
+      json.version || constants.DOCUMENTS_VERSION,
+      json.currency,
+      json.issuers,
+      json.signatures,
+      json.inputsRAW || json.inputs,
+      json.unlocks,
+      json.outputsRAW || json.outputs,
+      json.comment,
+      json.blockstamp,
+      json.blockstampTime,
+      json.locktime,
+      json.hash
+    )
+  }
+
+  static toRAW(json) {
+    const tx = Transaction.fromJSON(json)
+    let raw = ""
+    raw += "Version: " + (tx.version) + "\n"
+    raw += "Type: Transaction\n"
+    raw += "Currency: " + tx.currency + "\n"
+    raw += "Blockstamp: " + tx.blockstamp + "\n"
+    raw += "Locktime: " + tx.locktime + "\n"
+    raw += "Issuers:\n";
+    (tx.issuers || []).forEach((issuer) => {
+      raw += issuer + '\n'
+    })
+    raw += "Inputs:\n";
+    (tx.inputsRAW || []).forEach((input) => {
+      raw += input + '\n'
+    })
+    raw += "Unlocks:\n";
+    (tx.unlocks || []).forEach((unlock) => {
+      raw += unlock + '\n'
+    })
+    raw += "Outputs:\n";
+    (tx.outputsRAW  || []).forEach((output) => {
+      raw += output + '\n'
+    })
+    raw += "Comment: " + (tx.comment || "") + "\n";
+    (tx.signatures || []).forEach((signature) => {
+      raw += signature + '\n'
+    })
+    return raw
+  }
+
+  static inputObj2Str(inputObj) {
+    return [inputObj.amount, inputObj.base, inputObj.type, inputObj.identifier, inputObj.pos].join(':')
+  }
+
+  static outputObj2Str(oupoutObj) {
+    return [oupoutObj.amount, oupoutObj.base, oupoutObj.conditions].join(':')
+  }
+
+  static inputStr2Obj(inputStr) {
+    const sp = inputStr.split(':')
+    return {
+      amount:     sp[0],
+      base:       sp[1],
+      type:       sp[2],
+      identifier: sp[3],
+      pos:        parseInt(sp[4]),
+      raw:        inputStr
+    }
+  }
+
+  static outputStr2Obj(outputStr) {
+    const sp = outputStr.split(':')
+    return {
+      amount: parseInt(sp[0]),
+      base: parseInt(sp[1]),
+      conditions: sp[2],
+      raw: outputStr
+    }
+  }
+
+  static getCompactTransaction(json) {
+    const tx = Transaction.fromJSON(json)
+    let issuers = tx.issuers;
+    let raw = ["TX", tx.version, issuers.length, tx.inputs.length, tx.unlocks.length, tx.outputs.length, tx.comment ? 1 : 0, tx.locktime || 0].join(':') + '\n';
+    raw += tx.blockstamp + "\n";
+    (issuers || []).forEach((issuer) => {
+      raw += issuer + '\n';
+    });
+    (tx.inputsRAW || []).forEach((input) => {
+      raw += input + '\n';
+    });
+    (tx.unlocks || []).forEach((input) => {
+      raw += input + '\n';
+    });
+    (tx.outputsRAW || []).forEach((output) => {
+      raw += output + '\n';
+    });
+    if (tx.comment)
+      raw += tx.comment + '\n';
+    (tx.signatures || []).forEach((signature) => {
+      raw += signature + '\n'
+    })
+    return raw
+  }
+}
diff --git a/app/common/lib/dos2unix.js b/app/common/lib/dos2unix.js
new file mode 100644
index 000000000..32fdce593
--- /dev/null
+++ b/app/common/lib/dos2unix.js
@@ -0,0 +1,23 @@
+"use strict";
+const util     = require('util');
+const stream   = require('stream');
+
+module.exports = function (str) {
+  if (str)
+    return dos2unix(str);
+  else
+    return new Dos2UnixStream();
+};
+
+const dos2unix = (str) => str.replace(/\r\n/g, '\n');
+
+function Dos2UnixStream () {
+  stream.Transform.apply(this);
+
+  this._write = function (str, enc, done) {
+    this.push(dos2unix(str.toString()));
+    done();
+  }
+}
+
+util.inherits(Dos2UnixStream, stream.Transform);
diff --git a/app/common/lib/hashf.js b/app/common/lib/hashf.js
new file mode 100644
index 000000000..f1a1bd2ee
--- /dev/null
+++ b/app/common/lib/hashf.js
@@ -0,0 +1,8 @@
+"use strict";
+
+module.exports = function (str){
+  return require("crypto")
+    .createHash("sha256")
+    .update(str)
+    .digest("hex");
+};
diff --git a/app/common/lib/parsers/GenericParser.js b/app/common/lib/parsers/GenericParser.js
new file mode 100644
index 000000000..a1424acee
--- /dev/null
+++ b/app/common/lib/parsers/GenericParser.js
@@ -0,0 +1,108 @@
+"use strict";
+const util                 = require('util');
+const stream               = require('stream');
+const hashf                = require('../hashf');
+const constants            = require('../constants');
+
+module.exports = GenericParser;
+
+function GenericParser (captures, multipleLinesFields, rawerFunc) {
+
+  stream.Transform.call(this, { decodeStrings: false, objectMode: true });
+
+  this.rawerFunc = rawerFunc;
+
+  this._simpleLineExtraction = (pr, rawEntry, cap) => {
+    const fieldValue = rawEntry.match(cap.regexp);
+    if(fieldValue && fieldValue.length >= 2){
+      pr[cap.prop] = cap.parser ? cap.parser(fieldValue[1], pr) : fieldValue[1];
+    }
+    return;
+  };
+
+  this._multipleLinesExtraction = (am, wholeAmend, cap) => {
+    const fieldValue = wholeAmend.match(cap.regexp);
+    let line = 0;
+    am[cap.prop] = [];
+    if(fieldValue && fieldValue.length >= 2)
+    {
+      const lines = fieldValue[1].split(/\n/);
+      if(lines[lines.length - 1].match(/^$/)){
+        for (let i = 0; i < lines.length - 1; i++) {
+          line = lines[i];
+          let fprChange = line.match(/([+-][A-Z\d]{40})/);
+          if(fprChange && fprChange.length === 2){
+            am[cap.prop].push(fprChange[1]);
+          }
+          else{
+            return "Wrong structure for line: '" + line + "'";
+          }
+        }
+      }
+      else return "Wrong structure for line: '" + line + "'";
+    }
+  };
+
+  this.syncWrite = (str, logger) => {
+    let error;
+    const obj = {};
+    this._parse(str, obj);
+    this._clean(obj);
+    if (!error) {
+      error = this._verify(obj);
+    }
+    if (!error) {
+      const raw = this.rawerFunc(obj);
+      if (hashf(str) !== hashf(raw))
+        error = constants.ERRORS.WRONG_DOCUMENT;
+      if (error) {
+        logger && logger.trace(error);
+        logger && logger.trace('-----------------');
+        logger && logger.trace('Written: %s', JSON.stringify({ str: str }));
+        logger && logger.trace('Extract: %s', JSON.stringify({ raw: raw }));
+        logger && logger.trace('-----------------');
+      }
+    }
+    if (error){
+      logger && logger.trace(error);
+      throw constants.ERRORS.WRONG_DOCUMENT;
+    }
+    return obj;
+  };
+
+  this._parse = (str, obj) => {
+    let error;
+    if(!str){
+      error = "No document given";
+    } else {
+      error = "";
+      obj.hash = hashf(str).toUpperCase();
+      // Divide in 2 parts: document & signature
+      const sp = str.split('\n');
+      if (sp.length < 3) {
+        error = "Wrong document: must have at least 2 lines";
+      }
+      else {
+        const endOffset = str.match(/\n$/) ? 2 : 1;
+        obj.signature = sp[sp.length - endOffset];
+        obj.hash = hashf(str).toUpperCase();
+        obj.raw = sp.slice(0, sp.length - endOffset).join('\n') + '\n';
+        const docLF = obj.raw.replace(/\r\n/g, "\n");
+        if(docLF.match(/\n$/)){
+          captures.forEach((cap) => {
+            if(~multipleLinesFields.indexOf(multipleLinesFields))
+              error = this._multipleLinesExtraction(obj, docLF, cap);
+            else
+              this._simpleLineExtraction(obj, docLF, cap);
+          });
+        }
+        else{
+          error = "Bad document structure: no new line character at the end of the document.";
+        }
+      }
+    }
+    return error;
+  };
+}
+
+util.inherits(GenericParser, stream.Transform);
diff --git a/app/common/lib/parsers/block.js b/app/common/lib/parsers/block.js
new file mode 100644
index 000000000..31118f50f
--- /dev/null
+++ b/app/common/lib/parsers/block.js
@@ -0,0 +1,249 @@
+"use strict";
+const util          = require('util');
+const GenericParser = require('./GenericParser');
+const Block         = require('../../../../app/common/lib/document/block');
+const hashf         = require('../../../../app/common/lib/hashf');
+const rawer         = require('../../../../app/common/lib/rawer');
+const constants     = require('../../../../app/common/lib/constants');
+
+module.exports = BlockParser;
+
+function BlockParser (onError) {
+
+  const captures = [
+    {prop: "version",         regexp: constants.BLOCK.VERSION},
+    {prop: "type",            regexp: constants.BLOCK.TYPE},
+    {prop: "currency",        regexp: constants.BLOCK.CURRENCY},
+    {prop: "number",          regexp: constants.BLOCK.BNUMBER},
+    {prop: "powMin",          regexp: constants.BLOCK.POWMIN},
+    {prop: "time",            regexp: constants.BLOCK.TIME},
+    {prop: "medianTime",      regexp: constants.BLOCK.MEDIAN_TIME},
+    {prop: "dividend",        regexp: constants.BLOCK.UD},
+    {prop: "unitbase",        regexp: constants.BLOCK.UNIT_BASE},
+    {prop: "issuer",          regexp: constants.BLOCK.BLOCK_ISSUER},
+    {prop: "issuersFrame",    regexp: constants.BLOCK.BLOCK_ISSUERS_FRAME},
+    {prop: "issuersFrameVar", regexp: constants.BLOCK.BLOCK_ISSUERS_FRAME_VAR},
+    {prop: "issuersCount",    regexp: constants.BLOCK.DIFFERENT_ISSUERS_COUNT},
+    {prop: "parameters",      regexp: constants.BLOCK.PARAMETERS},
+    {prop: "previousHash",    regexp: constants.BLOCK.PREV_HASH},
+    {prop: "previousIssuer",  regexp: constants.BLOCK.PREV_ISSUER},
+    {prop: "membersCount",    regexp: constants.BLOCK.MEMBERS_COUNT},
+    {prop: "identities",      regexp: /Identities:\n([\s\S]*)Joiners/,          parser: splitAndMatch('\n', constants.IDENTITY.INLINE)},
+    {prop: "joiners",         regexp: /Joiners:\n([\s\S]*)Actives/,             parser: splitAndMatch('\n', constants.BLOCK.JOINER)},
+    {prop: "actives",         regexp: /Actives:\n([\s\S]*)Leavers/,             parser: splitAndMatch('\n', constants.BLOCK.ACTIVE)},
+    {prop: "leavers",         regexp: /Leavers:\n([\s\S]*)Excluded/,            parser: splitAndMatch('\n', constants.BLOCK.LEAVER)},
+    {prop: "revoked",         regexp: /Revoked:\n([\s\S]*)Excluded/,            parser: splitAndMatch('\n', constants.BLOCK.REVOCATION)},
+    {prop: "excluded",        regexp: /Excluded:\n([\s\S]*)Certifications/,     parser: splitAndMatch('\n', constants.PUBLIC_KEY)},
+    {prop: "certifications",  regexp: /Certifications:\n([\s\S]*)Transactions/, parser: splitAndMatch('\n', constants.CERT.OTHER.INLINE)},
+    {prop: "transactions",    regexp: /Transactions:\n([\s\S]*)/,               parser: extractTransactions},
+    {prop: "inner_hash",      regexp: constants.BLOCK.INNER_HASH},
+    {prop: "nonce",           regexp: constants.BLOCK.NONCE}
+  ];
+  const multilineFields = [];
+  GenericParser.call(this, captures, multilineFields, rawer.getBlock, onError);
+
+  this._clean = (obj) => {
+    obj.documentType = 'block';
+    obj.identities = obj.identities || [];
+    obj.joiners = obj.joiners || [];
+    obj.actives = obj.actives || [];
+    obj.leavers = obj.leavers || [];
+    obj.revoked = obj.revoked || [];
+    obj.excluded = obj.excluded || [];
+    obj.certifications = obj.certifications || [];
+    obj.transactions = obj.transactions || [];
+    obj.version = obj.version || '';
+    obj.type = obj.type || '';
+    obj.hash = hashf(rawer.getBlockInnerHashAndNonceWithSignature(obj)).toUpperCase();
+    obj.inner_hash = obj.inner_hash || '';
+    obj.currency = obj.currency || '';
+    obj.nonce = obj.nonce || '';
+    obj.number = obj.number || '';
+    obj.time = obj.time || '';
+    obj.medianTime = obj.medianTime || '';
+    obj.dividend = obj.dividend || null;
+    obj.unitbase = obj.unitbase || '';
+    obj.issuer = obj.issuer || '';
+    obj.parameters = obj.parameters || '';
+    obj.previousHash = obj.previousHash || '';
+    obj.previousIssuer = obj.previousIssuer || '';
+    obj.membersCount = obj.membersCount || '';
+    obj.transactions.map((tx) => {
+      tx.currency = obj.currency;
+      tx.hash = hashf(rawer.getTransaction(tx)).toUpperCase();
+    });
+    obj.len = Block.getLen(obj);
+  };
+
+  this._verify = (obj) => {
+    let err = null;
+    const codes = {
+      'BAD_VERSION': 150,
+      'BAD_CURRENCY': 151,
+      'BAD_NUMBER': 152,
+      'BAD_TYPE': 153,
+      'BAD_NONCE': 154,
+      'BAD_RECIPIENT_OF_NONTRANSFERT': 155,
+      'BAD_PREV_HASH_PRESENT': 156,
+      'BAD_PREV_HASH_ABSENT': 157,
+      'BAD_PREV_ISSUER_PRESENT': 158,
+      'BAD_PREV_ISSUER_ABSENT': 159,
+      'BAD_DIVIDEND': 160,
+      'BAD_TIME': 161,
+      'BAD_MEDIAN_TIME': 162,
+      'BAD_INNER_HASH': 163,
+      'BAD_MEMBERS_COUNT': 164,
+      'BAD_UNITBASE': 165
+    };
+    if(!err){
+      // Version
+      if(!obj.version || !obj.version.match(constants.DOCUMENTS_BLOCK_VERSION_REGEXP))
+        err = {code: codes.BAD_VERSION, message: "Version unknown"};
+    }
+    if(!err){
+      // Type
+      if(!obj.type || !obj.type.match(/^Block$/))
+        err = {code: codes.BAD_TYPE, message: "Not a Block type"};
+    }
+    if(!err){
+      // Nonce
+      if(!obj.nonce || !obj.nonce.match(constants.INTEGER))
+        err = {code: codes.BAD_NONCE, message: "Nonce must be an integer value"};
+    }
+    if(!err){
+      // Number
+      if(!obj.number || !obj.number.match(constants.INTEGER))
+        err = {code: codes.BAD_NUMBER, message: "Incorrect Number field"};
+    }
+    if(!err){
+      // Time
+      if(!obj.time || !obj.time.match(constants.INTEGER))
+        err = {code: codes.BAD_TIME, message: "Time must be an integer"};
+    }
+    if(!err){
+      // MedianTime
+      if(!obj.medianTime || !obj.medianTime.match(constants.INTEGER))
+        err = {code: codes.BAD_MEDIAN_TIME, message: "MedianTime must be an integer"};
+    }
+    if(!err){
+      if(obj.dividend && !obj.dividend.match(constants.INTEGER))
+        err = {code: codes.BAD_DIVIDEND, message: "Incorrect UniversalDividend field"};
+    }
+    if(!err){
+      if(obj.unitbase && !obj.unitbase.match(constants.INTEGER))
+        err = {code: codes.BAD_UNITBASE, message: "Incorrect UnitBase field"};
+    }
+    if(!err){
+      if(!obj.issuer || !obj.issuer.match(constants.BASE58))
+        err = {code: codes.BAD_ISSUER, message: "Incorrect Issuer field"};
+    }
+    if(!err){
+      // MembersCount
+      if(!obj.nonce || !obj.nonce.match(constants.INTEGER))
+        err = {code: codes.BAD_MEMBERS_COUNT, message: "MembersCount must be an integer value"};
+    }
+    if(!err){
+      // InnerHash
+      if(!obj.inner_hash || !obj.inner_hash.match(constants.FINGERPRINT))
+        err = {code: codes.BAD_INNER_HASH, message: "InnerHash must be a hash value"};
+    }
+    return err && err.message;
+  };
+}
+
+function splitAndMatch (separator, regexp) {
+  return function (raw) {
+    const lines = raw.split(new RegExp(separator));
+    const kept = [];
+    lines.forEach(function(line){
+      if (line.match(regexp))
+        kept.push(line);
+    });
+    return kept;
+  };
+}
+
+function extractTransactions(raw) {
+  const regexps = {
+    "issuers": constants.TRANSACTION.SENDER,
+    "inputs": constants.TRANSACTION.SOURCE_V3,
+    "unlocks": constants.TRANSACTION.UNLOCK,
+    "outputs": constants.TRANSACTION.TARGET,
+    "comments": constants.TRANSACTION.INLINE_COMMENT,
+    "signatures": constants.SIG
+  };
+  const transactions = [];
+  const lines = raw.split(/\n/);
+  for (let i = 0; i < lines.length; i++) {
+    const line = lines[i];
+    // On each header
+    if (line.match(constants.TRANSACTION.HEADER)) {
+      // Parse the transaction
+      const currentTX = { raw: line + '\n' };
+      const sp = line.split(':');
+      const version = parseInt(sp[1]);
+      const nbIssuers = parseInt(sp[2]);
+      const nbInputs = parseInt(sp[3]);
+      const nbUnlocks = parseInt(sp[4]);
+      const nbOutputs = parseInt(sp[5]);
+      const hasComment = parseInt(sp[6]);
+      const start = 2;
+      currentTX.version = version;
+      currentTX.blockstamp = lines[i + 1];
+      currentTX.raw += currentTX.blockstamp + '\n';
+      currentTX.locktime = parseInt(sp[7]);
+      const linesToExtract = {
+        issuers: {
+          start: start,
+          end: (start - 1) + nbIssuers
+        },
+        inputs: {
+          start: start + nbIssuers,
+          end: (start - 1) + nbIssuers + nbInputs
+        },
+        unlocks: {
+          start: start + nbIssuers + nbInputs,
+          end: (start - 1) + nbIssuers + nbInputs + nbUnlocks
+        },
+        outputs: {
+          start: start + nbIssuers + nbInputs + nbUnlocks,
+          end: (start - 1) + nbIssuers + nbInputs + nbUnlocks + nbOutputs
+        },
+        comments: {
+          start: start + nbIssuers + nbInputs + nbUnlocks + nbOutputs,
+          end: (start - 1) + nbIssuers + nbInputs + nbUnlocks + nbOutputs + hasComment
+        },
+        signatures: {
+          start: start + nbIssuers + nbInputs + nbUnlocks + nbOutputs + hasComment,
+          end: (start - 1) + 2 * nbIssuers + nbInputs + nbUnlocks + nbOutputs + hasComment
+        }
+      };
+      ['issuers', 'inputs', 'unlocks', 'outputs', 'comments', 'signatures'].forEach((prop) => {
+        currentTX[prop] = currentTX[prop] || [];
+        for (let j = linesToExtract[prop].start; j <= linesToExtract[prop].end; j++) {
+          const line = lines[i + j];
+          if (line.match(regexps[prop])) {
+            currentTX.raw += line + '\n';
+            currentTX[prop].push(line);
+          }
+        }
+      });
+      // Comment
+      if (hasComment) {
+        currentTX.comment = currentTX.comments[0];
+      } else {
+        currentTX.comment = '';
+      }
+      currentTX.hash = hashf(rawer.getTransaction(currentTX)).toUpperCase();
+      // Add to txs array
+      transactions.push(currentTX);
+      i = i + 1 + 2 * nbIssuers + nbInputs + nbUnlocks + nbOutputs + hasComment;
+    } else {
+      // Not a transaction header, stop reading
+      i = lines.length;
+    }
+  }
+  return transactions;
+}
+
+util.inherits(BlockParser, GenericParser);
diff --git a/app/common/lib/parsers/certification.js b/app/common/lib/parsers/certification.js
new file mode 100644
index 000000000..11d0321f5
--- /dev/null
+++ b/app/common/lib/parsers/certification.js
@@ -0,0 +1,43 @@
+"use strict";
+const GenericParser = require('./GenericParser');
+const util          = require('util');
+const rawer         = require('../../../../app/common/lib/rawer');
+const constants     = require('../../../../app/common/lib/constants');
+
+module.exports = CertificationParser;
+
+function CertificationParser (onError) {
+
+  const captures = [
+    {prop: "version",           regexp: constants.DOCUMENTS.DOC_VERSION },
+    {prop: "type",              regexp: constants.CERTIFICATION.CERT_TYPE },
+    {prop: "currency",          regexp: constants.DOCUMENTS.DOC_CURRENCY },
+    {prop: "issuer",            regexp: constants.DOCUMENTS.DOC_ISSUER },
+    {prop: "idty_issuer",       regexp: constants.CERTIFICATION.IDTY_ISSUER },
+    {prop: "idty_sig",          regexp: constants.CERTIFICATION.IDTY_SIG },
+    {prop: "idty_buid",         regexp: constants.CERTIFICATION.IDTY_TIMESTAMP},
+    {prop: "idty_uid",          regexp: constants.CERTIFICATION.IDTY_UID },
+    {prop: "buid",              regexp: constants.CERTIFICATION.CERT_TIMESTAMP }
+  ];
+  const multilineFields = [];
+  GenericParser.call(this, captures, multilineFields, rawer.getOfficialCertification, onError);
+
+  this._clean = (obj) => {
+    obj.documentType = 'certification';
+    obj.sig = obj.signature;
+    obj.block = obj.buid;
+    if (obj.block) {
+      obj.number = obj.block.split('-')[0];
+      obj.fpr = obj.block.split('-')[1];
+    } else {
+      obj.number = '0';
+      obj.fpr = '';
+    }
+  };
+
+  this._verify = (obj) => ["version", "type", "currency", "issuer", "idty_issuer", "idty_sig", "idty_buid", "idty_uid", "block"].reduce(function (p, field) {
+    return p || (!obj[field] && "Wrong format for certification");
+  }, null);
+}
+
+util.inherits(CertificationParser, GenericParser);
diff --git a/app/common/lib/parsers/identity.js b/app/common/lib/parsers/identity.js
new file mode 100644
index 000000000..4f0c41ff6
--- /dev/null
+++ b/app/common/lib/parsers/identity.js
@@ -0,0 +1,47 @@
+"use strict";
+const GenericParser = require('./GenericParser');
+const util          = require('util');
+const rawer         = require('../../../../app/common/lib/rawer');
+const hashf         = require('../../../../app/common/lib/hashf');
+const constants     = require('../../../../app/common/lib/constants');
+
+module.exports = IdentityParser;
+
+function IdentityParser (onError) {
+
+  const captures = [
+    {prop: "version",           regexp: constants.DOCUMENTS.DOC_VERSION },
+    {prop: "type",              regexp: constants.IDENTITY.IDTY_TYPE},
+    {prop: "currency",          regexp: constants.DOCUMENTS.DOC_CURRENCY },
+    {prop: "pubkey",            regexp: constants.DOCUMENTS.DOC_ISSUER },
+    {prop: "uid",               regexp: constants.IDENTITY.IDTY_UID },
+    {prop: "buid",              regexp: constants.DOCUMENTS.TIMESTAMP }
+  ];
+  const multilineFields = [];
+  GenericParser.call(this, captures, multilineFields, rawer.getOfficialIdentity, onError);
+
+  this._clean = (obj) => {
+    obj.documentType = 'identity';
+    obj.sig = obj.signature;
+    if (obj.uid && obj.buid && obj.pubkey) {
+      obj.hash = hashf(obj.uid + obj.buid + obj.pubkey).toUpperCase();
+    }
+  };
+
+  this._verify = (obj) => {
+    if (!obj.pubkey) {
+      return "No pubkey found";
+    }
+    if (!obj.uid) {
+      return "Wrong user id format";
+    }
+    if (!obj.buid) {
+      return "Could not extract block uid";
+    }
+    if (!obj.sig) {
+      return "No signature found for self-certification";
+    }
+  };
+}
+
+util.inherits(IdentityParser, GenericParser);
diff --git a/app/common/lib/parsers/index.js b/app/common/lib/parsers/index.js
new file mode 100644
index 000000000..7eec548c6
--- /dev/null
+++ b/app/common/lib/parsers/index.js
@@ -0,0 +1,11 @@
+"use strict";
+
+module.exports = {
+  parseIdentity:    (new (require('./identity'))),
+  parseCertification:(new (require('./certification'))),
+  parseRevocation:  (new (require('./revocation'))),
+  parseTransaction: (new (require('./transaction'))),
+  parsePeer:        (new (require('./peer'))),
+  parseMembership:  (new (require('./membership'))),
+  parseBlock:       (new (require('./block')))
+};
diff --git a/app/common/lib/parsers/membership.js b/app/common/lib/parsers/membership.js
new file mode 100644
index 000000000..2435ae526
--- /dev/null
+++ b/app/common/lib/parsers/membership.js
@@ -0,0 +1,75 @@
+"use strict";
+const GenericParser = require('./GenericParser');
+const ucp           = require('../buid');
+const rawer         = require('../rawer');
+const util          = require('util');
+const constants     = require('../constants');
+
+module.exports = MembershipParser;
+
+function MembershipParser (onError) {
+
+  const captures = [
+    {prop: "version",           regexp: constants.MEMBERSHIP.VERSION },
+    {prop: "currency",          regexp: constants.MEMBERSHIP.CURRENCY },
+    {prop: "issuer",            regexp: constants.MEMBERSHIP.ISSUER },
+    {prop: "membership",        regexp: constants.MEMBERSHIP.MEMBERSHIP },
+    {prop: "userid",            regexp: constants.MEMBERSHIP.USERID },
+    {prop: "block",             regexp: constants.MEMBERSHIP.BLOCK},
+    {prop: "certts",            regexp: constants.MEMBERSHIP.CERTTS}
+  ];
+  const multilineFields = [];
+  GenericParser.call(this, captures, multilineFields, rawer.getMembership, onError);
+
+  this._clean = (obj) => {
+    obj.documentType = 'membership';
+    if (obj.block) {
+      obj.number = obj.block.split('-')[0];
+      obj.fpr = obj.block.split('-')[1];
+    } else {
+      obj.number = '0';
+      obj.fpr = '';
+    }
+  };
+
+  this._verify = (obj) => {
+    let err = null;
+    const codes = {
+      'BAD_VERSION': 150,
+      'BAD_CURRENCY': 151,
+      'BAD_ISSUER': 152,
+      'BAD_MEMBERSHIP': 153,
+      'BAD_REGISTRY_TYPE': 154,
+      'BAD_BLOCK': 155,
+      'BAD_USERID': 156,
+      'BAD_CERTTS': 157
+    };
+    if(!err){
+      if(!obj.version || !obj.version.match(constants.DOCUMENTS_VERSION_REGEXP))
+        err = {code: codes.BAD_VERSION, message: "Version unknown"};
+    }
+    if(!err){
+      if(obj.issuer && !obj.issuer.match(constants.BASE58))
+        err = {code: codes.BAD_ISSUER, message: "Incorrect issuer field"};
+    }
+    if(!err){
+      if(!(obj.membership || "").match(/^(IN|OUT)$/))
+        err = {code: codes.BAD_MEMBERSHIP, message: "Incorrect Membership field: must be either IN or OUT"};
+    }
+    if(!err){
+      if(obj.block && !obj.block.match(constants.BLOCK_UID))
+        err = {code: codes.BAD_BLOCK, message: "Incorrect Block field: must be a positive or zero integer, a dash and an uppercased SHA1 hash"};
+    }
+    if(!err){
+      if(obj.userid && !obj.userid.match(constants.USER_ID))
+        err = {code: codes.BAD_USERID, message: "UserID must match udid2 format"};
+    }
+    if(!err){
+      if(!ucp.format.isBuid(obj.certts))
+        err = {code: codes.BAD_CERTTS, message: "CertTS must be a valid timestamp"};
+    }
+    return err && err.message;
+  };
+}
+
+util.inherits(MembershipParser, GenericParser);
diff --git a/app/common/lib/parsers/peer.js b/app/common/lib/parsers/peer.js
new file mode 100644
index 000000000..122f6ee34
--- /dev/null
+++ b/app/common/lib/parsers/peer.js
@@ -0,0 +1,105 @@
+"use strict";
+const GenericParser = require('./GenericParser');
+const rawer         = require('../rawer');
+const util          = require('util');
+const constants     = require('../constants');
+
+module.exports = PeerParser;
+
+function PeerParser (onError) {
+
+  const captures = [
+    {prop: "version",           regexp: /Version: (.*)/},
+    {prop: "currency",          regexp: /Currency: (.*)/},
+    {prop: "pubkey",            regexp: /PublicKey: (.*)/},
+    {prop: "block",             regexp: constants.PEER.BLOCK},
+    {
+      prop: "endpoints", regexp: /Endpoints:\n([\s\S]*)/, parser: (str) => str.split("\n")
+    }
+  ];
+  const multilineFields = [];
+  GenericParser.call(this, captures, multilineFields, rawer.getPeer, onError);
+
+  this._clean = (obj) => {
+    obj.documentType = 'peer';
+    obj.endpoints = obj.endpoints || [];
+    // Removes trailing space
+    if (obj.endpoints.length > 0)
+      obj.endpoints.splice(obj.endpoints.length - 1, 1);
+    obj.getBMA = function() {
+      let bma = null;
+      obj.endpoints.forEach((ep) => {
+        let matches = !bma && ep.match(constants.BMA_REGEXP);
+        if (matches) {
+          bma = {
+            "dns": matches[2] || '',
+            "ipv4": matches[4] || '',
+            "ipv6": matches[6] || '',
+            "port": matches[8] || 9101
+          };
+        }
+      });
+      return bma || {};
+    };
+  };
+
+  this._verify = (obj) => {
+    let err = null;
+    const codes = {
+      'BAD_VERSION': 150,
+      'BAD_CURRENCY': 151,
+      'BAD_DNS': 152,
+      'BAD_IPV4': 153,
+      'BAD_IPV6': 154,
+      'BAD_PORT': 155,
+      'BAD_FINGERPRINT': 156,
+      'BAD_BLOCK': 157,
+      'NO_IP_GIVEN': 158
+    };
+    if(!err){
+      // Version
+      if(!obj.version || !obj.version.match(constants.DOCUMENTS_VERSION_REGEXP))
+        err = {code: codes.BAD_VERSION, message: "Version unknown"};
+    }
+    if(!err){
+      // PublicKey
+      if(!obj.pubkey || !obj.pubkey.match(constants.BASE58))
+        err = {code: codes.BAD_FINGERPRINT, message: "Incorrect PublicKey field"};
+    }
+    if(!err){
+      // Block
+      if(!obj.block)
+        err = {code: codes.BAD_BLOCK, message: "Incorrect Block field"};
+    }
+    // Basic Merkled API requirements
+    let bma = obj.getBMA();
+    if(!err){
+      // DNS
+      if(bma.dns && !bma.dns.match(/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/))
+        err = {code: codes.BAD_DNS, message: "Incorrect Dns field"};
+    }
+    if(!err){
+      // IPv4
+      if(bma.ipv4 && !bma.ipv4.match(/^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/))
+        err = {code: codes.BAD_IPV4, message: "Incorrect IPv4 field"};
+    }
+    if(!err){
+      // IPv6
+      if(bma.ipv6 && !bma.ipv6.match(/^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((b((25[0-5])|(1d{2})|(2[0-4]d)|(d{1,2}))b).){3}(b((25[0-5])|(1d{2})|(2[0-4]d)|(d{1,2}))b))|(([0-9A-Fa-f]{1,4}:){0,5}:((b((25[0-5])|(1d{2})|(2[0-4]d)|(d{1,2}))b).){3}(b((25[0-5])|(1d{2})|(2[0-4]d)|(d{1,2}))b))|(::([0-9A-Fa-f]{1,4}:){0,5}((b((25[0-5])|(1d{2})|(2[0-4]d)|(d{1,2}))b).){3}(b((25[0-5])|(1d{2})|(2[0-4]d)|(d{1,2}))b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/))
+        err = {code: codes.BAD_IPV6, message: "Incorrect IPv6 field"};
+    }
+    if(!err){
+      // IP
+      if(!bma.dns && !bma.ipv4 && !bma.ipv6)
+        err = {code: codes.NO_IP_GIVEN, message: "It must be given at least DNS or one IP, either v4 or v6"};
+    }
+    if(!err){
+      // Port
+      if(bma.port && !(bma.port + "").match(/^\d+$/))
+        err = {code: codes.BAD_PORT, message: "Port must be provided and match an integer format"};
+    }
+    return err && err.message;
+  };
+}
+
+util.inherits(PeerParser, GenericParser);
diff --git a/app/common/lib/parsers/revocation.js b/app/common/lib/parsers/revocation.js
new file mode 100644
index 000000000..f86528bde
--- /dev/null
+++ b/app/common/lib/parsers/revocation.js
@@ -0,0 +1,52 @@
+"use strict";
+const GenericParser = require('./GenericParser');
+const util          = require('util');
+const rawer         = require('../rawer');
+const hashf         = require('../hashf');
+const constants     = require('../constants');
+
+module.exports = RevocationParser;
+
+function RevocationParser (onError) {
+
+  const captures = [
+    {prop: "version",           regexp: constants.DOCUMENTS.DOC_VERSION },
+    {prop: "type",              regexp: constants.REVOCATION.REVOC_TYPE },
+    {prop: "currency",          regexp: constants.DOCUMENTS.DOC_CURRENCY },
+    {prop: "issuer",            regexp: constants.DOCUMENTS.DOC_ISSUER },
+    {prop: "sig",               regexp: constants.REVOCATION.IDTY_SIG },
+    {prop: "buid",              regexp: constants.REVOCATION.IDTY_TIMESTAMP},
+    {prop: "uid",               regexp: constants.REVOCATION.IDTY_UID }
+  ];
+  const multilineFields = [];
+  GenericParser.call(this, captures, multilineFields, rawer.getOfficialRevocation, onError);
+
+  this._clean = (obj) => {
+    obj.documentType = 'revocation';
+    obj.pubkey = obj.issuer;
+    obj.revocation = obj.signature;
+    if (obj.uid && obj.buid && obj.pubkey) {
+      obj.hash = hashf(obj.uid + obj.buid + obj.pubkey).toUpperCase();
+    }
+  };
+
+  this._verify = (obj) => {
+    if (!obj.pubkey) {
+      return "No pubkey found";
+    }
+    if (!obj.uid) {
+      return "Wrong user id format";
+    }
+    if (!obj.buid) {
+      return "Could not extract block uid";
+    }
+    if (!obj.sig) {
+      return "No signature found for identity";
+    }
+    if (!obj.revocation) {
+      return "No revocation signature found";
+    }
+  };
+}
+
+util.inherits(RevocationParser, GenericParser);
diff --git a/app/common/lib/parsers/transaction.js b/app/common/lib/parsers/transaction.js
new file mode 100644
index 000000000..727607e19
--- /dev/null
+++ b/app/common/lib/parsers/transaction.js
@@ -0,0 +1,128 @@
+"use strict";
+const GenericParser = require('./GenericParser');
+const rawer         = require('../rawer');
+const constants     = require('../constants');
+const util          = require('util');
+
+module.exports = TransactionParser;
+
+function TransactionParser (onError) {
+
+  const captures = [
+    {prop: "version",    regexp: /Version: (.*)/},
+    {prop: "currency",   regexp: /Currency: (.*)/},
+    {prop: "issuers",    regexp: /Issuers:\n([\s\S]*)Inputs/, parser: extractIssuers },
+    {prop: "inputs",     regexp: /Inputs:\n([\s\S]*)Unlocks/, parser: extractInputs },
+    {prop: "unlocks",    regexp: /Unlocks:\n([\s\S]*)Outputs/,parser: extractUnlocks },
+    {prop: "outputs",    regexp: /Outputs:\n([\s\S]*)/,       parser: extractOutputs },
+    {prop: "comment",    regexp: constants.TRANSACTION.COMMENT },
+    {prop: "locktime",   regexp: constants.TRANSACTION.LOCKTIME },
+    {prop: "blockstamp", regexp: constants.TRANSACTION.BLOCKSTAMP },
+    {prop: "signatures", regexp: /Outputs:\n([\s\S]*)/,       parser: extractSignatures }
+  ];
+  const multilineFields = [];
+  GenericParser.call(this, captures, multilineFields, rawer.getTransaction, onError);
+
+  this._clean = (obj) => {
+    obj.documentType = 'transaction';
+    obj.comment = obj.comment || "";
+    obj.locktime = parseInt(obj.locktime) || 0;
+    obj.signatures.push(obj.signature);
+    const compactSize = 2 // Header + blockstamp
+      + obj.issuers.length
+      + obj.inputs.length
+      + obj.unlocks.length
+      + obj.outputs.length
+      + (obj.comment ? 1 : 0)
+      + obj.signatures;
+    if (compactSize > 100) {
+      throw 'A transaction has a maximum size of 100 lines';
+    }
+  };
+
+  this._verify = (obj) => {
+    let err = null;
+    const codes = {
+      'BAD_VERSION': 150,
+      'NO_BLOCKSTAMP': 151
+    };
+    if(!err){
+      // Version
+      if(!obj.version || !obj.version.match(constants.DOCUMENTS_TRANSACTION_VERSION_REGEXP))
+        err = {code: codes.BAD_VERSION, message: "Version unknown"};
+      // Blockstamp
+      if(!obj.blockstamp || !obj.blockstamp.match(constants.BLOCKSTAMP_REGEXP))
+        err = {code: codes.BAD_VERSION, message: "Blockstamp is required"};
+    }
+    return err && err.message;
+  };
+}
+
+function extractIssuers(raw) {
+  const issuers = [];
+  const lines = raw.split(/\n/);
+  for (const line of lines) {
+    if (line.match(constants.TRANSACTION.SENDER)) {
+      issuers.push(line);
+    } else {
+      // Not a pubkey, stop reading
+      break;
+    }
+  }
+  return issuers;
+}
+
+function extractInputs(raw) {
+  const inputs = [];
+  const lines = raw.split(/\n/);
+  for (const line of lines) {
+    if (line.match(constants.TRANSACTION.SOURCE_V3)) {
+      inputs.push(line);
+    } else {
+      // Not a transaction input, stop reading
+      break;
+    }
+  }
+  return inputs;
+}
+
+function extractUnlocks(raw) {
+  const unlocks = [];
+  const lines = raw.split(/\n/);
+  for (const line of lines) {
+    if (line.match(constants.TRANSACTION.UNLOCK)) {
+      unlocks.push(line);
+    } else {
+      // Not a transaction unlock, stop reading
+      break;
+    }
+  }
+  return unlocks;
+}
+
+function extractOutputs(raw) {
+  const outputs = [];
+  const lines = raw.split(/\n/);
+  for (const line of lines) {
+    if (line.match(constants.TRANSACTION.TARGET)) {
+      outputs.push(line);
+    } else {
+      // Not a transaction input, stop reading
+      break;
+    }
+  }
+  return outputs;
+}
+
+function extractSignatures(raw) {
+  const signatures = [];
+  const lines = raw.split(/\n/);
+  for (const line of lines) {
+    if (line.match(constants.SIG)) {
+      signatures.push(line);
+    }
+  }
+  return signatures;
+}
+
+util.inherits(TransactionParser, GenericParser);
diff --git a/app/common/lib/rawer.js b/app/common/lib/rawer.js
new file mode 100644
index 000000000..a99aa2a11
--- /dev/null
+++ b/app/common/lib/rawer.js
@@ -0,0 +1,92 @@
+"use strict";
+const dos2unix = require('./dos2unix');
+const document = require('./document');
+
+const DOCUMENTS_VERSION = 10;
+const SIGNED = false
+const UNSIGNED = true
+
+module.exports = new function() {
+
+  this.getOfficialIdentity = (json, withSig) => {
+    return document.Identity.toRAW(json, withSig !== false) // Defaut with sig
+  };
+
+  this.getOfficialCertification = (json) => {
+    let raw = getNormalHeader('Certification', json);
+    raw += "IdtyIssuer: " + json.idty_issuer + '\n';
+    raw += "IdtyUniqueID: " + json.idty_uid + '\n';
+    raw += "IdtyTimestamp: " + json.idty_buid + '\n';
+    raw += "IdtySignature: " + json.idty_sig + '\n';
+    raw += "CertTimestamp: " + json.buid + '\n';
+    if (json.sig) {
+      raw += json.sig + '\n';
+    }
+    return dos2unix(raw);
+  };
+
+  this.getOfficialRevocation = (json) => {
+    let raw = getNormalHeader('Revocation', json);
+    raw += "IdtyUniqueID: " + json.uid + '\n';
+    raw += "IdtyTimestamp: " + json.buid + '\n';
+    raw += "IdtySignature: " + json.sig + '\n';
+    if (json.revocation) {
+      raw += json.revocation + '\n';
+    }
+    return dos2unix(raw);
+  };
+
+  this.getPeerWithoutSignature = (json) => document.Peer.fromJSON(json).getRawUnsigned()
+
+  this.getPeer = (json) => document.Peer.fromJSON(json).getRaw()
+
+  this.getMembershipWithoutSignature = (json) => {
+    return document.Membership.toRAW(json)
+  };
+
+  this.getMembership = (json) => {
+    return dos2unix(signed(this.getMembershipWithoutSignature(json), json));
+  };
+
+  this.getBlockInnerPart = (json) => {
+    return document.Block.toRAWInnerPart(json)
+  };
+
+  this.getBlockWithInnerHashAndNonce = (json) => {
+    return document.Block.toRAWinnerPartWithHashAndNonce(json)
+  };
+
+  this.getBlockInnerHashAndNonce = (json) => {
+    return document.Block.toRAWHashAndNonce(json, UNSIGNED)
+  };
+
+  this.getBlockInnerHashAndNonceWithSignature = (json) => {
+    return document.Block.toRAWHashAndNonce(json, SIGNED)
+  };
+
+  this.getBlock = (json) => {
+    return dos2unix(signed(this.getBlockWithInnerHashAndNonce(json), json));
+  };
+
+  this.getTransaction = (json) => {
+    return document.Transaction.toRAW(json)
+  };
+
+  this.getCompactTransaction = (json) => {
+    return document.Transaction.getCompactTransaction(json)
+  };
+
+  let getNormalHeader = (doctype, json) => {
+    let raw = "";
+    raw += "Version: " + (json.version || DOCUMENTS_VERSION) + "\n";
+    raw += "Type: " + doctype + "\n";
+    raw += "Currency: " + json.currency + "\n";
+    raw += "Issuer: " + json.issuer + "\n";
+    return raw;
+  };
+
+  let signed = (raw, json) => {
+    raw += json.signature + '\n';
+    return raw;
+  };
+};
diff --git a/app/common/lib/regex.js b/app/common/lib/regex.js
new file mode 100644
index 000000000..a0ae4bdb5
--- /dev/null
+++ b/app/common/lib/regex.js
@@ -0,0 +1,5 @@
+"use strict";
+
+module.exports = {
+  BMA_REGEXP: /^BASIC_MERKLED_API( ([a-z_][a-z0-9-_.]*))?( ([0-9.]+))?( ([0-9a-f:]+))?( ([0-9]+))$/
+}
\ No newline at end of file
diff --git a/app/common/lib/txunlock.js b/app/common/lib/txunlock.js
new file mode 100644
index 000000000..e423355f0
--- /dev/null
+++ b/app/common/lib/txunlock.js
@@ -0,0 +1,73 @@
+"use strict";
+
+let Parser = require("jison").Parser;
+let buid = require('./buid');
+
+let grammar = {
+  "lex": {
+    "rules": [
+      ["\\s+",                    "/* skip whitespace */"],
+      ["\\&\\&",                  "return 'AND';"],
+      ["\\|\\|",                  "return 'OR';"],
+      ["\\(",                     "return '(';"],
+      ["\\)",                     "return ')';"],
+      ["[0-9A-Za-z]{40,64}",      "return 'PARAMETER';"],
+      ["[0-9]{1,10}",             "return 'PARAMETER';"],
+      ["SIG",                     "return 'SIG';"],
+      ["XHX",                     "return 'XHX';"],
+      ["CLTV",                    "return 'CLTV';"],
+      ["CSV",                     "return 'CSV';"],
+      ["$",                       "return 'EOF';"]
+    ]
+  },
+
+  "operators": [
+    ["left", "AND", "OR"]
+  ],
+
+  "bnf": {
+    "expressions" :[
+      [ "e EOF",   "return $1;"  ]
+    ],
+
+    "e" :[
+      [ "e AND e", "$$ = $1 && $3;" ],
+      [ "e OR e",  "$$ = $1 || $3;" ],
+      [ "SIG ( e )","$$ = yy.sig($3);"],
+      [ "XHX ( e )","$$ = yy.xHx($3);"],
+      [ "CLTV ( e )","$$ = yy.cltv($3);"],
+      [ "CSV ( e )","$$ = yy.csv($3);"],
+      [ "PARAMETER", "$$ = $1;" ],
+      [ "( e )",   "$$ = $2;" ]
+    ]
+  }
+};
+
+module.exports = function unlock(conditionsStr, executions, metadata) {
+
+  let parser = new Parser(grammar);
+
+  parser.yy = {
+    i: 0,
+    sig: function (pubkey) {
+      let sigParam = executions[this.i++];
+      return (sigParam && pubkey === sigParam.pubkey && sigParam.sigOK) || false;
+    },
+    xHx: function(hash) {
+      let xhxParam = executions[this.i++];
+      return buid.format.hashf(xhxParam) === hash;
+    },
+    cltv: function(deadline) {
+      return metadata.currentTime && metadata.currentTime >= parseInt(deadline);
+    },
+    csv: function(amountToWait) {
+      return metadata.elapsedTime && metadata.elapsedTime >= parseInt(amountToWait);
+    }
+  };
+
+  try {
+    return parser.parse(conditionsStr);
+  } catch(e) {
+    return false;
+  }
+};
diff --git a/app/lib/blockchain/DuniterBlockchain.ts b/app/lib/blockchain/DuniterBlockchain.ts
index 5f2739205..6ea0513af 100644
--- a/app/lib/blockchain/DuniterBlockchain.ts
+++ b/app/lib/blockchain/DuniterBlockchain.ts
@@ -13,7 +13,7 @@ import {MembershipDTO} from "../dto/MembershipDTO"
 import {TransactionDTO} from "../dto/TransactionDTO"
 
 const _ = require('underscore')
-const common          = require('duniter-common')
+const common          = require('../../../app/common')
 
 export class DuniterBlockchain extends MiscIndexedBlockchain {
 
diff --git a/app/lib/common/crypto/base58.ts b/app/lib/common-libs/crypto/base58.ts
similarity index 100%
rename from app/lib/common/crypto/base58.ts
rename to app/lib/common-libs/crypto/base58.ts
diff --git a/app/lib/common/crypto/keyring.ts b/app/lib/common-libs/crypto/keyring.ts
similarity index 100%
rename from app/lib/common/crypto/keyring.ts
rename to app/lib/common-libs/crypto/keyring.ts
diff --git a/app/lib/common/crypto/nacl-util.ts b/app/lib/common-libs/crypto/nacl-util.ts
similarity index 100%
rename from app/lib/common/crypto/nacl-util.ts
rename to app/lib/common-libs/crypto/nacl-util.ts
diff --git a/app/lib/common.ts b/app/lib/common.ts
index a16cf1bdc..edf0ef092 100644
--- a/app/lib/common.ts
+++ b/app/lib/common.ts
@@ -1,4 +1,4 @@
-const common = require('duniter-common')
+const common = require('../../app/common')
 
 export function hashf(str:string) {
   return common.hashf(str).toUpperCase()
diff --git a/app/lib/constants.ts b/app/lib/constants.ts
index 92fef4c10..a410214f9 100644
--- a/app/lib/constants.ts
+++ b/app/lib/constants.ts
@@ -1,6 +1,6 @@
 "use strict";
 
-const common = require('duniter-common')
+const common = require('../../app/common')
 
 const UDID2        = "udid2;c;([A-Z-]*);([A-Z-]*);(\\d{4}-\\d{2}-\\d{2});(e\\+\\d{2}\\.\\d{2}(\\+|-)\\d{3}\\.\\d{2});(\\d+)(;?)";
 const PUBKEY       = common.constants.FORMATS.PUBKEY
diff --git a/app/lib/dal/fileDAL.ts b/app/lib/dal/fileDAL.ts
index ab9e576f5..f02c58a8d 100644
--- a/app/lib/dal/fileDAL.ts
+++ b/app/lib/dal/fileDAL.ts
@@ -19,7 +19,7 @@ const fs      = require('fs')
 const path    = require('path')
 const readline = require('readline')
 const _       = require('underscore');
-const common = require('duniter-common');
+const common = require('../../../app/common');
 const indexer = require('../indexer').Indexer
 const logger = require('../logger').NewLogger('filedal');
 const constants = require('../constants');
diff --git a/app/lib/dal/sqliteDAL/MetaDAL.ts b/app/lib/dal/sqliteDAL/MetaDAL.ts
index 52047f2f6..8dccc0eef 100644
--- a/app/lib/dal/sqliteDAL/MetaDAL.ts
+++ b/app/lib/dal/sqliteDAL/MetaDAL.ts
@@ -14,8 +14,8 @@ import {IdentityDTO} from "../../dto/IdentityDTO"
 
 const _ = require('underscore')
 const logger = require('../../logger').NewLogger('metaDAL');
-const common = require('duniter-common');
-const rawer = require('duniter-common').rawer;
+const common = require('../../../../app/common');
+const rawer = require('../../../../app/common').rawer;
 const constants = require('./../../constants');
 
 export interface DBMeta {
diff --git a/app/lib/dal/sqliteDAL/index/CIndexDAL.ts b/app/lib/dal/sqliteDAL/index/CIndexDAL.ts
index 60c0bf938..8cea74334 100644
--- a/app/lib/dal/sqliteDAL/index/CIndexDAL.ts
+++ b/app/lib/dal/sqliteDAL/index/CIndexDAL.ts
@@ -3,7 +3,7 @@ import {SQLiteDriver} from "../../drivers/SQLiteDriver";
 import {CindexEntry} from "../../../indexer";
 
 const constants = require('./../../../constants');
-const common = require('duniter-common');
+const common = require('../../../../../app/common');
 const indexer         = require('../../../indexer').Indexer
 
 export class CIndexDAL extends AbstractIndex<CindexEntry> {
diff --git a/app/lib/dal/sqliteDAL/index/SIndexDAL.ts b/app/lib/dal/sqliteDAL/index/SIndexDAL.ts
index 13b0c04e3..b1d97503b 100644
--- a/app/lib/dal/sqliteDAL/index/SIndexDAL.ts
+++ b/app/lib/dal/sqliteDAL/index/SIndexDAL.ts
@@ -2,7 +2,7 @@ import {Indexer, SindexEntry} from "../../../indexer";
 import {SQLiteDriver} from "../../drivers/SQLiteDriver";
 import {AbstractIndex} from "../AbstractIndex";
 const _ = require('underscore');
-const common = require('duniter-common');
+const common = require('../../../../../app/common');
 const constants = require('../../../constants');
 
 export class SIndexDAL extends AbstractIndex<SindexEntry> {
diff --git a/app/lib/indexer.ts b/app/lib/indexer.ts
index dad52458f..cb2f4b22e 100644
--- a/app/lib/indexer.ts
+++ b/app/lib/indexer.ts
@@ -7,11 +7,11 @@ import {CertificationDTO} from "./dto/CertificationDTO"
 import {TransactionDTO} from "./dto/TransactionDTO"
 import {DBHead} from "./db/DBHead"
 import {LOCAL_RULES_HELPERS} from "./rules/local_rules"
-import {verify} from "./common/crypto/keyring"
+import {verify} from "./common-libs/crypto/keyring";
 
 const co              = require('co');
 const _               = require('underscore');
-const common          = require('duniter-common');
+const common          = require('../../app/common');
 
 const constants       = common.constants
 const rawer           = common.rawer
diff --git a/app/lib/rules/global_rules.ts b/app/lib/rules/global_rules.ts
index 4a6acb1b5..ca4eff2c4 100644
--- a/app/lib/rules/global_rules.ts
+++ b/app/lib/rules/global_rules.ts
@@ -5,10 +5,10 @@ import {DBBlock} from "../db/DBBlock"
 import {TransactionDTO} from "../dto/TransactionDTO"
 import * as local_rules from "./local_rules"
 import {BlockDTO} from "../dto/BlockDTO"
-import {verify} from "../common/crypto/keyring"
+import {verify} from "../common-libs/crypto/keyring"
 
 const _              = require('underscore');
-const common         = require('duniter-common');
+const common         = require('../../../app/common');
 const indexer        = require('../indexer').Indexer
 
 const constants      = common.constants
diff --git a/app/lib/rules/helpers.ts b/app/lib/rules/helpers.ts
index 0bb0dbea5..7203dc22b 100644
--- a/app/lib/rules/helpers.ts
+++ b/app/lib/rules/helpers.ts
@@ -1,6 +1,6 @@
 import {ConfDTO} from "../dto/ConfDTO"
 
-const common    = require('duniter-common');
+const common    = require('../../../app/common');
 const constants = common.constants
 
 export function maxAcceleration (conf:ConfDTO) {
diff --git a/app/lib/rules/index.ts b/app/lib/rules/index.ts
index e3cc2d4c6..950fc226a 100644
--- a/app/lib/rules/index.ts
+++ b/app/lib/rules/index.ts
@@ -4,7 +4,7 @@ import {ConfDTO} from "../dto/ConfDTO"
 import {IndexEntry} from "../indexer"
 import {LOCAL_RULES_FUNCTIONS} from "./local_rules"
 
-const common      = require('duniter-common');
+const common      = require('../../../app/common');
 const Block        = common.document.Block
 
 export const ALIAS = {
diff --git a/app/lib/rules/local_rules.ts b/app/lib/rules/local_rules.ts
index 1bf8139c8..b67f44b6f 100644
--- a/app/lib/rules/local_rules.ts
+++ b/app/lib/rules/local_rules.ts
@@ -4,10 +4,10 @@ import {ConfDTO} from "../dto/ConfDTO"
 import {CindexEntry, IndexEntry, Indexer, MindexEntry, SindexEntry} from "../indexer"
 import {BaseDTO, TransactionDTO} from "../dto/TransactionDTO"
 import {DBBlock} from "../db/DBBlock"
-import {verify} from "../common/crypto/keyring"
+import {verify} from "../common-libs/crypto/keyring"
 
 const _          = require('underscore');
-const common     = require('duniter-common');
+const common     = require('../../../app/common');
 
 const constants       = common.constants
 const hashf           = common.hashf
diff --git a/app/modules/bma/lib/controllers/blockchain.ts b/app/modules/bma/lib/controllers/blockchain.ts
index 4195fabb4..39ae637cc 100644
--- a/app/modules/bma/lib/controllers/blockchain.ts
+++ b/app/modules/bma/lib/controllers/blockchain.ts
@@ -6,7 +6,7 @@ import {BMAConstants} from "../constants"
 
 const co               = require('co');
 const _                = require('underscore');
-const common           = require('duniter-common');
+const common           = require('../../../../../app/common');
 const http2raw         = require('../http2raw');
 const toJson = require('../tojson');
 
diff --git a/app/modules/bma/lib/controllers/transactions.ts b/app/modules/bma/lib/controllers/transactions.ts
index 7b889682f..7d7a2de29 100644
--- a/app/modules/bma/lib/controllers/transactions.ts
+++ b/app/modules/bma/lib/controllers/transactions.ts
@@ -4,7 +4,7 @@ import {Source} from "../entity/source"
 import {BMAConstants} from "../constants";
 
 const _                = require('underscore');
-const common           = require('duniter-common');
+const common           = require('../../../../../app/common');
 const http2raw         = require('../http2raw');
 
 const Transaction = common.document.Transaction
diff --git a/app/modules/bma/lib/controllers/wot.ts b/app/modules/bma/lib/controllers/wot.ts
index 7f21cfe98..1fb49b5ab 100644
--- a/app/modules/bma/lib/controllers/wot.ts
+++ b/app/modules/bma/lib/controllers/wot.ts
@@ -2,7 +2,7 @@ import {AbstractController} from "./AbstractController"
 import {BMAConstants} from "../constants"
 
 const _        = require('underscore');
-const common   = require('duniter-common');
+const common   = require('../../../../../app/common');
 const http2raw = require('../http2raw');
 
 const Identity = common.document.Identity
diff --git a/app/modules/crawler/index.ts b/app/modules/crawler/index.ts
index aa0a078fd..3a777527c 100644
--- a/app/modules/crawler/index.ts
+++ b/app/modules/crawler/index.ts
@@ -6,7 +6,7 @@ import {Synchroniser} from "./lib/sync"
 import {req2fwd} from "./lib/req2fwd"
 import {CrawlerConstants} from "./lib/constants"
 
-const common = require('duniter-common');
+const common = require('../../../app/common');
 const Peer = common.document.Peer
 
 export const CrawlerDependency = {
diff --git a/app/modules/crawler/lib/constants.ts b/app/modules/crawler/lib/constants.ts
index 32c976546..a3ce7dd4d 100644
--- a/app/modules/crawler/lib/constants.ts
+++ b/app/modules/crawler/lib/constants.ts
@@ -1,4 +1,4 @@
-const common = require('duniter-common')
+const common = require('../../../../app/common')
 
 export const CrawlerConstants = {
 
diff --git a/app/modules/crawler/lib/crawler.ts b/app/modules/crawler/lib/crawler.ts
index 4af7079cb..c739ea047 100644
--- a/app/modules/crawler/lib/crawler.ts
+++ b/app/modules/crawler/lib/crawler.ts
@@ -15,7 +15,7 @@ import { cleanLongDownPeers } from "./garbager";
 const _ = require('underscore');
 const async = require('async');
 const querablep = require('querablep');
-const Peer = require('duniter-common').document.Peer;
+const Peer = require('../../../../app/common').document.Peer;
 
 /**
  * Service which triggers the server's peering generation (actualization of the Peer document).
diff --git a/app/modules/crawler/lib/req2fwd.ts b/app/modules/crawler/lib/req2fwd.ts
index 8592e56f2..bccb576bf 100644
--- a/app/modules/crawler/lib/req2fwd.ts
+++ b/app/modules/crawler/lib/req2fwd.ts
@@ -1,6 +1,6 @@
 import {Contacter} from "./contacter"
 
-const common = require('duniter-common')
+const common = require('../../../../app/common')
 
 export const req2fwd = async (requirements:any, toHost:string, toPort:number, logger:any) => {
   const mss:any = {};
diff --git a/app/modules/crawler/lib/sandbox.ts b/app/modules/crawler/lib/sandbox.ts
index 430edc921..af03705e1 100644
--- a/app/modules/crawler/lib/sandbox.ts
+++ b/app/modules/crawler/lib/sandbox.ts
@@ -2,8 +2,8 @@
 import {Contacter} from "./contacter"
 import {Server} from "../../../../server"
 
-const rawer = require('duniter-common').rawer;
-const parsers = require('duniter-common').parsers;
+const rawer = require('../../../../app/common').rawer;
+const parsers = require('../../../../app/common').parsers;
 
 export const pullSandbox = async (currency:string, fromHost:string, fromPort:number, toHost:string, toPort:number, logger:any) => {
   const from = new Contacter(fromHost, fromPort);
diff --git a/app/modules/crawler/lib/sync.ts b/app/modules/crawler/lib/sync.ts
index 1c5b21a33..95981ede9 100644
--- a/app/modules/crawler/lib/sync.ts
+++ b/app/modules/crawler/lib/sync.ts
@@ -17,7 +17,7 @@ const _            = require('underscore');
 const moment       = require('moment');
 const multimeter   = require('multimeter');
 const makeQuerablePromise = require('querablep');
-const common       = require('duniter-common');
+const common       = require('../../../../app/common');
 const Peer         = common.document.Peer;
 const dos2unix = common.dos2unix;
 const hashf = common.hashf;
diff --git a/app/modules/keypair/index.ts b/app/modules/keypair/index.ts
index 39e5d42a6..7bfcceae0 100644
--- a/app/modules/keypair/index.ts
+++ b/app/modules/keypair/index.ts
@@ -1,4 +1,4 @@
-import {randomKey} from "../../lib/common/crypto/keyring"
+import {randomKey} from "../../lib/common-libs/crypto/keyring"
 import {ConfDTO, KeypairConfDTO} from "../../lib/dto/ConfDTO"
 import {Scrypt} from "./lib/scrypt"
 
diff --git a/app/modules/keypair/lib/scrypt.ts b/app/modules/keypair/lib/scrypt.ts
index 2e292eaad..7092a400a 100644
--- a/app/modules/keypair/lib/scrypt.ts
+++ b/app/modules/keypair/lib/scrypt.ts
@@ -1,6 +1,6 @@
 "use strict";
-import {Base58encode} from "../../../lib/common/crypto/base58"
-import {decodeBase64} from "../../../lib/common/crypto/nacl-util"
+import {Base58encode} from "../../../lib/common-libs/crypto/base58"
+import {decodeBase64} from "../../../lib/common-libs/crypto/nacl-util"
 
 const nacl     = require('tweetnacl');
 const scrypt   = require('scryptb');
diff --git a/app/modules/prover/index.ts b/app/modules/prover/index.ts
index 9d343fb1f..e4e12e548 100644
--- a/app/modules/prover/index.ts
+++ b/app/modules/prover/index.ts
@@ -6,7 +6,7 @@ import {Prover} from "./lib/prover"
 import {Contacter} from "../crawler/lib/contacter";
 
 const async = require('async');
-const common = require('duniter-common');
+const common = require('../../../app/common');
 
 const Peer = common.document.Peer
 
diff --git a/app/modules/prover/lib/blockGenerator.ts b/app/modules/prover/lib/blockGenerator.ts
index d7a3b4032..2854cb6f3 100644
--- a/app/modules/prover/lib/blockGenerator.ts
+++ b/app/modules/prover/lib/blockGenerator.ts
@@ -7,12 +7,12 @@ import {LOCAL_RULES_HELPERS} from "../../../lib/rules/local_rules"
 import {Indexer} from "../../../lib/indexer"
 import {FileDAL} from "../../../lib/dal/fileDAL"
 import {DBBlock} from "../../../lib/db/DBBlock"
-import {verify} from "../../../lib/common/crypto/keyring"
+import {verify} from "../../../lib/common-libs/crypto/keyring"
 
 const _               = require('underscore');
 const moment          = require('moment');
 const inquirer        = require('inquirer');
-const common          = require('duniter-common');
+const common          = require('../../../../app/common');
 
 const hashf         = common.hashf;
 const rawer         = common.rawer;
diff --git a/app/modules/prover/lib/blockProver.ts b/app/modules/prover/lib/blockProver.ts
index e092b93f7..bb54b6b9d 100644
--- a/app/modules/prover/lib/blockProver.ts
+++ b/app/modules/prover/lib/blockProver.ts
@@ -4,7 +4,7 @@ import {PowEngine} from "./engine"
 import {DBBlock} from "../../../lib/db/DBBlock"
 
 const querablep       = require('querablep');
-const common          = require('duniter-common');
+const common          = require('../../../../app/common');
 
 const Block = common.document.Block
 
diff --git a/app/modules/prover/lib/permanentProver.ts b/app/modules/prover/lib/permanentProver.ts
index 142449125..42ac4442c 100644
--- a/app/modules/prover/lib/permanentProver.ts
+++ b/app/modules/prover/lib/permanentProver.ts
@@ -5,7 +5,7 @@ import {Constants} from "./constants"
 import {DBBlock} from "../../../lib/db/DBBlock"
 
 const querablep = require('querablep');
-const common = require('duniter-common');
+const common = require('../../../../app/common');
 const dos2unix = common.dos2unix;
 const parsers = common.parsers;
 
diff --git a/app/modules/prover/lib/proof.ts b/app/modules/prover/lib/proof.ts
index ccb196ca4..96c597842 100644
--- a/app/modules/prover/lib/proof.ts
+++ b/app/modules/prover/lib/proof.ts
@@ -3,12 +3,12 @@ import {hashf} from "../../../lib/common"
 import {DBBlock} from "../../../lib/db/DBBlock"
 import {ConfDTO} from "../../../lib/dto/ConfDTO"
 import {Constants} from "./constants"
-import {KeyGen} from "../../../lib/common/crypto/keyring"
+import {KeyGen} from "../../../lib/common-libs/crypto/keyring"
 
 const moment = require('moment');
-const dos2unix = require('duniter-common').dos2unix;
+const dos2unix = require('../../../../app/common').dos2unix;
 const querablep = require('querablep');
-const rawer = require('duniter-common').rawer;
+const rawer = require('../../../../app/common').rawer;
 
 const PAUSES_PER_TURN = 5;
 
diff --git a/app/service/BlockchainService.ts b/app/service/BlockchainService.ts
index b2195897c..760c37de5 100644
--- a/app/service/BlockchainService.ts
+++ b/app/service/BlockchainService.ts
@@ -11,7 +11,7 @@ import {GLOBAL_RULES_HELPERS} from "../lib/rules/global_rules"
 
 const _               = require('underscore');
 const co              = require('co');
-const parsers         = require('duniter-common').parsers;
+const parsers         = require('../../app/common').parsers;
 const constants       = require('../lib/constants');
 
 const CHECK_ALL_RULES = true;
diff --git a/app/service/IdentityService.ts b/app/service/IdentityService.ts
index 1bf368b01..b942c2677 100644
--- a/app/service/IdentityService.ts
+++ b/app/service/IdentityService.ts
@@ -8,7 +8,7 @@ import {RevocationDTO} from "../lib/dto/RevocationDTO"
 import {BasicIdentity, IdentityDTO} from "../lib/dto/IdentityDTO"
 import {CertificationDTO} from "../lib/dto/CertificationDTO"
 import {DBCert} from "../lib/dal/sqliteDAL/CertDAL"
-import {verify} from "../lib/common/crypto/keyring"
+import {verify} from "../lib/common-libs/crypto/keyring"
 
 "use strict";
 const constants       = require('../lib/constants');
diff --git a/app/service/MembershipService.ts b/app/service/MembershipService.ts
index 2693fc818..6c6d83a4f 100644
--- a/app/service/MembershipService.ts
+++ b/app/service/MembershipService.ts
@@ -6,7 +6,7 @@ import {LOCAL_RULES_HELPERS} from "../lib/rules/local_rules"
 import {GLOBAL_RULES_HELPERS} from "../lib/rules/global_rules"
 import {MembershipDTO} from "../lib/dto/MembershipDTO"
 
-const hashf           = require('duniter-common').hashf;
+const hashf           = require('../../app/common').hashf;
 const constants       = require('../lib/constants');
 
 export class MembershipService {
diff --git a/app/service/PeeringService.ts b/app/service/PeeringService.ts
index 52ed25c99..70d515191 100644
--- a/app/service/PeeringService.ts
+++ b/app/service/PeeringService.ts
@@ -5,16 +5,16 @@ import {DBPeer} from "../lib/dal/sqliteDAL/PeerDAL"
 import {DBBlock} from "../lib/db/DBBlock"
 import {Multicaster} from "../lib/streams/multicaster"
 import {PeerDTO} from "../lib/dto/PeerDTO"
-import {verify} from "../lib/common/crypto/keyring"
+import {verify} from "../lib/common-libs/crypto/keyring"
 
 const util           = require('util');
 const _              = require('underscore');
 const events         = require('events');
 const rp             = require('request-promise');
 const logger         = require('../lib/logger').NewLogger('peering');
-const dos2unix       = require('duniter-common').dos2unix;
-const hashf          = require('duniter-common').hashf;
-const rawer          = require('duniter-common').rawer;
+const dos2unix       = require('../../app/common').dos2unix;
+const hashf          = require('../../app/common').hashf;
+const rawer          = require('../../app/common').rawer;
 const constants      = require('../lib/constants');
 
 export interface Keyring {
diff --git a/package.json b/package.json
index ad6fec5e3..c286809cf 100644
--- a/package.json
+++ b/package.json
@@ -57,7 +57,6 @@
     "commander": "2.9.0",
     "daemonize2": "0.4.2",
     "ddos": "0.1.16",
-    "duniter-common": "1.4.x",
     "event-stream": "3.3.4",
     "errorhandler": "1.5.0",
     "express": "4.15.2",
@@ -65,6 +64,7 @@
     "express-fileupload": "0.0.5",
     "heapdump": "^0.3.9",
     "inquirer": "3.0.6",
+    "jison": "0.4.17",
     "merkle": "0.5.1",
     "moment": "2.18.1",
     "morgan": "1.8.1",
diff --git a/server.ts b/server.ts
index ada29a16b..5756973ac 100644
--- a/server.ts
+++ b/server.ts
@@ -8,7 +8,7 @@ import {FileDAL} from "./app/lib/dal/fileDAL"
 import {DuniterBlockchain} from "./app/lib/blockchain/DuniterBlockchain"
 import {SQLBlockchain} from "./app/lib/blockchain/SqlBlockchain"
 import * as stream from "stream"
-import {KeyGen, randomKey} from "./app/lib/common/crypto/keyring"
+import {KeyGen, randomKey} from "./app/lib/common-libs/crypto/keyring"
 
 interface HookableServer {
   getMainEndpoint: (...args:any[]) => Promise<any>
@@ -26,11 +26,11 @@ const archiver    = require('archiver');
 const unzip       = require('unzip2');
 const fs          = require('fs');
 const daemonize   = require("daemonize2")
-const parsers     = require('duniter-common').parsers;
+const parsers     = require('./app/common').parsers;
 const constants   = require('./app/lib/constants');
 const jsonpckg    = require('./package.json');
 const directory   = require('./app/lib/system/directory');
-const rawer       = require('duniter-common').rawer;
+const rawer       = require('./app/common').rawer;
 const logger      = require('./app/lib/logger').NewLogger('server');
 
 export class Server extends stream.Duplex implements HookableServer {
diff --git a/test/fast/block_format.js b/test/fast/block_format.js
index 9f57aa505..ee0ca79b3 100644
--- a/test/fast/block_format.js
+++ b/test/fast/block_format.js
@@ -1,6 +1,6 @@
 "use strict";
 const should  = require('should');
-const parsers = require('duniter-common').parsers;
+const parsers = require('../../app/common').parsers;
 
 const raw = "Version: 10\n" +
   "Type: Block\n" +
diff --git a/test/fast/block_local.js b/test/fast/block_local.js
index c8f286f5e..2bcb15fae 100644
--- a/test/fast/block_local.js
+++ b/test/fast/block_local.js
@@ -1,13 +1,13 @@
 "use strict";
 const co             = require('co');
 const should         = require('should');
-const parsers        = require('duniter-common').parsers;
+const parsers        = require('../../app/common').parsers;
 const indexer        = require('../../app/lib/indexer').Indexer
 const LOCAL_RULES    = require('../../app/lib/rules/local_rules').LOCAL_RULES_FUNCTIONS
 const ALIAS          = require('../../app/lib/rules').ALIAS
 const blocks         = require('../data/blocks.js');
 const parser         = parsers.parseBlock;
-const Block          = require('duniter-common').document.Block
+const Block          = require('../../app/common').document.Block
 const BlockDTO       = require('../../app/lib/dto/BlockDTO').BlockDTO
 
 const conf = {
diff --git a/test/fast/common/crypto.js b/test/fast/common/crypto.js
index 477a46f42..bdd3f903b 100644
--- a/test/fast/common/crypto.js
+++ b/test/fast/common/crypto.js
@@ -2,9 +2,9 @@
 const should = require('should');
 const co  = require('co');
 const nacl   = require('tweetnacl');
-const base58 = require('../../../app/lib/common/crypto/base58')
-const naclUtil = require('../../../app/lib/common/crypto/nacl-util')
-const keyring      = require('../../../app/lib/common/crypto/keyring')
+const base58 = require('../../../app/lib/common-libs/crypto/base58')
+const naclUtil = require('../../../app/lib/common-libs/crypto/nacl-util')
+const keyring      = require('../../../app/lib/common-libs/crypto/keyring')
 
 const Base58decode = base58.Base58decode
 const Base58encode = base58.Base58encode
diff --git a/test/fast/common/randomKey.js b/test/fast/common/randomKey.js
index 5c924fe65..404993d20 100644
--- a/test/fast/common/randomKey.js
+++ b/test/fast/common/randomKey.js
@@ -1,8 +1,8 @@
 "use strict";
 const co = require('co')
 const should = require('should');
-const keyring  = require('../../../app/lib/common/crypto/keyring')
-const naclUtil = require('../../../app/lib/common/crypto/nacl-util')
+const keyring  = require('../../../app/lib/common-libs/crypto/keyring')
+const naclUtil = require('../../../app/lib/common-libs/crypto/nacl-util')
 
 const enc = naclUtil.encodeBase64
 const dec = naclUtil.decodeBase64
diff --git a/test/fast/modules/common/crypto.js b/test/fast/modules/common/crypto.js
new file mode 100644
index 000000000..ce72bc5c5
--- /dev/null
+++ b/test/fast/modules/common/crypto.js
@@ -0,0 +1,50 @@
+"use strict";
+const should = require('should');
+const co  = require('co');
+const nacl   = require('tweetnacl');
+const base58 = require('../../../../app/common/lib/crypto/base58');
+const keyring      = require('../../../../app/common/lib/crypto/keyring');
+
+const enc = nacl.util.encodeBase64,
+    dec = nacl.util.decodeBase64;
+
+let pub, sec, rawPub, rawSec;
+
+describe('ed25519 tests:', function(){
+
+  before(() => co(function*() {
+    // Generate the keypair
+    const keyPair = keyring.Key('HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP');
+    pub = base58.decode(keyPair.publicKey);
+    sec = base58.decode(keyPair.secretKey);
+    rawPub = base58.encode(pub);
+    rawSec = base58.encode(sec);
+  }));
+
+  //it('good signature from existing secret key should be verified', function(done){
+  //  const keys = nacl.sign.scryptKeyPair.fromSecretKey(dec("TM0Imyj/ltqdtsNG7BFOD1uKMZ81q6Yk2oz27U+4pvs9QBfD6EOJWpK3CqdNG368nJgszy7ElozAzVXxKvRmDA=="));
+  //  const msg = "cg==";
+  //  const goodSig = dec("52Hh9omo9rxklulAE7gvVeYvAq0GgXYoZE2NB/gzehpCYIT04bMcGIs5bhYLaH93oib34jsVMWs9Udadr1B+AQ==");
+  //  const sig = crypto.signSync(msg, keys.secretKey);
+  //  sig.should.equal(enc(goodSig));
+  //  crypto.verify(msg, sig, enc(keys.publicKey)).should.be.true;
+  //  done();
+  //});
+
+  it('good signature from generated key should be verified', function(done){
+    const msg = "Some message to be signed";
+    const sig = keyring.Key(rawPub, rawSec).signSync(msg);
+    const verified = keyring.verify(msg, sig, rawPub);
+    verified.should.equal(true);
+    done();
+  });
+
+  it('wrong signature from generated key should NOT be verified', function(done){
+    const msg = "Some message to be signed";
+    const cor = dec(enc(msg) + 'delta');
+    const sig = keyring.Key(rawPub, rawSec).signSync(msg);
+    const verified = keyring.verify(cor, sig, rawPub);
+    verified.should.equal(false);
+    done();
+  });
+});
diff --git a/test/fast/modules/common/grammar-test.js b/test/fast/modules/common/grammar-test.js
new file mode 100644
index 000000000..3e27dcc86
--- /dev/null
+++ b/test/fast/modules/common/grammar-test.js
@@ -0,0 +1,70 @@
+"use strict";
+
+const unlock    = require('../../../../app/common/lib/txunlock');
+const should    = require('should');
+
+describe('Grammar', () => {
+
+  let k1 = "HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd";
+  let k2 = "GgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd";
+  let Ha = "CA978112CA1BBDCAFAC231B39A23DC4DA786EFF8147C4E72B9807785AFEE48BB";
+  let Hz = "594E519AE499312B29433B7DD8A97FF068DEFCBA9755B6D5D00E84C524D67B06";
+
+  it('SIG should work', () => {
+
+    unlock('SIG(' + k1 + ')', [{ pubkey: k1, sigOK: true }]).should.equal(true);
+    unlock('SIG(' + k1 + ')', [{ pubkey: k1, sigOK: false }]).should.equal(false);
+    unlock('SIG(' + k1 + ')', [{ pubkey: k2, sigOK: false }]).should.equal(false);
+    unlock('SIG(' + k1 + ')', [{ pubkey: k2, sigOK: true }]).should.equal(false);
+  });
+
+  it('XHX should work', () => {
+
+    unlock('XHX(' + Ha + ')', ['a']).should.equal(true);
+    unlock('XHX(' + Hz + ')', ['z']).should.equal(true);
+    unlock('XHX(' + Hz + ')', ['a']).should.equal(false);
+    unlock('XHX(' + Ha + ')', ['z']).should.equal(false);
+  });
+
+  it('&& keywork should work', () => {
+
+    unlock('SIG(' + k1 + ') && SIG(' + k2 + ')', [{ pubkey: k1, sigOK: true }, { pubkey: k1, sigOK: true }]).should.equal(false);
+    unlock('SIG(' + k1 + ') && SIG(' + k2 + ')', [{ pubkey: k1, sigOK: true }, { pubkey: k2, sigOK: false }]).should.equal(false);
+    unlock('SIG(' + k1 + ') && SIG(' + k2 + ')', [{ pubkey: k1, sigOK: true }, { pubkey: k2, sigOK: true }]).should.equal(true);
+  });
+
+  it('|| keywork should work', () => {
+
+    unlock('SIG(' + k1 + ') || SIG(' + k2 + ')', [{ pubkey: k1, sigOK: true }, { pubkey: k2, sigOK: true }]).should.equal(true);
+    unlock('SIG(' + k1 + ') || SIG(' + k2 + ')', [{ pubkey: k1, sigOK: false }, { pubkey: k2, sigOK: true }]).should.equal(true);
+    unlock('SIG(' + k1 + ') || SIG(' + k2 + ')', [{ pubkey: k1, sigOK: true }, { pubkey: k2, sigOK: false }]).should.equal(true);
+    unlock('SIG(' + k1 + ') || SIG(' + k2 + ')', [{ pubkey: k1, sigOK: false }, { pubkey: k2, sigOK: false }]).should.equal(false);
+  });
+
+  it('|| && keyworks combined should work', () => {
+
+    unlock('SIG(' + k1 + ') || (SIG(' + k1 + ') && SIG(' + k2 + '))', [{ pubkey: k1, sigOK: true },{ pubkey: k1, sigOK: true },{ pubkey: k2, sigOK: true }]).should.equal(true);
+    unlock('SIG(' + k2 + ') || (SIG(' + k1 + ') && SIG(' + k2 + '))', [{ pubkey: k2, sigOK: false },{ pubkey: k1, sigOK: true },{ pubkey: k2, sigOK: false }]).should.equal(false);
+  });
+
+  it('SIG XHX functions combined should work', () => {
+
+    unlock('SIG(' + k1 + ') && XHX(' + Ha + ')', [{ pubkey: k1, sigOK: true },'a']).should.equal(true);
+    unlock('SIG(' + k1 + ') && XHX(' + Ha + ')', [{ pubkey: k1, sigOK: true },'z']).should.equal(false);
+    unlock('SIG(' + k1 + ') || XHX(' + Ha + ')', [{ pubkey: k1, sigOK: true },'a']).should.equal(true);
+    unlock('SIG(' + k1 + ') || XHX(' + Ha + ')', [{ pubkey: k1, sigOK: true },'z']).should.equal(true);
+    unlock('SIG(' + k1 + ') || XHX(' + Ha + ')', [{ pubkey: k1, sigOK: false },'z']).should.equal(false);
+    unlock('SIG(' + k1 + ') || XHX(' + Ha + ')', [{ pubkey: k1, sigOK: false },'a']).should.equal(true);
+  });
+
+  it('SIG, XHX, &&, || words combined should work', () => {
+
+    unlock('SIG(' + k1 + ') && XHX(' + Ha + ') || XHX(' + Hz + ')', [{ pubkey: k1, sigOK: true },'a','z']).should.equal(true);
+    unlock('SIG(' + k1 + ') && XHX(' + Ha + ') || XHX(' + Hz + ')', [{ pubkey: k1, sigOK: true },'a','a']).should.equal(true);
+    unlock('SIG(' + k1 + ') && XHX(' + Ha + ') || XHX(' + Hz + ')', [{ pubkey: k1, sigOK: true },'z','a']).should.equal(false);
+    unlock('SIG(' + k1 + ') && XHX(' + Ha + ') || XHX(' + Hz + ')', [{ pubkey: k1, sigOK: false },'a','a']).should.equal(false);
+    unlock('SIG(' + k1 + ') && XHX(' + Ha + ') || XHX(' + Hz + ')', [{ pubkey: k1, sigOK: false },'a','z']).should.equal(true);
+    unlock('SIG(' + k1 + ') && XHX(' + Ha + ') || XHX(' + Hz + ')', [{ pubkey: k1, sigOK: false },'z','z']).should.equal(true);
+    unlock('(SIG(EA7Dsw39ShZg4SpURsrgMaMqrweJPUFPYHwZA8e92e3D) || XHX(03AC674216F3E15C761EE1A5E255F067953623C8B388B4459E13F978D7C846F4))', [{ pubkey: k1, sigOK: false },'1234']).should.equal(true);
+  });
+});
diff --git a/test/fast/modules/common/peering.js b/test/fast/modules/common/peering.js
new file mode 100644
index 000000000..864b29001
--- /dev/null
+++ b/test/fast/modules/common/peering.js
@@ -0,0 +1,63 @@
+"use strict";
+const should   = require('should');
+const assert   = require('assert');
+const parsers  = require('../../../../app/common/lib/parsers');
+
+const rawPeer = "" +
+  "Version: 10\n" +
+  "Type: Peer\n" +
+  "Currency: beta_brousouf\n" +
+  "PublicKey: 3Z7w5g4gC9oxwEbATnmK2UFgGWhLZPmZQb5dRxvNrXDu\n" +
+  "Block: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855\n" +
+  "Endpoints:\n" +
+  "BASIC_MERKLED_API duniter.twiced.fr 88.163.127.43 9101\n" +
+  "OTHER_PROTOCOL 88.163.127.43 9102\n" +
+  "bvuKzc6+cGWMGC8FIkZHN8kdQhaRL/MK60KYyw5vJqkKEgxXbygQHAzfoojeSY4gPKIu4FggBkR1HndSEm2FAQ==\n";
+
+const Peer = require('../../../../app/common/lib/document/peer');
+
+describe('Peer', function(){
+
+  describe('of some key', function(){
+
+    var pr;
+
+    before(function(done) {
+      pr = Peer.fromJSON(parsers.parsePeer.syncWrite(rawPeer));
+      done();
+    });
+
+    it('should be version 10', function(){
+      assert.equal(pr.version, 10);
+    });
+
+    it('should have beta_brousoufs currency name', function(){
+      assert.equal(pr.currency, 'beta_brousouf');
+    });
+
+    it('should have public key', function(){
+      assert.equal(pr.pubkey, '3Z7w5g4gC9oxwEbATnmK2UFgGWhLZPmZQb5dRxvNrXDu');
+    });
+
+    it('should have 2 endpoints', function(){
+      assert.equal(pr.endpoints.length, 2);
+    });
+
+    it('should have DNS', function(){
+      assert.equal(pr.getDns(), 'duniter.twiced.fr');
+    });
+
+    it('should have IPv4', function(){
+      should.exist(pr.getIPv4());
+      assert.equal(pr.getIPv4(), "88.163.127.43");
+    });
+
+    it('should have no IPv6 address', function(){
+      should.not.exist(pr.getIPv6());
+    });
+
+    it('should have port 9101', function(){
+      assert.equal(pr.getPort(), 9101);
+    });
+  });
+});
diff --git a/test/fast/modules/common/randomKey.js b/test/fast/modules/common/randomKey.js
new file mode 100644
index 000000000..7a21f9cf8
--- /dev/null
+++ b/test/fast/modules/common/randomKey.js
@@ -0,0 +1,35 @@
+"use strict";
+const should = require('should');
+const co  = require('co');
+const nacl   = require('tweetnacl');
+const keyring      = require('../../../../app/common/lib/crypto/keyring');
+
+const enc = nacl.util.encodeBase64,
+    dec = nacl.util.decodeBase64;
+
+let key;
+
+describe('Random keypair', function(){
+
+  before(() => co(function*() {
+    // Generate the keypair
+    key = keyring.randomKey()
+  }));
+
+  it('good signature from generated key should be verified', function(done){
+    const msg = "Some message to be signed";
+    const sig = keyring.Key(key.publicKey, key.secretKey).signSync(msg);
+    const verified = keyring.verify(msg, sig, key.publicKey);
+    verified.should.equal(true);
+    done();
+  });
+
+  it('wrong signature from generated key should NOT be verified', function(done){
+    const msg = "Some message to be signed";
+    const cor = dec(enc(msg) + 'delta');
+    const sig = keyring.Key(key.publicKey, key.secretKey).signSync(msg);
+    const verified = keyring.verify(cor, sig, key.publicKey);
+    verified.should.equal(false);
+    done();
+  });
+});
diff --git a/test/fast/modules/common/tx_format.js b/test/fast/modules/common/tx_format.js
new file mode 100644
index 000000000..10fc83238
--- /dev/null
+++ b/test/fast/modules/common/tx_format.js
@@ -0,0 +1,28 @@
+"use strict";
+var should  = require('should');
+var parsers = require('../../../../app/common/lib/parsers');
+
+var raw = "Version: 10\n" +
+    "Type: Transaction\n" +
+    "Currency: test_net\n" +
+    "Blockstamp: 3-2A27BD040B16B7AF59DDD88890E616987F4DD28AA47B9ABDBBEE46257B88E945\n" +
+    "Locktime: 0\n" +
+    "Issuers:\n" +
+    "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk\n" +
+    "Inputs:\n" +
+    "100000:0:D:HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:3428\n" +
+    "Unlocks:\n" +
+    "0:SIG(0)\n" +
+    "Outputs:\n" +
+    "1000:0:SIG(yGKRRB18B4eaZQdksWBZubea4VJKFSSpii2okemP7x1)\n" +
+    "99000:0:SIG(HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk)\n" +
+    "Comment: reessai\n" +
+    "P6MxJ/2SdkvNDyIyWuOkTz3MUwsgsfo70j+rpWeQWcm6GdvKQsbplB8482Ar1HMz2q0h5V3tfMqjCuAeWVQ+Ag==\n";
+
+describe("Transaction format", function(){
+
+    var parser = parsers.parseTransaction;
+
+    it('a valid block should be well formatted', () => parser.syncWrite(raw));
+
+});
diff --git a/test/fast/v1.0-local-index.js b/test/fast/v1.0-local-index.js
index 29592f579..1f27f3624 100644
--- a/test/fast/v1.0-local-index.js
+++ b/test/fast/v1.0-local-index.js
@@ -2,9 +2,9 @@
 
 const _       = require('underscore');
 const should  = require('should');
-const parsers = require('duniter-common').parsers;
+const parsers = require('../../app/common').parsers;
 const indexer = require('../../app/lib/indexer').Indexer
-const constants = require('duniter-common').constants;
+const constants = require('../../app/common').constants;
 const BlockDTO = require('../../app/lib/dto/BlockDTO').BlockDTO
 
 const raw = "Version: 10\n" +
diff --git a/test/integration/cli.js b/test/integration/cli.js
index f6572d0ed..a6bdcfe2b 100644
--- a/test/integration/cli.js
+++ b/test/integration/cli.js
@@ -8,7 +8,7 @@ const _         = require('underscore');
 const toolbox   = require('./tools/toolbox');
 const duniter   = require('../../index');
 const merkleh   = require('../../app/lib/helpers/merkle');
-const hashf     = require('duniter-common').hashf;
+const hashf     = require('../../app/common').hashf;
 const constants = require('../../app/lib/constants');
 const MerkleDTO = require('../../app/lib/dto/MerkleDTO').MerkleDTO
 
diff --git a/test/integration/server-sandbox.js b/test/integration/server-sandbox.js
index e76ef7a1b..be25bc56f 100644
--- a/test/integration/server-sandbox.js
+++ b/test/integration/server-sandbox.js
@@ -3,7 +3,7 @@
 const co        = require('co');
 const should    = require('should');
 const bma       = require('../../app/modules/bma').BmaDependency.duniter.methods.bma;
-const common    = require('duniter-common');
+const common    = require('../../app/common');
 const user      = require('./tools/user');
 const commit    = require('./tools/commit');
 const toolbox   = require('./tools/toolbox');
diff --git a/test/integration/tools/user.js b/test/integration/tools/user.js
index d2912b6e6..c25105a7d 100644
--- a/test/integration/tools/user.js
+++ b/test/integration/tools/user.js
@@ -4,9 +4,9 @@ const _ = require('underscore');
 const async		= require('async');
 const request	= require('request');
 const contacter = require('../../../app/modules/crawler').CrawlerDependency.duniter.methods.contacter;
-const common  = require('duniter-common');
+const common  = require('../../../app/common');
 const ucp     = common.buid;
-const parsers = require('duniter-common').parsers;
+const parsers = require('../../../app/common').parsers;
 const keyring	= common.keyring;
 const rawer		= common.rawer;
 const constants = require('../../../app/lib/constants');
diff --git a/test/integration/transactions-chaining.js b/test/integration/transactions-chaining.js
index 0ef947b48..c372b05b8 100644
--- a/test/integration/transactions-chaining.js
+++ b/test/integration/transactions-chaining.js
@@ -6,7 +6,7 @@ const should = require('should');
 const assert = require('assert');
 const constants = require('../../app/lib/constants');
 const bma       = require('../../app/modules/bma').BmaDependency.duniter.methods.bma;
-const common    = require('duniter-common');
+const common    = require('../../app/common');
 const toolbox   = require('./tools/toolbox');
 const node   = require('./tools/node');
 const user   = require('./tools/user');
diff --git a/test/integration/transactions-pruning.js b/test/integration/transactions-pruning.js
index d17dd4666..7b3be185f 100644
--- a/test/integration/transactions-pruning.js
+++ b/test/integration/transactions-pruning.js
@@ -6,7 +6,7 @@ const user      = require('./tools/user');
 const commit    = require('./tools/commit');
 const toolbox   = require('./tools/toolbox');
 const constants = require('../../app/lib/constants');
-const common    = require('duniter-common');
+const common    = require('../../app/common');
 
 const s1 = toolbox.server({
   currency: 'currency_one',
diff --git a/test/integration/v1.0-modules-api.js b/test/integration/v1.0-modules-api.js
index d0e79e535..c5f9c7874 100644
--- a/test/integration/v1.0-modules-api.js
+++ b/test/integration/v1.0-modules-api.js
@@ -7,7 +7,7 @@ const util    = require('util');
 const path    = require('path');
 const stream  = require('stream');
 const duniter = require('../../index');
-const parsers = require('duniter-common').parsers;
+const parsers = require('../../app/common').parsers;
 const querablep = require('querablep');
 
 describe("v1.0 Module API", () => {
-- 
GitLab